From 586024980dcf6c8baaa4323dcf2acde0bc7c7533 Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Sun, 5 Jul 2026 08:59:27 +0200 Subject: [PATCH] add more tab type options for long press --- .../presentation/candidates/open_new_tab.dart | 123 +++++++++++++----- 1 file changed, 90 insertions(+), 33 deletions(-) diff --git a/apps/weblibre/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart b/apps/weblibre/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart index 84c4f126..a13f4a2e 100644 --- a/apps/weblibre/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart +++ b/apps/weblibre/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart @@ -22,6 +22,8 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/design/app_colors.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart'; @@ -38,44 +40,99 @@ class OpenInNewTab extends HookConsumerWidget { return hitResult.isHttpLink(); } + Future _openInNewTab( + BuildContext context, + WidgetRef ref, + TabMode tabMode, + ) async { + final currentTab = ref.read(selectedTabStateProvider); + + final tabId = await ref + .read(tabRepositoryProvider.notifier) + .addTab( + url: hitResult.tryGetLink(), + parentId: currentTab?.id, + selectTab: false, + tabMode: tabMode, + ); + + if (context.mounted) { + //save reference before pop `ref` gets disposed + final repo = ref.read(tabRepositoryProvider.notifier); + + showTabSwitchMessage( + context, + onSwitch: () async { + await repo.selectTab(tabId); + }, + ); + + context.pop(); + } + } + @override Widget build(BuildContext context, WidgetRef ref) { + final settings = ref.watch(generalSettingsWithDefaultsProvider); + final currentTab = ref.watch(selectedTabStateProvider); + + final currentTabMode = + currentTab?.tabMode ?? + TabMode.fromTabType(settings.effectiveDefaultCreateTabType); + + // Alternative tab types the user can explicitly choose, excluding the type + // that the main tile action already opens (the current tab's type). + final alternativeTypes = + [ + TabType.regular, + TabType.private, + if (settings.showIsolatedTabUi) TabType.isolated, + ]..remove(currentTabMode.toTabType()); + return ListTile( leading: const Icon(MdiIcons.tabPlus), title: const Text('Open in new tab'), - onTap: () async { - final currentTab = ref.read(selectedTabStateProvider); - final tabMode = - currentTab?.tabMode ?? - TabMode.fromTabType( - ref - .read(generalSettingsWithDefaultsProvider) - .effectiveDefaultCreateTabType, - ); - - final tabId = await ref - .read(tabRepositoryProvider.notifier) - .addTab( - url: hitResult.tryGetLink(), - parentId: currentTab?.id, - selectTab: false, - tabMode: tabMode, - ); - - if (context.mounted) { - //save reference before pop `ref` gets disposed - final repo = ref.read(tabRepositoryProvider.notifier); - - showTabSwitchMessage( - context, - onSwitch: () async { - await repo.selectTab(tabId); - }, - ); - - context.pop(); - } - }, + trailing: alternativeTypes.isEmpty + ? null + : MenuAnchor( + menuChildren: [ + for (final type in alternativeTypes) + MenuItemButton( + onPressed: () => + _openInNewTab(context, ref, TabMode.fromTabType(type)), + leadingIcon: Icon( + _iconFor(type), + color: _colorFor(context, type), + ), + child: Text(_labelFor(type)), + ), + ], + builder: (context, controller, child) => IconButton( + icon: const Icon(Icons.expand_more), + tooltip: 'Open in a different tab type', + onPressed: () => + controller.isOpen ? controller.close() : controller.open(), + ), + ), + onTap: () => _openInNewTab(context, ref, currentTabMode), ); } } + +IconData _iconFor(TabType type) => switch (type) { + TabType.private => MdiIcons.dominoMask, + TabType.isolated => MdiIcons.snowflake, + _ => MdiIcons.tab, +}; + +Color? _colorFor(BuildContext context, TabType type) => switch (type) { + TabType.private => AppColors.of(context).privateTabPurple, + TabType.isolated => AppColors.of(context).isolatedTabTeal, + _ => null, +}; + +String _labelFor(TabType type) => switch (type) { + TabType.private => 'New private tab', + TabType.isolated => 'New isolated tab', + _ => 'New regular tab', +};