show tabmenu for quick tabs

This commit is contained in:
Fabian Freund
2025-12-29 09:56:11 +01:00
parent ee50ba9599
commit 03b73edd66
2 changed files with 363 additions and 290 deletions
@@ -331,12 +331,13 @@ class BrowserTabBar extends HookConsumerWidget {
TabMenu( TabMenu(
controller: trippleDotMenuController, controller: trippleDotMenuController,
selectedTabId: selectedTabId, selectedTabId: selectedTabId,
child: InkWell( builder: (context, controller, child) {
return InkWell(
onTap: () { onTap: () {
if (trippleDotMenuController.isOpen) { if (controller.isOpen) {
trippleDotMenuController.close(); controller.close();
} else { } else {
trippleDotMenuController.open(); controller.open();
} }
}, },
child: const Padding( child: const Padding(
@@ -346,7 +347,8 @@ class BrowserTabBar extends HookConsumerWidget {
), ),
child: Icon(MdiIcons.dotsVertical), child: Icon(MdiIcons.dotsVertical),
), ),
), );
},
), ),
if (showMainToolbarTabsCount) if (showMainToolbarTabsCount)
TabsCountButton( TabsCountButton(
@@ -500,6 +502,27 @@ class QuickTabSwitcher extends HookConsumerWidget {
} }
await animation; await animation;
}, },
itemWrap: (child, item) {
return TabMenu(
selectedTabId: item.id,
enableFindInPage: false,
enableFetchFeeds: false,
enableDesktopMode: false,
enableReaderMode: false,
builder: (context, controller, _) {
return InkWell(
onLongPress: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: child,
);
},
);
},
availableItems: tabStates.value availableItems: tabStates.value
.map( .map(
(state) => ( (state) => (
@@ -40,34 +40,56 @@ import 'package:weblibre/features/geckoview/features/tabs/data/models/container_
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
import 'package:weblibre/presentation/hooks/menu_controller.dart';
import 'package:weblibre/presentation/widgets/website_feed_menu_button.dart'; import 'package:weblibre/presentation/widgets/website_feed_menu_button.dart';
import 'package:weblibre/utils/ui_helper.dart' as ui_helper; import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
class TabMenu extends HookConsumerWidget { class TabMenu extends HookConsumerWidget {
final Widget child; final MenuAnchorChildBuilder builder;
final MenuController controller; final MenuController? controller;
final String selectedTabId; final String selectedTabId;
final bool enableFindInPage;
final bool enableReaderMode;
final bool enableDesktopMode;
final bool enableFetchFeeds;
final bool enableAddBookmark;
final bool enableCloneTab;
final bool enableContainer;
final bool enableShare;
final bool enableExport;
final bool enableCloseTab;
const TabMenu({ const TabMenu({
super.key, super.key,
required this.child, required this.builder,
required this.controller,
required this.selectedTabId, required this.selectedTabId,
this.controller,
this.enableFindInPage = true,
this.enableReaderMode = true,
this.enableDesktopMode = true,
this.enableFetchFeeds = true,
this.enableAddBookmark = true,
this.enableCloneTab = true,
this.enableContainer = true,
this.enableShare = true,
this.enableExport = true,
this.enableCloseTab = true,
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final showFeeds = useState(false); final showFeeds = useState(false);
final controller = this.controller ?? useMenuController();
return MenuAnchor( return MenuAnchor(
controller: controller, controller: controller,
onClose: () { onClose: () {
showFeeds.value = false; showFeeds.value = false;
}, },
builder: (context, controller, child) { builder: builder,
return child!;
},
menuChildren: [ menuChildren: [
if (enableFindInPage)
MenuItemButton( MenuItemButton(
onPressed: () { onPressed: () {
ref.read(bottomSheetControllerProvider.notifier).requestDismiss(); ref.read(bottomSheetControllerProvider.notifier).requestDismiss();
@@ -79,6 +101,7 @@ class TabMenu extends HookConsumerWidget {
leadingIcon: const Icon(Icons.search), leadingIcon: const Icon(Icons.search),
child: const Text('Find in page'), child: const Text('Find in page'),
), ),
if (enableReaderMode)
ReaderButton( ReaderButton(
buttonBuilder: (isLoading, readerActive, icon) => MenuItemButton( buttonBuilder: (isLoading, readerActive, icon) => MenuItemButton(
onPressed: isLoading onPressed: isLoading
@@ -103,13 +126,18 @@ class TabMenu extends HookConsumerWidget {
child: const Text('Reader Mode'), child: const Text('Reader Mode'),
), ),
), ),
if (enableDesktopMode)
Consumer( Consumer(
builder: (context, childRef, child) { builder: (context, childRef, child) {
final enabled = childRef.watch(desktopModeProvider(selectedTabId)); final enabled = childRef.watch(
desktopModeProvider(selectedTabId),
);
return MenuItemButton( return MenuItemButton(
onPressed: () { onPressed: () {
ref.read(desktopModeProvider(selectedTabId).notifier).toggle(); ref
.read(desktopModeProvider(selectedTabId).notifier)
.toggle();
}, },
leadingIcon: const Icon(MdiIcons.monitor), leadingIcon: const Icon(MdiIcons.monitor),
trailingIcon: Checkbox( trailingIcon: Checkbox(
@@ -127,7 +155,9 @@ class TabMenu extends HookConsumerWidget {
); );
}, },
), ),
if (enableFindInPage || enableReaderMode || enableDesktopMode)
const Divider(), const Divider(),
if (enableFetchFeeds)
Visibility( Visibility(
visible: showFeeds.value, visible: showFeeds.value,
replacement: MenuItemButton( replacement: MenuItemButton(
@@ -140,6 +170,7 @@ class TabMenu extends HookConsumerWidget {
), ),
child: WebsiteFeedMenuButton(selectedTabId), child: WebsiteFeedMenuButton(selectedTabId),
), ),
if (enableAddBookmark)
MenuItemButton( MenuItemButton(
leadingIcon: const Icon(MdiIcons.bookmarkPlus), leadingIcon: const Icon(MdiIcons.bookmarkPlus),
child: const Text('Add Bookmark'), child: const Text('Add Bookmark'),
@@ -156,6 +187,7 @@ class TabMenu extends HookConsumerWidget {
).push(context); ).push(context);
}, },
), ),
if (enableCloneTab)
SubmenuButton( SubmenuButton(
menuChildren: [ menuChildren: [
MenuItemButton( MenuItemButton(
@@ -193,7 +225,11 @@ class TabMenu extends HookConsumerWidget {
final tabId = await ref final tabId = await ref
.read(tabRepositoryProvider.notifier) .read(tabRepositoryProvider.notifier)
.addTab(url: tabState.url, private: true, selectTab: false); .addTab(
url: tabState.url,
private: true,
selectTab: false,
);
if (context.mounted) { if (context.mounted) {
//save reference before pop `ref` gets disposed //save reference before pop `ref` gets disposed
@@ -212,14 +248,17 @@ class TabMenu extends HookConsumerWidget {
leadingIcon: const Icon(MdiIcons.tabPlus), leadingIcon: const Icon(MdiIcons.tabPlus),
child: const Text('Clone Tab'), child: const Text('Clone Tab'),
), ),
if (enableContainer)
SubmenuButton( SubmenuButton(
menuChildren: [ menuChildren: [
MenuItemButton( MenuItemButton(
leadingIcon: const Icon(MdiIcons.folderArrowUpDownOutline), leadingIcon: const Icon(MdiIcons.folderArrowUpDownOutline),
child: const Text('Assign Container'), child: const Text('Assign Container'),
onPressed: () async { onPressed: () async {
final targetContainerId = await const ContainerSelectionRoute() final targetContainerId =
.push<String?>(context); await const ContainerSelectionRoute().push<String?>(
context,
);
if (targetContainerId != null) { if (targetContainerId != null) {
final containerData = await ref final containerData = await ref
@@ -227,7 +266,9 @@ class TabMenu extends HookConsumerWidget {
.getContainerData(targetContainerId); .getContainerData(targetContainerId);
if (containerData != null) { if (containerData != null) {
final tabState = ref.read(tabStateProvider(selectedTabId))!; final tabState = ref.read(
tabStateProvider(selectedTabId),
)!;
await ref await ref
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
@@ -255,17 +296,21 @@ class TabMenu extends HookConsumerWidget {
final tabState = ref.read( final tabState = ref.read(
tabStateProvider(selectedTabId), tabStateProvider(selectedTabId),
); );
final origin = tabState?.url.origin.mapNotNull(Uri.parse); final origin = tabState?.url.origin.mapNotNull(
Uri.parse,
);
if (origin != null) { if (origin != null) {
await ref await ref
.read(containerRepositoryProvider.notifier) .read(containerRepositoryProvider.notifier)
.replaceContainer( .replaceContainer(
containerData.copyWith.metadata( containerData.copyWith.metadata(
containerData.metadata.copyWith.assignedSites([ containerData.metadata.copyWith.assignedSites(
[
...?containerData.metadata.assignedSites, ...?containerData.metadata.assignedSites,
origin, origin,
]), ],
),
), ),
); );
} }
@@ -304,13 +349,17 @@ class TabMenu extends HookConsumerWidget {
).select((value) => value.value), ).select((value) => value.value),
); );
return Visibility(visible: containerId != null, child: child!); return Visibility(
visible: containerId != null,
child: child!,
);
}, },
), ),
], ],
leadingIcon: const Icon(MdiIcons.folder), leadingIcon: const Icon(MdiIcons.folder),
child: const Text('Container'), child: const Text('Container'),
), ),
if (enableShare)
SubmenuButton( SubmenuButton(
menuChildren: [ menuChildren: [
CopyAddressMenuItemButton(selectedTabId: selectedTabId), CopyAddressMenuItemButton(selectedTabId: selectedTabId),
@@ -322,6 +371,7 @@ class TabMenu extends HookConsumerWidget {
leadingIcon: const Icon(Icons.share), leadingIcon: const Icon(Icons.share),
child: const Text('Share'), child: const Text('Share'),
), ),
if (enableExport)
SubmenuButton( SubmenuButton(
menuChildren: [ menuChildren: [
ShareMarkdownActionMenuItemButton( ShareMarkdownActionMenuItemButton(
@@ -359,6 +409,7 @@ class TabMenu extends HookConsumerWidget {
leadingIcon: const Icon(MdiIcons.fileExport), leadingIcon: const Icon(MdiIcons.fileExport),
child: const Text('Export'), child: const Text('Export'),
), ),
if (enableCloseTab)
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
await ref await ref
@@ -376,7 +427,6 @@ class TabMenu extends HookConsumerWidget {
child: const Text('Close Tab'), child: const Text('Close Tab'),
), ),
], ],
child: child,
); );
} }
} }