add frame container (Cocoa)

Tue, 14 Oct 2025 09:45:44 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 14 Oct 2025 09:45:44 +0200
changeset 846
ffa983c223c1
parent 845
f3ab28ed22e5
child 847
50de0f36973f

add frame container (Cocoa)

make/xcode/toolkit/toolkit/main.m file | annotate | diff | comparison | revisions
ui/cocoa/BoxContainer.m file | annotate | diff | comparison | revisions
ui/cocoa/GridLayout.m file | annotate | diff | comparison | revisions
ui/cocoa/MainWindow.m file | annotate | diff | comparison | revisions
ui/cocoa/container.h file | annotate | diff | comparison | revisions
ui/cocoa/container.m file | annotate | diff | comparison | revisions
--- a/make/xcode/toolkit/toolkit/main.m	Mon Oct 13 21:31:58 2025 +0200
+++ b/make/xcode/toolkit/toolkit/main.m	Tue Oct 14 09:45:44 2025 +0200
@@ -144,8 +144,16 @@
         ui_grid(obj, .margin_left = 10, .margin_right = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) {
             ui_button(obj, .label = "left", .hexpand = TRUE, .hfill = TRUE);
             ui_newline(obj);
+            ui_linkbutton(obj, .varname = "link", .hexpand = TRUE, .hfill = TRUE);
+            ui_newline(obj);
             
-            ui_linkbutton(obj, .varname = "link", .hexpand = TRUE, .hfill = TRUE);
+            ui_frame(obj, .label = "Test", .margin = 10, .hfill = TRUE, .vfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .subcontainer = UI_CONTAINER_VBOX, .spacing = 10) {
+                ui_button(obj, .label = "Test Button");
+                ui_button(obj, .label = "Test Button");
+                ui_button(obj, .label = "Test Button");
+                ui_button(obj, .label = "Test Button");
+                ui_button(obj, .label = "Test Button");
+            }
         }
     }
     
--- a/ui/cocoa/BoxContainer.m	Mon Oct 13 21:31:58 2025 +0200
+++ b/ui/cocoa/BoxContainer.m	Tue Oct 14 09:45:44 2025 +0200
@@ -13,17 +13,15 @@
     return self;
 }
 
-- (void) addView:(NSView*)view  margin:(NSEdgeInsets)margin {
-    UiLayout layout = self.uilayout;
+- (void) addView:(NSView*)view  layout:(UiLayout*)layout {
     if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
-        layout.hexpand = TRUE;
-        layout.hfill = TRUE;
+        layout->hexpand = TRUE;
+        layout->hfill = TRUE;
     } else {
-        layout.vexpand = TRUE;
-        layout.vfill = TRUE;
+        layout->vexpand = TRUE;
+        layout->vfill = TRUE;
     }
-    self.uilayout = layout;
-    [super addView:view margin:margin];
+    [super addView:view layout:layout];
     if(_orientation == NSUserInterfaceLayoutOrientationVertical) {
         self.container->newline = TRUE;
     }
--- a/ui/cocoa/GridLayout.m	Mon Oct 13 21:31:58 2025 +0200
+++ b/ui/cocoa/GridLayout.m	Tue Oct 14 09:45:44 2025 +0200
@@ -32,8 +32,7 @@
 
 @implementation GridLayout
 
-@synthesize uilayout=_uilayout;
-@synthesize container=_container;
+@synthesize container = _container;
 
 - (GridLayout*)init {
     self = [super init];
@@ -293,6 +292,7 @@
         } else {
             frame.size.height = elm->preferred_height;
         }
+        frame.size.height -= elm->margin.top + elm->margin.bottom;
         
         frame.origin.x = col->pos + elm->margin.left;
         frame.origin.y = row->pos + elm->margin.top;
@@ -312,7 +312,7 @@
     return self.preferredSize;
 }
 
-- (void) addView:(NSView*)view margin:(NSEdgeInsets)margin {
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
     _preferredSize.width = -1;
     _preferredSize.height = -1;
     
@@ -325,19 +325,19 @@
     GridElm elm;
     elm.x = _x;
     elm.y = _y;
-    elm.margin = margin;
-    elm.colspan = _uilayout.colspan;
-    elm.rowspan = _uilayout.rowspan;
-    if(_uilayout.fill) {
+    elm.margin = NSEdgeInsetsMake(layout->margin_top, layout->margin_left, layout->margin_bottom, layout->margin_right);
+    elm.colspan = layout->colspan;
+    elm.rowspan = layout->rowspan;
+    if(layout->fill) {
         elm.hfill = TRUE;
         elm.vfill = TRUE;
         elm.hexpand = TRUE;
         elm.vexpand = TRUE;
     } else {
-        elm.hfill = _uilayout.hfill;
-        elm.vfill = _uilayout.vfill;
-        elm.hexpand = _uilayout.hexpand;
-        elm.vexpand = _uilayout.vexpand;
+        elm.hfill = layout->hfill;
+        elm.vfill = layout->vfill;
+        elm.hexpand = layout->hexpand;
+        elm.vexpand = layout->vexpand;
     }
     elm.view = view;
     cxListAdd(_children, &elm);
--- a/ui/cocoa/MainWindow.m	Mon Oct 13 21:31:58 2025 +0200
+++ b/ui/cocoa/MainWindow.m	Tue Oct 14 09:45:44 2025 +0200
@@ -129,7 +129,9 @@
             [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor],
             [vbox.bottomAnchor constraintEqualToAnchor:content.bottomAnchor],
         ]];
-        uic_object_push_container(obj, ui_create_container(obj, vbox));
+        UiContainerX *container = ui_create_container(obj, vbox);
+        vbox.container = container;
+        uic_object_push_container(obj, container);
     }
     _topOffset = top;
     
@@ -347,6 +349,7 @@
     
     // create a vertical stackview as default container
     BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+    vbox.container = ui_create_container(obj, vbox);
     //GridLayout *vbox = [[GridLayout alloc] init];
     vbox.translatesAutoresizingMaskIntoConstraints = false;
     [sidebar addSubview:vbox];
@@ -356,7 +359,7 @@
         [vbox.trailingAnchor constraintEqualToAnchor:sidebar.trailingAnchor],
         [vbox.bottomAnchor constraintEqualToAnchor:sidebar.bottomAnchor]
     ]];
-    uic_object_push_container(obj, ui_create_container(obj, vbox));
+    uic_object_push_container(obj, vbox.container);
     
     return NULL;
 }
@@ -365,6 +368,7 @@
     MainWindow *window = (__bridge MainWindow*)obj->wobj;
     BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0];
     //GridLayout *vbox = [[GridLayout alloc] init];
+    vbox.container = ui_create_container(obj, vbox);
     vbox.translatesAutoresizingMaskIntoConstraints = false;
     [panel addSubview:vbox];
     [NSLayoutConstraint activateConstraints:@[
@@ -373,7 +377,7 @@
         [vbox.trailingAnchor constraintEqualToAnchor:panel.trailingAnchor],
         [vbox.bottomAnchor constraintEqualToAnchor:panel.bottomAnchor],
     ]];
-    uic_object_push_container(obj, ui_create_container(obj, vbox));
+    uic_object_push_container(obj, vbox.container);
     return (__bridge void*)vbox;
 }
 
--- a/ui/cocoa/container.h	Mon Oct 13 21:31:58 2025 +0200
+++ b/ui/cocoa/container.h	Tue Oct 14 09:45:44 2025 +0200
@@ -53,10 +53,16 @@
 
 @protocol Container
 
-@property UiLayout uilayout;
 @property UiContainerX *container;
 
-- (void) addView:(NSView*)view margin:(NSEdgeInsets)margin;
+- (void) addView:(NSView*)view layout:(UiLayout*)layout;
+
+@end
+
+
+@interface FrameContainer : NSBox<Container>
+
+- (id)init:(NSString*)title;
 
 @end
 
--- a/ui/cocoa/container.m	Mon Oct 13 21:31:58 2025 +0200
+++ b/ui/cocoa/container.m	Tue Oct 14 09:45:44 2025 +0200
@@ -30,81 +30,20 @@
 #import "GridLayout.h"
 #import "BoxContainer.h"
 
-/* ------------------------- container classes ------------------------- */
-
-/*
-@implementation BoxContainer
-
-@synthesize label=_label;
-@synthesize uilayout=_uilayout;
-@synthesize newline=_newline;
-
-- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing {
-    self = [super init];
-    _label = NULL;
-    _uilayout = (UiLayout){ 0 };
-    _newline = false;
-    
-    self.distribution = NSStackViewDistributionFillProportionally;
-    self.spacing = spacing;
-    
-    self.orientation = orientation;
-    if(orientation == NSUserInterfaceLayoutOrientationHorizontal) {
-        self.alignment = NSLayoutAttributeHeight;
-    } else {
-        self.alignment = NSLayoutAttributeWidth;
-    }
-    
-    
-    return self;
-}
-
-- (void) addView:(NSView*)view {
-    UiBool fill = _uilayout.fill;
-    
-    [self addArrangedSubview:view];
-    
-    if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) {
-        [view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES;
-        if(!fill) {
-            NSSize isize = view.intrinsicContentSize;
-            [view.widthAnchor constraintEqualToConstant:isize.width].active = YES;
-        }
-    } else {
-        [view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES;
-        if(!fill) {
-            NSSize isize = view.intrinsicContentSize;
-            NSRect frame = view.frame;
-            CGFloat height = isize.height > 0 ? isize.height : frame.size.height;
-            if(height == 0) {
-                printf("debug");
-            }
-            if(height > 0) {
-                [view.heightAnchor constraintEqualToConstant:height].active = YES;
-            }
-        }
-    }
-    
-    // at the moment, only the fill layout option needs to be reset
-    _uilayout.fill = UI_DEFAULT;
-}
-
-@end
-*/
-
-
 /* -------------------- public container functions --------------------- */
 
 static UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, NSUserInterfaceLayoutOrientation orientation) {
     BoxContainer *box = [[BoxContainer alloc] init:orientation spacing:args->spacing];
     box.translatesAutoresizingMaskIntoConstraints = false;
+    UiContainerX *container = ui_create_container(obj, box);
+    box.container = container;
     
     // add box to the parent
     UiLayout layout = UI_INIT_LAYOUT(args);
     ui_container_add(obj, box, &layout);
     
     // add new box to the obj container chain
-    uic_object_push_container(obj, ui_create_container(obj, box));
+    uic_object_push_container(obj, container);
     
     return (__bridge void*)box;
 }
@@ -122,17 +61,63 @@
     grid.translatesAutoresizingMaskIntoConstraints = false;
     grid.columnspacing = args->columnspacing;
     grid.rowspacing = args->rowspacing;
+    UiContainerX *container = ui_create_container(obj, grid);
+    grid.container = container;
     
     // add box to the parent
     UiLayout layout = UI_INIT_LAYOUT(args);
     ui_container_add(obj, grid, &layout);
     
     // add new box to the obj container chain
-    uic_object_push_container(obj, ui_create_container(obj, grid));
+    uic_object_push_container(obj, container);
     
     return (__bridge void*)grid;
 }
 
+UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) {
+    NSString *title = args->label ? [[NSString alloc]initWithUTF8String:args->label] : nil;
+    FrameContainer *frame = [[FrameContainer alloc] init:title];    UiLayout layout = UI_ARGS2LAYOUT(args);
+    ui_container_add(obj, frame, &layout);
+    
+    // add container to the chain
+    UiContainerX *container;
+    UiLayout subLayout = {0};
+    switch(args->subcontainer) {
+        default: {
+            // UI_CONTAINER_NO_SUB
+            container = ui_create_container(obj, frame);
+            break;
+        }
+        case UI_CONTAINER_VBOX: {
+            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:args->spacing];
+            box.translatesAutoresizingMaskIntoConstraints = false;
+            [frame addView:box layout:&subLayout];
+            container = ui_create_container(obj, box);
+            break;
+        }
+        case UI_CONTAINER_HBOX: {
+            BoxContainer *box = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:args->spacing];
+            box.translatesAutoresizingMaskIntoConstraints = false;
+            [frame addView:box layout:&subLayout];
+            container = ui_create_container(obj, box);
+            break;
+        }
+        case UI_CONTAINER_GRID: {
+            GridLayout *grid = [[GridLayout alloc] init];
+            grid.translatesAutoresizingMaskIntoConstraints = false;
+            grid.columnspacing = args->columnspacing;
+            grid.rowspacing = args->rowspacing;
+            [frame addView:grid layout:&subLayout];
+            container = ui_create_container(obj, grid);
+            break;
+        }
+    }
+    
+    uic_object_push_container(obj, container);
+    
+    return NULL;
+}
+
 void ui_container_begin_close(UiObject *obj) {
     UiContainerX *ct = obj->container_end;
     ct->close = 1;
@@ -147,6 +132,35 @@
     return 1;
 }
 
+/* -------------------------- Frame Container -------------------------- */
+
+@implementation FrameContainer
+
+@synthesize container = _container;
+
+- (id)init:(NSString*)title {
+    self = [super init];
+    self.title = title;
+    self.boxType = NSBoxPrimary;
+    if(title != nil) {
+        self.titlePosition = NSAtTop;
+    }
+    return self;
+}
+
+- (void) addView:(NSView*)view layout:(UiLayout*)layout {
+    [self.contentView addSubview:view];
+    view.translatesAutoresizingMaskIntoConstraints = NO;
+    [NSLayoutConstraint activateConstraints:@[
+        [view.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0],
+        [view.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:0],
+        [view.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-0],
+        [view.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-0]
+    ]];
+}
+
+@end
+
 /* ------------------------- private functions ------------------------- */
 
 UiContainerX* ui_create_container(UiObject *obj, id<Container> container) {
@@ -162,19 +176,13 @@
 void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout) {
     UiContainerX *ctn = obj->container_end;
     id<Container> container = (__bridge id<Container>)ctn->container;
-    container.uilayout = *layout;
-    NSEdgeInsets margin = {0};
-    if(layout->margin > 0) {
-        margin.left = layout->margin;
-        margin.right = layout->margin;
-        margin.top = layout->margin;
-        margin.bottom = layout->margin;
-    } else {
-        margin.left = layout->margin_left;
-        margin.right = layout->margin_right;
-        margin.top = layout->margin_top;
-        margin.bottom = layout->margin_bottom;
+    UiLayout adjustedLayout = *layout;
+    if(adjustedLayout.margin > 0) {
+        adjustedLayout.margin_left = adjustedLayout.margin;
+        adjustedLayout.margin_right = adjustedLayout.margin;
+        adjustedLayout.margin_top = adjustedLayout.margin;
+        adjustedLayout.margin_bottom = adjustedLayout.margin;
     }
-    [container addView:view margin:margin];
+    [container addView:view layout:layout];
 }
 

mercurial