basalt-appkit 0.0.0-rc5 → 0.2.0
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.
- package/native/CMakeLists.txt +16 -2
- package/native/appkit/AppKitAppWindow.h +24 -0
- package/native/appkit/AppKitImageLoader.h +48 -1
- package/native/appkit/AppKitImageLoader.mm +228 -10
- package/native/appkit/AppKitMenuBar.mm +58 -0
- package/native/appkit/AppKitMountingManager.h +34 -0
- package/native/appkit/AppKitMountingManager.mm +582 -12
- package/native/appkit/AppKitScrollView.h +48 -0
- package/native/appkit/AppKitScrollView.mm +210 -0
- package/native/appkit/AppKitSkiaPeer.h +15 -0
- package/native/appkit/AppKitTextInput.h +38 -1
- package/native/appkit/AppKitTextInput.mm +235 -8
- package/native/appkit/AppKitTextPeer.h +60 -0
- package/native/appkit/AppKitTextPeer.mm +307 -0
- package/native/appkit/AppKitTouchDispatcher.h +56 -0
- package/native/appkit/AppKitTouchDispatcher.mm +140 -0
- package/native/appkit/AppKitWindowControl.mm +38 -3
- package/native/appkit/ComponentRegistryAppKit.mm +28 -3
- package/native/appkit/CoreTextLayout.h +64 -2
- package/native/appkit/CoreTextLayout.mm +431 -71
- package/native/appkit/CoreTextLayoutManager.mm +57 -6
- package/native/appkit/FontRegistryCoreText.mm +6 -5
- package/native/appkit/PlatformServicesAppKit.mm +101 -20
- package/native/appkit/RnAppKitView.h +492 -0
- package/native/appkit/RnAppKitView.mm +2266 -23
- package/native/appkit/RnTextLayout.h +106 -0
- package/native/appkit/RnTextLayout.mm +321 -2
- package/native/appkit/main_appkit.mm +174 -3
- package/native/tests/test_appkit_accessibility.mm +289 -0
- package/native/tests/test_appkit_borders.mm +975 -0
- package/native/tests/test_appkit_fonts.mm +203 -0
- package/native/tests/test_appkit_image.mm +392 -17
- package/native/tests/test_appkit_input.mm +209 -0
- package/native/tests/test_appkit_mounting.mm +360 -0
- package/native/tests/test_appkit_scrollbar.mm +125 -0
- package/native/tests/test_appkit_scrollview.mm +371 -0
- package/native/tests/test_appkit_selection.mm +349 -0
- package/native/tests/test_appkit_services.mm +51 -1
- package/native/tests/test_appkit_text.mm +1628 -0
- package/native/tests/test_appkit_textinput.mm +819 -0
- package/native/tests/test_appkit_titlebar.mm +43 -0
- package/native/tests/test_appkit_view.mm +609 -0
- package/package.json +4 -3
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
|
|
27
27
|
#ifdef __OBJC__
|
|
28
28
|
#import <Cocoa/Cocoa.h>
|
|
29
|
+
// For CALayer, which `boxShadow` is made of: Cocoa declares the view and
|
|
30
|
+
// QuartzCore the layers under it.
|
|
31
|
+
#import <CoreImage/CoreImage.h>
|
|
32
|
+
#import <QuartzCore/QuartzCore.h>
|
|
29
33
|
|
|
30
34
|
NS_ASSUME_NONNULL_BEGIN
|
|
31
35
|
|
|
@@ -196,6 +200,43 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
196
200
|
// fit.
|
|
197
201
|
- (void)setRnImage:(nullable CGImageRef)image fit:(RnAppKitImageFit)fit;
|
|
198
202
|
|
|
203
|
+
// The frames of an animated image, their delays, and how many times round.
|
|
204
|
+
//
|
|
205
|
+
// `frames` holds CGImageRefs and `delaysMs` the file's own delays, one per
|
|
206
|
+
// frame, unclamped: core/ImageAnimation.h applies the clamp, because that rule
|
|
207
|
+
// is the same on every host. `loopCount` is zero for forever. Fewer than two
|
|
208
|
+
// frames, or nil, is a still image and clears any animation.
|
|
209
|
+
//
|
|
210
|
+
// The view shows frame zero immediately and schedules the rest. Passing the
|
|
211
|
+
// same `frames` array again is a no-op, which is how a layout-only mutation
|
|
212
|
+
// avoids restarting the animation; AppKitImageLoader keeps one array per URI
|
|
213
|
+
// for exactly that.
|
|
214
|
+
- (void)setRnImageFrames:(nullable NSArray *)frames
|
|
215
|
+
delaysMs:(nullable NSArray<NSNumber *> *)delaysMs
|
|
216
|
+
loopCount:(NSUInteger)loopCount;
|
|
217
|
+
|
|
218
|
+
// Starts the timer that shows the rest of the frames, if one is not running.
|
|
219
|
+
//
|
|
220
|
+
// Separate from `setRnImageFrames:` so that nothing animates by itself: the
|
|
221
|
+
// mounting manager starts it because the view is in an app, and the suite never
|
|
222
|
+
// does, which keeps a test about frames deterministic. The GTK view is split
|
|
223
|
+
// the same way, where it matters more -- rendering a widget there spins the
|
|
224
|
+
// main loop, and a self-driven animation would be several frames further on
|
|
225
|
+
// than the test left it.
|
|
226
|
+
- (void)rnStartImageAnimation;
|
|
227
|
+
|
|
228
|
+
// Moves the animation on by `milliseconds` and shows the frame that lands on,
|
|
229
|
+
// answering with how long until the next one, or zero when nothing more is due.
|
|
230
|
+
//
|
|
231
|
+
// The elapsed time is an argument rather than a clock read inside: the host's
|
|
232
|
+
// timer passes the delay it waited, and the suite passes whatever it likes,
|
|
233
|
+
// which is what makes "a hundred and fifty milliseconds later" something a test
|
|
234
|
+
// can say without sleeping.
|
|
235
|
+
- (double)rnAdvanceImageAnimationBy:(double)milliseconds;
|
|
236
|
+
|
|
237
|
+
// Which frame is showing, zero for a still image.
|
|
238
|
+
@property(nonatomic, readonly) NSUInteger rnImageFrameIndex;
|
|
239
|
+
|
|
199
240
|
// `tintColor`: recolours the image, keeping its alpha, so one silhouette asset
|
|
200
241
|
// can be drawn in any colour. Pass nil to clear.
|
|
201
242
|
//
|
|
@@ -203,6 +244,15 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
203
244
|
// from a loader, and either can land first.
|
|
204
245
|
- (void)setRnImageTint:(nullable NSColor *)tint;
|
|
205
246
|
|
|
247
|
+
// `blurRadius`: blurs the image and not the view, which is what the prop means
|
|
248
|
+
// -- a blurred photograph behind sharp text is the usual reason to ask for it.
|
|
249
|
+
// Zero or negative is no blur.
|
|
250
|
+
//
|
|
251
|
+
// The radius is a distance in this view's own coordinates rather than in the
|
|
252
|
+
// image's pixels, so the same number is the same picture here as on the GTK
|
|
253
|
+
// side. Also separate from the image for the reason the tint is.
|
|
254
|
+
- (void)setRnImageBlur:(CGFloat)radius;
|
|
255
|
+
|
|
206
256
|
// The paragraph this view draws, or nil for a view that draws none.
|
|
207
257
|
//
|
|
208
258
|
// A view either paints a layer or draws text; nothing here does both, because
|
|
@@ -212,6 +262,43 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
212
262
|
// both the mounting manager and the measurement seam go through that, so the
|
|
213
263
|
// size a view draws at is the size Yoga was told.
|
|
214
264
|
- (void)setRnTextLayout:(nullable id)layout;
|
|
265
|
+
|
|
266
|
+
// --- Selecting that text ------------------------------------------------------
|
|
267
|
+
//
|
|
268
|
+
// `<Text selectable>`, which `userSelect` also arrives in. The same four
|
|
269
|
+
// questions the GTK side answers in C, in the same order and with the same
|
|
270
|
+
// units: whether this paragraph may be selected, where a point lands in its
|
|
271
|
+
// text, what range is selected, and what that range says. The state machine
|
|
272
|
+
// above them is core/TextSelection.h, shared by all three hosts.
|
|
273
|
+
//
|
|
274
|
+
// Indices are UTF-16 code units, which is what `NSAttributedString` counts;
|
|
275
|
+
// the GTK side counts UTF-8 bytes, and core/TextSelection.h says why the two
|
|
276
|
+
// never have to agree.
|
|
277
|
+
- (void)setRnTextSelectable:(BOOL)selectable;
|
|
278
|
+
- (BOOL)rnTextSelectable;
|
|
279
|
+
|
|
280
|
+
// -1 when this view draws no paragraph. `point` is in the view's own
|
|
281
|
+
// coordinates.
|
|
282
|
+
- (NSInteger)rnTextIndexAtPoint:(CGPoint)point;
|
|
283
|
+
|
|
284
|
+
// How far down its box this view's paragraph is drawn, which is zero for every
|
|
285
|
+
// view that draws no text and for every paragraph that says nothing about
|
|
286
|
+
// `textAlignVertical`.
|
|
287
|
+
//
|
|
288
|
+
// The inline views inside a paragraph are moved by it. React Native measures an
|
|
289
|
+
// attachment against the content box and places it from the top --
|
|
290
|
+
// `ParagraphShadowNode::layout` adds the content insets and nothing else -- so
|
|
291
|
+
// it knows nothing about a box taller than its text, which is what
|
|
292
|
+
// `textAlignVertical` is about. Without this a marker inside a bottom-aligned
|
|
293
|
+
// sentence stayed at the top while the words went to the bottom.
|
|
294
|
+
- (CGFloat)rnTextVerticalOffset;
|
|
295
|
+
|
|
296
|
+
- (void)setRnTextSelectionStart:(NSInteger)start length:(NSInteger)length;
|
|
297
|
+
- (NSInteger)rnTextSelectionStart;
|
|
298
|
+
- (NSInteger)rnTextSelectionLength;
|
|
299
|
+
|
|
300
|
+
// The selected text, or nil when nothing is selected.
|
|
301
|
+
- (nullable NSString *)rnSelectedText;
|
|
215
302
|
- (void)setRnClipsChildren:(BOOL)clips;
|
|
216
303
|
|
|
217
304
|
// `zIndex`. Reorders painting and hit testing, and never the child list --
|
|
@@ -285,6 +372,217 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
285
372
|
- (void)setRnBorderWidths:(nullable const CGFloat *)widths
|
|
286
373
|
colors:(nullable const CGFloat *)colors;
|
|
287
374
|
|
|
375
|
+
// One colour stop of a gradient: an offset from 0 to 1 along the gradient line
|
|
376
|
+
// and the colour there, as RGBA components.
|
|
377
|
+
typedef struct {
|
|
378
|
+
CGFloat offset;
|
|
379
|
+
CGFloat color[4];
|
|
380
|
+
} RnAppKitGradientStop;
|
|
381
|
+
|
|
382
|
+
// Which kind of gradient a record is. CSS's `background-image` is one list that
|
|
383
|
+
// can hold both, and the first in it is the one on top, so they cannot be two
|
|
384
|
+
// lists here without losing the order between them.
|
|
385
|
+
typedef NS_ENUM(NSInteger, RnAppKitGradientKind) {
|
|
386
|
+
RnAppKitGradientKindLinear,
|
|
387
|
+
RnAppKitGradientKindRadial,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// One `backgroundImage` gradient, already resolved against this view's size: a
|
|
391
|
+
// linear one's two end points, or a radial one's centre and two radii, in this
|
|
392
|
+
// view's own coordinates.
|
|
393
|
+
//
|
|
394
|
+
// Resolved rather than described, because the resolution is CSS's and is shared
|
|
395
|
+
// with the GTK host in core/Gradients.h -- the angle or the ending shape, the box
|
|
396
|
+
// size and the stop fixup all go into these numbers and the offsets. This layer
|
|
397
|
+
// draws what it is given. The stops are borrowed for the duration of the call.
|
|
398
|
+
typedef struct {
|
|
399
|
+
RnAppKitGradientKind kind;
|
|
400
|
+
// Linear only: the ends of the gradient line.
|
|
401
|
+
CGPoint start;
|
|
402
|
+
CGPoint end;
|
|
403
|
+
// Radial only: the centre of the ending shape and its two radii. A circle is
|
|
404
|
+
// an ellipse whose radii are equal, which is what CSS makes of one.
|
|
405
|
+
CGPoint center;
|
|
406
|
+
CGFloat radiusX;
|
|
407
|
+
CGFloat radiusY;
|
|
408
|
+
// Where the image goes and how it tiles, from `backgroundSize`,
|
|
409
|
+
// `backgroundPosition` and `backgroundRepeat`, resolved in
|
|
410
|
+
// core/BackgroundLayers.h: `area` is the rectangle the gradient fills, in this
|
|
411
|
+
// view's coordinates, and `tile` is what it repeats in -- the same rectangle
|
|
412
|
+
// for `repeat`, wider for `space`, and the painting area's extent on an axis
|
|
413
|
+
// that does not repeat, so one step covers it.
|
|
414
|
+
//
|
|
415
|
+
// The gradient's own geometry above is resolved against `area`'s size and
|
|
416
|
+
// offset to its origin, so this layer draws rather than positions.
|
|
417
|
+
CGRect area;
|
|
418
|
+
CGRect tile;
|
|
419
|
+
bool repeats;
|
|
420
|
+
const RnAppKitGradientStop *stops;
|
|
421
|
+
NSInteger stopCount;
|
|
422
|
+
} RnAppKitGradient;
|
|
423
|
+
|
|
424
|
+
// `backgroundImage`. Replaces whatever was there; pass NULL or 0 for none.
|
|
425
|
+
//
|
|
426
|
+
// Drawn above the background colour, which is a layer property and therefore
|
|
427
|
+
// below anything `drawRect:` paints, and below the image and the text, which is
|
|
428
|
+
// where CSS puts a background image. First in the list is on top, so they are
|
|
429
|
+
// drawn back to front.
|
|
430
|
+
- (void)setRnGradients:(nullable const RnAppKitGradient *)gradients count:(NSInteger)count;
|
|
431
|
+
|
|
432
|
+
// How many are set, for the tests and the tree dump.
|
|
433
|
+
@property(nonatomic, readonly) NSInteger rnGradientCount;
|
|
434
|
+
|
|
435
|
+
// One CSS box shadow, in React Native's own terms: `BoxShadow` carries exactly
|
|
436
|
+
// these fields. The colour is RGBA components in that order, as the border
|
|
437
|
+
// colours arrive, so this header stays free of NSColor for the same reason the
|
|
438
|
+
// rest of it is.
|
|
439
|
+
typedef struct {
|
|
440
|
+
CGFloat dx;
|
|
441
|
+
CGFloat dy;
|
|
442
|
+
CGFloat blur;
|
|
443
|
+
CGFloat spread;
|
|
444
|
+
CGFloat color[4];
|
|
445
|
+
bool inset;
|
|
446
|
+
} RnAppKitBoxShadow;
|
|
447
|
+
|
|
448
|
+
// `boxShadow`. Replaces whatever was there; pass NULL or 0 for none.
|
|
449
|
+
//
|
|
450
|
+
// Order is CSS's: the first shadow in the list is the one on top. Each becomes a
|
|
451
|
+
// CALayer of its own with a shadow path and a mask, which is the only way to
|
|
452
|
+
// paint outside a view -- `drawRect:` is clipped to the bounds and an outset
|
|
453
|
+
// shadow is by definition outside them. It is also what React Native's own iOS
|
|
454
|
+
// half does, so a shadow lands in the same place on both.
|
|
455
|
+
- (void)setRnBoxShadows:(nullable const RnAppKitBoxShadow *)shadows count:(NSInteger)count;
|
|
456
|
+
|
|
457
|
+
// How many are set, for the tests and the tree dump.
|
|
458
|
+
@property(nonatomic, readonly) NSInteger rnBoxShadowCount;
|
|
459
|
+
|
|
460
|
+
// The layers those shadows became, bottom to top, for the tests: each one's
|
|
461
|
+
// `shadowPath`, `shadowRadius` and `mask` are the whole feature, and they are
|
|
462
|
+
// what macOS composites rather than anything this code draws.
|
|
463
|
+
@property(nonatomic, readonly) NSArray<CALayer *> *rnBoxShadowLayers;
|
|
464
|
+
|
|
465
|
+
// `filter`, already resolved: the colour matrix the list comes to, one blur
|
|
466
|
+
// radius and one opacity. See core/Filters.h, which is shared with the GTK host
|
|
467
|
+
// and does the arithmetic the Filter Effects spec specifies.
|
|
468
|
+
//
|
|
469
|
+
// `matrix` is row-major in R, G, B, A order and `offset` is added after it, both
|
|
470
|
+
// in straight colour, which is what CIColorMatrix takes. `blurRadius` is CSS's
|
|
471
|
+
// radius, twice the gaussian's sigma. `opacity` is 1 when the list has no
|
|
472
|
+
// `opacity()`.
|
|
473
|
+
// One `dropShadow()` from a filter list: a shadow of the subtree's alpha rather
|
|
474
|
+
// than of its box, which is what makes it a filter and not a box shadow.
|
|
475
|
+
//
|
|
476
|
+
// `standardDeviation` is the gaussian's, which is what React Native parses and
|
|
477
|
+
// what `CALayer.shadowRadius` takes, so it crosses unchanged. The colour is RGBA
|
|
478
|
+
// components, as the border and shadow colours are.
|
|
479
|
+
typedef struct {
|
|
480
|
+
CGFloat dx;
|
|
481
|
+
CGFloat dy;
|
|
482
|
+
CGFloat standardDeviation;
|
|
483
|
+
CGFloat color[4];
|
|
484
|
+
} RnAppKitFilterShadow;
|
|
485
|
+
|
|
486
|
+
typedef struct {
|
|
487
|
+
bool hasMatrix;
|
|
488
|
+
CGFloat matrix[16];
|
|
489
|
+
CGFloat offset[4];
|
|
490
|
+
CGFloat blurRadius;
|
|
491
|
+
CGFloat opacity;
|
|
492
|
+
// Borrowed for the duration of the call, in the order they were written: the
|
|
493
|
+
// first is the one nearest the content, which CSS applying a filter list
|
|
494
|
+
// left to right is what makes true. A CALayer carries one shadow, so the
|
|
495
|
+
// first is the view's own layer's -- cast from the content's alpha -- and
|
|
496
|
+
// each of the rest is a layer of its own cast from the view's box. The
|
|
497
|
+
// difference shows only on content with holes in it, and
|
|
498
|
+
// backlog/platform-macos.md records it.
|
|
499
|
+
const RnAppKitFilterShadow *shadows;
|
|
500
|
+
NSInteger shadowCount;
|
|
501
|
+
} RnAppKitFilters;
|
|
502
|
+
|
|
503
|
+
// `filter`. Pass NULL for none.
|
|
504
|
+
//
|
|
505
|
+
// Applies to this view and everything inside it, which is what CSS does.
|
|
506
|
+
// `CALayer.filters` is the mechanism and is the reason this is one of the few
|
|
507
|
+
// props macOS does better than React Native's own iOS half: Core Image filters
|
|
508
|
+
// on a layer are public API here and private there, so iOS builds a SwiftUI
|
|
509
|
+
// wrapper and a multiply-blend layer to approximate what this sets directly.
|
|
510
|
+
- (void)setRnFilters:(nullable const RnAppKitFilters *)filters;
|
|
511
|
+
|
|
512
|
+
// The Core Image filters the layer is carrying, for the tests: a shadow or a
|
|
513
|
+
// filter is composited by Core Animation and `renderInContext:` draws neither,
|
|
514
|
+
// so what each filter was given is the observable half here. The GTK side has
|
|
515
|
+
// the render tree and checks a pixel.
|
|
516
|
+
@property(nonatomic, readonly) NSArray<CIFilter *> *rnFilters;
|
|
517
|
+
|
|
518
|
+
// The drop shadow the filter list asked for, as the layer properties it became,
|
|
519
|
+
// or all zeroes when there is none. Public for the tests, for the reason
|
|
520
|
+
// `rnFilters` is: Core Animation composites a shadow and `renderInContext:` does
|
|
521
|
+
// not draw one.
|
|
522
|
+
@property(nonatomic, readonly) RnAppKitFilterShadow rnFilterShadow;
|
|
523
|
+
|
|
524
|
+
// `hitSlop`: how far outside its own box this view answers a press, in the order
|
|
525
|
+
// top, right, bottom, left -- the order CSS names edges and the order the border
|
|
526
|
+
// widths arrive in. Null or all zeroes is no slop.
|
|
527
|
+
//
|
|
528
|
+
// Positive values grow the target. A 44pt row with a 20pt icon in it is the
|
|
529
|
+
// reason the prop exists: the icon is what the eye aims at and the finger, or on
|
|
530
|
+
// a desktop a hurried cursor, misses it.
|
|
531
|
+
//
|
|
532
|
+
// It changes hit testing and nothing else: the view is drawn in its own frame,
|
|
533
|
+
// and `RnAppKitHitTest` is the only reader. That also means it applies to hover
|
|
534
|
+
// and to a drop as well as to a press, which is what iOS does, those all going
|
|
535
|
+
// through one hit test.
|
|
536
|
+
- (void)setRnHitSlop:(nullable const CGFloat *)insets;
|
|
537
|
+
|
|
538
|
+
// The box a press has to land in: the bounds, grown by `hitSlop`. Read by
|
|
539
|
+
// `RnAppKitHitTest` and by the tests, which is why it is here rather than in the
|
|
540
|
+
// implementation.
|
|
541
|
+
@property(nonatomic, readonly) NSRect rnHitArea;
|
|
542
|
+
|
|
543
|
+
// `borderStyle`, for the two values that are not solid.
|
|
544
|
+
typedef NS_ENUM(NSInteger, RnAppKitBorderStyle) {
|
|
545
|
+
RnAppKitBorderStyleSolid = 0,
|
|
546
|
+
RnAppKitBorderStyleDotted,
|
|
547
|
+
RnAppKitBorderStyleDashed,
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// A dotted or dashed border is drawn differently from a solid one: one stroked
|
|
551
|
+
// path around the view's own rounded rectangle, with a dash pattern scaled to
|
|
552
|
+
// the border width the way every browser does it, since a fixed pattern looks
|
|
553
|
+
// like a hairline on a thick border and like a solid line on a thin one.
|
|
554
|
+
//
|
|
555
|
+
// It replaces the four-edge fill rather than joining it, or the outline would be
|
|
556
|
+
// painted twice and the dashes would sit on a solid line.
|
|
557
|
+
//
|
|
558
|
+
// One style for the whole outline rather than one per side, which is the limit of
|
|
559
|
+
// this and is the limit on the other hosts for the same reason: a stroked path
|
|
560
|
+
// carries one dash pattern, and React Native's per-side `borderStyles` would need
|
|
561
|
+
// four paths with the corners divided between them. The first side that asks for
|
|
562
|
+
// something other than solid decides the outline, and the backlog records that.
|
|
563
|
+
- (void)setRnBorderStyle:(RnAppKitBorderStyle)style;
|
|
564
|
+
|
|
565
|
+
// `outlineWidth`, `outlineColor`, `outlineOffset` and `outlineStyle`.
|
|
566
|
+
//
|
|
567
|
+
// CSS's outline, which is not a border: drawn *outside* the box, no layout space,
|
|
568
|
+
// and `offset` away from the border edge. A focus ring is what it is for.
|
|
569
|
+
//
|
|
570
|
+
// A layer rather than a `drawRect:`, for the reason an outset box shadow is one:
|
|
571
|
+
// `drawRect:` is clipped to the bounds and this is outside them. It follows the
|
|
572
|
+
// same placement rule as the shadows -- inside the view, or in the parent when
|
|
573
|
+
// the view clips its own layer -- so the two props behave alike.
|
|
574
|
+
//
|
|
575
|
+
// The colour is RGBA components, as the border colours arrive. A zero width is no
|
|
576
|
+
// outline.
|
|
577
|
+
- (void)setRnOutlineWidth:(CGFloat)width
|
|
578
|
+
offset:(CGFloat)offset
|
|
579
|
+
color:(nullable const CGFloat *)color
|
|
580
|
+
style:(RnAppKitBorderStyle)style;
|
|
581
|
+
|
|
582
|
+
// The layer that outline became, for the tests: a stroked shape outside the box,
|
|
583
|
+
// which `renderInContext:` will not draw and a `drawRect:` bitmap cannot see.
|
|
584
|
+
@property(nonatomic, readonly, nullable) CAShapeLayer *rnOutlineLayer;
|
|
585
|
+
|
|
288
586
|
// Fabric's Insert and Remove carry an index into the parent's child list, so
|
|
289
587
|
// that list has to stay in mutation order.
|
|
290
588
|
- (void)insertRnChild:(RnAppKitView *)child atIndex:(NSInteger)index;
|
|
@@ -324,12 +622,70 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
324
622
|
// Accessible states. Each is a tri-state: unset leaves AppKit's default alone,
|
|
325
623
|
// which is not the same as setting it false.
|
|
326
624
|
|
|
625
|
+
// accessibilityValue: a range and a position in it, or a text form that reads
|
|
626
|
+
// better than the number. nil for a part the app did not give, which is how an
|
|
627
|
+
// element says it has none. See the definition: this shares AppKit's one
|
|
628
|
+
// `accessibilityValue` with the checked state, and an explicit value wins.
|
|
629
|
+
- (void)setRnAccessibleValueMin:(nullable NSNumber *)min
|
|
630
|
+
max:(nullable NSNumber *)max
|
|
631
|
+
now:(nullable NSNumber *)now
|
|
632
|
+
text:(nullable NSString *)text;
|
|
633
|
+
|
|
327
634
|
- (void)setRnAccessibleStateDisabled:(RnAppKitAccessibleFlag)disabled
|
|
328
635
|
checked:(RnAppKitAccessibleFlag)checked
|
|
329
636
|
selected:(RnAppKitAccessibleFlag)selected
|
|
330
637
|
expanded:(RnAppKitAccessibleFlag)expanded
|
|
331
638
|
busy:(RnAppKitAccessibleFlag)busy;
|
|
332
639
|
|
|
640
|
+
// `accessibilityLabelledBy`, already resolved: the views whose text names this
|
|
641
|
+
// one. Pass nil or an empty array to take the relation off again.
|
|
642
|
+
//
|
|
643
|
+
// The prop names other views by their `nativeID`, and the resolution -- and
|
|
644
|
+
// above all *when* to resolve, Fabric mounting a field before the label that
|
|
645
|
+
// follows it -- is shared with the GTK host in core/LabelRegistry.h. This layer
|
|
646
|
+
// is handed the answer.
|
|
647
|
+
//
|
|
648
|
+
// **AppKit takes one.** `accessibilityTitleUIElement` is a single element where
|
|
649
|
+
// the prop is a list and GTK's relation is a list, so the first is used and the
|
|
650
|
+
// rest are kept only for the tree dump, which reports what was resolved on both
|
|
651
|
+
// hosts. backlog/accessibility.md records that.
|
|
652
|
+
- (void)setRnLabelledBy:(nullable NSArray<RnAppKitView *> *)labels;
|
|
653
|
+
|
|
654
|
+
// `experimental_accessibilityOrder`, resolved: the children this view wants a
|
|
655
|
+
// screen reader to read, in that order. Sets `accessibilityChildren`, which is
|
|
656
|
+
// what VoiceOver walks in place of the view hierarchy.
|
|
657
|
+
- (void)setRnAccessibilityOrder:(nullable NSArray<RnAppKitView *> *)children;
|
|
658
|
+
|
|
659
|
+
// The text of this view and everything inside it, as a screen reader would read
|
|
660
|
+
// it: each paragraph's string in tree order, separated by single spaces.
|
|
661
|
+
//
|
|
662
|
+
// Here for `accessibilityLiveRegion`, which announces a status message when it
|
|
663
|
+
// changes and so needs to know what the message currently says. An accessible
|
|
664
|
+
// label stands in for the text where one was set, which the mounting manager
|
|
665
|
+
// decides before calling this, as the GTK host does.
|
|
666
|
+
@property(nonatomic, readonly) NSString *rnCollectedText;
|
|
667
|
+
|
|
668
|
+
// The text this view draws itself, or nil: the paragraph it holds, and nothing
|
|
669
|
+
// from its children. The recursive half of `rnCollectedText` reads it from each
|
|
670
|
+
// view it walks, which is why it is here rather than in the implementation.
|
|
671
|
+
@property(nonatomic, readonly, nullable) NSString *rnOwnText;
|
|
672
|
+
|
|
673
|
+
// Reads `text` out to VoiceOver now, as `accessibilityLiveRegion` asks. Not a
|
|
674
|
+
// property: AppKit posts an announcement, and whether anybody is listening is
|
|
675
|
+
// VoiceOver's business.
|
|
676
|
+
//
|
|
677
|
+
// `assertive` asks to interrupt what is being read; polite waits for a gap,
|
|
678
|
+
// which is what the two values of the prop mean.
|
|
679
|
+
//
|
|
680
|
+
// The announcement is also remembered, which is the only way to see that it
|
|
681
|
+
// happened: nothing in an automated run is running VoiceOver, so
|
|
682
|
+
// `rnLastAnnouncement` is what the tests read. Deliberately not in the tree dump
|
|
683
|
+
// -- an announcement is an event and the dump is state -- and the host logs it.
|
|
684
|
+
- (void)rnAnnounce:(NSString *)text assertive:(BOOL)assertive;
|
|
685
|
+
|
|
686
|
+
// The last text this view announced, or nil.
|
|
687
|
+
@property(nonatomic, readonly, nullable) NSString *rnLastAnnouncement;
|
|
688
|
+
|
|
333
689
|
// Hidden from assistive technology, for accessible={false} and
|
|
334
690
|
// accessibilityElementsHidden.
|
|
335
691
|
- (void)setRnAccessibleHidden:(BOOL)hidden;
|
|
@@ -340,6 +696,120 @@ typedef NS_ENUM(NSInteger, RnAppKitImageFit) {
|
|
|
340
696
|
// same idea. Stored and never drawn.
|
|
341
697
|
@property(nonatomic, copy, nullable) NSString *rnNativeId;
|
|
342
698
|
|
|
699
|
+
// The app's `testID`, which is not `nativeID`: one is how an app names a view
|
|
700
|
+
// to itself, the other is how something driving the app from outside finds it.
|
|
701
|
+
// Setting it sets `accessibilityIdentifier`, which is what XCTest, Appium and
|
|
702
|
+
// the Accessibility Inspector all look for, and it is printed in the tree dump
|
|
703
|
+
// the way React Native spelled it.
|
|
704
|
+
@property(nonatomic, copy, nullable) NSString *rnTestId;
|
|
705
|
+
|
|
706
|
+
// `accessibilityViewIsModal`: a screen reader should stay inside this view
|
|
707
|
+
// rather than reading what is behind it. Sets AppKit's `accessibilityModal`,
|
|
708
|
+
// which is the same idea as ARIA's `aria-modal` and UIA's `IsDialog`.
|
|
709
|
+
@property(nonatomic, assign) BOOL rnAccessibleModal;
|
|
710
|
+
|
|
711
|
+
// `writingDirection`, for the tree dump: "ltr", "rtl", "natural", or nil when
|
|
712
|
+
// the app asked for nothing. The direction itself is in the paragraph style the
|
|
713
|
+
// layout carries; this is so the two hosts' dumps compare line by line.
|
|
714
|
+
@property(nonatomic, copy, nullable) NSString *rnWritingDirection;
|
|
715
|
+
|
|
716
|
+
// The edge the paragraph's text sits against, resolved: @"left", @"center",
|
|
717
|
+
// @"right", @"justified", or nil for a view that is not a paragraph. From
|
|
718
|
+
// core/TextAlignments.h, so the three hosts print the same word for the same
|
|
719
|
+
// answer -- `textAlign: 'end'` is one word and two edges.
|
|
720
|
+
@property(nonatomic, copy, nullable) NSString *rnTextAlign;
|
|
721
|
+
|
|
722
|
+
// `textAlignVertical`, for the tree dump: @"auto", @"top", @"center",
|
|
723
|
+
// @"bottom", or nil when the app said nothing. The prop rather than the offset,
|
|
724
|
+
// which core/TextAlignments.h argues.
|
|
725
|
+
@property(nonatomic, copy, nullable) NSString *rnTextVerticalAlign;
|
|
726
|
+
|
|
727
|
+
// Where the paragraph's box ends and its text begins: padding and border,
|
|
728
|
+
// which Fabric adds up into `layoutMetrics.contentInsets`.
|
|
729
|
+
//
|
|
730
|
+
// A <Text> may carry padding of its own, and React Native's own LogBox does:
|
|
731
|
+
// its message is a `<Text style={{paddingHorizontal: 12}}>` while the heading
|
|
732
|
+
// above it is a padded <View> around one, so the two lined up on a phone and
|
|
733
|
+
// did not here. Yoga lays the box out with the padding either way; what was
|
|
734
|
+
// missing is that the paragraph was drawn at the box's corner rather than at
|
|
735
|
+
// the content's.
|
|
736
|
+
//
|
|
737
|
+
// Everything that reads a point against the text uses it: the draw, the
|
|
738
|
+
// selection highlight that is drawn with it, the vertical alignment, and
|
|
739
|
+
// `rnTextIndexAtPoint:`. React Native offsets an inline view's frame by the
|
|
740
|
+
// same insets in `ParagraphShadowNode::layout`, so a paragraph drawn at the
|
|
741
|
+
// corner also disagreed with the views inside it.
|
|
742
|
+
@property(nonatomic) NSEdgeInsets rnTextInset;
|
|
743
|
+
|
|
744
|
+
// What this scroll view's snapping is set to, as core/ScrollSnap.h formats it:
|
|
745
|
+
// a fragment the tree dump prints verbatim, or nil for a view that does not
|
|
746
|
+
// snap. Pushed in by the scroll manager, which is where the props are read.
|
|
747
|
+
@property(nonatomic, copy, nullable) NSString *rnScrollSnap;
|
|
748
|
+
|
|
749
|
+
// `spellCheck` and `autoCorrect`, for the tree dump: @"on", @"off" or nil for a
|
|
750
|
+
// field that said nothing, which is the third state rather than a default. What
|
|
751
|
+
// this host does with them is two properties on the NSTextView a field is or
|
|
752
|
+
// borrows; see AppKitTextPeer.h.
|
|
753
|
+
@property(nonatomic, copy, nullable) NSString *rnSpellCheck;
|
|
754
|
+
@property(nonatomic, copy, nullable) NSString *rnAutoCorrect;
|
|
755
|
+
|
|
756
|
+
// `autoCapitalize` and `keyboardType`, for the tree dump, as React Native
|
|
757
|
+
// spells them. **This host acts on neither**, and says so rather than
|
|
758
|
+
// pretending: macOS has no per-field automatic capitalisation
|
|
759
|
+
// (`NSSpellChecker`'s is the person's system setting) and no software keyboard
|
|
760
|
+
// to choose a layout for. GTK turns both into input hints and an input
|
|
761
|
+
// purpose, so printing the same words is what keeps the two dumps comparable
|
|
762
|
+
// and puts the difference in the support page, where it belongs.
|
|
763
|
+
@property(nonatomic, copy, nullable) NSString *rnAutoCapitalize;
|
|
764
|
+
@property(nonatomic, copy, nullable) NSString *rnKeyboardType;
|
|
765
|
+
|
|
766
|
+
// `caretHidden` and `contextMenuHidden`, for the tree dump. What this host
|
|
767
|
+
// *does* with them is a clear insertion point and a peer that refuses to build
|
|
768
|
+
// a menu; see AppKitTextPeer.h, which also records the half of the second one a
|
|
769
|
+
// single-line field cannot answer. Printed because each is the absence of
|
|
770
|
+
// something -- a blink, a menu -- so nothing else can see that they arrived.
|
|
771
|
+
@property(nonatomic, assign) BOOL rnCaretHidden;
|
|
772
|
+
@property(nonatomic, assign) BOOL rnContextMenuHidden;
|
|
773
|
+
|
|
774
|
+
// The `cursor` style property, as the CSS keyword React Native uses: "pointer",
|
|
775
|
+
// "text", "grab", "ns-resize" and the rest. Nil or empty leaves the cursor to
|
|
776
|
+
// whatever encloses this view, which is what `cursor: 'auto'` means.
|
|
777
|
+
//
|
|
778
|
+
// A keyword rather than an NSCursor because the name is what crosses the seam
|
|
779
|
+
// from the mounting manager, shared with the GTK host through
|
|
780
|
+
// core/CursorNames.h, and because macOS has no cursor for some of them: the
|
|
781
|
+
// mapping and its holes belong on this side. See `rnResolvedCursor`.
|
|
782
|
+
- (void)setRnCursorName:(nullable NSString *)name;
|
|
783
|
+
|
|
784
|
+
// The NSCursor that name resolves to, or nil for a keyword macOS has no cursor
|
|
785
|
+
// for -- "wait", "help", "move", "progress", "cell", "all-scroll". Nil means
|
|
786
|
+
// this view installs no cursor rect at all, so the cursor is inherited rather
|
|
787
|
+
// than forced back to an arrow.
|
|
788
|
+
//
|
|
789
|
+
// Public for the tests, which assert the mapping, and read by nothing else: the
|
|
790
|
+
// cursor itself is applied through `resetCursorRects`, as AppKit expects.
|
|
791
|
+
@property(nonatomic, readonly, nullable) NSCursor *rnResolvedCursor;
|
|
792
|
+
|
|
793
|
+
// `mixBlendMode`, as the CSS keyword, shared with the GTK host through
|
|
794
|
+
// core/BlendModes.h: "multiply", "screen", "color-dodge" and the rest. Nil or
|
|
795
|
+
// empty is `normal`, meaning ordinary source-over compositing.
|
|
796
|
+
//
|
|
797
|
+
// Core Animation does the whole feature in one property on macOS:
|
|
798
|
+
// `CALayer.compositingFilter` takes a Core Image blend-mode filter and blends
|
|
799
|
+
// the layer with what is already beneath it. The keyword maps onto one
|
|
800
|
+
// `CI<Name>BlendMode` per CSS mode, including `plus-lighter`, which GSK has no
|
|
801
|
+
// node for.
|
|
802
|
+
- (void)setRnBlendModeName:(nullable NSString *)name;
|
|
803
|
+
|
|
804
|
+
// The keyword that was set, or nil: what the app asked for.
|
|
805
|
+
@property(nonatomic, readonly, nullable) NSString *rnBlendModeName;
|
|
806
|
+
|
|
807
|
+
// The name of the Core Image filter that keyword resolved to, or nil. Public for
|
|
808
|
+
// the tests, which assert the mapping and that Core Image has each filter: a
|
|
809
|
+
// compositing filter cannot be seen in a snapshot, because `renderInContext:`
|
|
810
|
+
// composites nothing.
|
|
811
|
+
@property(nonatomic, readonly, nullable) NSString *rnBlendFilterName;
|
|
812
|
+
|
|
343
813
|
// Set on the surface root. See RnAppKitInputHandler.
|
|
344
814
|
@property(nonatomic, weak, nullable) id<RnAppKitInputHandler> rnInputHandler;
|
|
345
815
|
|
|
@@ -404,6 +874,28 @@ typedef NS_ENUM(NSInteger, RnAppKitControlKind) {
|
|
|
404
874
|
// job is to have one covering it. That view is not an RnAppKitView, so it stays
|
|
405
875
|
// out of the paint order, out of hit testing and out of `describeTree`'s
|
|
406
876
|
// children -- the numbers are reported on the ScrollView's own line instead.
|
|
877
|
+
// `indicatorStyle`, arriving as the colour `core/ScrollIndicator.h` resolved it
|
|
878
|
+
// to rather than as the enum: the mapping is the same on all three desktops and
|
|
879
|
+
// belongs in one place, and this layer knows nothing about React Native's
|
|
880
|
+
// vocabulary.
|
|
881
|
+
- (void)setRnScrollIndicatorColourRed:(CGFloat)red
|
|
882
|
+
green:(CGFloat)green
|
|
883
|
+
blue:(CGFloat)blue
|
|
884
|
+
alpha:(CGFloat)alpha;
|
|
885
|
+
|
|
886
|
+
// The hairline around the thumb, which `indicatorStyle: 'default'` has and the
|
|
887
|
+
// other two styles do not: iOS's default is black with a white border, and
|
|
888
|
+
// that border is the whole difference between `default` and `black`. A
|
|
889
|
+
// transparent colour means none. Resolved by `core/ScrollIndicator.h`, like
|
|
890
|
+
// the fill, so the three hosts cannot drift.
|
|
891
|
+
- (void)setRnScrollIndicatorBorderColourRed:(CGFloat)red
|
|
892
|
+
green:(CGFloat)green
|
|
893
|
+
blue:(CGFloat)blue
|
|
894
|
+
alpha:(CGFloat)alpha;
|
|
895
|
+
// What that colour is now, for the test suite: the thumb is drawn into a layer
|
|
896
|
+
// a test can rasterise, but reading the colour back is the cheaper assertion.
|
|
897
|
+
- (NSColor *)rnScrollIndicatorColour;
|
|
898
|
+
|
|
407
899
|
- (void)setRnScrollIndicatorVerticalOffset:(CGFloat)verticalOffset
|
|
408
900
|
verticalLength:(CGFloat)verticalLength
|
|
409
901
|
horizontalOffset:(CGFloat)horizontalOffset
|