Thu, 16 Oct 2025 10:48:16 +0200
add tabview (Cocoa)
--- a/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj Wed Oct 15 18:50:52 2025 +0200 +++ b/make/xcode/toolkit/toolkit.xcodeproj/project.pbxproj Thu Oct 16 10:48:16 2025 +0200 @@ -51,6 +51,7 @@ ED6FB03F2E95466F006C6E8E /* wrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = ED6FB03C2E95466F006C6E8E /* wrapper.c */; }; ED83C2BF2E8EA49200054B22 /* BoxContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = ED83C2BE2E8EA49200054B22 /* BoxContainer.m */; }; ED8687E52D999CF3002F3EC2 /* menu.m in Sources */ = {isa = PBXBuildFile; fileRef = ED8687E42D999CF3002F3EC2 /* menu.m */; }; + ED895DAA2EA0CACC00040078 /* TabView.m in Sources */ = {isa = PBXBuildFile; fileRef = ED895DA92EA0CACC00040078 /* TabView.m */; }; ED99F04A2E5CBD2E00A4CC97 /* widget.m in Sources */ = {isa = PBXBuildFile; fileRef = ED99F0492E5CBD2E00A4CC97 /* widget.m */; }; EDB452C32E302C65006FB12D /* image.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB452C22E302C65006FB12D /* image.m */; }; EDC315A92E9A739300403776 /* json.c in Sources */ = {isa = PBXBuildFile; fileRef = EDC315A62E9A739300403776 /* json.c */; }; @@ -172,6 +173,8 @@ ED83C2BE2E8EA49200054B22 /* BoxContainer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BoxContainer.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/BoxContainer.m; sourceTree = "<absolute>"; }; ED8687E32D999CF3002F3EC2 /* menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = menu.h; path = ../../../ui/cocoa/menu.h; sourceTree = SOURCE_ROOT; }; ED8687E42D999CF3002F3EC2 /* menu.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = menu.m; path = ../../../ui/cocoa/menu.m; sourceTree = SOURCE_ROOT; }; + ED895DA82EA0CACC00040078 /* TabView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TabView.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/TabView.h; sourceTree = "<absolute>"; }; + ED895DA92EA0CACC00040078 /* TabView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TabView.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/TabView.m; sourceTree = "<absolute>"; }; ED99F0482E5CBD2E00A4CC97 /* widget.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = widget.h; path = /Users/olaf/Projekte/toolkit/ui/cocoa/widget.h; sourceTree = "<absolute>"; }; ED99F0492E5CBD2E00A4CC97 /* widget.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = widget.m; path = /Users/olaf/Projekte/toolkit/ui/cocoa/widget.m; sourceTree = "<absolute>"; }; ED99F04B2E5CBE5000A4CC97 /* webview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = webview.h; path = /Users/olaf/Projekte/toolkit/ui/ui/webview.h; sourceTree = "<absolute>"; }; @@ -322,6 +325,8 @@ ED65812E2CFF1A7200F5402F /* cocoa */ = { isa = PBXGroup; children = ( + ED895DA82EA0CACC00040078 /* TabView.h */, + ED895DA92EA0CACC00040078 /* TabView.m */, ED83C2BD2E8EA49200054B22 /* BoxContainer.h */, ED83C2BE2E8EA49200054B22 /* BoxContainer.m */, ED18C9212E76CA5500B64EA5 /* entry.h */, @@ -511,6 +516,7 @@ ED6581462CFF3BCE00F5402F /* GridLayout.m in Sources */, ED6581392CFF287300F5402F /* EventData.m in Sources */, ED65812B2CFF1A3000F5402F /* buffer.c in Sources */, + ED895DAA2EA0CACC00040078 /* TabView.m in Sources */, ED65812C2CFF1A3000F5402F /* printf.c in Sources */, ED6580F32CFF19F900F5402F /* object.c in Sources */, ED6580F42CFF19F900F5402F /* toolbar.c in Sources */,
--- a/make/xcode/toolkit/toolkit/main.m Wed Oct 15 18:50:52 2025 +0200 +++ b/make/xcode/toolkit/toolkit/main.m Thu Oct 16 10:48:16 2025 +0200 @@ -158,12 +158,17 @@ } ui_right_panel0(obj) { + ui_tabview(obj, .padding = 20, .spacing = 10, .margin_left = 10, .margin_right = 10, .margin_bottom = 10, .fill = TRUE) { + ui_tab(obj, "Tab 1") { + ui_button(obj, .label = "Test"); + ui_textarea(obj, .fill = TRUE); + } + ui_tab(obj, "Tab 2") { + ui_button(obj, .label = "Tab 2 Content"); + } + } + /* - ui_grid(obj, .margin_left = 10, .margin_right = 10, .fill = TRUE) { - ui_button(obj, .label = "right", .hexpand = TRUE, .hfill = TRUE); - } - */ - ui_scrolledwindow(obj, .margin_left = 10, .margin_right = 10, .fill = TRUE, .subcontainer = UI_CONTAINER_NO_SUB) { ui_vbox(obj, .margin = 0) { for(int i=0;i<50;i++) { @@ -173,6 +178,7 @@ } } } + */ } ui_linkbutton_value_set(doc->link, "unixwork", "https://unixwork.de/");
--- a/ui/cocoa/GridLayout.m Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/cocoa/GridLayout.m Thu Oct 16 10:48:16 2025 +0200 @@ -316,7 +316,7 @@ _preferredSize.width = -1; _preferredSize.height = -1; - if(self.container->newline) { + if(self.container != nil && self.container->newline) { _y++; _x = 0; self.container->newline = FALSE;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/TabView.h Thu Oct 16 10:48:16 2025 +0200 @@ -0,0 +1,55 @@ +/* + * 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" + +@protocol TabView + +- (NSView<Container>*) createTab:(int)index title:(NSString*)title; +- (void) selectTab:(int)index; +- (void) removeTab:(int)index; +- (UiObject*) addTab:(int)index title:(NSString*)title; + +@end + +@interface UiTopTabView : NSTabView<TabView, Container> + +@property UiObject *obj; +@property UiSubContainerType subcontainer; +@property int padding; +@property int spacing; +@property int columnspacing; +@property int rowspacing; +@property ui_callback onchange; +@property void *onchangedata; +@property UiVar *var; + +- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args; + + +@end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/TabView.m Thu Oct 16 10:48:16 2025 +0200 @@ -0,0 +1,115 @@ +/* + * 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 "TabView.h" +#import "BoxContainer.h" +#import "GridLayout.h" + +@implementation UiTopTabView + +@synthesize container = _container; + +- (id)init:(UiObject*)obj args:(UiTabViewArgs*)args { + self = [super init]; + _obj = obj; + _subcontainer = args->subcontainer; + _padding = args->padding; + _spacing = args->spacing; + _columnspacing = args->columnspacing; + _rowspacing = args->rowspacing; + _onchange = args->onchange; + _onchangedata = args->onchangedata; + _var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); + + if(args->tabview == UI_TABVIEW_INVISIBLE || args->tabview == UI_TABVIEW_NAVIGATION_SIDE) { + self.tabViewType = NSNoTabsNoBorder; + } + + return self; +} + +- (void) addView:(NSView*)view layout:(UiLayout*)layout { + // noop +} + +- (NSView<Container>*) createTab:(int)index title:(NSString*)title { + NSTabViewItem *item = [[NSTabViewItem alloc]initWithIdentifier:nil]; + [item setLabel:title]; + if(index < 0) { + [self addTabViewItem:item]; + } else { + [self insertTabViewItem:item atIndex:index]; + } + + BoxContainer *content = [[BoxContainer alloc]init]; + item.view = content; + + GridLayout *sub; + switch(_subcontainer) { + default: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:_spacing]; break; + case UI_CONTAINER_HBOX: sub = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationHorizontal spacing:_spacing]; break; + case UI_CONTAINER_GRID: { + sub = [[GridLayout alloc] init]; + sub.columnspacing = _columnspacing; + sub.rowspacing = _rowspacing; + break; + } + } + UiLayout layout = { + .margin = _padding, + .margin_left = _padding, .margin_right = _padding, .margin_top = _padding, .margin_bottom = _padding, + .fill = TRUE }; + [content addView:sub layout:&layout]; + + return sub; +} + +- (void) selectTab:(int)index { + [self selectTabViewItemAtIndex:index]; +} + +- (void) removeTab:(int)index { + NSTabViewItem *item = [self tabViewItemAtIndex:index]; + if(item != nil) { + [self removeTabViewItem:item]; + } +} + +- (UiObject*) addTab:(int)index title:(NSString*)title { + NSView<Container> *sub = [self createTab:index title:title]; + + UiObject *newobj = uic_object_new_toplevel(); + newobj->widget = (__bridge void*)sub; + + UiContainerX *container = ui_create_container(newobj, sub); + uic_object_push_container(newobj, container); + + return newobj; +} + +@end
--- a/ui/cocoa/container.h Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/cocoa/container.h Thu Oct 16 10:48:16 2025 +0200 @@ -30,8 +30,6 @@ #import "../ui/container.h" -#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) -#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) typedef struct UiLayout UiLayout; @@ -74,6 +72,7 @@ @end + UiContainerX* ui_create_container(UiObject *obj, id<Container> container); void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout);
--- a/ui/cocoa/container.m Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/cocoa/container.m Thu Oct 16 10:48:16 2025 +0200 @@ -29,6 +29,7 @@ #import "Container.h" #import "GridLayout.h" #import "BoxContainer.h" +#import "TabView.h" /* -------------------- public container functions --------------------- */ @@ -36,7 +37,6 @@ 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); @@ -119,6 +119,10 @@ 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; @@ -139,6 +143,50 @@ 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;
--- a/ui/cocoa/toolkit.m Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/cocoa/toolkit.m Thu Oct 16 10:48:16 2025 +0200 @@ -149,12 +149,17 @@ void ui_show(UiObject *obj) { if(obj->wobj) { NSWindow *window = (__bridge NSWindow*)obj->wobj; + + if(!window.isVisible) { + obj->ref++; + } + [window makeKeyAndOrderFront:nil]; } } void ui_close(UiObject *obj) { - + // TODO: unref, window close, ... } /* ------------------- Job Control / Threadpool functions ------------------- */
--- a/ui/cocoa/window.m Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/cocoa/window.m Thu Oct 16 10:48:16 2025 +0200 @@ -38,16 +38,13 @@ #include "../common/context.h" #include "../common/menu.h" #include "../common/toolbar.h" +#include "../common/object.h" #include <cx/mempool.h> static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) { - CxMempool *mp = cxMempoolCreateSimple(256); - UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); - obj->ref = 0; - - obj->ctx = uic_context(obj, mp); + UiObject *obj = uic_object_new_toplevel(); MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar withSplitview:splitview]; [[WindowManager sharedWindowManager] addWindow:window];
--- a/ui/ui/container.h Wed Oct 15 18:50:52 2025 +0200 +++ b/ui/ui/container.h Thu Oct 16 10:48:16 2025 +0200 @@ -365,18 +365,6 @@ UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible); -// box container layout functions -UIEXPORT void ui_layout_fill(UiObject *obj, UiBool fill); -// grid container layout functions -UIEXPORT void ui_layout_hexpand(UiObject *obj, UiBool expand); -UIEXPORT void ui_layout_vexpand(UiObject *obj, UiBool expand); -UIEXPORT void ui_layout_hfill(UiObject *obj, UiBool fill); -UIEXPORT void ui_layout_vfill(UiObject *obj, UiBool fill); -UIEXPORT void ui_layout_override_defaults(UiObject *obj, UiBool d); -UIEXPORT void ui_layout_width(UiObject *obj, int width); -UIEXPORT void ui_layout_height(UiObject* obj, int width); -UIEXPORT void ui_layout_colspan(UiObject *obj, int cols); -UIEXPORT void ui_layout_rowspan(UiObject* obj, int rows); UIEXPORT void ui_newline(UiObject *obj); // TODO