Added uiGrid on OS X. Now to fix build errors.
This commit is contained in:
parent
61a94fde57
commit
d528fae1f4
|
@ -1,9 +0,0 @@
|
|||
func (g *grid) preferredSize(d *sizing) (width, height int) {}
|
||||
|
||||
func (g *grid) nTabStops() int {
|
||||
n := 0
|
||||
for _, c := range g.controls {
|
||||
n += c.control.nTabStops()
|
||||
}
|
||||
return n
|
||||
}
|
|
@ -19,6 +19,7 @@ list(APPEND _LIBUI_SOURCES
|
|||
darwin/entry.m
|
||||
darwin/fontbutton.m
|
||||
darwin/form.m
|
||||
darwin/grid.m
|
||||
darwin/group.m
|
||||
darwin/label.m
|
||||
darwin/main.m
|
||||
|
|
|
@ -333,6 +333,7 @@ struct uiForm {
|
|||
[self addSubview:fc];
|
||||
|
||||
uiControlSetParent(fc.c, uiControl(self->f));
|
||||
// TODO fix this it's wrong
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f)));
|
||||
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
// 11 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO wrap the child in a view if its align isn't fill
|
||||
// maybe it's easier to do it regardless of align
|
||||
@interface gridChild
|
||||
@property uiControl *c;
|
||||
@property intmax_t left;
|
||||
@property intmax_t top;
|
||||
@property intmax_t hspan;
|
||||
@property intmax_t vspan;
|
||||
@property int hexpand;
|
||||
@property uiAlign halign;
|
||||
@property int vexpand;
|
||||
@property uiAlign valign;
|
||||
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface gridView : NSView {
|
||||
uiGrid *g;
|
||||
NSMutableArray *children;
|
||||
int padded;
|
||||
uintmax_t nhexpand;
|
||||
uintmax_t nvexpand;
|
||||
|
||||
NSMutableArray *edges;
|
||||
NSMutableArray *inBetweens;
|
||||
}
|
||||
- (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;
|
||||
@end
|
||||
|
||||
struct uiGrid {
|
||||
uiDarwinControl c;
|
||||
gridView *view;
|
||||
};
|
||||
|
||||
@implementation formChild
|
||||
|
||||
- (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->nStretchy = 0;
|
||||
|
||||
self->edges = [NSMutableArray new];
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->edges release];
|
||||
[self->inBetweens release];
|
||||
|
||||
for (gc in self->children) {
|
||||
uiControlSetParent(gc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(gc.c), nil);
|
||||
uiControlDestroy(gc.c);
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
if ([self->edges count] != 0) {
|
||||
[self removeConstraints:self->edges];
|
||||
[self->edges removeAllObjects];
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens 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);
|
||||
}
|
||||
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
gridChild *gc;
|
||||
CGFloat padding;
|
||||
intmax_t xmin, ymin;
|
||||
intmax_t xmax, ymax;
|
||||
intmax_t xcount, ycount;
|
||||
BOOL first;
|
||||
int **gg;
|
||||
intmax_t x, y;
|
||||
int i;
|
||||
NSMutableSet *set;
|
||||
NSNumber *number;
|
||||
NSLayoutConstraint *c;
|
||||
NSView **colviews, **rowviews;
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first, figure out the minimum and maximum row and column numbers
|
||||
first = YES;
|
||||
for (gc in self->children) {
|
||||
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;
|
||||
}
|
||||
xcount = xmax - xmin;
|
||||
ycount = ymax - ymin;
|
||||
|
||||
// now build a topological map of the grid gg[y][x]
|
||||
gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]");
|
||||
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];
|
||||
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;
|
||||
}
|
||||
|
||||
// now establish all the edge constraints
|
||||
// leading edge
|
||||
set = [NSMutableSet new];
|
||||
for (y = 0; y < ycount; y++)
|
||||
[set addObject:[NSNumber numberWithInt:gg[y][0]]];
|
||||
for (number in set)
|
||||
if ([number intValue] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:[number intValue]];
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
[gc view], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid leading edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
// top
|
||||
[set removeAllObjects];
|
||||
for (x = 0; x < xcount; x++)
|
||||
[set addObject:[NSNumber numberWithInt:gg[0][x]]];
|
||||
for (number in set)
|
||||
if ([number intValue] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:[number intValue]];
|
||||
c = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
[gc view], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid top edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
// trailing edge
|
||||
[set removeAllObjects];
|
||||
for (y = 0; y < ycount; y++)
|
||||
[set addObject:[NSNumber numberWithInt:gg[y][xcount - 1]]];
|
||||
for (number in set)
|
||||
if ([number intValue] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:[number intValue]];
|
||||
c = mkConstraint(self, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
[gc view], NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiGrid trailing edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
// bottom
|
||||
[set removeAllObjects];
|
||||
for (x = 0; x < xcount; x++)
|
||||
[set addObject:[NSNumber numberWithInt:gg[ycount - 1][x]]];
|
||||
for (number in set)
|
||||
if ([number intValue] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:[number intValue]];
|
||||
c = mkConstraint(self, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
[gc view], NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiGrid bottom edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
[set release];
|
||||
|
||||
// now go through every row and column and extract SOME view from that row and column for the inner constraints
|
||||
// if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows)
|
||||
// note that the edges cannot be empty because we built a smallest fitting rectangle way back in step 1
|
||||
colviews = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]");
|
||||
for (x = 0; x < xcount; x++) {
|
||||
for (y = 0; y < ycount; y++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
colviews[x] = [gc view];
|
||||
break;
|
||||
}
|
||||
if (colviews[x] == nil)
|
||||
colviews[x] = colviews[x - 1];
|
||||
}
|
||||
rowviews = (NSView **) uiAlloc(ycount * sizeof (NSView *), "NSView *[]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
rowviews[y] = [gc view];
|
||||
break;
|
||||
}
|
||||
if (rowviews[y] == nil)
|
||||
rowviews[y] = colviews[y - 1];
|
||||
}
|
||||
|
||||
// now string all the views together
|
||||
for (gc in self->children) {
|
||||
if (gc.left != xmin) {
|
||||
c = mkConstraint([gc view], NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
colviews[(gc.left - 1) - xmin], NSLayoutAttributeTrailing,
|
||||
1, padding,
|
||||
@"uiGrid leading constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
if (gc.top != ymin) {
|
||||
c = mkConstraint([gc view], NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
rowviews[(gc.top - 1) - ymin], NSLayoutAttributeBottom,
|
||||
1, padding,
|
||||
@"uiGrid top constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
if ((gc.left + gc.xspan) != xmax) {
|
||||
c = mkConstraint([gc view], NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
colviews[(gc.left + gc.xspan) - xmin], NSLayoutAttributeLeading,
|
||||
1, -padding,
|
||||
@"uiGrid trailing constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
if ((gc.top + gc.yspan) != ymax) {
|
||||
c = mkConstraint([gc view], NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
rowviews[(gc.top + gc.yspan) - ymin], NSLayoutAttributeTop,
|
||||
1, -padding,
|
||||
@"uiGrid bottom constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO make all expanding rows/columns the same height/width
|
||||
|
||||
// and finally clean up
|
||||
uiFree(colviews);
|
||||
uiFree(rowviews);
|
||||
for (y = 0; y < ycount; y++)
|
||||
uiFree(gg[y]);
|
||||
uiFree(gg);
|
||||
}
|
||||
|
||||
- (void)append:(gridChild *)gc
|
||||
{
|
||||
NSLayoutPriority priority;
|
||||
BOOL update;
|
||||
intmax_t oldn;
|
||||
|
||||
uiControlSetParent(gc.c, uiControl(self->g));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), uiControlEnabledToUser(uiControl(self->g)));
|
||||
|
||||
// if a control expands horizontally, it should not hug horizontally
|
||||
// otherwise, it should *forcibly* hug
|
||||
if (gc.hexpand)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal);
|
||||
// same for vertical direction
|
||||
if (gc.vexpand)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
[self->children addObject:gc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
update = NO;
|
||||
if (fc.hexpand) {
|
||||
oldn = self->nhexpand;
|
||||
self->nhexpand++;
|
||||
if (oldn == 0)
|
||||
update = YES;
|
||||
}
|
||||
if (fc.vexpand) {
|
||||
oldn = self->nvexpand;
|
||||
self->nvexpand++;
|
||||
if (oldn == 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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 gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
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.c = c;
|
||||
gc.xspan = xspan;
|
||||
gc.yspan = yspan;
|
||||
gc.hexpand = hexpand;
|
||||
gc.halign = halign;
|
||||
gc.vexpand = vexpand;
|
||||
gc.valign = valign;
|
||||
gc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationHorizontal);
|
||||
gc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationVertical);
|
||||
return gc;
|
||||
}
|
||||
|
||||
void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t 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);
|
||||
gc.left = left;
|
||||
gc.top = top;
|
||||
[g->view append:gc];
|
||||
}
|
||||
|
||||
void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign);
|
||||
[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 *uiNewForm(void)
|
||||
{
|
||||
uiGrid *g;
|
||||
|
||||
uiDarwinNewControl(uiGrid, g);
|
||||
|
||||
g->view = [[gridView alloc] initWithG:g];
|
||||
|
||||
return g;
|
||||
}
|
Loading…
Reference in New Issue