update icons and tab order

This commit is contained in:
Fabian Freund
2026-02-27 20:34:10 +01:00
parent 3d1293b352
commit 00bf76b0ba
12 changed files with 89 additions and 70 deletions
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
../../../font/src/assets/generated-icons/WebLibre.ttf
+2 -1
View File
@@ -23,7 +23,8 @@ import 'package:weblibre/utils/uri_policy.dart';
extension UriX on Uri { extension UriX on Uri {
Uri get base => Uri.parse('$scheme://$authority'); Uri get base => Uri.parse('$scheme://$authority');
bool get hasSupportedScheme => allSupportedSchemes.any((s) => s.name == scheme); bool get hasSupportedScheme =>
allSupportedSchemes.any((s) => s.name == scheme);
bool get isHttp => isScheme('http'); bool get isHttp => isScheme('http');
bool get isHttps => isScheme('https'); bool get isHttps => isScheme('https');
@@ -152,13 +152,6 @@ class AppBarTitle extends HookConsumerWidget {
), ),
Row( Row(
children: [ children: [
if (isTabTuneledAsync.hasValue &&
isTabTuneledAsync.value == true) ...[
const Icon(MdiIcons.tunnelOutline, size: 14),
const SizedBox(width: 4),
],
icon,
const SizedBox(width: 4),
if (tabState.tabMode is PrivateTabMode) ...[ if (tabState.tabMode is PrivateTabMode) ...[
Icon( Icon(
MdiIcons.dominoMask, MdiIcons.dominoMask,
@@ -168,12 +161,19 @@ class AppBarTitle extends HookConsumerWidget {
const SizedBox(width: 4), const SizedBox(width: 4),
] else if (tabState.tabMode is IsolatedTabMode) ...[ ] else if (tabState.tabMode is IsolatedTabMode) ...[
Icon( Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: appColors.isolatedTabTeal, color: appColors.isolatedTabTeal,
size: 14, size: 14,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
], ],
if (isTabTuneledAsync.hasValue &&
isTabTuneledAsync.value == true) ...[
const Icon(MdiIcons.tunnelOutline, size: 14),
const SizedBox(width: 4),
],
icon,
const SizedBox(width: 4),
Expanded( Expanded(
child: UriBreadcrumb( child: UriBreadcrumb(
uri: tabState.url, uri: tabState.url,
@@ -553,7 +553,7 @@ class QuickTabSwitcher extends HookConsumerWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 8.0), padding: const EdgeInsets.only(left: 8.0),
child: Icon( child: Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: appColors.isolatedTabTeal, color: appColors.isolatedTabTeal,
size: 20, size: 20,
), ),
@@ -23,7 +23,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart'; import 'package:weblibre/core/design/app_colors.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
class TabCreationMenu extends HookConsumerWidget { class TabCreationMenu extends HookConsumerWidget {
final Widget child; final Widget child;
@@ -61,24 +60,6 @@ class TabCreationMenu extends HookConsumerWidget {
leadingIcon: const Icon(MdiIcons.tab), leadingIcon: const Icon(MdiIcons.tab),
child: const Text('Add Regular Tab'), child: const Text('Add Regular Tab'),
), ),
MenuItemButton(
onPressed: () async {
await const SearchRoute(tabType: TabType.private).push(context);
},
leadingIcon: const Icon(WebLibreIcons.privateTab),
child: const Text('Add Private Tab'),
),
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'),
),
if (createChildTabsOption) if (createChildTabsOption)
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
@@ -87,6 +68,27 @@ class TabCreationMenu extends HookConsumerWidget {
leadingIcon: const Icon(MdiIcons.fileTree), leadingIcon: const Icon(MdiIcons.fileTree),
child: const Text('Add Child Tab'), child: const Text('Add Child Tab'),
), ),
MenuItemButton(
onPressed: () async {
await const SearchRoute(tabType: TabType.private).push(context);
},
leadingIcon: Icon(
MdiIcons.dominoMask,
color: AppColors.of(context).privateTabPurple,
),
child: const Text('Add Private Tab'),
),
if (showIsolatedTabUi)
MenuItemButton(
onPressed: () async {
await const SearchRoute(tabType: TabType.isolated).push(context);
},
leadingIcon: Icon(
MdiIcons.snowflake,
color: AppColors.of(context).isolatedTabTeal,
),
child: const Text('Add Isolated Tab'),
),
], ],
child: child, child: child,
); );
@@ -50,7 +50,6 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/co
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/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/hooks/menu_controller.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'; 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;
@@ -273,7 +272,10 @@ class TabMenu extends HookConsumerWidget {
}, },
), ),
MenuItemButton( MenuItemButton(
leadingIcon: const Icon(WebLibreIcons.privateTab), leadingIcon: Icon(
MdiIcons.dominoMask,
color: AppColors.of(context).privateTabPurple,
),
child: const Text('Private'), child: const Text('Private'),
onPressed: () async { onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId))!; final tabState = ref.read(tabStateProvider(selectedTabId))!;
@@ -318,7 +320,7 @@ class TabMenu extends HookConsumerWidget {
if (settings.showIsolatedTabUi) if (settings.showIsolatedTabUi)
MenuItemButton( MenuItemButton(
leadingIcon: Icon( leadingIcon: Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: AppColors.of(context).isolatedTabTeal, color: AppColors.of(context).isolatedTabTeal,
), ),
child: const Text('Isolated'), child: const Text('Isolated'),
@@ -233,7 +233,7 @@ class GridTabPreview extends HookConsumerWidget {
top: -4, top: -4,
child: Icon( child: Icon(
tabState.tabMode is IsolatedTabMode tabState.tabMode is IsolatedTabMode
? MdiIcons.shieldLock ? MdiIcons.snowflake
: MdiIcons.dominoMask, : MdiIcons.dominoMask,
color: tabState.tabMode is IsolatedTabMode color: tabState.tabMode is IsolatedTabMode
? appColors.isolatedTabTeal ? appColors.isolatedTabTeal
@@ -337,7 +337,7 @@ class ListTabPreview extends HookConsumerWidget {
}; };
final (modeBadgeIcon, modeBadgeColor) = switch (tabState.tabMode) { final (modeBadgeIcon, modeBadgeColor) = switch (tabState.tabMode) {
PrivateTabMode() => (MdiIcons.dominoMask, appColors.privateTabPurple), PrivateTabMode() => (MdiIcons.dominoMask, appColors.privateTabPurple),
IsolatedTabMode() => (MdiIcons.shieldLock, appColors.isolatedTabTeal), IsolatedTabMode() => (MdiIcons.snowflake, appColors.isolatedTabTeal),
RegularTabMode() => (null, null), RegularTabMode() => (null, null),
}; };
@@ -516,7 +516,7 @@ class TabViewHeader extends HookConsumerWidget {
if (showIsolatedTabUi) if (showIsolatedTabUi)
MenuItemButton( MenuItemButton(
leadingIcon: Icon( leadingIcon: Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: AppColors.of(context).isolatedTabTeal, color: AppColors.of(context).isolatedTabTeal,
), ),
onPressed: isSyncedScope onPressed: isSyncedScope
@@ -42,6 +42,7 @@ import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/c
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/hooks/cached_future.dart'; import 'package:weblibre/presentation/hooks/cached_future.dart';
import 'package:weblibre/presentation/hooks/debouncer.dart'; import 'package:weblibre/presentation/hooks/debouncer.dart';
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
import 'package:weblibre/utils/form_validators.dart'; import 'package:weblibre/utils/form_validators.dart';
import 'package:weblibre/utils/ui_helper.dart'; import 'package:weblibre/utils/ui_helper.dart';
@@ -381,7 +382,7 @@ class OpenSharedContent extends HookConsumerWidget {
subtitle: 'Add to your browser tabs', subtitle: 'Add to your browser tabs',
icon: MdiIcons.tab, icon: MdiIcons.tab,
trailing: PopupMenuButton<TabMode>( trailing: PopupMenuButton<TabMode>(
icon: const Icon(MdiIcons.tabUnselected, size: 24), icon: const Icon(WebLibreIcons.tabType, size: 24),
tooltip: settings.showIsolatedTabUi tooltip: settings.showIsolatedTabUi
? 'Private / Isolated' ? 'Private / Isolated'
: 'Private', : 'Private',
@@ -407,7 +408,7 @@ class OpenSharedContent extends HookConsumerWidget {
child: Row( child: Row(
children: [ children: [
Icon( Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: appColors.isolatedTabTeal, color: appColors.isolatedTabTeal,
size: 20, size: 20,
), ),
@@ -20,7 +20,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
/// An animated tab type switcher that only shows the label for the currently /// An animated tab type switcher that only shows the label for the currently
/// active option. Inactive options collapse to show only their icon. /// active option. Inactive options collapse to show only their icon.
@@ -65,26 +64,6 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
selectedBackgroundColor: selectedBackgroundColor, selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.regular), onTap: () => onChanged(TabType.regular),
), ),
_divider(borderColor),
_Segment(
tabType: TabType.private,
icon: WebLibreIcons.privateTab,
label: 'Private',
isSelected: selected == TabType.private,
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.private),
),
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) ...[ if (showChildOption) ...[
_divider(borderColor), _divider(borderColor),
_Segment( _Segment(
@@ -96,6 +75,26 @@ class AnimatedTabTypeSwitcher extends StatelessWidget {
onTap: () => onChanged(TabType.child), onTap: () => onChanged(TabType.child),
), ),
], ],
_divider(borderColor),
_Segment(
tabType: TabType.private,
icon: MdiIcons.dominoMask,
label: 'Private',
isSelected: selected == TabType.private,
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.private),
),
if (showIsolatedOption) ...[
_divider(borderColor),
_Segment(
tabType: TabType.isolated,
icon: MdiIcons.snowflake,
label: 'Isolated',
isSelected: selected == TabType.isolated,
selectedBackgroundColor: selectedBackgroundColor,
onTap: () => onChanged(TabType.isolated),
),
],
], ],
), ),
), ),
@@ -30,7 +30,6 @@ import 'package:weblibre/features/settings/presentation/controllers/save_setting
import 'package:weblibre/features/settings/presentation/widgets/sections.dart'; import 'package:weblibre/features/settings/presentation/widgets/sections.dart';
import 'package:weblibre/features/user/data/models/general_settings.dart'; import 'package:weblibre/features/user/data/models/general_settings.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/presentation/icons/weblibre_icons.dart';
class TabsBehaviorSettingsScreen extends StatelessWidget { class TabsBehaviorSettingsScreen extends StatelessWidget {
const TabsBehaviorSettingsScreen({super.key}); const TabsBehaviorSettingsScreen({super.key});
@@ -137,17 +136,22 @@ class _NewTabDefaultSection extends HookConsumerWidget {
label: Text('Regular'), label: Text('Regular'),
icon: Icon(MdiIcons.tab), icon: Icon(MdiIcons.tab),
), ),
const ButtonSegment( ButtonSegment(
value: TabType.private, value: TabType.private,
label: Text('Private'), label: const Text('Private'),
icon: Icon(WebLibreIcons.privateTab), icon: Icon(
MdiIcons.dominoMask,
color: defaultCreateTabType == TabType.private
? null
: appColors.privateTabPurple,
),
), ),
if (settings.showIsolatedTabUi) if (settings.showIsolatedTabUi)
ButtonSegment( ButtonSegment(
value: TabType.isolated, value: TabType.isolated,
label: const Text('Isolated'), label: const Text('Isolated'),
icon: Icon( icon: Icon(
MdiIcons.shieldLock, MdiIcons.snowflake,
color: defaultCreateTabType == TabType.isolated color: defaultCreateTabType == TabType.isolated
? null ? null
: appColors.isolatedTabTeal, : appColors.isolatedTabTeal,
@@ -206,21 +210,26 @@ class _ExternalLinkHandlingSection extends HookConsumerWidget {
Center( Center(
child: SegmentedButton( child: SegmentedButton(
showSelectedIcon: false, showSelectedIcon: false,
segments: const [ segments: [
ButtonSegment( const ButtonSegment(
value: TabIntentOpenSetting.ask, value: TabIntentOpenSetting.ask,
label: Text('Prompt'), label: Text('Prompt'),
icon: Icon(MdiIcons.messageQuestion), icon: Icon(MdiIcons.messageQuestion),
), ),
ButtonSegment( const ButtonSegment(
value: TabIntentOpenSetting.regular, value: TabIntentOpenSetting.regular,
label: Text('Regular'), label: Text('Regular'),
icon: Icon(MdiIcons.tab), icon: Icon(MdiIcons.tab),
), ),
ButtonSegment( ButtonSegment(
value: TabIntentOpenSetting.private, value: TabIntentOpenSetting.private,
label: Text('Private'), label: const Text('Private'),
icon: Icon(WebLibreIcons.privateTab), icon: Icon(
MdiIcons.dominoMask,
color: tabIntentOpenSetting == TabIntentOpenSetting.private
? null
: appColors.privateTabPurple,
),
), ),
], ],
selected: {tabIntentOpenSetting}, selected: {tabIntentOpenSetting},
@@ -326,7 +335,10 @@ class _ShowIsolatedTabUiTile extends HookConsumerWidget {
return SwitchListTile.adaptive( return SwitchListTile.adaptive(
title: const Text('Show Isolated Tab UI'), title: const Text('Show Isolated Tab UI'),
subtitle: const Text('Show isolated-tab creation options in the UI'), subtitle: const Text('Show isolated-tab creation options in the UI'),
secondary: const Icon(MdiIcons.shieldLock), secondary: Icon(
MdiIcons.snowflake,
color: AppColors.of(context).isolatedTabTeal,
),
value: showIsolatedTabUi, value: showIsolatedTabUi,
onChanged: (value) async { onChanged: (value) async {
await ref.read(saveGeneralSettingsControllerProvider.notifier).save(( await ref.read(saveGeneralSettingsControllerProvider.notifier).save((
+3 -1
View File
@@ -45,7 +45,9 @@ const allSupportedSchemes = [
const httpOnlySchemes = [kSchemeHttp, kSchemeHttps]; const httpOnlySchemes = [kSchemeHttp, kSchemeHttps];
bool schemeRequiresAuthority(String scheme) { bool schemeRequiresAuthority(String scheme) {
return allSupportedSchemes.any((s) => s.name == scheme && s.requiresAuthority); return allSupportedSchemes.any(
(s) => s.name == scheme && s.requiresAuthority,
);
} }
enum SchemePolicy { enum SchemePolicy {