setting to hide container and isolated tab ui

This commit is contained in:
Fabian Freund
2026-02-27 18:23:33 +01:00
parent 60838b446d
commit 3d1293b352
36 changed files with 611 additions and 292 deletions
@@ -216,9 +216,14 @@ EquatableValue<List<TabStateWirthContainer>> quickTabSwitcherTabStates(
Ref ref,
QuickTabSwitcherMode mode,
) {
final effectiveMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.effectiveUiQuickTabSwitcherMode(),
),
);
final selectedTabId = ref.watch(selectedTabProvider);
final tabStates = switch (mode) {
final tabStates = switch (effectiveMode) {
QuickTabSwitcherMode.lastUsedTabs => ref.watch(fifoTabStatesProvider).value,
QuickTabSwitcherMode.containerTabs =>
ref.watch(selectedContainerTabStatesWithContainerProvider).value,
@@ -234,6 +239,11 @@ Future<List<VisitInfo>> quickTabSwitcherHistorySuggestions(
Ref ref,
QuickTabSwitcherMode mode,
) async {
final effectiveMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.effectiveUiQuickTabSwitcherMode(),
),
);
final showHistorySuggestions = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.quickTabSwitcherShowHistorySuggestions,
@@ -246,7 +256,7 @@ Future<List<VisitInfo>> quickTabSwitcherHistorySuggestions(
final hasTabStates = ref.watch(
quickTabSwitcherTabStatesProvider(
mode,
effectiveMode,
).select((value) => value.value.isNotEmpty),
);
if (hasTabStates) {
@@ -263,6 +273,11 @@ AsyncValue<bool> quickTabSwitcherHasResults(
Ref ref,
QuickTabSwitcherMode mode,
) {
final effectiveMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.effectiveUiQuickTabSwitcherMode(),
),
);
final showQuickTabSwitcherBar = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.tabBarShowQuickTabSwitcherBar,
@@ -274,7 +289,7 @@ AsyncValue<bool> quickTabSwitcherHasResults(
final hasResults = ref.watch(
quickTabSwitcherTabStatesProvider(
mode,
effectiveMode,
).select((value) => value.value.isNotEmpty),
);
@@ -283,7 +298,7 @@ AsyncValue<bool> quickTabSwitcherHasResults(
}
return ref
.watch(quickTabSwitcherHistorySuggestionsProvider(mode))
.watch(quickTabSwitcherHistorySuggestionsProvider(effectiveMode))
.whenData((visits) => visits.isNotEmpty);
}
@@ -559,7 +559,7 @@ final class QuickTabSwitcherTabStatesProvider
}
String _$quickTabSwitcherTabStatesHash() =>
r'db82bf9e338c94768b0531aeaffb152ff726f81f';
r'df990fb96cca62a272572c925028725f81a0e96b';
final class QuickTabSwitcherTabStatesFamily extends $Family
with
@@ -642,7 +642,7 @@ final class QuickTabSwitcherHistorySuggestionsProvider
}
String _$quickTabSwitcherHistorySuggestionsHash() =>
r'3446741dfedf6fda9ab61421c1b74370c7f43f5b';
r'2e689ce72aac6b5b5b41e0390b168b3fdf5d4251';
final class QuickTabSwitcherHistorySuggestionsFamily extends $Family
with
@@ -730,7 +730,7 @@ final class QuickTabSwitcherHasResultsProvider
}
String _$quickTabSwitcherHasResultsHash() =>
r'bcbddfefe7f5eb2c5d8c7c1925fe593a857b477b';
r'5c488c9933372350c9e87ca1b83145176823dc88';
final class QuickTabSwitcherHasResultsFamily extends $Family
with $FunctionalFamilyOverride<AsyncValue<bool>, QuickTabSwitcherMode> {
@@ -277,7 +277,7 @@ class BrowserScreen extends HookConsumerWidget {
);
final quickTabSwitcherMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.quickTabSwitcherMode,
(value) => value.effectiveUiQuickTabSwitcherMode(),
),
);
final quickTabSwitcherHasResults = ref.watch(
@@ -103,7 +103,7 @@ class TabViewScreen extends HookConsumerWidget {
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
).push(context);
if (context.mounted) {
@@ -194,6 +194,7 @@ class BrowserTabBar extends HookConsumerWidget {
final trippleDotMenuController = useMenuController();
final selectedTabId = ref.watch(selectedTabProvider);
final settings = ref.watch(generalSettingsWithDefaultsProvider);
final containerColor = ref.watch(
watchTabContainerDataProvider(
@@ -201,23 +202,10 @@ class BrowserTabBar extends HookConsumerWidget {
).select((data) => data.value?.color),
);
final showExtensionShortcut = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.showExtensionShortcut,
),
);
final showExtensionShortcut = settings.showExtensionShortcut;
final quickTabSwitcherMode = settings.effectiveUiQuickTabSwitcherMode();
final quickTabSwitcherMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.quickTabSwitcherMode,
),
);
final tabBarPosition = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.tabBarPosition,
),
);
final tabBarPosition = settings.tabBarPosition;
final dragStartPosition = useRef(Offset.zero);
@@ -300,7 +288,9 @@ class BrowserTabBar extends HookConsumerWidget {
leadingWidth: 40.0,
toolbarHeight: kToolbarHeight,
backgroundColor:
(containerColor != null && displayedSheet is! ViewTabsSheet)
(settings.showContainerUi &&
containerColor != null &&
displayedSheet is! ViewTabsSheet)
? ContainerColors.forAppBar(containerColor)
: null,
leading: showMainToolbarNavigationButton
@@ -485,6 +475,9 @@ class QuickTabSwitcher extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final appColors = AppColors.of(context);
final showIsolatedTabUi = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.showIsolatedTabUi),
);
final showTitles = ref.watch(
generalSettingsWithDefaultsProvider.select(
(s) => s.quickTabSwitcherShowTitles,
@@ -556,7 +549,7 @@ class QuickTabSwitcher extends HookConsumerWidget {
constraints: const BoxConstraints(maxWidth: 64),
child: Text(item.title),
),
if (item.tabMode is IsolatedTabMode)
if (showIsolatedTabUi && item.tabMode is IsolatedTabMode)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Icon(
@@ -710,7 +703,7 @@ class AddTabButton extends HookConsumerWidget {
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
).push(context);
if (context.mounted) {
@@ -240,7 +240,7 @@ class _BrowserViewState extends ConsumerState<BrowserView>
final route = SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
searchText: sharedContent.text,
launchedFromIntent: true, //launched from intent
);
@@ -334,6 +334,13 @@ class _BrowserViewState extends ConsumerState<BrowserView>
await router.push(route.location);
} else if (type == 'new_isolated_tab') {
final settings = ref.read(
generalSettingsWithDefaultsProvider,
);
if (!settings.showIsolatedTabUi) {
return;
}
lastAction = DateTime.now();
final router = await ref.read(routerProvider.future);
@@ -348,6 +355,9 @@ class _BrowserViewState extends ConsumerState<BrowserView>
}
});
final settings = ref.read(
generalSettingsWithDefaultsProvider,
);
await quickActions.setShortcutItems([
//TODO: add icons
const ShortcutItem(
@@ -358,10 +368,11 @@ class _BrowserViewState extends ConsumerState<BrowserView>
type: 'new_private_tab',
localizedTitle: 'New Private Tab',
),
const ShortcutItem(
type: 'new_isolated_tab',
localizedTitle: 'New Isolated Tab',
),
if (settings.showIsolatedTabUi)
const ShortcutItem(
type: 'new_isolated_tab',
localizedTitle: 'New Isolated Tab',
),
]);
initializationCompleter.complete();
@@ -592,7 +603,7 @@ class _BrowserViewState extends ConsumerState<BrowserView>
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
searchText: searchText ?? SearchRoute.emptySearchText,
).push(context);
},
@@ -52,6 +52,9 @@ class BrowserNavigationDrawer extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final colorScheme = Theme.of(context).colorScheme;
final showContainerUi = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.showContainerUi),
);
return NavigationDrawer(
backgroundColor: colorScheme.surface,
@@ -137,14 +140,15 @@ class BrowserNavigationDrawer extends HookConsumerWidget {
},
),
ListTile(
leading: const Icon(MdiIcons.folder),
title: const Text('Containers'),
onTap: () async {
Navigator.of(context).pop();
await const ContainerListRoute().push(context);
},
),
if (showContainerUi)
ListTile(
leading: const Icon(MdiIcons.folder),
title: const Text('Containers'),
onTap: () async {
Navigator.of(context).pop();
await const ContainerListRoute().push(context);
},
),
ListTile(
leading: const Icon(Icons.rss_feed),
@@ -322,7 +326,7 @@ class _ExtensionsSection extends HookConsumerWidget {
final tabMode = TabMode.fromTabType(
ref
.read(generalSettingsWithDefaultsProvider)
.defaultCreateTabType,
.effectiveDefaultCreateTabType,
);
await ref
@@ -42,6 +42,11 @@ class TabCreationMenu extends HookConsumerWidget {
(value) => value.createChildTabsOption,
),
);
final showIsolatedTabUi = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.showIsolatedTabUi,
),
);
return MenuAnchor(
controller: controller,
@@ -63,16 +68,17 @@ class TabCreationMenu extends HookConsumerWidget {
leadingIcon: const Icon(WebLibreIcons.privateTab),
child: const Text('Add Private Tab'),
),
MenuItemButton(
onPressed: () async {
await const SearchRoute(tabType: TabType.isolated).push(context);
},
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
if (showIsolatedTabUi)
MenuItemButton(
onPressed: () async {
await const SearchRoute(tabType: TabType.isolated).push(context);
},
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
),
child: const Text('Add Isolated Tab'),
),
child: const Text('Add Isolated Tab'),
),
if (createChildTabsOption)
MenuItemButton(
onPressed: () async {
@@ -48,6 +48,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/entities/contai
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/tab.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/hooks/menu_controller.dart';
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
import 'package:weblibre/presentation/widgets/website_feed_menu_button.dart';
@@ -94,6 +95,7 @@ class TabMenu extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final showFeeds = useState(false);
final settings = ref.watch(generalSettingsWithDefaultsProvider);
final controller = this.controller ?? useMenuController();
@@ -313,46 +315,47 @@ class TabMenu extends HookConsumerWidget {
}
},
),
MenuItemButton(
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
),
child: const Text('Isolated'),
onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId))!;
final containerData = await ref
.read(tabDataRepositoryProvider.notifier)
.getTabContainerData(selectedTabId);
if (settings.showIsolatedTabUi)
MenuItemButton(
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
),
child: const Text('Isolated'),
onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId))!;
final containerData = await ref
.read(tabDataRepositoryProvider.notifier)
.getTabContainerData(selectedTabId);
final tabId = await ref
.read(tabRepositoryProvider.notifier)
.addTab(
url: tabState.url,
tabMode: TabMode.newIsolated(),
containerSelection: containerData == null
? const TabContainerSelection.unassigned()
: TabContainerSelection.specific(containerData),
selectTab: false,
final tabId = await ref
.read(tabRepositoryProvider.notifier)
.addTab(
url: tabState.url,
tabMode: TabMode.newIsolated(),
containerSelection: containerData == null
? const TabContainerSelection.unassigned()
: TabContainerSelection.specific(containerData),
selectTab: false,
);
if (context.mounted) {
final repo = ref.read(tabRepositoryProvider.notifier);
ui_helper.showTabSwitchMessage(
context,
onSwitch: () async {
await repo.selectTab(tabId);
},
);
if (context.mounted) {
final repo = ref.read(tabRepositoryProvider.notifier);
ui_helper.showTabSwitchMessage(
context,
onSwitch: () async {
await repo.selectTab(tabId);
},
);
}
},
),
}
},
),
],
leadingIcon: const Icon(MdiIcons.tabPlus),
child: const Text('Clone Tab'),
),
if (enableContainer)
if (enableContainer && settings.showContainerUi)
SubmenuButton(
menuChildren: [
MenuItemButton(
@@ -577,7 +577,7 @@ class ViewTabGridWidget extends HookConsumerWidget {
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
).push(context);
onClose();
@@ -606,7 +606,7 @@ class ViewTabListWidget extends HookConsumerWidget {
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
).push(context);
onClose();
@@ -345,7 +345,7 @@ class ViewTabTreesWidget extends HookConsumerWidget {
await SearchRoute(
tabType:
ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType,
settings.effectiveDefaultCreateTabType,
).push(context);
onClose();
@@ -230,6 +230,16 @@ class TabViewHeader extends HookConsumerWidget {
(settings) => settings.enableLocalAiFeatures,
),
);
final showContainerUi = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.showContainerUi,
),
);
final showIsolatedTabUi = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.showIsolatedTabUi,
),
);
final selectedContainerId = ref.watch(selectedContainerProvider);
final isSyncedScope = ref.watch(
@@ -503,55 +513,63 @@ class TabViewHeader extends HookConsumerWidget {
},
child: const Text('Close Private Tabs'),
),
MenuItemButton(
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
),
onPressed: isSyncedScope
? null
: () async {
// Count distinct isolation groups that will be destroyed
final allStates = ref.read(tabStatesProvider);
final isolatedContextIds = allStates.values
.where(
(s) =>
s.tabMode is IsolatedTabMode &&
s.isolationContextId != null,
)
.map((s) => s.isolationContextId!)
.toSet();
if (isolatedContextIds.isNotEmpty &&
context.mounted) {
final confirmed = await ui_helper
.confirmIsolatedTabClose(
context,
groupCount: isolatedContextIds.length,
);
if (!confirmed) return;
}
final count = await ref
.read(tabDataRepositoryProvider.notifier)
.closeContainerTabs(
selectedContainerId,
includeRegular: false,
includePrivate: false,
);
if (context.mounted) {
ui_helper.showTabUndoClose(
context,
ref
.read(tabRepositoryProvider.notifier)
.undoClose,
count: count.length,
if (showIsolatedTabUi)
MenuItemButton(
leadingIcon: Icon(
MdiIcons.shieldLock,
color: AppColors.of(context).isolatedTabTeal,
),
onPressed: isSyncedScope
? null
: () async {
// Count distinct isolation groups that will be destroyed
final allStates = ref.read(
tabStatesProvider,
);
}
},
child: const Text('Close Isolated Tabs'),
),
final isolatedContextIds = allStates.values
.where(
(s) =>
s.tabMode is IsolatedTabMode &&
s.isolationContextId != null,
)
.map((s) => s.isolationContextId!)
.toSet();
if (isolatedContextIds.isNotEmpty &&
context.mounted) {
final confirmed = await ui_helper
.confirmIsolatedTabClose(
context,
groupCount:
isolatedContextIds.length,
);
if (!confirmed) return;
}
final count = await ref
.read(
tabDataRepositoryProvider.notifier,
)
.closeContainerTabs(
selectedContainerId,
includeRegular: false,
includePrivate: false,
);
if (context.mounted) {
ui_helper.showTabUndoClose(
context,
ref
.read(
tabRepositoryProvider.notifier,
)
.undoClose,
count: count.length,
);
}
},
child: const Text('Close Isolated Tabs'),
),
const Divider(),
MenuItemButton(
leadingIcon: const Icon(MdiIcons.bookmarkPlusOutline),
@@ -809,11 +827,12 @@ class TabViewHeader extends HookConsumerWidget {
),
),
),
Consumer(
builder: (context, ref, child) {
return _TabFilters(tabsViewMode: tabsViewMode);
},
),
if (showContainerUi)
Consumer(
builder: (context, ref, child) {
return _TabFilters(tabsViewMode: tabsViewMode);
},
),
const SizedBox(height: 8),
],
),
@@ -50,7 +50,7 @@ class OpenImageInNewTab extends HookConsumerWidget {
TabMode.fromTabType(
ref
.read(generalSettingsWithDefaultsProvider)
.defaultCreateTabType,
.effectiveDefaultCreateTabType,
);
final tabId = await ref
@@ -61,6 +61,11 @@ class OpenInContainer extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(generalSettingsWithDefaultsProvider);
if (!settings.showContainerUi) {
return const SizedBox.shrink();
}
return ListTile(
leading: const Icon(MdiIcons.selectGroup),
title: const Text('Open in container'),
@@ -92,11 +97,7 @@ class OpenInContainer extends HookConsumerWidget {
final currentTab = ref.read(selectedTabStateProvider);
final tabMode =
currentTab?.tabMode ??
TabMode.fromTabType(
ref
.read(generalSettingsWithDefaultsProvider)
.defaultCreateTabType,
);
TabMode.fromTabType(settings.effectiveDefaultCreateTabType);
final tabId = await ref
.read(tabRepositoryProvider.notifier)
@@ -50,7 +50,7 @@ class OpenInNewTab extends HookConsumerWidget {
TabMode.fromTabType(
ref
.read(generalSettingsWithDefaultsProvider)
.defaultCreateTabType,
.effectiveDefaultCreateTabType,
);
final tabId = await ref
@@ -37,6 +37,7 @@ import 'package:weblibre/features/geckoview/features/contextmenu/presentation/ca
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/share_email.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/share_image.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/candidates/share_link.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/hooks/cached_future.dart';
class ContextMenuDialog extends HookConsumerWidget {
@@ -46,6 +47,10 @@ class ContextMenuDialog extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final showContainerUi = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.showContainerUi),
);
return SimpleDialog(
title: AutoSizeText(
hitResult.getTitle(),
@@ -58,7 +63,7 @@ class ContextMenuDialog extends HookConsumerWidget {
children: [
if (OpenInNewTab.isSupported(hitResult))
OpenInNewTab(hitResult: hitResult),
if (OpenInContainer.isSupported(hitResult))
if (showContainerUi && OpenInContainer.isSupported(hitResult))
OpenInContainer(hitResult: hitResult),
if (CopyLink.isSupported(hitResult)) CopyLink(hitResult: hitResult),
if (SaveFile.isSupported(hitResult)) SaveFile(hitResult: hitResult),
@@ -240,16 +240,17 @@ class OpenSharedContent extends HookConsumerWidget {
children: [
Text('Open link', style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 16),
ContainerChips(
displayMenu: false,
selectedContainer: selectedContainer.value,
onSelected: (container) {
selectedContainer.value = container;
},
onDeleted: (container) {
selectedContainer.value = null;
},
),
if (settings.showContainerUi)
ContainerChips(
displayMenu: false,
selectedContainer: selectedContainer.value,
onSelected: (container) {
selectedContainer.value = container;
},
onDeleted: (container) {
selectedContainer.value = null;
},
),
TextFormField(
controller: textController,
keyboardType: TextInputType.url,
@@ -380,8 +381,10 @@ class OpenSharedContent extends HookConsumerWidget {
subtitle: 'Add to your browser tabs',
icon: MdiIcons.tab,
trailing: PopupMenuButton<TabMode>(
icon: Icon(MdiIcons.tabUnselected, size: 24),
tooltip: 'Private / Isolated',
icon: const Icon(MdiIcons.tabUnselected, size: 24),
tooltip: settings.showIsolatedTabUi
? 'Private / Isolated'
: 'Private',
onSelected: openTab,
itemBuilder: (context) => [
PopupMenuItem(
@@ -398,20 +401,21 @@ class OpenSharedContent extends HookConsumerWidget {
],
),
),
PopupMenuItem(
value: TabMode.newIsolated(),
child: Row(
children: [
Icon(
MdiIcons.shieldLock,
color: appColors.isolatedTabTeal,
size: 20,
),
const SizedBox(width: 12),
const Text('Isolated'),
],
if (settings.showIsolatedTabUi)
PopupMenuItem(
value: TabMode.newIsolated(),
child: Row(
children: [
Icon(
MdiIcons.shieldLock,
color: appColors.isolatedTabTeal,
size: 20,
),
const SizedBox(width: 12),
const Text('Isolated'),
],
),
),
),
],
),
onTap: () => openTab(TabMode.regular),
@@ -78,10 +78,24 @@ class SearchScreen extends HookConsumerWidget {
(value) => value.createChildTabsOption,
),
);
final settings = ref.watch(generalSettingsWithDefaultsProvider);
final selectedTabType = useState(tabType);
final initialTabType =
!settings.showIsolatedTabUi && tabType == TabType.isolated
? TabType.regular
: tabType;
final selectedTabType = useState(initialTabType);
final currentTabTabType = ref.watch(selectedTabTypeProvider);
useEffect(() {
if (!settings.showIsolatedTabUi &&
selectedTabType.value == TabType.isolated) {
selectedTabType.value = TabType.regular;
}
return null;
}, [settings.showIsolatedTabUi]);
final selectedContainer = ref.watch(
selectedContainerDataProvider.select((value) => value.value),
);
@@ -288,58 +302,72 @@ class SearchScreen extends HookConsumerWidget {
? null
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
Expanded(
flex: 4,
child: Align(
alignment: Alignment.centerLeft,
child: Focus(
canRequestFocus: false,
child: AnimatedTabTypeSwitcher(
selected: selectedTabType.value,
onChanged: (value) {
selectedTabType.value = value;
// Restore focus to search field after segment change
WidgetsBinding.instance
.addPostFrameCallback((_) {
searchFocusNode.requestFocus();
});
},
showChildOption: createChildTabsOption,
selectedBackgroundColor:
switch (selectedTabType.value) {
TabType.regular => null,
TabType.private =>
appColors.privateSelectionOverlay,
TabType.isolated =>
appColors.isolatedSelectionOverlay,
TabType.child =>
switch (currentTabTabType) {
TabType.private =>
appColors
.privateSelectionOverlay,
TabType.isolated =>
appColors
.isolatedSelectionOverlay,
_ => null,
},
},
child: Builder(
builder: (context) {
final tabTypeSwitcher = Focus(
canRequestFocus: false,
child: AnimatedTabTypeSwitcher(
selected: selectedTabType.value,
onChanged: (value) {
selectedTabType.value = value;
// Restore focus to search field after segment change
WidgetsBinding.instance.addPostFrameCallback((
_,
) {
searchFocusNode.requestFocus();
});
},
showChildOption: createChildTabsOption,
showIsolatedOption: settings.showIsolatedTabUi,
selectedBackgroundColor: switch (selectedTabType
.value) {
TabType.regular => null,
TabType.private =>
appColors.privateSelectionOverlay,
TabType.isolated =>
appColors.isolatedSelectionOverlay,
TabType.child => switch (currentTabTabType) {
TabType.private =>
appColors.privateSelectionOverlay,
TabType.isolated =>
appColors.isolatedSelectionOverlay,
_ => null,
},
},
),
);
if (!settings.showContainerUi) {
return Center(
child: Transform.scale(
scale: 1.08,
child: tabTypeSwitcher,
),
);
}
return Row(
children: [
Expanded(
flex: 4,
child: Align(
alignment: Alignment.centerLeft,
child: tabTypeSwitcher,
),
),
),
),
const SizedBox(width: 8),
Flexible(
flex: 2,
child: Align(
alignment: Alignment.centerRight,
child: CompactContainerSelector(
selectedContainer: selectedContainer,
const SizedBox(width: 8),
Flexible(
flex: 2,
child: Align(
alignment: Alignment.centerRight,
child: CompactContainerSelector(
selectedContainer: selectedContainer,
),
),
),
),
),
],
],
);
},
),
),
bottom: PreferredSize(
@@ -28,6 +28,7 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
final TabType selected;
final ValueChanged<TabType> onChanged;
final bool showChildOption;
final bool showIsolatedOption;
final Color? selectedBackgroundColor;
const AnimatedTabTypeSwitcher({
@@ -35,6 +36,7 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
required this.selected,
required this.onChanged,
this.showChildOption = false,
this.showIsolatedOption = true,
this.selectedBackgroundColor,
});
@@ -72,15 +74,17 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.private),
),
_divider(borderColor),
_Segment(
tabType: TabType.isolated,
icon: MdiIcons.shieldLock,
label: 'Isolated',
isSelected: selected == TabType.isolated,
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.isolated),
),
if (showIsolatedOption) ...[
_divider(borderColor),
_Segment(
tabType: TabType.isolated,
icon: MdiIcons.shieldLock,
label: 'Isolated',
isSelected: selected == TabType.isolated,
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.isolated),
),
],
if (showChildOption) ...[
_divider(borderColor),
_Segment(
@@ -28,6 +28,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/entities/contai
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
/// A compact container selector that displays only the currently selected
/// container (or "unassigned" if none selected) without counts.
@@ -40,6 +41,15 @@ class CompactContainerSelector extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final containerUiEnabled = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.showContainerUi,
),
);
if (!containerUiEnabled) {
return const SizedBox.shrink();
}
final theme = Theme.of(context);
final isSelected = selectedContainer != null;
@@ -200,6 +200,15 @@ class ContainerChips extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final containerUiEnabled = ref.watch(
generalSettingsWithDefaultsProvider.select(
(settings) => settings.showContainerUi,
),
);
if (!containerUiEnabled) {
return const SizedBox.shrink();
}
final searchText = useListenableSelector(
searchTextListenable,
() => searchTextListenable?.value.text,