migrate to new graph view
This commit is contained in:
@@ -25,62 +25,14 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:graphview/GraphView.dart';
|
import 'package:graphview/GraphView.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' show Vector3;
|
|
||||||
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart';
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
class BuchheimWalkerAlgorithmWithFocus extends BuchheimWalkerAlgorithm {
|
|
||||||
bool _hasScrolled = false;
|
|
||||||
|
|
||||||
final Size childSize;
|
|
||||||
final TransformationController transforamtionController;
|
|
||||||
final dynamic initialNodeId;
|
|
||||||
|
|
||||||
BuchheimWalkerAlgorithmWithFocus(
|
|
||||||
super.configuration,
|
|
||||||
super.renderer, {
|
|
||||||
required this.childSize,
|
|
||||||
required this.transforamtionController,
|
|
||||||
this.initialNodeId,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void setFocusedNode(Node node) {
|
|
||||||
final scale = transforamtionController.value.getMaxScaleOnAxis();
|
|
||||||
final newPosition =
|
|
||||||
(node.position - Offset(childSize.width / 2, childSize.height / 2)) *
|
|
||||||
scale;
|
|
||||||
|
|
||||||
transforamtionController.value = transforamtionController.value.clone()
|
|
||||||
..setTranslation(Vector3(-newPosition.dx, -newPosition.dy, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Size run(Graph? graph, double shiftX, double shiftY) {
|
|
||||||
final size = super.run(graph, shiftX, shiftY);
|
|
||||||
|
|
||||||
if (initialNodeId != null && !_hasScrolled) {
|
|
||||||
final node = graph?.nodes.firstWhereOrNull(
|
|
||||||
(element) => element.key?.value == initialNodeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (node != null && node.position != Offset.zero) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
setFocusedNode(node);
|
|
||||||
});
|
|
||||||
|
|
||||||
_hasScrolled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TabTreeDialog extends HookConsumerWidget {
|
class TabTreeDialog extends HookConsumerWidget {
|
||||||
final String tabId;
|
final String tabId;
|
||||||
final Size childSize;
|
final Size childSize;
|
||||||
@@ -93,8 +45,6 @@ class TabTreeDialog extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final transforamtionController = useTransformationController();
|
|
||||||
|
|
||||||
final selectedTabId = ref.watch(selectedTabProvider);
|
final selectedTabId = ref.watch(selectedTabProvider);
|
||||||
final tabs = ref.watch(tabDescendantsProvider(tabId));
|
final tabs = ref.watch(tabDescendantsProvider(tabId));
|
||||||
|
|
||||||
@@ -115,6 +65,8 @@ class TabTreeDialog extends HookConsumerWidget {
|
|||||||
return graph;
|
return graph;
|
||||||
}, [EquatableValue(tabs.value), selectedTabId]);
|
}, [EquatableValue(tabs.value), selectedTabId]);
|
||||||
|
|
||||||
|
final graphViewController = useMemoized(() => GraphViewController());
|
||||||
|
|
||||||
final graphAlgo = useMemoized(() {
|
final graphAlgo = useMemoized(() {
|
||||||
final config = BuchheimWalkerConfiguration()
|
final config = BuchheimWalkerConfiguration()
|
||||||
..siblingSeparation = 100
|
..siblingSeparation = 100
|
||||||
@@ -122,13 +74,7 @@ class TabTreeDialog extends HookConsumerWidget {
|
|||||||
..subtreeSeparation = 150
|
..subtreeSeparation = 150
|
||||||
..orientation = BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM;
|
..orientation = BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM;
|
||||||
|
|
||||||
return BuchheimWalkerAlgorithmWithFocus(
|
return BuchheimWalkerAlgorithm(config, TreeEdgeRenderer(config));
|
||||||
config,
|
|
||||||
TreeEdgeRenderer(config),
|
|
||||||
childSize: childSize,
|
|
||||||
transforamtionController: transforamtionController,
|
|
||||||
initialNodeId: selectedTabId,
|
|
||||||
);
|
|
||||||
}, [selectedTabId]);
|
}, [selectedTabId]);
|
||||||
|
|
||||||
return Dialog.fullscreen(
|
return Dialog.fullscreen(
|
||||||
@@ -146,57 +92,54 @@ class TabTreeDialog extends HookConsumerWidget {
|
|||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
child: const Icon(MdiIcons.target),
|
child: const Icon(MdiIcons.target),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final node = graph.nodes.firstWhereOrNull(
|
if (selectedTabId != null) {
|
||||||
(element) => element.key?.value == selectedTabId,
|
final node = graph.nodes.firstWhereOrNull(
|
||||||
);
|
(element) => element.key?.value == selectedTabId,
|
||||||
|
|
||||||
if (node != null) {
|
|
||||||
graphAlgo.setFocusedNode(node);
|
|
||||||
} else {
|
|
||||||
showErrorMessage(
|
|
||||||
context,
|
|
||||||
'The current tab is not part of this tree',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (node != null) {
|
||||||
|
graphViewController.animateToNode(ValueKey(selectedTabId));
|
||||||
|
} else {
|
||||||
|
showErrorMessage(
|
||||||
|
context,
|
||||||
|
'The current tab is not part of this tree',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
body: InteractiveViewer(
|
body: Skeletonizer(
|
||||||
transformationController: transforamtionController,
|
enabled: !tabs.hasValue || tabs.value?.isEmpty == true,
|
||||||
constrained: false,
|
child: Skeleton.replace(
|
||||||
boundaryMargin: const EdgeInsets.all(250),
|
replacement: const Bone.square(),
|
||||||
minScale: 0.1,
|
child: GraphView.builder(
|
||||||
maxScale: 5.0,
|
graph: graph,
|
||||||
child: Skeletonizer(
|
algorithm: graphAlgo,
|
||||||
enabled: !tabs.hasValue || tabs.value?.isEmpty == true,
|
controller: graphViewController,
|
||||||
child: Skeleton.replace(
|
initialNode: selectedTabId.mapNotNull((tabId) => ValueKey(tabId)),
|
||||||
replacement: const Bone.square(),
|
paint: Paint()
|
||||||
child: GraphView(
|
..color = Theme.of(context).colorScheme.outline
|
||||||
graph: graph,
|
..strokeWidth = 1
|
||||||
algorithm: graphAlgo,
|
..style = PaintingStyle.stroke,
|
||||||
paint: Paint()
|
builder: (Node node) {
|
||||||
..color = Theme.of(context).colorScheme.outline
|
final id = node.key!.value as String;
|
||||||
..strokeWidth = 1
|
return SizedBox.fromSize(
|
||||||
..style = PaintingStyle.stroke,
|
size: childSize,
|
||||||
builder: (Node node) {
|
child: SingleTabPreview(
|
||||||
final id = node.key!.value as String;
|
key: ValueKey(id),
|
||||||
return SizedBox.fromSize(
|
tabId: id,
|
||||||
size: childSize,
|
activeTabId: selectedTabId,
|
||||||
child: SingleTabPreview(
|
onClose: () {
|
||||||
key: ValueKey(id),
|
context.pop();
|
||||||
tabId: id,
|
ref
|
||||||
activeTabId: selectedTabId,
|
.read(bottomSheetControllerProvider.notifier)
|
||||||
onClose: () {
|
.requestDismiss();
|
||||||
context.pop();
|
},
|
||||||
ref
|
sourceSearchQuery: null,
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
),
|
||||||
.requestDismiss();
|
);
|
||||||
},
|
},
|
||||||
sourceSearchQuery: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user