Merge pull request #105 from gnustep/NSStackView_branch2

NSStackView merge
This commit is contained in:
Gregory Casamento 2021-05-29 09:26:09 -04:00 committed by GitHub
commit 3695d41452
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1190 additions and 9 deletions

View file

@ -1,3 +1,13 @@
2021-05-20 Gregory John Casamento <greg.casamento@gmail.com>
* Headers/AppKit/AppKit.h: Add NSStackView.h to header.
* Headers/AppKit/NSStackView.h: Header
* MISSING: Remove NSStackView from the list and update others.
* Source/GNUmakefile: Add NSStackView.[hm] to GNUmakefile.
* Source/GSXib5KeyedUnarchiver.m: Add decoders and mappings to decode
NSStackView content.
* Source/NSStackView.m: Implementation of NSStackView.
2021-05-01 Gregory John Casamento <greg.casamento@gmail.com>
* Headers/Additions/GNUstepGUI/GSXibLoading.h: Add declaration

View file

@ -228,6 +228,7 @@
#import <AppKit/NSSound.h>
#import <AppKit/NSSpeechRecognizer.h>
#import <AppKit/NSSpeechSynthesizer.h>
#import <AppKit/NSStackView.h>
#import <AppKit/NSStepperTouchBarItem.h>
#import <AppKit/NSStepper.h>
#import <AppKit/NSStepperCell.h>

View file

@ -0,0 +1,174 @@
/* Definition of class NSStackView
Copyright (C) 2020 Free Software Foundation, Inc.
By: Gregory John Casamento <greg.casamento@gmail.com>
Date: 08-08-2020
This file is part of the GNUstep Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
#ifndef _NSStackView_h_GNUSTEP_GUI_INCLUDE
#define _NSStackView_h_GNUSTEP_GUI_INCLUDE
#import <AppKit/NSView.h>
#import <AppKit/NSLayoutConstraint.h>
#if OS_API_VERSION(MAC_OS_X_VERSION_10_9, GS_API_LATEST)
#if defined(__cplusplus)
extern "C" {
#endif
// Gravity
enum {
NSStackViewGravityTop = 1,
NSStackViewGravityLeading = 1,
NSStackViewGravityCenter = 2,
NSStackViewGravityBottom = 3,
NSStackViewGravityTrailing = 3
};
typedef NSInteger NSStackViewGravity;
// Distribution
enum {
NSStackViewDistributionGravityAreas = -1,
NSStackViewDistributionFill = 0,
NSStackViewDistributionFillEqually,
NSStackViewDistributionFillProportionally,
NSStackViewDistributionEqualSpacing,
NSStackViewDistributionEqualCentering
};
typedef NSInteger NSStackViewDistribution;
typedef float NSStackViewVisibilityPriority;
static const NSStackViewVisibilityPriority NSStackViewVisibilityPriorityMustHold = 1000.0;
static const NSStackViewVisibilityPriority NSStackViewVisibilityPriorityDetachOnlyIfNecessary = 900.0;
static const NSStackViewVisibilityPriority NSStackViewVisibilityPriorityNotVisible = 0.0;
static const CGFloat NSStackViewSpacingUseDefault = FLT_MAX;
@protocol NSStackViewDelegate;
@interface NSStackView : NSView
{
id<NSStackViewDelegate> _delegate;
NSUserInterfaceLayoutOrientation _orientation;
NSLayoutPriority _horizontalHuggingPriority;
NSLayoutPriority _verticalHuggingPriority;
NSLayoutPriority _horizontalClippingResistancePriority;
NSLayoutPriority _verticalClippingResistancePriority;
NSLayoutAttribute _alignment;
NSLayoutAttribute _secondaryAlignment;
NSEdgeInsets _edgeInsets;
NSStackViewDistribution _distribution;
CGFloat _spacing;
BOOL _detachesHiddenViews;
BOOL _hasFlagViewHierarchy;
BOOL _hasEqualSpacing;
NSMutableArray *_arrangedSubviews;
NSMutableArray *_detachedViews;
NSMutableArray *_views;
// Containers
id _beginningContainer;
id _middleContainer;
id _endContainer;
NSMapTable *_customSpacingMap;
NSMapTable *_visiblePriorityMap;
}
// Properties
- (void) setDelegate: (id<NSStackViewDelegate>)delegate;
- (id<NSStackViewDelegate>) delegate;
- (void) setOrientation: (NSUserInterfaceLayoutOrientation)o;
- (NSUserInterfaceLayoutOrientation) orientation;
- (void) setAlignment: (NSLayoutAttribute)alignment;
- (NSLayoutAttribute) alignment;
- (void) setEdgeInsets: (NSEdgeInsets)insets;
- (NSEdgeInsets) edgeInsets;
- (void) setDistribution: (NSStackViewDistribution)d;
- (NSStackViewDistribution) distribution;
- (void) setSpacing: (CGFloat)f;
- (CGFloat) spacing;
- (void) setDetachesHiddenViews: (BOOL)f;
- (BOOL) detachesHiddenViews;
- (void) setArrangedSubviews: (NSArray *)arrangedSubviews;
- (NSArray *) arrangedSubviews;
- (void) setDetachedViews: (NSArray *)detachedViews;
- (NSArray *) detachedViews;
// Instance methods
// Manage views...
+ (instancetype) stackViewWithViews: (NSArray *)views;
- (void) setCustomSpacing: (CGFloat)spacing afterView: (NSView *)v;
- (CGFloat) customSpacingAfterView: (NSView *)v;
- (void) addArrangedSubview: (NSView *)v;
- (void) insertArrangedSubview: (NSView *)v atIndex: (NSInteger)idx;
- (void) removeArrangedSubview: (NSView *)v;
// Custom priorities
- (void)setVisibilityPriority: (NSStackViewVisibilityPriority)priority
forView: (NSView *)v;
- (NSStackViewVisibilityPriority) visibilityPriorityForView: (NSView *)v;
- (NSLayoutPriority)clippingResistancePriorityForOrientation:(NSLayoutConstraintOrientation)orientation;
- (void) setClippingResistancePriority: (NSLayoutPriority)clippingResistancePriorityn
forOrientation: (NSLayoutConstraintOrientation)orientation;
- (NSLayoutPriority) huggingPriorityForOrientation: (NSLayoutConstraintOrientation)o;
- (void) setHuggingPriority: (NSLayoutPriority)huggingPriority
forOrientation: (NSLayoutConstraintOrientation)o;
- (void) setHasEqualSpacing: (BOOL)f; // deprecated
- (BOOL) hasEqualSpacing; // deprecated
- (void)addView: (NSView *)view inGravity: (NSStackViewGravity)gravity;
- (void)insertView: (NSView *)view atIndex: (NSUInteger)index inGravity: (NSStackViewGravity)gravity;
- (void)removeView: (NSView *)view;
- (NSArray *) viewsInGravity: (NSStackViewGravity)gravity;
- (void)setViews: (NSArray *)views inGravity: (NSStackViewGravity)gravity;
@end
// Protocol
@protocol NSStackViewDelegate <NSObject>
- (void) stackView: (NSStackView *)stackView willDetachViews: (NSArray *)views;
- (void) stackView: (NSStackView *)stackView didReattachViews: (NSArray *)views;
@end
#if defined(__cplusplus)
}
#endif
#endif /* GS_API_MACOSX */
#endif /* _NSStackView_h_GNUSTEP_GUI_INCLUDE */

View file

@ -15,8 +15,7 @@ MISSING HEADERS ( * = difficult, - = quick, + = placeholder, x = won't do )
> NSGlyphInfo.h -
> NSItemProvider.h +
> NSMenuToolbarItem.h -
> NSOpenGLLayer.h *
> NSStackView.h *
> NSOpenGLLayer.h +
> NSTableCellView.h *
> NSTableRowView.h *
> NSTableViewRowAction.h *

View file

@ -132,6 +132,7 @@ NSSlider.m \
NSSliderCell.m \
NSSliderAccessory.m \
NSSliderTouchBarItem.m \
NSStackView.m \
NSStepperTouchBarItem.m \
NSSwitch.m \
NSTitlebarAccessoryViewController.m \
@ -532,6 +533,7 @@ NSSlider.h \
NSSliderCell.h \
NSSliderAccessory.h \
NSSliderTouchBarItem.h \
NSStackView.h \
NSSound.h \
NSSpeechRecognizer.h \
NSSpeechSynthesizer.h \

View file

@ -59,6 +59,7 @@
#import "AppKit/NSScrollView.h"
#import "AppKit/NSSliderCell.h"
#import "AppKit/NSSplitView.h"
#import "AppKit/NSStackView.h"
#import "AppKit/NSTableColumn.h"
#import "AppKit/NSTableHeaderView.h"
#import "AppKit/NSTableView.h"
@ -224,7 +225,10 @@ static NSArray *XmlBoolDefaultYes = nil;
@"IBUserDefinedRuntimeAttribute5", @"userDefinedRuntimeAttribute",
@"NSURL", @"url",
@"NSLayoutConstraint", @"constraint",
@"NSPageController", @"pagecontroller", // why is pagecontroller capitalized this way?
@"NSPageController", @"pagecontroller", // inconsistent capitalization
@"NSStackViewContainer", @"beginningViews",
@"NSStackViewContainer", @"middleViews",
@"NSStackViewContainer", @"endViews",
nil];
RETAIN(XmlTagToObjectClassMap);
@ -299,7 +303,7 @@ static NSArray *XmlBoolDefaultYes = nil;
@"string", @"NS.relative",
@"canPropagateSelectedChildViewControllerTitle",
@"NSTabViewControllerCanPropagateSelectedChildViewControllerTitle",
@"rowAlignment", @"NSGrid_alignment",
@"rowAlignment", @"NSGrid_alignment", // NSGridView
@"rowSpacing", @"NSGrid_rowSpacing",
@"columnSpacing", @"NSGrid_columnSpacing",
@"hidden", @"NSGrid_hidden",
@ -317,6 +321,9 @@ static NSArray *XmlBoolDefaultYes = nil;
@"contentView", @"NSGrid_content",
@"row", @"NSGrid_owningRow",
@"column", @"NSGrid_owningColumn",
@"beginningViews", @"NSStackViewBeginningContainer", // NSStackView
@"middleViews", @"NSStackViewMiddleContainer",
@"endViews", @"NSStackViewEndContainer",
nil];
RETAIN(XmlKeyMapTable);
@ -348,7 +355,8 @@ static NSArray *XmlBoolDefaultYes = nil;
@"NSMatrixFlags", @"NSNumCols", @"NSNumRows",
@"NSSharedData", @"NSFlags", @"NSTVFlags",
@"NSDefaultParagraphStyle",
@"NSpiFlags", nil];
@"NSpiFlags", @"NSStackViewContainerNonDroppedViews",
nil];
RETAIN(XmlKeysDefined);
// These define XML tags (i.e. '<autoresizingMask ...') to an associated decode method...
@ -365,6 +373,9 @@ static NSArray *XmlBoolDefaultYes = nil;
// decoding the integer flag masks...
XmlKeyToDecoderSelectorMap =
[NSDictionary dictionaryWithObjectsAndKeys:
@"decodeStackViewNonDroppedViewsForElement:", @"NSStackViewContainerNonDroppedViews",
@"decodeDistributionForElement:", @"NSStackViewdistribution",
@"decodeOrientationForElement:", @"NSStackViewOrientation",
@"decodeXPlacementForElement:", @"NSGrid_xPlacement",
@"decodeYPlacementForElement:", @"NSGrid_yPlacement",
@"decodeRowAlignmentForElement:", @"NSGrid_alignment",
@ -3025,10 +3036,6 @@ didStartElement: (NSString*)elementName
{
alignment = NSGridCellPlacementFill;
}
else // if not specified then assume none...
{
alignment = NSGridCellPlacementNone;
}
return [NSNumber numberWithInteger: alignment];
}
@ -3044,6 +3051,62 @@ didStartElement: (NSString*)elementName
return [self _decodePlacementForObject: obj];
}
- (id) _decodeOrientationForObject: (id)obj
{
NSLayoutConstraintOrientation orientation = 0L;
if ([obj isEqualToString: @"vertical"])
{
orientation = NSLayoutConstraintOrientationVertical;
}
else if ([obj isEqualToString: @"horizontal"])
{
orientation = NSLayoutConstraintOrientationHorizontal;
}
return [NSNumber numberWithInteger: orientation];
}
- (id) decodeOrientationForElement: (GSXibElement *)element
{
id obj = [element attributeForKey: @"orientation"];
return [self _decodeOrientationForObject: obj];
}
- (id) _decodeDistributionForObject: (id)obj
{
NSStackViewDistribution d = 0L;
if ([obj isEqualToString: @"equalCentering"])
{
d = NSStackViewDistributionEqualCentering;
}
else if ([obj isEqualToString: @"equalSpacing"])
{
d = NSStackViewDistributionEqualSpacing;
}
else if ([obj isEqualToString: @"fill"])
{
d = NSStackViewDistributionFill;
}
else if ([obj isEqualToString: @"fillEqually"])
{
d = NSStackViewDistributionFillEqually;
}
else if ([obj isEqualToString: @"fillProportionally"])
{
d = NSStackViewDistributionFillProportionally;
}
else if ([obj isEqualToString: @"gravityAreas"])
{
d = NSStackViewDistributionGravityAreas;
}
return [NSNumber numberWithInteger: d];
}
- (id) decodeDistributionForElement: (GSXibElement *)element
{
id obj = [element attributeForKey: @"distribution"];
return [self _decodeDistributionForObject: obj];
}
- (id) decodeRowAlignmentForElement: (GSXibElement *)element
{
id obj = [element attributeForKey: @"rowAlignment"];
@ -3067,6 +3130,22 @@ didStartElement: (NSString*)elementName
return [NSNumber numberWithInteger: alignment];
}
- (id) decodeStackViewNonDroppedViewsForElement: (GSXibElement *)element
{
NSMutableArray *result = [NSMutableArray array];
NSDictionary *elements = [element elements];
NSEnumerator *en = [elements objectEnumerator];
GSXibElement *e = nil;
while ((e = [en nextObject]) != nil)
{
id o = [self objectForXib: e];
[result addObject: o];
}
return result;
}
- (id) objectForXib: (GSXibElement*)element
{
id object = [super objectForXib: element];

916
Source/NSStackView.m Normal file
View file

@ -0,0 +1,916 @@
/* Implementation of class NSStackView
Copyright (C) 2020 Free Software Foundation, Inc.
By: Gregory John Casamento <greg.casamento@gmail.com>
Date: 08-08-2020
This file is part of the GNUstep Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
#import "AppKit/NSStackView.h"
#import "AppKit/NSTextStorage.h"
#import "AppKit/NSTextContainer.h"
#import "AppKit/NSLayoutManager.h"
#import "AppKit/NSButton.h"
#import "GSFastEnumeration.h"
@interface NSString (__StackViewPrivate__)
- (NSRect) _rectOfString;
@end
@interface NSView (__NSViewPrivateMethods__)
- (void) _insertSubview: (NSView *)sv atIndex: (NSUInteger)idx; // implemented in NSView.m
@end
@interface NSView (__StackViewPrivate__)
- (void) _insertView: (NSView *)v atIndex: (NSUInteger)i;
- (void) _removeAllSubviews;
- (void) _addSubviews: (NSArray *)views;
@end
@interface NSStackViewContainer : NSView
{
NSMutableArray *_nonDroppedViews;
NSMutableDictionary *_customAfterSpaceMap;
}
- (NSArray *) nonDroppedViews;
- (NSDictionary *) customAfterSpaceMap;
@end
@implementation NSString (__StackViewPrivate__)
- (NSRect) _rectOfString;
{
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString: self];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
[layoutManager addTextContainer:textContainer];
[textContainer release];
[textStorage addLayoutManager:layoutManager];
[layoutManager release];
//Figure out the bounding rectangle
NSRect stringRect =
[layoutManager boundingRectForGlyphRange:NSMakeRange(0, [layoutManager numberOfGlyphs])
inTextContainer:textContainer];
return stringRect;
}
@end
@implementation NSStackViewContainer
- (instancetype) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self != nil)
{
if ([coder allowsKeyedCoding])
{
if ([coder containsValueForKey: @"NSStackViewContainerNonDroppedViews"])
{
ASSIGN(_nonDroppedViews,
[coder decodeObjectForKey: @"NSStackViewContainerNonDroppedViews"]);
}
if ([coder containsValueForKey: @"NSStackViewContainerViewToCustomAfterSpaceMap"])
{
ASSIGN(_customAfterSpaceMap,
[coder decodeObjectForKey: @"NSStackViewContainerViewToCustomAfterSpaceMap"]);
}
}
else
{
ASSIGN(_nonDroppedViews, [coder decodeObject]);
ASSIGN(_customAfterSpaceMap, [coder decodeObject]);
}
[self _addSubviews: _nonDroppedViews];
}
return self;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[super encodeWithCoder: coder];
if ([coder allowsKeyedCoding])
{
[coder encodeObject: _nonDroppedViews
forKey: @"NSStackViewContainerNonDroppedViews"];
[coder encodeObject: _customAfterSpaceMap
forKey: @"NSStackViewContainerViewToCustomAfterSpaceMap"];
}
else
{
[coder encodeObject: _nonDroppedViews];
[coder encodeObject: _customAfterSpaceMap];
}
}
- (NSArray *) nonDroppedViews
{
return _nonDroppedViews;
}
- (NSDictionary *) customAfterSpaceMap
{
return _customAfterSpaceMap;
}
@end
@implementation NSView (__StackViewPrivate__)
- (void) _insertView: (NSView *)v atIndex: (NSUInteger)i
{
[self _insertSubview: v atIndex: i];
}
- (void) _removeAllSubviews
{
NSArray *subviews = [self subviews];
FOR_IN(NSView*, v, subviews)
{
[v removeFromSuperview];
}
END_FOR_IN(subviews);
}
- (void) _addSubviews: (NSArray *)views
{
FOR_IN(NSView*, v, views)
{
[self addSubview: v];
}
END_FOR_IN(views);
}
@end
@implementation NSStackView
- (void) _layoutViewsWithOrientation: (NSUserInterfaceLayoutOrientation)o
{
NSRect currentFrame = [self frame];
NSRect newFrame = currentFrame;
NSArray *sv = [self subviews];
NSUInteger n = [sv count];
CGFloat sp = [self spacing];
CGFloat x = 0.0;
CGFloat y = 0.0;
CGFloat newHeight = 0.0;
CGFloat newWidth = 0.0;
NSUInteger i = 0;
// Advance vertically or horizontally depending on orientation...
if (o == NSUserInterfaceLayoutOrientationVertical)
{
if (sp == 0.0)
{
if (n >= 0)
{
NSView *v = [sv objectAtIndex: 0];
sp = [v frame].size.height;
}
}
newFrame.size.height += sp; // * 2; // expand height
newFrame.origin.y -= (sp / 2.0); // move the view down.
newHeight = newFrame.size.height; // start at top of view...
}
else
{
if (sp == 0.0)
{
if (n >= 0)
{
NSView *v = [sv objectAtIndex: 0];
sp = [v frame].size.width;
}
}
newFrame.size.width += sp;
newWidth = newFrame.size.width;
}
[self setFrame: newFrame];
FOR_IN(NSView*,v,sv)
{
NSRect f;
NSString *str = nil;
NSRect sr = NSZeroRect;
if ([v respondsToSelector: @selector(title)])
{
str = [(NSButton *)v title];
}
f = [v frame];
if (f.origin.x < 0.0)
{
f.origin.x = 0.0;
}
if (str != nil)
{
sr = [str _rectOfString];
}
// Calculate control position...
if (o == NSUserInterfaceLayoutOrientationVertical)
{
y = newHeight - ((CGFloat)i * sp) - f.size.height;
f.origin.y = y;
}
else
{
x = (CGFloat)i * sp;
f.origin.x = x;
}
// expand width if control is too short for title...
if (f.size.width < sr.size.width)
{
f.size.width = sr.size.width + 5.0; //+5 to accomodate border...
}
[v setFrame: f];
i++;
}
END_FOR_IN(sv);
[self setNeedsDisplay: YES];
}
- (void) _refreshView
{
NSRect currentFrame = [self frame];
if (_beginningContainer != nil)
{
NSSize s = currentFrame.size;
NSUInteger i = 0;
CGFloat y = 0.0;
CGFloat x = 0.0;
CGFloat w = 0.0;
CGFloat h = 0.0;
if (_orientation == NSUserInterfaceLayoutOrientationHorizontal)
{
w = s.width / 3.0; // three sections, always.
h = s.height; // since we are horiz. the height is the height of the view
}
else
{
h = s.height / 3.0; // 3 sections
w = s.width;
}
for (i = 0; i < 3; i++)
{
NSRect f;
NSStackViewContainer *c = nil;
if (_orientation == NSUserInterfaceLayoutOrientationHorizontal)
{
x = w * (CGFloat)i;
}
else
{
y = h * (CGFloat)i;
}
f = NSMakeRect(x,y,w,h);
if (i == 0)
{
c = _beginningContainer;
}
if (i == 1)
{
c = _middleContainer;
}
if (i == 2)
{
c = _endContainer;
}
[super addSubview: c];
[c setFrame: f];
}
}
else
{
[self _layoutViewsWithOrientation: _orientation];
}
[self setNeedsDisplay: YES];
}
// Overridden methods
- (NSArray *) subviews
{
if (_beginningContainer != nil)
{
NSMutableArray *result = [NSMutableArray array];
[result addObjectsFromArray: [_beginningContainer nonDroppedViews]];
[result addObjectsFromArray: [_middleContainer nonDroppedViews]];
[result addObjectsFromArray: [_endContainer nonDroppedViews]];
return result;
}
return [super subviews];
}
// Properties
- (void) setDelegate: (id<NSStackViewDelegate>)delegate
{
_delegate = delegate;
}
- (id<NSStackViewDelegate>) delegate
{
return _delegate;
}
- (void) setOrientation: (NSUserInterfaceLayoutOrientation)o
{
_orientation = o;
[self _refreshView];
}
- (NSUserInterfaceLayoutOrientation) orientation
{
return _orientation;
}
- (void) setAlignment: (NSLayoutAttribute)alignment
{
_alignment = alignment;
[self _refreshView];
}
- (NSLayoutAttribute) alignment
{
return _alignment;
}
- (void) setEdgeInsets: (NSEdgeInsets)insets
{
_edgeInsets = insets;
[self _refreshView];
}
- (NSEdgeInsets) edgeInsets
{
return _edgeInsets;
}
- (void) setDistribution: (NSStackViewDistribution)d
{
_distribution = d;
[self _refreshView];
}
- (NSStackViewDistribution) distribution
{
return _distribution;
}
- (void) setSpacing: (CGFloat)f
{
_spacing = f;
[self _refreshView];
}
- (CGFloat) spacing
{
return _spacing;
}
- (void) setDetachesHiddenViews: (BOOL)f
{
_detachesHiddenViews = f;
}
- (BOOL) detachesHiddenViews
{
return _detachesHiddenViews;
}
- (void) setArrangedSubviews: (NSArray *)arrangedSubviews
{
ASSIGN(_arrangedSubviews, arrangedSubviews);
_distribution = NSStackViewDistributionFill;
[self _removeAllSubviews];
_beginningContainer = nil;
_middleContainer = nil;
_endContainer = nil;
[self _addSubviews: arrangedSubviews];
[self _refreshView];
}
- (NSArray *) arrangedSubviews
{
return [self subviews];
}
- (void) setDetachedViews: (NSArray *)detachedViews
{
ASSIGN(_detachedViews, detachedViews);
}
- (NSArray *) detachedViews
{
return _detachedViews;
}
// Instance methods
// Manage views...
- (instancetype) initWithViews: (NSArray *)views
{
self = [super init];
if (self != nil)
{
NSUInteger c = [views count];
_arrangedSubviews = [[NSMutableArray alloc] initWithArray: views];
_detachedViews = [[NSMutableArray alloc] initWithCapacity: c];
ASSIGNCOPY(_views, views);
_customSpacingMap = RETAIN([NSMapTable weakToWeakObjectsMapTable]);
_visiblePriorityMap = RETAIN([NSMapTable weakToWeakObjectsMapTable]);
// Gravity... not used by default.
_beginningContainer = nil;
_middleContainer = nil;
_endContainer = nil;
[self _refreshView];
}
return self;
}
- (void) dealloc
{
RELEASE(_arrangedSubviews);
RELEASE(_detachedViews);
RELEASE(_views);
RELEASE(_customSpacingMap);
RELEASE(_visiblePriorityMap);
RELEASE(_beginningContainer);
RELEASE(_middleContainer);
RELEASE(_endContainer);
_delegate = nil;
[super dealloc];
}
+ (instancetype) stackViewWithViews: (NSArray *)views
{
return AUTORELEASE([[self alloc] initWithViews: views]);
}
- (void) setCustomSpacing: (CGFloat)spacing afterView: (NSView *)v
{
if (_hasEqualSpacing == NO)
{
NSNumber *n = [NSNumber numberWithFloat: spacing];
[_customSpacingMap setObject: n
forKey: v];
[self _refreshView];
}
}
- (CGFloat) customSpacingAfterView: (NSView *)v
{
return [[_customSpacingMap objectForKey: v] floatValue];
}
- (void) addArrangedSubview: (NSView *)v
{
[_arrangedSubviews addObject: v];
[self _refreshView];
}
- (void) insertArrangedSubview: (NSView *)v atIndex: (NSInteger)idx
{
[_arrangedSubviews insertObject: v atIndex: idx];
}
- (void) removeArrangedSubview: (NSView *)v
{
[_arrangedSubviews removeObject: v];
}
// Custom priorities
- (void)setVisibilityPriority: (NSStackViewVisibilityPriority)priority
forView: (NSView *)v
{
NSNumber *n = [NSNumber numberWithInteger: priority];
[_visiblePriorityMap setObject: n
forKey: v];
[self _refreshView];
}
- (NSStackViewVisibilityPriority) visibilityPriorityForView: (NSView *)v
{
NSNumber *n = [_visiblePriorityMap objectForKey: v];
NSStackViewVisibilityPriority p = (NSStackViewVisibilityPriority)[n integerValue];
return p;
}
- (NSLayoutPriority)clippingResistancePriorityForOrientation:(NSLayoutConstraintOrientation)o
{
NSLayoutPriority p = 0L;
if (o == NSLayoutConstraintOrientationHorizontal)
{
p = _horizontalClippingResistancePriority;
}
else if (o == NSLayoutConstraintOrientationVertical)
{
p = _verticalClippingResistancePriority;
}
return p;
}
- (void) setClippingResistancePriority: (NSLayoutPriority)clippingResistancePriority
forOrientation: (NSLayoutConstraintOrientation)o
{
if (o == NSLayoutConstraintOrientationHorizontal)
{
_horizontalClippingResistancePriority = clippingResistancePriority;
}
else if (o == NSLayoutConstraintOrientationVertical)
{
_verticalClippingResistancePriority = clippingResistancePriority;
}
[self _refreshView];
}
- (NSLayoutPriority) huggingPriorityForOrientation: (NSLayoutConstraintOrientation)o
{
NSLayoutPriority p = 0L;
if (o == NSLayoutConstraintOrientationHorizontal)
{
p = _horizontalHuggingPriority;
}
else if (o == NSLayoutConstraintOrientationVertical)
{
p = _verticalHuggingPriority;
}
return p;
}
- (void) setHuggingPriority: (NSLayoutPriority)huggingPriority
forOrientation: (NSLayoutConstraintOrientation)o
{
if (o == NSLayoutConstraintOrientationHorizontal)
{
_horizontalHuggingPriority = huggingPriority;
}
else if (o == NSLayoutConstraintOrientationVertical)
{
_verticalHuggingPriority = huggingPriority;
}
[self _refreshView];
}
- (void) setHasEqualSpacing: (BOOL)f
{
_hasEqualSpacing = f;
}
- (BOOL) hasEqualSpacing
{
return _hasEqualSpacing;
}
- (void)addView: (NSView *)view inGravity: (NSStackViewGravity)gravity
{
if (_beginningContainer != nil)
{
switch (gravity)
{
case NSStackViewGravityTop: // or leading...
[_beginningContainer addSubview: view];
break;
case NSStackViewGravityCenter:
[_middleContainer addSubview: view];
break;
case NSStackViewGravityBottom:
[_endContainer addSubview: view]; // or trailing...
break;
default:
[NSException raise: NSInternalInconsistencyException
format: @"Attempt to add view %@ to unknown container %ld.", view, gravity];
break;
}
}
else
{
[super addSubview: view];
}
[self _refreshView];
}
- (void)insertView: (NSView *)view atIndex: (NSUInteger)index inGravity: (NSStackViewGravity)gravity
{
switch (gravity)
{
case NSStackViewGravityTop: // or leading...
[_beginningContainer _insertView: view atIndex: index];
break;
case NSStackViewGravityCenter:
[_middleContainer _insertView: view atIndex: index];
break;
case NSStackViewGravityBottom:
[_endContainer _insertView: view atIndex: index]; // or trailing...
break;
default:
[NSException raise: NSInternalInconsistencyException
format: @"Attempt insert view %@ at index %ld into unknown container %ld.", view, index, gravity];
break;
}
[self _refreshView];
}
- (void)removeView: (NSView *)view
{
[view removeFromSuperview];
[self _refreshView];
}
- (NSArray *) viewsInGravity: (NSStackViewGravity)gravity
{
NSMutableArray *result = [NSMutableArray array];
if (_beginningContainer != nil)
{
switch (gravity)
{
case NSStackViewGravityTop: // or leading...
[result addObjectsFromArray: [_beginningContainer subviews]];
break;
case NSStackViewGravityCenter:
[result addObjectsFromArray: [_middleContainer subviews]];
break;
case NSStackViewGravityBottom:
[result addObjectsFromArray: [_endContainer subviews]];
break;
default:
[NSException raise: NSInternalInconsistencyException
format: @"Attempt get array of views from unknown gravity %ld.", gravity];
break;
}
}
return result;
}
- (void)setViews: (NSArray *)views inGravity: (NSStackViewGravity)gravity
{
if (_beginningContainer != nil)
{
switch (gravity)
{
case NSStackViewGravityTop: // or leading...
[_beginningContainer _removeAllSubviews];
[_beginningContainer _addSubviews: views];
break;
case NSStackViewGravityCenter:
[_middleContainer _removeAllSubviews];
[_middleContainer _addSubviews: views];
break;
case NSStackViewGravityBottom:
[_endContainer _removeAllSubviews];
[_endContainer _addSubviews: views];
break;
default:
[NSException raise: NSInternalInconsistencyException
format: @"Attempt set array of views %@ into unknown gravity %ld.", views, gravity];
break;
}
}
else
{
[super _addSubviews: views];
}
[self _refreshView];
}
- (void) setViews: (NSArray *)views
{
ASSIGN(_arrangedSubviews, views);
[self _refreshView];
}
- (NSArray *) views
{
return _arrangedSubviews;
}
// Encoding...
- (void) encodeWithCoder: (NSCoder *)coder
{
[super encodeWithCoder: coder];
if ([coder allowsKeyedCoding])
{
[coder encodeInteger: _alignment forKey: @"NSStackViewAlignment"];
[coder encodeObject: _beginningContainer forKey: @"NSStackViewBeginningContainer"];
[coder encodeObject: _middleContainer forKey: @"NSStackViewMiddleContainer"];
[coder encodeObject: _endContainer forKey: @"NSStackViewEndContainer"];
[coder encodeBool: _detachesHiddenViews forKey: @"NSStackViewDetachesHiddenViews"];
[coder encodeFloat: _edgeInsets.bottom forKey: @"NSStackViewEdgeInsets.bottom"];
[coder encodeFloat: _edgeInsets.left forKey: @"NSStackViewEdgeInsets.left"];
[coder encodeFloat: _edgeInsets.right forKey: @"NSStackViewEdgeInsets.right"];
[coder encodeFloat: _edgeInsets.top forKey: @"NSStackViewEdgeInsets.top"];
[coder encodeBool: _hasFlagViewHierarchy forKey: @"NSStackViewHasFlagViewHierarchy"];
[coder encodeFloat: _horizontalClippingResistancePriority forKey: @"NSStackViewHorizontalClippingResistance"];
[coder encodeFloat: _horizontalHuggingPriority forKey: @"NSStackViewHorizontalHuggingPriority"];
[coder encodeInteger: _orientation forKey: @"NSStackViewOrientation"];
[coder encodeInteger: _alignment forKey: @"NSStackViewSecondaryAlignment"];
[coder encodeFloat: _spacing forKey: @"NSStackViewSpacing"];
[coder encodeFloat: _verticalClippingResistancePriority forKey: @"NSStackViewVerticalClippingResistance"];
[coder encodeFloat: _verticalHuggingPriority forKey: @"NSStackViewVerticalHuggingPriority"];
[coder encodeInteger: _distribution forKey: @"NSStackViewdistribution"];
}
else
{
[coder encodeValueOfObjCType: @encode(NSUInteger)
at: &_alignment];
[coder encodeObject: _beginningContainer];
[coder encodeObject: _middleContainer];
[coder encodeObject: _endContainer];
[coder encodeValueOfObjCType: @encode(BOOL)
at: &_detachesHiddenViews];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.bottom];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.left];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.right];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.top];
[coder encodeValueOfObjCType: @encode(BOOL)
at: &_hasFlagViewHierarchy];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_horizontalClippingResistancePriority];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_horizontalHuggingPriority];
[coder encodeValueOfObjCType: @encode(NSInteger)
at: &_orientation];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_secondaryAlignment];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_spacing];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_verticalClippingResistancePriority];
[coder encodeValueOfObjCType: @encode(CGFloat)
at: &_verticalHuggingPriority];
[coder encodeValueOfObjCType: @encode(NSInteger)
at: &_distribution];
}
}
- (instancetype) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self != nil)
{
if ([coder allowsKeyedCoding])
{
if ([coder containsValueForKey: @"NSStackViewAlignment"])
{
_alignment = [coder decodeIntForKey: @"NSStackViewAlignment"];
}
if ([coder containsValueForKey: @"NSStackViewBeginningContainer"])
{
ASSIGN(_beginningContainer, [coder decodeObjectForKey: @"NSStackViewBeginningContainer"]);
}
if ([coder containsValueForKey: @"NSStackViewMiddleContainer"])
{
ASSIGN(_middleContainer, [coder decodeObjectForKey: @"NSStackViewMiddleContainer"]);
}
if ([coder containsValueForKey: @"NSStackViewEndContainer"])
{
ASSIGN(_endContainer, [coder decodeObjectForKey: @"NSStackViewEndContainer"]);
}
if ([coder containsValueForKey: @"NSStackViewDetachesHiddenViews"])
{
_detachesHiddenViews = [coder decodeBoolForKey: @"NSStackViewDetachesHiddenViews"];
}
if ([coder containsValueForKey: @"NSStackViewEdgeInsets.bottom"])
{
_edgeInsets.bottom = [coder decodeFloatForKey: @"NSStackViewEdgeInsets.bottom"];
}
if ([coder containsValueForKey: @"NSStackViewEdgeInsets.left"])
{
_edgeInsets.left = [coder decodeFloatForKey: @"NSStackViewEdgeInsets.left"];
}
if ([coder containsValueForKey: @"NSStackViewEdgeInsets.right"])
{
_edgeInsets.right = [coder decodeFloatForKey: @"NSStackViewEdgeInsets.right"];
}
if ([coder containsValueForKey: @"NSStackViewEdgeInsets.top"])
{
_edgeInsets.top = [coder decodeFloatForKey: @"NSStackViewEdgeInsets.top"];
}
if ([coder containsValueForKey: @"NSStackViewHasFlagViewHierarchy"])
{
_hasFlagViewHierarchy = [coder decodeBoolForKey: @"NSStackViewHasFlagViewHierarchy"];
}
if ([coder containsValueForKey: @"NSStackViewHorizontalClippingResistance"])
{
_horizontalClippingResistancePriority = [coder decodeFloatForKey: @"NSStackViewHorizontalClippingResistance"];
}
if ([coder containsValueForKey: @"NSStackViewHorizontalHuggingPriority"])
{
_horizontalHuggingPriority = [coder decodeFloatForKey: @"NSStackViewHorizontalHuggingPriority"];
}
if ([coder containsValueForKey: @"NSStackViewOrientation"])
{
_orientation = [coder decodeIntForKey: @"NSStackViewOrientation"];
}
if ([coder containsValueForKey: @"NSStackViewSecondaryAlignment"])
{
_secondaryAlignment = [coder decodeFloatForKey: @"NSStackViewSecondaryAlignment"];
}
if ([coder containsValueForKey: @"NSStackViewSpacing"])
{
_spacing = [coder decodeFloatForKey: @"NSStackViewSpacing"];
}
if ([coder containsValueForKey: @"NSStackViewVerticalClippingResistance"])
{
_verticalClippingResistancePriority = [coder decodeFloatForKey: @"NSStackViewVerticalClippingResistance"];
}
if ([coder containsValueForKey: @"NSStackViewVerticalHugging"])
{
_verticalHuggingPriority = [coder decodeFloatForKey: @"NSStackViewVerticalHugging"];
}
if ([coder containsValueForKey: @"NSStackViewdistribution"])
{
_distribution = [coder decodeIntForKey: @"NSStackViewdistribution"];
}
}
else
{
[coder decodeValueOfObjCType: @encode(NSUInteger)
at: &_alignment];
ASSIGN(_beginningContainer, [coder decodeObject]);
[coder decodeValueOfObjCType: @encode(BOOL)
at: &_detachesHiddenViews];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.bottom];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.left];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.right];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_edgeInsets.top];
[coder decodeValueOfObjCType: @encode(BOOL)
at: &_hasFlagViewHierarchy];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_horizontalClippingResistancePriority];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_horizontalHuggingPriority];
[coder decodeValueOfObjCType: @encode(NSInteger)
at: &_orientation];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_secondaryAlignment];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_spacing];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_verticalClippingResistancePriority];
[coder decodeValueOfObjCType: @encode(CGFloat)
at: &_verticalHuggingPriority];
[coder decodeValueOfObjCType: @encode(NSInteger)
at: &_distribution];
}
[self _refreshView];
}
return self;
}
@end