Sun, 19 Oct 2025 21:20:08 +0200
update toolkit
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #import "Container.h" #import "GridLayout.h" #import "BoxContainer.h" #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, container); return (__bridge void*)box; } UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs *args) { return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationVertical); } UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs *args) { return ui_box_create(obj, args, NSUserInterfaceLayoutOrientationHorizontal); } 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, 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; } int ui_container_finish(UiObject *obj) { UiContainerX *ct = obj->container_end; if(ct->close) { ui_end_new(obj); return 0; } 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) { UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX)); ctn->container = (__bridge void*)container; 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; 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]; }