+152
-187
@@ -57,7 +57,6 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart
|
||||
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||
import 'package:weblibre/presentation/widgets/non_focusable.dart';
|
||||
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||
|
||||
@@ -326,90 +325,78 @@ class BrowserTabBar extends HookConsumerWidget {
|
||||
|
||||
return Visibility(
|
||||
visible: tabBarReaderView || readerabilityStateActive,
|
||||
child: NonFocusable(
|
||||
child: ReaderButton(
|
||||
buttonBuilder: (isLoading, readerActive, icon) =>
|
||||
InkWell(
|
||||
onTap: isLoading
|
||||
? null
|
||||
: () async {
|
||||
await ref
|
||||
.read(
|
||||
readerableScreenControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.toggleReaderView(
|
||||
!readerActive,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 15.0,
|
||||
horizontal: 8.0,
|
||||
),
|
||||
child: icon,
|
||||
child: ReaderButton(
|
||||
buttonBuilder: (isLoading, readerActive, icon) =>
|
||||
InkWell(
|
||||
onTap: isLoading
|
||||
? null
|
||||
: () async {
|
||||
await ref
|
||||
.read(
|
||||
readerableScreenControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.toggleReaderView(!readerActive);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 15.0,
|
||||
horizontal: 8.0,
|
||||
),
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (showExtensionShortcut)
|
||||
NonFocusable(
|
||||
child: ExtensionShortcutMenu(
|
||||
controller: extensionMenuController,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (extensionMenuController.isOpen) {
|
||||
extensionMenuController.close();
|
||||
} else {
|
||||
extensionMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.puzzle),
|
||||
),
|
||||
ExtensionShortcutMenu(
|
||||
controller: extensionMenuController,
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (extensionMenuController.isOpen) {
|
||||
extensionMenuController.close();
|
||||
} else {
|
||||
extensionMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.puzzle),
|
||||
),
|
||||
),
|
||||
if (selectedTabId != null)
|
||||
NonFocusable(
|
||||
child: TabMenu(
|
||||
controller: trippleDotMenuController,
|
||||
selectedTabId: selectedTabId,
|
||||
builder: (context, controller, child) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 15.0,
|
||||
),
|
||||
child: Icon(MdiIcons.dotsVertical),
|
||||
TabMenu(
|
||||
controller: trippleDotMenuController,
|
||||
selectedTabId: selectedTabId,
|
||||
builder: (context, controller, child) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 15.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: Icon(MdiIcons.dotsVertical),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (showMainToolbarTabsCount)
|
||||
NonFocusable(
|
||||
child: TabsCountButton(
|
||||
selectedTabId: selectedTabId,
|
||||
displayedSheet: displayedSheet,
|
||||
showLongPressMenu: true,
|
||||
),
|
||||
TabsCountButton(
|
||||
selectedTabId: selectedTabId,
|
||||
displayedSheet: displayedSheet,
|
||||
showLongPressMenu: true,
|
||||
),
|
||||
if (showMainToolbarNavigationButton)
|
||||
NonFocusable(
|
||||
child: NavigationMenuButton(
|
||||
selectedTabId: selectedTabId,
|
||||
showNavigationButtons: true,
|
||||
),
|
||||
NavigationMenuButton(
|
||||
selectedTabId: selectedTabId,
|
||||
showNavigationButtons: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -453,42 +440,32 @@ class ContextualToolbar extends HookConsumerWidget {
|
||||
children: [
|
||||
if (tabState?.historyState.canGoBack == true ||
|
||||
tabState?.isLoading == true)
|
||||
NonFocusable(
|
||||
child: NavigateBackButton(
|
||||
selectedTabId: selectedTabId,
|
||||
isLoading: tabState?.isLoading ?? false,
|
||||
),
|
||||
NavigateBackButton(
|
||||
selectedTabId: selectedTabId,
|
||||
isLoading: tabState?.isLoading ?? false,
|
||||
)
|
||||
else
|
||||
NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
await BookmarkListRoute(
|
||||
entryGuid: BookmarkRoot.root.id,
|
||||
).push(context);
|
||||
},
|
||||
icon: const Icon(MdiIcons.bookmarkMultiple),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await BookmarkListRoute(
|
||||
entryGuid: BookmarkRoot.root.id,
|
||||
).push(context);
|
||||
},
|
||||
icon: const Icon(MdiIcons.bookmarkMultiple),
|
||||
),
|
||||
if (tabState?.historyState.canGoForward == true)
|
||||
NonFocusable(
|
||||
child: NavigateForwardButton(selectedTabId: selectedTabId),
|
||||
)
|
||||
NavigateForwardButton(selectedTabId: selectedTabId)
|
||||
else
|
||||
NonFocusable(child: ShareMenuButton(selectedTabId: selectedTabId)),
|
||||
const NonFocusable(child: AddTabButton()),
|
||||
NonFocusable(
|
||||
child: TabsCountButton(
|
||||
selectedTabId: selectedTabId,
|
||||
displayedSheet: displayedSheet,
|
||||
showLongPressMenu: false,
|
||||
),
|
||||
ShareMenuButton(selectedTabId: selectedTabId),
|
||||
const AddTabButton(),
|
||||
TabsCountButton(
|
||||
selectedTabId: selectedTabId,
|
||||
displayedSheet: displayedSheet,
|
||||
showLongPressMenu: false,
|
||||
),
|
||||
NonFocusable(
|
||||
child: NavigationMenuButton(
|
||||
selectedTabId: selectedTabId,
|
||||
showNavigationButtons: false,
|
||||
),
|
||||
NavigationMenuButton(
|
||||
selectedTabId: selectedTabId,
|
||||
showNavigationButtons: false,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -651,17 +628,15 @@ class ShareMenuButton extends HookConsumerWidget {
|
||||
ShowQrCodeMenuItemButton(selectedTabId: selectedTabId),
|
||||
],
|
||||
builder: (context, controller, child) {
|
||||
return NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.share),
|
||||
),
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.share),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -730,19 +705,17 @@ class NavigationMenuButton extends HookConsumerWidget {
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
...pageExtensions.map(
|
||||
(extension) => NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
//Use parents .ref because after onPressed this consumer gets disposed already
|
||||
await addonService.invokeAddonAction(
|
||||
extension.extensionId,
|
||||
WebExtensionActionType.page,
|
||||
);
|
||||
},
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: ExtensionBadgeIcon(extension),
|
||||
),
|
||||
(extension) => IconButton(
|
||||
onPressed: () async {
|
||||
//Use parents .ref because after onPressed this consumer gets disposed already
|
||||
await addonService.invokeAddonAction(
|
||||
extension.extensionId,
|
||||
WebExtensionActionType.page,
|
||||
);
|
||||
},
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: ExtensionBadgeIcon(extension),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -974,20 +947,18 @@ class NavigateForwardButton extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: canGoForward
|
||||
? () async {
|
||||
final controller = ref.read(
|
||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||
);
|
||||
return IconButton(
|
||||
onPressed: canGoForward
|
||||
? () async {
|
||||
final controller = ref.read(
|
||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||
);
|
||||
|
||||
await controller.goForward();
|
||||
menuControllerToClose?.close();
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
),
|
||||
await controller.goForward();
|
||||
menuControllerToClose?.close();
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1008,37 +979,33 @@ class NavigateBackButton extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: (canGoBack || isLoading)
|
||||
? () async {
|
||||
final controller = ref.read(
|
||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||
);
|
||||
return IconButton(
|
||||
onPressed: (canGoBack || isLoading)
|
||||
? () async {
|
||||
final controller = ref.read(
|
||||
tabSessionProvider(tabId: selectedTabId).notifier,
|
||||
);
|
||||
|
||||
final isReaderActive = ref.read(
|
||||
selectedTabStateProvider.select(
|
||||
(state) => state?.readerableState.active ?? false,
|
||||
),
|
||||
);
|
||||
final isReaderActive = ref.read(
|
||||
selectedTabStateProvider.select(
|
||||
(state) => state?.readerableState.active ?? false,
|
||||
),
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
await controller.stopLoading();
|
||||
} else if (isReaderActive) {
|
||||
await ref
|
||||
.read(readerableScreenControllerProvider.notifier)
|
||||
.toggleReaderView(false);
|
||||
} else {
|
||||
await controller.goBack();
|
||||
}
|
||||
|
||||
menuControllerToClose?.close();
|
||||
if (isLoading) {
|
||||
await controller.stopLoading();
|
||||
} else if (isReaderActive) {
|
||||
await ref
|
||||
.read(readerableScreenControllerProvider.notifier)
|
||||
.toggleReaderView(false);
|
||||
} else {
|
||||
await controller.goBack();
|
||||
}
|
||||
: null,
|
||||
icon: isLoading
|
||||
? const Icon(Icons.close)
|
||||
: const Icon(Icons.arrow_back),
|
||||
),
|
||||
|
||||
menuControllerToClose?.close();
|
||||
}
|
||||
: null,
|
||||
icon: isLoading ? const Icon(Icons.close) : const Icon(Icons.arrow_back),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1052,30 +1019,28 @@ class AddTabButton extends HookConsumerWidget {
|
||||
|
||||
return TabCreationMenu(
|
||||
controller: tabMenuController,
|
||||
child: NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
final settings = ref.read(generalSettingsWithDefaultsProvider);
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
final settings = ref.read(generalSettingsWithDefaultsProvider);
|
||||
|
||||
await SearchRoute(
|
||||
tabType:
|
||||
ref.read(selectedTabTypeProvider) ??
|
||||
settings.defaultCreateTabType,
|
||||
).push(context);
|
||||
await SearchRoute(
|
||||
tabType:
|
||||
ref.read(selectedTabTypeProvider) ??
|
||||
settings.defaultCreateTabType,
|
||||
).push(context);
|
||||
|
||||
if (context.mounted) {
|
||||
const BrowserRoute().go(context);
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.tabPlus),
|
||||
onLongPress: () {
|
||||
if (tabMenuController.isOpen) {
|
||||
tabMenuController.close();
|
||||
} else {
|
||||
tabMenuController.open();
|
||||
}
|
||||
},
|
||||
),
|
||||
if (context.mounted) {
|
||||
const BrowserRoute().go(context);
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.tabPlus),
|
||||
onLongPress: () {
|
||||
if (tabMenuController.isOpen) {
|
||||
tabMenuController.close();
|
||||
} else {
|
||||
tabMenuController.open();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
+34
-42
@@ -152,24 +152,22 @@ class GridTabPreview extends HookConsumerWidget {
|
||||
child: Text('Close all from ${tabState.url.host}'),
|
||||
),
|
||||
],
|
||||
child: NonFocusable(
|
||||
child: IconButton(
|
||||
visualDensity: const VisualDensity(
|
||||
horizontal: -4.0,
|
||||
vertical: -4.0,
|
||||
),
|
||||
onPressed: onDelete,
|
||||
onLongPress: onDeleteAll != null
|
||||
? () {
|
||||
if (extendedDeleteMenuController.isOpen) {
|
||||
extendedDeleteMenuController.close();
|
||||
} else {
|
||||
extendedDeleteMenuController.open();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.close),
|
||||
child: IconButton(
|
||||
visualDensity: const VisualDensity(
|
||||
horizontal: -4.0,
|
||||
vertical: -4.0,
|
||||
),
|
||||
onPressed: onDelete,
|
||||
onLongPress: onDeleteAll != null
|
||||
? () {
|
||||
if (extendedDeleteMenuController.isOpen) {
|
||||
extendedDeleteMenuController.close();
|
||||
} else {
|
||||
extendedDeleteMenuController.open();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
),
|
||||
?trailingChild,
|
||||
@@ -352,20 +350,18 @@ class ListTabPreview extends HookConsumerWidget {
|
||||
child: Text('Close all from ${tabState.url.host}'),
|
||||
),
|
||||
],
|
||||
child: NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: onDelete,
|
||||
onLongPress: onDeleteAll != null
|
||||
? () {
|
||||
if (extendedDeleteMenuController.isOpen) {
|
||||
extendedDeleteMenuController.close();
|
||||
} else {
|
||||
extendedDeleteMenuController.open();
|
||||
}
|
||||
child: IconButton(
|
||||
onPressed: onDelete,
|
||||
onLongPress: onDeleteAll != null
|
||||
? () {
|
||||
if (extendedDeleteMenuController.isOpen) {
|
||||
extendedDeleteMenuController.close();
|
||||
} else {
|
||||
extendedDeleteMenuController.open();
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
),
|
||||
?trailingChild,
|
||||
@@ -631,12 +627,10 @@ class SuggestedSingleGridTabPreview extends StatelessWidget {
|
||||
tabId: tabId,
|
||||
isActive: tabId == activeTabId,
|
||||
onTap: onTap,
|
||||
trailingChild: const NonFocusable(
|
||||
child: IconButton(
|
||||
visualDensity: VisualDensity(horizontal: -4.0, vertical: -4.0),
|
||||
icon: Icon(MdiIcons.creation),
|
||||
onPressed: null,
|
||||
),
|
||||
trailingChild: const IconButton(
|
||||
visualDensity: VisualDensity(horizontal: -4.0, vertical: -4.0),
|
||||
icon: Icon(MdiIcons.creation),
|
||||
onPressed: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -665,12 +659,10 @@ class SuggestedSingleListTabPreview extends StatelessWidget {
|
||||
tabId: tabId,
|
||||
isActive: tabId == activeTabId,
|
||||
onTap: onTap,
|
||||
trailingChild: const NonFocusable(
|
||||
child: IconButton(
|
||||
visualDensity: VisualDensity(horizontal: -4.0, vertical: -4.0),
|
||||
icon: Icon(MdiIcons.creation),
|
||||
onPressed: null,
|
||||
),
|
||||
trailingChild: const IconButton(
|
||||
visualDensity: VisualDensity(horizontal: -4.0, vertical: -4.0),
|
||||
icon: Icon(MdiIcons.creation),
|
||||
onPressed: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
+79
-92
@@ -26,7 +26,6 @@ import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/c
|
||||
import 'package:weblibre/features/tor/presentation/controllers/start_tor_proxy.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||
import 'package:weblibre/presentation/widgets/non_focusable.dart';
|
||||
import 'package:weblibre/presentation/widgets/speech_to_text_button.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
@@ -83,31 +82,27 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
NonFocusable(
|
||||
child: IconButton(
|
||||
icon: const Icon(MdiIcons.tabSearch),
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: 'Search inside tabs',
|
||||
onPressed: () {
|
||||
switch (ref.read(
|
||||
tabsViewModeControllerProvider,
|
||||
)) {
|
||||
case TabsViewMode.tree:
|
||||
case TabsViewMode.list:
|
||||
break;
|
||||
case TabsViewMode.grid:
|
||||
ref
|
||||
.read(
|
||||
tabsViewModeControllerProvider.notifier,
|
||||
)
|
||||
.set(TabsViewMode.list);
|
||||
}
|
||||
IconButton(
|
||||
icon: const Icon(MdiIcons.tabSearch),
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: 'Search inside tabs',
|
||||
onPressed: () {
|
||||
switch (ref.read(tabsViewModeControllerProvider)) {
|
||||
case TabsViewMode.tree:
|
||||
case TabsViewMode.list:
|
||||
break;
|
||||
case TabsViewMode.grid:
|
||||
ref
|
||||
.read(
|
||||
tabsViewModeControllerProvider.notifier,
|
||||
)
|
||||
.set(TabsViewMode.list);
|
||||
}
|
||||
|
||||
searchMode.value = true;
|
||||
searchTextFocus.requestFocus();
|
||||
},
|
||||
),
|
||||
searchMode.value = true;
|
||||
searchTextFocus.requestFocus();
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
@@ -131,23 +126,21 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: NonFocusable(
|
||||
child: IconButton(
|
||||
tooltip: 'Change view mode',
|
||||
onPressed: () {
|
||||
if (viewModeMenuController.isOpen) {
|
||||
viewModeMenuController.close();
|
||||
} else {
|
||||
viewModeMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(tabsViewMode.icon, size: 18),
|
||||
const Icon(Icons.arrow_drop_down, size: 18),
|
||||
],
|
||||
),
|
||||
child: IconButton(
|
||||
tooltip: 'Change view mode',
|
||||
onPressed: () {
|
||||
if (viewModeMenuController.isOpen) {
|
||||
viewModeMenuController.close();
|
||||
} else {
|
||||
viewModeMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(tabsViewMode.icon, size: 18),
|
||||
const Icon(Icons.arrow_drop_down, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -174,42 +167,40 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
style: const TextStyle(fontSize: 10),
|
||||
)
|
||||
: null,
|
||||
child: NonFocusable(
|
||||
child: IconButton.filledTonal(
|
||||
icon: const Icon(MdiIcons.imageAutoAdjust),
|
||||
isSelected: tabSuggestionsEnabled,
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: downloadProgress != null
|
||||
? 'Downloading AI models (${downloadProgress.progress.toInt()}%)'
|
||||
: tabSuggestionsEnabled
|
||||
? 'Disable AI tab suggestions'
|
||||
: 'Enable AI tab suggestions',
|
||||
onPressed: () async {
|
||||
if (!tabSuggestionsEnabled) {
|
||||
final result =
|
||||
await showEnableAiTabSuggestionsDialog(
|
||||
context,
|
||||
);
|
||||
child: IconButton.filledTonal(
|
||||
icon: const Icon(MdiIcons.imageAutoAdjust),
|
||||
isSelected: tabSuggestionsEnabled,
|
||||
iconSize: 18,
|
||||
padding: EdgeInsets.zero,
|
||||
tooltip: downloadProgress != null
|
||||
? 'Downloading AI models (${downloadProgress.progress.toInt()}%)'
|
||||
: tabSuggestionsEnabled
|
||||
? 'Disable AI tab suggestions'
|
||||
: 'Enable AI tab suggestions',
|
||||
onPressed: () async {
|
||||
if (!tabSuggestionsEnabled) {
|
||||
final result =
|
||||
await showEnableAiTabSuggestionsDialog(
|
||||
context,
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.enable();
|
||||
}
|
||||
} else {
|
||||
if (result == true) {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.disable();
|
||||
.enable();
|
||||
}
|
||||
},
|
||||
),
|
||||
} else {
|
||||
ref
|
||||
.read(
|
||||
tabSuggestionsControllerProvider
|
||||
.notifier,
|
||||
)
|
||||
.disable();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -513,18 +504,16 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
],
|
||||
child: NonFocusable(
|
||||
child: IconButton(
|
||||
tooltip: 'Tab actions',
|
||||
onPressed: () {
|
||||
if (tabsActionMenuController.isOpen) {
|
||||
tabsActionMenuController.close();
|
||||
} else {
|
||||
tabsActionMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.dotsVertical),
|
||||
),
|
||||
child: IconButton(
|
||||
tooltip: 'Tab actions',
|
||||
onPressed: () {
|
||||
if (tabsActionMenuController.isOpen) {
|
||||
tabsActionMenuController.close();
|
||||
} else {
|
||||
tabsActionMenuController.open();
|
||||
}
|
||||
},
|
||||
icon: const Icon(MdiIcons.dotsVertical),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -548,15 +537,13 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
searchTextController.text = data;
|
||||
},
|
||||
),
|
||||
NonFocusable(
|
||||
child: IconButton(
|
||||
onPressed: () {
|
||||
searchTextController.clear();
|
||||
searchTextFocus.requestFocus();
|
||||
searchMode.value = false;
|
||||
},
|
||||
icon: const Icon(Icons.clear),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
searchTextController.clear();
|
||||
searchTextFocus.requestFocus();
|
||||
searchMode.value = false;
|
||||
},
|
||||
icon: const Icon(Icons.clear),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
+55
-58
@@ -26,7 +26,6 @@ import 'package:weblibre/features/geckoview/features/tabs/data/entities/containe
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
|
||||
import 'package:weblibre/presentation/widgets/non_focusable.dart';
|
||||
|
||||
class TabsActionButton extends HookConsumerWidget {
|
||||
final bool isActive;
|
||||
@@ -57,65 +56,63 @@ class TabsActionButton extends HookConsumerWidget {
|
||||
selectedContainerDataProvider.select((value) => value.value?.color),
|
||||
);
|
||||
|
||||
return NonFocusable(
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onLongPress: onLongPress,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 15.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2.0,
|
||||
color: isActive
|
||||
? theme.colorScheme.primary
|
||||
: DefaultTextStyle.of(context).style.color!,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: containerColor.mapNotNull(ContainerColors.forAppBar),
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onLongPress: onLongPress,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 15.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 2.0,
|
||||
color: isActive
|
||||
? theme.colorScheme.primary
|
||||
: DefaultTextStyle.of(context).style.color!,
|
||||
),
|
||||
constraints: const BoxConstraints(minWidth: 25.0),
|
||||
child: Center(
|
||||
child: tabCount.when(
|
||||
skipLoadingOnReload: true,
|
||||
data: (count) {
|
||||
return Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => (tabCount.hasValue)
|
||||
? Text(
|
||||
tabCount.value.toString(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
)
|
||||
: const Skeletonizer(child: Text('00')),
|
||||
error: (error, stackTrace) {
|
||||
logger.e(
|
||||
'Could not determine tab count',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: containerColor.mapNotNull(ContainerColors.forAppBar),
|
||||
),
|
||||
constraints: const BoxConstraints(minWidth: 25.0),
|
||||
child: Center(
|
||||
child: tabCount.when(
|
||||
skipLoadingOnReload: true,
|
||||
data: (count) {
|
||||
return Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
loading: () => (tabCount.hasValue)
|
||||
? Text(
|
||||
tabCount.value.toString(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
)
|
||||
: const Skeletonizer(child: Text('00')),
|
||||
error: (error, stackTrace) {
|
||||
logger.e(
|
||||
'Could not determine tab count',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
|
||||
return Text(
|
||||
'-1',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
return Text(
|
||||
'-1',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: isActive ? theme.colorScheme.primary : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A wrapper that prevents a widget from receiving keyboard focus.
|
||||
///
|
||||
/// This fixes the issue where arrow keys on a bluetooth keyboard navigate
|
||||
/// to toolbar buttons instead of moving the text cursor in input fields.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// NonFocusable(
|
||||
/// child: IconButton(
|
||||
/// onPressed: () {},
|
||||
/// icon: Icon(Icons.menu),
|
||||
/// ),
|
||||
/// )
|
||||
/// ```
|
||||
class NonFocusable extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const NonFocusable({required this.child, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Focus(canRequestFocus: false, skipTraversal: true, child: child);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user