Add a Settings option to reorder the selection action bar's actions
NooSelectionBar's fixed inline slots (3 mobile / 4 desktop, the rest behind "More") previously just took the first N actions in whatever order each screen happened to build them in. Give bulk actions a stable SelectionActionKind identity (independent of their dynamic icon/label, e.g. favorite vs. unfavorite) so Settings can offer a drag-to-reorder list - mirroring the existing Tabs section, minus the pin/hide concept, which doesn't apply here - and each screen applies that saved priority order via orderSelectionActions before handing its action list to the bar.
This commit is contained in:
@@ -11,6 +11,11 @@ class NooTabOrderRow extends StatelessWidget {
|
||||
final bool pinned;
|
||||
final VoidCallback? onTogglePin;
|
||||
|
||||
/// False hides the trailing pin toggle entirely - for reorder-only lists
|
||||
/// (Settings' Action bar section) that have no "pinned" concept, just an
|
||||
/// order.
|
||||
final bool showPin;
|
||||
|
||||
/// This row's index inside its `ReorderableListView` - when set, only the
|
||||
/// grip icon starts a drag (`ReorderableDragStartListener` wraps just the
|
||||
/// icon, not the whole row), so a vertical drag/scroll starting anywhere
|
||||
@@ -25,6 +30,7 @@ class NooTabOrderRow extends StatelessWidget {
|
||||
required this.label,
|
||||
this.pinned = false,
|
||||
this.onTogglePin,
|
||||
this.showPin = true,
|
||||
this.dragIndex,
|
||||
});
|
||||
|
||||
@@ -50,23 +56,24 @@ class NooTabOrderRow extends StatelessWidget {
|
||||
style: NooText.bodyL.copyWith(height: 1, color: colors.fg1),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: onTogglePin,
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: pinned ? colors.accentSoft : colors.surface2,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
pinned ? LucideIcons.pin : LucideIcons.pinOff,
|
||||
size: 16,
|
||||
color: pinned ? colors.accentText : colors.fg3,
|
||||
if (showPin)
|
||||
GestureDetector(
|
||||
onTap: onTogglePin,
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: pinned ? colors.accentSoft : colors.surface2,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
pinned ? LucideIcons.pin : LucideIcons.pinOff,
|
||||
size: 16,
|
||||
color: pinned ? colors.accentText : colors.fg3,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../models/selection_action.dart';
|
||||
import '../../providers/settings_controller.dart';
|
||||
import '../../theme/design_tokens.dart';
|
||||
import '../noo/lists/noo_tab_order_row.dart';
|
||||
import '../noo/noo_layout.dart';
|
||||
|
||||
/// Settings section: the priority order bulk actions (favorite, share,
|
||||
/// download, ...) show in on the Files/Photos/Favorites multi-select
|
||||
/// action bar. Every action stays available everywhere - this only decides
|
||||
/// which ones land in `NooSelectionBar`'s fixed inline slots (3 mobile / 4
|
||||
/// desktop, see its own doc comment) vs. behind its trailing "More" -
|
||||
/// there's no pin/hide concept here the way Tabs has, so this is just a
|
||||
/// reorder list, not a whole section of its own option rows.
|
||||
class SettingsActionBarSection extends StatelessWidget {
|
||||
const SettingsActionBarSection({super.key});
|
||||
|
||||
static const _subtitle =
|
||||
'Drag to reorder - the first ones shown appear directly in the bar, the rest sit behind "More"';
|
||||
|
||||
void _handleReorder(SettingsController settings, int oldIndex, int newIndex) {
|
||||
final order = List<SelectionActionKind>.from(settings.selectionActionOrder);
|
||||
final kind = order.removeAt(oldIndex);
|
||||
order.insert(newIndex, kind);
|
||||
settings.setSelectionActionOrder(order);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settings = context.watch<SettingsController>();
|
||||
final desktop = NooLayout.isDesktop(context);
|
||||
|
||||
final orderList = _ActionOrderList(
|
||||
order: settings.selectionActionOrder,
|
||||
onReorder: (oldIndex, newIndex) =>
|
||||
_handleReorder(settings, oldIndex, newIndex),
|
||||
);
|
||||
|
||||
final colors = context.nooColors;
|
||||
|
||||
if (!desktop) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
'Action bar',
|
||||
style: NooText.label.copyWith(color: colors.fg2),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
_subtitle,
|
||||
style: NooText.meta.copyWith(color: colors.fg3),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
orderList,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: colors.surface,
|
||||
border: Border.all(color: colors.line),
|
||||
borderRadius: BorderRadius.circular(NooRadii.card),
|
||||
),
|
||||
padding: const EdgeInsets.all(NooSpace.lg),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Action bar',
|
||||
style: NooText.cardTitle.copyWith(color: colors.fg1),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(_subtitle, style: NooText.meta.copyWith(color: colors.fg3)),
|
||||
const SizedBox(height: NooSpace.md),
|
||||
orderList,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The reorderable [NooTabOrderRow] stack for [SelectionActionKind]s - same
|
||||
/// radius-20, line-divided card `settings_tabs.dart`'s `_TabOrderList`
|
||||
/// draws, minus the pin toggle (`showPin: false`): there's nothing to
|
||||
/// show/hide here, only an order.
|
||||
class _ActionOrderList extends StatelessWidget {
|
||||
final List<SelectionActionKind> order;
|
||||
final void Function(int oldIndex, int newIndex) onReorder;
|
||||
|
||||
const _ActionOrderList({required this.order, required this.onReorder});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
final list = ReorderableListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
buildDefaultDragHandles: false,
|
||||
itemCount: order.length,
|
||||
onReorderItem: onReorder,
|
||||
itemBuilder: (context, index) {
|
||||
final kind = order[index];
|
||||
return Padding(
|
||||
key: ValueKey(kind),
|
||||
padding: EdgeInsets.only(top: index > 0 ? 1 : 0),
|
||||
child: NooTabOrderRow(
|
||||
icon: kind.icon,
|
||||
label: kind.label,
|
||||
showPin: false,
|
||||
dragIndex: index,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(NooRadii.card),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: colors.line),
|
||||
child: list,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user