--- a/ui/cocoa/container.m Sat Oct 04 14:54:25 2025 +0200 +++ b/ui/cocoa/container.m Sun Oct 19 21:20:08 2025 +0200 @@ -29,82 +29,21 @@ #import "Container.h" #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 -*/ - +#import "TabView.h" /* -------------------- 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); // 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; } @@ -120,17 +59,134 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { GridLayout *grid = [[GridLayout alloc] init]; 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 (__bridge void*)frame; +} + +UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) { + return ui_frame_create(obj, args); // TODO +} + +UIWIDGET ui_scrolledwindow_create(UiObject *obj, UiFrameArgs *args) { + int colspacing = args->spacing; + int rowspacing = args->spacing; + if(args->subcontainer == UI_CONTAINER_GRID) { + colspacing = args->columnspacing; + rowspacing = args->rowspacing; + } + ScrollViewContainer *scrollview = [[ScrollViewContainer alloc]init:args->subcontainer columnSpacing:colspacing rowSpacing:rowspacing]; + scrollview.hasVerticalScroller = YES; + scrollview.scrollerStyle = NSScrollerStyleOverlay; + scrollview.autohidesScrollers = YES; + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, scrollview, &layout); + + UiContainerX *container = ui_create_container(obj, scrollview); + uic_object_push_container(obj, container); + + return (__bridge void*)scrollview; +} + +UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) { + NSView<TabView, Container> *tabview; + switch(args->tabview) { + default: tabview = [[UiTopTabView alloc]init:obj args:args]; break; + } + + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, tabview, &layout); + + UiContainerX *container = ui_create_container(obj, tabview); + uic_object_push_container(obj, container); + + return (__bridge void*)tabview; +} + +void ui_tab_create(UiObject *obj, const char* title) { + UiContainerX *ctn = obj->container_end; + id<TabView> tabview = (__bridge id<TabView>)ctn->container; + NSString *s = title ? [[NSString alloc]initWithUTF8String:title] : @""; + NSView<Container> *sub = [tabview createTab:-1 title:s]; + + UiContainerX *container = ui_create_container(obj, sub); + uic_object_push_container(obj, container); +} + +void ui_tabview_select(UIWIDGET tabview, int tab) { + id<TabView> tabv = (__bridge id<TabView>)tabview; + [tabv selectTab:tab]; +} + +void ui_tabview_remove(UIWIDGET tabview, int tab) { + id<TabView> tabv = (__bridge id<TabView>)tabview; + [tabv removeTab:tab]; +} + +UiObject* ui_tabview_add(UIWIDGET tabview, const char *name, int tab_index) { + id<TabView> tabv = (__bridge id<TabView>)tabview; + NSString *s = name ? [[NSString alloc]initWithUTF8String:name] : @""; + return [tabv addTab:tab_index title:s]; +} + + + + void ui_container_begin_close(UiObject *obj) { UiContainerX *ct = obj->container_end; ct->close = 1; @@ -145,6 +201,94 @@ 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 + + +/* -------------------------- Expander Container -------------------------- */ + +// TODO + + +/* ------------------------ ScrollView Container ------------------------ */ + +@implementation ScrollViewContainer + +@synthesize container = _container; + +- (id)init:(UiSubContainerType)subContainer columnSpacing:(int)columnSpacing rowSpacing:(int)rowSpacing { + self = [super init]; + + if(subContainer != UI_CONTAINER_NO_SUB) { + GridLayout *child; + switch(subContainer) { + default: + case UI_CONTAINER_VBOX: { + child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationVertical spacing:columnSpacing]; + break; + } + case UI_CONTAINER_HBOX: { + child = [[BoxContainer alloc]init:NSUserInterfaceLayoutOrientationHorizontal spacing:columnSpacing]; + break; + } + case UI_CONTAINER_GRID: { + child = [[GridLayout alloc]init]; + child.columnspacing = columnSpacing; + child.rowspacing = rowSpacing; + break; + } + } + child.translatesAutoresizingMaskIntoConstraints = NO; + + self.documentView = child; + [child.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES; + + _child = child; + } + + + return self; +} + +- (void) addView:(NSView*)view layout:(UiLayout*)layout { + if(_child != nil) { + _child.container = self.container; // required, otherwise child has no container and can't access the newline property + view.translatesAutoresizingMaskIntoConstraints = NO; + [_child addView:view layout:layout]; + } else { + self.documentView = view; + [view.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES; + } +} + +@end + /* ------------------------- private functions ------------------------- */ UiContainerX* ui_create_container(UiObject *obj, id<Container> container) { @@ -153,24 +297,20 @@ ctn->close = 0; ctn->prev = NULL; ctn->next = NULL; + container.container = ctn; return ctn; } 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; - [container addView:view]; + 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 layout:&adjustedLayout]; } -/* ---------------------- public layout functions ----------------------- */ - -void ui_newline(UiObject *obj) { - UiContainerX *ctn = obj->container_end; - if(ctn) { - id<Container> container = (__bridge id<Container>)ctn->container; - container.newline = TRUE; - } else { - fprintf(stderr, "Error: obj has no container\n"); - } -}