Skip to content

Commit 33afd13

Browse files
committed
Sync with core 286a96e: native window system menus (#66)
1 parent 06ac108 commit 33afd13

17 files changed

Lines changed: 234 additions & 2 deletions

File tree

‎bindings/csharp/src/CNativeAPI/generated/Window.cs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ public static partial class Interop
147147
[return: MarshalAs(UnmanagedType.I1)]
148148
public static extern bool native_window_is_shaped(ulong self);
149149

150+
[DllImport(Libraries.NativeApi, CallingConvention = CallingConvention.Cdecl)]
151+
[return: MarshalAs(UnmanagedType.I1)]
152+
public static extern bool native_window_is_system_menu_supported();
153+
150154
[DllImport(Libraries.NativeApi, CallingConvention = CallingConvention.Cdecl)]
151155
[return: MarshalAs(UnmanagedType.I1)]
152156
public static extern bool native_window_is_visible(ulong self);
@@ -207,6 +211,10 @@ public static partial class Interop
207211
[return: MarshalAs(UnmanagedType.I1)]
208212
public static extern bool native_window_set_visual_effect(ulong self, int effect);
209213

214+
[DllImport(Libraries.NativeApi, CallingConvention = CallingConvention.Cdecl)]
215+
[return: MarshalAs(UnmanagedType.I1)]
216+
public static extern bool native_window_show_system_menu(ulong self, native_point_t position);
217+
210218
[DllImport(Libraries.NativeApi, CallingConvention = CallingConvention.Cdecl)]
211219
public static extern IntPtr native_window_get_native_object(ulong handle);
212220

‎bindings/csharp/src/NativeAPI/Window.cs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,19 @@ public bool IsFocusable
801801
}
802802
}
803803

804+
public bool ShowSystemMenu(Point position)
805+
{
806+
var rawPosition = position.ToRaw();
807+
var rawResult = Interop.native_window_show_system_menu(NativeHandle, rawPosition);
808+
return rawResult;
809+
}
810+
811+
public static bool IsSystemMenuSupported()
812+
{
813+
var rawResult = Interop.native_window_is_system_menu_supported();
814+
return rawResult;
815+
}
816+
804817
public void StartDragging()
805818
{
806819
Interop.native_window_start_dragging(NativeHandle);

‎bindings/dart/cnativeapi/lib/src/bindings_generated.dart‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,6 +2553,9 @@ external bool native_window_is_shape_supported();
25532553
@ffi.Native<ffi.Bool Function(native_window_t)>()
25542554
external bool native_window_is_shaped(int window);
25552555

2556+
@ffi.Native<ffi.Bool Function()>()
2557+
external bool native_window_is_system_menu_supported();
2558+
25562559
@ffi.Native<ffi.Bool Function(native_window_t)>()
25572560
external bool native_window_is_visible(int window);
25582561

@@ -2930,6 +2933,12 @@ external void native_window_show(int window);
29302933
@ffi.Native<ffi.Void Function(native_window_t)>()
29312934
external void native_window_show_inactive(int window);
29322935

2936+
@ffi.Native<ffi.Bool Function(native_window_t, native_point_t)>()
2937+
external bool native_window_show_system_menu(
2938+
int window,
2939+
native_point_t position,
2940+
);
2941+
29332942
@ffi.Native<ffi.Void Function(native_window_t)>()
29342943
external void native_window_start_dragging(int window);
29352944

‎bindings/dart/nativeapi/CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
## 0.4.1
22

3+
* `Window.showSystemMenu(Point)` and `Window.isSystemMenuSupported()`: native
4+
system window menu on Windows and supporting Linux window managers. Positions
5+
are logical pixels relative to the content area. Windows keeps command states
6+
current; Wayland requires an active pointer press in the target window.
7+
38
* `Window.setContentProtection()`, `isContentProtected` and
49
`Window.isContentProtectionSupported()`: native capture policy on Windows and
510
macOS. Recent macOS ScreenCaptureKit ignores the legacy sharing policy; Linux

‎bindings/dart/nativeapi/lib/src/window.dart‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,20 @@ class Window {
742742
return c.native_window_is_focusable(nativeHandle);
743743
}
744744

745+
bool showSystemMenu(Point position) {
746+
final positionPointer = position.allocNative();
747+
final result = c.native_window_show_system_menu(
748+
nativeHandle,
749+
positionPointer.ref,
750+
);
751+
Point.freeNative(positionPointer);
752+
return result;
753+
}
754+
755+
static bool isSystemMenuSupported() {
756+
return c.native_window_is_system_menu_supported();
757+
}
758+
745759
void startDragging() {
746760
c.native_window_start_dragging(nativeHandle);
747761
}

‎bindings/dart/nativeapi_flutter/CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
## 0.4.1
22

33
* Requires nativeapi 0.4.1.
4+
* `DragToMoveArea` opens the native system window menu on a secondary-button
5+
press on Windows and supporting Linux window managers.
46
* `runNativeApp` is not re-exported: it is for plain Dart programs, and a
57
Flutter app's engine already runs the event loop.
68

‎bindings/dart/nativeapi_flutter/lib/src/widgets/drag_to_move_area.dart‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import 'package:nativeapi/nativeapi.dart'
1313
View;
1414

1515
/// A custom title-bar region that drags the window and toggles maximization
16-
/// on double tap.
16+
/// on double tap. A secondary-button press opens the native system window menu
17+
/// where supported (Windows, or a supporting Linux window manager).
1718
class DragToMoveArea extends StatelessWidget {
1819
const DragToMoveArea({super.key, required this.child, this.window});
1920

@@ -26,6 +27,11 @@ class DragToMoveArea extends StatelessWidget {
2627
Widget build(BuildContext context) {
2728
return GestureDetector(
2829
behavior: HitTestBehavior.translucent,
30+
onSecondaryTapDown: (details) {
31+
(window ?? WindowManager.instance.getCurrent())?.showSystemMenu(
32+
Point(x: details.globalPosition.dx, y: details.globalPosition.dy),
33+
);
34+
},
2935
onPanStart: (_) {
3036
(window ?? WindowManager.instance.getCurrent())?.startDragging();
3137
},

‎bindings/dart/nativeapi_flutter/test/widgets/window_drag_areas_test.dart‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ class _Window extends Window {
99
int dragCount = 0;
1010
final edges = <ResizeEdge>[];
1111
bool maximized = false;
12+
final systemMenuPositions = <Point>[];
13+
14+
@override
15+
bool showSystemMenu(Point position) {
16+
systemMenuPositions.add(position);
17+
return true;
18+
}
1219

1320
@override
1421
void startDragging() => dragCount++;
@@ -42,6 +49,35 @@ Future<void> _drag(WidgetTester tester, Offset position) async {
4249
}
4350

4451
void main() {
52+
testWidgets(
53+
'move area opens system menu on secondary down at content coordinates',
54+
(tester) async {
55+
final window = _Window();
56+
await tester.pumpWidget(
57+
_host(
58+
DragToMoveArea(
59+
window: window,
60+
child: const SizedBox(width: 200, height: 100),
61+
),
62+
),
63+
);
64+
final point =
65+
tester.getCenter(find.byType(DragToMoveArea)) + const Offset(12, 8);
66+
final gesture = await tester.startGesture(
67+
point,
68+
kind: PointerDeviceKind.mouse,
69+
buttons: kSecondaryMouseButton,
70+
);
71+
await tester.pump(const Duration(milliseconds: 150));
72+
expect(window.systemMenuPositions, hasLength(1));
73+
expect(window.systemMenuPositions.single.x, point.dx);
74+
expect(window.systemMenuPositions.single.y, point.dy);
75+
expect(window.dragCount, 0);
76+
await gesture.up();
77+
await tester.pumpAndSettle();
78+
expect(window.systemMenuPositions, hasLength(1));
79+
},
80+
);
4581
testWidgets('move area drags and toggles maximization on double tap', (
4682
tester,
4783
) async {

‎bindings/js/lib/window.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ export class Window extends NativeObject {
462462
return native.native_window_is_focusable(this.nativeHandle);
463463
}
464464

465+
showSystemMenu(position: Point): boolean {
466+
return native.native_window_show_system_menu(this.nativeHandle, position);
467+
}
468+
469+
static isSystemMenuSupported(): boolean {
470+
return native.native_window_is_system_menu_supported();
471+
}
472+
465473
startDragging(): void {
466474
native.native_window_start_dragging(this.nativeHandle);
467475
}

‎bindings/js/src/generated/window.cc‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,36 @@ napi_value Js_native_window_is_focusable(napi_env env, napi_callback_info info)
16481648
return Value::Bool(result).ToJs(env);
16491649
}
16501650

1651+
napi_value Js_native_window_show_system_menu(napi_env env, napi_callback_info info) {
1652+
Args args(env, info);
1653+
if (!args.ok()) {
1654+
return nullptr;
1655+
}
1656+
Arena arena;
1657+
(void)arena;
1658+
uint64_t self = 0;
1659+
if (!GetHandle(env, args[0], &self)) {
1660+
return nullptr;
1661+
}
1662+
native_point_t p0 = {};
1663+
if (!FromJs(env, args[1], &p0, arena)) {
1664+
return nullptr;
1665+
}
1666+
auto result = OnMainThread([&] { return native_window_show_system_menu(self, p0); });
1667+
return Value::Bool(result).ToJs(env);
1668+
}
1669+
1670+
napi_value Js_native_window_is_system_menu_supported(napi_env env, napi_callback_info info) {
1671+
Args args(env, info);
1672+
if (!args.ok()) {
1673+
return nullptr;
1674+
}
1675+
Arena arena;
1676+
(void)arena;
1677+
auto result = OnMainThread([&] { return native_window_is_system_menu_supported(); });
1678+
return Value::Bool(result).ToJs(env);
1679+
}
1680+
16511681
napi_value Js_native_window_start_dragging(napi_env env, napi_callback_info info) {
16521682
Args args(env, info);
16531683
if (!args.ok()) {
@@ -1785,6 +1815,8 @@ void RegisterWindow(napi_env env, napi_value exports) {
17851815
Export(env, exports, "native_window_is_ignore_mouse_events", Js_native_window_is_ignore_mouse_events);
17861816
Export(env, exports, "native_window_set_focusable", Js_native_window_set_focusable);
17871817
Export(env, exports, "native_window_is_focusable", Js_native_window_is_focusable);
1818+
Export(env, exports, "native_window_show_system_menu", Js_native_window_show_system_menu);
1819+
Export(env, exports, "native_window_is_system_menu_supported", Js_native_window_is_system_menu_supported);
17881820
Export(env, exports, "native_window_start_dragging", Js_native_window_start_dragging);
17891821
Export(env, exports, "native_window_start_resizing", Js_native_window_start_resizing);
17901822
}

0 commit comments

Comments
 (0)