From 655827819a3625a79e45b689d3a6bad0d452952c Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Wed, 24 Jan 2024 20:57:33 -0300 Subject: [PATCH] feat(desktop): Search feature for Events History --- .../device_grid/desktop/layout_manager.dart | 2 +- lib/widgets/events/events_screen.dart | 44 ++++++++- .../desktop/timeline_sidebar.dart | 90 ++++++++++++------- 3 files changed, 100 insertions(+), 36 deletions(-) diff --git a/lib/widgets/device_grid/desktop/layout_manager.dart b/lib/widgets/device_grid/desktop/layout_manager.dart index 0ba12d6a..351642b0 100644 --- a/lib/widgets/device_grid/desktop/layout_manager.dart +++ b/lib/widgets/device_grid/desktop/layout_manager.dart @@ -115,7 +115,7 @@ class _LayoutManagerState extends State { color: IconTheme.of(context).color, ), tooltip: searchVisible - ? 'Disable Search' + ? 'Disable search' : MaterialLocalizations.of(context).searchFieldLabel, onPressed: () { setState(() => searchVisible = !searchVisible); diff --git a/lib/widgets/events/events_screen.dart b/lib/widgets/events/events_screen.dart index a70f6361..62eec5e4 100644 --- a/lib/widgets/events/events_screen.dart +++ b/lib/widgets/events/events_screen.dart @@ -37,6 +37,7 @@ import 'package:bluecherry_client/widgets/desktop_buttons.dart'; import 'package:bluecherry_client/widgets/downloads_manager.dart'; import 'package:bluecherry_client/widgets/error_warning.dart'; import 'package:bluecherry_client/widgets/events/event_player_desktop.dart'; +import 'package:bluecherry_client/widgets/events_timeline/desktop/timeline_sidebar.dart'; import 'package:bluecherry_client/widgets/misc.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -172,6 +173,11 @@ class EventsScreenState extends State { }); } + bool searchVisible = false; + final searchController = TextEditingController(); + final searchFocusNode = FocusNode(); + String searchQuery = ''; + @override Widget build(BuildContext context) { if (ServersProvider.instance.servers.isEmpty) { @@ -205,10 +211,42 @@ class EventsScreenState extends State { SubHeader( loc.servers, height: 38.0, - trailing: Text( - '${ServersProvider.instance.servers.length}', + trailing: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 6.0), + child: SquaredIconButton( + icon: Icon( + searchVisible ? Icons.search_off : Icons.search, + size: 20.0, + ), + tooltip: searchVisible + ? 'Disable search' + : MaterialLocalizations.of(context) + .searchFieldLabel, + onPressed: () { + setState(() => searchVisible = !searchVisible); + if (searchVisible) { + searchFocusNode.requestFocus(); + } + }, + ), + ), + Text( + '${ServersProvider.instance.servers.length}', + ), + ], ), ), + EventsSearchBar( + searchVisible: searchVisible, + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearchChanged: (query) { + super.setState(() => searchQuery = query); + }, + ), Expanded( child: SingleChildScrollView( child: EventsDevicesPicker( @@ -218,7 +256,7 @@ class EventsScreenState extends State { setState(() => disabledDevices.add(device)), onDisabledDeviceRemoved: (device) => setState(() => disabledDevices.remove(device)), - searchQuery: '', // TODO(bdlukaa): + searchQuery: searchQuery, ), ), ), diff --git a/lib/widgets/events_timeline/desktop/timeline_sidebar.dart b/lib/widgets/events_timeline/desktop/timeline_sidebar.dart index cd3a8738..5daf4bce 100644 --- a/lib/widgets/events_timeline/desktop/timeline_sidebar.dart +++ b/lib/widgets/events_timeline/desktop/timeline_sidebar.dart @@ -91,6 +91,9 @@ class _TimelineSidebarState extends State { searchVisible ? Icons.search_off : Icons.search, size: 22.0, ), + tooltip: searchVisible + ? 'Disable search' + : MaterialLocalizations.of(context).searchFieldLabel, onPressed: () { setState(() => searchVisible = !searchVisible); if (searchVisible) { @@ -103,38 +106,11 @@ class _TimelineSidebarState extends State { ), padding: const EdgeInsetsDirectional.only(start: 16.0, end: 4.0), ), - AnimatedSize( - duration: kThemeChangeDuration, - curve: Curves.easeInOut, - child: Builder(builder: (context) { - if (!searchVisible) return const SizedBox.shrink(); - return Column(children: [ - const Divider(height: 1.0), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextField( - controller: searchController, - focusNode: searchFocusNode, - decoration: const InputDecoration( - hintText: 'Search', - isDense: true, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - contentPadding: EdgeInsetsDirectional.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - ), - onChanged: (value) { - setState(() => searchQuery = value); - }, - ), - ), - const Divider(height: 1.0), - const SizedBox(height: 8.0), - ]); - }), + EventsSearchBar( + searchVisible: searchVisible, + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearchChanged: (query) => setState(() => searchQuery = query), ), Expanded( child: StatefulBuilder(builder: (context, setState) { @@ -184,3 +160,53 @@ class _TimelineSidebarState extends State { ); } } + +class EventsSearchBar extends StatelessWidget { + final bool searchVisible; + final TextEditingController searchController; + final FocusNode searchFocusNode; + final ValueChanged onSearchChanged; + + const EventsSearchBar({ + super.key, + required this.searchVisible, + required this.searchController, + required this.searchFocusNode, + required this.onSearchChanged, + }); + + @override + Widget build(BuildContext context) { + return AnimatedSize( + duration: kThemeChangeDuration, + curve: Curves.easeInOut, + child: Builder(builder: (context) { + if (!searchVisible) return const SizedBox.shrink(); + return Column(children: [ + const Divider(height: 1.0), + Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: searchController, + focusNode: searchFocusNode, + decoration: const InputDecoration( + hintText: 'Search', + isDense: true, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + contentPadding: EdgeInsetsDirectional.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + ), + onChanged: onSearchChanged, + ), + ), + const Divider(height: 1.0), + const SizedBox(height: 8.0), + ]); + }), + ); + } +}