gesture feature initial
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 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 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/core/providers/router.dart';
|
||||
import 'package:weblibre/core/routing/routes.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers/desktop_mode.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
|
||||
import 'package:weblibre/features/geckoview/features/bookmarks/domain/utils/bookmark_tree_utils.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/entities/font_size_constants.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart';
|
||||
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
|
||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/features/gestures/data/models/gesture_action.dart';
|
||||
import 'package:weblibre/features/gestures/data/models/gesture_settings.dart';
|
||||
import 'package:weblibre/features/gestures/data/models/gesture_stroke.dart';
|
||||
import 'package:weblibre/features/gestures/domain/repositories/gesture_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/presentation/dialogs/quit_browser_dialog.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:weblibre/features/web_search/domain/controllers/sandbox_capture_controller.dart';
|
||||
import 'package:weblibre/utils/exit_app.dart';
|
||||
import 'package:weblibre/utils/move_to_background.dart';
|
||||
|
||||
part 'gesture_control.g.dart';
|
||||
|
||||
/// Bridges gesture settings and recognized-gesture events to app actions.
|
||||
///
|
||||
/// On build it (1) keeps the native recognizer's [GestureConfig] in sync with
|
||||
/// the user's [GestureSettings], and (2) subscribes to recognized gestures and
|
||||
/// dispatches the bound [GestureAction] against the currently selected tab.
|
||||
///
|
||||
/// Must be kept alive (eagerly listened to from the browser view) for the
|
||||
/// lifetime of the browser so the subscription stays active.
|
||||
@Riverpod(keepAlive: true)
|
||||
class GestureControlService extends _$GestureControlService {
|
||||
/// Timestamp (ms since epoch) of the last fired gesture, for cooldown.
|
||||
int _lastDispatchMs = 0;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
final service = ref.read(gestureServiceProvider);
|
||||
|
||||
// Keep the native recognizer in sync with the effective configuration
|
||||
// (settings folded together with the current site's exclusion state).
|
||||
ref.listen(
|
||||
fireImmediately: true,
|
||||
gestureNativeConfigProvider,
|
||||
(previous, next) async {
|
||||
await service.setGestureConfig(next);
|
||||
},
|
||||
onError: (error, stackTrace) {
|
||||
logger.e(
|
||||
'Error syncing gesture configuration',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
final subscription = service.recognizedGestures.listen(
|
||||
(gestureKey) {
|
||||
unawaited(_dispatch(gestureKey));
|
||||
},
|
||||
onError: (Object error, StackTrace stackTrace) {
|
||||
logger.e(
|
||||
'Error handling recognized gesture',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
},
|
||||
);
|
||||
ref.onDispose(subscription.cancel);
|
||||
}
|
||||
|
||||
Future<void> _dispatch(String gestureKey) async {
|
||||
final settings = ref.read(gestureSettingsWithDefaultsProvider);
|
||||
final action = settings.bindings[gestureKey];
|
||||
if (action == null) return;
|
||||
|
||||
// Cooldown: ignore gestures fired within intervalMs of the previous one.
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
if (settings.intervalMs > 0 &&
|
||||
now - _lastDispatchMs < settings.intervalMs) {
|
||||
return;
|
||||
}
|
||||
_lastDispatchMs = now;
|
||||
|
||||
final tabId = ref.read(selectedTabProvider);
|
||||
if (tabId == null) return;
|
||||
|
||||
try {
|
||||
// Feedback is handled live by the gesture overlay while the stroke is
|
||||
// drawn (driven by the native progress events), so nothing to show here.
|
||||
await _execute(action, tabId);
|
||||
} catch (error, stackTrace) {
|
||||
logger.e(
|
||||
'Error executing gesture action ${action.name}',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _execute(GestureAction action, String tabId) async {
|
||||
final tabRepository = ref.read(tabRepositoryProvider.notifier);
|
||||
|
||||
switch (action) {
|
||||
case GestureAction.back:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).goBack();
|
||||
case GestureAction.forward:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).goForward();
|
||||
case GestureAction.reload:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).reload();
|
||||
case GestureAction.scrollTop:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).scrollToTop();
|
||||
case GestureAction.scrollBottom:
|
||||
await ref
|
||||
.read(tabSessionProvider(tabId: tabId).notifier)
|
||||
.scrollToBottom();
|
||||
case GestureAction.pageUp:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).pageUp();
|
||||
case GestureAction.pageDown:
|
||||
await ref.read(tabSessionProvider(tabId: tabId).notifier).pageDown();
|
||||
case GestureAction.newTab:
|
||||
await _openNewTab(tabId);
|
||||
case GestureAction.closeTab:
|
||||
await tabRepository.closeTab(tabId);
|
||||
case GestureAction.duplicateTab:
|
||||
final containerData = await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.getTabContainerData(tabId);
|
||||
await tabRepository.duplicateTab(
|
||||
selectTabId: tabId,
|
||||
containerData: containerData,
|
||||
selectTab: true,
|
||||
);
|
||||
case GestureAction.nextTab:
|
||||
await tabRepository.selectNextTab(tabId);
|
||||
case GestureAction.previousTab:
|
||||
await tabRepository.selectPreviousTab(tabId);
|
||||
case GestureAction.lastUsedTab:
|
||||
await tabRepository.selectPreviouslyOpenedTab(tabId);
|
||||
case GestureAction.togglePinTab:
|
||||
final pinned =
|
||||
ref.read(watchPinnedTabIdsProvider).value?.contains(tabId) ?? false;
|
||||
await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.setPinned(tabId, pinned: !pinned);
|
||||
case GestureAction.toggleReaderMode:
|
||||
final readerActive =
|
||||
ref.read(tabStateProvider(tabId))?.readerableState.active ?? false;
|
||||
await ref
|
||||
.read(readerableScreenControllerProvider.notifier)
|
||||
.toggleReaderView(!readerActive);
|
||||
case GestureAction.toggleDesktopMode:
|
||||
ref.read(desktopModeProvider(tabId).notifier).toggle();
|
||||
case GestureAction.findInPage:
|
||||
ref.read(findInPageControllerProvider(tabId).notifier).show();
|
||||
case GestureAction.increaseFontSize:
|
||||
await _adjustFontSize(increase: true);
|
||||
case GestureAction.decreaseFontSize:
|
||||
await _adjustFontSize(increase: false);
|
||||
case GestureAction.showHistory:
|
||||
await _pushLocation(const HistoryRoute().location);
|
||||
case GestureAction.showBookmarks:
|
||||
await _pushLocation(
|
||||
BookmarkListRoute(entryGuid: BookmarkRoot.root.id).location,
|
||||
);
|
||||
case GestureAction.toggleBookmark:
|
||||
await _toggleBookmark(tabId);
|
||||
case GestureAction.translatePage:
|
||||
final context = await _navigatorContext();
|
||||
if (context != null && context.mounted) {
|
||||
await showTranslationBottomSheet(context, selectedTabId: tabId);
|
||||
}
|
||||
case GestureAction.moveToBackground:
|
||||
await moveToBackground();
|
||||
case GestureAction.quitBrowser:
|
||||
final context = await _navigatorContext();
|
||||
if (context != null && context.mounted) {
|
||||
final confirmed = await showQuitBrowserDialog(context);
|
||||
if (confirmed == true && context.mounted) {
|
||||
await exitApp(ProviderScope.containerOf(context, listen: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the current page to bookmarks, or removes it if already bookmarked,
|
||||
/// mirroring the contextual toolbar's bookmark toggle button.
|
||||
Future<void> _toggleBookmark(String tabId) async {
|
||||
final tabState = ref.read(tabStateProvider(tabId));
|
||||
if (tabState == null) return;
|
||||
|
||||
final bookmarkUrl =
|
||||
ref.read(sandboxSourceUriForTabProvider(tabId: tabId)) ?? tabState.url;
|
||||
|
||||
final bookmarks = ref.read(bookmarksRepositoryProvider).value;
|
||||
final existingGuids = bookmarkGuidsForUrl(bookmarks, bookmarkUrl);
|
||||
|
||||
final repository = ref.read(bookmarksRepositoryProvider.notifier);
|
||||
if (existingGuids.isNotEmpty) {
|
||||
for (final guid in existingGuids) {
|
||||
await repository.delete(guid);
|
||||
}
|
||||
} else {
|
||||
await repository.addBookmark(
|
||||
parentGuid: BookmarkRoot.mobile.id,
|
||||
url: bookmarkUrl,
|
||||
title: tabState.titleOrAuthority,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the root navigator's [BuildContext] for actions that open a
|
||||
/// dialog/sheet. Null when the navigator is not currently mounted.
|
||||
Future<BuildContext?> _navigatorContext() async {
|
||||
final router = await ref.read(routerProvider.future);
|
||||
if (!ref.mounted) return null;
|
||||
return router.routerDelegate.navigatorKey.currentContext;
|
||||
}
|
||||
|
||||
Future<void> _pushLocation(String location) async {
|
||||
final router = await ref.read(routerProvider.future);
|
||||
if (!ref.mounted) return;
|
||||
await router.push(location);
|
||||
}
|
||||
|
||||
Future<void> _adjustFontSize({required bool increase}) async {
|
||||
final settings = ref.read(engineSettingsWithDefaultsProvider);
|
||||
|
||||
// Manual adjustment is a no-op while automatic font sizing is enabled.
|
||||
if (settings.automaticFontSizeAdjustment) return;
|
||||
|
||||
final current = settings.fontSizeFactor;
|
||||
final newValue = increase
|
||||
? (current + fontSizeStep).clamp(fontSizeMin, fontSizeMax)
|
||||
: (current - fontSizeStep).clamp(fontSizeMin, fontSizeMax);
|
||||
final rounded = (newValue * 10).round() / 10;
|
||||
if (rounded == current) return;
|
||||
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith.fontSizeFactor(rounded),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openNewTab(String tabId) async {
|
||||
final router = await ref.read(routerProvider.future);
|
||||
if (!ref.mounted) return;
|
||||
|
||||
final settings = ref.read(generalSettingsWithDefaultsProvider);
|
||||
final selectedTabType = ref
|
||||
.read(tabStatesProvider)[tabId]
|
||||
?.tabMode
|
||||
.toTabType();
|
||||
|
||||
final route = SearchRoute(
|
||||
tabType: selectedTabType ?? settings.effectiveDefaultCreateTabType,
|
||||
);
|
||||
await router.push(route.location);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether gestures are currently disabled because the selected tab's site is
|
||||
/// on the user's exclusion list.
|
||||
@Riverpod(keepAlive: true)
|
||||
bool gestureSiteExcluded(Ref ref) {
|
||||
final excludedSites = ref.watch(
|
||||
gestureSettingsWithDefaultsProvider.select((s) => s.excludedSites),
|
||||
);
|
||||
if (excludedSites.isEmpty) return false;
|
||||
|
||||
final tabId = ref.watch(selectedTabProvider);
|
||||
if (tabId == null) return false;
|
||||
|
||||
final url = ref.watch(tabStateProvider(tabId).select((state) => state?.url));
|
||||
if (url == null) return false;
|
||||
|
||||
return isGestureSiteExcluded(url, excludedSites);
|
||||
}
|
||||
|
||||
/// The effective native recognizer configuration: the user's settings with the
|
||||
/// recognizer disabled while the current site is excluded.
|
||||
@Riverpod(keepAlive: true)
|
||||
GestureConfig gestureNativeConfig(Ref ref) {
|
||||
final settings = ref.watch(gestureSettingsWithDefaultsProvider);
|
||||
final excluded = ref.watch(gestureSiteExcludedProvider);
|
||||
|
||||
// Recognize as many fingers as any bound gesture requires, so multi-finger
|
||||
// bindings work without a separate finger setting.
|
||||
final requiredFingers = settings.bindings.keys
|
||||
.map((key) => GestureStroke.fromKey(key).fingers)
|
||||
.fold(settings.maxFingers, max);
|
||||
|
||||
return GestureConfig(
|
||||
enabled: settings.effectiveEnabled && !excluded,
|
||||
strokeSize: settings.strokeSize,
|
||||
timeoutMs: settings.timeoutMs,
|
||||
maxFingers: requiredFingers,
|
||||
activeGestureKeys: settings.bindings.keys.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// The in-progress stroke shown by the live feedback overlay.
|
||||
///
|
||||
/// Emits the current partial canonical key (e.g. `R:D`) while a stroke is being
|
||||
/// drawn, and null when nothing should be shown — driven by the native gesture
|
||||
/// progress/reset events.
|
||||
@riverpod
|
||||
Stream<String?> gestureProgress(Ref ref) {
|
||||
return ref.watch(gestureServiceProvider).gestureProgress;
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'gesture_control.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
/// Bridges gesture settings and recognized-gesture events to app actions.
|
||||
///
|
||||
/// On build it (1) keeps the native recognizer's [GestureConfig] in sync with
|
||||
/// the user's [GestureSettings], and (2) subscribes to recognized gestures and
|
||||
/// dispatches the bound [GestureAction] against the currently selected tab.
|
||||
///
|
||||
/// Must be kept alive (eagerly listened to from the browser view) for the
|
||||
/// lifetime of the browser so the subscription stays active.
|
||||
|
||||
@ProviderFor(GestureControlService)
|
||||
final gestureControlServiceProvider = GestureControlServiceProvider._();
|
||||
|
||||
/// Bridges gesture settings and recognized-gesture events to app actions.
|
||||
///
|
||||
/// On build it (1) keeps the native recognizer's [GestureConfig] in sync with
|
||||
/// the user's [GestureSettings], and (2) subscribes to recognized gestures and
|
||||
/// dispatches the bound [GestureAction] against the currently selected tab.
|
||||
///
|
||||
/// Must be kept alive (eagerly listened to from the browser view) for the
|
||||
/// lifetime of the browser so the subscription stays active.
|
||||
final class GestureControlServiceProvider
|
||||
extends $NotifierProvider<GestureControlService, void> {
|
||||
/// Bridges gesture settings and recognized-gesture events to app actions.
|
||||
///
|
||||
/// On build it (1) keeps the native recognizer's [GestureConfig] in sync with
|
||||
/// the user's [GestureSettings], and (2) subscribes to recognized gestures and
|
||||
/// dispatches the bound [GestureAction] against the currently selected tab.
|
||||
///
|
||||
/// Must be kept alive (eagerly listened to from the browser view) for the
|
||||
/// lifetime of the browser so the subscription stays active.
|
||||
GestureControlServiceProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'gestureControlServiceProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$gestureControlServiceHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
GestureControlService create() => GestureControlService();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(void value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<void>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$gestureControlServiceHash() =>
|
||||
r'12bd852a5b90b67bee4a94e7bd55fccc53c11bd4';
|
||||
|
||||
/// Bridges gesture settings and recognized-gesture events to app actions.
|
||||
///
|
||||
/// On build it (1) keeps the native recognizer's [GestureConfig] in sync with
|
||||
/// the user's [GestureSettings], and (2) subscribes to recognized gestures and
|
||||
/// dispatches the bound [GestureAction] against the currently selected tab.
|
||||
///
|
||||
/// Must be kept alive (eagerly listened to from the browser view) for the
|
||||
/// lifetime of the browser so the subscription stays active.
|
||||
|
||||
abstract class _$GestureControlService extends $Notifier<void> {
|
||||
void build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<void, void>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<void, void>,
|
||||
void,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether gestures are currently disabled because the selected tab's site is
|
||||
/// on the user's exclusion list.
|
||||
|
||||
@ProviderFor(gestureSiteExcluded)
|
||||
final gestureSiteExcludedProvider = GestureSiteExcludedProvider._();
|
||||
|
||||
/// Whether gestures are currently disabled because the selected tab's site is
|
||||
/// on the user's exclusion list.
|
||||
|
||||
final class GestureSiteExcludedProvider
|
||||
extends $FunctionalProvider<bool, bool, bool>
|
||||
with $Provider<bool> {
|
||||
/// Whether gestures are currently disabled because the selected tab's site is
|
||||
/// on the user's exclusion list.
|
||||
GestureSiteExcludedProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'gestureSiteExcludedProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$gestureSiteExcludedHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<bool> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
bool create(Ref ref) {
|
||||
return gestureSiteExcluded(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$gestureSiteExcludedHash() =>
|
||||
r'15dd371a176303a7c52c37df560c93bbb03c5039';
|
||||
|
||||
/// The effective native recognizer configuration: the user's settings with the
|
||||
/// recognizer disabled while the current site is excluded.
|
||||
|
||||
@ProviderFor(gestureNativeConfig)
|
||||
final gestureNativeConfigProvider = GestureNativeConfigProvider._();
|
||||
|
||||
/// The effective native recognizer configuration: the user's settings with the
|
||||
/// recognizer disabled while the current site is excluded.
|
||||
|
||||
final class GestureNativeConfigProvider
|
||||
extends $FunctionalProvider<GestureConfig, GestureConfig, GestureConfig>
|
||||
with $Provider<GestureConfig> {
|
||||
/// The effective native recognizer configuration: the user's settings with the
|
||||
/// recognizer disabled while the current site is excluded.
|
||||
GestureNativeConfigProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'gestureNativeConfigProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$gestureNativeConfigHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<GestureConfig> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
GestureConfig create(Ref ref) {
|
||||
return gestureNativeConfig(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(GestureConfig value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<GestureConfig>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$gestureNativeConfigHash() =>
|
||||
r'57aada8631dfc3d8355f9498f6b7c6954b0cdcf1';
|
||||
|
||||
/// The in-progress stroke shown by the live feedback overlay.
|
||||
///
|
||||
/// Emits the current partial canonical key (e.g. `R:D`) while a stroke is being
|
||||
/// drawn, and null when nothing should be shown — driven by the native gesture
|
||||
/// progress/reset events.
|
||||
|
||||
@ProviderFor(gestureProgress)
|
||||
final gestureProgressProvider = GestureProgressProvider._();
|
||||
|
||||
/// The in-progress stroke shown by the live feedback overlay.
|
||||
///
|
||||
/// Emits the current partial canonical key (e.g. `R:D`) while a stroke is being
|
||||
/// drawn, and null when nothing should be shown — driven by the native gesture
|
||||
/// progress/reset events.
|
||||
|
||||
final class GestureProgressProvider
|
||||
extends $FunctionalProvider<AsyncValue<String?>, String?, Stream<String?>>
|
||||
with $FutureModifier<String?>, $StreamProvider<String?> {
|
||||
/// The in-progress stroke shown by the live feedback overlay.
|
||||
///
|
||||
/// Emits the current partial canonical key (e.g. `R:D`) while a stroke is being
|
||||
/// drawn, and null when nothing should be shown — driven by the native gesture
|
||||
/// progress/reset events.
|
||||
GestureProgressProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'gestureProgressProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$gestureProgressHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$StreamProviderElement<String?> $createElement($ProviderPointer pointer) =>
|
||||
$StreamProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Stream<String?> create(Ref ref) {
|
||||
return gestureProgress(ref);
|
||||
}
|
||||
}
|
||||
|
||||
String _$gestureProgressHash() => r'2f3ed4a8db41d317869db2e5aa71e737afb5364b';
|
||||
Reference in New Issue
Block a user