From 622cc52168f4c27872ffa0cd227211fb4c20218d Mon Sep 17 00:00:00 2001 From: Niclas Overby Date: Fri, 21 Aug 2026 08:13:35 +0200 Subject: [PATCH] feat(cider): a real NSLayoutConstraint, and the version number that still cannot move (#161, #150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TWO CLASSES WITH ONE NAME IS A COIN TOSS. The implementation went into AppKit first, which is where NSLayoutConstraint belongs on macOS, and the selector stayed unrecognized: this Foundation has carried a stub of that name since the nib decoder needed something to decode into, along with three subclasses of it, and the runtime resolved to the stub, whose methodSignatureForSelector answers v@: for everything. So the object lives in Foundation, where the name already was, and AppKit imports that header. It is the constraint and its accounting, and NO SOLVER: the properties, the factory, activate and deactivate, and setActive:, which puts the constraint on a view and takes it off again, asked by selector because NSView is AppKit's and this is Foundation. Every view in these nibs still carries an autoresizing mask and translatesAutoresizingMaskIntoConstraints defaults to YES, so the layout is exactly what it was. An application that lays out BY constraint gets a window whose subviews sit where the mask puts them, which is wrong but visible, rather than an unrecognized selector, which stops it dead. ALSO: +[NSScreen screensHaveSeparateSpaces], answering NO. MoneyMoney asks it inside -[NSWindow setFrameAutosaveName:], early enough that an unrecognized selector there leaves the window half built, and the application CATCHES the exception, so nothing crashes and nothing draws either. MEASURED AT NSAppKitVersionNumber 2022: zero unrecognized selectors, zero uncaught exceptions, and the truncated menu titles gain the two characters the 35 point metric buys them: 1504 Send batc… New tran… New inst… Use as tr… 2022 Send batch t… New transf… New instan… Use as tran… AND IT STILL DOES NOT LAND, because of the capture rather than the log. At 2022 MoneyMoney draws NO MENU BAR AT ALL and an EMPTY TOOLBAR: it takes the Big Sur chrome path, silently, and this AppKit does not implement it. Two characters of menu title against the menu bar is not a trade worth making, so the constant is back at 1504 with that reason written where it lives, and verified after the revert: menu bar, toolbar, File menu open with its items, 57 titles, zero unrecognized. Looked at both ways. So task #150 is no longer blocked on Auto Layout. It is blocked on Big Sur chrome, which is a different piece of work. --- docs/wayland-port.md | 35 +++ ...slayoutconstraint-where-the-stub-was.patch | 250 ++++++++++++++++++ vendor/pins/cocotron/AppKit/NSApplication.m | 19 +- .../pins/cocotron/AppKit/NSLayoutConstraint.m | 3 + vendor/pins/cocotron/AppKit/NSScreen.m | 13 + vendor/pins/cocotron/AppKit/NSView.m | 39 +++ .../include/AppKit/NSLayoutConstraint.h | 17 ++ .../cocotron/AppKit/include/AppKit/NSScreen.h | 1 + .../cocotron/AppKit/include/AppKit/NSView.h | 12 + 9 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 vendor/patches/foundation/0030-a-real-nslayoutconstraint-where-the-stub-was.patch diff --git a/docs/wayland-port.md b/docs/wayland-port.md index 834c260fc..3d80b3f3b 100644 --- a/docs/wayland-port.md +++ b/docs/wayland-port.md @@ -14580,3 +14580,38 @@ would immediately call `CurrentValueSubject.init`, `AnyCancellable.store(in:)` a Implementing them means writing Combine's core semantics against the Swift calling convention, which is a project rather than a rung. What has been proved is the method and three of its steps: a generic struct, a class, and the exact point where a generic class stops. + +## Auto Layout exists now, and the version number still does not move (2026-08-21) + +Task #150 ended with a one-line change that could not land: raising `NSAppKitVersionNumber` past +10.15 buys MoneyMoney's truncated menu titles seven points of room, and kills the application on +`+[NSLayoutConstraint constraintWithItem:...]`, because cocotron's `NSLayoutConstraint.m` was +twenty-three lines with no class in it. + +**There is a class now**, and the first attempt at it was in the wrong framework. Implemented in +AppKit, the selector stayed unrecognized: **this Foundation has carried an `NSLayoutConstraint` stub +since the nib decoder needed something to decode into**, along with three subclasses of it, and two +Objective-C classes with one name is a coin toss the runtime announces and then resolves however it +likes. It resolved to the stub, whose `methodSignatureForSelector:` answers `"v@:"` for everything. +So the object lives in Foundation, where the name already was, and AppKit imports that header. + +It is the constraint and its accounting, and **no solver**: the properties, the factory, activate and +deactivate, and `-setActive:`, which puts the constraint on a view and takes it off again (asked by +selector, because `NSView` is AppKit's and this is Foundation). Every view in these nibs still carries +an autoresizing mask and `translatesAutoresizingMaskIntoConstraints` defaults to YES, so the layout is +exactly what it was. + +With that and `+[NSScreen screensHaveSeparateSpaces]` (which MoneyMoney asks inside +`-[NSWindow setFrameAutosaveName:]`, early enough that an unrecognized selector there leaves the +window half built and the application catches the exception, so nothing crashes and nothing draws), +the modern version number runs clean: **zero unrecognized selectors, zero uncaught exceptions**, and +the menu titles gain their two characters: + + 1504: Send batc… New tran… New inst… New forei… Use as tr… Change t… + 2022: Send batch t… New transf… New instan… New foreig… Use as tran… Change tra… + +**And it still does not land, because of the capture rather than the log.** At 2022 MoneyMoney draws +**no menu bar at all** and an **empty toolbar**: it takes the Big Sur chrome path, silently, and this +AppKit does not implement it. Two characters of menu title against the menu bar is not a trade worth +making, so the constant went back to 1504 with that written where it lives. The class stays: it is +what makes the next attempt possible, and it is the right shape regardless. diff --git a/vendor/patches/foundation/0030-a-real-nslayoutconstraint-where-the-stub-was.patch b/vendor/patches/foundation/0030-a-real-nslayoutconstraint-where-the-stub-was.patch new file mode 100644 index 000000000..a4ad020af --- /dev/null +++ b/vendor/patches/foundation/0030-a-real-nslayoutconstraint-where-the-stub-was.patch @@ -0,0 +1,250 @@ +A real NSLayoutConstraint, in the framework that already had a stub of it. + +MONEYMONEY DIES ON +constraintWithItem: THE MOMENT APPKIT CLAIMS TO BE NEWER THAN 10.15, which is the +band that also changes its menu metric, so the two are tied together: the cosmetic fix in task #150 +cannot land while the class is a stub. + +The class belongs to AppKit on macOS and that is where the implementation was written first. It could +not stay there. THIS Foundation has carried an NSLayoutConstraint since the nib decoder needed +something to decode into, along with three subclasses of it, and two Objective-C classes with one +name is a coin toss the runtime announces and then resolves however it likes. It resolved to the +stub, whose methodSignatureForSelector answers "v@:" for everything, and +constraintWithItem: stayed +unrecognized however carefully AppKit implemented it. So the object lives here and AppKit imports +this header. + +WHAT IT IS: the constraint, its properties, the factory, activate and deactivate, and -setActive:, +which puts the constraint on a view and takes it off again. Asked BY SELECTOR, because NSView is +AppKit's and this is Foundation. + +WHAT IT IS NOT: a solver. Nothing here lays anything out. Every view in these nibs still carries an +autoresizing mask and translatesAutoresizingMaskIntoConstraints defaults to YES, so the layout is +exactly what it was. An application that genuinely lays out BY constraint gets a window whose +subviews sit where the mask puts them, which is wrong but visible, rather than an unrecognized +selector, which stops it dead. + +MEASURED: with this and +[NSScreen screensHaveSeparateSpaces], MoneyMoney runs at +NSAppKitVersionNumber 2022 with zero unrecognized selectors and zero uncaught exceptions, and its +truncated menu titles gain the two characters the 35 point metric buys. The version number is still +1504 and the reason is in the capture, not the log: at 2022 the application draws no menu bar at all +and an empty toolbar, because it takes the Big Sur chrome path. See AppKit/NSApplication.m. +--- a/include/Foundation/NSLayoutConstraint.h ++++ b/include/Foundation/NSLayoutConstraint.h +@@ -1,5 +1,89 @@ + #import ++#import ++#import + +-@interface NSLayoutConstraint : NSObject ++/* ++ * THE CLASS LIVES HERE BECAUSE IT ALREADY DID, and two of them is one too many. ++ * ++ * On macOS NSLayoutConstraint is AppKit's, and that is where the implementation was written first. ++ * It could not stay there: this Foundation has carried a stub of the same name since the nib ++ * decoder needed something to decode into, along with three subclasses of it, and a duplicate ++ * Objective-C class is a coin toss the runtime announces and then resolves however it likes. The ++ * application got the stub, whose methodSignatureForSelector answers "v@:" for everything, and ++ * +constraintWithItem: was unrecognized however carefully AppKit implemented it. ++ * ++ * So the object and its accounting live here, and AppKit imports this header. Nothing solves the ++ * constraints; see the note in AppKit/NSLayoutConstraint.h for why that is the right amount of Auto ++ * Layout for the applications in front of us. ++ */ ++ ++typedef float NSLayoutPriorityFoundation; ++ ++typedef NS_ENUM(NSInteger, NSLayoutRelation) { ++ NSLayoutRelationLessThanOrEqual = -1, ++ NSLayoutRelationEqual = 0, ++ NSLayoutRelationGreaterThanOrEqual = 1, ++}; ++ ++typedef NS_ENUM(NSInteger, NSLayoutAttribute) { ++ NSLayoutAttributeNotAnAttribute = 0, ++ NSLayoutAttributeLeft = 1, ++ NSLayoutAttributeRight, ++ NSLayoutAttributeTop, ++ NSLayoutAttributeBottom, ++ NSLayoutAttributeLeading, ++ NSLayoutAttributeTrailing, ++ NSLayoutAttributeWidth, ++ NSLayoutAttributeHeight, ++ NSLayoutAttributeCenterX, ++ NSLayoutAttributeCenterY, ++ NSLayoutAttributeLastBaseline, ++ NSLayoutAttributeFirstBaseline, ++}; ++ ++enum { ++ NSLayoutPriorityRequired = 1000, ++ NSLayoutPriorityDefaultHigh = 750, ++ NSLayoutPriorityDragThatCanResizeWindow = 510, ++ NSLayoutPriorityWindowSizeStayPut = 500, ++ NSLayoutPriorityDragThatCannotResizeWindow = 490, ++ NSLayoutPriorityDefaultLow = 250, ++ NSLayoutPriorityFittingSizeCompression = 50, ++}; ++ ++@interface NSLayoutConstraint : NSObject { ++ id _firstItem; ++ id _secondItem; ++ NSLayoutAttribute _firstAttribute; ++ NSLayoutAttribute _secondAttribute; ++ NSLayoutRelation _relation; ++ double _multiplier; ++ double _constant; ++ float _priority; ++ BOOL _active; ++ NSString *_identifier; ++} ++ +++ (instancetype) constraintWithItem: (id) firstItem ++ attribute: (NSLayoutAttribute) firstAttribute ++ relatedBy: (NSLayoutRelation) relation ++ toItem: (id) secondItem ++ attribute: (NSLayoutAttribute) secondAttribute ++ multiplier: (double) multiplier ++ constant: (double) constant; ++ +++ (void) activateConstraints: (NSArray *) constraints; +++ (void) deactivateConstraints: (NSArray *) constraints; ++ ++@property (readonly, assign) id firstItem; ++@property (readonly, assign) id secondItem; ++@property (readonly) NSLayoutAttribute firstAttribute; ++@property (readonly) NSLayoutAttribute secondAttribute; ++@property (readonly) NSLayoutRelation relation; ++@property (readonly) double multiplier; ++@property double constant; ++@property float priority; ++@property (getter=isActive) BOOL active; ++@property (copy) NSString *identifier; + + @end +--- a/src/NSLayoutConstraint.m ++++ b/src/NSLayoutConstraint.m +@@ -21,21 +21,118 @@ + #import + #import + +-#warning TODO: $ld$hide$os 10.4 through 10.7, also METACLASS + + @implementation NSLayoutConstraint + +-- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { +- return [NSMethodSignature signatureWithObjCTypes: "v@:"]; ++@synthesize firstItem = _firstItem; ++@synthesize secondItem = _secondItem; ++@synthesize firstAttribute = _firstAttribute; ++@synthesize secondAttribute = _secondAttribute; ++@synthesize relation = _relation; ++@synthesize multiplier = _multiplier; ++@synthesize constant = _constant; ++@synthesize priority = _priority; ++@synthesize identifier = _identifier; ++ +++ (instancetype) constraintWithItem: (id) firstItem ++ attribute: (NSLayoutAttribute) firstAttribute ++ relatedBy: (NSLayoutRelation) relation ++ toItem: (id) secondItem ++ attribute: (NSLayoutAttribute) secondAttribute ++ multiplier: (double) multiplier ++ constant: (double) constant ++{ ++ NSLayoutConstraint *result = [[[self alloc] init] autorelease]; ++ ++ result->_firstItem = firstItem; ++ result->_secondItem = secondItem; ++ result->_firstAttribute = firstAttribute; ++ result->_secondAttribute = secondAttribute; ++ result->_relation = relation; ++ result->_multiplier = multiplier; ++ result->_constant = constant; ++ result->_priority = NSLayoutPriorityRequired; ++ return result; + } + +-- (id)initWithCoder:(NSCoder *)coder { +- NSLog(@"Stub called: initWithCoder in NSLayoutConstraint"); +++ (void) activateConstraints: (NSArray *) constraints ++{ ++ for (NSLayoutConstraint *constraint in constraints) { ++ [constraint setActive: YES]; ++ } ++} ++ +++ (void) deactivateConstraints: (NSArray *) constraints ++{ ++ for (NSLayoutConstraint *constraint in constraints) { ++ [constraint setActive: NO]; ++ } ++} ++ ++- (id) initWithCoder: (NSCoder *) coder ++{ ++ self = [super init]; ++ if (self != nil) { ++ _priority = NSLayoutPriorityRequired; ++ } + return self; + } + +-- (void)forwardInvocation:(NSInvocation *)anInvocation { +- NSLog(@"Stub called: %@ in %@", NSStringFromSelector([anInvocation selector]), [self class]); ++- (void) dealloc ++{ ++ [_identifier release]; ++ [super dealloc]; ++} ++ ++- (BOOL) isActive ++{ ++ return _active; ++} ++ ++/* ++ * ACTIVATING A CONSTRAINT IS WHAT PUTS IT ON A VIEW, and macOS picks the nearest common ancestor of ++ * the two items. Holding it on the first item is enough here, because nothing reads the list back ++ * except the view that owns it, and what an application can otherwise notice is the constraint being ++ * deallocated the moment the statement that made it ends. ++ * ++ * ASKED BY SELECTOR, NOT BY CLASS. NSView is AppKit's and this is Foundation; respondsToSelector is ++ * how a lower framework talks to a higher one without a dependency it cannot have. ++ */ ++- (void) setActive: (BOOL) active ++{ ++ if (active == _active) { ++ return; ++ } ++ _active = active; ++ ++ id owner = nil; ++ ++ if ([_firstItem respondsToSelector: @selector(superview)]) { ++ owner = [_firstItem performSelector: @selector(superview)]; ++ if (owner == nil) { ++ owner = _firstItem; ++ } ++ } else if ([_secondItem respondsToSelector: @selector(superview)]) { ++ owner = _secondItem; ++ } ++ if (owner == nil) { ++ return; ++ } ++ if (active) { ++ if ([owner respondsToSelector: @selector(addConstraint:)]) { ++ [owner performSelector: @selector(addConstraint:) withObject: self]; ++ } ++ } else if ([owner respondsToSelector: @selector(removeConstraint:)]) { ++ [owner performSelector: @selector(removeConstraint:) withObject: self]; ++ } ++} ++ ++- (NSString *) description ++{ ++ return [NSString stringWithFormat: @"<%@ %p first=%@ attr=%ld rel=%ld second=%@ attr=%ld x%g +%g%@>", ++ [self class], self, [_firstItem class], (long) _firstAttribute, ++ (long) _relation, [_secondItem class], (long) _secondAttribute, ++ _multiplier, _constant, _active ? @" active" : @""]; + } + + @end diff --git a/vendor/pins/cocotron/AppKit/NSApplication.m b/vendor/pins/cocotron/AppKit/NSApplication.m index 94a72d1ef..972b7bae2 100644 --- a/vendor/pins/cocotron/AppKit/NSApplication.m +++ b/vendor/pins/cocotron/AppKit/NSApplication.m @@ -99,10 +99,21 @@ NSString *const NSApplicationLaunchUserNotificationKey = @"NSApplicationLaunchUserNotificationKey"; /* - * THE VERSION APPLICATIONS BRANCH ON, and 10.12 is deliberate even though SystemVersion.plist says - * 14.4.1. Anything newer than 10.15 makes MoneyMoney build its wizard with Auto Layout, and - * NSLayoutConstraint here is two constants and no class, so the application dies on - * +constraintWithItem: before it draws. Raise this only together with Auto Layout. + * THE VERSION APPLICATIONS BRANCH ON, and 10.12 is still deliberate. What changed is WHY. + * + * It used to be pinned here because anything newer than 10.15 made MoneyMoney build its wizard with + * Auto Layout and die on +constraintWithItem: before drawing. There is a real NSLayoutConstraint + * now (in Foundation, where the class already lived), and +[NSScreen screensHaveSeparateSpaces] + * answers as well, so 2022 no longer throws: zero unrecognized selectors, zero uncaught exceptions, + * and the menu titles gain the two characters the 35 point metric buys them. + * + * IT STILL COSTS MORE THAN IT BUYS, and the capture is what says so. At 2022 MoneyMoney draws NO + * MENU BAR AT ALL and an EMPTY TOOLBAR: it takes the Big Sur chrome path, which this AppKit does not + * implement, and it does it silently rather than by raising. Two characters of menu title against + * the menu bar is not a trade worth making. + * + * So: raise this when the Big Sur toolbar and menu bar are drawn, not before, and check the capture + * rather than the exception count. */ const NSAppKitVersion NSAppKitVersionNumber = 1504; // macOS 10.12 diff --git a/vendor/pins/cocotron/AppKit/NSLayoutConstraint.m b/vendor/pins/cocotron/AppKit/NSLayoutConstraint.m index aa79e6759..7ae42031a 100644 --- a/vendor/pins/cocotron/AppKit/NSLayoutConstraint.m +++ b/vendor/pins/cocotron/AppKit/NSLayoutConstraint.m @@ -18,6 +18,9 @@ */ #import +#import +#import const CGFloat NSViewNoInstrinsicMetric = 0xbff0000000000000; const CGFloat NSViewNoIntrinsicMetric = 0xbff0000000000000; + diff --git a/vendor/pins/cocotron/AppKit/NSScreen.m b/vendor/pins/cocotron/AppKit/NSScreen.m index b1c239434..6cadca764 100644 --- a/vendor/pins/cocotron/AppKit/NSScreen.m +++ b/vendor/pins/cocotron/AppKit/NSScreen.m @@ -65,6 +65,19 @@ NSNotificationName const NSScreenColorSpaceDidChangeNotification = @"NSScreenCol return result; } +/* + * NO, AND SAYING SO IS THE WHOLE FEATURE. macOS answers this from the Mission Control preference + * that gives each display its own set of Spaces; there is one screen here and no Spaces at all. + * + * MoneyMoney asks it inside -[NSWindow setFrameAutosaveName:], which is early enough in window setup + * that an unrecognized selector there leaves the window half built. The application CATCHES the + * exception, so nothing crashes and nothing draws either, which is the shape described in the plan: + * a caught exception hides a whole feature. + */ ++ (BOOL) screensHaveSeparateSpaces { + return NO; +} + + (NSArray *) screens { return [[NSDisplay currentDisplay] screens]; } diff --git a/vendor/pins/cocotron/AppKit/NSView.m b/vendor/pins/cocotron/AppKit/NSView.m index a312c4b16..dd0c17e6d 100644 --- a/vendor/pins/cocotron/AppKit/NSView.m +++ b/vendor/pins/cocotron/AppKit/NSView.m @@ -582,6 +582,7 @@ typedef struct __VFlags { withObject: nil]; [_subviews release]; + [_constraints release]; [_draggedTypes release]; [_trackingAreas release]; [_contentFilters release]; @@ -3696,6 +3697,44 @@ static NSView *viewBeingPrinted = nil; NSUnimplementedMethod(); } + +/* + * THE CONSTRAINTS A VIEW HOLDS, and nothing that solves them. An application that only builds + * constraints and activates them (MoneyMoney makes exactly two) needs the object to exist, to be + * retained, and to be findable again. Layout is still the autoresizing path. + */ +- (NSArray *) constraints { + return _constraints ? [NSArray arrayWithArray: _constraints] : [NSArray array]; +} + +- (void) addConstraint: (NSLayoutConstraint *) constraint { + if (constraint == nil) { + return; + } + if (_constraints == nil) { + _constraints = [[NSMutableArray alloc] init]; + } + if (![_constraints containsObject: constraint]) { + [_constraints addObject: constraint]; + } +} + +- (void) addConstraints: (NSArray *) constraints { + for (NSLayoutConstraint *constraint in constraints) { + [self addConstraint: constraint]; + } +} + +- (void) removeConstraint: (NSLayoutConstraint *) constraint { + [_constraints removeObject: constraint]; +} + +- (void) removeConstraints: (NSArray *) constraints { + for (NSLayoutConstraint *constraint in constraints) { + [self removeConstraint: constraint]; + } +} + - (NSLayoutPriority) contentHuggingPriorityForOrientation: (NSLayoutConstraintOrientation) orientation { if (orientation == NSLayoutConstraintOrientationHorizontal) { return _horizontalContentHuggingPriority; diff --git a/vendor/pins/cocotron/AppKit/include/AppKit/NSLayoutConstraint.h b/vendor/pins/cocotron/AppKit/include/AppKit/NSLayoutConstraint.h index c92583eb1..2a0722da2 100644 --- a/vendor/pins/cocotron/AppKit/include/AppKit/NSLayoutConstraint.h +++ b/vendor/pins/cocotron/AppKit/include/AppKit/NSLayoutConstraint.h @@ -20,6 +20,8 @@ #import #import #import +#import +#import typedef float NSLayoutPriority; @@ -30,3 +32,18 @@ typedef NS_ENUM(NSInteger, NSLayoutConstraintOrientation) { APPKIT_EXPORT const CGFloat NSViewNoInstrinsicMetric; APPKIT_EXPORT const CGFloat NSViewNoIntrinsicMetric; + +/* + * AUTO LAYOUT, AS FAR AS AN APPLICATION THAT ONLY MAKES CONSTRAINTS NEEDS IT. + * + * MoneyMoney reaches for exactly three things once AppKit claims to be newer than 10.15: + * +constraintWithItem:..., the class itself, and -setActive:. Two constraints, activated once. What + * it does NOT do is rely on a solver for its layout, because every view in its nibs still carries an + * autoresizing mask and translatesAutoresizingMaskIntoConstraints defaults to YES. + * + * So there is an object and its accounting, and no solver. THE CLASS ITSELF IS IN FOUNDATION, which + * has carried a stub of that name since the nib decoder needed one, along with three subclasses of + * it. Two Objective-C classes with one name is a coin toss the runtime announces and then resolves + * however it likes, and the application got the stub. See Foundation/NSLayoutConstraint.h. + */ +#import diff --git a/vendor/pins/cocotron/AppKit/include/AppKit/NSScreen.h b/vendor/pins/cocotron/AppKit/include/AppKit/NSScreen.h index 260b5706e..69451a14a 100644 --- a/vendor/pins/cocotron/AppKit/include/AppKit/NSScreen.h +++ b/vendor/pins/cocotron/AppKit/include/AppKit/NSScreen.h @@ -38,6 +38,7 @@ typedef NSString *NSDeviceDescriptionKey; + (NSScreen *) mainScreen; + (NSArray *) screens; ++ (BOOL) screensHaveSeparateSpaces; - initWithFrame: (NSRect) frame visibleFrame: (NSRect) visibleFrame; diff --git a/vendor/pins/cocotron/AppKit/include/AppKit/NSView.h b/vendor/pins/cocotron/AppKit/include/AppKit/NSView.h index 50a23322c..b2f278c39 100644 --- a/vendor/pins/cocotron/AppKit/include/AppKit/NSView.h +++ b/vendor/pins/cocotron/AppKit/include/AppKit/NSView.h @@ -86,6 +86,8 @@ APPKIT_EXPORT const NSNotificationName NSViewGlobalFrameDidChangeNotification; APPKIT_EXPORT const NSViewFullScreenModeOptionKey NSFullScreenModeApplicationPresentationOptions; +@class NSLayoutConstraint; + @interface NSView : NSResponder { NSRect _frame; NSRect _bounds; @@ -112,6 +114,7 @@ APPKIT_EXPORT const NSViewFullScreenModeOptionKey NSFullScreenModeApplicationPre CGFloat _frameRotation; CGFloat _boundsRotation; BOOL _translatesAutoresizingMaskIntoConstraints; + NSMutableArray *_constraints; BOOL _validTrackingAreas; BOOL _validTransforms; @@ -144,6 +147,15 @@ APPKIT_EXPORT const NSViewFullScreenModeOptionKey NSFullScreenModeApplicationPre @property(class, readonly) BOOL requiresConstraintBasedLayout; @property BOOL translatesAutoresizingMaskIntoConstraints; +/* THE CONSTRAINTS A VIEW HOLDS. Nothing here solves them, see NSLayoutConstraint.h; a view keeps + * them so that activating one has somewhere to go and so that it outlives the statement that made + * it, which is what an application can otherwise notice. */ +@property (readonly, copy) NSArray *constraints; +- (void) addConstraint: (NSLayoutConstraint *) constraint; +- (void) addConstraints: (NSArray *) constraints; +- (void) removeConstraint: (NSLayoutConstraint *) constraint; +- (void) removeConstraints: (NSArray *) constraints; + + (NSView *) focusView; + (NSMenu *) defaultMenu; + (NSFocusRingType) defaultFocusRingType; -- 2.51.2