aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/darwin/grid.m
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/libui/darwin/grid.m')
-rw-r--r--src/libui_sdl/libui/darwin/grid.m800
1 files changed, 800 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/darwin/grid.m b/src/libui_sdl/libui/darwin/grid.m
new file mode 100644
index 0000000..d5c5fb1
--- /dev/null
+++ b/src/libui_sdl/libui/darwin/grid.m
@@ -0,0 +1,800 @@
+// 11 june 2016
+#import "uipriv_darwin.h"
+
+// TODO the assorted test doesn't work right at all
+
+@interface gridChild : NSView
+@property uiControl *c;
+@property int left;
+@property int top;
+@property int xspan;
+@property int yspan;
+@property int hexpand;
+@property uiAlign halign;
+@property int vexpand;
+@property uiAlign valign;
+
+@property (strong) NSLayoutConstraint *leadingc;
+@property (strong) NSLayoutConstraint *topc;
+@property (strong) NSLayoutConstraint *trailingc;
+@property (strong) NSLayoutConstraint *bottomc;
+@property (strong) NSLayoutConstraint *xcenterc;
+@property (strong) NSLayoutConstraint *ycenterc;
+
+@property NSLayoutPriority oldHorzHuggingPri;
+@property NSLayoutPriority oldVertHuggingPri;
+- (void)setC:(uiControl *)c grid:(uiGrid *)g;
+- (void)onDestroy;
+- (NSView *)view;
+@end
+
+@interface gridView : NSView {
+ uiGrid *g;
+ NSMutableArray *children;
+ int padded;
+
+ NSMutableArray *edges;
+ NSMutableArray *inBetweens;
+
+ NSMutableArray *emptyCellViews;
+}
+- (id)initWithG:(uiGrid *)gg;
+- (void)onDestroy;
+- (void)removeOurConstraints;
+- (void)syncEnableStates:(int)enabled;
+- (CGFloat)paddingAmount;
+- (void)establishOurConstraints;
+- (void)append:(gridChild *)gc;
+- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at;
+- (int)isPadded;
+- (void)setPadded:(int)p;
+- (BOOL)hugsTrailing;
+- (BOOL)hugsBottom;
+- (int)nhexpand;
+- (int)nvexpand;
+@end
+
+struct uiGrid {
+ uiDarwinControl c;
+ gridView *view;
+};
+
+@implementation gridChild
+
+- (void)setC:(uiControl *)c grid:(uiGrid *)g
+{
+ self.c = c;
+ self.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationHorizontal);
+ self.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationVertical);
+
+ uiControlSetParent(self.c, uiControl(g));
+ uiDarwinControlSetSuperview(uiDarwinControl(self.c), self);
+ uiDarwinControlSyncEnableState(uiDarwinControl(self.c), uiControlEnabledToUser(uiControl(g)));
+
+ if (self.halign == uiAlignStart || self.halign == uiAlignFill) {
+ self.leadingc = mkConstraint(self, NSLayoutAttributeLeading,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeLeading,
+ 1, 0,
+ @"uiGrid child horizontal alignment start constraint");
+ [self addConstraint:self.leadingc];
+ }
+ if (self.halign == uiAlignCenter) {
+ self.xcenterc = mkConstraint(self, NSLayoutAttributeCenterX,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeCenterX,
+ 1, 0,
+ @"uiGrid child horizontal alignment center constraint");
+ [self addConstraint:self.xcenterc];
+ }
+ if (self.halign == uiAlignEnd || self.halign == uiAlignFill) {
+ self.trailingc = mkConstraint(self, NSLayoutAttributeTrailing,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeTrailing,
+ 1, 0,
+ @"uiGrid child horizontal alignment end constraint");
+ [self addConstraint:self.trailingc];
+ }
+
+ if (self.valign == uiAlignStart || self.valign == uiAlignFill) {
+ self.topc = mkConstraint(self, NSLayoutAttributeTop,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeTop,
+ 1, 0,
+ @"uiGrid child vertical alignment start constraint");
+ [self addConstraint:self.topc];
+ }
+ if (self.valign == uiAlignCenter) {
+ self.ycenterc = mkConstraint(self, NSLayoutAttributeCenterY,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeCenterY,
+ 1, 0,
+ @"uiGrid child vertical alignment center constraint");
+ [self addConstraint:self.ycenterc];
+ }
+ if (self.valign == uiAlignEnd || self.valign == uiAlignFill) {
+ self.bottomc = mkConstraint(self, NSLayoutAttributeBottom,
+ NSLayoutRelationEqual,
+ [self view], NSLayoutAttributeBottom,
+ 1, 0,
+ @"uiGrid child vertical alignment end constraint");
+ [self addConstraint:self.bottomc];
+ }
+}
+
+- (void)onDestroy
+{
+ if (self.leadingc != nil) {
+ [self removeConstraint:self.leadingc];
+ self.leadingc = nil;
+ }
+ if (self.topc != nil) {
+ [self removeConstraint:self.topc];
+ self.topc = nil;
+ }
+ if (self.trailingc != nil) {
+ [self removeConstraint:self.trailingc];
+ self.trailingc = nil;
+ }
+ if (self.bottomc != nil) {
+ [self removeConstraint:self.bottomc];
+ self.bottomc = nil;
+ }
+ if (self.xcenterc != nil) {
+ [self removeConstraint:self.xcenterc];
+ self.xcenterc = nil;
+ }
+ if (self.ycenterc != nil) {
+ [self removeConstraint:self.ycenterc];
+ self.ycenterc = nil;
+ }
+
+ uiControlSetParent(self.c, NULL);
+ uiDarwinControlSetSuperview(uiDarwinControl(self.c), nil);
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
+}
+
+- (NSView *)view
+{
+ return (NSView *) uiControlHandle(self.c);
+}
+
+@end
+
+@implementation gridView
+
+- (id)initWithG:(uiGrid *)gg
+{
+ self = [super initWithFrame:NSZeroRect];
+ if (self != nil) {
+ self->g = gg;
+ self->padded = 0;
+ self->children = [NSMutableArray new];
+
+ self->edges = [NSMutableArray new];
+ self->inBetweens = [NSMutableArray new];
+
+ self->emptyCellViews = [NSMutableArray new];
+ }
+ return self;
+}
+
+- (void)onDestroy
+{
+ gridChild *gc;
+
+ [self removeOurConstraints];
+ [self->edges release];
+ [self->inBetweens release];
+
+ [self->emptyCellViews release];
+
+ for (gc in self->children) {
+ [gc onDestroy];
+ uiControlDestroy(gc.c);
+ [gc removeFromSuperview];
+ }
+ [self->children release];
+}
+
+- (void)removeOurConstraints
+{
+ NSView *v;
+
+ if ([self->edges count] != 0) {
+ [self removeConstraints:self->edges];
+ [self->edges removeAllObjects];
+ }
+ if ([self->inBetweens count] != 0) {
+ [self removeConstraints:self->inBetweens];
+ [self->inBetweens removeAllObjects];
+ }
+
+ for (v in self->emptyCellViews)
+ [v removeFromSuperview];
+ [self->emptyCellViews removeAllObjects];
+}
+
+- (void)syncEnableStates:(int)enabled
+{
+ gridChild *gc;
+
+ for (gc in self->children)
+ uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), enabled);
+}
+
+- (CGFloat)paddingAmount
+{
+ if (!self->padded)
+ return 0.0;
+ return uiDarwinPaddingAmount(NULL);
+}
+
+// LONGTERM stop early if all controls are hidden
+- (void)establishOurConstraints
+{
+ gridChild *gc;
+ CGFloat padding;
+ int xmin, ymin;
+ int xmax, ymax;
+ int xcount, ycount;
+ BOOL first;
+ int **gg;
+ NSView ***gv;
+ BOOL **gspan;
+ int x, y;
+ int i;
+ NSLayoutConstraint *c;
+ int firstx, firsty;
+ BOOL *hexpand, *vexpand;
+ BOOL doit;
+ BOOL onlyEmptyAndSpanning;
+
+ [self removeOurConstraints];
+ if ([self->children count] == 0)
+ return;
+ padding = [self paddingAmount];
+
+ // first, figure out the minimum and maximum row and column numbers
+ // ignore hidden controls
+ first = YES;
+ for (gc in self->children) {
+ // this bit is important: it ensures row ymin and column xmin have at least one cell to draw, so the onlyEmptyAndSpanning logic below will never run on those rows
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (first) {
+ xmin = gc.left;
+ ymin = gc.top;
+ xmax = gc.left + gc.xspan;
+ ymax = gc.top + gc.yspan;
+ first = NO;
+ continue;
+ }
+ if (xmin > gc.left)
+ xmin = gc.left;
+ if (ymin > gc.top)
+ ymin = gc.top;
+ if (xmax < (gc.left + gc.xspan))
+ xmax = gc.left + gc.xspan;
+ if (ymax < (gc.top + gc.yspan))
+ ymax = gc.top + gc.yspan;
+ }
+ if (first != NO) // the entire grid is hidden; do nothing
+ return;
+ xcount = xmax - xmin;
+ ycount = ymax - ymin;
+
+ // now build a topological map of the grid gg[y][x]
+ // also figure out which cells contain spanned views so they can be ignored later
+ // treat hidden controls by keeping the indices -1
+ gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]");
+ gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]");
+ for (y = 0; y < ycount; y++) {
+ gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]");
+ gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
+ for (x = 0; x < xcount; x++)
+ gg[y][x] = -1; // empty
+ }
+ for (i = 0; i < [self->children count]; i++) {
+ gc = (gridChild *) [self->children objectAtIndex:i];
+ if (!uiControlVisible(gc.c))
+ continue;
+ for (y = gc.top; y < gc.top + gc.yspan; y++)
+ for (x = gc.left; x < gc.left + gc.xspan; x++) {
+ gg[y - ymin][x - xmin] = i;
+ if (x != gc.left || y != gc.top)
+ gspan[y - ymin][x - xmin] = YES;
+ }
+ }
+
+ // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column
+ for (y = 0; y < ycount; y++) {
+ onlyEmptyAndSpanning = YES;
+ for (x = 0; x < xcount; x++)
+ if (gg[y][x] != -1) {
+ gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
+ if (gc.yspan == 1 || gc.top - ymin == y) {
+ onlyEmptyAndSpanning = NO;
+ break;
+ }
+ }
+ if (onlyEmptyAndSpanning)
+ for (x = 0; x < xcount; x++) {
+ gg[y][x] = gg[y - 1][x];
+ gspan[y][x] = YES;
+ }
+ }
+ for (x = 0; x < xcount; x++) {
+ onlyEmptyAndSpanning = YES;
+ for (y = 0; y < ycount; y++)
+ if (gg[y][x] != -1) {
+ gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
+ if (gc.xspan == 1 || gc.left - xmin == x) {
+ onlyEmptyAndSpanning = NO;
+ break;
+ }
+ }
+ if (onlyEmptyAndSpanning)
+ for (y = 0; y < ycount; y++) {
+ gg[y][x] = gg[y][x - 1];
+ gspan[y][x] = YES;
+ }
+ }
+
+ // now build a topological map of the grid's views gv[y][x]
+ // for any empty cell, create a dummy view
+ gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]");
+ for (y = 0; y < ycount; y++) {
+ gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]");
+ for (x = 0; x < xcount; x++)
+ if (gg[y][x] == -1) {
+ gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect];
+ [gv[y][x] setTranslatesAutoresizingMaskIntoConstraints:NO];
+ [self addSubview:gv[y][x]];
+ [self->emptyCellViews addObject:gv[y][x]];
+ } else {
+ gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
+ gv[y][x] = gc;
+ }
+ }
+
+ // now figure out which rows and columns really expand
+ hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
+ vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]");
+ // first, which don't span
+ for (gc in self->children) {
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (gc.hexpand && gc.xspan == 1)
+ hexpand[gc.left - xmin] = YES;
+ if (gc.vexpand && gc.yspan == 1)
+ vexpand[gc.top - ymin] = YES;
+ }
+ // second, which do span
+ // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand
+ for (gc in self->children) {
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (gc.hexpand && gc.xspan != 1) {
+ doit = YES;
+ for (x = gc.left; x < gc.left + gc.xspan; x++)
+ if (hexpand[x - xmin]) {
+ doit = NO;
+ break;
+ }
+ if (doit)
+ for (x = gc.left; x < gc.left + gc.xspan; x++)
+ hexpand[x - xmin] = YES;
+ }
+ if (gc.vexpand && gc.yspan != 1) {
+ doit = YES;
+ for (y = gc.top; y < gc.top + gc.yspan; y++)
+ if (vexpand[y - ymin]) {
+ doit = NO;
+ break;
+ }
+ if (doit)
+ for (y = gc.top; y < gc.top + gc.yspan; y++)
+ vexpand[y - ymin] = YES;
+ }
+ }
+
+ // now establish all the edge constraints
+ // leading and trailing edges
+ for (y = 0; y < ycount; y++) {
+ c = mkConstraint(self, NSLayoutAttributeLeading,
+ NSLayoutRelationEqual,
+ gv[y][0], NSLayoutAttributeLeading,
+ 1, 0,
+ @"uiGrid leading edge constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ c = mkConstraint(self, NSLayoutAttributeTrailing,
+ NSLayoutRelationEqual,
+ gv[y][xcount - 1], NSLayoutAttributeTrailing,
+ 1, 0,
+ @"uiGrid trailing edge constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ }
+ // top and bottom edges
+ for (x = 0; x < xcount; x++) {
+ c = mkConstraint(self, NSLayoutAttributeTop,
+ NSLayoutRelationEqual,
+ gv[0][x], NSLayoutAttributeTop,
+ 1, 0,
+ @"uiGrid top edge constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ c = mkConstraint(self, NSLayoutAttributeBottom,
+ NSLayoutRelationEqual,
+ gv[ycount - 1][x], NSLayoutAttributeBottom,
+ 1, 0,
+ @"uiGrid bottom edge constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ }
+
+ // now align leading and top edges
+ // do NOT align spanning cells!
+ for (x = 0; x < xcount; x++) {
+ for (y = 0; y < ycount; y++)
+ if (!gspan[y][x])
+ break;
+ firsty = y;
+ for (y++; y < ycount; y++) {
+ if (gspan[y][x])
+ continue;
+ c = mkConstraint(gv[firsty][x], NSLayoutAttributeLeading,
+ NSLayoutRelationEqual,
+ gv[y][x], NSLayoutAttributeLeading,
+ 1, 0,
+ @"uiGrid column leading constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ }
+ }
+ for (y = 0; y < ycount; y++) {
+ for (x = 0; x < xcount; x++)
+ if (!gspan[y][x])
+ break;
+ firstx = x;
+ for (x++; x < xcount; x++) {
+ if (gspan[y][x])
+ continue;
+ c = mkConstraint(gv[y][firstx], NSLayoutAttributeTop,
+ NSLayoutRelationEqual,
+ gv[y][x], NSLayoutAttributeTop,
+ 1, 0,
+ @"uiGrid row top constraint");
+ [self addConstraint:c];
+ [self->edges addObject:c];
+ }
+ }
+
+ // now string adjacent views together
+ for (y = 0; y < ycount; y++)
+ for (x = 1; x < xcount; x++)
+ if (gv[y][x - 1] != gv[y][x]) {
+ c = mkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing,
+ NSLayoutRelationEqual,
+ gv[y][x], NSLayoutAttributeLeading,
+ 1, -padding,
+ @"uiGrid internal horizontal constraint");
+ [self addConstraint:c];
+ [self->inBetweens addObject:c];
+ }
+ for (x = 0; x < xcount; x++)
+ for (y = 1; y < ycount; y++)
+ if (gv[y - 1][x] != gv[y][x]) {
+ c = mkConstraint(gv[y - 1][x], NSLayoutAttributeBottom,
+ NSLayoutRelationEqual,
+ gv[y][x], NSLayoutAttributeTop,
+ 1, -padding,
+ @"uiGrid internal vertical constraint");
+ [self addConstraint:c];
+ [self->inBetweens addObject:c];
+ }
+
+ // now set priorities for all widgets that expand or not
+ // if a cell is in an expanding row, OR If it spans, then it must be willing to stretch
+ // otherwise, it tries not to
+ // note we don't use NSLayoutPriorityRequired as that will cause things to squish when they shouldn't
+ for (gc in self->children) {
+ NSLayoutPriority priority;
+
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (hexpand[gc.left - xmin] || gc.xspan != 1)
+ priority = NSLayoutPriorityDefaultLow;
+ else
+ priority = NSLayoutPriorityDefaultHigh;
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal);
+ // same for vertical direction
+ if (vexpand[gc.top - ymin] || gc.yspan != 1)
+ priority = NSLayoutPriorityDefaultLow;
+ else
+ priority = NSLayoutPriorityDefaultHigh;
+ uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical);
+ }
+
+ // TODO make all expanding rows/columns the same height/width
+
+ // and finally clean up
+ uiFree(hexpand);
+ uiFree(vexpand);
+ for (y = 0; y < ycount; y++) {
+ uiFree(gg[y]);
+ uiFree(gv[y]);
+ uiFree(gspan[y]);
+ }
+ uiFree(gg);
+ uiFree(gv);
+ uiFree(gspan);
+}
+
+- (void)append:(gridChild *)gc
+{
+ BOOL update;
+ int oldnh, oldnv;
+
+ [gc setTranslatesAutoresizingMaskIntoConstraints:NO];
+ [self addSubview:gc];
+
+ // no need to set priority here; that's done in establishOurConstraints
+
+ oldnh = [self nhexpand];
+ oldnv = [self nvexpand];
+ [self->children addObject:gc];
+
+ [self establishOurConstraints];
+ update = NO;
+ if (gc.hexpand)
+ if (oldnh == 0)
+ update = YES;
+ if (gc.vexpand)
+ if (oldnv == 0)
+ update = YES;
+ if (update)
+ uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->g));
+
+ [gc release]; // we don't need the initial reference now
+}
+
+- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at
+{
+ gridChild *other;
+ BOOL found;
+
+ found = NO;
+ for (other in self->children)
+ if (other.c == c) {
+ found = YES;
+ break;
+ }
+ if (!found)
+ userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g);
+
+ switch (at) {
+ case uiAtLeading:
+ gc.left = other.left - gc.xspan;
+ gc.top = other.top;
+ break;
+ case uiAtTop:
+ gc.left = other.left;
+ gc.top = other.top - gc.yspan;
+ break;
+ case uiAtTrailing:
+ gc.left = other.left + other.xspan;
+ gc.top = other.top;
+ break;
+ case uiAtBottom:
+ gc.left = other.left;
+ gc.top = other.top + other.yspan;
+ break;
+ // TODO add error checks to ALL enums
+ }
+
+ [self append:gc];
+}
+
+- (int)isPadded
+{
+ return self->padded;
+}
+
+- (void)setPadded:(int)p
+{
+ CGFloat padding;
+ NSLayoutConstraint *c;
+
+#if 0 /* TODO */
+dispatch_after(
+dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC),
+dispatch_get_main_queue(),
+^{ [[self window] visualizeConstraints:[self constraints]]; }
+);
+#endif
+ self->padded = p;
+ padding = [self paddingAmount];
+ for (c in self->inBetweens)
+ switch ([c firstAttribute]) {
+ case NSLayoutAttributeLeading:
+ case NSLayoutAttributeTop:
+ [c setConstant:padding];
+ break;
+ case NSLayoutAttributeTrailing:
+ case NSLayoutAttributeBottom:
+ [c setConstant:-padding];
+ break;
+ }
+}
+
+- (BOOL)hugsTrailing
+{
+ // only hug if we have horizontally expanding
+ return [self nhexpand] != 0;
+}
+
+- (BOOL)hugsBottom
+{
+ // only hug if we have vertically expanding
+ return [self nvexpand] != 0;
+}
+
+- (int)nhexpand
+{
+ gridChild *gc;
+ int n;
+
+ n = 0;
+ for (gc in self->children) {
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (gc.hexpand)
+ n++;
+ }
+ return n;
+}
+
+- (int)nvexpand
+{
+ gridChild *gc;
+ int n;
+
+ n = 0;
+ for (gc in self->children) {
+ if (!uiControlVisible(gc.c))
+ continue;
+ if (gc.vexpand)
+ n++;
+ }
+ return n;
+}
+
+@end
+
+static void uiGridDestroy(uiControl *c)
+{
+ uiGrid *g = uiGrid(c);
+
+ [g->view onDestroy];
+ [g->view release];
+ uiFreeControl(uiControl(g));
+}
+
+uiDarwinControlDefaultHandle(uiGrid, view)
+uiDarwinControlDefaultParent(uiGrid, view)
+uiDarwinControlDefaultSetParent(uiGrid, view)
+uiDarwinControlDefaultToplevel(uiGrid, view)
+uiDarwinControlDefaultVisible(uiGrid, view)
+uiDarwinControlDefaultShow(uiGrid, view)
+uiDarwinControlDefaultHide(uiGrid, view)
+uiDarwinControlDefaultEnabled(uiGrid, view)
+uiDarwinControlDefaultEnable(uiGrid, view)
+uiDarwinControlDefaultDisable(uiGrid, view)
+
+static void uiGridSyncEnableState(uiDarwinControl *c, int enabled)
+{
+ uiGrid *g = uiGrid(c);
+
+ if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled))
+ return;
+ [g->view syncEnableStates:enabled];
+}
+
+uiDarwinControlDefaultSetSuperview(uiGrid, view)
+
+static BOOL uiGridHugsTrailingEdge(uiDarwinControl *c)
+{
+ uiGrid *g = uiGrid(c);
+
+ return [g->view hugsTrailing];
+}
+
+static BOOL uiGridHugsBottom(uiDarwinControl *c)
+{
+ uiGrid *g = uiGrid(c);
+
+ return [g->view hugsBottom];
+}
+
+static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c)
+{
+ uiGrid *g = uiGrid(c);
+
+ [g->view establishOurConstraints];
+}
+
+uiDarwinControlDefaultHuggingPriority(uiGrid, view)
+uiDarwinControlDefaultSetHuggingPriority(uiGrid, view)
+
+static void uiGridChildVisibilityChanged(uiDarwinControl *c)
+{
+ uiGrid *g = uiGrid(c);
+
+ [g->view establishOurConstraints];
+}
+
+static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign, uiGrid *g)
+{
+ gridChild *gc;
+
+ if (xspan < 0)
+ userbug("You cannot have a negative xspan in a uiGrid cell.");
+ if (yspan < 0)
+ userbug("You cannot have a negative yspan in a uiGrid cell.");
+ gc = [gridChild new];
+ gc.xspan = xspan;
+ gc.yspan = yspan;
+ gc.hexpand = hexpand;
+ gc.halign = halign;
+ gc.vexpand = vexpand;
+ gc.valign = valign;
+ [gc setC:c grid:g];
+ return gc;
+}
+
+void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
+{
+ gridChild *gc;
+
+ // 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 uiGrid.");
+ gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
+ gc.left = left;
+ gc.top = top;
+ [g->view append:gc];
+}
+
+void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
+{
+ gridChild *gc;
+
+ gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
+ [g->view insert:gc after:existing at:at];
+}
+
+int uiGridPadded(uiGrid *g)
+{
+ return [g->view isPadded];
+}
+
+void uiGridSetPadded(uiGrid *g, int padded)
+{
+ [g->view setPadded:padded];
+}
+
+uiGrid *uiNewGrid(void)
+{
+ uiGrid *g;
+
+ uiDarwinNewControl(uiGrid, g);
+
+ g->view = [[gridView alloc] initWithG:g];
+
+ return g;
+}