@@ -5,6 +5,7 @@ import '../graph/cursor_theme.dart';
55import '../graph/node_flow_controller.dart' ;
66import '../graph/node_flow_theme.dart' ;
77import '../shared/resizer_widget.dart' ;
8+ import '../shared/unbounded_widgets.dart' ;
89import 'group_annotation.dart' ;
910
1011/// Widget that renders a group annotation with resize handles when selected.
@@ -57,52 +58,57 @@ class GroupAnnotationWidget extends StatelessWidget {
5758 isInteractive: group.isInteractive,
5859 );
5960
61+ // Use UnboundedStack to allow hit testing on resize handles
62+ // that extend outside the annotation bounds
6063 return Positioned (
6164 left: position.dx,
6265 top: position.dy,
63- child: MouseRegion (
64- cursor: cursor,
65- child: GestureDetector (
66- behavior: HitTestBehavior .opaque,
67- onDoubleTap: onDoubleTap,
68- onSecondaryTapUp: onContextMenu != null
69- ? (details) => onContextMenu !(details.globalPosition)
70- : null ,
71- onPanStart: (_) => controller.startAnnotationDrag (group.id),
72- onPanUpdate: (details) =>
73- controller.moveAnnotationDrag (details.delta),
74- onPanEnd: (_) => controller.endAnnotationDrag (),
75- child: SizedBox (
76- width: groupSize.width,
77- height: groupSize.height,
78- child: isSelected && group.isResizable
79- ? ResizerWidget (
80- handleSize: theme.resizerTheme.handleSize,
81- color: theme.resizerTheme.color,
82- borderColor: theme.resizerTheme.borderColor,
83- borderWidth: theme.resizerTheme.borderWidth,
84- snapDistance: theme.resizerTheme.snapDistance,
85- onResizeStart: (handle) => controller.annotations
86- .startGroupResize (group.id, _toGroupHandle (handle)),
87- onResizeUpdate: (delta) =>
88- controller.annotations.updateGroupResize (delta),
89- onResizeEnd: () =>
90- controller.annotations.endGroupResize (),
91- child: _GroupContent (
92- group: group,
93- controller: controller,
94- isSelected: isSelected,
95- isHighlighted: isHighlighted,
96- ),
97- )
98- : _GroupContent (
99- group: group,
100- controller: controller,
101- isSelected: isSelected,
102- isHighlighted: isHighlighted,
103- ),
66+ width: groupSize.width,
67+ height: groupSize.height,
68+ child: UnboundedStack (
69+ clipBehavior: Clip .none,
70+ children: [
71+ // The annotation content
72+ Positioned .fill (
73+ child: MouseRegion (
74+ cursor: cursor,
75+ child: GestureDetector (
76+ behavior: HitTestBehavior .opaque,
77+ onDoubleTap: onDoubleTap,
78+ onSecondaryTapUp: onContextMenu != null
79+ ? (details) => onContextMenu !(details.globalPosition)
80+ : null ,
81+ onPanStart: (_) => controller.startAnnotationDrag (group.id),
82+ onPanUpdate: (details) =>
83+ controller.moveAnnotationDrag (details.delta),
84+ onPanEnd: (_) => controller.endAnnotationDrag (),
85+ child: _GroupContent (
86+ group: group,
87+ controller: controller,
88+ isSelected: isSelected,
89+ isHighlighted: isHighlighted,
90+ ),
91+ ),
92+ ),
10493 ),
105- ),
94+ // Resize handles as overlay (only when selected and resizable)
95+ if (isSelected && group.isResizable)
96+ Positioned .fill (
97+ child: ResizerWidget (
98+ handleSize: theme.resizerTheme.handleSize,
99+ color: theme.resizerTheme.color,
100+ borderColor: theme.resizerTheme.borderColor,
101+ borderWidth: theme.resizerTheme.borderWidth,
102+ snapDistance: theme.resizerTheme.snapDistance,
103+ onResizeStart: (handle) => controller.annotations
104+ .startGroupResize (group.id, _toGroupHandle (handle)),
105+ onResizeUpdate: (delta) =>
106+ controller.annotations.updateGroupResize (delta),
107+ onResizeEnd: () => controller.annotations.endGroupResize (),
108+ child: const SizedBox .expand (),
109+ ),
110+ ),
111+ ],
106112 ),
107113 );
108114 },
0 commit comments