From 7da05d07808e717f215a8d86b68b4534b15a2f41 Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Fri, 26 Sep 2025 13:47:29 +0200 Subject: [PATCH] use uri breadcrumb representation wherever possible --- .../browser_modules/app_bar_title.dart | 5 +- .../widgets/search_modules/tab_search.dart | 7 +-- .../presentation/widgets/uri_breadcrumb.dart | 50 +++++++++++++++++++ .../widgets/website_title_tile.dart | 5 +- 4 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 app/lib/presentation/widgets/uri_breadcrumb.dart diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart index 5dcc677e..ac37f7f6 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/app_bar_title.dart @@ -25,6 +25,7 @@ import 'package:skeletonizer/skeletonizer.dart'; import 'package:text_scroll/text_scroll.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_icon.dart'; +import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart'; class AppBarTitle extends HookConsumerWidget { const AppBarTitle({super.key}); @@ -100,8 +101,8 @@ class AppBarTitle extends HookConsumerWidget { icon, const SizedBox(width: 4), Expanded( - child: Text( - tabState.url.authority, + child: UriBreadcrumb( + uri: tabState.url, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface, ), diff --git a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart index a5d5936f..0b07d7b8 100644 --- a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart +++ b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart @@ -36,6 +36,7 @@ import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selec import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab_search.dart'; import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_chips.dart'; import 'package:weblibre/presentation/hooks/listenable_callback.dart'; +import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart'; import 'package:weblibre/presentation/widgets/url_icon.dart'; class TabSearch extends HookConsumerWidget { @@ -152,11 +153,7 @@ class TabSearch extends HookConsumerWidget { ), ), ) - : Text( - result.url.toString(), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), + : UriBreadcrumb(uri: result.url), onTap: () async { await ref .read(tabRepositoryProvider.notifier) diff --git a/app/lib/presentation/widgets/uri_breadcrumb.dart b/app/lib/presentation/widgets/uri_breadcrumb.dart new file mode 100644 index 00000000..fce9c270 --- /dev/null +++ b/app/lib/presentation/widgets/uri_breadcrumb.dart @@ -0,0 +1,50 @@ +import 'package:collection/collection.dart'; +import 'package:fading_scroll/fading_scroll.dart'; +import 'package:flutter/material.dart'; + +class UriBreadcrumb extends StatelessWidget { + final Uri uri; + final Widget? icon; + final TextStyle? style; + + const UriBreadcrumb({super.key, required this.uri, this.icon, this.style}); + + @override + Widget build(BuildContext context) { + return Tooltip( + message: uri.toString(), + child: DefaultTextStyle( + style: style ?? DefaultTextStyle.of(context).style, + child: FadingScroll( + fadingSize: 15, + builder: (context, controller) { + return SingleChildScrollView( + controller: controller, + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ?icon, + Text( + uri.authority, + maxLines: 1, + softWrap: false, + overflow: TextOverflow.visible, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + if (uri.pathSegments.any((s) => s.isNotEmpty)) + Text( + ' › ${uri.pathSegments.whereNot((s) => s.isEmpty).join(' › ')}', + maxLines: 1, + softWrap: false, + overflow: TextOverflow.visible, + ), + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/app/lib/presentation/widgets/website_title_tile.dart b/app/lib/presentation/widgets/website_title_tile.dart index a675259b..c659b9e0 100644 --- a/app/lib/presentation/widgets/website_title_tile.dart +++ b/app/lib/presentation/widgets/website_title_tile.dart @@ -25,6 +25,7 @@ import 'package:skeletonizer/skeletonizer.dart'; import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/presentation/controllers/website_title.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; +import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart'; class WebsiteTitleTile extends HookConsumerWidget { final TabState initialTabState; @@ -58,7 +59,7 @@ class WebsiteTitleTile extends HookConsumerWidget { maxLines: 6, overflow: TextOverflow.ellipsis, ), - subtitle: Text(initialTabState.url.authority), + subtitle: UriBreadcrumb(uri: initialTabState.url), ); }, error: (error, stackTrace) { @@ -77,7 +78,7 @@ class WebsiteTitleTile extends HookConsumerWidget { ), contentPadding: EdgeInsets.zero, title: Text(initialTabState.title.whenNotEmpty ?? 'Untitled'), - subtitle: Text(initialTabState.url.authority), + subtitle: UriBreadcrumb(uri: initialTabState.url), ), ), );