viewer: expand one submenu at once

This commit is contained in:
Thibault Deckers 2022-06-18 15:42:23 +09:00
parent 9bced31927
commit 1f3a81e243
3 changed files with 52 additions and 35 deletions

View file

@ -284,6 +284,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: PopupMenuItemExpansionPanel<EntrySetAction>( child: PopupMenuItemExpansionPanel<EntrySetAction>(
enabled: canApplyEditActions, enabled: canApplyEditActions,
value: 'edit',
icon: AIcons.edit, icon: AIcons.edit,
title: context.l10n.collectionActionEdit, title: context.l10n.collectionActionEdit,
items: [ items: [

View file

@ -55,25 +55,27 @@ class MenuIconTheme extends StatelessWidget {
class PopupMenuItemExpansionPanel<T> extends StatefulWidget { class PopupMenuItemExpansionPanel<T> extends StatefulWidget {
final bool enabled; final bool enabled;
final String value;
final ValueNotifier<String?> expandedNotifier;
final IconData icon; final IconData icon;
final String title; final String title;
final List<PopupMenuEntry<T>> items; final List<PopupMenuEntry<T>> items;
const PopupMenuItemExpansionPanel({ PopupMenuItemExpansionPanel({
super.key, super.key,
this.enabled = true, this.enabled = true,
required this.value,
ValueNotifier<String?>? expandedNotifier,
required this.icon, required this.icon,
required this.title, required this.title,
required this.items, required this.items,
}); }) : expandedNotifier = expandedNotifier ?? ValueNotifier(null);
@override @override
State<PopupMenuItemExpansionPanel<T>> createState() => _PopupMenuItemExpansionPanelState<T>(); State<PopupMenuItemExpansionPanel<T>> createState() => _PopupMenuItemExpansionPanelState<T>();
} }
class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionPanel<T>> { class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionPanel<T>> {
bool _isExpanded = false;
// ref `_kMenuHorizontalPadding` used in `PopupMenuItem` // ref `_kMenuHorizontalPadding` used in `PopupMenuItem`
static const double _horizontalPadding = 16; static const double _horizontalPadding = 16;
@ -86,38 +88,43 @@ class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionP
} }
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation); final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);
Widget child = ExpansionPanelList( Widget child = ValueListenableBuilder<String?>(
expansionCallback: (index, isExpanded) { valueListenable: widget.expandedNotifier,
setState(() => _isExpanded = !isExpanded); builder: (context, expandedValue, child) {
}, return ExpansionPanelList(
animationDuration: animationDuration, expansionCallback: (index, isExpanded) {
expandedHeaderPadding: EdgeInsets.zero, widget.expandedNotifier.value = isExpanded ? null : widget.value;
elevation: 0, },
children: [ animationDuration: animationDuration,
ExpansionPanel( expandedHeaderPadding: EdgeInsets.zero,
headerBuilder: (context, isExpanded) => DefaultTextStyle( elevation: 0,
style: style, children: [
child: Padding( ExpansionPanel(
padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding), headerBuilder: (context, isExpanded) => DefaultTextStyle(
child: MenuRow( style: style,
text: widget.title, child: Padding(
icon: Icon(widget.icon), 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) { if (!widget.enabled) {
child = IgnorePointer(child: child); child = IgnorePointer(child: child);

View file

@ -142,12 +142,13 @@ class ViewerButtonRowContent extends StatelessWidget {
final List<EntryAction> quickActions, topLevelActions, exportActions, videoActions; final List<EntryAction> quickActions, topLevelActions, exportActions, videoActions;
final Animation<double> scale; final Animation<double> scale;
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
final ValueNotifier<String?> _popupExpandedNotifier = ValueNotifier(null);
AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry; AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry;
static const double padding = 8; static const double padding = 8;
const ViewerButtonRowContent({ ViewerButtonRowContent({
super.key, super.key,
required this.quickActions, required this.quickActions,
required this.topLevelActions, required this.topLevelActions,
@ -188,6 +189,8 @@ class ViewerButtonRowContent extends StatelessWidget {
PopupMenuItem<EntryAction>( PopupMenuItem<EntryAction>(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: PopupMenuItemExpansionPanel<EntryAction>( child: PopupMenuItemExpansionPanel<EntryAction>(
value: 'export',
expandedNotifier: _popupExpandedNotifier,
icon: AIcons.export, icon: AIcons.export,
title: context.l10n.entryActionExport, title: context.l10n.entryActionExport,
items: [ items: [
@ -201,6 +204,8 @@ class ViewerButtonRowContent extends StatelessWidget {
PopupMenuItem<EntryAction>( PopupMenuItem<EntryAction>(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: PopupMenuItemExpansionPanel<EntryAction>( child: PopupMenuItemExpansionPanel<EntryAction>(
value: 'video',
expandedNotifier: _popupExpandedNotifier,
icon: AIcons.video, icon: AIcons.video,
title: context.l10n.settingsSectionVideo, title: context.l10n.settingsSectionVideo,
items: [ items: [
@ -215,9 +220,13 @@ class ViewerButtonRowContent extends StatelessWidget {
]; ];
}, },
onSelected: (action) { onSelected: (action) {
_popupExpandedNotifier.value = null;
// wait for the popup menu to hide before proceeding with the action // wait for the popup menu to hide before proceeding with the action
Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action)); Future.delayed(Durations.popupMenuAnimation * timeDilation, () => _onActionSelected(context, action));
}, },
onCanceled: () {
_popupExpandedNotifier.value = null;
},
onMenuOpened: () { onMenuOpened: () {
// if the menu is opened while overlay is hiding, // if the menu is opened while overlay is hiding,
// the popup menu button is disposed and menu items are ineffective, // the popup menu button is disposed and menu items are ineffective,