From 1f3a81e24347954a36a90d92a9cd88302a665985 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sat, 18 Jun 2022 15:42:23 +0900 Subject: [PATCH] viewer: expand one submenu at once --- lib/widgets/collection/app_bar.dart | 1 + lib/widgets/common/basic/menu.dart | 75 ++++++++++--------- .../viewer/overlay/viewer_buttons.dart | 11 ++- 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 0bbcdb4a4..5d1624d68 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -284,6 +284,7 @@ class _CollectionAppBarState extends State with SingleTickerPr padding: EdgeInsets.zero, child: PopupMenuItemExpansionPanel( enabled: canApplyEditActions, + value: 'edit', icon: AIcons.edit, title: context.l10n.collectionActionEdit, items: [ diff --git a/lib/widgets/common/basic/menu.dart b/lib/widgets/common/basic/menu.dart index 695e2c73e..ff589e4e6 100644 --- a/lib/widgets/common/basic/menu.dart +++ b/lib/widgets/common/basic/menu.dart @@ -55,25 +55,27 @@ class MenuIconTheme extends StatelessWidget { class PopupMenuItemExpansionPanel extends StatefulWidget { final bool enabled; + final String value; + final ValueNotifier expandedNotifier; final IconData icon; final String title; final List> items; - const PopupMenuItemExpansionPanel({ + PopupMenuItemExpansionPanel({ super.key, this.enabled = true, + required this.value, + ValueNotifier? expandedNotifier, required this.icon, required this.title, required this.items, - }); + }) : expandedNotifier = expandedNotifier ?? ValueNotifier(null); @override State> createState() => _PopupMenuItemExpansionPanelState(); } class _PopupMenuItemExpansionPanelState extends State> { - bool _isExpanded = false; - // ref `_kMenuHorizontalPadding` used in `PopupMenuItem` static const double _horizontalPadding = 16; @@ -86,38 +88,43 @@ class _PopupMenuItemExpansionPanelState extends State((v) => v.expansionTileAnimation); - Widget child = ExpansionPanelList( - expansionCallback: (index, isExpanded) { - setState(() => _isExpanded = !isExpanded); - }, - animationDuration: animationDuration, - expandedHeaderPadding: EdgeInsets.zero, - elevation: 0, - children: [ - ExpansionPanel( - headerBuilder: (context, isExpanded) => DefaultTextStyle( - style: style, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding), - child: MenuRow( - text: widget.title, - icon: Icon(widget.icon), + Widget child = ValueListenableBuilder( + valueListenable: widget.expandedNotifier, + builder: (context, expandedValue, child) { + return ExpansionPanelList( + expansionCallback: (index, isExpanded) { + widget.expandedNotifier.value = isExpanded ? null : widget.value; + }, + animationDuration: animationDuration, + expandedHeaderPadding: EdgeInsets.zero, + elevation: 0, + children: [ + ExpansionPanel( + headerBuilder: (context, isExpanded) => DefaultTextStyle( + style: style, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding), + child: MenuRow( + text: widget.title, + icon: Icon(widget.icon), + ), + ), ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const PopupMenuDivider(height: 0), + ...widget.items, + const PopupMenuDivider(height: 0), + ], + ), + isExpanded: expandedValue == widget.value, + canTapOnHeader: true, + backgroundColor: Colors.transparent, ), - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const PopupMenuDivider(height: 0), - ...widget.items, - const PopupMenuDivider(height: 0), - ], - ), - isExpanded: _isExpanded, - canTapOnHeader: true, - backgroundColor: Colors.transparent, - ), - ], + ], + ); + }, ); if (!widget.enabled) { child = IgnorePointer(child: child); diff --git a/lib/widgets/viewer/overlay/viewer_buttons.dart b/lib/widgets/viewer/overlay/viewer_buttons.dart index 5c46953ae..47a8e6ce6 100644 --- a/lib/widgets/viewer/overlay/viewer_buttons.dart +++ b/lib/widgets/viewer/overlay/viewer_buttons.dart @@ -142,12 +142,13 @@ class ViewerButtonRowContent extends StatelessWidget { final List quickActions, topLevelActions, exportActions, videoActions; final Animation scale; final AvesEntry mainEntry, pageEntry; + final ValueNotifier _popupExpandedNotifier = ValueNotifier(null); AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry; static const double padding = 8; - const ViewerButtonRowContent({ + ViewerButtonRowContent({ super.key, required this.quickActions, required this.topLevelActions, @@ -188,6 +189,8 @@ class ViewerButtonRowContent extends StatelessWidget { PopupMenuItem( padding: EdgeInsets.zero, child: PopupMenuItemExpansionPanel( + value: 'export', + expandedNotifier: _popupExpandedNotifier, icon: AIcons.export, title: context.l10n.entryActionExport, items: [ @@ -201,6 +204,8 @@ class ViewerButtonRowContent extends StatelessWidget { PopupMenuItem( padding: EdgeInsets.zero, child: PopupMenuItemExpansionPanel( + value: 'video', + expandedNotifier: _popupExpandedNotifier, icon: AIcons.video, title: context.l10n.settingsSectionVideo, items: [ @@ -215,9 +220,13 @@ class ViewerButtonRowContent extends StatelessWidget { ]; }, onSelected: (action) { + _popupExpandedNotifier.value = null; // wait for the popup menu to hide before proceeding with the action Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action)); }, + onCanceled: () { + _popupExpandedNotifier.value = null; + }, onMenuOpened: () { // if the menu is opened while overlay is hiding, // the popup menu button is disposed and menu items are ineffective,