Skip to content

Commit 47f6c2e

Browse files
committed
Refactor Placement enum and update positioning strategy
Moved the Placement enum from geometry.dart to a new placement.dart file and updated all imports accordingly. Renamed positioning_strategy.dart for clarity and added support for positioning relative to a Window in PositioningStrategy, including dynamic bounds retrieval. Updated context menu region logic to use the new relativeToWindow method for menu positioning.
1 parent 7db3099 commit 47f6c2e

8 files changed

Lines changed: 147 additions & 99 deletions

File tree

packages/cnativeapi/macos/Classes/cnativeapi.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
#include "../../src/libnativeapi/src/application.cpp"
3939
#include "../../src/libnativeapi/src/display_manager.cpp"
4040
#include "../../src/libnativeapi/src/foundation/id_allocator.cpp"
41-
#include "../../src/libnativeapi/src/foundation/positioning_strategy.cpp"
4241
#include "../../src/libnativeapi/src/menu.cpp"
42+
#include "../../src/libnativeapi/src/positioning_strategy.cpp"
4343
#include "../../src/libnativeapi/src/preferences.cpp"
4444
#include "../../src/libnativeapi/src/secure_storage.cpp"
4545
#include "../../src/libnativeapi/src/tray_manager.cpp"

packages/nativeapi/lib/nativeapi.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export 'src/display_manager.dart';
77
export 'src/foundation/event.dart';
88
export 'src/foundation/event_emitter.dart';
99
export 'src/foundation/geometry.dart';
10-
export 'src/foundation/positioning_strategy.dart';
10+
export 'src/placement.dart';
11+
export 'src/positioning_strategy.dart';
1112
export 'src/foundation/storage.dart';
1213
export 'src/image.dart';
1314
export 'src/menu.dart';
Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1 @@
11
export 'dart:ui' show Offset, Size, Rect;
2-
3-
import 'package:cnativeapi/cnativeapi.dart' show native_placement_t;
4-
5-
/// Placement options for positioning UI elements relative to an anchor.
6-
///
7-
/// Placement defines how a UI element (such as a menu or popover) should be
8-
/// positioned relative to a reference point or rectangle.
9-
///
10-
/// Example:
11-
/// ```dart
12-
/// // Position menu below the anchor, horizontally centered
13-
/// menu.open(strategy, Placement.bottom);
14-
///
15-
/// // Position menu below the anchor, aligned to the left
16-
/// menu.open(strategy, Placement.bottomStart);
17-
///
18-
/// // Position menu above the anchor, aligned to the right
19-
/// menu.open(strategy, Placement.topEnd);
20-
/// ```
21-
enum Placement {
22-
/// Position above the anchor, horizontally centered.
23-
top,
24-
25-
/// Position above the anchor, aligned to the start (left).
26-
topStart,
27-
28-
/// Position above the anchor, aligned to the end (right).
29-
topEnd,
30-
31-
/// Position to the right of the anchor, vertically centered.
32-
right,
33-
34-
/// Position to the right of the anchor, aligned to the start (top).
35-
rightStart,
36-
37-
/// Position to the right of the anchor, aligned to the end (bottom).
38-
rightEnd,
39-
40-
/// Position below the anchor, horizontally centered.
41-
bottom,
42-
43-
/// Position below the anchor, aligned to the start (left).
44-
bottomStart,
45-
46-
/// Position below the anchor, aligned to the end (right).
47-
bottomEnd,
48-
49-
/// Position to the left of the anchor, vertically centered.
50-
left,
51-
52-
/// Position to the left of the anchor, aligned to the start (top).
53-
leftStart,
54-
55-
/// Position to the left of the anchor, aligned to the end (bottom).
56-
leftEnd;
57-
58-
/// Convert this Placement to a native placement enum value.
59-
native_placement_t toNative() {
60-
switch (this) {
61-
case Placement.top:
62-
return native_placement_t.NATIVE_PLACEMENT_TOP;
63-
case Placement.topStart:
64-
return native_placement_t.NATIVE_PLACEMENT_TOP_START;
65-
case Placement.topEnd:
66-
return native_placement_t.NATIVE_PLACEMENT_TOP_END;
67-
case Placement.right:
68-
return native_placement_t.NATIVE_PLACEMENT_RIGHT;
69-
case Placement.rightStart:
70-
return native_placement_t.NATIVE_PLACEMENT_RIGHT_START;
71-
case Placement.rightEnd:
72-
return native_placement_t.NATIVE_PLACEMENT_RIGHT_END;
73-
case Placement.bottom:
74-
return native_placement_t.NATIVE_PLACEMENT_BOTTOM;
75-
case Placement.bottomStart:
76-
return native_placement_t.NATIVE_PLACEMENT_BOTTOM_START;
77-
case Placement.bottomEnd:
78-
return native_placement_t.NATIVE_PLACEMENT_BOTTOM_END;
79-
case Placement.left:
80-
return native_placement_t.NATIVE_PLACEMENT_LEFT;
81-
case Placement.leftStart:
82-
return native_placement_t.NATIVE_PLACEMENT_LEFT_START;
83-
case Placement.leftEnd:
84-
return native_placement_t.NATIVE_PLACEMENT_LEFT_END;
85-
}
86-
}
87-
}

packages/nativeapi/lib/src/menu.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import 'dart:ffi';
33
import 'package:ffi/ffi.dart' as ffi;
44
import 'package:nativeapi/src/foundation/cnativeapi_bindings_mixin.dart';
55
import 'package:nativeapi/src/foundation/event_emitter.dart';
6-
import 'package:nativeapi/src/foundation/geometry.dart';
76
import 'package:nativeapi/src/foundation/native_handle_wrapper.dart';
8-
import 'package:nativeapi/src/foundation/positioning_strategy.dart';
7+
import 'package:nativeapi/src/placement.dart';
8+
import 'package:nativeapi/src/positioning_strategy.dart';
99
import 'package:nativeapi/src/image.dart';
1010
import 'package:nativeapi/src/menu_event.dart';
1111

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import 'package:cnativeapi/cnativeapi.dart' show native_placement_t;
2+
3+
/// Placement options for positioning UI elements relative to an anchor.
4+
///
5+
/// Placement defines how a UI element (such as a menu or popover) should be
6+
/// positioned relative to a reference point or rectangle.
7+
///
8+
/// Example:
9+
/// ```dart
10+
/// // Position menu below the anchor, horizontally centered
11+
/// menu.open(strategy, Placement.bottom);
12+
///
13+
/// // Position menu below the anchor, aligned to the left
14+
/// menu.open(strategy, Placement.bottomStart);
15+
///
16+
/// // Position menu above the anchor, aligned to the right
17+
/// menu.open(strategy, Placement.topEnd);
18+
/// ```
19+
enum Placement {
20+
/// Position above the anchor, horizontally centered.
21+
top,
22+
23+
/// Position above the anchor, aligned to the start (left).
24+
topStart,
25+
26+
/// Position above the anchor, aligned to the end (right).
27+
topEnd,
28+
29+
/// Position to the right of the anchor, vertically centered.
30+
right,
31+
32+
/// Position to the right of the anchor, aligned to the start (top).
33+
rightStart,
34+
35+
/// Position to the right of the anchor, aligned to the end (bottom).
36+
rightEnd,
37+
38+
/// Position below the anchor, horizontally centered.
39+
bottom,
40+
41+
/// Position below the anchor, aligned to the start (left).
42+
bottomStart,
43+
44+
/// Position below the anchor, aligned to the end (right).
45+
bottomEnd,
46+
47+
/// Position to the left of the anchor, vertically centered.
48+
left,
49+
50+
/// Position to the left of the anchor, aligned to the start (top).
51+
leftStart,
52+
53+
/// Position to the left of the anchor, aligned to the end (bottom).
54+
leftEnd;
55+
56+
/// Convert this Placement to a native placement enum value.
57+
native_placement_t toNative() {
58+
switch (this) {
59+
case Placement.top:
60+
return native_placement_t.NATIVE_PLACEMENT_TOP;
61+
case Placement.topStart:
62+
return native_placement_t.NATIVE_PLACEMENT_TOP_START;
63+
case Placement.topEnd:
64+
return native_placement_t.NATIVE_PLACEMENT_TOP_END;
65+
case Placement.right:
66+
return native_placement_t.NATIVE_PLACEMENT_RIGHT;
67+
case Placement.rightStart:
68+
return native_placement_t.NATIVE_PLACEMENT_RIGHT_START;
69+
case Placement.rightEnd:
70+
return native_placement_t.NATIVE_PLACEMENT_RIGHT_END;
71+
case Placement.bottom:
72+
return native_placement_t.NATIVE_PLACEMENT_BOTTOM;
73+
case Placement.bottomStart:
74+
return native_placement_t.NATIVE_PLACEMENT_BOTTOM_START;
75+
case Placement.bottomEnd:
76+
return native_placement_t.NATIVE_PLACEMENT_BOTTOM_END;
77+
case Placement.left:
78+
return native_placement_t.NATIVE_PLACEMENT_LEFT;
79+
case Placement.leftStart:
80+
return native_placement_t.NATIVE_PLACEMENT_LEFT_START;
81+
case Placement.leftEnd:
82+
return native_placement_t.NATIVE_PLACEMENT_LEFT_END;
83+
}
84+
}
85+
}

packages/nativeapi/lib/src/foundation/positioning_strategy.dart renamed to packages/nativeapi/lib/src/positioning_strategy.dart

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:ffi';
33
import 'package:ffi/ffi.dart' as ffi;
44
import 'package:nativeapi/src/foundation/cnativeapi_bindings_mixin.dart';
55
import 'package:nativeapi/src/foundation/geometry.dart';
6+
import 'package:nativeapi/src/window.dart';
67

78
/// Type of positioning strategy.
89
enum PositioningStrategyType {
@@ -22,7 +23,7 @@ enum PositioningStrategyType {
2223
/// such as menus, tooltips, or popovers. It supports various positioning modes:
2324
/// - Absolute: Fixed screen coordinates
2425
/// - CursorPosition: Current mouse cursor position
25-
/// - Relative: Position relative to a rectangle
26+
/// - Relative: Position relative to a rectangle or window
2627
///
2728
/// Example:
2829
/// ```dart
@@ -35,22 +36,29 @@ enum PositioningStrategyType {
3536
/// // Position menu relative to a rectangle with offset
3637
/// final buttonRect = Rect.fromLTWH(10, 10, 100, 30);
3738
/// menu.open(PositioningStrategy.relative(buttonRect, Offset(0, 10)));
39+
///
40+
/// // Position menu relative to a window with offset
41+
/// final window = WindowManager.instance.create(options);
42+
/// menu.open(PositioningStrategy.relativeToWindow(window, Offset(0, 10)));
3843
/// ```
3944
class PositioningStrategy with CNativeApiBindingsMixin {
4045
final PositioningStrategyType _type;
4146
final Offset? _absolutePosition;
4247
final Rect? _relativeRectangle;
4348
final Offset? _relativeOffset;
49+
final Window? _relativeWindow;
4450

4551
PositioningStrategy._({
4652
required PositioningStrategyType type,
4753
Offset? absolutePosition,
4854
Rect? relativeRectangle,
4955
Offset? relativeOffset,
56+
Window? relativeWindow,
5057
}) : _type = type,
5158
_absolutePosition = absolutePosition,
5259
_relativeRectangle = relativeRectangle,
53-
_relativeOffset = relativeOffset;
60+
_relativeOffset = relativeOffset,
61+
_relativeWindow = relativeWindow;
5462

5563
/// Create a strategy for absolute positioning at fixed coordinates.
5664
///
@@ -101,6 +109,34 @@ class PositioningStrategy with CNativeApiBindingsMixin {
101109
);
102110
}
103111

112+
/// Create a strategy for positioning relative to a window.
113+
///
114+
/// This method stores a reference to the window and will obtain its bounds
115+
/// dynamically when [relativeRectangle] is accessed, ensuring the position
116+
/// reflects the window's current state.
117+
///
118+
/// Example:
119+
/// ```dart
120+
/// final window = WindowManager.instance.create(options);
121+
/// // Position menu at bottom of window (no offset)
122+
/// final strategy = PositioningStrategy.relativeToWindow(window);
123+
/// menu.open(strategy);
124+
///
125+
/// // Position menu at bottom of window with 10px vertical offset
126+
/// final strategy2 = PositioningStrategy.relativeToWindow(window, Offset(0, 10));
127+
/// menu.open(strategy2);
128+
/// ```
129+
factory PositioningStrategy.relativeToWindow(
130+
Window window, [
131+
Offset offset = Offset.zero,
132+
]) {
133+
return PositioningStrategy._(
134+
type: PositioningStrategyType.relative,
135+
relativeWindow: window,
136+
relativeOffset: offset,
137+
);
138+
}
139+
104140
/// Get the type of this positioning strategy.
105141
PositioningStrategyType get type => _type;
106142

@@ -112,13 +148,25 @@ class PositioningStrategy with CNativeApiBindingsMixin {
112148
/// Get the relative rectangle (for Relative type).
113149
///
114150
/// Only valid when type == PositioningStrategyType.relative
115-
Rect? get relativeRectangle => _relativeRectangle;
151+
/// If the strategy was created with a Window, this will return the
152+
/// window's current bounds (obtained dynamically).
153+
Rect? get relativeRectangle {
154+
if (_relativeWindow != null) {
155+
return _relativeWindow!.bounds;
156+
}
157+
return _relativeRectangle;
158+
}
116159

117160
/// Get the relative offset point (for Relative type).
118161
///
119162
/// Only valid when type == PositioningStrategyType.relative
120163
Offset? get relativeOffset => _relativeOffset;
121164

165+
/// Get the relative window (for Relative type created with Window).
166+
///
167+
/// Only valid when type == PositioningStrategyType.relative and strategy was created with a Window
168+
Window? get relativeWindow => _relativeWindow;
169+
122170
/// Convert this strategy to a native positioning strategy handle.
123171
///
124172
/// The caller is responsible for freeing the returned handle using
@@ -147,7 +195,7 @@ class PositioningStrategy with CNativeApiBindingsMixin {
147195
return bindings.native_positioning_strategy_cursor_position();
148196

149197
case PositioningStrategyType.relative:
150-
final rect = _relativeRectangle;
198+
final rect = relativeRectangle; // This will get window bounds if needed
151199
if (rect == null) {
152200
throw StateError(
153201
'Relative rectangle is required for relative strategy',

packages/nativeapi/lib/src/widgets/context_menu_region.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:flutter/gestures.dart';
22
import 'package:flutter/widgets.dart';
3-
import 'package:nativeapi/src/foundation/geometry.dart' show Placement;
4-
import 'package:nativeapi/src/foundation/positioning_strategy.dart';
3+
import 'package:nativeapi/src/placement.dart';
4+
import 'package:nativeapi/src/positioning_strategy.dart';
55
import 'package:nativeapi/src/menu.dart';
66
import 'package:nativeapi/src/window.dart';
77
import 'package:nativeapi/src/window_manager.dart';
@@ -80,8 +80,8 @@ class _ContextMenuRegionState extends State<ContextMenuRegion> {
8080

8181
// Open the menu at the click position using relative positioning
8282
widget.menu.open(
83-
PositioningStrategy.relative(
84-
_activeWindow!.contentBounds,
83+
PositioningStrategy.relativeToWindow(
84+
_activeWindow!,
8585
Offset(event.position.dx, event.position.dy),
8686
),
8787
widget.placement,

0 commit comments

Comments
 (0)