From 65d11cfbde08e3221b3c4a7722298c57a5888143 Mon Sep 17 00:00:00 2001 From: ThatNerdSquared <72814106+ThatNerdSquared@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:21:25 -0800 Subject: [PATCH] wip: cmd palette for searching/opening tags --- lib/main.dart | 10 ++- lib/widgets/desktop_frame.dart | 8 ++ lib/widgets/peregrine_app_bar.dart | 18 +++++ lib/widgets/peregrine_entry_box.dart | 15 +++- lib/widgets/pret_command_palette.dart | 109 ++++++++++++++++++++++++++ lib/widgets/sidebar.dart | 8 +- 6 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 lib/widgets/pret_command_palette.dart diff --git a/lib/main.dart b/lib/main.dart index 4c0c8c3..fceaef6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'model/entry_filter.dart'; import 'model/tag_data.dart'; import 'widgets/desktop_frame.dart'; import 'widgets/entry_list_view.dart'; +import 'widgets/pret_command_palette.dart'; import 'widgets/sidebar.dart'; const uuID = Uuid(); @@ -96,11 +97,11 @@ class MyApp extends ConsumerWidget { } } -class PeregrineHomeView extends StatelessWidget { +class PeregrineHomeView extends ConsumerWidget { const PeregrineHomeView({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final view = PretMainView( leftSidebar: Sidebar( searchBoxFocusNode: searchBoxFocusNode, @@ -121,7 +122,10 @@ class PeregrineHomeView extends StatelessWidget { body: DesktopFrame( entryBoxFocusNode: entryBoxFocusNode, searchBoxFocusNode: searchBoxFocusNode, - child: view, + child: PretCmdPaletteScope( + searchItems: ref.read(tagsProvider).keys.toList(), + child: view, + ), ), ); } diff --git a/lib/widgets/desktop_frame.dart b/lib/widgets/desktop_frame.dart index b710b44..b8aa8ae 100644 --- a/lib/widgets/desktop_frame.dart +++ b/lib/widgets/desktop_frame.dart @@ -37,6 +37,14 @@ class DesktopFrame extends ConsumerWidget { ), onSelected: entryBoxFocusNode.requestFocus, ), + const PlatformMenuItem( + label: 'Quick Open Tag', + shortcut: SingleActivator( + LogicalKeyboardKey.keyP, + meta: true, + ), + //onSelected: () => PretCmdPalette.of(context).togglePalette(), + ), ], ), ], diff --git a/lib/widgets/peregrine_app_bar.dart b/lib/widgets/peregrine_app_bar.dart index fdb9d5b..4639efe 100644 --- a/lib/widgets/peregrine_app_bar.dart +++ b/lib/widgets/peregrine_app_bar.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pret_a_porter/pret_a_porter.dart'; import '../main.dart'; +import 'pret_command_palette.dart'; class PeregrineAppBar extends ConsumerWidget { final FocusNode searchBoxFocusNode; @@ -45,6 +46,23 @@ class PeregrineAppBar extends ConsumerWidget { filter.name, style: Theme.of(context).textTheme.titleMedium, )), + IconButton.filled( + style: IconButton.styleFrom( + iconSize: 20, + minimumSize: const Size(36, 36), + maximumSize: const Size(36, 36), + backgroundColor: const Color(0xffdac6b0), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(PretConfig.thinBorderRounding), + )), + onPressed: () => PretPaletteToggle.of(context).togglePalette(), + icon: Icon( + Icons.tag, + color: Theme.of(context).colorScheme.surface, + ), + ), + const Padding( + padding: EdgeInsets.only(right: PretConfig.thinElementSpacing)), IconButton.filled( style: IconButton.styleFrom( iconSize: 20, diff --git a/lib/widgets/peregrine_entry_box.dart b/lib/widgets/peregrine_entry_box.dart index 187dacf..add3f0b 100644 --- a/lib/widgets/peregrine_entry_box.dart +++ b/lib/widgets/peregrine_entry_box.dart @@ -26,6 +26,14 @@ class PeregrineEntryBoxState extends ConsumerState { _controller.text = ''; } + void insertIndent() { + final cursor = _controller.selection.base.offset; + final beforeCursor = _controller.text.substring(0, cursor); + final afterCursor = _controller.text.substring(cursor); + // this should probably be abstracted into a setting + _controller.text = '$beforeCursor $afterCursor'; + } + @override Widget build(BuildContext context) { return ConstrainedBox( @@ -33,8 +41,11 @@ class PeregrineEntryBoxState extends ConsumerState { BoxConstraints(maxHeight: MediaQuery.of(context).size.height / 2), child: CallbackShortcuts( bindings: { - const SingleActivator(LogicalKeyboardKey.enter, meta: true): - submitNewLogEntry + const SingleActivator( + LogicalKeyboardKey.enter, + meta: true, + ): submitNewLogEntry, + const SingleActivator(LogicalKeyboardKey.tab): insertIndent }, child: Row(children: [ Expanded( diff --git a/lib/widgets/pret_command_palette.dart b/lib/widgets/pret_command_palette.dart new file mode 100644 index 0000000..a9a2b05 --- /dev/null +++ b/lib/widgets/pret_command_palette.dart @@ -0,0 +1,109 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:pret_a_porter/pret_a_porter.dart'; + +import '../main.dart'; + +class PretCmdPaletteScope extends ConsumerStatefulWidget { + final Widget child; + final List searchItems; + + const PretCmdPaletteScope({ + super.key, + required this.child, + required this.searchItems, + }); + + @override + PretCmdPaletteScopeState createState() => PretCmdPaletteScopeState(); +} + +class PretCmdPaletteScopeState extends ConsumerState { + bool _isPaletteShown = false; + + void togglePalette() => setState(() { + _isPaletteShown = !_isPaletteShown; + }); + + @override + Widget build(BuildContext context) => PretPaletteToggle( + isPaletteShown: _isPaletteShown, + togglePalette: togglePalette, + child: !_isPaletteShown + ? widget.child + : LayoutBuilder( + builder: (context, constraints) => Container( + alignment: Alignment.topCenter, + child: Stack( + children: [ + widget.child, + CallbackShortcuts( + bindings: { + const SingleActivator( + LogicalKeyboardKey.escape, + ): togglePalette + }, + child: Container( + constraints: BoxConstraints( + maxHeight: min(0.8 * constraints.maxHeight, 600), + maxWidth: min(0.8 * constraints.maxWidth, 900), + ), + child: PretCard( + child: Column( + children: [ + TextFormField( + autofocus: true, + ), + Expanded( + child: ListView.builder( + itemCount: widget.searchItems.length, + itemBuilder: (context, index) => ListTile( + title: Text(widget.searchItems[index]), + onTap: () { + togglePalette(); + ref + .read(entryFilterProvider.notifier) + .setTagFilter( + widget.searchItems[index], + ); + }, + ), + ), + ), + ], + ), + ), + ), + ) + ], + ), + ), + ), + ); +} + +class PretPaletteToggle extends InheritedWidget { + final bool isPaletteShown; + final VoidCallback togglePalette; + + const PretPaletteToggle({ + super.key, + required this.isPaletteShown, + required this.togglePalette, + required Widget child, + }) : super(child: child); + + static PretPaletteToggle of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()!; + } + + @override + bool updateShouldNotify(covariant InheritedWidget oldWidget) { + return false; + // TODO: implement updateShouldNotify + // throw UnimplementedError(); + } +} diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index 5206edf..6018f03 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -69,8 +69,8 @@ class Sidebar extends ConsumerWidget { SliverToBoxAdapter( child: SidebarToggleList( toggleTitle: 'Tags', - items: tags.keys.map((tagName) { - return Container( + items: tags.keys.map( + (tagName) => Container( padding: const EdgeInsets.only( left: PretConfig.defaultElementSpacing, right: PretConfig.defaultElementSpacing), @@ -105,8 +105,8 @@ class Sidebar extends ConsumerWidget { count: tags[tagName]!.count, icon: Icons.tag, ), - )); - }), + )), + ), )), ]); }