diff --git a/vendor/pins/cocotron/AppKit/NSSplitView.m b/vendor/pins/cocotron/AppKit/NSSplitView.m index 49367dcaf..60fc02d15 100644 --- a/vendor/pins/cocotron/AppKit/NSSplitView.m +++ b/vendor/pins/cocotron/AppKit/NSSplitView.m @@ -154,6 +154,20 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = /** adjust all the non-collapsed subviews so that they are equally spaced * horizontally within the splitview */ +/* + * A PANE THE DELEGATE WILL NOT LET US RESIZE keeps the size it has, and the room left over goes to + * the panes that will. Without this every pane is treated as elastic and the space is shared by + * ratio, which is wrong whenever one pane is a fixed strip: Swift Publisher stacks a toolbar, a + * page preview and the document canvas in one split view and answers YES for the canvas alone, so + * asking made the difference between a canvas with a height and a canvas with none. + */ +- (BOOL) _delegateShouldAdjustSizeOfSubview: (NSView *) subview { + if (![_delegate respondsToSelector: @selector(splitView:shouldAdjustSizeOfSubview:)]) + return YES; + + return [_delegate splitView: self shouldAdjustSizeOfSubview: subview]; +} + - (void) _adjustSubviewWidths { // Set all the subview heights to the bounds height of the split view CGFloat height = NSHeight([self bounds]); @@ -192,6 +206,24 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = visibleCount++; } + /* The panes the delegate holds fixed are subtracted from both sides of the ratio, so what is + * shared out below is only the room the elastic panes may have. */ + CGFloat fixedWidth = 0.0; + + for (i = 0; i < count; i++) { + NSView *subview = [_subviews objectAtIndex: i]; + + if ([self isSubviewCollapsed: subview] == NO && + [self _delegateShouldAdjustSizeOfSubview: subview] == NO) { + fixedWidth += NSWidth([subview frame]); + totalWidthBefore -= NSWidth([subview frame]); + visibleCount--; + } + } + totalWidthAfter -= fixedWidth; + if (!(totalWidthAfter >= 0.0)) + totalWidthAfter = 0.0; + BOOL haveProportions = (totalWidthBefore > 0.0) && (visibleCount > 0); CGFloat delta = haveProportions ? (totalWidthAfter / totalWidthBefore) : 0.0; CGFloat equalShare = (visibleCount > 0) ? (totalWidthAfter / visibleCount) : 0.0; @@ -200,7 +232,11 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = for (i = 0; i < count; i++) { NSView *subview = [_subviews objectAtIndex: i]; if ([self isSubviewCollapsed: subview] == NO) { - frame.size.width = haveProportions ? (NSWidth([subview frame]) * delta) : equalShare; + if ([self _delegateShouldAdjustSizeOfSubview: subview] == NO) + frame.size.width = NSWidth([subview frame]); + else + frame.size.width = + haveProportions ? (NSWidth([subview frame]) * delta) : equalShare; frame.size.width = floor(frame.size.width); if (!(frame.size.width >= 0.0)) /* also false for NAN */ frame.size.width = 0.0; @@ -246,6 +282,23 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = visibleCount++; } + /* Same as the width case: a pane the delegate holds fixed leaves the ratio entirely. */ + CGFloat fixedHeight = 0.0; + + for (i = 0; i < count; i++) { + NSView *subview = [_subviews objectAtIndex: i]; + + if ([self isSubviewCollapsed: subview] == NO && + [self _delegateShouldAdjustSizeOfSubview: subview] == NO) { + fixedHeight += NSHeight([subview frame]); + totalHeightBefore -= NSHeight([subview frame]); + visibleCount--; + } + } + totalHeightAfter -= fixedHeight; + if (!(totalHeightAfter >= 0.0)) + totalHeightAfter = 0.0; + BOOL haveProportions = (totalHeightBefore > 0.0) && (visibleCount > 0); CGFloat delta = haveProportions ? (totalHeightAfter / totalHeightBefore) : 0.0; CGFloat equalShare = (visibleCount > 0) ? (totalHeightAfter / visibleCount) : 0.0; @@ -254,7 +307,11 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = for (i = 0; i < count; i++) { NSView *subview = [_subviews objectAtIndex: i]; if ([self isSubviewCollapsed: subview] == NO) { - frame.size.height = haveProportions ? (NSHeight([subview frame]) * delta) : equalShare; + if ([self _delegateShouldAdjustSizeOfSubview: subview] == NO) + frame.size.height = NSHeight([subview frame]); + else + frame.size.height = + haveProportions ? (NSHeight([subview frame]) * delta) : equalShare; frame.size.height = floor(frame.size.height); if (!(frame.size.height >= 0.0)) /* also false for NAN */ frame.size.height = 0.0; diff --git a/vendor/pins/cocotron/AppKit/NSTableView.m b/vendor/pins/cocotron/AppKit/NSTableView.m index 49f7d0a87..a7b512c6c 100644 --- a/vendor/pins/cocotron/AppKit/NSTableView.m +++ b/vendor/pins/cocotron/AppKit/NSTableView.m @@ -122,6 +122,9 @@ const CGFloat NSTableViewDefaultRowHeight = 16.0f; _allowsColumnReordering = (flags & 0x80000000) ? YES : NO; _allowsColumnResizing = (flags & 0x40000000) ? YES : NO; _autoresizesAllColumnsToFit = (flags & 0x00008000) ? YES : NO; + _columnAutoresizingStyle = _autoresizesAllColumnsToFit + ? NSTableViewUniformColumnAutoresizingStyle + : NSTableViewLastColumnOnlyAutoresizingStyle; _allowsMultipleSelection = (flags & 0x08000000) ? YES : NO; _allowsEmptySelection = (flags & 0x10000000) ? YES : NO; _allowsColumnSelection = (flags & 0x04000000) ? YES : NO; @@ -165,6 +168,7 @@ const CGFloat NSTableViewDefaultRowHeight = 16.0f; _autoresizesAllColumnsToFit = NO; // the default isn't actually given in the spec, but this seems // more like default behavior + _columnAutoresizingStyle = NSTableViewLastColumnOnlyAutoresizingStyle; _allowsMultipleSelection = NO; _allowsEmptySelection = YES; _allowsColumnSelection = YES; @@ -274,6 +278,10 @@ const CGFloat NSTableViewDefaultRowHeight = 16.0f; return _autoresizesAllColumnsToFit; } +- (NSTableViewColumnAutoresizingStyle) columnAutoresizingStyle { + return _columnAutoresizingStyle; +} + - (BOOL) allowsMultipleSelection { return _allowsMultipleSelection; } @@ -649,7 +657,21 @@ static CGFloat rowHeightAtIndex(NSTableView *self, NSInteger index) { _allowsColumnResizing = flag; } +/* + * THE SAME SETTING, TWO SPELLINGS. autoresizesAllColumnsToFit is the older boolean and + * columnAutoresizingStyle replaced it, so they are kept in step here rather than allowed to + * disagree: the boolean is exactly the uniform style, and its NO is last-column-only, which is what + * the resize below already did. + */ +- (void) setColumnAutoresizingStyle: (NSTableViewColumnAutoresizingStyle) style { + _columnAutoresizingStyle = style; + _autoresizesAllColumnsToFit = + (style == NSTableViewUniformColumnAutoresizingStyle) ? YES : NO; +} + - (void) setAutoresizesAllColumnsToFit: (BOOL) flag { + _columnAutoresizingStyle = flag ? NSTableViewUniformColumnAutoresizingStyle + : NSTableViewLastColumnOnlyAutoresizingStyle; _autoresizesAllColumnsToFit = flag; } @@ -1449,26 +1471,68 @@ static CGFloat rowHeightAtIndex(NSTableView *self, NSInteger index) { return result; } -- (void) resizeWithOldSuperviewSize: (NSSize) oldSize { - NSSize size = [self frame].size; - - if (size.width < [[self superview] bounds].size.width) { - size.width = [[self superview] bounds].size.width; - [self setFrameSize: size]; - } +/* + * WHO TAKES UP THE SLACK when the table is wider or narrower than its columns. The styles differ + * only in which columns are offered the difference, so this is one walk with a direction and a + * limit: sequential works from the last column towards the first and reverse sequential from the + * first towards the last, each column taking as much of the difference as its minimum and maximum + * width allow before the next one is asked. + */ +- (void) _autoresizeColumns { + CGFloat delta = [[self enclosingScrollView] contentSize].width - + [self _displayWidthOfColumns]; + NSInteger count = [_tableColumns count]; + NSInteger i; - if (_autoresizesAllColumnsToFit) { - CGFloat delta = [[self enclosingScrollView] contentSize].width - - [self _displayWidthOfColumns]; - NSInteger i, count = [_tableColumns count]; + if (count == 0 || _columnAutoresizingStyle == NSTableViewNoColumnAutoresizing) + return; + switch (_columnAutoresizingStyle) { + case NSTableViewUniformColumnAutoresizingStyle: for (i = 0; i < count; ++i) { NSTableColumn *column = [_tableColumns objectAtIndex: i]; + [column setWidth: [column width] + floor((delta / count))]; } + return; - } else + case NSTableViewLastColumnOnlyAutoresizingStyle: [self sizeLastColumnToFit]; + return; + + case NSTableViewFirstColumnOnlyAutoresizingStyle: { + NSTableColumn *column = [_tableColumns objectAtIndex: 0]; + + [column setWidth: [column width] + delta]; + return; + } + + default: + break; + } + + for (i = 0; i < count && delta != 0; ++i) { + NSInteger index = (_columnAutoresizingStyle == + NSTableViewReverseSequentialColumnAutoresizingStyle) + ? i + : (count - 1 - i); + NSTableColumn *column = [_tableColumns objectAtIndex: index]; + CGFloat was = [column width]; + + [column setWidth: was + delta]; + delta -= [column width] - was; + } +} + +- (void) resizeWithOldSuperviewSize: (NSSize) oldSize { + NSSize size = [self frame].size; + + if (size.width < [[self superview] bounds].size.width) { + size.width = [[self superview] bounds].size.width; + [self setFrameSize: size]; + } + + [self _autoresizeColumns]; [self tile]; } diff --git a/vendor/pins/cocotron/AppKit/NSView.m b/vendor/pins/cocotron/AppKit/NSView.m index a61a3ecca..ff2c1dc91 100644 --- a/vendor/pins/cocotron/AppKit/NSView.m +++ b/vendor/pins/cocotron/AppKit/NSView.m @@ -1280,14 +1280,33 @@ static inline void buildTransformsIfNeeded(NSView *self) { return NSMakeRect(minx, miny, maxx - minx, maxy - miny); } + +/* + * WHICH VIEWS TO FOLLOW. CIDER_TRACE_SVFRAME=1 still means every scroll view, which is what the + * Swift Publisher canvas work needed; any other value is a class NAME SUBSTRING, so a run can + * follow exactly the view that is behaving oddly rather than a category of them. + */ +static BOOL _CiderTraceFrameFor(NSView *view) { + const char *want = getenv("CIDER_TRACE_SVFRAME"); + + if (want == NULL || want[0] == '\0') + return NO; + + if (strcmp(want, "1") == 0) + return [view isKindOfClass: [NSScrollView class]]; + + return strstr(object_getClassName(view), want) != NULL; +} + - (void) setFrame: (NSRect) frame { /* EVERY SIZE A SCROLL VIEW IS GIVEN, WITH THE CALLER. The document scroll view of Swift * Publisher arrives at zero and nothing in AppKit can size it (no mask, plain superview), so * the question is whether the application sizes it later and from where. Subtract the image * base from the caller to read it against the disassembly. */ - if (getenv("CIDER_TRACE_SVFRAME") != NULL && [self isKindOfClass: [NSScrollView class]]) { - fprintf(stderr, "CIDER_SVFRAME %s %s -> %gx%g caller=%p\n", object_getClassName(self), - "setFrame", frame.size.width, frame.size.height, __builtin_return_address(0)); + if (_CiderTraceFrameFor(self)) { + fprintf(stderr, "CIDER_SVFRAME %s %s -> %gx%g at %g,%g caller=%p\n", + object_getClassName(self), "setFrame", frame.size.width, frame.size.height, + frame.origin.x, frame.origin.y, __builtin_return_address(0)); fflush(stderr); } @@ -1404,7 +1423,7 @@ static inline void buildTransformsIfNeeded(NSView *self) { * Publisher arrives at zero and nothing in AppKit can size it (no mask, plain superview), so * the question is whether the application sizes it later and from where. Subtract the image * base from the caller to read it against the disassembly. */ - if (getenv("CIDER_TRACE_SVFRAME") != NULL && [self isKindOfClass: [NSScrollView class]]) { + if (_CiderTraceFrameFor(self)) { fprintf(stderr, "CIDER_SVFRAME %s %s -> %gx%g caller=%p\n", object_getClassName(self), "setFrameSize", size.width, size.height, __builtin_return_address(0)); fflush(stderr); diff --git a/vendor/pins/cocotron/AppKit/NSViewController.m b/vendor/pins/cocotron/AppKit/NSViewController.m index 78aecc851..82414bb97 100644 --- a/vendor/pins/cocotron/AppKit/NSViewController.m +++ b/vendor/pins/cocotron/AppKit/NSViewController.m @@ -8,6 +8,24 @@ @synthesize identifier = _identifier; +/* + * NEW HAS TO REACH THE DESIGNATED INITIALISER. AppKit documents -init on a view controller as + * initWithNibName:nil bundle:nil, and a subclass that builds its view in code overrides that one + * method, because it is the only one every other initialiser funnels through. Without this, an + * ordinary [MyViewController new] runs NSObject's -init, the subclass override never executes, and + * the controller comes up with no view at all. + * + * That is not a theoretical tidiness point. Swift Publisher creates its document container that + * way: the override installs a real NSSplitView subclass as the controller's view, and the window + * then adds the page-preview strip and the canvas scroll view to it as panes, both with no + * autoresizing mask, because a split view sizes its own subviews. Skipping the override left them + * in the empty NSView that loadView hands back for a nib-less controller, which lays nothing out, + * so the canvas scroll view stayed at zero by zero for the life of the window. + */ +- init { + return [self initWithNibName: nil bundle: nil]; +} + - initWithNibName: (NSString *) name bundle: (NSBundle *) bundle { _nibName = [name copy]; _nibBundle = [bundle retain]; diff --git a/vendor/pins/cocotron/AppKit/NSWindow.m b/vendor/pins/cocotron/AppKit/NSWindow.m index 5a26fac68..e0f560278 100644 --- a/vendor/pins/cocotron/AppKit/NSWindow.m +++ b/vendor/pins/cocotron/AppKit/NSWindow.m @@ -230,9 +230,12 @@ static void _CiderDumpViewTree(NSView *view, int depth) else if ([view respondsToSelector: @selector(string)]) text = [(id) view string]; - fprintf(stderr, "CIDER_VIEW %*s%s %.0fx%.0f at %.0f,%.0f%s%s\n", depth * 2, "", + /* The mask belongs next to the frame: a subview that stays empty while its container grows is + * either masked to stay that way or was never given a size to grow from, and the two look + * identical in a frame-only dump. */ + fprintf(stderr, "CIDER_VIEW %*s%s %.0fx%.0f at %.0f,%.0f mask=0x%x%s%s\n", depth * 2, "", object_getClassName(view), frame.size.width, frame.size.height, - frame.origin.x, frame.origin.y, + frame.origin.x, frame.origin.y, (unsigned) [view autoresizingMask], (text != nil && [text length] > 0) ? " text: " : "", (text != nil && [text length] > 0) ? [text UTF8String] : ""); @@ -2374,6 +2377,27 @@ static BOOL _allowsAutomaticWindowTabbing; return; } + /* THE TREE OF EVERY WINDOW THAT EVER DRAWS, ONCE. -display dumps it, but a window that only + * ever goes through displayIfNeeded was invisible to that instrument, which is every document + * window in Swift Publisher. One dump per window is enough to answer whether a layout ran. */ + if (getenv("CIDER_TRACE_VIEWS") != NULL) { + static NSWindow *dumped[64]; + static int dumpedCount = 0; + BOOL seen = NO; + + for (int i = 0; i < dumpedCount; i++) + if (dumped[i] == self) + seen = YES; + + if (!seen && dumpedCount < 64) { + dumped[dumpedCount++] = self; + fprintf(stderr, "CIDER_VIEW tree(displayIfNeeded) of %s title=%s\n", + object_getClassName(self), [[self title] UTF8String] ?: "(none)"); + fflush(stderr); + _CiderDumpViewTree(_backgroundView, 1); + } + } + @autoreleasepool { if ([NSGraphicsContext quartzDebuggingIsEnabled]) { // Show all the views getting redrawn. diff --git a/vendor/pins/cocotron/AppKit/include/AppKit/NSTableView.h b/vendor/pins/cocotron/AppKit/include/AppKit/NSTableView.h index 4a4e0e824..80b5736f0 100644 --- a/vendor/pins/cocotron/AppKit/include/AppKit/NSTableView.h +++ b/vendor/pins/cocotron/AppKit/include/AppKit/NSTableView.h @@ -75,6 +75,16 @@ typedef enum { NSTableViewDropAbove } NSTableViewDropOperation; +enum { + NSTableViewNoColumnAutoresizing = 0, + NSTableViewUniformColumnAutoresizingStyle, + NSTableViewSequentialColumnAutoresizingStyle, + NSTableViewReverseSequentialColumnAutoresizingStyle, + NSTableViewLastColumnOnlyAutoresizingStyle, + NSTableViewFirstColumnOnlyAutoresizingStyle +}; +typedef NSUInteger NSTableViewColumnAutoresizingStyle; + @interface NSTableView : NSControl { id _target; SEL _action; @@ -95,6 +105,7 @@ typedef enum { BOOL _allowsColumnReordering; BOOL _allowsColumnResizing; BOOL _autoresizesAllColumnsToFit; + NSTableViewColumnAutoresizingStyle _columnAutoresizingStyle; BOOL _allowsMultipleSelection; BOOL _allowsEmptySelection; BOOL _allowsColumnSelection; @@ -140,6 +151,7 @@ typedef enum { - (BOOL) allowsColumnReordering; - (BOOL) allowsColumnResizing; - (BOOL) autoresizesAllColumnsToFit; +- (NSTableViewColumnAutoresizingStyle) columnAutoresizingStyle; - (BOOL) allowsMultipleSelection; - (BOOL) allowsEmptySelection; - (BOOL) allowsColumnSelection; @@ -181,6 +193,7 @@ typedef enum { - (void) setAllowsColumnReordering: (BOOL) flag; - (void) setAllowsColumnResizing: (BOOL) flag; - (void) setAutoresizesAllColumnsToFit: (BOOL) flag; +- (void) setColumnAutoresizingStyle: (NSTableViewColumnAutoresizingStyle) style; - (void) setAllowsMultipleSelection: (BOOL) flag; - (void) setAllowsEmptySelection: (BOOL) flag; - (void) setAllowsColumnSelection: (BOOL) flag;