visual nesting indicator for quick tab switcher
This commit is contained in:
+15
-4
@@ -608,9 +608,12 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
||||
final sortPinnedFirst = ref.watch(
|
||||
tabViewFilterControllerProvider.select((v) => v.sortPinnedFirst),
|
||||
);
|
||||
final showHierarchicalTabs = ref.watch(
|
||||
tabViewFilterControllerProvider.select((v) => v.showHierarchicalTabs),
|
||||
final hierarchyGlyphs = ref.watch(
|
||||
generalSettingsWithDefaultsProvider.select(
|
||||
(s) => s.quickTabSwitcherHierarchyGlyphs,
|
||||
),
|
||||
);
|
||||
final showHierarchicalTabs = hierarchyGlyphs > 0;
|
||||
final selectedContainerId = ref.watch(selectedContainerProvider);
|
||||
final hierarchyContainerId =
|
||||
effectiveMode == QuickTabSwitcherMode.containerTabs
|
||||
@@ -761,6 +764,7 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
||||
activeItemKey: activeItemKey.value,
|
||||
showTitles: showTitles,
|
||||
showIsolatedTabUi: showIsolatedTabUi,
|
||||
hierarchyGlyphs: hierarchyGlyphs,
|
||||
enablePinTabInMenu: effectiveMode == QuickTabSwitcherMode.containerTabs,
|
||||
onSelected: (item) async {
|
||||
if (!item.isHistory && item.isActive) {
|
||||
@@ -832,6 +836,7 @@ class QuickTabSwitcherView extends StatelessWidget {
|
||||
this.activeItemKey,
|
||||
required this.showTitles,
|
||||
required this.showIsolatedTabUi,
|
||||
this.hierarchyGlyphs = defaultQuickTabSwitcherHierarchyGlyphs,
|
||||
required this.enablePinTabInMenu,
|
||||
required this.onSelected,
|
||||
this.onReorderItem,
|
||||
@@ -845,6 +850,11 @@ class QuickTabSwitcherView extends StatelessWidget {
|
||||
final GlobalKey? activeItemKey;
|
||||
final bool showTitles;
|
||||
final bool showIsolatedTabUi;
|
||||
|
||||
/// Max inline chevron glyphs on a chip's depth indicator before collapsing
|
||||
/// into an icon + count badge. A value of 0 hides the indicator entirely.
|
||||
final int hierarchyGlyphs;
|
||||
|
||||
final bool enablePinTabInMenu;
|
||||
final Future<void> Function(QuickTabSwitcherItem item) onSelected;
|
||||
|
||||
@@ -1007,7 +1017,7 @@ class QuickTabSwitcherView extends StatelessWidget {
|
||||
!item.isHistory &&
|
||||
!item.isPinned &&
|
||||
!item.isSandbox &&
|
||||
item.depth == 0 &&
|
||||
(item.depth == 0 || hierarchyGlyphs == 0) &&
|
||||
item.tabMode is! PrivateTabMode &&
|
||||
item.tabMode is! IsolatedTabMode)
|
||||
? EdgeInsets.zero
|
||||
@@ -1025,7 +1035,7 @@ class QuickTabSwitcherView extends StatelessWidget {
|
||||
final row = Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (item.depth > 0)
|
||||
if (item.depth > 0 && hierarchyGlyphs > 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 6.0),
|
||||
child: TabDepthIndicator(
|
||||
@@ -1033,6 +1043,7 @@ class QuickTabSwitcherView extends StatelessWidget {
|
||||
height: 24.0,
|
||||
iconSize: 14.0,
|
||||
horizontalPadding: 4.0,
|
||||
maxInlineGlyphs: hierarchyGlyphs,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
|
||||
+8
-3
@@ -21,8 +21,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:weblibre/presentation/widgets/inline_count_badge.dart';
|
||||
|
||||
// Above this depth, collapse the chevrons into a single icon + count badge.
|
||||
const int _maxInlineGlyphs = 2;
|
||||
// Default cap on inline chevrons before collapsing into a single icon + badge.
|
||||
const int _defaultMaxInlineGlyphs = 2;
|
||||
|
||||
class TabDepthIndicator extends StatelessWidget {
|
||||
final int depth;
|
||||
@@ -30,18 +30,23 @@ class TabDepthIndicator extends StatelessWidget {
|
||||
final double iconSize;
|
||||
final double horizontalPadding;
|
||||
|
||||
/// Number of chevron glyphs shown inline before the indicator collapses
|
||||
/// into a single icon + count badge.
|
||||
final int maxInlineGlyphs;
|
||||
|
||||
const TabDepthIndicator({
|
||||
required this.depth,
|
||||
this.height = 28.0,
|
||||
this.iconSize = 16.0,
|
||||
this.horizontalPadding = 6.0,
|
||||
this.maxInlineGlyphs = _defaultMaxInlineGlyphs,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final showBadge = depth > _maxInlineGlyphs;
|
||||
final showBadge = depth > maxInlineGlyphs;
|
||||
final glyphCount = showBadge ? 1 : depth;
|
||||
|
||||
return SizedBox(
|
||||
|
||||
@@ -35,7 +35,7 @@ final class GestureSettingsRepositoryProvider
|
||||
}
|
||||
|
||||
String _$gestureSettingsRepositoryHash() =>
|
||||
r'fc898b669a484fe45f89024d8456190f08d56085';
|
||||
r'fcd226987b9748e9da2c1dbc454b5f6853232ea1';
|
||||
|
||||
abstract class _$GestureSettingsRepository
|
||||
extends $StreamNotifier<GestureSettings> {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
@@ -100,6 +101,12 @@ const List<SettingsSectionDefinition> toolbarLayoutSettingsSections = [
|
||||
keywords: ['page titles'],
|
||||
child: _QuickTabSwitcherShowTitlesTile(),
|
||||
),
|
||||
SettingsEntryDefinition(
|
||||
title: 'Hierarchy Depth in Quick Tab Switcher',
|
||||
subtitle: 'How many nesting chevrons to show on switcher chips',
|
||||
keywords: ['hierarchy', 'nesting', 'depth', 'tree', 'chevrons'],
|
||||
child: _QuickTabSwitcherHierarchyGlyphsTile(),
|
||||
),
|
||||
],
|
||||
),
|
||||
SettingsSectionDefinition(
|
||||
@@ -453,6 +460,100 @@ class _QuickTabSwitcherShowTitlesTile extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _QuickTabSwitcherHierarchyGlyphsTile extends HookConsumerWidget {
|
||||
const _QuickTabSwitcherHierarchyGlyphsTile();
|
||||
|
||||
static String _label(int glyphs) => switch (glyphs) {
|
||||
0 => 'Off',
|
||||
1 => '1 level',
|
||||
_ => '$glyphs levels',
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final hierarchyGlyphs = ref.watch(
|
||||
generalSettingsWithDefaultsProvider.select(
|
||||
(s) => s.quickTabSwitcherHierarchyGlyphs,
|
||||
),
|
||||
);
|
||||
final tabBarShowQuickTabSwitcherBar = ref.watch(
|
||||
generalSettingsWithDefaultsProvider.select(
|
||||
(s) => s.tabBarShowQuickTabSwitcherBar,
|
||||
),
|
||||
);
|
||||
|
||||
final sliderValue = useState(hierarchyGlyphs.toDouble());
|
||||
useEffect(() {
|
||||
sliderValue.value = hierarchyGlyphs.toDouble();
|
||||
return null;
|
||||
}, [hierarchyGlyphs]);
|
||||
|
||||
final currentGlyphs = sliderValue.value.round();
|
||||
final enabled = tabBarShowQuickTabSwitcherBar;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Hierarchy Depth in Quick Tab Switcher'),
|
||||
subtitle: const Text(
|
||||
'How many nesting chevrons to show on switcher chips before '
|
||||
'collapsing into a count badge (0 hides the indicator)',
|
||||
),
|
||||
leading: const Icon(MdiIcons.fileTree),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
enabled: enabled,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Slider(
|
||||
min: minQuickTabSwitcherHierarchyGlyphs.toDouble(),
|
||||
max: maxQuickTabSwitcherHierarchyGlyphs.toDouble(),
|
||||
divisions:
|
||||
maxQuickTabSwitcherHierarchyGlyphs -
|
||||
minQuickTabSwitcherHierarchyGlyphs,
|
||||
label: _label(currentGlyphs),
|
||||
value: sliderValue.value.clamp(
|
||||
minQuickTabSwitcherHierarchyGlyphs.toDouble(),
|
||||
maxQuickTabSwitcherHierarchyGlyphs.toDouble(),
|
||||
),
|
||||
onChanged: enabled
|
||||
? (value) {
|
||||
sliderValue.value = value;
|
||||
}
|
||||
: null,
|
||||
onChangeEnd: enabled
|
||||
? (value) async {
|
||||
final normalized = value.round();
|
||||
sliderValue.value = normalized.toDouble();
|
||||
await ref
|
||||
.read(
|
||||
saveGeneralSettingsControllerProvider.notifier,
|
||||
)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith
|
||||
.quickTabSwitcherHierarchyGlyphs(normalized),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_label(currentGlyphs),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AutoHideTabBarTile extends HookConsumerWidget {
|
||||
const _AutoHideTabBarTile();
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ class TabBarPreviewCard extends HookWidget {
|
||||
tabMode: TabMode.isolated('preview-isolated-context'),
|
||||
isHistory: false,
|
||||
isPinned: false,
|
||||
depth: 2,
|
||||
depth: 3,
|
||||
url: Uri.parse('https://example.com/bank'),
|
||||
color: null,
|
||||
avatar: const Icon(MdiIcons.web, size: 20),
|
||||
@@ -203,6 +203,7 @@ class TabBarPreviewCard extends HookWidget {
|
||||
scrollController: quickTabsController,
|
||||
showTitles: settings.quickTabSwitcherShowTitles,
|
||||
showIsolatedTabUi: settings.showIsolatedTabUi,
|
||||
hierarchyGlyphs: settings.quickTabSwitcherHierarchyGlyphs,
|
||||
enablePinTabInMenu: false,
|
||||
onSelected: (_) async {},
|
||||
);
|
||||
|
||||
@@ -40,6 +40,13 @@ const minUiScaleFactor = 0.5;
|
||||
const maxUiScaleFactor = 1.5;
|
||||
const uiScaleFactorStep = 0.05;
|
||||
|
||||
/// Max number of inline chevron glyphs shown on a quick tab switcher chip
|
||||
/// before the depth indicator collapses into a single icon + count badge.
|
||||
/// A value of 0 hides the hierarchy indicator entirely.
|
||||
const defaultQuickTabSwitcherHierarchyGlyphs = 2;
|
||||
const minQuickTabSwitcherHierarchyGlyphs = 0;
|
||||
const maxQuickTabSwitcherHierarchyGlyphs = 4;
|
||||
|
||||
enum TabBarSwipeAction { switchLastOpened, navigateOrderedTabs }
|
||||
|
||||
enum QuickTabSwitcherMode { lastUsedTabs, containerTabs }
|
||||
@@ -106,6 +113,7 @@ class GeneralSettings with FastEquatable {
|
||||
final bool allowClipboardAccess;
|
||||
final bool tabListShowFavicons;
|
||||
final bool quickTabSwitcherShowTitles;
|
||||
final int quickTabSwitcherHierarchyGlyphs;
|
||||
final bool quickTabSwitcherShowHistorySuggestions;
|
||||
final String syncServerOverride;
|
||||
final String syncTokenServerOverride;
|
||||
@@ -173,6 +181,7 @@ class GeneralSettings with FastEquatable {
|
||||
required this.allowClipboardAccess,
|
||||
required this.tabListShowFavicons,
|
||||
required this.quickTabSwitcherShowTitles,
|
||||
required this.quickTabSwitcherHierarchyGlyphs,
|
||||
required this.quickTabSwitcherShowHistorySuggestions,
|
||||
required this.syncServerOverride,
|
||||
required this.syncTokenServerOverride,
|
||||
@@ -232,6 +241,7 @@ class GeneralSettings with FastEquatable {
|
||||
bool? allowClipboardAccess,
|
||||
bool? tabListShowFavicons,
|
||||
bool? quickTabSwitcherShowTitles,
|
||||
int? quickTabSwitcherHierarchyGlyphs,
|
||||
bool? quickTabSwitcherShowHistorySuggestions,
|
||||
String? syncServerOverride,
|
||||
String? syncTokenServerOverride,
|
||||
@@ -293,6 +303,9 @@ class GeneralSettings with FastEquatable {
|
||||
allowClipboardAccess = allowClipboardAccess ?? true,
|
||||
tabListShowFavicons = tabListShowFavicons ?? false,
|
||||
quickTabSwitcherShowTitles = quickTabSwitcherShowTitles ?? true,
|
||||
quickTabSwitcherHierarchyGlyphs =
|
||||
quickTabSwitcherHierarchyGlyphs ??
|
||||
defaultQuickTabSwitcherHierarchyGlyphs,
|
||||
quickTabSwitcherShowHistorySuggestions =
|
||||
quickTabSwitcherShowHistorySuggestions ?? true,
|
||||
syncServerOverride = syncServerOverride ?? '',
|
||||
@@ -407,6 +420,7 @@ class GeneralSettings with FastEquatable {
|
||||
allowClipboardAccess,
|
||||
tabListShowFavicons,
|
||||
quickTabSwitcherShowTitles,
|
||||
quickTabSwitcherHierarchyGlyphs,
|
||||
quickTabSwitcherShowHistorySuggestions,
|
||||
syncServerOverride,
|
||||
syncTokenServerOverride,
|
||||
|
||||
@@ -91,6 +91,10 @@ abstract class _$GeneralSettingsCWProxy {
|
||||
|
||||
GeneralSettings quickTabSwitcherShowTitles(bool quickTabSwitcherShowTitles);
|
||||
|
||||
GeneralSettings quickTabSwitcherHierarchyGlyphs(
|
||||
int quickTabSwitcherHierarchyGlyphs,
|
||||
);
|
||||
|
||||
GeneralSettings quickTabSwitcherShowHistorySuggestions(
|
||||
bool quickTabSwitcherShowHistorySuggestions,
|
||||
);
|
||||
@@ -182,6 +186,7 @@ abstract class _$GeneralSettingsCWProxy {
|
||||
bool allowClipboardAccess,
|
||||
bool tabListShowFavicons,
|
||||
bool quickTabSwitcherShowTitles,
|
||||
int quickTabSwitcherHierarchyGlyphs,
|
||||
bool quickTabSwitcherShowHistorySuggestions,
|
||||
String syncServerOverride,
|
||||
String syncTokenServerOverride,
|
||||
@@ -360,6 +365,11 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
GeneralSettings quickTabSwitcherShowTitles(bool quickTabSwitcherShowTitles) =>
|
||||
call(quickTabSwitcherShowTitles: quickTabSwitcherShowTitles);
|
||||
|
||||
@override
|
||||
GeneralSettings quickTabSwitcherHierarchyGlyphs(
|
||||
int quickTabSwitcherHierarchyGlyphs,
|
||||
) => call(quickTabSwitcherHierarchyGlyphs: quickTabSwitcherHierarchyGlyphs);
|
||||
|
||||
@override
|
||||
GeneralSettings quickTabSwitcherShowHistorySuggestions(
|
||||
bool quickTabSwitcherShowHistorySuggestions,
|
||||
@@ -495,6 +505,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
Object? allowClipboardAccess = const $CopyWithPlaceholder(),
|
||||
Object? tabListShowFavicons = const $CopyWithPlaceholder(),
|
||||
Object? quickTabSwitcherShowTitles = const $CopyWithPlaceholder(),
|
||||
Object? quickTabSwitcherHierarchyGlyphs = const $CopyWithPlaceholder(),
|
||||
Object? quickTabSwitcherShowHistorySuggestions =
|
||||
const $CopyWithPlaceholder(),
|
||||
Object? syncServerOverride = const $CopyWithPlaceholder(),
|
||||
@@ -723,6 +734,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
||||
? _value.quickTabSwitcherShowTitles
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: quickTabSwitcherShowTitles as bool,
|
||||
quickTabSwitcherHierarchyGlyphs:
|
||||
quickTabSwitcherHierarchyGlyphs == const $CopyWithPlaceholder() ||
|
||||
quickTabSwitcherHierarchyGlyphs == null
|
||||
? _value.quickTabSwitcherHierarchyGlyphs
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: quickTabSwitcherHierarchyGlyphs as int,
|
||||
quickTabSwitcherShowHistorySuggestions:
|
||||
quickTabSwitcherShowHistorySuggestions ==
|
||||
const $CopyWithPlaceholder() ||
|
||||
@@ -943,6 +960,8 @@ GeneralSettings _$GeneralSettingsFromJson(
|
||||
allowClipboardAccess: json['allowClipboardAccess'] as bool?,
|
||||
tabListShowFavicons: json['tabListShowFavicons'] as bool?,
|
||||
quickTabSwitcherShowTitles: json['quickTabSwitcherShowTitles'] as bool?,
|
||||
quickTabSwitcherHierarchyGlyphs:
|
||||
(json['quickTabSwitcherHierarchyGlyphs'] as num?)?.toInt(),
|
||||
quickTabSwitcherShowHistorySuggestions:
|
||||
json['quickTabSwitcherShowHistorySuggestions'] as bool?,
|
||||
syncServerOverride: json['syncServerOverride'] as String?,
|
||||
@@ -1023,6 +1042,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
||||
'allowClipboardAccess': instance.allowClipboardAccess,
|
||||
'tabListShowFavicons': instance.tabListShowFavicons,
|
||||
'quickTabSwitcherShowTitles': instance.quickTabSwitcherShowTitles,
|
||||
'quickTabSwitcherHierarchyGlyphs': instance.quickTabSwitcherHierarchyGlyphs,
|
||||
'quickTabSwitcherShowHistorySuggestions':
|
||||
instance.quickTabSwitcherShowHistorySuggestions,
|
||||
'syncServerOverride': instance.syncServerOverride,
|
||||
|
||||
@@ -181,6 +181,11 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
|
||||
),
|
||||
'quickTabSwitcherShowTitles': settings['quickTabSwitcherShowTitles']
|
||||
?.readAs(DriftSqlType.bool, db.typeMapping),
|
||||
'quickTabSwitcherHierarchyGlyphs':
|
||||
settings['quickTabSwitcherHierarchyGlyphs']?.readAs(
|
||||
DriftSqlType.int,
|
||||
db.typeMapping,
|
||||
),
|
||||
'quickTabSwitcherShowHistorySuggestions':
|
||||
settings['quickTabSwitcherShowHistorySuggestions']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
|
||||
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
|
||||
}
|
||||
|
||||
String _$generalSettingsRepositoryHash() =>
|
||||
r'b269ac847d1ae1547bdfe0b926d8d4ffc5ec8f7a';
|
||||
r'1e7c7955669933a2dac5a279bcfbac54246672ca';
|
||||
|
||||
abstract class _$GeneralSettingsRepository
|
||||
extends $StreamNotifier<GeneralSettings> {
|
||||
|
||||
Reference in New Issue
Block a user