801 lines
20 KiB
Mathematica
801 lines
20 KiB
Mathematica
|
// 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 = uiprivMkConstraint(self, NSLayoutAttributeLeading,
|
||
|
NSLayoutRelationEqual,
|
||
|
[self view], NSLayoutAttributeLeading,
|
||
|
1, 0,
|
||
|
@"uiGrid child horizontal alignment start constraint");
|
||
|
[self addConstraint:self.leadingc];
|
||
|
}
|
||
|
if (self.halign == uiAlignCenter) {
|
||
|
self.xcenterc = uiprivMkConstraint(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 = uiprivMkConstraint(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 = uiprivMkConstraint(self, NSLayoutAttributeTop,
|
||
|
NSLayoutRelationEqual,
|
||
|
[self view], NSLayoutAttributeTop,
|
||
|
1, 0,
|
||
|
@"uiGrid child vertical alignment start constraint");
|
||
|
[self addConstraint:self.topc];
|
||
|
}
|
||
|
if (self.valign == uiAlignCenter) {
|
||
|
self.ycenterc = uiprivMkConstraint(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 = uiprivMkConstraint(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 **) uiprivAlloc(ycount * sizeof (int *), "int[][]");
|
||
|
gspan = (BOOL **) uiprivAlloc(ycount * sizeof (BOOL *), "BOOL[][]");
|
||
|
for (y = 0; y < ycount; y++) {
|
||
|
gg[y] = (int *) uiprivAlloc(xcount * sizeof (int), "int[]");
|
||
|
gspan[y] = (BOOL *) uiprivAlloc(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 ***) uiprivAlloc(ycount * sizeof (NSView **), "NSView *[][]");
|
||
|
for (y = 0; y < ycount; y++) {
|
||
|
gv[y] = (NSView **) uiprivAlloc(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 *) uiprivAlloc(xcount * sizeof (BOOL), "BOOL[]");
|
||
|
vexpand = (BOOL *) uiprivAlloc(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 = uiprivMkConstraint(self, NSLayoutAttributeLeading,
|
||
|
NSLayoutRelationEqual,
|
||
|
gv[y][0], NSLayoutAttributeLeading,
|
||
|
1, 0,
|
||
|
@"uiGrid leading edge constraint");
|
||
|
[self addConstraint:c];
|
||
|
[self->edges addObject:c];
|
||
|
c = uiprivMkConstraint(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 = uiprivMkConstraint(self, NSLayoutAttributeTop,
|
||
|
NSLayoutRelationEqual,
|
||
|
gv[0][x], NSLayoutAttributeTop,
|
||
|
1, 0,
|
||
|
@"uiGrid top edge constraint");
|
||
|
[self addConstraint:c];
|
||
|
[self->edges addObject:c];
|
||
|
c = uiprivMkConstraint(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 = uiprivMkConstraint(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 = uiprivMkConstraint(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 = uiprivMkConstraint(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 = uiprivMkConstraint(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
|
||
|
uiprivFree(hexpand);
|
||
|
uiprivFree(vexpand);
|
||
|
for (y = 0; y < ycount; y++) {
|
||
|
uiprivFree(gg[y]);
|
||
|
uiprivFree(gv[y]);
|
||
|
uiprivFree(gspan[y]);
|
||
|
}
|
||
|
uiprivFree(gg);
|
||
|
uiprivFree(gv);
|
||
|
uiprivFree(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)
|
||
|
uiprivUserBug("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)
|
||
|
uiprivUserBug("You cannot have a negative xspan in a uiGrid cell.");
|
||
|
if (yspan < 0)
|
||
|
uiprivUserBug("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)
|
||
|
uiprivUserBug("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;
|
||
|
}
|