aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/darwin/box.m
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/libui/darwin/box.m')
-rw-r--r--src/libui_sdl/libui/darwin/box.m469
1 files changed, 469 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/darwin/box.m b/src/libui_sdl/libui/darwin/box.m
new file mode 100644
index 0000000..18d536d
--- /dev/null
+++ b/src/libui_sdl/libui/darwin/box.m
@@ -0,0 +1,469 @@
+// 15 august 2015
+#import "uipriv_darwin.h"
+
+// TODO hiding all stretchy controls still hugs trailing edge
+
+@interface boxChild : NSObject
+@property uiControl *c;
+@property BOOL stretchy;
+@property NSLayoutPriority oldPrimaryHuggingPri;
+@property NSLayoutPriority oldSecondaryHuggingPri;
+- (NSView *)view;
+@end
+
+@interface boxView : NSView {
+ uiBox *b;
+ NSMutableArray *children;
+ BOOL vertical;
+ int padded;
+
+ NSLayoutConstraint *first;
+ NSMutableArray *inBetweens;
+ NSLayoutConstraint *last;
+ NSMutableArray *otherConstraints;
+
+ NSLayoutAttribute primaryStart;
+ NSLayoutAttribute primaryEnd;
+ NSLayoutAttribute secondaryStart;
+ NSLayoutAttribute secondaryEnd;
+ NSLayoutAttribute primarySize;
+ NSLayoutConstraintOrientation primaryOrientation;
+ NSLayoutConstraintOrientation secondaryOrientation;
+}
+- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb;
+- (void)onDestroy;
+- (void)removeOurConstraints;
+- (void)syncEnableStates:(int)enabled;
+- (CGFloat)paddingAmount;
+- (void)establishOurConstraints;
+- (void)append:(uiControl *)c stretchy:(int)stretchy;
+- (void)delete:(int)n;
+- (int)isPadded;
+- (void)setPadded:(int)p;
+- (BOOL)hugsTrailing;
+- (BOOL)hugsBottom;
+- (int)nStretchy;
+@end
+
+struct uiBox {
+ uiDarwinControl c;
+ boxView *view;
+};
+
+@implementation boxChild
+
+- (NSView *)view
+{
+ return (NSView *) uiControlHandle(self.c);
+}
+
+@end
+
+@implementation boxView
+
+- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb
+{
+ self = [super initWithFrame:NSZeroRect];
+ if (self != nil) {
+ // the weird names vert and bb are to shut the compiler up about shadowing because implicit this/self is stupid
+ self->b = bb;
+ self->vertical = vert;
+ self->padded = 0;
+ self->children = [NSMutableArray new];
+
+ self->inBetweens = [NSMutableArray new];
+ self->otherConstraints = [NSMutableArray new];
+
+ if (self->vertical) {
+ self->primaryStart = NSLayoutAttributeTop;
+ self->primaryEnd = NSLayoutAttributeBottom;
+ self->secondaryStart = NSLayoutAttributeLeading;
+ self->secondaryEnd = NSLayoutAttributeTrailing;
+ self->primarySize = NSLayoutAttributeHeight;
+ self->primaryOrientation = NSLayoutConstraintOrientationVertical;
+ self->secondaryOrientation = NSLayoutConstraintOrientationHorizontal;
+ } else {
+ self->primaryStart = NSLayoutAttributeLeading;
+ self->primaryEnd = NSLayoutAttributeTrailing;
+ self->secondaryStart = NSLayoutAttributeTop;
+ self->secondaryEnd = NSLayoutAttributeBottom;
+ self->primarySize = NSLayoutAttributeWidth;
+ self->primaryOrientation = NSLayoutConstraintOrientationHorizontal;
+ self->secondaryOrientation = NSLayoutConstraintOrientationVertical;
+ }
+ }
+ return self;
+}
+
+- (void)onDestroy
+{
+ boxChild *bc;
+
+ [self removeOurConstraints];
+ [self->inBetweens release];
+ [self->otherConstraints release];
+
+ for (bc in self->children) {
+ uiControlSetParent(bc.c, NULL);
+ uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
+ uiControlDestroy(bc.c);
+ }
+ [self->children release];
+}
+
+- (void)removeOurConstraints
+{
+ if (self->first != nil) {
+ [self removeConstraint:self->first];
+ [self->first release];
+ self->first = nil;
+ }
+ if ([self->inBetweens count] != 0) {
+ [self removeConstraints:self->inBetweens];
+ [self->inBetweens removeAllObjects];
+ }
+ if (self->last != nil) {
+ [self removeConstraint:self->last];
+ [self->last release];
+ self->last = nil;
+ }
+ if ([self->otherConstraints count] != 0) {
+ [self removeConstraints:self->otherConstraints];
+ [self->otherConstraints removeAllObjects];
+ }
+}
+
+- (void)syncEnableStates:(int)enabled
+{
+ boxChild *bc;
+
+ for (bc in self->children)
+ uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), enabled);
+}
+
+- (CGFloat)paddingAmount
+{
+ if (!self->padded)
+ return 0.0;
+ return uiDarwinPaddingAmount(NULL);
+}
+
+- (void)establishOurConstraints
+{
+ boxChild *bc;
+ CGFloat padding;
+ NSView *prev;
+ NSLayoutConstraint *c;
+ BOOL (*hugsSecondary)(uiDarwinControl *);
+
+ [self removeOurConstraints];
+ if ([self->children count] == 0)
+ return;
+ padding = [self paddingAmount];
+
+ // first arrange in the primary direction
+ prev = nil;
+ for (bc in self->children) {
+ if (!uiControlVisible(bc.c))
+ continue;
+ if (prev == nil) { // first view
+ self->first = mkConstraint(self, self->primaryStart,
+ NSLayoutRelationEqual,
+ [bc view], self->primaryStart,
+ 1, 0,
+ @"uiBox first primary constraint");
+ [self addConstraint:self->first];
+ [self->first retain];
+ prev = [bc view];
+ continue;
+ }
+ // not the first; link it
+ c = mkConstraint(prev, self->primaryEnd,
+ NSLayoutRelationEqual,
+ [bc view], self->primaryStart,
+ 1, -padding,
+ @"uiBox in-between primary constraint");
+ [self addConstraint:c];
+ [self->inBetweens addObject:c];
+ prev = [bc view];
+ }
+ if (prev == nil) // no control visible; act as if no controls
+ return;
+ self->last = mkConstraint(prev, self->primaryEnd,
+ NSLayoutRelationEqual,
+ self, self->primaryEnd,
+ 1, 0,
+ @"uiBox last primary constraint");
+ [self addConstraint:self->last];
+ [self->last retain];
+
+ // then arrange in the secondary direction
+ hugsSecondary = uiDarwinControlHugsTrailingEdge;
+ if (!self->vertical)
+ hugsSecondary = uiDarwinControlHugsBottom;
+ for (bc in self->children) {
+ if (!uiControlVisible(bc.c))
+ continue;
+ c = mkConstraint(self, self->secondaryStart,
+ NSLayoutRelationEqual,
+ [bc view], self->secondaryStart,
+ 1, 0,
+ @"uiBox secondary start constraint");
+ [self addConstraint:c];
+ [self->otherConstraints addObject:c];
+ c = mkConstraint([bc view], self->secondaryEnd,
+ NSLayoutRelationLessThanOrEqual,
+ self, self->secondaryEnd,
+ 1, 0,
+ @"uiBox secondary end <= constraint");
+ if ((*hugsSecondary)(uiDarwinControl(bc.c)))
+ [c setPriority:NSLayoutPriorityDefaultLow];
+ [self addConstraint:c];
+ [self->otherConstraints addObject:c];
+ c = mkConstraint([bc view], self->secondaryEnd,
+ NSLayoutRelationEqual,
+ self, self->secondaryEnd,
+ 1, 0,
+ @"uiBox secondary end == constraint");
+ if (!(*hugsSecondary)(uiDarwinControl(bc.c)))
+ [c setPriority:NSLayoutPriorityDefaultLow];
+ [self addConstraint:c];
+ [self->otherConstraints addObject:c];
+ }
+
+ // and make all stretchy controls the same size
+ if ([self nStretchy] == 0)
+ return;
+ prev = nil; // first stretchy view
+ for (bc in self->children) {
+ if (!uiControlVisible(bc.c))
+ continue;
+ if (!bc.stretchy)
+ continue;
+ if (prev == nil) {
+ prev = [bc view];
+ continue;
+ }
+ c = mkConstraint(prev, self->primarySize,
+ NSLayoutRelationEqual,
+ [bc view], self->primarySize,
+ 1, 0,
+ @"uiBox stretchy size constraint");
+ [self addConstraint:c];
+ [self->otherConstraints addObject:c];
+ }
+}
+
+- (void)append:(uiControl *)c stretchy:(int)stretchy
+{
+ boxChild *bc;
+ NSLayoutPriority priority;
+ int oldnStretchy;
+
+ bc = [boxChild new];
+ bc.c = c;
+ bc.stretchy = stretchy;
+ bc.oldPrimaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->primaryOrientation);
+ bc.oldSecondaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->secondaryOrientation);
+
+ uiControlSetParent(bc.c, uiControl(self->b));
+ uiDarwinControlSetSuperview(uiDarwinControl(bc.c), self);
+ uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), uiControlEnabledToUser(uiControl(self->b)));
+
+ // if a control is stretchy, it should not hug in the primary direction
+ // otherwise, it should *forcibly* hug
+ if (bc.stretchy)
+ priority = NSLayoutPriorityDefaultLow;
+ else
+ // LONGTERM will default high work?
+ priority = NSLayoutPriorityRequired;
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), priority, self->primaryOrientation);
+ // make sure controls don't hug their secondary direction so they fill the width of the view
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), NSLayoutPriorityDefaultLow, self->secondaryOrientation);
+
+ oldnStretchy = [self nStretchy];
+ [self->children addObject:bc];
+
+ [self establishOurConstraints];
+ if (bc.stretchy)
+ if (oldnStretchy == 0)
+ uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
+
+ [bc release]; // we don't need the initial reference now
+}
+
+- (void)delete:(int)n
+{
+ boxChild *bc;
+ int stretchy;
+
+ bc = (boxChild *) [self->children objectAtIndex:n];
+ stretchy = bc.stretchy;
+
+ uiControlSetParent(bc.c, NULL);
+ uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
+
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldPrimaryHuggingPri, self->primaryOrientation);
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldSecondaryHuggingPri, self->secondaryOrientation);
+
+ [self->children removeObjectAtIndex:n];
+
+ [self establishOurConstraints];
+ if (stretchy)
+ if ([self nStretchy] == 0)
+ uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
+}
+
+- (int)isPadded
+{
+ return self->padded;
+}
+
+- (void)setPadded:(int)p
+{
+ CGFloat padding;
+ NSLayoutConstraint *c;
+
+ self->padded = p;
+ padding = [self paddingAmount];
+ for (c in self->inBetweens)
+ [c setConstant:-padding];
+}
+
+- (BOOL)hugsTrailing
+{
+ if (self->vertical) // always hug if vertical
+ return YES;
+ return [self nStretchy] != 0;
+}
+
+- (BOOL)hugsBottom
+{
+ if (!self->vertical) // always hug if horizontal
+ return YES;
+ return [self nStretchy] != 0;
+}
+
+- (int)nStretchy
+{
+ boxChild *bc;
+ int n;
+
+ n = 0;
+ for (bc in self->children) {
+ if (!uiControlVisible(bc.c))
+ continue;
+ if (bc.stretchy)
+ n++;
+ }
+ return n;
+}
+
+@end
+
+static void uiBoxDestroy(uiControl *c)
+{
+ uiBox *b = uiBox(c);
+
+ [b->view onDestroy];
+ [b->view release];
+ uiFreeControl(uiControl(b));
+}
+
+uiDarwinControlDefaultHandle(uiBox, view)
+uiDarwinControlDefaultParent(uiBox, view)
+uiDarwinControlDefaultSetParent(uiBox, view)
+uiDarwinControlDefaultToplevel(uiBox, view)
+uiDarwinControlDefaultVisible(uiBox, view)
+uiDarwinControlDefaultShow(uiBox, view)
+uiDarwinControlDefaultHide(uiBox, view)
+uiDarwinControlDefaultEnabled(uiBox, view)
+uiDarwinControlDefaultEnable(uiBox, view)
+uiDarwinControlDefaultDisable(uiBox, view)
+
+static void uiBoxSyncEnableState(uiDarwinControl *c, int enabled)
+{
+ uiBox *b = uiBox(c);
+
+ if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(b), enabled))
+ return;
+ [b->view syncEnableStates:enabled];
+}
+
+uiDarwinControlDefaultSetSuperview(uiBox, view)
+
+static BOOL uiBoxHugsTrailingEdge(uiDarwinControl *c)
+{
+ uiBox *b = uiBox(c);
+
+ return [b->view hugsTrailing];
+}
+
+static BOOL uiBoxHugsBottom(uiDarwinControl *c)
+{
+ uiBox *b = uiBox(c);
+
+ return [b->view hugsBottom];
+}
+
+static void uiBoxChildEdgeHuggingChanged(uiDarwinControl *c)
+{
+ uiBox *b = uiBox(c);
+
+ [b->view establishOurConstraints];
+}
+
+uiDarwinControlDefaultHuggingPriority(uiBox, view)
+uiDarwinControlDefaultSetHuggingPriority(uiBox, view)
+
+static void uiBoxChildVisibilityChanged(uiDarwinControl *c)
+{
+ uiBox *b = uiBox(c);
+
+ [b->view establishOurConstraints];
+}
+
+void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
+{
+ // LONGTERM on other platforms
+ // or at leat allow this and implicitly turn it into a spacer
+ if (c == NULL)
+ userbug("You cannot add NULL to a uiBox.");
+ [b->view append:c stretchy:stretchy];
+}
+
+void uiBoxDelete(uiBox *b, int n)
+{
+ [b->view delete:n];
+}
+
+int uiBoxPadded(uiBox *b)
+{
+ return [b->view isPadded];
+}
+
+void uiBoxSetPadded(uiBox *b, int padded)
+{
+ [b->view setPadded:padded];
+}
+
+static uiBox *finishNewBox(BOOL vertical)
+{
+ uiBox *b;
+
+ uiDarwinNewControl(uiBox, b);
+
+ b->view = [[boxView alloc] initWithVertical:vertical b:b];
+
+ return b;
+}
+
+uiBox *uiNewHorizontalBox(void)
+{
+ return finishNewBox(NO);
+}
+
+uiBox *uiNewVerticalBox(void)
+{
+ return finishNewBox(YES);
+}