react-native-nswindow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,570 @@
1
+ #import "RNNSWindow.h"
2
+ #import "RNNSWindowHelper.h"
3
+ #import <React/RCTConvert.h>
4
+
5
+ // ─── C++ Implementation ───
6
+
7
+ namespace facebook::react {
8
+
9
+ RNNSWindow::RNNSWindow(std::shared_ptr<CallInvoker> jsInvoker)
10
+ : NativeNSWindowCxxSpec(std::move(jsInvoker)) {
11
+ dispatch_async(dispatch_get_main_queue(), ^{
12
+ [RNNSWindowHelper shared].module = this;
13
+ });
14
+ }
15
+
16
+ RNNSWindow::~RNNSWindow() {
17
+ dispatch_async(dispatch_get_main_queue(), ^{
18
+ if ([RNNSWindowHelper shared].module == this) {
19
+ [RNNSWindowHelper shared].module = nullptr;
20
+ }
21
+ });
22
+ }
23
+
24
+ jsi::Value RNNSWindow::addWindow(jsi::Runtime &rt, jsi::Object props) {
25
+ auto p = NativeNSWindowWindowPropsBridging<WindowProps>::fromJs(rt, props,
26
+ jsInvoker_);
27
+
28
+ NSString *nsComponentName =
29
+ [NSString stringWithUTF8String:p.componentName.c_str()];
30
+ NSString *nsWindowName = [NSString stringWithUTF8String:p.windowName.c_str()];
31
+
32
+ auto toNSString = [](const std::optional<std::string> &s) -> NSString * {
33
+ return s ? [NSString stringWithUTF8String:s->c_str()] : nil;
34
+ };
35
+ auto toNSNumber = [](const std::optional<double> &d) -> NSNumber * {
36
+ return d ? @(*d) : nil;
37
+ };
38
+
39
+ // Convert initialProps jsi::Object → NSDictionary (shallow)
40
+ NSDictionary *initialProps = nil;
41
+ {
42
+ auto names = p.initialProps.getPropertyNames(rt);
43
+ if (names.size(rt) > 0) {
44
+ NSMutableDictionary *dict = [NSMutableDictionary new];
45
+ for (size_t i = 0; i < names.size(rt); i++) {
46
+ auto name = names.getValueAtIndex(rt, i).asString(rt);
47
+ auto val = p.initialProps.getProperty(rt, name);
48
+ NSString *key = [NSString stringWithUTF8String:name.utf8(rt).c_str()];
49
+ if (val.isString()) {
50
+ dict[key] =
51
+ [NSString stringWithUTF8String:val.asString(rt).utf8(rt).c_str()];
52
+ } else if (val.isNumber()) {
53
+ dict[key] = @(val.asNumber());
54
+ } else if (val.isBool()) {
55
+ dict[key] = @(val.getBool());
56
+ }
57
+ }
58
+ initialProps = dict;
59
+ }
60
+ }
61
+
62
+ NSNumber *x = toNSNumber(p.x);
63
+ NSNumber *y = toNSNumber(p.y);
64
+ NSNumber *width = toNSNumber(p.width);
65
+ NSNumber *height = toNSNumber(p.height);
66
+ NSNumber *minWidth = toNSNumber(p.minWidth);
67
+ NSNumber *minHeight = toNSNumber(p.minHeight);
68
+ NSNumber *maxWidth = toNSNumber(p.maxWidth);
69
+ NSNumber *maxHeight = toNSNumber(p.maxHeight);
70
+ BOOL center = p.center.value_or(false);
71
+ NSString *title = toNSString(p.title);
72
+ NSString *titleBarStyle = toNSString(p.titleBarStyle);
73
+ NSString *vibrancy = toNSString(p.vibrancy);
74
+ NSString *backgroundColor = toNSString(p.backgroundColor);
75
+ BOOL transparent = p.transparent.value_or(false);
76
+ BOOL hasShadow = p.hasShadow.value_or(true);
77
+ BOOL resizable = p.resizable.value_or(true);
78
+ BOOL movable = p.movable.value_or(true);
79
+ BOOL minimizable = p.minimizable.value_or(true);
80
+ BOOL closable = p.closable.value_or(true);
81
+ BOOL zoomable = p.zoomable.value_or(true);
82
+ BOOL alwaysOnTop = p.alwaysOnTop.value_or(false);
83
+ NSString *level = toNSString(p.level);
84
+ BOOL show = p.show.value_or(true);
85
+ BOOL focusOnCreate = p.focusOnCreate.value_or(true);
86
+ NSString *autoSaveFrame = toNSString(p.autoSaveFrame);
87
+ BOOL stopShouldClose = p.stopShouldClose.value_or(false);
88
+
89
+ return createPromiseAsJSIValue(
90
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
91
+ dispatch_async(dispatch_get_main_queue(), ^{
92
+ NSString *windowId = [[RNNSWindowHelper shared]
93
+ createWindowWithComponent:nsComponentName
94
+ windowName:nsWindowName
95
+ initialProps:initialProps
96
+ x:x
97
+ y:y
98
+ width:width
99
+ height:height
100
+ minWidth:minWidth
101
+ minHeight:minHeight
102
+ maxWidth:maxWidth
103
+ maxHeight:maxHeight
104
+ center:center
105
+ title:title
106
+ titleBarStyle:titleBarStyle
107
+ vibrancy:vibrancy
108
+ backgroundColor:backgroundColor
109
+ transparent:transparent
110
+ hasShadow:hasShadow
111
+ resizable:resizable
112
+ movable:movable
113
+ minimizable:minimizable
114
+ closable:closable
115
+ zoomable:zoomable
116
+ alwaysOnTop:alwaysOnTop
117
+ level:level
118
+ show:show
119
+ focusOnCreate:focusOnCreate
120
+ autoSaveFrame:autoSaveFrame];
121
+
122
+ if (stopShouldClose) {
123
+ [[RNNSWindowHelper shared] setStopShouldClose:YES
124
+ forWindowId:windowId];
125
+ }
126
+
127
+ std::string wid = [windowId UTF8String];
128
+ jsInvoker_->invokeAsync([promise, wid](jsi::Runtime &rt3) {
129
+ promise->resolve(jsi::String::createFromUtf8(rt3, wid));
130
+ });
131
+ });
132
+ });
133
+ }
134
+
135
+ jsi::Value RNNSWindow::closeWindow(jsi::Runtime &rt, jsi::String windowId) {
136
+ std::string wid = windowId.utf8(rt);
137
+ return createPromiseAsJSIValue(
138
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
139
+ dispatch_async(dispatch_get_main_queue(), ^{
140
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
141
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
142
+ [window close];
143
+
144
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
145
+ promise->resolve(jsi::Value::undefined());
146
+ });
147
+ });
148
+ });
149
+ }
150
+
151
+ jsi::Value RNNSWindow::modifyWindow(jsi::Runtime &rt, jsi::String windowId,
152
+ jsi::Object props) {
153
+ std::string wid = windowId.utf8(rt);
154
+ auto p = NativeNSWindowModifyableWindowPropsBridging<ModifyProps>::fromJs(
155
+ rt, props, jsInvoker_);
156
+
157
+ auto toNSString = [](const std::optional<std::string> &s) -> NSString * {
158
+ return s ? [NSString stringWithUTF8String:s->c_str()] : nil;
159
+ };
160
+ auto toNSNumber = [](const std::optional<double> &d) -> NSNumber * {
161
+ return d ? @(*d) : nil;
162
+ };
163
+ auto toNSBool = [](const std::optional<bool> &b) -> NSNumber * {
164
+ return b ? @(*b) : nil;
165
+ };
166
+
167
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
168
+ NSNumber *nx = toNSNumber(p.x);
169
+ NSNumber *ny = toNSNumber(p.y);
170
+ NSNumber *nWidth = toNSNumber(p.width);
171
+ NSNumber *nHeight = toNSNumber(p.height);
172
+ NSNumber *nMinWidth = toNSNumber(p.minWidth);
173
+ NSNumber *nMinHeight = toNSNumber(p.minHeight);
174
+ NSNumber *nMaxWidth = toNSNumber(p.maxWidth);
175
+ NSNumber *nMaxHeight = toNSNumber(p.maxHeight);
176
+ NSNumber *nCenter = toNSBool(p.center);
177
+ NSString *title = toNSString(p.title);
178
+ NSString *titleBarStyle = toNSString(p.titleBarStyle);
179
+ NSString *vibrancy = toNSString(p.vibrancy);
180
+ NSString *backgroundColor = toNSString(p.backgroundColor);
181
+ NSNumber *nTransparent = toNSBool(p.transparent);
182
+ NSNumber *nHasShadow = toNSBool(p.hasShadow);
183
+ NSNumber *nResizable = toNSBool(p.resizable);
184
+ NSNumber *nMovable = toNSBool(p.movable);
185
+ NSNumber *nMinimizable = toNSBool(p.minimizable);
186
+ NSNumber *nClosable = toNSBool(p.closable);
187
+ NSNumber *nZoomable = toNSBool(p.zoomable);
188
+ NSNumber *nAlwaysOnTop = toNSBool(p.alwaysOnTop);
189
+ NSString *level = toNSString(p.level);
190
+ NSNumber *nShow = toNSBool(p.show);
191
+ NSNumber *nFocusOnCreate = toNSBool(p.focusOnCreate);
192
+ NSString *autoSaveFrame = toNSString(p.autoSaveFrame);
193
+ NSNumber *nStopShouldClose = toNSBool(p.stopShouldClose);
194
+
195
+ return createPromiseAsJSIValue(
196
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
197
+ dispatch_async(dispatch_get_main_queue(), ^{
198
+ BOOL success =
199
+ [[RNNSWindowHelper shared] modifyWindow:nsWid
200
+ x:nx
201
+ y:ny
202
+ width:nWidth
203
+ height:nHeight
204
+ minWidth:nMinWidth
205
+ minHeight:nMinHeight
206
+ maxWidth:nMaxWidth
207
+ maxHeight:nMaxHeight
208
+ center:nCenter
209
+ title:title
210
+ titleBarStyle:titleBarStyle
211
+ vibrancy:vibrancy
212
+ backgroundColor:backgroundColor
213
+ transparent:nTransparent
214
+ hasShadow:nHasShadow
215
+ resizable:nResizable
216
+ movable:nMovable
217
+ minimizable:nMinimizable
218
+ closable:nClosable
219
+ zoomable:nZoomable
220
+ alwaysOnTop:nAlwaysOnTop
221
+ level:level
222
+ show:nShow
223
+ focusOnCreate:nFocusOnCreate
224
+ autoSaveFrame:autoSaveFrame
225
+ stopShouldClose:nStopShouldClose];
226
+
227
+ if (!success) {
228
+ jsInvoker_->invokeAsync([promise, wid](jsi::Runtime &) {
229
+ promise->reject("Window not found: " + wid);
230
+ });
231
+ return;
232
+ }
233
+
234
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
235
+ promise->resolve(jsi::Value::undefined());
236
+ });
237
+ });
238
+ });
239
+ }
240
+
241
+ jsi::Value RNNSWindow::listWindows(jsi::Runtime &rt) {
242
+ return createPromiseAsJSIValue(rt, [this](jsi::Runtime &,
243
+ std::shared_ptr<Promise> promise) {
244
+ dispatch_async(dispatch_get_main_queue(), ^{
245
+ NSArray<NSString *> *ids = [[RNNSWindowHelper shared] allWindowIds];
246
+ std::vector<std::string> vec;
247
+ for (NSString *wid in ids) {
248
+ vec.push_back([wid UTF8String]);
249
+ }
250
+ jsInvoker_->invokeAsync([promise,
251
+ vec = std::move(vec)](jsi::Runtime &rt2) {
252
+ auto arr = jsi::Array(rt2, vec.size());
253
+ for (size_t i = 0; i < vec.size(); i++) {
254
+ arr.setValueAtIndex(rt2, i, jsi::String::createFromUtf8(rt2, vec[i]));
255
+ }
256
+ promise->resolve(std::move(arr));
257
+ });
258
+ });
259
+ });
260
+ }
261
+
262
+ jsi::Value RNNSWindow::getWindowState(jsi::Runtime &rt, jsi::String windowId) {
263
+ std::string wid = windowId.utf8(rt);
264
+ return createPromiseAsJSIValue(rt, [=,
265
+ this](jsi::Runtime &,
266
+ std::shared_ptr<Promise> promise) {
267
+ dispatch_async(dispatch_get_main_queue(), ^{
268
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
269
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
270
+ NSString *name = [[RNNSWindowHelper shared] windowNameForId:nsWid] ?: @"";
271
+
272
+ if (!window) {
273
+ jsInvoker_->invokeAsync([promise, wid](jsi::Runtime &) {
274
+ promise->reject("Window not found: " + wid);
275
+ });
276
+ return;
277
+ }
278
+
279
+ NSRect frame = window.frame;
280
+ bool isKey = [window isKeyWindow];
281
+ bool isMini = [window isMiniaturized];
282
+ bool isFS = (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
283
+ bool isVis = [window isVisible];
284
+ bool isOccluded =
285
+ (window.occlusionState & NSWindowOcclusionStateVisible) == 0;
286
+ double scaleFactor = [window backingScaleFactor];
287
+ std::string nameStr = [name UTF8String];
288
+ double x = frame.origin.x;
289
+ double y = frame.origin.y;
290
+ double w = frame.size.width;
291
+ double h = frame.size.height;
292
+
293
+ // Screen frame (nullable)
294
+ bool hasScreen = (window.screen != nil);
295
+ double sx = 0, sy = 0, sw = 0, sh = 0;
296
+ if (hasScreen) {
297
+ NSRect sf = window.screen.frame;
298
+ sx = sf.origin.x;
299
+ sy = sf.origin.y;
300
+ sw = sf.size.width;
301
+ sh = sf.size.height;
302
+ }
303
+
304
+ jsInvoker_->invokeAsync([promise, wid, nameStr, x, y, w, h, isKey, isMini,
305
+ isFS, isVis, isOccluded, scaleFactor, hasScreen,
306
+ sx, sy, sw, sh](jsi::Runtime &rt2) {
307
+ auto obj = jsi::Object(rt2);
308
+ obj.setProperty(rt2, "windowId", jsi::String::createFromUtf8(rt2, wid));
309
+ obj.setProperty(rt2, "windowName",
310
+ jsi::String::createFromUtf8(rt2, nameStr));
311
+ obj.setProperty(rt2, "x", x);
312
+ obj.setProperty(rt2, "y", y);
313
+ obj.setProperty(rt2, "width", w);
314
+ obj.setProperty(rt2, "height", h);
315
+ obj.setProperty(rt2, "isKeyWindow", isKey);
316
+ obj.setProperty(rt2, "isMinimized", isMini);
317
+ obj.setProperty(rt2, "isFullScreen", isFS);
318
+ obj.setProperty(rt2, "isVisible", isVis);
319
+ obj.setProperty(rt2, "isOccluded", isOccluded);
320
+ obj.setProperty(rt2, "backingScaleFactor", scaleFactor);
321
+ if (hasScreen) {
322
+ auto screen = jsi::Object(rt2);
323
+ screen.setProperty(rt2, "x", sx);
324
+ screen.setProperty(rt2, "y", sy);
325
+ screen.setProperty(rt2, "width", sw);
326
+ screen.setProperty(rt2, "height", sh);
327
+ obj.setProperty(rt2, "screen", std::move(screen));
328
+ } else {
329
+ obj.setProperty(rt2, "screen", jsi::Value::null());
330
+ }
331
+ promise->resolve(std::move(obj));
332
+ });
333
+ });
334
+ });
335
+ }
336
+
337
+ jsi::Value RNNSWindow::focusWindow(jsi::Runtime &rt, jsi::String windowId) {
338
+ std::string wid = windowId.utf8(rt);
339
+ return createPromiseAsJSIValue(
340
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
341
+ dispatch_async(dispatch_get_main_queue(), ^{
342
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
343
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
344
+ [window makeKeyAndOrderFront:nil];
345
+
346
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
347
+ promise->resolve(jsi::Value::undefined());
348
+ });
349
+ });
350
+ });
351
+ }
352
+
353
+ jsi::Value RNNSWindow::hideWindow(jsi::Runtime &rt, jsi::String windowId) {
354
+ std::string wid = windowId.utf8(rt);
355
+ return createPromiseAsJSIValue(
356
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
357
+ dispatch_async(dispatch_get_main_queue(), ^{
358
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
359
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
360
+ [window orderOut:nil];
361
+
362
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
363
+ promise->resolve(jsi::Value::undefined());
364
+ });
365
+ });
366
+ });
367
+ }
368
+
369
+ jsi::Value RNNSWindow::showWindow(jsi::Runtime &rt, jsi::String windowId) {
370
+ std::string wid = windowId.utf8(rt);
371
+ return createPromiseAsJSIValue(
372
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
373
+ dispatch_async(dispatch_get_main_queue(), ^{
374
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
375
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
376
+ [window orderFront:nil];
377
+
378
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
379
+ promise->resolve(jsi::Value::undefined());
380
+ });
381
+ });
382
+ });
383
+ }
384
+
385
+ jsi::Value RNNSWindow::minimizeWindow(jsi::Runtime &rt, jsi::String windowId) {
386
+ std::string wid = windowId.utf8(rt);
387
+ return createPromiseAsJSIValue(
388
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
389
+ dispatch_async(dispatch_get_main_queue(), ^{
390
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
391
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
392
+ [window miniaturize:nil];
393
+
394
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
395
+ promise->resolve(jsi::Value::undefined());
396
+ });
397
+ });
398
+ });
399
+ }
400
+
401
+ jsi::Value RNNSWindow::deminimizeWindow(jsi::Runtime &rt,
402
+ jsi::String windowId) {
403
+ std::string wid = windowId.utf8(rt);
404
+ return createPromiseAsJSIValue(
405
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
406
+ dispatch_async(dispatch_get_main_queue(), ^{
407
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
408
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
409
+ [window deminiaturize:nil];
410
+
411
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
412
+ promise->resolve(jsi::Value::undefined());
413
+ });
414
+ });
415
+ });
416
+ }
417
+
418
+ jsi::Value RNNSWindow::setFullScreen(jsi::Runtime &rt, jsi::String windowId,
419
+ bool fullscreen) {
420
+ std::string wid = windowId.utf8(rt);
421
+ return createPromiseAsJSIValue(
422
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
423
+ dispatch_async(dispatch_get_main_queue(), ^{
424
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
425
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
426
+ if (window) {
427
+ BOOL isFS = (window.styleMask & NSWindowStyleMaskFullScreen) != 0;
428
+ if (fullscreen != isFS) {
429
+ [window toggleFullScreen:nil];
430
+ }
431
+ }
432
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
433
+ promise->resolve(jsi::Value::undefined());
434
+ });
435
+ });
436
+ });
437
+ }
438
+
439
+ jsi::Value RNNSWindow::bringToFront(jsi::Runtime &rt, jsi::String windowId) {
440
+ std::string wid = windowId.utf8(rt);
441
+ return createPromiseAsJSIValue(
442
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
443
+ dispatch_async(dispatch_get_main_queue(), ^{
444
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
445
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
446
+ [window orderFront:nil];
447
+
448
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
449
+ promise->resolve(jsi::Value::undefined());
450
+ });
451
+ });
452
+ });
453
+ }
454
+
455
+ jsi::Value RNNSWindow::sendToBack(jsi::Runtime &rt, jsi::String windowId) {
456
+ std::string wid = windowId.utf8(rt);
457
+ return createPromiseAsJSIValue(
458
+ rt, [=, this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
459
+ dispatch_async(dispatch_get_main_queue(), ^{
460
+ NSString *nsWid = [NSString stringWithUTF8String:wid.c_str()];
461
+ NSWindow *window = [[RNNSWindowHelper shared] windowForId:nsWid];
462
+ [window orderBack:nil];
463
+
464
+ jsInvoker_->invokeAsync([promise](jsi::Runtime &) {
465
+ promise->resolve(jsi::Value::undefined());
466
+ });
467
+ });
468
+ });
469
+ }
470
+
471
+ jsi::Value RNNSWindow::getScreenInfo(jsi::Runtime &rt) {
472
+ return createPromiseAsJSIValue(
473
+ rt, [this](jsi::Runtime &, std::shared_ptr<Promise> promise) {
474
+ dispatch_async(dispatch_get_main_queue(), ^{
475
+ NSArray<NSScreen *> *screens = [NSScreen screens];
476
+
477
+ // Compute total bounding rect across all screens
478
+ double minX = CGFLOAT_MAX, minY = CGFLOAT_MAX;
479
+ double maxX = -CGFLOAT_MAX, maxY = -CGFLOAT_MAX;
480
+ double minVX = CGFLOAT_MAX, minVY = CGFLOAT_MAX;
481
+ double maxVX = -CGFLOAT_MAX, maxVY = -CGFLOAT_MAX;
482
+
483
+ struct ScreenData {
484
+ double fx, fy, fw, fh;
485
+ double vx, vy, vw, vh;
486
+ };
487
+ std::vector<ScreenData> screenData;
488
+
489
+ for (NSScreen *s in screens) {
490
+ NSRect f = s.frame;
491
+ NSRect v = s.visibleFrame;
492
+ screenData.push_back({f.origin.x, f.origin.y, f.size.width,
493
+ f.size.height, v.origin.x, v.origin.y,
494
+ v.size.width, v.size.height});
495
+ minX = std::min(minX, f.origin.x);
496
+ minY = std::min(minY, f.origin.y);
497
+ maxX = std::max(maxX, f.origin.x + f.size.width);
498
+ maxY = std::max(maxY, f.origin.y + f.size.height);
499
+ minVX = std::min(minVX, v.origin.x);
500
+ minVY = std::min(minVY, v.origin.y);
501
+ maxVX = std::max(maxVX, v.origin.x + v.size.width);
502
+ maxVY = std::max(maxVY, v.origin.y + v.size.height);
503
+ }
504
+
505
+ double totalX = minX, totalY = minY;
506
+ double totalW = maxX - minX, totalH = maxY - minY;
507
+ double totalVX = minVX, totalVY = minVY;
508
+ double totalVW = maxVX - minVX, totalVH = maxVY - minVY;
509
+
510
+ // Main screen frame
511
+ NSRect mainFrame = NSScreen.mainScreen.frame;
512
+ double mx = mainFrame.origin.x, my = mainFrame.origin.y;
513
+ double mw = mainFrame.size.width, mh = mainFrame.size.height;
514
+
515
+ jsInvoker_->invokeAsync([promise, screenData, totalX, totalY, totalW,
516
+ totalH, totalVX, totalVY, totalVW, totalVH,
517
+ mx, my, mw, mh](jsi::Runtime &rt2) {
518
+ auto obj = jsi::Object(rt2);
519
+
520
+ auto total = jsi::Object(rt2);
521
+ total.setProperty(rt2, "x", totalX);
522
+ total.setProperty(rt2, "y", totalY);
523
+ total.setProperty(rt2, "width", totalW);
524
+ total.setProperty(rt2, "height", totalH);
525
+ obj.setProperty(rt2, "total", std::move(total));
526
+
527
+ auto totalVis = jsi::Object(rt2);
528
+ totalVis.setProperty(rt2, "x", totalVX);
529
+ totalVis.setProperty(rt2, "y", totalVY);
530
+ totalVis.setProperty(rt2, "width", totalVW);
531
+ totalVis.setProperty(rt2, "height", totalVH);
532
+ obj.setProperty(rt2, "totalVisibleFrame", std::move(totalVis));
533
+
534
+ auto screensArr = jsi::Array(rt2, screenData.size());
535
+ for (size_t i = 0; i < screenData.size(); i++) {
536
+ auto &sd = screenData[i];
537
+ auto sObj = jsi::Object(rt2);
538
+
539
+ auto frame = jsi::Object(rt2);
540
+ frame.setProperty(rt2, "x", sd.fx);
541
+ frame.setProperty(rt2, "y", sd.fy);
542
+ frame.setProperty(rt2, "width", sd.fw);
543
+ frame.setProperty(rt2, "height", sd.fh);
544
+ sObj.setProperty(rt2, "frame", std::move(frame));
545
+
546
+ auto vis = jsi::Object(rt2);
547
+ vis.setProperty(rt2, "x", sd.vx);
548
+ vis.setProperty(rt2, "y", sd.vy);
549
+ vis.setProperty(rt2, "width", sd.vw);
550
+ vis.setProperty(rt2, "height", sd.vh);
551
+ sObj.setProperty(rt2, "visibleFrame", std::move(vis));
552
+
553
+ screensArr.setValueAtIndex(rt2, i, std::move(sObj));
554
+ }
555
+ obj.setProperty(rt2, "screens", std::move(screensArr));
556
+
557
+ auto main = jsi::Object(rt2);
558
+ main.setProperty(rt2, "x", mx);
559
+ main.setProperty(rt2, "y", my);
560
+ main.setProperty(rt2, "width", mw);
561
+ main.setProperty(rt2, "height", mh);
562
+ obj.setProperty(rt2, "main", std::move(main));
563
+
564
+ promise->resolve(std::move(obj));
565
+ });
566
+ });
567
+ });
568
+ }
569
+
570
+ } // namespace facebook::react
@@ -0,0 +1,80 @@
1
+ #pragma once
2
+
3
+ #import <AppKit/AppKit.h>
4
+ #import <Foundation/Foundation.h>
5
+
6
+ namespace facebook::react {
7
+ class RNNSWindow;
8
+ }
9
+
10
+ @interface RNNSWindowHelper : NSObject <NSWindowDelegate>
11
+
12
+ @property(nonatomic, assign) facebook::react::RNNSWindow *module;
13
+
14
+ + (instancetype)shared;
15
+
16
+ - (NSString *)createWindowWithComponent:(NSString *)componentName
17
+ windowName:(NSString *)windowName
18
+ initialProps:(NSDictionary *_Nullable)initialProps
19
+ x:(NSNumber *_Nullable)x
20
+ y:(NSNumber *_Nullable)y
21
+ width:(NSNumber *_Nullable)width
22
+ height:(NSNumber *_Nullable)height
23
+ minWidth:(NSNumber *_Nullable)minWidth
24
+ minHeight:(NSNumber *_Nullable)minHeight
25
+ maxWidth:(NSNumber *_Nullable)maxWidth
26
+ maxHeight:(NSNumber *_Nullable)maxHeight
27
+ center:(BOOL)center
28
+ title:(NSString *_Nullable)title
29
+ titleBarStyle:(NSString *_Nullable)titleBarStyle
30
+ vibrancy:(NSString *_Nullable)vibrancy
31
+ backgroundColor:(NSString *_Nullable)backgroundColor
32
+ transparent:(BOOL)transparent
33
+ hasShadow:(BOOL)hasShadow
34
+ resizable:(BOOL)resizable
35
+ movable:(BOOL)movable
36
+ minimizable:(BOOL)minimizable
37
+ closable:(BOOL)closable
38
+ zoomable:(BOOL)zoomable
39
+ alwaysOnTop:(BOOL)alwaysOnTop
40
+ level:(NSString *_Nullable)level
41
+ show:(BOOL)show
42
+ focusOnCreate:(BOOL)focusOnCreate
43
+ autoSaveFrame:(NSString *_Nullable)autoSaveFrame;
44
+
45
+ - (BOOL)modifyWindow:(NSString *)windowId
46
+ x:(NSNumber *_Nullable)x
47
+ y:(NSNumber *_Nullable)y
48
+ width:(NSNumber *_Nullable)width
49
+ height:(NSNumber *_Nullable)height
50
+ minWidth:(NSNumber *_Nullable)minWidth
51
+ minHeight:(NSNumber *_Nullable)minHeight
52
+ maxWidth:(NSNumber *_Nullable)maxWidth
53
+ maxHeight:(NSNumber *_Nullable)maxHeight
54
+ center:(NSNumber *_Nullable)center
55
+ title:(NSString *_Nullable)title
56
+ titleBarStyle:(NSString *_Nullable)titleBarStyle
57
+ vibrancy:(NSString *_Nullable)vibrancy
58
+ backgroundColor:(NSString *_Nullable)backgroundColor
59
+ transparent:(NSNumber *_Nullable)transparent
60
+ hasShadow:(NSNumber *_Nullable)hasShadow
61
+ resizable:(NSNumber *_Nullable)resizable
62
+ movable:(NSNumber *_Nullable)movable
63
+ minimizable:(NSNumber *_Nullable)minimizable
64
+ closable:(NSNumber *_Nullable)closable
65
+ zoomable:(NSNumber *_Nullable)zoomable
66
+ alwaysOnTop:(NSNumber *_Nullable)alwaysOnTop
67
+ level:(NSString *_Nullable)level
68
+ show:(NSNumber *_Nullable)show
69
+ focusOnCreate:(NSNumber *_Nullable)focusOnCreate
70
+ autoSaveFrame:(NSString *_Nullable)autoSaveFrame
71
+ stopShouldClose:(NSNumber *_Nullable)stopShouldClose;
72
+
73
+ - (NSWindow *_Nullable)windowForId:(NSString *)windowId;
74
+ - (NSArray<NSString *> *)allWindowIds;
75
+ - (NSString *_Nullable)windowNameForId:(NSString *)windowId;
76
+ - (void)removeWindowForId:(NSString *)windowId;
77
+ - (void)setStopShouldClose:(BOOL)stop forWindowId:(NSString *)windowId;
78
+ - (NSVisualEffectMaterial)materialForVibrancy:(NSString *)vibrancy;
79
+
80
+ @end