mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
Replace objc directive with native Javascript version // MARK: to facilitate elimination of preproccsing pass.
701 lines
22 KiB
Plaintext
701 lines
22 KiB
Plaintext
/*
|
||
* CPStackView.j
|
||
* AppKit
|
||
*
|
||
* Created by Daniel Boehringer.
|
||
* Copyright 2025, Cappuccino Project.
|
||
*
|
||
* 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-1301 USA
|
||
*/
|
||
|
||
#include "../Foundation/Foundation.h"
|
||
|
||
@import "CPView.j"
|
||
|
||
// Gravity Areas
|
||
@typedef CPStackViewGravity
|
||
CPStackViewGravityTop = 1;
|
||
CPStackViewGravityLeading = 1;
|
||
CPStackViewGravityCenter = 2;
|
||
CPStackViewGravityBottom = 3;
|
||
CPStackViewGravityTrailing = 3;
|
||
|
||
// Distribution (Deprecated in modern macOS, but kept for compatibility/logic)
|
||
@typedef CPStackViewDistribution
|
||
CPStackViewDistributionGravityAreas = 0;
|
||
CPStackViewDistributionFill = 1;
|
||
CPStackViewDistributionFillEqually = 2;
|
||
CPStackViewDistributionFillProportionally = 3;
|
||
CPStackViewDistributionEqualSpacing = 4;
|
||
CPStackViewDistributionEqualCentering = 5;
|
||
|
||
// Visibility Priority
|
||
@typedef CPStackViewVisibilityPriority
|
||
CPStackViewVisibilityPriorityMustHold = 1000.0;
|
||
CPStackViewVisibilityPriorityNotVisible = 0.0;
|
||
|
||
var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
|
||
|
||
/*!
|
||
@ingroup appkit
|
||
@class CPStackView
|
||
|
||
CPStackView arranges an array of views horizontally or vertically and updates
|
||
their placement and sizing when the window size changes.
|
||
|
||
Unlike a simple list, CPStackView supports "Gravity Areas" (Leading, Center, Trailing),
|
||
allowing you to pin groups of views to specific sections of the layout.
|
||
*/
|
||
@implementation CPStackView : CPView
|
||
{
|
||
CPUserInterfaceLayoutOrientation _orientation;
|
||
CPLayoutAttribute _alignment;
|
||
float _spacing;
|
||
CPEdgeInsets _edgeInsets;
|
||
|
||
BOOL _detachesHiddenViews;
|
||
|
||
// View Storage by Gravity
|
||
CPMutableArray _viewsLeading;
|
||
CPMutableArray _viewsCenter;
|
||
CPMutableArray _viewsTrailing;
|
||
|
||
// Internal cache of all arranged subviews to maintain order for hittesting/iterating
|
||
CPMutableArray _arrangedSubviews;
|
||
|
||
// Custom Spacing storage
|
||
CPMapTable _customSpacings;
|
||
|
||
// Visibility Priorities
|
||
CPMapTable _visibilityPriorities;
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Initialization
|
||
|
||
+ (CPStackView)stackViewWithViews:(CPArray)views
|
||
{
|
||
var stackView = [[CPStackView alloc] initWithFrame:CGRectMakeZero()];
|
||
|
||
for (var i = 0, count = [views count]; i < count; i++)
|
||
[stackView addView:views[i] inGravity:CPStackViewGravityLeading];
|
||
|
||
return stackView;
|
||
}
|
||
|
||
- (id)initWithFrame:(CGRect)aFrame
|
||
{
|
||
if (self = [super initWithFrame:aFrame])
|
||
{
|
||
_orientation = CPUserInterfaceLayoutOrientationHorizontal;
|
||
_alignment = CPLayoutAttributeCenterY; // Default alignment
|
||
_spacing = 8.0; // Default Cocoa spacing
|
||
_edgeInsets = CPEdgeInsetsMake(0, 0, 0, 0);
|
||
_detachesHiddenViews = YES;
|
||
|
||
_viewsLeading = [[CPMutableArray alloc] init];
|
||
_viewsCenter = [[CPMutableArray alloc] init];
|
||
_viewsTrailing = [[CPMutableArray alloc] init];
|
||
_arrangedSubviews = [[CPMutableArray alloc] init];
|
||
|
||
_customSpacings = [[CPMapTable alloc] init];
|
||
_visibilityPriorities = [[CPMapTable alloc] init];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Configuration
|
||
|
||
/*!
|
||
The horizontal or vertical layout direction of the stack view.
|
||
*/
|
||
- (CPUserInterfaceLayoutOrientation)orientation
|
||
{
|
||
return _orientation;
|
||
}
|
||
|
||
- (void)setOrientation:(CPUserInterfaceLayoutOrientation)anOrientation
|
||
{
|
||
if (_orientation === anOrientation)
|
||
return;
|
||
|
||
_orientation = anOrientation;
|
||
|
||
// Reset default alignment based on new orientation if needed,
|
||
// though usually developer sets alignment explicitly.
|
||
// If switching to Vertical, CenterY makes less sense, usually CenterX.
|
||
if (_orientation === CPUserInterfaceLayoutOrientationVertical)
|
||
{
|
||
if (_alignment === CPLayoutAttributeCenterY)
|
||
_alignment = CPLayoutAttributeCenterX;
|
||
}
|
||
else
|
||
{
|
||
if (_alignment === CPLayoutAttributeCenterX)
|
||
_alignment = CPLayoutAttributeCenterY;
|
||
}
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
The view alignment within the stack view.
|
||
Common values:
|
||
Horizontal: CPLayoutAttributeTop, CPLayoutAttributeBottom, CPLayoutAttributeCenterY, CPLayoutAttributeHeight (fill)
|
||
Vertical: CPLayoutAttributeLeading, CPLayoutAttributeTrailing, CPLayoutAttributeCenterX, CPLayoutAttributeWidth (fill)
|
||
*/
|
||
- (CPLayoutAttribute)alignment
|
||
{
|
||
return _alignment;
|
||
}
|
||
|
||
- (void)setAlignment:(CPLayoutAttribute)anAlignment
|
||
{
|
||
if (_alignment === anAlignment)
|
||
return;
|
||
|
||
_alignment = anAlignment;
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
The minimum spacing, in points, between adjacent views in the stack view.
|
||
*/
|
||
- (float)spacing
|
||
{
|
||
return _spacing;
|
||
}
|
||
|
||
- (void)setSpacing:(float)aSpacing
|
||
{
|
||
if (_spacing === aSpacing)
|
||
return;
|
||
|
||
_spacing = aSpacing;
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
The geometric padding, in points, inside the stack view, surrounding its views.
|
||
*/
|
||
- (CPEdgeInsets)edgeInsets
|
||
{
|
||
return _edgeInsets;
|
||
}
|
||
|
||
- (void)setEdgeInsets:(CPEdgeInsets)insets
|
||
{
|
||
if (CPEdgeInsetsEqualToEdgeInsets(_edgeInsets, insets))
|
||
return;
|
||
|
||
_edgeInsets = insets;
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
A Boolean value that indicates whether the stack view removes hidden views from its view hierarchy.
|
||
*/
|
||
- (BOOL)detachesHiddenViews
|
||
{
|
||
return _detachesHiddenViews;
|
||
}
|
||
|
||
- (void)setDetachesHiddenViews:(BOOL)shouldDetach
|
||
{
|
||
if (_detachesHiddenViews === shouldDetach)
|
||
return;
|
||
|
||
_detachesHiddenViews = shouldDetach;
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Managing Views in Gravity Areas
|
||
|
||
- (CPArray)_containerForGravity:(CPStackViewGravity)gravity
|
||
{
|
||
if (gravity === CPStackViewGravityCenter)
|
||
return _viewsCenter;
|
||
else if (gravity === CPStackViewGravityTrailing) // or Bottom
|
||
return _viewsTrailing;
|
||
|
||
return _viewsLeading; // Leading or Top
|
||
}
|
||
|
||
/*!
|
||
Adds a view to the end of the stack view gravity area.
|
||
*/
|
||
- (void)addView:(CPView)aView inGravity:(CPStackViewGravity)gravity
|
||
{
|
||
var container = [self _containerForGravity:gravity];
|
||
|
||
// Check if view is already in a container
|
||
if ([_arrangedSubviews containsObject:aView])
|
||
[self removeView:aView];
|
||
|
||
[container addObject:aView];
|
||
[_arrangedSubviews addObject:aView];
|
||
|
||
// Add as actual subview
|
||
if ([aView superview] !== self)
|
||
[self addSubview:aView];
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
Adds a view to a stack view gravity area at a specified index position.
|
||
*/
|
||
- (void)insertView:(CPView)aView atIndex:(CPInteger)index inGravity:(CPStackViewGravity)gravity
|
||
{
|
||
var container = [self _containerForGravity:gravity];
|
||
|
||
if ([_arrangedSubviews containsObject:aView])
|
||
[self removeView:aView];
|
||
|
||
if (index >= [container count])
|
||
[container addObject:aView];
|
||
else
|
||
[container insertObject:aView atIndex:index];
|
||
|
||
[_arrangedSubviews addObject:aView];
|
||
|
||
if ([aView superview] !== self)
|
||
[self addSubview:aView];
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
Specifies an array of views for a specified gravity area in the stack view, replacing any previous views in that area.
|
||
*/
|
||
- (void)setViews:(CPArray)views inGravity:(CPStackViewGravity)gravity
|
||
{
|
||
var container = [self _containerForGravity:gravity];
|
||
|
||
// Remove old views from arranged list and superview
|
||
for (var i = 0; i < [container count]; i++)
|
||
{
|
||
var oldView = container[i];
|
||
[oldView removeFromSuperview];
|
||
[_arrangedSubviews removeObject:oldView];
|
||
}
|
||
|
||
[container removeAllObjects];
|
||
|
||
for (var i = 0; i < [views count]; i++)
|
||
{
|
||
var newView = views[i];
|
||
[container addObject:newView];
|
||
[_arrangedSubviews addObject:newView];
|
||
[self addSubview:newView];
|
||
}
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
Removes a specified view from the stack view.
|
||
*/
|
||
- (void)removeView:(CPView)aView
|
||
{
|
||
if (![_arrangedSubviews containsObject:aView])
|
||
return;
|
||
|
||
[_viewsLeading removeObject:aView];
|
||
[_viewsCenter removeObject:aView];
|
||
[_viewsTrailing removeObject:aView];
|
||
[_arrangedSubviews removeObject:aView];
|
||
|
||
[aView removeFromSuperview];
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
/*!
|
||
Returns the array of views in the specified gravity area in the stack view.
|
||
*/
|
||
- (CPArray)viewsInGravity:(CPStackViewGravity)gravity
|
||
{
|
||
return [[self _containerForGravity:gravity] copy];
|
||
}
|
||
|
||
/*!
|
||
The array of views arranged by the stack view.
|
||
*/
|
||
- (CPArray)arrangedSubviews
|
||
{
|
||
return [_arrangedSubviews copy];
|
||
}
|
||
|
||
/*!
|
||
Adds the specified view to the end of the arranged subviews list.
|
||
(Defaults to Leading gravity if not specified).
|
||
*/
|
||
- (void)addArrangedSubview:(CPView)view
|
||
{
|
||
[self addView:view inGravity:CPStackViewGravityLeading];
|
||
}
|
||
|
||
/*!
|
||
Removes the provided view from the stack’s array of arranged subviews.
|
||
*/
|
||
- (void)removeArrangedSubview:(CPView)view
|
||
{
|
||
[self removeView:view];
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Custom Spacing
|
||
|
||
- (float)customSpacingAfterView:(CPView)aView
|
||
{
|
||
var val = [_customSpacings objectForKey:aView];
|
||
if (val)
|
||
return [val floatValue];
|
||
|
||
return CPStackViewSpacingUseDefault;
|
||
}
|
||
|
||
- (void)setCustomSpacing:(float)spacing afterView:(CPView)aView
|
||
{
|
||
if (spacing === CPStackViewSpacingUseDefault)
|
||
[_customSpacings removeObjectForKey:aView];
|
||
else
|
||
[_customSpacings setObject:spacing forKey:aView];
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
- (float)_spacingAfterView:(CPView)aView
|
||
{
|
||
var custom = [self customSpacingAfterView:aView];
|
||
if (custom !== CPStackViewSpacingUseDefault)
|
||
return custom;
|
||
return _spacing;
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Visibility Priority
|
||
|
||
- (void)setVisibilityPriority:(float)priority forView:(CPView)aView
|
||
{
|
||
[_visibilityPriorities setObject:priority forKey:aView];
|
||
|
||
if (priority === CPStackViewVisibilityPriorityNotVisible)
|
||
{
|
||
[aView setHidden:YES];
|
||
}
|
||
else if (priority === CPStackViewVisibilityPriorityMustHold)
|
||
{
|
||
[aView setHidden:NO];
|
||
}
|
||
// Note: Intermediate priorities require complex constraint logic
|
||
// or a multi-pass layout system to determine fitting, which is
|
||
// simplified here to basic Hidden/Visible states.
|
||
|
||
[self setNeedsLayout:YES];
|
||
}
|
||
|
||
- (float)visibilityPriorityForView:(CPView)aView
|
||
{
|
||
var val = [_visibilityPriorities objectForKey:aView];
|
||
if (val)
|
||
return [val floatValue];
|
||
return CPStackViewVisibilityPriorityMustHold;
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: Layout
|
||
|
||
- (void)resizeSubviewsWithOldSize:(CGSize)oldSize
|
||
{
|
||
[self layoutSubviews];
|
||
}
|
||
|
||
- (void)layoutSubviews
|
||
{
|
||
if (_orientation === CPUserInterfaceLayoutOrientationVertical)
|
||
[self _layoutVertical];
|
||
else
|
||
[self _layoutHorizontal];
|
||
}
|
||
|
||
- (void)_layoutHorizontal
|
||
{
|
||
var bounds = [self bounds],
|
||
availWidth = CGRectGetWidth(bounds) - _edgeInsets.left - _edgeInsets.right,
|
||
availHeight = CGRectGetHeight(bounds) - _edgeInsets.top - _edgeInsets.bottom,
|
||
currentX = _edgeInsets.left;
|
||
|
||
// 1. Layout Leading Views
|
||
currentX = [self _layoutViews:_viewsLeading startOffset:currentX availableOrthogonalSize:availHeight direction:1];
|
||
|
||
// 2. Layout Trailing Views
|
||
// We layout backwards from the right
|
||
var startRight = CGRectGetWidth(bounds) - _edgeInsets.right;
|
||
[self _layoutViews:_viewsTrailing startOffset:startRight availableOrthogonalSize:availHeight direction:-1];
|
||
|
||
// 3. Layout Center Views
|
||
if ([_viewsCenter count] > 0)
|
||
{
|
||
// Calculate total width of center stack
|
||
var centerStackWidth = 0.0;
|
||
for (var i = 0; i < [_viewsCenter count]; i++)
|
||
{
|
||
var view = _viewsCenter[i];
|
||
if (_detachesHiddenViews && [view isHidden]) continue;
|
||
|
||
centerStackWidth += CGRectGetWidth([view frame]);
|
||
if (i < [_viewsCenter count] - 1)
|
||
centerStackWidth += [self _spacingAfterView:view];
|
||
}
|
||
|
||
var centerStart = (CGRectGetWidth(bounds) / 2.0) - (centerStackWidth / 2.0);
|
||
|
||
// Clamp to prevent overlap with Leading (simplified collision logic)
|
||
// ideally stack view compresses views, but here we just shift/clip
|
||
if (centerStart < currentX)
|
||
centerStart = currentX;
|
||
|
||
[self _layoutViews:_viewsCenter startOffset:centerStart availableOrthogonalSize:availHeight direction:1];
|
||
}
|
||
}
|
||
|
||
- (void)_layoutVertical
|
||
{
|
||
var bounds = [self bounds],
|
||
availWidth = CGRectGetWidth(bounds) - _edgeInsets.left - _edgeInsets.right,
|
||
availHeight = CGRectGetHeight(bounds) - _edgeInsets.top - _edgeInsets.bottom,
|
||
currentY = _edgeInsets.top;
|
||
|
||
// 1. Layout Top (Leading) Views
|
||
currentY = [self _layoutViews:_viewsLeading startOffset:currentY availableOrthogonalSize:availWidth direction:1];
|
||
|
||
// 2. Layout Bottom (Trailing) Views
|
||
var startBottom = CGRectGetHeight(bounds) - _edgeInsets.bottom;
|
||
[self _layoutViews:_viewsTrailing startOffset:startBottom availableOrthogonalSize:availWidth direction:-1];
|
||
|
||
// 3. Layout Center Views
|
||
if ([_viewsCenter count] > 0)
|
||
{
|
||
var centerStackHeight = 0.0;
|
||
for (var i = 0; i < [_viewsCenter count]; i++)
|
||
{
|
||
var view = _viewsCenter[i];
|
||
if (_detachesHiddenViews && [view isHidden]) continue;
|
||
|
||
centerStackHeight += CGRectGetHeight([view frame]);
|
||
if (i < [_viewsCenter count] - 1)
|
||
centerStackHeight += [self _spacingAfterView:view];
|
||
}
|
||
|
||
var centerStart = (CGRectGetHeight(bounds) / 2.0) - (centerStackHeight / 2.0);
|
||
|
||
if (centerStart < currentY)
|
||
centerStart = currentY;
|
||
|
||
[self _layoutViews:_viewsCenter startOffset:centerStart availableOrthogonalSize:availWidth direction:1];
|
||
}
|
||
}
|
||
|
||
// Helper to layout a specific array of views in one direction
|
||
// Returns the ending offset
|
||
- (float)_layoutViews:(CPArray)views startOffset:(float)offset availableOrthogonalSize:(float)orthoSize direction:(int)dir
|
||
{
|
||
var cursor = offset;
|
||
var isVert = (_orientation === CPUserInterfaceLayoutOrientationVertical);
|
||
|
||
// If direction is -1 (Trailing/Bottom), we iterate backwards
|
||
// However, the standard behavior for trailing gravity is that the *last* view added is at the *end*.
|
||
// Leading: [A] [B] ->
|
||
// Trailing: -> [C] [D] (where D is rightmost)
|
||
// To support Trailing logic: We start at Right Edge, move left by Width(D), place D, move left by Spacing...
|
||
|
||
var count = [views count];
|
||
if (count === 0) return cursor;
|
||
|
||
// If direction is negative (Trailing), we process list in reverse order to stack them from edge inwards
|
||
var i = (dir === 1) ? 0 : count - 1;
|
||
var limit = (dir === 1) ? count : -1;
|
||
var step = (dir === 1) ? 1 : -1;
|
||
|
||
for (; i !== limit; i += step)
|
||
{
|
||
var view = views[i];
|
||
|
||
if (_detachesHiddenViews && [view isHidden])
|
||
continue;
|
||
|
||
var viewFrame = [view frame];
|
||
var viewSizePrimary = isVert ? CGRectGetHeight(viewFrame) : CGRectGetWidth(viewFrame);
|
||
|
||
// Handle Alignment (Orthogonal Axis)
|
||
var orthoPos = 0.0;
|
||
var viewOrthoSize = isVert ? CGRectGetWidth(viewFrame) : CGRectGetHeight(viewFrame);
|
||
|
||
// Apply Stretch/Fill Alignment
|
||
if (isVert)
|
||
{
|
||
// Vertical Stack, dealing with Width
|
||
if (_alignment === CPLayoutAttributeWidth || _alignment === CPLayoutAttributeLeading || _alignment === CPLayoutAttributeTrailing)
|
||
{
|
||
// Note: CPLayoutAttributeLeading/Trailing in this context implies filling width usually,
|
||
// or aligning to edges. Let's assume Width/Fill for Leading/Trailing/Left/Right
|
||
// in this simplified implementation, or strictly left/right.
|
||
|
||
if (_alignment === CPLayoutAttributeWidth || _alignment === CPLayoutAttributeLeft || _alignment === CPLayoutAttributeLeading)
|
||
{
|
||
// Fill width if explicit, or just align left
|
||
if (_alignment === CPLayoutAttributeWidth) viewOrthoSize = orthoSize;
|
||
orthoPos = _edgeInsets.left;
|
||
}
|
||
else if (_alignment === CPLayoutAttributeRight || _alignment === CPLayoutAttributeTrailing)
|
||
{
|
||
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize);
|
||
}
|
||
else // CenterX
|
||
{
|
||
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize) / 2.0;
|
||
}
|
||
}
|
||
else // Default CenterX
|
||
{
|
||
orthoPos = _edgeInsets.left + (orthoSize - viewOrthoSize) / 2.0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Horizontal Stack, dealing with Height
|
||
if (_alignment === CPLayoutAttributeHeight || _alignment === CPLayoutAttributeTop || _alignment === CPLayoutAttributeBottom)
|
||
{
|
||
if (_alignment === CPLayoutAttributeHeight)
|
||
{
|
||
viewOrthoSize = orthoSize;
|
||
orthoPos = _edgeInsets.top;
|
||
}
|
||
else if (_alignment === CPLayoutAttributeTop)
|
||
{
|
||
orthoPos = _edgeInsets.top;
|
||
}
|
||
else if (_alignment === CPLayoutAttributeBottom)
|
||
{
|
||
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize);
|
||
}
|
||
else // CenterY
|
||
{
|
||
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize) / 2.0;
|
||
}
|
||
}
|
||
else // Default CenterY
|
||
{
|
||
orthoPos = _edgeInsets.top + (orthoSize - viewOrthoSize) / 2.0;
|
||
}
|
||
}
|
||
|
||
// Calculate Position
|
||
var originX = 0.0, originY = 0.0;
|
||
var sizeW = 0.0, sizeH = 0.0;
|
||
|
||
if (isVert)
|
||
{
|
||
// Vertical
|
||
sizeH = viewSizePrimary;
|
||
sizeW = viewOrthoSize;
|
||
originX = orthoPos;
|
||
|
||
if (dir === 1) {
|
||
originY = cursor;
|
||
cursor += sizeH + [self _spacingAfterView:view];
|
||
} else {
|
||
cursor -= sizeH;
|
||
originY = cursor;
|
||
cursor -= [self _spacingAfterView:view];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Horizontal
|
||
sizeW = viewSizePrimary;
|
||
sizeH = viewOrthoSize;
|
||
originY = orthoPos;
|
||
|
||
if (dir === 1) {
|
||
originX = cursor;
|
||
cursor += sizeW + [self _spacingAfterView:view];
|
||
} else {
|
||
cursor -= sizeW;
|
||
originX = cursor;
|
||
cursor -= [self _spacingAfterView:view];
|
||
}
|
||
}
|
||
|
||
[view setFrame:CGRectMake(originX, originY, sizeW, sizeH)];
|
||
}
|
||
|
||
return cursor;
|
||
}
|
||
|
||
// MARK: -
|
||
// MARK: CPCoding
|
||
|
||
- (id)initWithCoder:(CPCoder)aCoder
|
||
{
|
||
self = [super initWithCoder:aCoder];
|
||
if (self)
|
||
{
|
||
_orientation = [aCoder decodeIntForKey:@"CPStackViewOrientation"];
|
||
_alignment = [aCoder decodeIntForKey:@"CPStackViewAlignment"];
|
||
_spacing = [aCoder decodeFloatForKey:@"CPStackViewSpacing"];
|
||
_edgeInsets = [aCoder decodeObjectForKey:@"CPStackViewEdgeInsets"]; // Assuming CPEdgeInsets supports obj coding or manual decode
|
||
if (!_edgeInsets) _edgeInsets = CPEdgeInsetsMake(0,0,0,0);
|
||
|
||
_detachesHiddenViews = [aCoder decodeBoolForKey:@"CPStackViewDetachesHiddenViews"];
|
||
|
||
_viewsLeading = [aCoder decodeObjectForKey:@"CPStackViewViewsLeading"] || [];
|
||
_viewsCenter = [aCoder decodeObjectForKey:@"CPStackViewViewsCenter"] || [];
|
||
_viewsTrailing = [aCoder decodeObjectForKey:@"CPStackViewViewsTrailing"] || [];
|
||
|
||
// Rebuild arranged subviews cache
|
||
_arrangedSubviews = [[CPMutableArray alloc] init];
|
||
[_arrangedSubviews addObjectsFromArray:_viewsLeading];
|
||
[_arrangedSubviews addObjectsFromArray:_viewsCenter];
|
||
[_arrangedSubviews addObjectsFromArray:_viewsTrailing];
|
||
|
||
_customSpacings = [aCoder decodeObjectForKey:@"CPStackViewCustomSpacings"] || [[CPMapTable alloc] init];
|
||
_visibilityPriorities = [[CPMapTable alloc] init]; // usually not persisted
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)encodeWithCoder:(CPCoder)aCoder
|
||
{
|
||
[super encodeWithCoder:aCoder];
|
||
[aCoder encodeInt:_orientation forKey:@"CPStackViewOrientation"];
|
||
[aCoder encodeInt:_alignment forKey:@"CPStackViewAlignment"];
|
||
[aCoder encodeFloat:_spacing forKey:@"CPStackViewSpacing"];
|
||
[aCoder encodeObject:_edgeInsets forKey:@"CPStackViewEdgeInsets"];
|
||
[aCoder encodeBool:_detachesHiddenViews forKey:@"CPStackViewDetachesHiddenViews"];
|
||
|
||
[aCoder encodeObject:_viewsLeading forKey:@"CPStackViewViewsLeading"];
|
||
[aCoder encodeObject:_viewsCenter forKey:@"CPStackViewViewsCenter"];
|
||
[aCoder encodeObject:_viewsTrailing forKey:@"CPStackViewViewsTrailing"];
|
||
|
||
[aCoder encodeObject:_customSpacings forKey:@"CPStackViewCustomSpacings"];
|
||
}
|
||
|
||
@end
|