add setting to show favicon instead of thumbnail in tab list (#174)

This commit is contained in:
Fabian Freund
2026-02-11 11:51:46 +01:00
parent a1c4072a4f
commit f7496ff3e3
7 changed files with 70 additions and 11 deletions
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
} }
} }
String _$tabRepositoryHash() => r'0158f3aeb942dda3dab07c1129ced1695c7703c7'; String _$tabRepositoryHash() => r'db35cef59703f15f1b099b6adb42f7623bec612f';
abstract class _$TabRepository extends $Notifier<void> { abstract class _$TabRepository extends $Notifier<void> {
void build(); void build();
@@ -32,6 +32,7 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/widget
import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/domain/entities/find_in_page_state.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.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/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/widgets/safe_raw_image.dart'; import 'package:weblibre/presentation/widgets/safe_raw_image.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart'; import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
@@ -275,8 +276,19 @@ class ListTabPreview extends HookConsumerWidget {
) ?? ) ??
TabState.$default(tabId); TabState.$default(tabId);
final tabListShowFavicons = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabListShowFavicons),
);
final extendedDeleteMenuController = useMenuController(); final extendedDeleteMenuController = useMenuController();
final leadingWidget = switch ((tabListShowFavicons, tabState.thumbnail)) {
(false, final thumbnail?) when !thumbnail.isDisposed => RepaintBoundary(
child: SafeRawImage(image: thumbnail, fit: BoxFit.fitHeight),
),
_ => TabIcon(tabState: tabState, iconSize: 32),
};
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: tabState.isPrivate ? appColors.privateTabBackground : null, color: tabState.isPrivate ? appColors.privateTabBackground : null,
@@ -291,14 +303,7 @@ class ListTabPreview extends HookConsumerWidget {
onTap: onTap, onTap: onTap,
onLongPress: onLongPress, onLongPress: onLongPress,
contentPadding: const EdgeInsets.only(left: 4), contentPadding: const EdgeInsets.only(left: 4),
leading: (tabState.thumbnail != null && !tabState.thumbnail!.isDisposed) leading: leadingWidget,
? RepaintBoundary(
child: SafeRawImage(
image: tabState.thumbnail,
fit: BoxFit.fitHeight,
),
)
: TabIcon(tabState: tabState, iconSize: 32),
title: Text( title: Text(
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
tabState.titleOrAuthority, tabState.titleOrAuthority,
@@ -79,6 +79,7 @@ class _TabBarLayoutSection extends StatelessWidget {
_ShowContextualTabBarTile(), _ShowContextualTabBarTile(),
_AutoHideTabBarTile(), _AutoHideTabBarTile(),
_BottomSheetTabViewTile(), _BottomSheetTabViewTile(),
_TabListShowFaviconsTile(),
_ShowQuickTabSwitcherBarTile(), _ShowQuickTabSwitcherBarTile(),
_QuickTabSwitcherModeSection(), _QuickTabSwitcherModeSection(),
], ],
@@ -377,6 +378,34 @@ class _BottomSheetTabViewTile extends HookConsumerWidget {
} }
} }
class _TabListShowFaviconsTile extends HookConsumerWidget {
const _TabListShowFaviconsTile();
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabListShowFavicons = ref.watch(
generalSettingsWithDefaultsProvider.select((s) => s.tabListShowFavicons),
);
return SwitchListTile.adaptive(
title: const Text('Show Favicons in List View'),
subtitle: const Text(
'Display website icons instead of page thumbnails in tab list view',
),
secondary: const Icon(MdiIcons.web),
value: tabListShowFavicons,
onChanged: (value) async {
await ref
.read(saveGeneralSettingsControllerProvider.notifier)
.save(
(currentSettings) =>
currentSettings.copyWith.tabListShowFavicons(value),
);
},
);
}
}
class _PullToRefreshTile extends HookConsumerWidget { class _PullToRefreshTile extends HookConsumerWidget {
const _PullToRefreshTile(); const _PullToRefreshTile();
@@ -86,6 +86,7 @@ class GeneralSettings with FastEquatable {
final Duration unassignedTabsAutoCleanInterval; final Duration unassignedTabsAutoCleanInterval;
final int maxSearchHistoryEntries; final int maxSearchHistoryEntries;
final bool allowClipboardAccess; final bool allowClipboardAccess;
final bool tabListShowFavicons;
GeneralSettings({ GeneralSettings({
required this.themeMode, required this.themeMode,
@@ -114,6 +115,7 @@ class GeneralSettings with FastEquatable {
required this.unassignedTabsAutoCleanInterval, required this.unassignedTabsAutoCleanInterval,
required this.maxSearchHistoryEntries, required this.maxSearchHistoryEntries,
required this.allowClipboardAccess, required this.allowClipboardAccess,
required this.tabListShowFavicons,
}); });
GeneralSettings.withDefaults({ GeneralSettings.withDefaults({
@@ -143,6 +145,7 @@ class GeneralSettings with FastEquatable {
Duration? unassignedTabsAutoCleanInterval, Duration? unassignedTabsAutoCleanInterval,
int? maxSearchHistoryEntries, int? maxSearchHistoryEntries,
bool? allowClipboardAccess, bool? allowClipboardAccess,
bool? tabListShowFavicons,
}) : themeMode = themeMode ?? ThemeMode.dark, }) : themeMode = themeMode ?? ThemeMode.dark,
enableReadability = enableReadability ?? true, enableReadability = enableReadability ?? true,
enforceReadability = enforceReadability ?? false, enforceReadability = enforceReadability ?? false,
@@ -172,7 +175,8 @@ class GeneralSettings with FastEquatable {
unassignedTabsAutoCleanInterval = unassignedTabsAutoCleanInterval =
unassignedTabsAutoCleanInterval ?? Duration.zero, unassignedTabsAutoCleanInterval ?? Duration.zero,
maxSearchHistoryEntries = maxSearchHistoryEntries ?? 5, maxSearchHistoryEntries = maxSearchHistoryEntries ?? 5,
allowClipboardAccess = allowClipboardAccess ?? true; allowClipboardAccess = allowClipboardAccess ?? true,
tabListShowFavicons = tabListShowFavicons ?? false;
factory GeneralSettings.fromJson(Map<String, dynamic> json) => factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
_$GeneralSettingsFromJson(json); _$GeneralSettingsFromJson(json);
@@ -207,5 +211,6 @@ class GeneralSettings with FastEquatable {
unassignedTabsAutoCleanInterval, unassignedTabsAutoCleanInterval,
maxSearchHistoryEntries, maxSearchHistoryEntries,
allowClipboardAccess, allowClipboardAccess,
tabListShowFavicons,
]; ];
} }
@@ -71,6 +71,8 @@ abstract class _$GeneralSettingsCWProxy {
GeneralSettings allowClipboardAccess(bool allowClipboardAccess); GeneralSettings allowClipboardAccess(bool allowClipboardAccess);
GeneralSettings tabListShowFavicons(bool tabListShowFavicons);
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
/// ///
@@ -105,6 +107,7 @@ abstract class _$GeneralSettingsCWProxy {
Duration unassignedTabsAutoCleanInterval, Duration unassignedTabsAutoCleanInterval,
int maxSearchHistoryEntries, int maxSearchHistoryEntries,
bool allowClipboardAccess, bool allowClipboardAccess,
bool tabListShowFavicons,
}); });
} }
@@ -224,6 +227,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
GeneralSettings allowClipboardAccess(bool allowClipboardAccess) => GeneralSettings allowClipboardAccess(bool allowClipboardAccess) =>
call(allowClipboardAccess: allowClipboardAccess); call(allowClipboardAccess: allowClipboardAccess);
@override
GeneralSettings tabListShowFavicons(bool tabListShowFavicons) =>
call(tabListShowFavicons: tabListShowFavicons);
@override @override
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`.
@@ -259,6 +266,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
Object? unassignedTabsAutoCleanInterval = const $CopyWithPlaceholder(), Object? unassignedTabsAutoCleanInterval = const $CopyWithPlaceholder(),
Object? maxSearchHistoryEntries = const $CopyWithPlaceholder(), Object? maxSearchHistoryEntries = const $CopyWithPlaceholder(),
Object? allowClipboardAccess = const $CopyWithPlaceholder(), Object? allowClipboardAccess = const $CopyWithPlaceholder(),
Object? tabListShowFavicons = const $CopyWithPlaceholder(),
}) { }) {
return GeneralSettings( return GeneralSettings(
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
@@ -413,6 +421,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
? _value.allowClipboardAccess ? _value.allowClipboardAccess
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: allowClipboardAccess as bool, : allowClipboardAccess as bool,
tabListShowFavicons:
tabListShowFavicons == const $CopyWithPlaceholder() ||
tabListShowFavicons == null
? _value.tabListShowFavicons
// ignore: cast_nullable_to_non_nullable
: tabListShowFavicons as bool,
); );
} }
} }
@@ -489,6 +503,7 @@ GeneralSettings _$GeneralSettingsFromJson(
), ),
maxSearchHistoryEntries: (json['maxSearchHistoryEntries'] as num?)?.toInt(), maxSearchHistoryEntries: (json['maxSearchHistoryEntries'] as num?)?.toInt(),
allowClipboardAccess: json['allowClipboardAccess'] as bool?, allowClipboardAccess: json['allowClipboardAccess'] as bool?,
tabListShowFavicons: json['tabListShowFavicons'] as bool?,
); );
Map<String, dynamic> _$GeneralSettingsToJson( Map<String, dynamic> _$GeneralSettingsToJson(
@@ -529,6 +544,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
instance.unassignedTabsAutoCleanInterval.inMicroseconds, instance.unassignedTabsAutoCleanInterval.inMicroseconds,
'maxSearchHistoryEntries': instance.maxSearchHistoryEntries, 'maxSearchHistoryEntries': instance.maxSearchHistoryEntries,
'allowClipboardAccess': instance.allowClipboardAccess, 'allowClipboardAccess': instance.allowClipboardAccess,
'tabListShowFavicons': instance.tabListShowFavicons,
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {
@@ -145,6 +145,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
DriftSqlType.bool, DriftSqlType.bool,
db.typeMapping, db.typeMapping,
), ),
'tabListShowFavicons': settings['tabListShowFavicons']?.readAs(
DriftSqlType.bool,
db.typeMapping,
),
}); });
} }
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
} }
String _$generalSettingsRepositoryHash() => String _$generalSettingsRepositoryHash() =>
r'302d33b57212a8f3affcf13a8ef5227d68e56ecb'; r'a4e4fb6f4e5b40d32b4cc676532af3bacdbe54d2';
abstract class _$GeneralSettingsRepository abstract class _$GeneralSettingsRepository
extends $StreamNotifier<GeneralSettings> { extends $StreamNotifier<GeneralSettings> {