From d0ceac86c204a79c30b8395730e9c0c73c886a8d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 15 Aug 2015 23:32:34 -0400 Subject: [PATCH] Wrote up the Auto Layout-based uiBox implementation. --- redo/reredo/darwin/autolayout.m | 15 ++ redo/reredo/darwin/box.m | 282 ++++++++++++++++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 redo/reredo/darwin/box.m diff --git a/redo/reredo/darwin/autolayout.m b/redo/reredo/darwin/autolayout.m index 50d1b193..85f9590d 100644 --- a/redo/reredo/darwin/autolayout.m +++ b/redo/reredo/darwin/autolayout.m @@ -12,6 +12,21 @@ void addConstraint(NSView *view, NSString *constraint, NSDictionary *metrics, NS [view addConstraints:constraints]; } +NSLayoutPriority horzHuggingPri(NSView *view) +{ + return [view contentHuggingPriorityForOrientation:NSLayoutConstraintOrientationHorizontal]; +} + +NSLayoutPriority vertHuggingPri(NSView *view) +{ + return [view contentHuggingPriorityForOrientation:NSLayoutConstraintOrientationVertical]; +} + +void setHuggingPri(NSView *view, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation) +{ + [view setContentHuggingPriority:priority forOrientation:orientation]; +} + void layoutSingleView(NSView *superview, NSView *subview, int margined) { NSDictionary *views; diff --git a/redo/reredo/darwin/box.m b/redo/reredo/darwin/box.m new file mode 100644 index 00000000..2eadf6c0 --- /dev/null +++ b/redo/reredo/darwin/box.m @@ -0,0 +1,282 @@ +// 15 august 2015 +#import "uipriv_darwin.h" + +struct uiBox { + uiDarwinControl c; + NSView *view; + BOOL vertical; + int padded; + NSMutableArray *children; // []NSValue + NSMutableArray *stretchy; // []NSNumber + // this view is made stretchy if there are no stretchy views + NSView *noStretchyView; + NSString *primaryDirPrefix; + NSString *secondaryDirPrefix; + NSLayoutConstraintOrientation primaryOrientation; + NSLayoutConstraintOrientation secondaryOrientation; +}; + +static void onDestroy(uiBox *); + +uiDarwinDefineControlWithOnDestroy( + uiBox, // type name + uiBoxType, // type function + view, // handle + onDestroy(this); // on destroy +) + +static uiControl *childAt(uiBox *b, uintmax_t n) +{ + NSValue *v; + uiControl *c; + + v = (NSValue *) [b->children objectAtIndex:n]; + return (uiControl *) [v pointerValue]; +} + +static void onDestroy(uiTab *t) +{ + uintmax_t i; + uiControl *child; + NSView *childView; + + for (i = 0; i < [t->children count]; i++) { + child = childAt(b, i); + childView = (NSView *) uiControlHandle(child); + [childView removeFromSuperview]; + uiControlSetParent(child, NULL); + uiControlDestroy(child); + } + if ([b->noStretchyView superview] != nil) + [b->noStretchyView removeFromSuperview]; + [b->noStretchyView release]; + [b->children release]; + [b->stretchy release]; +} + +// TODO container update state + +// TODO change i to n +static NSString *viewName(uintmax_t i) +{ + return [NSString stringWithFormat:@"view%ju", i]; +} + +static NSString *widthMetricName(uintmax_t i) +{ + return [NSString stringWithFormat:@"view%juwidth", i]; +} + +static NSString *heightMetricName(uintmax_t i) +{ + return [NSString stringWithFormat:@"view%juheight", i]; +} + +static int isStretchy(uiBox *b, uintmax_t n) +{ + NSNumber *num; + + num = (NSNumber *) [b->stretchy objectAtIndex:n]; + return [num intValue]; +} + +// TODO do we still need to set hugging? I think we do for stretchy controls... +// TODO try unsetting spinbox intrinsics and seeing what happens +static void relayout(uiBox *b) +{ + NSMutableDictionary *metrics; + NSMutableDictionary *views; + uintmax_t i, n; + BOOL hasStretchy; + uintmax_t firstStretchy; + NSMutableString *constraint; + + if ([b->children count] == 0) + return; + + [b->view removeConstraints:[b->view constraints]]; + + // first collect the views and their fitting sizes (for non-stretchy controls) + // also figure out which is the first stretchy control, if any + metrics = [NSMutableDictionary new]; + views = [NSMutableDictionary new]; + hasStretchy = NO; + n = 0; + while (n < [b->children count]) { + uiControl *child; + NSView *childView; + NSSize fittingSize; + + child = childAt(b, n); + childView = (NSView *) uiControlHandle(child); + [views setObject:childView forKey:viewName(n)]; + fittingSize = fittingAlignmentSize(childView); + [metrics setObject:[NSNumber numberWithDouble:fittingSize.width] + forKey:widthMetricName(n)]; + [metrics setObject:[NSNumber numberWithDouble:fittingSize.height] + forKey:heightMetricName(n)]; + if (!hasStretchy && isStretchy(b, n)) { + hasStretchy = YES; + firstStretchy = n; + } + n++; + } + + // if there are no stretchy controls, we must add the no-stretchy view + // if there are, we must remove it + if (!hasStretchy) { + if ([b->noStretchyView superview] == nil) + [b->view addSubview:b->noStretchyView]; + [views setObject:b->noStretchyView forKey:@"noStretchyView"]; + } else { + if ([b->noStretchyView superview] != nil) + [b->noStretchyView removeFromSuperview]; + } + + // next, assemble the views in the primary direction + // they all go in a straight line + constraint = [NSMutableString new]; + [constraint appendString:b->primaryDirPrefix]; + [constraint appendString:@"|"]; + for (i = 0; i < n; i++) { + if (b->padded && i != 0) + [constraint appendString:@"-"]; + [constraint appendString:@"["]; + [constraint appendString:viewName(i)]; + // implement multiple stretchiness properly + if (isStretchy(b, i) && i != firstStretchy) { + [constraint appendString:@"(=="]; + [constraint appendString:viewName(firstStretchy)]; + [constraint appendString:@")"]; + } + // if the control is not stretchy, restrict it to the fitting size + if (!isStretchy(b, i)) { + [constraint appendString:@"(=="]; + if (b->vertical) + [constraint appendString:heightMetricName(i)]; + else + [constraint appendString:widthMetricName(i)]; + [constraint appendString:@")"]; + } + [constraint appendString:@"]"]; + } + if (!hasStretchy) + // don't space between the last control and the no-stretchy view + [constraint appendString:@"[noStretchyView]"]; + [constraint appendString:@"|"]; + addConstraints(b->view, constraint, metrics, views); + [constraint release]; + + // next: assemble the views in the secondary direction + // each of them will span the secondary direction + for (i = 0; i < n; i++) { + constraint = [NSMutableString new]; + [constraint appendString:b->secondaryDirPrefix]; + [constraint appendString:@"|["]; + [constraint appendString:viewName(i)]; + [constraint appendString:@"]|"]; + addConstraint(b->view, constraint, nil, views); + [constraint release]; + } + if (!hasStretchy) { // and again to the no-stretchy view + constraint = [NSMutableString new]; + [constraint appendString:b->secondaryDirPrefix]; + [constraint appendString:@"|[noStretchyView]|"]; + addConstraint(b->view, constraint, nil, views); + [constraint release]; + } + + [metrics release]; + [views release]; +} + +void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) +{ + NSView *childView; + + childView = (NSView *) uiControlHandle(c); + [b->children addObject:[NSValue valueWithPointer:c]]; + [b->stretchy addObject:[NSNumber numberWithInt:stretchy]]; + + // TODO save the old hugging priorities + // if a control is stretchy, it should not hug in the primary direction + // otherwise, it should *forcibly* hug + if (stretchy) + setHuggingPri(childView, NSLayoutPriorityDefaultLow, b->primaryOrientation); + else + // TODO will default high work? + setHuggingPri(childView NSLayoutPriorityRequired, b->primaryOrientation); + // make sure controls don't hug their secondary direction so they fill the width of the view + setHuggingPri(childView, NSLayoutPriorityDefaultLow, b->secondaryOrientation) + + uiControlSetParent(c, uiControl(b)); + relayout(b); +} + +void uiBoxDelete(uiBox *b, uintmax_t n) +{ + NSValue *v; + uiControl *removed; + + v = (NSValue *) [b->children objectAtIndex:n]; + removed = (uiControl *) [v pointerValue]; + uiControlSetParent(removed, NULL); + [b->children removeObjectAtIndex:n]; + [b->stretchy removeObjectAtIndex:n]; + relayout(b); +} + +int uiBoxPadded(uiBox *b) +{ + return b->padded; +} + +void uiBoxSetPadded(uiBox *ss, int padded) +{ + b->padded = padded; + relayout(b); +} + +static uiBox *finishBox(BOOL vertical) +{ + uiBox *b; + + b = (uiBox *) uiNewControl(uiBoxType()); + + b->view = [[NSView alloc] initWithFrame:NSZeroRect]; + + b->children = [NSMutableArray alloc]; + b->stretchy = [NSMutableArray alloc]; + + b->vertical = vertical; + if (b->vertical) { + b->primaryDirString = @"V:"; + b->secondaryDirString = @"H:"; + b->primaryOrientation = NSLayoutConstraintOrientationVertical; + b->secondaryOrientation = NSLayoutConstraintOrientationHorizontal; + } else { + b->primaryDirString = @"H:"; + b->secondaryDirString = @"V:"; + b->primaryOrientation = NSLayoutConstraintOrientationHorizontal; + b->secondaryOrientation = NSLayoutConstraintOrientationVertical; + } + + b->noStretchyView = [[NSView alloc] initWithFrame:NSZeroRect]; + [b->noStretchyView setTranslatesAutoresizingMaskIntoConstraints:NO]; + setHorzHuggingPri(b->noStretchyView, NSLayoutPriorityDefaultLow); + setVertHuggingPri(b->noStretchyView, NSLayoutPriorityDefaultLow); + + uiDarwinFinishNewControl(b, uiBox); + + return b; +} + +uiBox *uiNewHorizontalBox(void) +{ + return finishNewBox(NO); +} + +uiBox *uiNewVerticalBox(void) +{ + return finishNewBox(YES); +}