# HG changeset patch # User Olaf Wintermann # Date 1734030103 -3600 # Node ID d2bd73d28ff1d56f072470b93beff2f0e2dbca84 # Parent b9767cb5b06b6303c236e60467e9b40082e62470 update toolkit diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/EventData.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/EventData.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,43 @@ +/* + * 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 "../ui/toolkit.h" +#import "../common/context.h" + +@interface EventData : NSObject +@property UiObject *obj; +@property ui_callback callback; +@property void *userdata; +@property void *data; +@property int value; + +- (EventData*)init:(ui_callback)cb userdata:(void*)userdata; + +- (void)handleEvent:(id)sender; + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/EventData.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/EventData.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,52 @@ +/* + * 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 "EventData.h" + +@implementation EventData + +- (EventData*)init:(ui_callback)cb userdata:(void*)userdata { + _callback = cb; + _userdata = userdata; + return self; +} + +- (void)handleEvent:(id)sender { + if(self.callback) { + UiEvent event; + event.obj = self.obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = self.data; + event.intval = self.value; + self.callback(&event, self.userdata); + } +} + + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/GridLayout.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/GridLayout.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,71 @@ +/* + * 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 "toolkit.h" + +#import "Container.h" + +#import + +typedef struct GridElm { + NSView *view; + int margin; + int x; + int y; + int colspan; + int rowspan; + BOOL hexpand; + BOOL vexpand; + BOOL hfill; + BOOL vfill; +} GridElm; + +typedef struct GridDef { + int size; + int pos; + int preferred_size; + BOOL extend; +} GridDef; + +@interface GridLayout : NSView + +@property int columnspacing; +@property int rowspacing; +@property CxList *children; +@property NSSize preferredSize; + +@property NSButton *test; + +@property int x; +@property int y; +@property int cols; +@property int rows; + +- (GridLayout*)init; + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/GridLayout.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/GridLayout.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,214 @@ +/* + * 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 "GridLayout.h" + + + +@implementation GridLayout + +@synthesize label=_label; +@synthesize uilayout=_uilayout; +@synthesize newline=_newline; + +- (GridLayout*)init { + self = [super init]; + _columnspacing = 0; + _rowspacing = 0; + _children = cxArrayListCreateSimple(sizeof(GridElm), 32); + + return self; +} + +/* +- (void) layout { + [super layout]; + + NSRect r1 = _test.frame; + NSSize s1 = _test.intrinsicContentSize; + NSEdgeInsets e1 = _test.alignmentRectInsets; + + printf("fuck\n"); +} + */ + + +- (void) layout { + int ncols = _cols+1; + int nrows = _rows+1; + + GridDef *coldef = calloc(ncols, sizeof(GridDef)); + GridDef *rowdef = calloc(nrows, sizeof(GridDef)); + + NSRect viewFrame = self.frame; + + int colspacing = _columnspacing; + int rowspacing = _rowspacing; + + CxIterator i = cxListIterator(_children); + cx_foreach(GridElm *, elm, i) { + NSSize size = elm->view.intrinsicContentSize; + NSEdgeInsets alignment = elm->view.alignmentRectInsets; + if(size.width != NSViewNoIntrinsicMetric) { + CGFloat width = size.width + alignment.left + alignment.right; + if(width > coldef[elm->x].preferred_size) { + coldef[elm->x].preferred_size = width; + } + } + if(size.height != NSViewNoIntrinsicMetric) { + CGFloat height = size.height + alignment.top + alignment.right; + //CGFloat height = size.height; + if(height > rowdef[elm->y].preferred_size) { + rowdef[elm->y].preferred_size = height; + } + } + + if(elm->hexpand) { + coldef[elm->x].extend = TRUE; + } + if(elm->vexpand) { + rowdef[elm->y].extend = TRUE; + } + } + + int col_ext = 0; + int row_ext = 0; + + int preferred_width = 0; + int preferred_height = 0; + for(int j=0;jextend) { + col->size = col->preferred_size + hext; + } else { + col->size = col->preferred_size; + } + } + for(int j=0;jextend) { + row->size = row->preferred_size + vext; + } else { + row->size = row->preferred_size; + } + } + + int pos = 0; + for(int j=0;jview.intrinsicContentSize; + GridDef *col = &coldef[elm->x]; + GridDef *row = &rowdef[elm->y]; + + NSEdgeInsets alignment = elm->view.alignmentRectInsets; + NSRect frame; + frame.size.width = col->size; + frame.size.height = row->size; + frame.origin.x = col->pos - (alignment.left+alignment.right)/2; + frame.origin.y = viewFrame.size.height - row->pos - frame.size.height + ((alignment.top+alignment.right)/2); + elm->view.frame = frame; + } + + free(coldef); + free(rowdef); +} + + +- (NSSize)intrinsicContentSize { + return self.preferredSize; +} + +- (void) addView:(NSView*)view fill:(BOOL)fill { + if(_newline) { + _y++; + _x = 0; + _newline = FALSE; + } + + GridElm elm; + elm.x = _x; + elm.y = _y; + elm.margin = 0; + elm.colspan = _uilayout.colspan; + elm.rowspan = _uilayout.rowspan; + elm.hfill = _uilayout.hfill; + elm.vfill = _uilayout.vfill; + elm.hexpand = _uilayout.hexpand; + elm.vexpand = _uilayout.vexpand; + elm.view = view; + cxListAdd(_children, &elm); + + [self addSubview:view]; + self.needsLayout = YES; + + if(_x > _cols) { + _cols = _x; + } + if(_y > _rows) { + _rows = _y; + } + _x++; +} + +- (void) dealloc { + cxListDestroy(_children); +} + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/MainWindow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/MainWindow.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,38 @@ +/* + * 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 "toolkit.h" +#import "../ui/window.h" + +@interface MainWindow : NSWindow + +@property UiObject *uiobj; + +- (MainWindow*)init:(UiObject*)obj; + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/MainWindow.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/MainWindow.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,65 @@ +/* + * 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 "MainWindow.h" +#import "Container.h" +#import "GridLayout.h" +#import "../common/object.h" + +@implementation MainWindow + +- (MainWindow*)init:(UiObject*)obj { + self.uiobj = obj; + NSRect frame = NSMakeRect(300, 200, 600, 500); + + self = [self initWithContentRect:frame + styleMask:NSWindowStyleMaskTitled | + NSWindowStyleMaskResizable | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable + backing:NSBackingStoreBuffered + defer:false]; + + // create a vertical stackview as default container + //BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; + GridLayout *vbox = [[GridLayout alloc] init]; + vbox.translatesAutoresizingMaskIntoConstraints = false; + [self.contentView addSubview:vbox]; + [NSLayoutConstraint activateConstraints:@[ + [vbox.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:4], + [vbox.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor], + [vbox.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor], + [vbox.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor] + ]]; + + uic_object_push_container(obj, ui_create_container(obj, vbox)); + + return self; +} + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/Makefile --- a/ui/cocoa/Makefile Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/Makefile Thu Dec 12 20:01:43 2024 +0100 @@ -27,7 +27,7 @@ # $(COCOA_OBJPRE)%.o: cocoa/%.m - $(CC) -o $@ -c $(CFLAGS) $< + $(CC) -o $@ -c -I../ucx -fobjc-arc $(CFLAGS) $(TK_CFLAGS) $< $(UI_LIB): $(OBJ) $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ) diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/UiJob.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/UiJob.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,30 @@ +/* + * 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 "toolkit.h" + diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/UiJob.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/UiJob.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,29 @@ +/* + * 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 "UiJob.h" diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/WindowManager.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/WindowManager.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,41 @@ +/* + * 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 "toolkit.h" + +@interface WindowManager : NSObject + +@property NSMutableArray *windows; + ++ (WindowManager*) sharedWindowManager; + +- (WindowManager*)init; + +- (void)addWindow:(NSWindow*)win; + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/WindowManager.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/WindowManager.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,57 @@ +/* + * 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 "WindowManager.h" + +@implementation WindowManager + +static WindowManager *instance = nil; + ++ (WindowManager*) sharedWindowManager { + if(instance == nil) { + instance = [[WindowManager alloc] init]; + } + return instance; +} + +- (WindowManager*)init { + _windows = [[NSMutableArray alloc] initWithCapacity:16]; + return self; +} + +- (void)addWindow:(NSWindow*)win { + [_windows addObject:win]; + [win setDelegate:self]; +} + +- (void) windowWillClose:(NSNotification *) notification { + NSWindow *window = notification.object; + [_windows removeObject:window]; +} + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/appdelegate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/appdelegate.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,37 @@ +/* + * 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. + */ + + +#include "../ui/toolkit.h" +#include "../common/context.h" +#include "../common/object.h" + +@interface AppDelegate : NSObject + + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/appdelegate.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/appdelegate.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,49 @@ +/* + * 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 "AppDelegate.h" + +#import "toolkit.h" + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + ui_cocoa_onstartup(); +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + ui_cocoa_onexit(); +} + + +- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app { + return YES; +} + + +@end diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/button.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/button.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,31 @@ +/* + * 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 "toolkit.h" + +#import "../ui/button.h" diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/button.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/button.m Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,53 @@ +/* + * 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 "button.h" +#import "EventData.h" +#import "Container.h" +#import + +UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { + NSButton *button = [[NSButton alloc] init]; + if(args.label) { + NSString *label = [[NSString alloc] initWithUTF8String:args.label]; + button.title = label; + } + + if(args.onclick) { + EventData *event = [[EventData alloc] init:args.onclick userdata:args.onclickdata]; + event.obj = obj; + button.target = event; + button.action = @selector(handleEvent:); + objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, button, &layout, FALSE); + + return (__bridge void*)button; +} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/container.h --- a/ui/cocoa/container.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/container.h Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -26,19 +26,65 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import "../ui/toolkit.h" #import "toolkit.h" -typedef void(*ui_container_add_f)(UiContainer*, NSView*); +#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; +typedef enum UiLayoutBool UiLayoutBool; -struct UiContainer { - NSView* widget; - void (*add)(UiContainer*, NSView*); - NSRect (*getframe)(UiContainer*); +enum UiLayoutBool { + UI_LAYOUT_UNDEFINED = 0, + UI_LAYOUT_TRUE, + UI_LAYOUT_FALSE, +}; + +struct UiLayout { + UiTri fill; + //UiBool newline; + //char *label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + //int width; + int colspan; + int rowspan; }; -UiContainer* ui_window_container(UiObject *obj, NSWindow *window); +#define UI_INIT_LAYOUT(args) (UiLayout) {\ + .fill = args.fill, \ + .hexpand = args.hexpand, \ + .vexpand = args.vexpand, \ + .hfill = args.hfill, \ + .vfill = args.vfill, \ + .colspan = args.colspan, \ + .rowspan = args.rowspan } + + +@protocol Container + +@property UiLayout uilayout; +@property const char *label; +@property UiBool newline; -NSRect ui_container_getframe(UiContainer *ct); -void ui_container_add(UiContainer *ct, NSView *view); +- (void) addView:(NSView*)view fill:(BOOL)fill; + +@end + +@interface BoxContainer : NSStackView + +- (BoxContainer*)init:(NSUserInterfaceLayoutOrientation)orientation spacing:(int)spacing; +@end + + + + + +UiContainerX* ui_create_container(UiObject *obj, id container); + +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill); diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/container.m --- a/ui/cocoa/container.m Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/container.m Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -26,81 +26,143 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import -#import +#import "Container.h" +#import "GridLayout.h" + +/* ------------------------- container classes ------------------------- */ -#import "container.h" +@implementation BoxContainer +@synthesize label=_label; +@synthesize uilayout=_uilayout; +@synthesize newline=_newline; -UiContainer* ui_window_container(UiObject *obj, NSWindow *window) { - UiContainer *ct = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct->widget = [window contentView]; - ct->add = ui_container_add; - ct->getframe = ui_container_getframe; - return ct; +- (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; } -UIWIDGET ui_sidebar(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - NSRect frame = ct->getframe(ct); +- (void) addView:(NSView*)view fill:(BOOL)fill { + if(_uilayout.fill != UI_LAYOUT_UNDEFINED) { + fill = ui_lb2bool(_uilayout.fill); + } - // create and add views - NSSplitView *splitview = [[NSSplitView alloc] initWithFrame:frame]; - [splitview setVertical:YES]; - [splitview setDividerStyle:NSSplitViewDividerStyleThin]; - ct->add(ct, splitview); + [self addArrangedSubview:view]; - NSRect lframe; - lframe.origin.x = 0; - lframe.origin.y = 0; - lframe.size.width = 200; - lframe.size.height = frame.size.height; - - NSRect rframe; - rframe.origin.x = 0; - rframe.origin.y = 0; - rframe.size.width = frame.size.width - 201; - rframe.size.height = frame.size.height; - - NSView *sidebar = [[NSView alloc]initWithFrame:lframe]; - NSView *contentarea = [[NSView alloc]initWithFrame:rframe]; + if(self.orientation == NSUserInterfaceLayoutOrientationHorizontal) { + [view.heightAnchor constraintEqualToAnchor:self.heightAnchor].active = YES; + if(!fill) { + [view.widthAnchor constraintEqualToConstant:view.intrinsicContentSize.width].active = YES; + } + } else { + [view.widthAnchor constraintEqualToAnchor:self.widthAnchor].active = YES; + if(!fill) { + [view.heightAnchor constraintEqualToConstant:view.intrinsicContentSize.height].active = YES; + } + } - [splitview addSubview:sidebar]; - [splitview addSubview:contentarea]; + // 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; - // add ui objects for the sidebar and contentarea - // the sidebar is added last, so that new views are added first to it - UiObject *left = uic_object_new(obj, sidebar); - UiContainer *ct1 = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct1->widget = sidebar; - ct1->add = ui_container_add; - ct1->getframe = ui_container_getframe; - left->container = ct1; + // add box to the parent + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, box, &layout, TRUE); + + // add new box to the obj container chain + uic_object_push_container(obj, ui_create_container(obj, box)); - UiObject *right = uic_object_new(obj, sidebar); - UiContainer *ct2 = ucx_mempool_malloc( - obj->ctx->mempool, - sizeof(UiContainer)); - ct2->widget = contentarea; - ct2->add = ui_container_add; - ct2->getframe = ui_container_getframe; - right->container = ct2; + 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; - uic_obj_add(obj, right); - uic_obj_add(obj, left); + // add box to the parent + UiLayout layout = UI_INIT_LAYOUT(args); + ui_container_add(obj, grid, &layout, TRUE); - return splitview; + // add new box to the obj container chain + uic_object_push_container(obj, ui_create_container(obj, grid)); + + return (__bridge void*)grid; } -NSRect ui_container_getframe(UiContainer *ct) { - return [ct->widget frame]; +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; } -void ui_container_add(UiContainer *ct, NSView *view) { - [ct->widget addSubview: view]; +/* ------------------------- private functions ------------------------- */ + +UiContainerX* ui_create_container(UiObject *obj, id container) { + UiContainerX *ctn = ui_malloc(obj->ctx, sizeof(UiContainerX)); + ctn->container = (__bridge void*)container; + ctn->close = 0; + ctn->prev = NULL; + ctn->next = NULL; + return ctn; } + +void ui_container_add(UiObject *obj, NSView *view, UiLayout *layout, UiBool fill) { + UiContainerX *ctn = obj->container_end; + id container = (__bridge id)ctn->container; + container.uilayout = *layout; + [container addView:view fill:fill]; +} + +/* ---------------------- public layout functions ----------------------- */ + +void ui_newline(UiObject *obj) { + UiContainerX *ctn = obj->container_end; + if(ctn) { + id container = (__bridge id)ctn->container; + container.newline = TRUE; + } else { + fprintf(stderr, "Error: obj has no container\n"); + } +} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/objs.mk --- a/ui/cocoa/objs.mk Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/objs.mk Thu Dec 12 20:01:43 2024 +0100 @@ -30,16 +30,15 @@ COCOA_OBJPRE = $(OBJ_DIR)/$(COCOA_SRC_DIR) COCOAOBJ = toolkit.o +COCOAOBJ += AppDelegate.o +COCOAOBJ += GridLayout.o +COCOAOBJ += EventData.o +COCOAOBJ += UiJob.o +COCOAOBJ += MainWindow.o +COCOAOBJ += WindowManager.o COCOAOBJ += window.o -COCOAOBJ += menu.o -COCOAOBJ += stock.o -COCOAOBJ += toolbar.o -COCOAOBJ += container.o -COCOAOBJ += text.o -COCOAOBJ += resource.o -COCOAOBJ += tree.o -COCOAOBJ += graphics.o - +COCOAOBJ += Container.o +COCOAOBJ += button.o TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%) TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m) diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/toolkit.h --- a/ui/cocoa/toolkit.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/toolkit.h Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -31,58 +31,7 @@ #include "../common/context.h" #include "../common/object.h" - -@interface UiApplicationDelegate : NSObject { - -} - -- (void)applicationWillTerminate:(NSNotification*)notification; - -- (BOOL)applicationShouldHandleReopen:(NSApplication *)app hasVisibleWindows:(BOOL)visible; - -- (BOOL)application:(NSApplication *)application openFile:(NSString *)filename; - -@end - -@interface EventWrapper : NSObject { - void *data; - ui_callback callback; - int value; -} - -- (EventWrapper*) initWithData: (void*)data callback:(ui_callback) f; - -- (void*) data; -- (void) setData:(void*)d; -- (ui_callback) callback; -- (void) setCallback: (ui_callback)f; -- (int) intval; -- (void) setIntval:(int)i; +void ui_cocoa_onstartup(void); +void ui_cocoa_onopen(const char *file); +void ui_cocoa_onexit(void); -- (BOOL)handleEvent:(id)sender; -- (BOOL)handleStateEvent:(id)sender; -- (BOOL)handleToggleEvent:(id)sender; - -@end - -@interface UiThread : NSObject { - UiObject *obj; - ui_threadfunc job_func; - void *job_data; - ui_callback finish_callback; - void *finish_data; -} - -- (id) initWithObject:(UiObject*)object; -- (void) setJobFunction:(ui_threadfunc)func; -- (void) setJobData:(void*)data; -- (void) setFinishCallback:(ui_callback)callback; -- (void) setFinishData:(void*)data; - -- (void) start; -- (void) runJob:(id)n; -- (void) finish:(id)n; - -@end - - diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/toolkit.m --- a/ui/cocoa/toolkit.m Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/toolkit.m Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * 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: @@ -26,321 +26,126 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import -#import -#import -#import -#import - -#import "../common/context.h" -#import "../common/document.h" -#import "../common/properties.h" +#import "toolkit.h" -#import "toolkit.h" -#import "window.h" -#import "menu.h" -#import "toolbar.h" -#import "stock.h" +#include "../common/document.h" +#include "../common/properties.h" +#include "../common/menu.h" +#include "../common/toolbar.h" +#include "../common/threadpool.h" -NSAutoreleasePool *pool; +#import "AppDelegate.h" -static char *application_name; +static const char *application_name; + +static int app_argc; +static const char **app_argv; -static ui_callback appclose_fnc; -static void *appclose_udata; +static ui_callback startup_func; +static void *startup_data; +static ui_callback open_func; +void *open_data; +static ui_callback exit_func; +void *exit_data; -static ui_callback openfile_fnc; -static void *openfile_udata; +/* ------------------- App Init / Event Loop functions ------------------- */ -void ui_init(char *appname, int argc, char **argv) { - pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; - [NSBundle loadNibNamed:@"MainMenu" owner:NSApp]; +void ui_init(const char *appname, int argc, char **argv) { + application_name = appname; + app_argc = argc; + app_argv = (const char**)argv; - UiApplicationDelegate *delegate = [[UiApplicationDelegate alloc]init]; - [NSApp setDelegate: delegate]; - - + uic_init_global_context(); + uic_docmgr_init(); - ui_menu_init(); - ui_toolbar_init(); - ui_stock_init(); - + uic_menu_init(); + uic_toolbar_init(); + uic_load_app_properties(); + + [NSApplication sharedApplication]; + //[NSBundle loadNibNamed:@"MainMenu" owner:NSApp ]; + //[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&topLevelObjects]; } -char* ui_appname() { +const char* ui_appname() { return application_name; } -void ui_exitfunc(ui_callback f, void *userdata) { - appclose_fnc = f; - appclose_udata = userdata; -} - -void ui_openfilefunc(ui_callback f, void *userdata) { - openfile_fnc = f; - openfile_udata = userdata; +void ui_onstartup(ui_callback f, void *userdata) { + startup_func = f; + startup_data = userdata; } -void ui_show(UiObject *obj) { - uic_check_group_widgets(obj->ctx); - if([obj->widget class] == [UiCocoaWindow class]) { - UiCocoaWindow *window = (UiCocoaWindow*)obj->widget; - [window makeKeyAndOrderFront:nil]; - } else { - printf("Error: ui_show: Object is not a Window!\n"); - } -} - -void ui_set_show_all(UIWIDGET widget, int value) { - // TODO -} - -void ui_set_visible(UIWIDGET widget, int visible) { - // TODO -} - -void ui_set_enabled(UIWIDGET widget, int enabled) { - [(id)widget setEnabled: enabled]; +void ui_onopen(ui_callback f, void *userdata) { + open_func = f; + open_data = userdata; } - - -void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { - UiThread *thread = [[UiThread alloc]initWithObject:obj]; - [thread setJobFunction:tf]; - [thread setJobData:td]; - [thread setFinishCallback:f]; - [thread setFinishData:fd]; - [thread start]; -} - -void ui_main() { - [NSApp run]; - [pool release]; +void ui_onexit(ui_callback f, void *userdata) { + exit_func = f; + exit_data = userdata; } - -void ui_clipboard_set(char *str) { - NSString *string = [[NSString alloc] initWithUTF8String:str]; - NSPasteboard * pasteBoard = [NSPasteboard generalPasteboard]; - [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; - [pasteBoard setString:string forType:NSStringPboardType]; -} - -char* ui_clipboard_get() { - NSPasteboard * pasteBoard = [NSPasteboard generalPasteboard]; - NSArray *classes = [[NSArray alloc] initWithObjects:[NSString class], nil]; - NSDictionary *options = [NSDictionary dictionary]; - NSArray *data = [pasteBoard readObjectsForClasses:classes options:options]; - - if(data != nil) { - NSString *str = [data componentsJoinedByString: @""]; - - // copy C string - size_t length = [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; - const char *cstr = [str UTF8String]; - char *value = malloc(length + 1); - memcpy(value, cstr, length); - value[length] = '\0'; - - return value; - } else { - return NULL; +void ui_cocoa_onstartup(void) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(startup_func) { + startup_func(&e, startup_data); } } - -@implementation UiApplicationDelegate - -- (void)applicationWillTerminate:(NSNotification*)notification { - printf("terminate\n"); -} - -- (BOOL)applicationShouldHandleReopen:(NSApplication *)app hasVisibleWindows:(BOOL)visible; { - if(!visible) { - printf("reopen\n"); - } - return NO; -} - -- (BOOL)application:(NSApplication*)application openFile:(NSString*)filename { - if(openfile_fnc) { - UiEvent event; - event.obj = NULL; - event.document = NULL; - event.window = NULL; - event.eventdata = (void*)[filename UTF8String]; - event.intval = 0; - openfile_fnc(&event, openfile_udata); +void ui_cocoa_onopen(const char *file) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(open_func) { + open_func(&e, open_data); } - - return NO; -} - -@end - - -@implementation EventWrapper - -- (EventWrapper*) initWithData: (void*)d callback:(ui_callback) f { - data = d; - callback = f; - value = 0; - return self; -} - - -- (void*) data { - return data; -} - -- (void) setData:(void*)d { - data = d; -} - - -- (ui_callback) callback { - return callback; -} - -- (void) setCallback: (ui_callback)f { - callback = f; -} - -- (int) intval { - return value; -} - -- (void) setIntval:(int)i { - value = i; -} - - -- (BOOL)handleEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - - UiEvent event; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - event.intval = value; - } - if(callback) { - callback(&event, data); - } - - return true; } -- (BOOL)handleStateEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - int state = [sender state] ? NSOffState : NSOnState; - - UiEvent event; - event.intval = state; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - // if the sender is a menu item, we have to save the state for this - // window - UiMenuItem *wmi = [(UiCocoaWindow*)activeWindow getMenuItem: sender]; - if(wmi) { - // update state in window data - wmi->state = state; - } - } else { - event.window = NULL; - event.document = NULL; - } - if(callback) { - callback(&event, data); - } - [sender setState: state]; - - return true; -} - -- (BOOL)handleToggleEvent:(id)sender { - NSWindow *activeWindow = [NSApp keyWindow]; - - UiEvent event; - event.intval = [sender state]; - event.eventdata = NULL; - if([activeWindow class] == [UiCocoaWindow class]) { - event.obj = [(UiCocoaWindow*)activeWindow object]; - event.window = event.obj->window; - event.document = event.obj->ctx->document; - } else { - event.window = NULL; - event.document = NULL; - } - if(callback) { - callback(&event, data); - } - - return true; -} - -@end - -@implementation UiThread - -- (id) initWithObject:(UiObject*)object { - obj = object; - job_func = NULL; - job_data = NULL; - finish_callback = NULL; - finish_data = NULL; - return self; -} - -- (void) setJobFunction:(ui_threadfunc)func { - job_func = func; -} - -- (void) setJobData:(void*)data { - job_data = data; -} - -- (void) setFinishCallback:(ui_callback)callback { - finish_callback = callback; -} - -- (void) setFinishData:(void*)data { - finish_data = data; -} - -- (void) start { - [NSThread detachNewThreadSelector:@selector(runJob:) - toTarget:self - withObject:nil]; -} - -- (void) runJob:(id)n { - int result = job_func(job_data); - if(!result) { - [self performSelectorOnMainThread:@selector(finish:) - withObject:nil - waitUntilDone:NO]; +void ui_cocoa_onexit(void) { + UiEvent e; + e.obj = NULL; + e.window = NULL; + e.document = NULL; + e.eventdata = NULL; + e.intval = 0; + if(exit_func) { + exit_func(&e, exit_data); } } -- (void) finish:(id)n { - UiEvent event; - event.obj = obj; - event.window = obj->window; - event.document = obj->ctx->document; - event.eventdata = NULL; - event.intval = 0; - finish_callback(&event, finish_data); +void ui_main(void) { + NSApplicationMain(app_argc, app_argv); +} + +/* ------------------- Window Visibility functions ------------------- */ + +void ui_show(UiObject *obj) { + if(obj->wobj) { + NSWindow *window = (__bridge NSWindow*)obj->wobj; + [window makeKeyAndOrderFront:nil]; + } } -@end +void ui_close(UiObject *obj) { + +} + +/* ------------------- Job Control / Threadpool functions ------------------- */ +void ui_job(UiObject *obj, ui_threadfunc tf, void *td, ui_callback f, void *fd) { +} + +void ui_call_mainthread(ui_threadfunc tf, void* td) { + +} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/window.h --- a/ui/cocoa/window.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/window.h Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * 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: @@ -26,27 +26,4 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#import -#import "../ui/window.h" -#import -#import - -#import "menu.h" - - - -@interface UiCocoaWindow : NSWindow { - UiObject *uiobj; - UcxMap *menus; // key: NSMenu value: UcxList of UiMenuItem - UcxMap *items; // key: NSMenuItem value: UiMenuItem -} - -- (UiCocoaWindow*) init: (NSRect)frame object: (UiObject*)obj; -- (UiObject*) object; -- (void) setObject:(UiObject*)obj; -- (void) setMenuItems:(UcxList*)menuItems; -- (void) setMenuItemLists:(UcxList*)itemLists; -- (UiMenuItem*) getMenuItem:(NSMenuItem*)item; -- (void) updateMenu:(NSMenu*)menu; - -@end +#import "toolkit.h" \ No newline at end of file diff -r b9767cb5b06b -r d2bd73d28ff1 ui/cocoa/window.m --- a/ui/cocoa/window.m Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/cocoa/window.m Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2012 Olaf Wintermann. All rights reserved. + * 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: @@ -26,195 +26,43 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include +#import "window.h" -#import "window.h" -#import "menu.h" -#import "toolbar.h" -#import "container.h" -#import -#import "../common/context.h" - -static int window_default_width = 600; -static int window_default_height = 500; - -@implementation UiCocoaWindow +#import "MainWindow.h" +#import "WindowManager.h" -- (UiCocoaWindow*) init: (NSRect)frame object: (UiObject*)obj { - self = [self initWithContentRect:frame - styleMask:NSTitledWindowMask | - NSResizableWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered - defer:false]; - - uiobj = obj; - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; - menus = ucx_map_new_a(allocator, 8); - items = ucx_map_new_a(allocator, 64); - - return self; -} +#include "../ui/window.h" +#include "../ui/properties.h" +#include "../common/context.h" +#include "../common/menu.h" +#include "../common/toolbar.h" -- (UiObject*) object { - return uiobj; -} +#include -- (void) setObject:(UiObject*)obj { - uiobj = obj; -} - -- (void) setMenuItems:(UcxList*)menuItems { - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; +static UiObject* create_window(const char *title, BOOL simple) { + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + obj->ref = 0; - UCX_FOREACH(elm, menuItems) { - UiStateItem *item = elm->data; - NSMenu *menu = [item->item menu]; - - // create UiMenuItem which represents an NSMenuItem for a Window - UiMenuItem *windowItem = ucx_mempool_malloc(uiobj->ctx->mempool, sizeof(UiMenuItem)); - windowItem->item = item->item; - windowItem->state = 0; - if(item->var) { - // bind value - UiVar *var = uic_connect_var(uiobj->ctx, item->var, UI_VAR_INTEGER); - if(var) { - UiInteger *value = var->value; - value->obj = windowItem; - value->get = ui_menuitem_get; - value->set = ui_menuitem_set; - value = 0; - } else { - // TODO: error - } - } - - // add item - UiAbstractMenuItem *abstractItem = malloc(sizeof(UiAbstractMenuItem)); - abstractItem->update = ui_update_item; - abstractItem->item_data = windowItem; - UcxList *itemList = ucx_map_get(menus, ucx_key(&menu, sizeof(void*))); - itemList = ucx_list_append_a(allocator, itemList, abstractItem); - ucx_map_put(menus, ucx_key(&menu, sizeof(void*)), itemList); - - ucx_map_put(items, ucx_key(&windowItem->item, sizeof(void*)), windowItem); - } -} - -- (void) setMenuItemLists:(UcxList*)itemLists { - UcxAllocator *allocator = uiobj->ctx->mempool->allocator; - - UCX_FOREACH(elm, itemLists) { - UiMenuItemList *list = elm->data; - - UiAbstractMenuItem *abstractItem = malloc(sizeof(UiAbstractMenuItem)); - abstractItem->update = ui_update_item_list; - abstractItem->item_data = list; - - UcxList *itemList = ucx_map_get(menus, ucx_key(&list->menu, sizeof(void*))); - itemList = ucx_list_append_a(allocator, itemList, abstractItem); - ucx_map_put(menus, ucx_key(&list->menu, sizeof(void*)), itemList); - - } -} - -- (UiMenuItem*) getMenuItem:(NSMenuItem*)item { - return ucx_map_get(items, ucx_key(&item, sizeof(void*))); -} - -- (void) updateMenu:(NSMenu*)menu { - UcxList *itemList = ucx_map_get(menus, ucx_key(&menu, sizeof(void*))); - UCX_FOREACH(elm, itemList) { - UiAbstractMenuItem *item = elm->data; - item->update(self, item->item_data); - } - - // update group items - // TODO: use only one loop for all items - int ngroups = 0; - int *groups = ui_active_groups(uiobj->ctx, &ngroups); - - NSArray *groupItems = [menu itemArray]; - int count = [groupItems count]; - for(int i=0;ictx = uic_context(obj, mp); - // create native window - NSRect frame = NSMakeRect( - 300, - 200, - window_default_width, - window_default_height); + MainWindow *window = [[MainWindow alloc] init:obj]; + [[WindowManager sharedWindowManager] addWindow:window]; + window.releasedWhenClosed = false; - /* - UiCocoaWindow *window = [[UiCocoaWindow alloc] initWithContentRect:frame - styleMask:NSTitledWindowMask | NSResizableWindowMask | - NSClosableWindowMask | NSMiniaturizableWindowMask - backing:NSBackingStoreBuffered - defer:false]; - */ - UiCocoaWindow *window = [[UiCocoaWindow alloc] init:frame object:obj]; - - NSString *titleStr = [[NSString alloc] initWithUTF8String:title]; - [window setTitle:titleStr]; - - UiMenuDelegate *menuDelegate = ui_menu_delegate(); - [window setMenuItems: [menuDelegate items]]; - [window setMenuItemLists: [menuDelegate lists]]; - - NSToolbar *toolbar = ui_create_toolbar(obj); - [window setToolbar: toolbar]; - - obj->widget = (NSView*)window; - obj->window = window_data; - obj->container = ui_window_container(obj, window); - + obj->wobj = (__bridge void*)window; return obj; } -void ui_close(UiObject *obj) { - // TODO +UiObject* ui_window(const char *title, void *window_data) { + UiObject *obj = create_window(title, FALSE); + obj->window = window_data; + return obj; } -char* ui_openfiledialog(UiObject *obj) { - NSOpenPanel* op = [NSOpenPanel openPanel]; - if ([op runModal] == NSOKButton) { - NSArray *urls = [op URLs]; - NSURL *url = [urls objectAtIndex:0]; - - const char *str = [[url path] UTF8String]; - return (char*)strdup(str); - } - return NULL; +UiObject* ui_simple_window(const char *title, void *window_data) { + UiObject *obj = create_window(title, TRUE); + obj->window = window_data; + return obj; } - -char* ui_savefiledialog(UiObject *obj) { - NSSavePanel* sp = [NSSavePanel savePanel]; - if ([sp runModal] == NSOKButton) { - NSURL *url = [sp URL]; - - const char *str = [[url path] UTF8String]; - return (char*)strdup(str); - } - return NULL; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/condvar.c --- a/ui/common/condvar.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/condvar.c Thu Dec 12 20:01:43 2024 +0100 @@ -28,7 +28,7 @@ #include "condvar.h" - +#include UiCondVar* ui_condvar_create(void) { UiPosixCondVar *var = malloc(sizeof(UiPosixCondVar)); diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/context.c --- a/ui/common/context.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/context.c Thu Dec 12 20:01:43 2024 +0100 @@ -187,6 +187,13 @@ } UiVar* uic_create_var(UiContext *ctx, const char *name, UiVarType type) { + if(ctx->vars_unbound) { + UiVar *unbound = cxMapGet(ctx->vars_unbound, name); + if(unbound) { + return unbound; + } + } + UiVar *var = uic_get_var(ctx, name); if(var) { if(var->type == type) { diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/menu.c --- a/ui/common/menu.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/menu.c Thu Dec 12 20:01:43 2024 +0100 @@ -30,6 +30,7 @@ #include #include +#include #include #include diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/object.c --- a/ui/common/object.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/object.c Thu Dec 12 20:01:43 2024 +0100 @@ -32,6 +32,8 @@ #include "object.h" #include "context.h" +#include "../ui/container.h" + void ui_end(UiObject *obj) { if(!obj->next) { return; @@ -49,20 +51,30 @@ } } +void ui_end_new(UiObject *obj) { + if(!obj->container_end) { + return; + } + UiContainerX *rm = obj->container_end; + uic_object_pop_container(obj); + ui_free(obj->ctx, rm); +} + void ui_object_ref(UiObject *obj) { obj->ref++; } -void ui_object_unref(UiObject *obj) { +int ui_object_unref(UiObject *obj) { // it is possible to have 0 references, in case // a window was created but ui_show was never called if(obj->ref == 0 || --obj->ref == 0) { if(obj->destroy) { obj->destroy(obj); - } else { - uic_object_destroy(obj); } + uic_object_destroy(obj); + return 0; } + return 1; } void uic_object_destroy(UiObject *obj) { @@ -109,3 +121,23 @@ UiContainer* uic_get_current_container(UiObject *obj) { return uic_current_obj(obj)->container; } + +void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer) { + newcontainer->prev = toplevel->container_end; + if(toplevel->container_end) { + toplevel->container_end->next = newcontainer; + toplevel->container_end = newcontainer; + } else { + toplevel->container_begin = newcontainer; + toplevel->container_end = newcontainer; + } +} + +void uic_object_pop_container(UiObject *toplevel) { + toplevel->container_end = toplevel->container_end->prev; + if(toplevel->container_end) { + toplevel->container_end->next = NULL; + } else { + toplevel->container_begin = NULL; + } +} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/object.h --- a/ui/common/object.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/object.h Thu Dec 12 20:01:43 2024 +0100 @@ -42,7 +42,11 @@ void uic_obj_add(UiObject *toplevel, UiObject *ctobj); UiObject* uic_current_obj(UiObject *toplevel); -UiContainer* uic_get_current_container(UiObject *obj);; +UiContainer* uic_get_current_container(UiObject *obj); // deprecated + +void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer); +void uic_object_pop_container(UiObject *toplevel); + #ifdef __cplusplus diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/threadpool.c --- a/ui/common/threadpool.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/threadpool.c Thu Dec 12 20:01:43 2024 +0100 @@ -29,10 +29,12 @@ #include "threadpool.h" #include "context.h" -#include - #ifndef _WIN32 +#include +#include +#include +#include static threadpool_job kill_job; diff -r b9767cb5b06b -r d2bd73d28ff1 ui/common/threadpool.h --- a/ui/common/threadpool.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/common/threadpool.h Thu Dec 12 20:01:43 2024 +0100 @@ -31,6 +31,10 @@ #include "../ui/toolkit.h" +#ifndef _WIN32 +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/button.c --- a/ui/gtk/button.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/button.c Thu Dec 12 20:01:43 2024 +0100 @@ -398,12 +398,14 @@ } UiRadioButtonData; static void destroy_radiobutton(GtkWidget *w, UiRadioButtonData *data) { - ui_destroy_vardata(w, data->eventdata); if(data->first) { + ui_destroy_vardata(w, data->eventdata); g_slist_free(data->value->obj); data->value->obj = NULL; data->value->get = NULL; data->value->set = NULL; + } else { + free(data->eventdata); } free(data); } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/container.c --- a/ui/gtk/container.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/container.c Thu Dec 12 20:01:43 2024 +0100 @@ -891,6 +891,42 @@ #endif +/* -------------------- Sidebar -------------------- */ + +#ifdef UI_LIBADWAITA +UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { + GtkWidget *sidebar_toolbar_view = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); + if(!sidebar_toolbar_view) { + fprintf(stderr, "Error: window is not configured for sidebar\n"); + return NULL; + } + + GtkWidget *box = ui_gtk_vbox_new(args.spacing); + ui_box_set_margin(box, args.margin); + adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), box); + + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_obj_add(obj, newobj); + + return box; +} +#else +UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args) { + GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); + + GtkWidget *box = ui_gtk_vbox_new(args.spacing); + ui_box_set_margin(box, args.margin); + BOX_ADD_EXPAND(sidebar_vbox, box); + + UiObject *newobj = uic_object_new(obj, box); + newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX); + uic_obj_add(obj, newobj); + + return box; +} +#endif + /* -------------------- Splitpane -------------------- */ static GtkWidget* create_paned(UiOrientation orientation) { diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/list.c --- a/ui/gtk/list.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/list.c Thu Dec 12 20:01:43 2024 +0100 @@ -36,6 +36,7 @@ #include "container.h" #include +#include #include "list.h" #include "icon.h" @@ -982,3 +983,296 @@ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]); } } + + +/* ------------------------------ Source List ------------------------------ */ + +static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { + cxListDestroy(v->sublists); + free(v); +} + +static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) { + free(sublist->header); + ui_destroy_boundvar(obj->ctx, sublist->var); + cxListDestroy(sublist->widgets); +} + +static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) { + // first rows in sublists have the ui_listbox property + UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox"); + if(!listbox) { + return; + } + + UiListBoxSubList *sublist = g_object_get_data(G_OBJECT(row), "ui_listbox_sublist"); + if(!sublist) { + return; + } + + if(sublist->separator) { + GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + gtk_list_box_row_set_header(row, separator); + } else if(sublist->header) { + GtkWidget *header = gtk_label_new(sublist->header); + gtk_widget_set_halign(header, GTK_ALIGN_START); + if(row == listbox->first_row) { + WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header-first"); + } else { + WIDGET_ADD_CSS_CLASS(header, "ui-listbox-header"); + } + gtk_list_box_row_set_header(row, header); + } +} + +#ifdef UI_GTK3 +typedef struct _UiSidebarListBoxClass { + GtkListBoxClass parent_class; +} UiSidebarListBoxClass; + +typedef struct _UiSidebarListBox { + GtkListBox parent_instance; +} UiSidebarListBox; + +G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box, GTK_TYPE_LIST_BOX) + +/* Initialize the instance */ +static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) { + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + gtk_widget_class_set_css_name (widget_class, "placessidebar"); +} + +static void ui_sidebar_list_box_init(UiSidebarListBox *self) { + +} +#endif + +UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) { + UiObject* current = uic_current_obj(obj); + +#ifdef UI_GTK3 + GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); +#else + GtkWidget *listbox = gtk_list_box_new(); +#endif + if(!args.style_class) { +#if GTK_MAJOR_VERSION >= 4 + WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar"); +#else + WIDGET_ADD_CSS_CLASS(listbox, "sidebar"); +#endif + } + gtk_list_box_set_header_func(GTK_LIST_BOX(listbox), listbox_create_header, NULL, NULL); + GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); + SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); + + ui_set_name_and_style(listbox, args.name, args.style_class); + ui_set_widget_groups(obj->ctx, listbox, args.groups); + UI_APPLY_LAYOUT1(current, args); + current->container->add(current->container, scroll_area, TRUE); + + UiListBox *uilistbox = malloc(sizeof(UiListBox)); + uilistbox->obj = obj; + uilistbox->listbox = GTK_LIST_BOX(listbox); + uilistbox->getvalue = args.getvalue; + uilistbox->onactivate = args.onactivate; + uilistbox->onactivatedata = args.onactivatedata; + uilistbox->onbuttonclick = args.onbuttonclick; + uilistbox->onbuttonclickdata = args.onbuttonclickdata; + uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4); + uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy; + uilistbox->sublists->collection.destructor_data = obj; + uilistbox->first_row = NULL; + + if(args.numsublists == 0 && args.sublists) { + args.numsublists = INT_MAX; + } + for(int i=0;ictx, + current->ctx, + sublist.value, + sublist.varname, + UI_VAR_LIST); + uisublist.numitems = 0; + uisublist.header = sublist.header ? strdup(sublist.header) : NULL; + uisublist.separator = sublist.separator; + uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS); + uisublist.listbox = uilistbox; + uisublist.userdata = sublist.userdata; + uisublist.index = i; + + cxListAdd(uilistbox->sublists, &uisublist); + + // bind UiList + UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-1); + UiList *list = uisublist.var->value; + if(list) { + list->obj = sublist_ptr; + list->update = ui_listbox_list_update; + } + } + // fill items + ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); + + // register uilistbox for both widgets, so it doesn't matter which + // widget is used later + g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); + g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); + + // signals + g_signal_connect( + listbox, + "destroy", + G_CALLBACK(ui_destroy_sourcelist), + uilistbox); + + if(args.onactivate) { + g_signal_connect( + listbox, + "row-activated", + G_CALLBACK(ui_listbox_row_activate), + NULL); + } + + return scroll_area; +} + +void ui_listbox_update(UiListBox *listbox, int from, int to) { + CxIterator i = cxListIterator(listbox->sublists); + size_t pos = 0; + cx_foreach(UiListBoxSubList *, sublist, i) { + if(i.index < from) { + pos += sublist->numitems; + continue; + } + if(i.index > to) { + break; + } + + // reload sublist + ui_listbox_update_sublist(listbox, sublist, pos); + pos += sublist->numitems; + } +} + +static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); + if(item->icon) { + GtkWidget *icon = ICON_IMAGE(item->icon); + BOX_ADD(hbox, icon); + } + GtkWidget *label = gtk_label_new(item->label); + gtk_widget_set_halign(label, GTK_ALIGN_START); + BOX_ADD_EXPAND(hbox, label); + // TODO: badge, button + GtkWidget *row = gtk_list_box_row_new(); + LISTBOX_ROW_SET_CHILD(row, hbox); + + // signals + UiEventDataExt *event = malloc(sizeof(UiEventDataExt)); + memset(event, 0, sizeof(UiEventDataExt)); + event->obj = listbox->obj; + event->customdata0 = sublist; + event->customdata1 = sublist->var; + event->customdata2 = item->eventdata; + event->callback = listbox->onactivate; + event->userdata = listbox->onactivatedata; + event->callback2 = listbox->onbuttonclick; + event->userdata2 = listbox->onbuttonclickdata; + event->value0 = index; + + g_signal_connect( + row, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + + g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); + + return row; +} + +void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { + // clear sublist + CxIterator r = cxListIterator(sublist->widgets); + cx_foreach(GtkWidget*, widget, r) { + LISTBOX_REMOVE(listbox->listbox, widget); + } + cxListClear(sublist->widgets); + + sublist->numitems = 0; + + // create items for each UiList element + UiList *list = sublist->var->value; + if(!list) { + return; + } + + size_t index = 0; + void *elm = list->first(list); + while(elm) { + UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; + listbox->getvalue(sublist->userdata, elm, index, &item); + + // create listbox item + GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); + if(index == 0) { + // first row in the sublist, set ui_listbox data to the row + // which is then used by the headerfunc + g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); + g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); + + if(listbox_insert_index == 0) { + // first row in the GtkListBox + listbox->first_row = GTK_LIST_BOX_ROW(row); + } + } + intptr_t rowindex = listbox_insert_index + index; + g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + cxListAdd(sublist->widgets, row); + + // cleanup + free(item.label); + free(item.icon); + free(item.button_label); + free(item.button_icon); + free(item.badge); + + // next row + elm = list->next(list); + index++; + } + + sublist->numitems = cxListSize(sublist->widgets); +} + +void ui_listbox_list_update(UiList *list, int i) { + UiListBoxSubList *sublist = list->obj; +} + +void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { + UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); + if(!data) { + return; + } + UiListBoxSubList *sublist = data->customdata0; + + UiEvent event; + event.obj = data->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = data->customdata2; + event.intval = data->value0; + + if(data->callback) { + data->callback(&event, data->userdata); + } +} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/list.h --- a/ui/gtk/list.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/list.h Thu Dec 12 20:01:43 2024 +0100 @@ -32,6 +32,8 @@ #include "../ui/tree.h" #include "toolkit.h" +#include + #ifdef __cplusplus extern "C" { #endif @@ -57,7 +59,33 @@ void *activatedata; void *selectiondata; } UiTreeEventData; + +typedef struct UiListBox UiListBox; + +typedef struct UiListBoxSubList { + UiVar *var; + size_t numitems; + char *header; + UiBool separator; + CxList *widgets; + UiListBox *listbox; + void *userdata; + size_t index; +} UiListBoxSubList; + +struct UiListBox { + UiObject *obj; + GtkListBox *listbox; + CxList *sublists; // contains UiListBoxSubList elements + ui_sublist_getvalue_func getvalue; + ui_callback onactivate; + void *onactivatedata; + ui_callback onbuttonclick; + void *onbuttonclickdata; + GtkListBoxRow *first_row; +}; + void* ui_strmodel_getvalue(void *elm, int column); UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); @@ -94,6 +122,12 @@ void ui_combobox_modelupdate(UiList *list, int i); UiListSelection ui_combobox_getselection(UiList *list); void ui_combobox_setselection(UiList *list, UiListSelection selection); + +void ui_listbox_update(UiListBox *listbox, int from, int to); +void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index); +void ui_listbox_list_update(UiList *list, int i); + +void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/text.c --- a/ui/gtk/text.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/text.c Thu Dec 12 20:01:43 2024 +0100 @@ -551,6 +551,8 @@ uitext->var = var; uitext->onchange = args.onchange; uitext->onchangedata = args.onchangedata; + uitext->onactivate = args.onactivate; + uitext->onactivatedata = args.onactivatedata; g_signal_connect( textfield, @@ -599,6 +601,14 @@ uitext); } + if(args.onactivate) { + g_signal_connect( + textfield, + "activate", + G_CALLBACK(ui_textfield_activate), + uitext); + } + return textfield; } @@ -638,6 +648,17 @@ } } +void ui_textfield_activate(GtkEntry* self, UiTextField *textfield) { + if(textfield->onactivate) { + UiEvent e; + e.obj = textfield->obj; + e.window = e.obj->window; + e.document = textfield->obj->ctx->document; + e.eventdata = NULL; + e.intval = 0; + textfield->onactivate(&e, textfield->onactivatedata); + } +} char* ui_textfield_get(UiString *str) { if(str->value.ptr) { diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/text.h --- a/ui/gtk/text.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/text.h Thu Dec 12 20:01:43 2024 +0100 @@ -74,6 +74,8 @@ UiVar *var; ui_callback onchange; void *onchangedata; + ui_callback onactivate; + void *onactivatedata; } UiTextField; typedef struct UiPathTextField { @@ -140,6 +142,7 @@ void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield); void ui_textfield_changed(GtkEditable *editable, UiTextField *textfield); +void ui_textfield_activate(GtkEntry* self, UiTextField *textfield); char* ui_textfield_get(UiString *str); void ui_textfield_set(UiString *str, const char *value); diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/toolkit.c --- a/ui/gtk/toolkit.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/toolkit.c Thu Dec 12 20:01:43 2024 +0100 @@ -300,6 +300,10 @@ free(data); } +void ui_destroy_widget_var(GtkWidget *object, UiVar *var) { + ui_destroy_boundvar(NULL, var); +} + void ui_destroy_boundvar(UiContext *ctx, UiVar *var) { uic_unbind_var(var); @@ -362,6 +366,18 @@ ".ui_label_title {\n" " font-weight: bold;\n" "}\n" +".ui-listbox-header {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 12px;\n" +" margin-bottom: 10px;\n" +"}\n" +".ui-listbox-header-first {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 4px;\n" +" margin-bottom: 10px;\n" +"}\n" ; #elif GTK_MAJOR_VERSION == 3 @@ -380,6 +396,21 @@ ".ui_label_title {\n" " font-weight: bold;\n" "}\n" +"placessidebar row {\n" +" padding-left: 10px;\n" +"}\n" +".ui-listbox-header {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 12px;\n" +" margin-bottom: 10px;\n" +"}\n" +".ui-listbox-header-first {\n" +" font-weight: bold;\n" +" margin-left: 10px;\n" +" margin-top: 4px;\n" +" margin-bottom: 10px;\n" +"}\n" ; #endif diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/toolkit.h --- a/ui/gtk/toolkit.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/toolkit.h Thu Dec 12 20:01:43 2024 +0100 @@ -40,7 +40,7 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" -#if GLIB_MAJOR_VERSION * 1000 + GLIB_MINOR_VERSION > 74 +#if GLIB_MAJOR_VERSION * 1000 + GLIB_MINOR_VERSION > 2074 #define UI_G_APPLICATION_FLAGS G_APPLICATION_DEFAULT_FLAGS #else #define UI_G_APPLICATION_FLAGS G_APPLICATION_FLAGS_NONE @@ -70,6 +70,9 @@ #define EXPANDER_SET_CHILD(expander, child) gtk_expander_set_child(GTK_EXPANDER(expander), child) #define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_widget_add_css_class(w, cssclass) #define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_widget_remove_css_class(w, cssclass) +#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon) +#define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row) +#define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child) #else #define WINDOW_SHOW(window) gtk_widget_show_all(window) #define WINDOW_DESTROY(window) gtk_widget_destroy(window) @@ -86,6 +89,9 @@ #define EXPANDER_SET_CHILD(expander, child) gtk_container_add(GTK_CONTAINER(expander), child) #define WIDGET_ADD_CSS_CLASS(w, cssclass) gtk_style_context_add_class(gtk_widget_get_style_context(w), cssclass) #define WIDGET_REMOVE_CSS_CLASS(w, cssclass) gtk_style_context_remove_class(gtk_widget_get_style_context(w), cssclass) +#define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON) +#define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row) +#define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child) #endif #ifdef UI_GTK2 @@ -168,6 +174,7 @@ void ui_destroy_userdata(GtkWidget *object, void *userdata); void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data); +void ui_destroy_widget_var(GtkWidget *object, UiVar *var); void ui_destroy_boundvar(UiContext *ctx, UiVar *var); void ui_set_active_window(UiObject *obj); diff -r b9767cb5b06b -r d2bd73d28ff1 ui/gtk/window.c --- a/ui/gtk/window.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/gtk/window.c Thu Dec 12 20:01:43 2024 +0100 @@ -101,7 +101,7 @@ } #endif -static UiObject* create_window(const char *title, void *window_data, UiBool simple) { +static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) { CxMempool *mp = cxBasicMempoolCreate(256); UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); obj->ref = 0; @@ -136,7 +136,7 @@ } else { gtk_window_set_default_size( GTK_WINDOW(obj->widget), - window_default_width, + window_default_width + sidebar*250, window_default_height); } @@ -163,8 +163,27 @@ GtkWidget *vbox = ui_gtk_vbox_new(0); #ifdef UI_LIBADWAITA GtkWidget *toolbar_view = adw_toolbar_view_new(); - adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view); adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox); + + GtkWidget *content_box = ui_gtk_vbox_new(0); + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + + if(sidebar) { + GtkWidget *splitview = adw_overlay_split_view_new(); + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview); + + GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new(); + adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view); + GtkWidget *sidebar_headerbar = adw_header_bar_new(); + adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), sidebar_headerbar); + + adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), toolbar_view); + + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view); + } else { + adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view); + } + GtkWidget *headerbar = adw_header_bar_new(); adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar); @@ -174,10 +193,19 @@ ui_fill_headerbar(obj, headerbar); } #elif GTK_MAJOR_VERSION >= 4 + GtkWidget *content_box = ui_gtk_vbox_new(0); WINDOW_SET_CONTENT(obj->widget, vbox); + if(sidebar) { + GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); + gtk_paned_set_start_child(GTK_PANED(paned), sidebar_vbox); + gtk_paned_set_end_child(GTK_PANED(paned), content_box); + BOX_ADD_EXPAND(GTK_BOX(vbox), paned); + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); + } else { + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + } #else - gtk_container_add(GTK_CONTAINER(obj->widget), vbox); - if(!simple) { // menu if(uic_get_menu_list()) { @@ -198,6 +226,21 @@ //GtkWidget *hb = ui_create_headerbar(obj); //gtk_window_set_titlebar(GTK_WINDOW(obj->widget), hb); } + + GtkWidget *content_box = ui_gtk_vbox_new(0); + WINDOW_SET_CONTENT(obj->widget, vbox); + if(sidebar) { + GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); + GtkWidget *sidebar_vbox = ui_gtk_vbox_new(0); + gtk_paned_add1(GTK_PANED(paned), sidebar_vbox); + gtk_paned_add2(GTK_PANED(paned), content_box); + BOX_ADD_EXPAND(GTK_BOX(vbox), paned); + g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_vbox); + gtk_paned_set_position (GTK_PANED(paned), 200); + } else { + BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); + } + #endif // window content @@ -213,8 +256,6 @@ gtk_container_add(GTK_CONTAINER(frame), content_box); obj->container = ui_box_container(obj, content_box); */ - GtkWidget *content_box = ui_gtk_vbox_new(0); - BOX_ADD_EXPAND(GTK_BOX(vbox), content_box); obj->container = ui_box_container(obj, content_box, UI_CONTAINER_VBOX); nwindows++; @@ -223,11 +264,15 @@ UiObject* ui_window(const char *title, void *window_data) { - return create_window(title, window_data, FALSE); + return create_window(title, window_data, FALSE, FALSE); +} + +UiObject *ui_sidebar_window(const char *title, void *window_data) { + return create_window(title, window_data, TRUE, FALSE); } UiObject* ui_simple_window(const char *title, void *window_data) { - return create_window(title, window_data, TRUE); + return create_window(title, window_data, FALSE, TRUE); } void ui_window_size(UiObject *obj, int width, int height) { diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/Grid.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/Grid.c Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,582 @@ +/* + * 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. + */ + +/* + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include "Grid.h" + +#include + + + +static XtActionsRec actionslist[] = { + {"getfocus",grid_getfocus}, + {"loosefocus",grid_loosefocus}, + {"NULL",NULL} +}; + +//static char defaultTranslations[] = ": mousedown()\n"; +static char defaultTranslations[] = "\ +: getfocus()\n\ +: loosefocus()\n"; + + +///* +static XtResource constraints[] = +{ + { + gridColumn, + gridColumn, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.x), + XmRImmediate, + (XtPointer) 0 + }, + { + gridRow, + gridRow, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.y), + XmRImmediate, + (XtPointer) 0 + }, + { + gridColspan, + gridColspan, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.colspan), + XmRImmediate, + (XtPointer) 0 + }, + { + gridRowspan, + gridRowspan, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.rowspan), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginLeft, + gridMarginLeft, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_left), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginRight, + gridMarginRight, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_right), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginTop, + gridMarginTop, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_top), + XmRImmediate, + (XtPointer) 0 + }, + { + gridMarginBottom, + gridMarginBottom, + XmRDimension, + sizeof (Dimension), + XtOffsetOf( GridConstraintRec, + grid.margin_bottom), + XmRImmediate, + (XtPointer) 0 + }, + { + gridHExpand, + gridHExpand, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.hexpand), + XmRImmediate, + (XtPointer) 0 + }, + { + gridVExpand, + gridVExpand, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.vexpand), + XmRImmediate, + (XtPointer) 0 + }, + { + gridHFill, + gridHFill, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.hfill), + XmRImmediate, + (XtPointer) 0 + }, + { + gridVFill, + gridVFill, + XmRBoolean, + sizeof (Boolean), + XtOffsetOf( GridConstraintRec, + grid.vfill), + XmRImmediate, + (XtPointer) 0 + } + +}; +//*/ +//static XtResource constraints[] = {}; + +GridClassRec gridClassRec = { + // Core Class + { + //(WidgetClass)&constraintClassRec, // superclass + (WidgetClass)&xmManagerClassRec, + "Grid", // class_name + sizeof(GridRec), // widget_size + grid_class_initialize, // class_initialize + NULL, // class_part_initialize + FALSE, // class_inited + (XtInitProc)grid_initialize, // initialize + NULL, // initialize_hook + grid_realize, // realize + actionslist, // actions + XtNumber(actionslist), // num_actions + NULL, // resources + 0, // num_resources + NULLQUARK, // xrm_class + True, // compress_motion + True, // compress_exposure + True, // compress_enterleave + False, // visible_interest + (XtWidgetProc)grid_destroy, // destroy + (XtWidgetProc)grid_resize, // resize + (XtExposeProc)grid_expose, // expose + grid_set_values, // set_values + NULL, // set_values_hook + XtInheritSetValuesAlmost, // set_values_almost + NULL, // get_values_hook + (XtAcceptFocusProc)grid_acceptfocus, // accept_focus + XtVersion, // version + NULL, // callback_offsets + //NULL, // tm_table + defaultTranslations, + XtInheritQueryGeometry, // query_geometry + NULL, // display_accelerator + NULL, // extension + }, + // Composite Class + { + GridGeometryManager, /* geometry_manager */ + GridChangeManaged, /* change_managed */ + XtInheritInsertChild, /* insert_child */ + XtInheritDeleteChild, /* delete_child */ + NULL, /* extension */ + }, + // Constraint Class + { + constraints, /* resources */ + XtNumber(constraints), /* num_resources */ + sizeof(GridConstraintRec), /* constraint_size */ + grid_constraint_init, /* initialize */ + NULL, /* destroy */ + ConstraintSetValues, /* set_values */ + NULL, /* extension */ + }, + // XmManager Class + ///* + { + NULL, + NULL, + 0, + NULL, + 0, + NULL, + NULL + }, + //*/ + // MyWidget Class + { + 0 + } +}; + +WidgetClass gridClass = (WidgetClass)&gridClassRec; + + +void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) { + +} +void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) { + MyWidget mn = (MyWidget)new; + + mn->mywidget.max_col = 0; + mn->mywidget.max_row = 0; + +} +void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) { + XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL); + (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); + grid_place_children(w); +} + + +void grid_destroy(MyWidget widget) { + +} +void grid_resize(MyWidget widget) { + grid_place_children(widget); +} + +void grid_expose(MyWidget widget, XEvent *event, Region region) { + +} + + +Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { + return False; +} + +Boolean grid_acceptfocus(Widget w, Time *t) { + +} + +void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { + +} + +void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) { + +} + + + +XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply) { + GridRec *grid = (GridRec*)XtParent(widget); + GridConstraintRec *constraints = widget->core.constraints; + //XtVaSetValues(widget, XmNwidth, request->width, XmNheight, request->height, NULL); + if((request->request_mode & CWWidth) == CWWidth) { + widget->core.width = request->width; + constraints->grid.pref_width = request->width; + } + if((request->request_mode & CWHeight) == CWHeight) { + widget->core.height = request->height; + constraints->grid.pref_height = request->height; + } + grid_place_children((MyWidget)XtParent(widget)); + return XtGeometryYes; +} + +void GridChangeManaged(Widget widget) { + +} + +Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) { + GridConstraintRec *constraints = neww->core.constraints; + MyWidget grid = (MyWidget)XtParent(neww); + if(constraints->grid.x > grid->mywidget.max_col) { + grid->mywidget.max_col = constraints->grid.x; + } + if(constraints->grid.y > grid->mywidget.max_row) { + grid->mywidget.max_row = constraints->grid.y; + } +} + + +void grid_constraint_init( + Widget request, + Widget neww, + ArgList args, + Cardinal* num_args +) +{ + GridConstraintRec *constraints = neww->core.constraints; + + MyWidget grid = (MyWidget)XtParent(neww); + if(constraints->grid.x > grid->mywidget.max_col) { + grid->mywidget.max_col = constraints->grid.x; + } + if(constraints->grid.y > grid->mywidget.max_row) { + grid->mywidget.max_row = constraints->grid.y; + } + constraints->grid.pref_width = neww->core.width; + constraints->grid.pref_height = neww->core.height; +} + +void grid_place_children(MyWidget w) { + int ncols = w->mywidget.max_col+1; + int nrows = w->mywidget.max_row+1; + GridDef *cols = calloc(ncols, sizeof(GridDef)); + GridDef *rows = calloc(nrows, sizeof(GridDef)); + int num_cols_expanding = 0; + int num_rows_expanding = 0; + int req_width = 0; + int req_height = 0; + + // calculate the minimum size requirements for all columns and rows + // we need to run this 2 times: for widgets without colspan/rowspan first + // and then again for colspan/rowspan > 1 + int span_max = 1; + for(int r=0;r<2;r++) { + for(int i=0;icomposite.num_children;i++) { + Widget child = w->composite.children[i]; + GridConstraintRec *constraints = child->core.constraints; + + if(constraints->grid.colspan > span_max || constraints->grid.rowspan > span_max) { + continue; + } + + int x = constraints->grid.x; + int y = constraints->grid.y; + // make sure ncols/nrows is correct + // errors shouldn't happen, unless someone messes up the grid internals + if(x >= ncols) { + fprintf(stderr, "Error: widget x out of bounds\n"); + continue; + } + if(y >= nrows) { + fprintf(stderr, "Error: widget y out of bounds\n"); + continue; + } + GridDef *col = &cols[x]; + GridDef *row = &rows[y]; + + if(constraints->grid.hexpand) { + if(constraints->grid.colspan > 1) { + // check if any column in the span is expanding + // if not, make the last column expanding + GridDef *last_col = col; + for(int c=x;cexpand) { + break; + } + } + last_col->expand = TRUE; + } else { + col->expand = TRUE; + } + } + if(constraints->grid.vexpand) { + if(constraints->grid.rowspan > 1) { + GridDef *last_row = row; + for(int c=x;cexpand) { + break; + } + } + last_row->expand = TRUE; + } else { + row->expand = TRUE; + } + } + + // column size + if(constraints->grid.colspan > 1) { + // check size of all columns in span + Dimension span_width = col->size; + GridDef *last_col = col; + for(int s=x+1;ssize; + + } + int diff = constraints->grid.pref_width - span_width; + if(diff > 0) { + last_col->size += diff; + } + } else if(constraints->grid.pref_width > col->size) { + col->size = constraints->grid.pref_width; + } + // row size + if(constraints->grid.rowspan > 1) { + Dimension span_height = row->size; + GridDef *last_row = row; + for(int s=x+1;ssize; + + } + int diff = constraints->grid.pref_height - span_height; + if(diff > 0) { + last_row->size += diff; + } + } else if(constraints->grid.pref_height > row->size) { + row->size = constraints->grid.pref_height; + } + } + span_max = 50000; // not sure if this is unreasonable low or high + } + + + for(int i=0;i 0 && req_height > 0) { + Widget parent = w->core.parent; + Dimension rwidth = req_width; + Dimension rheight = req_height; + if(rwidth < w->core.width) { + //rwidth = w->core.width; + } + if(rheight < w->core.height) { + //rheight = w->core.height; + } + + if(!w->mywidget.sizerequest) { + Dimension actual_width, actual_height; + w->mywidget.sizerequest = TRUE; + XtMakeResizeRequest((Widget)w, req_width, req_height, &actual_width, &actual_height); + w->mywidget.sizerequest = FALSE; + //printf("size request: %d %d\n", (int)actual_width, (int)actual_height); + } + + + + } + + int hexpand = 0; + int width_diff = (int)w->core.width - req_width; + int hexpand2 = 0; + if(width_diff > 0 && num_cols_expanding > 0) { + hexpand = width_diff / num_cols_expanding; + hexpand2 = width_diff-hexpand*num_cols_expanding; + } + int x = 0; + for(int i=0;icore.height - req_height; + int vexpand2 = 0; + if(height_diff > 0 && num_rows_expanding > 0) { + vexpand = height_diff / num_rows_expanding; + vexpand2 = height_diff-vexpand*num_rows_expanding; + } + int y = 0; + for(int i=0;icomposite.num_children;i++) { + Widget child = w->composite.children[i]; + GridConstraintRec *constraints = child->core.constraints; + GridDef c = cols[constraints->grid.x]; + GridDef r = rows[constraints->grid.y]; + int x = c.pos; + int y = r.pos; + int width = constraints->grid.pref_width; + int height = constraints->grid.pref_height; + if(constraints->grid.hfill) { + if(constraints->grid.colspan > 1) { + Dimension cwidth = 0; + for(int j=0;jgrid.colspan;j++) { + if(constraints->grid.x+j < ncols) { + cwidth += cols[constraints->grid.x+j].size; + } + } + width = cwidth; + } else { + width = c.size; + } + } + if(constraints->grid.vfill) { + if(constraints->grid.rowspan > 1) { + Dimension cheight = 0; + for(int j=0;jgrid.rowspan;j++) { + if(constraints->grid.y+j < nrows) { + cheight += rows[constraints->grid.y+j].size; + } + } + height = cheight; + } else { + height = r.size; + } + } + + XtConfigureWidget(child, x, y, width, height, child->core.border_width); + //printf("child %d %d - %d %d\n", (int)child->core.x, (int)child->core.y, (int)child->core.width, (int)child->core.height); + } + + free(cols); + free(rows); +} + diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/Grid.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/Grid.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#ifndef GRID_H +#define GRID_H + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define gridColumn "gridColumn" +#define gridRow "gridRow" +#define gridColspan "gridColspan" +#define gridRowspan "gridRowspan" +#define gridHExpand "gridHExpand" +#define gridVExpand "gridVExpand" +#define gridHFill "gridHFill" +#define gridVFill "gridVFill" +#define gridMarginLeft "gridMarginLeft" +#define gridMarginRight "gridMarginRight" +#define gridMarginTop "gridMarginTop" +#define gridMarginBottom "gridMarginBottom" + + +typedef struct GridDef { + Dimension size; + Dimension pos; + Boolean expand; +} GridDef; + +typedef struct GridClassPart { + int test; +} GridClassPart; + +typedef struct GridClassRec { + CoreClassPart core_class; + CompositeClassPart composite_class; + ConstraintClassPart constraint_class; + XmManagerClassPart manager_class; + GridClassPart mywidgetclass; +} GridClassRec; + + +typedef struct GridPart { + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int max_col; + int max_row; + + Boolean sizerequest; +} GridPart; + +typedef struct GridRec { + CorePart core; + CompositePart composite; + ConstraintPart constraint; + XmManagerPart manager; + GridPart mywidget; +} GridRec; + +typedef struct GridContraintPart { + Dimension x; + Dimension y; + Dimension margin_left; + Dimension margin_right; + Dimension margin_top; + Dimension margin_bottom; + Boolean hexpand; + Boolean vexpand; + Boolean hfill; + Boolean vfill; + Dimension colspan; + Dimension rowspan; + Dimension pref_width; + Dimension pref_height; +} GridContraintPart; + +typedef struct GridConstraintRec { + XmManagerConstraintPart manager; + GridContraintPart grid; +} GridConstraintRec; + +typedef GridRec* MyWidget; + +extern WidgetClass gridClass; + +void grid_class_initialize(); +void grid_initialize(); +void grid_realize(); +void grid_destroy(); +void grid_resize(); +void grid_expose(); +Boolean grid_set_values(); +Boolean grid_acceptfocus(Widget , Time*); + +void grid_place_children(MyWidget w); + +void grid_getfocus(); +void grid_loosefocus(); + +void grid_constraint_init( + Widget request, + Widget neww, + ArgList args, + Cardinal* num_args +); + +XtGeometryResult GridGeometryManager(Widget widget, XtWidgetGeometry *request, XtWidgetGeometry *reply); +void GridChangeManaged(Widget widget); +Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args); + + +#ifdef __cplusplus +} +#endif + +#endif /* GRID_H */ + diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/button.c --- a/ui/motif/button.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/button.c Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -38,69 +38,54 @@ #include #include +#include -UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); + +UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { + Arg xargs[16]; + int n = 0; - int n = 0; - Arg args[16]; + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - XtSetArg(args[n], XmNlabelString, str); - n++; + Widget parent = ctn->prepare(ctn, xargs, &n); - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget button = XmCreatePushButton(parent, "button", args, n); - ct->add(ct, button); + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } - if(f) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = data; - event->callback = f; - event->value = 0; + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreatePushButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + if(args.onclick) { + UiEventData *eventdata = malloc(sizeof(UiEventData)); + eventdata->callback = args.onclick; + eventdata->userdata = args.onclickdata; + eventdata->obj = obj; + eventdata->value = 0; XtAddCallback( button, XmNactivateCallback, (XtCallbackProc)ui_push_button_callback, - event); + eventdata); + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + eventdata); } - XtManageChild(button); + XmStringFree(label); return button; } -// wrapper -int64_t ui_toggle_button_get(UiInteger *i) { - int state = 0; - XtVaGetValues(i->obj, XmNset, &state, NULL); - i->value = state; - return state; -} - -void ui_toggle_button_set(UiInteger *i, int64_t value) { - Arg arg; - XtSetArg(arg, XmNset, value); - XtSetValues(i->obj, &arg, 1); - i->value = value; -} - -void ui_toggle_button_callback( - Widget widget, - UiEventData *event, - XmToggleButtonCallbackStruct *tb) -{ - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - // TODO: e.document - e.intval = tb->set; - event->callback(&e, event->userdata); -} - void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) { UiEvent e; e.obj = event->obj; @@ -110,105 +95,295 @@ event->callback(&e, event->userdata); } +UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + XtSetArg(xargs[n], XmNfillOnSelect, True); n++; + XtSetArg(xargs[n], XmNindicatorOn, False); n++; + + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "togglebutton"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); + + XmStringFree(label); + return button; +} -static void radio_callback( - Widget widget, - RadioEventData *event, - XmToggleButtonCallbackStruct *tb) -{ - if(tb->set) { - RadioButtonGroup *group = event->group; - if(group->current) { - Arg arg; - XtSetArg(arg, XmNset, FALSE); - XtSetValues(group->current, &arg, 1); +UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); + + ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); + + XmStringFree(label); + return button; +} + +UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { + return ui_checkbox_create(obj, args); +} + +static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { + if(event->value > 0) { + // button in configured to enable/disable states + if(tb->set) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); } - group->current = widget; + } + + UiEvent e; + e.obj = event->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = NULL; + e.intval = XmToggleButtonGetState(w); + + if(event->callback) { + event->callback(&e, event->userdata); + } + + if(event->var && event->var->value) { + UiInteger *v = event->var->value; + v->value = e.intval; + ui_notify_evt(v->observers, &e); } } -UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); - - int n = 0; - Arg args[16]; - - XtSetArg(args[n], XmNlabelString, str); - n++; - XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); - n++; - - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget button = XmCreateToggleButton(parent, "radiobutton", args, n); - ct->add(ct, button); - - if(rgroup) { - RadioButtonGroup *group; - if(rgroup->obj) { - group = rgroup->obj; - if(!group->buttons) { - group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8); - } - cxListAdd(group->buttons, button); - group->ref++; - } else { - group = malloc(sizeof(RadioButtonGroup)); - group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8); - cxListAdd(group->buttons, button); - group->current = button; - // this is the first button in the radiobutton group - // so we should enable it - Arg arg; - XtSetArg(arg, XmNset, TRUE); - XtSetValues(button, &arg, 1); - rgroup->obj = group; - - group->current = button; +void ui_bind_togglebutton( + UiObject *obj, + Widget widget, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state) +{ + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER); + if(var) { + value = (UiInteger*)var->value; + value->obj = widget; + value->get = ui_togglebutton_get; + value->set = ui_togglebutton_set; + + if(value->value) { + XmToggleButtonSetState(widget, True, False); } - - RadioEventData *event = malloc(sizeof(RadioEventData)); - event->obj = obj; - event->callback = NULL; - event->userdata = NULL; - event->group = group; - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)radio_callback, - event); - - rgroup->get = ui_radiobutton_get; - rgroup->set = ui_radiobutton_set; } - XtManageChild(button); - return button; + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = onchange; + event->userdata = onchangedata; + event->var = var; + event->observers = NULL; + event->value = enable_state; + XtAddCallback( + widget, + XmNvalueChangedCallback, + (XtCallbackProc)togglebutton_changed, + event); + XtAddCallback( + widget, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + event); +} + +int64_t ui_togglebutton_get(UiInteger *i) { + Widget togglebutton = i->obj; + Boolean state = XmToggleButtonGetState(togglebutton); + i->value = state; + return state; +} + +void ui_togglebutton_set(UiInteger *i, int64_t value) { + Widget togglebutton = i->obj; + i->value = value; + XmToggleButtonSetState(togglebutton, (Boolean)value, False); +} + +static void destroy_list(Widget w, CxList *list, XtPointer d) { + cxListDestroy(list); } -int64_t ui_radiobutton_get(UiInteger *value) { - RadioButtonGroup *group = value->obj; +static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { + if(event->value > 0) { + // button in configured to enable/disable states + if(tb->set) { + ui_set_group(event->obj->ctx, event->value); + } else { + ui_unset_group(event->obj->ctx, event->value); + } + } + + if(!tb->set) { + return; // only handle set-events + } - int i = cxListFind(group->buttons, group->current); - if (i >= 0) { - value->value = i; - return i; - } else { - return 0; + UiInteger *value = NULL; + int64_t v = 0; + if(event->var) { + value = event->var->value; + // find widget index and update all radiobuttons + // the UiInteger value must always be up-to-date + CxList *list = value->obj; + CxIterator i = cxListIterator(list); + cx_foreach(Widget, button, i) { + Boolean state = False; + if(button == w) { + value->value = i.index+1; // update value + state = True; + } + XmToggleButtonSetState(button, state, False); + } + v = value->value; + } + + UiEvent e; + e.obj = event->obj; + e.window = e.obj->window; + e.document = e.obj->ctx->document; + e.eventdata = value; + e.intval = v; + + if(event->callback) { + event->callback(&e, event->userdata); + } + + if(value) { + ui_notify_evt(value->observers, &e); } } -void ui_radiobutton_set(UiInteger *value, int64_t i) { - RadioButtonGroup *group = value->obj; - Arg arg; +UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + Widget parent = ctn->prepare(ctn, xargs, &n); + XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++; + XmString label = NULL; + if(args.label) { + label = XmStringCreateLocalized((char*)args.label); + XtSetArg(xargs[n], XmNlabelString, label); n++; + } + + char *name = args.name ? (char*)args.name : "button"; + Widget button = XmCreateToggleButton(parent, name, xargs, n); + XtManageChild(button); + ctn->add(ctn, button); + + ui_set_widget_groups(obj->ctx, button, args.groups); - XtSetArg(arg, XmNset, FALSE); - XtSetValues(group->current, &arg, 1); + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER); + if(var) { + UiInteger *value = var->value; + CxList *rb = value->obj; + if(!rb) { + // first button in the radiobutton group + // create a list for all buttons and use the list as value obj + rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); + value->obj = rb; + value->get = ui_radiobutton_get; + value->set = ui_radiobutton_set; + + // the first radio button is also responsible for cleanup + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)destroy_list, + rb); + } + cxListAdd(rb, button); + + // set the radiobutton state, if the value is already set + if(cxListSize(rb) == value->value) { + XmToggleButtonSetState(button, True, False); + } + } - Widget button = cxListAt(group->buttons, i); - if(button) { - XtSetArg(arg, XmNset, TRUE); - XtSetValues(button, &arg, 1); - group->current = button; + // the radio button needs to handle change events to update all + // other buttons in the radio button group + UiVarEventData *event = malloc(sizeof(UiVarEventData)); + event->obj = obj; + event->callback = args.onchange; + event->userdata = args.onchangedata; + event->observers = NULL; + event->var = var; + event->value = args.enable_group; + XtAddCallback( + button, + XmNvalueChangedCallback, + (XtCallbackProc)radiobutton_changed, + event); + XtAddCallback( + button, + XmNdestroyCallback, + (XtCallbackProc)ui_destroy_eventdata, + event); + + XmStringFree(label); + return button; + + +} + +int64_t ui_radiobutton_get(UiInteger *i) { + // the UiInteger should be updated automatically by change events + return i->value; +} + +void ui_radiobutton_set(UiInteger *i, int64_t value) { + CxList *list = i->obj; + if(i->value > 0) { + Widget current = cxListAt(list, i->value-1); + if(current) { + XmToggleButtonSetState(current, False, False); + } + } + if(value > 0 && value <= cxListSize(list)) { + Widget button = cxListAt(list, value-1); + if(button) { + XmToggleButtonSetState(button, True, False); + i->value = value; + } } } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/button.h --- a/ui/motif/button.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/button.h Thu Dec 12 20:01:43 2024 +0100 @@ -36,30 +36,22 @@ extern "C" { #endif -typedef struct { - CxList *buttons; - Widget current; - int ref; -} RadioButtonGroup; - -typedef struct { - UiObject *obj; - ui_callback callback; - void *userdata; - RadioButtonGroup *group; -} RadioEventData; - -// wrapper -int64_t ui_toggle_button_get(UiInteger *i); -void ui_toggle_button_set(UiInteger *i, int64_t value); -void ui_toggle_button_callback( - Widget widget, - UiEventData *data, - XmToggleButtonCallbackStruct *e); void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d); -int64_t ui_radiobutton_get(UiInteger *value); -void ui_radiobutton_set(UiInteger *value, int64_t i); +void ui_bind_togglebutton( + UiObject *obj, + Widget widget, + const char *varname, + UiInteger *value, + ui_callback onchange, + void *onchangedata, + int enable_state); + +int64_t ui_togglebutton_get(UiInteger *i); +void ui_togglebutton_set(UiInteger *i, int64_t value); + +int64_t ui_radiobutton_get(UiInteger *i); +void ui_radiobutton_set(UiInteger *i, int64_t value); #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/container.c --- a/ui/motif/container.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/container.c Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -34,751 +34,189 @@ #include "../common/context.h" #include "../common/object.h" -#include -#include -#include - -#define UI_GRID_MAX_COLUMNS 512 - -static UiBool ui_lb2bool(UiLayoutBool b) { - return b == UI_LAYOUT_TRUE ? TRUE : FALSE; -} - -static UiLayoutBool ui_bool2lb(UiBool b) { - return b ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE; -} - +#include "Grid.h" -UiContainer* ui_frame_container(UiObject *obj, Widget frame) { - UiContainer *ct = cxCalloc( - obj->ctx->allocator, - 1, - sizeof(UiContainer)); - ct->widget = frame; - ct->prepare = ui_frame_container_prepare; - ct->add = ui_frame_container_add; - return ct; -} - -Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - return ct->widget; -} - -void ui_frame_container_add(UiContainer *ct, Widget widget) { - ui_reset_layout(ct->layout); - ct->current = widget; -} - +/* ---------------------------- Box Container ---------------------------- */ -UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation) { - UiBoxContainer *ct = cxCalloc( - obj->ctx->allocator, - 1, - sizeof(UiBoxContainer)); - ct->container.widget = box; - ct->container.prepare = ui_box_container_prepare; - ct->container.add = ui_box_container_add; - ct->orientation = orientation; - ct->margin = margin; - ct->spacing = spacing; - return (UiContainer*)ct; -} - -Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - UiBoxContainer *bc = (UiBoxContainer*)ct; - if(ct->layout.fill != UI_LAYOUT_UNDEFINED) { - fill = ui_lb2bool(ct->layout.fill); - } +static UIWIDGET box_create(UiObject *obj, UiContainerArgs args, UiBoxOrientation orientation) { + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - if(bc->has_fill && fill) { - fprintf(stderr, "UiError: container has 2 filled widgets"); - fill = FALSE; - } - if(fill) { - bc->has_fill = TRUE; - } + Arg xargs[16]; + int n = 0; - int a = *n; - // determine fixed and dynamic attachments - void *f1; - void *f2; - void *d1; - void *d2; - void *w1; - void *w2; - if(bc->orientation == UI_BOX_VERTICAL) { - f1 = XmNleftAttachment; - f2 = XmNrightAttachment; - d1 = XmNtopAttachment; - d2 = XmNbottomAttachment; - w1 = XmNtopWidget; - w2 = XmNbottomWidget; - - // margin/spacing - XtSetArg(args[a], XmNleftOffset, bc->margin); a++; - XtSetArg(args[a], XmNrightOffset, bc->margin); a++; - - XtSetArg(args[a], XmNtopOffset, bc->prev_widget ? bc->spacing : bc->margin); a++; + if(orientation == UI_BOX_VERTICAL) { + //XtSetArg(xargs[n], gridRowSpacing, args.spacing); n++; } else { - f1 = XmNtopAttachment; - f2 = XmNbottomAttachment; - d1 = XmNleftAttachment; - d2 = XmNrightAttachment; - w1 = XmNleftWidget; - w2 = XmNrightWidget; - - // margin/spacing - XtSetArg(args[a], XmNtopOffset, bc->margin); a++; - XtSetArg(args[a], XmNbottomOffset, bc->margin); a++; - - XtSetArg(args[a], XmNleftOffset, bc->prev_widget ? bc->spacing : bc->margin); a++; - } - XtSetArg(args[a], f1, XmATTACH_FORM); a++; - XtSetArg(args[a], f2, XmATTACH_FORM); a++; - - if(fill) { - XtSetArg(args[a], d2, XmATTACH_FORM); a++; - } - if(bc->prev_widget) { - XtSetArg(args[a], d1, XmATTACH_WIDGET); a++; - XtSetArg(args[a], w1, bc->prev_widget); a++; - } else { - XtSetArg(args[a], d1, XmATTACH_FORM); a++; - } - - *n = a; - return ct->widget; -} - -void ui_box_container_add(UiContainer *ct, Widget widget) { - UiBoxContainer *bc = (UiBoxContainer*)ct; - // determine dynamic attachments - void *d1; - void *d2; - void *w1; - void *w2; - if(bc->orientation == UI_BOX_VERTICAL) { - d1 = XmNtopAttachment; - d2 = XmNbottomAttachment; - w1 = XmNtopWidget; - w2 = XmNbottomWidget; - - } else { - d1 = XmNleftAttachment; - d2 = XmNrightAttachment; - w1 = XmNleftWidget; - w2 = XmNrightWidget; - } - - if(bc->prev_widget) { - int v = 0; - XtVaGetValues(bc->prev_widget, d2, &v, NULL); - if(v == XmATTACH_FORM) { - XtVaSetValues( - bc->prev_widget, - d2, - XmATTACH_WIDGET, - w2, - widget, - NULL); - XtVaSetValues( - widget, - d1, - XmATTACH_NONE, - d2, - XmATTACH_FORM, - NULL); - } - } - bc->prev_widget = widget; - - ui_reset_layout(ct->layout); - ct->current = widget; -} - -UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing) { - UiGridContainer *ct = cxCalloc( - obj->ctx->allocator, - 1, - sizeof(UiGridContainer)); - ct->container.widget = form; - ct->container.prepare = ui_grid_container_prepare; - ct->container.add = ui_grid_container_add; - ct->columnspacing = columnspacing; - ct->rowspacing = rowspacing; - ct->lines = cxLinkedListCreateSimple(CX_STORE_POINTERS); - return (UiContainer*)ct; -} - -void ui_grid_newline(UiGridContainer *grid) { - if(grid->current) { - grid->current = NULL; - } - grid->container.layout.newline = FALSE; -} - -Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - UiGridContainer *grid = (UiGridContainer*)ct; - if(ct->layout.newline) { - ui_grid_newline(grid); - } - return ct->widget; -} - -void ui_grid_container_add(UiContainer *ct, Widget widget) { - UiGridContainer *grid = (UiGridContainer*)ct; - - if(grid->current) { - cxListAdd(grid->current, widget); - } else { - grid->current = cxLinkedListCreateSimple(CX_STORE_POINTERS); - cxListAdd(grid->current, widget); - cxListAdd(grid->lines, grid->current); + //XtSetArg(xargs[n], gridColumnSpacing, args.spacing); n++; } - ui_reset_layout(ct->layout); - ct->current = widget; -} - -static void ui_grid_resize(Widget widget, XtPointer udata, XtPointer cdata) { - UiGridContainer *grid = udata; - - CxList *rowdim = cxArrayListCreateSimple(sizeof(int), grid->lines->size); - int coldim[UI_GRID_MAX_COLUMNS]; - memset(coldim, 0, UI_GRID_MAX_COLUMNS*sizeof(int)); - int numcol = 0; - - // get the minimum size of the columns and rows - int sumw = 0; - int sumh = 0; - CxIterator lineIterator = cxListIterator(grid->lines); - cx_foreach(CxList *, row, lineIterator) { - int rheight = 0; - int i=0; - int sum_width = 0; - CxIterator colIterator = cxListIterator(row); - cx_foreach(Widget, w, colIterator) { - int widget_width = 0; - int widget_height = 0; - XtVaGetValues( - w, - XmNwidth, - &widget_width, - XmNheight, - &widget_height, - NULL); - - // get the maximum height in this row - if(widget_height > rheight) { - rheight = widget_height; - } - - // get the maximum width in this column - if(widget_width > coldim[i]) { - coldim[i] = widget_width; - } - sum_width += widget_width; - if(sum_width > sumw) { - sumw = sum_width; - } - - i++; - if(i > numcol) { - numcol = i; - } - } - cxListAdd(rowdim, &rheight); - sumh += rheight; - } - - // check container size - int gwidth = 0; - int gheight = 0; - XtVaGetValues(widget, XmNwidth, &gwidth, XmNheight, &gheight, NULL); - if(gwidth < sumw || gheight < sumh) { - XtVaSetValues(widget, XmNwidth, sumw, XmNheight, sumh, NULL); - cxListDestroy(rowdim); - return; - } - - - // adjust the positions of all children - int y = 0; - lineIterator = cxListIterator(grid->lines); - cx_foreach(CxList *, row, lineIterator) { - int x = 0; - int i=0; - int *rowheight = cxListAt(rowdim, lineIterator.index); - CxIterator colIterator = cxListIterator(row); - cx_foreach(Widget, w, colIterator) { - XtVaSetValues( - w, - XmNx, x, - XmNy, y, - XmNwidth, coldim[i], - XmNheight, *rowheight, - NULL); - - x += coldim[i]; - i++; - } - y += *rowheight; - } + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget grid = XtCreateManagedWidget(args.name ? args.name : "boxcontainer", gridClass, parent, xargs, n); + ctn->add(ctn, grid); - cxListDestroy(rowdim); -} - -UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow) { - UiContainer *ct = cxCalloc( - obj->ctx->allocator, - 1, - sizeof(UiContainer)); - ct->widget = scrolledwindow; - ct->prepare = ui_scrolledwindow_container_prepare; - ct->add = ui_scrolledwindow_container_add; - return ct; -} - -Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - return ct->widget; -} - -void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget) { - ui_reset_layout(ct->layout); - ct->current = widget; -} - - -UiContainer* ui_tabview_container(UiObject *obj, Widget frame) { - UiTabViewContainer *ct = cxCalloc( - obj->ctx->allocator, - 1, - sizeof(UiTabViewContainer)); - ct->context = obj->ctx; - ct->container.widget = frame; - ct->container.prepare = ui_tabview_container_prepare; - ct->container.add = ui_tabview_container_add; - ct->tabs = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 16); - return (UiContainer*)ct; -} - -Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill) { - int a = *n; - XtSetArg(args[a], XmNleftAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNrightAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNtopAttachment, XmATTACH_FORM); a++; - XtSetArg(args[a], XmNbottomAttachment, XmATTACH_FORM); a++; - *n = a; - return ct->widget; -} - -void ui_tabview_container_add(UiContainer *ct, Widget widget) { - UiTabViewContainer *tabview = (UiTabViewContainer*)ct; - - if(tabview->current) { - XtUnmanageChild(tabview->current); - } - - tabview->current = widget; - cxListAdd(tabview->tabs, widget); - - ui_select_tab(ct->widget, 0); - ui_reset_layout(ct->layout); - ct->current = widget; -} - -UIWIDGET ui_box(UiObject *obj, int margin, int spacing, UiBoxOrientation orientation) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget form = XmCreateForm(parent, "vbox", args, n); - ct->add(ct, form); - XtManageChild(form); - - UiObject *newobj = uic_object_new(obj, form); - newobj->container = ui_box_container(obj, form, margin, spacing, orientation); - uic_obj_add(obj, newobj); - - return form; -} - -UIWIDGET ui_vbox(UiObject *obj) { - return ui_box(obj, 0, 0, UI_BOX_VERTICAL); -} - -UIWIDGET ui_hbox(UiObject *obj) { - return ui_box(obj, 0, 0, UI_BOX_HORIZONTAL); -} - -UIWIDGET ui_vbox_sp(UiObject *obj, int margin, int spacing) { - return ui_box(obj, margin, spacing, UI_BOX_VERTICAL); -} - -UIWIDGET ui_hbox_sp(UiObject *obj, int margin, int spacing) { - return ui_box(obj, margin, spacing, UI_BOX_HORIZONTAL); -} - -UIWIDGET ui_grid(UiObject *obj) { - return ui_grid_sp(obj, 0, 0, 0); -} - -UIWIDGET ui_grid_sp(UiObject *obj, int margin, int columnspacing, int rowspacing) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget grid = XmCreateDrawingArea(parent, "grid", args, n); - ct->add(ct, grid); - XtManageChild(grid); - - UiObject *newobj = uic_object_new(obj, grid); - newobj->container = ui_grid_container(obj, grid, columnspacing, rowspacing); - uic_obj_add(obj, newobj); - - XtAddCallback (grid, XmNresizeCallback , ui_grid_resize, newobj->container); + UiContainerX *container = ui_box_container(obj, grid, orientation); + uic_object_push_container(obj, container); return grid; } -UIWIDGET ui_scrolledwindow(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); // TODO: dosn't work, use XmAPPLICATION_DEFINED - n++; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget scrolledwindow = XmCreateScrolledWindow(parent, "scrolledwindow", args, n); - ct->add(ct, scrolledwindow); - XtManageChild(scrolledwindow); - - UiObject *newobj = uic_object_new(obj, scrolledwindow); - newobj->container = ui_scrolledwindow_container(obj, scrolledwindow); - uic_obj_add(obj, newobj); - - return scrolledwindow; +// public +UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args) { + return box_create(obj, args, UI_BOX_VERTICAL); +} + +// public +UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args) { + return box_create(obj, args, UI_BOX_HORIZONTAL); } -UIWIDGET ui_sidebar(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - Arg args[16]; - int n = 0; - - XtSetArg(args[n], XmNorientation, XmHORIZONTAL); - n++; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget pane = XmCreatePanedWindow(parent, "pane", args, n); - ct->add(ct, pane); - XtManageChild(pane); - - // add sidebar widget - Widget sidebar = XmCreateForm(pane, "sidebar", args, 0); - XtManageChild(sidebar); - - UiObject *left = uic_object_new(obj, sidebar); - left->container = ui_box_container(left, sidebar, 0, 0, UI_BOX_VERTICAL); - - // add content widget - XtSetArg (args[0], XmNpaneMaximum, 8000); - Widget content = XmCreateForm(pane, "content_area", args, 1); - XtManageChild(content); - - UiObject *right = uic_object_new(obj, content); - right->container = ui_box_container(right, content, 0, 0, UI_BOX_VERTICAL); - - uic_obj_add(obj, right); - uic_obj_add(obj, left); - - return sidebar; +UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation) { + UiBoxContainer *ctn = ui_malloc(obj->ctx, sizeof(UiBoxContainer)); + memset(ctn, 0, sizeof(UiBoxContainer)); + ctn->container.prepare = orientation == UI_BOX_VERTICAL ? ui_vbox_prepare : ui_hbox_prepare; + ctn->container.add = ui_box_container_add; + ctn->container.widget = grid; + ctn->n = 0; + return (UiContainerX*)ctn; } -UIWIDGET ui_tabview(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - - // create a simple frame as container widget - // when tabs are selected, the current child will be replaced by the - // the new tab widget - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_OUT); - n++; - XtSetArg(args[n], XmNshadowThickness, 0); - n++; - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget form = XmCreateForm(parent, "tabview", args, n); - ct->add(ct, form); - XtManageChild(form); - - UiObject *tabviewobj = uic_object_new(obj, form); - tabviewobj->container = ui_tabview_container(obj, form); - uic_obj_add(obj, tabviewobj); - - XtVaSetValues(form, XmNuserData, tabviewobj->container, NULL); - - return form; +static Widget ui_box_container_prepare(UiBoxContainer *box, Arg *args, int *n) { + int a = *n; + box->n++; + return box->container.widget; } -void ui_tab(UiObject *obj, char *title) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.label = title; - - ui_vbox(obj); -} - -void ui_select_tab(UIWIDGET tabview, int tab) { - UiTabViewContainer *ct = NULL; - XtVaGetValues(tabview, XmNuserData, &ct, NULL); - if(ct) { - XtUnmanageChild(ct->current); - Widget w = cxListAt(ct->tabs, tab); - if(w) { - XtManageChild(w); - ct->current = w; - } else { - fprintf(stderr, "UiError: front tab index: %d\n", tab); - } - } else { - fprintf(stderr, "UiError: widget is not a tabview\n"); +Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiBoxContainer *box = (UiBoxContainer*)ctn; + int a = *n; + XtSetArg(args[a], gridRow, box->n); a++; + if(box->container.layout.fill == UI_ON) { + XtSetArg(args[a], gridVExpand, TRUE); a++; + XtSetArg(args[a], gridVFill, TRUE); a++; } + XtSetArg(args[a], gridHExpand, TRUE); a++; + XtSetArg(args[a], gridHFill, TRUE); a++; + *n = a; + return ui_box_container_prepare(box, args, n); } - -/* document tabview */ - -static void ui_tabbar_resize(Widget widget, XtPointer udata, XtPointer cdata) { - MotifTabbedPane *v = (MotifTabbedPane*)udata; - - int width = 0; - int height = 0; - XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL); - int button_width = width / 4; - int x = 0; - CxIterator tabIterator = cxListIterator(v->tabs); - cx_foreach(UiTab*, tab, tabIterator) { - XtVaSetValues( - tab->tab_button, - XmNx, x, - XmNy, 0, - XmNwidth, - button_width, - - NULL); - x += button_width; +Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiBoxContainer *box = (UiBoxContainer*)ctn; + int a = *n; + XtSetArg(args[a], gridColumn, box->n); a++; + if(box->container.layout.fill == UI_ON) { + XtSetArg(args[a], gridHExpand, TRUE); a++; + XtSetArg(args[a], gridHFill, TRUE); a++; } - - if(height <= v->height) { - XtVaSetValues(widget, XmNheight, v->height + 4, NULL); - } + XtSetArg(args[a], gridVExpand, TRUE); a++; + XtSetArg(args[a], gridVFill, TRUE); a++; + *n = a; + return ui_box_container_prepare(box, args, n); } -static void ui_tabbar_expose(Widget widget, XtPointer udata, XtPointer cdata) { - MotifTabbedPane *v = (MotifTabbedPane*)udata; - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)cdata; - XEvent *event = cbs->event; - Display *dpy = XtDisplay(widget); - - XGCValues gcvals; - GC gc; - Pixel fgpix; - - int tab_x; - int tab_width; - XtVaGetValues(v->current->tab_button, XmNx, &tab_x, XmNwidth, &tab_width, XmNhighlightColor, &fgpix, NULL); - - gcvals.foreground = v->bg1; - gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals); - - int width = 0; - int height = 0; - XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, NULL); - XFillRectangle(dpy, XtWindow(widget), gc, 0, 0, width, height); - - gcvals.foreground = fgpix; - gc = XCreateGC( dpy, XtWindow(widget), (GCForeground), &gcvals); - - XFillRectangle(dpy, XtWindow(widget), gc, tab_x, 0, tab_width, height); +void ui_box_container_add(UiContainerPrivate *ctn, Widget widget) { + ui_reset_layout(ctn->layout); } -UiTabbedPane* ui_tabbed_document_view(UiObject *obj) { + +/* ---------------------------- Grid Container ---------------------------- */ + +// public +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs args) { + Arg xargs[16]; int n = 0; - Arg args[16]; - - UiContainer *ct = uic_get_current_container(obj); - Widget parent = ct->prepare(ct, args, &n, TRUE); - - Widget tabview = XmCreateForm(parent, "tabview_form", args, n); - XtManageChild(tabview); - XtSetArg(args[0], XmNorientation, XmHORIZONTAL); - XtSetArg(args[1], XmNpacking, XmPACK_TIGHT); - XtSetArg(args[2], XmNspacing, 1); - XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[6], XmNmarginWidth, 0); - XtSetArg(args[7], XmNmarginHeight, 0); - Widget tabbar = XmCreateDrawingArea(tabview, "tabbar", args, 8); - XtManageChild(tabbar); + XtSetArg(xargs[n], XmNbackground, 0); n++; - XtSetArg(args[0], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[1], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[2], XmNtopAttachment, XmATTACH_WIDGET); - XtSetArg(args[3], XmNtopWidget, tabbar); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNshadowThickness, 0); - Widget tabct = XmCreateForm(tabview, "tabview", args, 6); - XtManageChild(tabct); + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); - MotifTabbedPane *tabbedpane = ui_malloc(obj->ctx, sizeof(MotifTabbedPane)); - tabbedpane->view.ctx = uic_current_obj(obj)->ctx; - tabbedpane->view.widget = tabct; - tabbedpane->view.document = NULL; - tabbedpane->tabbar = tabbar; - tabbedpane->tabs = cxArrayListCreate(obj->ctx->allocator, cx_cmp_uintptr, CX_STORE_POINTERS, 16); - tabbedpane->current = NULL; - tabbedpane->height = 0; + Widget parent = ctn->prepare(ctn, xargs, &n); + Widget grid = XtCreateManagedWidget(args.name ? args.name : "gridcontainer", gridClass, parent, xargs, n); + ctn->add(ctn, grid); - XtAddCallback(tabbar, XmNresizeCallback , ui_tabbar_resize, tabbedpane); - XtAddCallback(tabbar, XmNexposeCallback, ui_tabbar_expose, tabbedpane); + UiContainerX *container = ui_grid_container(obj, grid); + uic_object_push_container(obj, container); - return &tabbedpane->view; + return grid; } -UiObject* ui_document_tab(UiTabbedPane *view) { - MotifTabbedPane *v = (MotifTabbedPane*)view; - int n = 0; - Arg args[16]; - - // hide the current tab content - if(v->current) { - XtUnmanageChild(v->current->content->widget); +UiContainerX* ui_grid_container(UiObject *obj, Widget grid) { + UiGridContainer *ctn = ui_malloc(obj->ctx, sizeof(UiGridContainer)); + memset(ctn, 0, sizeof(UiBoxContainer)); + ctn->container.prepare = ui_grid_container_prepare; + ctn->container.add = ui_grid_container_add; + ctn->container.widget = grid; + ctn->x = 0; + ctn->y = 0; + return (UiContainerX*)ctn; +} + +Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n) { + UiGridContainer *grid = (UiGridContainer*)ctn; + if(ctn->layout.newline) { + grid->y++; + grid->x = 0; } - UiTab *tab = ui_malloc(view->ctx, sizeof(UiTab)); - - // create the new tab content - XtSetArg(args[0], XmNshadowThickness, 0); - XtSetArg(args[1], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[2], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - XtSetArg(args[5], XmNuserData, tab); - Widget frame = XmCreateFrame(view->widget, "tab", args, 6); - XtManageChild(frame); - - UiObject *content = ui_malloc(view->ctx, sizeof(UiObject)); - content->widget = NULL; // initialization for uic_context() - content->ctx = uic_context(content, view->ctx->mp); - content->ctx->parent = view->ctx; - content->ctx->attach_document = uic_context_attach_document; - content->ctx->detach_document2 = uic_context_detach_document2; - content->widget = frame; - content->window = view->ctx->obj->window; - content->container = ui_frame_container(content, frame); - content->next = NULL; - - // add tab button - cxListAdd(v->tabs, tab); + int a = *n; + XtSetArg(args[a], gridColumn, grid->x); a++; + XtSetArg(args[a], gridRow, grid->y); a++; + if(ctn->layout.colspan > 0) { + XtSetArg(args[a], gridColspan, ctn->layout.colspan); a++; + } + if(ctn->layout.rowspan > 0) { + XtSetArg(args[a], gridRowspan, ctn->layout.rowspan); a++; + } - XmString label = XmStringCreateLocalized("tab"); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 0); - XtSetArg(args[2], XmNhighlightThickness, 0); - - Widget button = XmCreatePushButton(v->tabbar, "tab_button", args, 3); - tab->tabbedpane = v; - tab->content = content; - tab->tab_button = button; - XtManageChild(button); - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_tab_button_callback, - tab); - - if(v->height == 0) { - XtVaGetValues( - button, - XmNarmColor, - &v->bg1, - XmNbackground, - &v->bg2, - XmNheight, - &v->height, - NULL); - v->height += 2; // border + if(grid->container.layout.fill == UI_ON) { + grid->container.layout.hfill = TRUE; + grid->container.layout.vfill = TRUE; + grid->container.layout.hexpand = TRUE; + grid->container.layout.vexpand = TRUE; } - ui_change_tab(v, tab); - ui_tabbar_resize(v->tabbar, v, NULL); + if(grid->container.layout.hfill) { + XtSetArg(args[a], gridHFill, TRUE); a++; + } + if(grid->container.layout.vfill) { + XtSetArg(args[a], gridVFill, TRUE); a++; + } + if(grid->container.layout.hexpand) { + XtSetArg(args[a], gridHExpand, TRUE); a++; + } + if(grid->container.layout.vexpand) { + XtSetArg(args[a], gridVExpand, TRUE); a++; + } - return content; -} - -void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d) { - MotifTabbedPane *t = tab->tabbedpane; - if(t->current) { - XtUnmanageChild(t->current->content->widget); - XtVaSetValues(t->current->tab_button, XmNset, 0, NULL); - } - XtManageChild(tab->content->widget); - - ui_change_tab(t, tab); - + *n = a; + return ctn->widget; } -void ui_change_tab(MotifTabbedPane *pane, UiTab *tab) { - UiContext *ctx = tab->content->ctx; - ctx->parent->detach_document2(ctx->parent, pane->current->content->ctx->document); - ctx->parent->attach_document(ctx->parent, ctx->document); - - if(pane->current) { - XtVaSetValues(pane->current->tab_button, XmNshadowThickness, 0, XmNbackground, pane->bg1, NULL); - } - XtVaSetValues(tab->tab_button, XmNshadowThickness, 1, XmNbackground, pane->bg2, NULL); - - pane->current = tab; - pane->index = cxListFind(pane->tabs, tab); - printf("index: %d\n", pane->index); - - // redraw tabbar - Display *dpy = XtDisplay(pane->tabbar); - Window window = XtWindow(pane->tabbar); - if(dpy && window) { - XClearArea(dpy, XtWindow(pane->tabbar), 0, 0, 0, 0, TRUE); - XFlush(dpy); - } +void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget) { + UiGridContainer *grid = (UiGridContainer*)ctn; + grid->x++; + ui_reset_layout(ctn->layout); } -void ui_tab_set_document(UiContext *ctx, void *document) { - if(ctx->parent->document) { - //ctx->parent->detach_document(ctx->parent, ctx->parent->document); - } - uic_context_attach_document(ctx, document); - //uic_context_set_document(ctx->parent, document); - //ctx->parent->document = document; - - UiTab *tab = NULL; - XtVaGetValues( - ctx->obj->widget, - XmNuserData, - &tab, - NULL); - if(tab) { - if(tab->tabbedpane->current == tab) { - ctx->parent->attach_document(ctx->parent, ctx->document); - } - } else { - fprintf(stderr, "UiError: ui_bar_set_document: Cannot set document"); - } + +/* -------------------- Container Helper Functions -------------------- */ + +void ui_container_begin_close(UiObject *obj) { + UiContainerPrivate *ct = ui_obj_container(obj); + ct->container.close = 1; } +int ui_container_finish(UiObject *obj) { + UiContainerPrivate *ct = ui_obj_container(obj); + if(ct->container.close) { + ui_end_new(obj); + return 0; + } + return 1; +} /* @@ -788,27 +226,7 @@ * */ -void ui_layout_fill(UiObject *obj, UiBool fill) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.fill = ui_bool2lb(fill); -} - -void ui_layout_hexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.hexpand = expand; -} - -void ui_layout_vexpand(UiObject *obj, UiBool expand) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.vexpand = expand; -} - -void ui_layout_gridwidth(UiObject *obj, int width) { - UiContainer *ct = uic_get_current_container(obj); - ct->layout.gridwidth = width; -} - void ui_newline(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); + UiContainerPrivate *ct = ui_obj_container(obj); ct->layout.newline = TRUE; } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/container.h --- a/ui/motif/container.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/container.h Thu Dec 12 20:01:43 2024 +0100 @@ -37,26 +37,37 @@ #ifdef __cplusplus extern "C" { #endif - -#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) -typedef struct MotifTabbedPane MotifTabbedPane; -typedef struct UiTab UiTab; -typedef struct UiBoxContainer UiBoxContainer; -typedef struct UiGridContainer UiGridContainer; -typedef struct UiTabViewContainer UiTabViewContainer; -typedef struct UiLayout UiLayout; +#define UI_APPLY_LAYOUT(layout, args) \ + layout.fill = args.fill; \ + layout.hexpand = args.hexpand; \ + layout.vexpand = args.vexpand; \ + layout.hfill = args.hfill; \ + layout.vfill = args.vfill; \ + layout.colspan = args.colspan; \ + layout.rowspan = args.rowspan + +typedef enum UiBoxOrientation UiBoxOrientation; + +#define ui_reset_layout(layout) memset(&(layout), 0, sizeof(UiLayout)) +#define ui_lb2bool(b) ((b) == UI_LAYOUT_TRUE ? TRUE : FALSE) +#define ui_bool2lb(b) ((b) ? UI_LAYOUT_TRUE : UI_LAYOUT_FALSE) -typedef Widget (*ui_container_add_f)(UiContainer*, Arg*, int*, UiBool); - -typedef enum UiLayoutBool UiLayoutBool; -typedef enum UiBoxOrientation UiBoxOrientation; +#define ui_obj_container(obj) (UiContainerPrivate*)obj->container_end + +typedef struct UiLayout UiLayout; - -enum UiLayoutBool { - UI_LAYOUT_UNDEFINED = 0, - UI_LAYOUT_TRUE, - UI_LAYOUT_FALSE, +struct UiLayout { + UiTri fill; + UiBool newline; + char *label; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int width; + int colspan; + int rowspan; }; enum UiBoxOrientation { @@ -64,93 +75,37 @@ UI_BOX_HORIZONTAL }; -struct UiLayout { - UiLayoutBool fill; - UiBool newline; - char *label; - UiBool hexpand; - UiBool vexpand; - int gridwidth; -}; +typedef struct UiContainerPrivate UiContainerPrivate; -struct UiContainer { - Widget widget; - Widget (*prepare)(UiContainer*, Arg *, int*, UiBool); - void (*add)(UiContainer*, Widget); - UiLayout layout; - Widget current; - Widget menu; -}; -struct UiBoxContainer { - UiContainer container; - Widget prev_widget; - UiBool has_fill; - UiBoxOrientation orientation; - int margin; - int spacing; -}; - -struct UiGridContainer { - UiContainer container; - CxList *lines; - CxList *current; - int columnspacing; - int rowspacing; -}; - -struct UiTabViewContainer { - UiContainer container; - UiContext *context; - Widget widget; - CxList *tabs; - Widget current; +struct UiContainerPrivate { + UiContainerX container; + Widget (*prepare)(UiContainerPrivate*, Arg *, int*); + void (*add)(UiContainerPrivate*, Widget); + Widget widget; + UiLayout layout; }; -struct MotifTabbedPane { - UiTabbedPane view; - Widget tabbar; - CxList *tabs; - UiTab *current; - int index; - Pixel bg1; - Pixel bg2; - int height; -}; - -struct UiTab { - MotifTabbedPane *tabbedpane; - UiObject *content; - Widget tab_button; -}; - - -UiContainer* ui_frame_container(UiObject *obj, Widget frame); -Widget ui_frame_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_frame_container_add(UiContainer *ct, Widget widget); +typedef struct UiBoxContainer { + UiContainerPrivate container; + Dimension n; +} UiBoxContainer; -UiContainer* ui_box_container(UiObject *obj, Widget box, int margin, int spacing, UiBoxOrientation orientation); -Widget ui_box_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_box_container_add(UiContainer *ct, Widget widget); - -UiContainer* ui_grid_container(UiObject *obj, Widget form, int columnspacing, int rowspacing); -Widget ui_grid_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_grid_container_add(UiContainer *ct, Widget widget); +typedef struct UiGridContainer { + UiContainerPrivate container; + Dimension x; + Dimension y; +} UiGridContainer; -UiContainer* ui_scrolledwindow_container(UiObject *obj, Widget scrolledwindow); -Widget ui_scrolledwindow_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_scrolledwindow_container_add(UiContainer *ct, Widget widget); +UiContainerX* ui_box_container(UiObject *obj, Widget grid, UiBoxOrientation orientation); +Widget ui_vbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +Widget ui_hbox_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +void ui_box_container_add(UiContainerPrivate *ctn, Widget widget); -UiContainer* ui_tabview_container(UiObject *obj, Widget rowcolumn); -Widget ui_tabview_container_prepare(UiContainer *ct, Arg *args, int *n, UiBool fill); -void ui_tabview_container_add(UiContainer *ct, Widget widget); -void ui_tab_button_callback(Widget widget, UiTab *tab, XtPointer d); -void ui_change_tab(MotifTabbedPane *pane, UiTab *tab); - -void ui_tab_set_document(UiContext *ctx, void *document); -void ui_tab_detach_document(UiContext *ctx); - +UiContainerX* ui_grid_container(UiObject *obj, Widget grid); +Widget ui_grid_container_prepare(UiContainerPrivate *ctn, Arg *args, int *n); +void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget); #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/dnd.c --- a/ui/motif/dnd.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/dnd.c Thu Dec 12 20:01:43 2024 +0100 @@ -28,18 +28,3 @@ #include "dnd.h" -void ui_selection_settext(UiSelection *sel, char *str, int len) { - -} - -void ui_selection_seturis(UiSelection *sel, char **uris, int nelm) { - -} - -char* ui_selection_gettext(UiSelection *sel) { - return NULL; -} - -char** ui_selection_geturis(UiSelection *sel, size_t *nelm) { - return NULL; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/graphics.c --- a/ui/motif/graphics.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/graphics.c Thu Dec 12 20:01:43 2024 +0100 @@ -34,250 +34,3 @@ #include "graphics.h" #include "container.h" - -static void ui_drawingarea_expose(Widget widget, XtPointer u, XtPointer c) { - UiDrawEvent *drawevent = u; - //XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)c; - //XEvent *event = cbs->event; - Display *dpy = XtDisplay(widget); - - UiEvent ev; - ev.obj = drawevent->obj; - ev.window = drawevent->obj->window; - ev.document = drawevent->obj->ctx->document; - ev.eventdata = NULL; - ev.intval = 0; - - XtVaGetValues( - widget, - XmNwidth, - &drawevent->gr.g.width, - XmNheight, - &drawevent->gr.g.height, - NULL); - - XGCValues gcvals; - gcvals.foreground = BlackPixelOfScreen(XtScreen(widget)); - drawevent->gr.gc = XCreateGC(dpy, XtWindow(widget), (GCForeground), &gcvals); - - drawevent->callback(&ev, &drawevent->gr.g, drawevent->userdata); -} - -UIWIDGET ui_drawingarea(UiObject *obj, ui_drawfunc f, void *userdata) { - UiContainer *ct = uic_get_current_container(obj); - - int n = 0; - Arg args[16]; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget drawingarea = XmCreateDrawingArea(parent, "drawingarea", args, n); - - if(f) { - UiDrawEvent *event = malloc(sizeof(UiDrawEvent)); - event->obj = obj; - event->callback = f; - event->userdata = userdata; - - event->gr.display = XtDisplay(drawingarea); - event->gr.widget = drawingarea; - - Colormap colormap; - XtVaGetValues(drawingarea, XmNcolormap, &colormap, NULL); - event->gr.colormap = colormap; - - XtAddCallback( - drawingarea, - XmNexposeCallback, - ui_drawingarea_expose, - event); - - XtVaSetValues(drawingarea, XmNuserData, event, NULL); - } - - XtManageChild(drawingarea); - return drawingarea; -} - -static void ui_drawingarea_input(Widget widget, XtPointer u, XtPointer c) { - XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c; - XEvent *xevent = cbs->event; - UiMouseEventData *event = u; - - if (cbs->reason == XmCR_INPUT) { - if (xevent->xany.type == ButtonPress) { - UiMouseEvent me; - me.x = xevent->xbutton.x; - me.y = xevent->xbutton.y; - // TODO: configurable double click time - me.type = xevent->xbutton.time - event->last_event > 300 ? UI_PRESS : UI_PRESS2; - - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.eventdata = &me; - e.intval = 0; - event->callback(&e, event->userdata); - - - event->last_event = me.type == UI_PRESS2 ? 0 : xevent->xbutton.time; - } - } - -} - -void ui_drawingarea_mousehandler(UiObject *obj, UIWIDGET widget, ui_callback f, void *u) { - if(f) { - UiMouseEventData *event = malloc(sizeof(UiMouseEventData)); - event->obj = obj; - event->callback = f; - event->userdata = u; - event->last_event = 0; - - XtAddCallback(widget, XmNinputCallback, ui_drawingarea_input, event); - } -} - -void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { - XtVaGetValues( - drawingarea, - XmNwidth, - width, - XmNheight, - height, - NULL); -} - -void ui_drawingarea_redraw(UIWIDGET drawingarea) { - //XClearArea(XtDisplay(drawingarea), drawingarea->core.window, 0, 0, drawingarea->core.width, drawingarea->core.height, True); - UiDrawEvent *event; - XtVaGetValues(drawingarea, XmNuserData, &event, NULL); - ui_drawingarea_expose(drawingarea, event, NULL); -} - - -/* -------------------- text layout functions -------------------- */ -UiTextLayout* ui_text(UiGraphics *g) { - UiTextLayout *text = malloc(sizeof(UiTextLayout)); - memset(text, 0, sizeof(UiTextLayout)); - text->text = NULL; - text->length = 0; - text->widget = ((UiXlibGraphics*)g)->widget; - text->fontset = NULL; - return text; -} - -static void create_default_fontset(UiTextLayout *layout) { - char **missing = NULL; - int num_missing = 0; - char *def = NULL; - Display *dpy = XtDisplay(layout->widget); - XFontSet fs = XCreateFontSet( - dpy, - "-dt-interface system-medium-r-normal-s*utf*:," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-1," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-10," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-15," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-2," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-3," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-4," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-5," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-iso8859-9," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-e," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-r," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-ru," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-u," - "-misc-liberation sans-medium-r-normal--0-0-0-0-p-0-koi8-uni," - "-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208", - &missing, &num_missing, &def); - layout->fontset = fs; -} - -void ui_text_free(UiTextLayout *text) { - // TODO -} - -void ui_text_setstring(UiTextLayout *layout, char *str) { - ui_text_setstringl(layout, str, strlen(str)); -} - -void ui_text_setstringl(UiTextLayout *layout, char *str, int len) { - layout->text = str; - layout->length = len; - layout->changed = 1; -} - -void ui_text_setfont(UiTextLayout *layout, char *font, int size) { - create_default_fontset(layout);//TODO - layout->changed = 1; -} - -void ui_text_getsize(UiTextLayout *layout, int *width, int *height) { - if(layout->changed) { - XRectangle ext, lext; - XmbTextExtents(layout->fontset, layout->text, layout->length, &ext, &lext); - layout->width = ext.width; - layout->height = ext.height; - layout->changed = 0; - } - *width = layout->width; - *height = layout->height; -} - -void ui_text_setwidth(UiTextLayout *layout, int width) { - layout->maxwidth = width; -} - - -/* -------------------- drawing functions -------------------- */ - -void ui_graphics_color(UiGraphics *g, int red, int green, int blue) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - XColor color; - color.flags= DoRed | DoGreen | DoBlue; - color.red = red * 257; - color.green = green * 257; - color.blue = blue * 257; - XAllocColor(gr->display, gr->colormap, &color); - XSetForeground(gr->display, gr->gc, color.pixel); -} - -void ui_draw_line(UiGraphics *g, int x1, int y1, int x2, int y2) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - XDrawLine(gr->display, XtWindow(gr->widget), gr->gc, x1, y1, x2, y2); -} - -void ui_draw_rect(UiGraphics *g, int x, int y, int w, int h, int fill) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - if(fill) { - XFillRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h); - } else { - XDrawRectangle(gr->display, XtWindow(gr->widget), gr->gc, x, y, w, h); - } -} - -void ui_draw_text(UiGraphics *g, int x, int y, UiTextLayout *text) { - UiXlibGraphics *gr = (UiXlibGraphics*)g; - int width, height; - ui_text_getsize(text, &width, &height); - if(text->maxwidth > 0) { - XRectangle clip; - clip.x = x; - clip.y = y; - clip.width = text->maxwidth; - clip.height = height; - XSetClipRectangles(gr->display, gr->gc, 0, 0, &clip, 1, Unsorted); - } - - XmbDrawString( - gr->display, - XtWindow(gr->widget), - text->fontset, - gr->gc, - x, - y + height, - text->text, - text->length); - - XSetClipMask(gr->display, gr->gc, None); -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/graphics.h --- a/ui/motif/graphics.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/graphics.h Thu Dec 12 20:01:43 2024 +0100 @@ -36,38 +36,6 @@ extern "C" { #endif -typedef struct UiXlibGraphics { - UiGraphics g; - Display *display; - Widget widget; - Colormap colormap; - GC gc; -} UiXlibGraphics; - -typedef struct UiDrawEvent { - ui_drawfunc callback; - UiObject *obj; - void *userdata; - UiXlibGraphics gr; -} UiDrawEvent; - -typedef struct UiMouseEventData { - UiObject *obj; - ui_callback callback; - void *userdata; - Time last_event; -} UiMouseEventData; - -struct UiTextLayout { - char *text; - size_t length; - Widget widget; - XFontSet fontset; - int maxwidth; - int width; - int height; - int changed; -}; #ifdef __cplusplus diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/image.c --- a/ui/motif/image.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/image.c Thu Dec 12 20:01:43 2024 +0100 @@ -6,35 +6,3 @@ #include "image.h" -UiIcon* ui_icon(const char *name, int size) { - return NULL; -} - -UiIcon* ui_icon_unscaled(const char *name, int size) { - return NULL; -} - -void ui_free_icon(UiIcon *icon) { - -} - -UiImage* ui_icon_image(UiIcon *icon) { - return NULL; -} - -UiImage* ui_image(const char *filename) { - return NULL; -} - -UiImage* ui_named_image(const char *filename, const char *name) { - return NULL; -} - -UiImage* ui_load_image_from_path(const char *path, const char *name) { - return NULL; -} - -void ui_free_image(UiImage *img) { - -} - diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/label.c --- a/ui/motif/label.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/label.c Thu Dec 12 20:01:43 2024 +0100 @@ -34,36 +34,3 @@ #include "../common/context.h" #include "../common/object.h" -UIWIDGET ui_label(UiObject *obj, char *label) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(label); - - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNlabelString, str); - n++; - - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget widget = XmCreateLabel(parent, "label", args, n); - ct->add(ct, widget); - XtManageChild(widget); - - return widget; -} - -UIWIDGET ui_space(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - XmString str = XmStringCreateLocalized(""); - - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNlabelString, str); - n++; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget widget = XmCreateLabel(parent, "space_label", args, n); - ct->add(ct, widget); - XtManageChild(widget); - - return widget; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/list.c --- a/ui/motif/list.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/list.c Thu Dec 12 20:01:43 2024 +0100 @@ -34,175 +34,3 @@ #include "list.h" #include "../common/object.h" - -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; -} - - -UIWIDGET ui_listview_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_listview(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - int count; - XmStringTable items = ui_create_stringlist(var->value, getvalue, &count); - - Arg args[8]; - int n = 0; - XtSetArg(args[n], XmNitemCount, count); - n++; - XtSetArg(args[n], XmNitems, count == 0 ? NULL : items); - n++; - - UiContainer *ct = uic_get_current_container(obj); - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget widget = XmCreateScrolledList(parent, "listview", args, n); - ct->add(ct, XtParent(widget)); - XtManageChild(widget); - - UiListView *listview = cxMalloc(obj->ctx->allocator, sizeof(UiListView)); - listview->widget = widget; - listview->list = var; - listview->getvalue = getvalue; - - for (int i=0;ictx->allocator, - sizeof(UiListViewEventData)); - event->event.obj = obj; - event->event.userdata = udata; - event->event.callback = f; - event->event.value = 0; - event->var = var; - XtAddCallback( - widget, - XmNdefaultActionCallback, - (XtCallbackProc)ui_list_selection_callback, - event); - } - - return widget; -} - -UIWIDGET ui_listview(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_listview_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_listview_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - UiListVar *value = var->value; - return ui_listview_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - - -XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count) { - int num = list->count(list); - XmStringTable items = (XmStringTable)XtMalloc(num * sizeof(XmString)); - void *data = list->first(list); - for(int i=0;inext(list); - } - - *count = num; - return items; -} - - -void ui_listview_update(UiEvent *event, UiListView *view) { - int count; - XmStringTable items = ui_create_stringlist( - view->list->value, - view->getvalue, - &count); - - XtVaSetValues( - view->widget, - XmNitems, count == 0 ? NULL : items, - XmNitemCount, - count, - NULL); - - for (int i=0;ievent.obj; - e.window = event->event.obj->window; - e.document = event->event.obj->ctx->document; - UiList *list = event->var->value; - e.eventdata = list->get(list, cbs->item_position - 1); - e.intval = cbs->item_position - 1; - event->event.callback(&e, event->event.userdata); -} - - -/* --------------------------- ComboBox --------------------------- */ - -UIWIDGET ui_combobox_str(UiObject *obj, UiList *list, ui_callback f, void *udata) { - return ui_combobox(obj, list, ui_strmodel_getvalue, f, udata); -} - -UIWIDGET ui_combobox(UiObject *obj, UiList *list, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = list; - var->type = UI_VAR_SPECIAL; - return ui_combobox_var(obj, var, getvalue, f, udata); -} - -UIWIDGET ui_combobox_nv(UiObject *obj, char *varname, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_LIST); - if(var) { - UiListVar *value = var->value; - return ui_combobox_var(obj, var, getvalue, f, udata); - } else { - // TODO: error - } - return NULL; -} - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata) { - UiListView *listview = cxMalloc( - obj->ctx->allocator, - sizeof(UiListView)); - - UiContainer *ct = uic_get_current_container(obj); - Arg args[16]; - int n = 0; - XtSetArg(args[n], XmNindicatorOn, XmINDICATOR_NONE); - n++; - XtSetArg(args[n], XmNtraversalOn, FALSE); - n++; - XtSetArg(args[n], XmNwidth, 160); - n++; - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget combobox = XmCreateDropDownList(parent, "combobox", args, n); - XtManageChild(combobox); - listview->widget = combobox; - listview->list = var; - listview->getvalue = getvalue; - - ui_listview_update(NULL, listview); - - return parent; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/list.h --- a/ui/motif/list.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/list.h Thu Dec 12 20:01:43 2024 +0100 @@ -37,24 +37,7 @@ extern "C" { #endif -typedef struct UiListView { - Widget widget; - UiVar *list; - ui_getvaluefunc getvalue; -} UiListView; - -typedef struct UiListViewEventData { - UiEventData event; - UiVar *var; -} UiListViewEventData; - -void* ui_strmodel_getvalue(void *elm, int column); - -XmStringTable ui_create_stringlist(UiList *list, ui_getvaluefunc getvalue, int *count); -void ui_listview_update(UiEvent *event, UiListView *view); -void ui_list_selection_callback (Widget widget, UiListViewEventData *event, XtPointer data); - -UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); + #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/menu.c --- a/ui/motif/menu.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/menu.c Thu Dec 12 20:01:43 2024 +0100 @@ -41,444 +41,3 @@ #include #include -static ui_menu_add_f createMenuItem[] = { - /* UI_MENU */ add_menu_widget, - /* UI_MENU_SUBMENU */ add_menu_widget, - /* UI_MENU_ITEM */ add_menuitem_widget, - /* UI_MENU_STOCK_ITEM */ add_menuitem_st_widget, - /* UI_MENU_CHECK_ITEM */ add_checkitem_widget, - /* UI_MENU_CHECK_ITEM_NV */ add_checkitemnv_widget, - /* UI_MENU_ITEM_LIST */ add_menuitem_list_widget, - /* UI_MENU_ITEM_LIST_NV */ NULL, // TODO - /* UI_MENU_SEPARATOR */ add_menuseparator_widget -}; - -// private menu functions -void ui_create_menubar(UiObject *obj) { - UiMenu *menus = uic_get_menu_list(); - if(!menus) { - return; - } - - Widget menubar = XmCreateMenuBar(obj->widget, "main_list", NULL, 0); - XtManageChild(menubar); - - UiMenu *menu = menus; - int menu_index = 0; - while(menu) { - menu_index += add_menu_widget(menubar, menu_index, &menu->item, obj); - - menu = (UiMenu*)menu->item.next; - } -} - -int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { - UiMenu *menu = (UiMenu*)item; - - Widget menuItem = XtVaCreateManagedWidget( - menu->label, - xmCascadeButtonWidgetClass, - parent, - NULL); - Widget m = XmVaCreateSimplePulldownMenu(parent, menu->label, i, NULL, NULL); - - UiMenuItemI *mi = menu->items_begin; - int menu_index = 0; - while(mi) { - menu_index += createMenuItem[mi->type](m, menu_index, mi, obj); - mi = mi->next; - } - - return 1; -} - -int add_menuitem_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiMenuItem *mi = (UiMenuItem*)item; - - Arg args[1]; - XmString label = XmStringCreateLocalized(mi->label); - XtSetArg(args[0], XmNlabelString, label); - - Widget mitem = XtCreateManagedWidget( - "menubutton", - xmPushButtonWidgetClass, - parent, - args, - 1); - XmStringFree(label); - - if(mi->callback != NULL) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = mi->userdata; - event->callback = mi->callback; - event->value = 0; - XtAddCallback( - mitem, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - if(mi->groups) { - uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); - } - - return 1; -} - -int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) { - UiStMenuItem *mi = (UiStMenuItem*)item; - - UiStockItem *si = ui_get_stock_item(mi->stockid); - if(!si) { - fprintf(stderr, "UI Error: unknown stock id: %s\n", mi->stockid); - return 0; - } - - int n = 0; - Arg args[4]; - XmString label = XmStringCreateLocalized(si->label); - XmString at = NULL; - - XtSetArg(args[n], XmNlabelString, label); - n++; - if(si->accelerator) { - XtSetArg(args[n], XmNaccelerator, si->accelerator); - n++; - } - if(si->accelerator_label) { - at = XmStringCreateLocalized(si->accelerator_label); - XtSetArg(args[n], XmNacceleratorText, at); - n++; - } - - Widget mitem = XtCreateManagedWidget( - "menubutton", - xmPushButtonWidgetClass, - parent, - args, - n); - XmStringFree(label); - if(at) { - XmStringFree(at); - } - - if(mi->callback != NULL) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = mi->userdata; - event->callback = mi->callback; - event->value = 0; - XtAddCallback( - mitem, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - if(mi->groups) { - uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups); - } - - return 1; -} - -int add_menuseparator_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - Widget s = XmCreateSeparatorGadget (parent, "menu_separator", NULL, 0); - XtManageChild(s); - return 1; -} - -int add_checkitem_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiCheckItem *ci = (UiCheckItem*)item; - - Arg args[3]; - XmString label = XmStringCreateLocalized(ci->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNvisibleWhenOff, 1); - Widget checkbox = XtCreateManagedWidget( - "menutogglebutton", - xmToggleButtonWidgetClass, - parent, - args, - 2); - XmStringFree(label); - - if(ci->callback) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = ci->userdata; - event->callback = ci->callback; - XtAddCallback( - checkbox, - XmNvalueChangedCallback, - (XtCallbackProc)ui_toggle_button_callback, - event); - } - - return 1; -} - -int add_checkitemnv_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiCheckItemNV *ci = (UiCheckItemNV*)item; - - Arg args[3]; - XmString label = XmStringCreateLocalized(ci->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNvisibleWhenOff, 1); - Widget checkbox = XtCreateManagedWidget( - "menutogglebutton", - xmToggleButtonWidgetClass, - parent, - args, - 2); - XmStringFree(label); - - UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER); - if(var) { - UiInteger *value = var->value; - value->obj = checkbox; - value->get = ui_toggle_button_get; - value->set = ui_toggle_button_set; - value = 0; - } else { - // TODO: error - } - - return 1; -} - -int add_menuitem_list_widget( - Widget parent, - int i, - UiMenuItemI *item, - UiObject *obj) -{ - UiMenuItemList *il = (UiMenuItemList*)item; - - UiActiveMenuItemList *ls = cxMalloc( - obj->ctx->allocator, - sizeof(UiActiveMenuItemList)); - - ls->object = obj; - ls->menu = parent; - ls->index = i; - ls->oldcount = 0; - ls->list = il->list; - ls->callback = il->callback; - ls->userdata = il->userdata; - - ls->list->observers = ui_add_observer( - ls->list->observers, - (ui_callback)ui_update_menuitem_list, - ls); - - ui_update_menuitem_list(NULL, ls); - - return 0; -} - -void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) { - Arg args[4]; - - // remove old items - if(list->oldcount > 0) { - Widget *children; - int nc; - - XtVaGetValues( - list->menu, - XmNchildren, - &children, - XmNnumChildren, - &nc, - NULL); - - for(int i=0;ioldcount;i++) { - XtDestroyWidget(children[list->index + i]); - } - } - - char *str = ui_list_first(list->list); - if(str) { - // add separator - XtSetArg(args[0], XmNpositionIndex, list->index); - Widget s = XmCreateSeparatorGadget (list->menu, "menu_separator", args, 1); - XtManageChild(s); - } - int i = 1; - while(str) { - XmString label = XmStringCreateLocalized(str); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNpositionIndex, list->index + i); - - Widget mitem = XtCreateManagedWidget( - "menubutton", - xmPushButtonWidgetClass, - list->menu, - args, - 2); - XmStringFree(label); - - if(list->callback) { - // TODO: use mempool - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = list->object; - event->userdata = list->userdata; - event->callback = list->callback; - event->value = i - 1; - - XtAddCallback( - mitem, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - str = ui_list_next(list->list); - i++; - } - - list->oldcount = i; -} - -void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata) { - UiEventData *event = udata; - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.intval = 0; - event->callback(&e, event->userdata); -} - - -/* - * widget menu functions - */ - -static void ui_popup_handler(Widget widget, XtPointer data, XEvent *event, Boolean *c) { - Widget menu = data; - XmMenuPosition(menu, (XButtonPressedEvent *)event); - XtManageChild(menu); - - *c = FALSE; -} - -UIMENU ui_contextmenu(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - if(ct->current) { - return ui_contextmenu_w(obj, ct->current); - } else { - return NULL; // TODO: warn - } -} - -UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { - UiContainer *ct = uic_get_current_container(obj); - - Widget menu = XmCreatePopupMenu(widget, "popup_menu", NULL, 0); - ct->menu = menu; - - XtAddEventHandler(widget, ButtonPressMask, FALSE, ui_popup_handler, menu); - - return menu; -} - -void ui_contextmenu_popup(UIMENU menu) { - -} - -void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { - ui_widget_menuitem_gr(obj, label, f, userdata, -1); -} - -void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - CxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!groups) { - groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); - } - cxListAdd(groups, &group); - } - va_end(ap); - - // create menuitem - Arg args[4]; - XmString labelstr = XmStringCreateLocalized(label); - XtSetArg(args[0], XmNlabelString, labelstr); - - Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); - XtManageChild(item); - XmStringFree(labelstr); -} - -void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { - ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); -} - -void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - CxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!groups) { - groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); - } - cxListAdd(groups, &group); - } - va_end(ap); - - // create menuitem - UiStockItem *stockItem = ui_get_stock_item(stockid); - Arg args[4]; - XmString labelstr = XmStringCreateLocalized(stockItem->label); - XtSetArg(args[0], XmNlabelString, labelstr); - - Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1); - XtManageChild(item); - XmStringFree(labelstr); -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/menu.h --- a/ui/motif/menu.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/menu.h Thu Dec 12 20:01:43 2024 +0100 @@ -37,33 +37,6 @@ #endif -typedef struct UiActiveMenuItemList UiActiveMenuItemList; - -typedef int(*ui_menu_add_f)(Widget, int, UiMenuItemI*, UiObject*); - -struct UiActiveMenuItemList { - UiObject *object; - Widget menu; - int index; - int oldcount; - UiList *list; - ui_callback callback; - void *userdata; -}; - -void ui_create_menubar(UiObject *obj); - -int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuseparator_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_checkitem_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_checkitemnv_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); -int add_menuitem_list_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj); - -void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list); -void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata); - #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/objs.mk --- a/ui/motif/objs.mk Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/objs.mk Thu Dec 12 20:01:43 2024 +0100 @@ -39,11 +39,11 @@ MOTIFOBJ += label.o MOTIFOBJ += text.o MOTIFOBJ += list.o -MOTIFOBJ += tree.o MOTIFOBJ += graphics.o MOTIFOBJ += range.o MOTIFOBJ += dnd.o MOTIFOBJ += image.o +MOTIFOBJ += Grid.o TOOLKITOBJS += $(MOTIFOBJ:%=$(MOTIF_OBJPRE)%) TOOLKITSOURCE += $(MOTIFOBJ:%.o=motif/%.c) diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/range.c --- a/ui/motif/range.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/range.c Thu Dec 12 20:01:43 2024 +0100 @@ -34,104 +34,3 @@ #include "../common/context.h" #include "../common/object.h" - -static UIWIDGET ui_scrollbar(UiObject *obj, UiOrientation orientation, UiRange *range, ui_callback f, void *userdata) { - UiContainer *ct = uic_get_current_container(obj); - - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNorientation, orientation == UI_HORIZONTAL ? XmHORIZONTAL : XmVERTICAL); - n++; - XtSetArg (args[n], XmNmaximum, 10); - n++; - XtSetArg (args[n], XmNsliderSize, 1); - n++; - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget scrollbar = XmCreateScrollBar(parent, "scrollbar", args, n); - XtManageChild(scrollbar); - ct->add(ct, scrollbar); - - if(range) { - range->get = ui_scrollbar_get; - range->set = ui_scrollbar_set; - range->setrange = ui_scrollbar_setrange; - range->setextent = ui_scrollbar_setextent; - range->obj = scrollbar; - } - - if(f) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = userdata; - event->callback = f; - event->value = 0; - XtAddCallback( - scrollbar, - XmNvalueChangedCallback, - (XtCallbackProc)ui_scrollbar_callback, - event); - } - - return scrollbar; -} - -UIWIDGET ui_hscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) { - return ui_scrollbar(obj, UI_HORIZONTAL, range, f, userdata); -} - -UIWIDGET ui_vscrollbar(UiObject *obj, UiRange *range, ui_callback f, void *userdata) { - return ui_scrollbar(obj, UI_VERTICAL, range, f, userdata); -} - -void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata) { - UiEvent e; - e.obj = event->obj; - e.window = event->obj->window; - e.document = event->obj->ctx->document; - e.intval = event->value; - event->callback(&e, event->userdata); -} - -double ui_scrollbar_get(UiRange *range) { - int intval; - XtVaGetValues( - range->obj, - XmNvalue, - &intval, - NULL); - double value = (double)intval / 10; - range->value = value; - return value; -} - -void ui_scrollbar_set(UiRange *range, double value) { - XtVaSetValues( - range->obj, - XmNvalue, - (int)(value * 10), - NULL); - range->value = value; -} - -void ui_scrollbar_setrange(UiRange *range, double min, double max) { - XtVaSetValues( - range->obj, - XmNminimum, - (int)(min * 10), - XmNmaximum, - (int)(max * 10), - NULL); - range->min = min; - range->max = max; -} - -void ui_scrollbar_setextent(UiRange *range, double extent) { - XtVaSetValues( - range->obj, - XmNsliderSize, - (int)(extent * 10), - NULL); - range->extent = extent; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/range.h --- a/ui/motif/range.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/range.h Thu Dec 12 20:01:43 2024 +0100 @@ -36,11 +36,6 @@ extern "C" { #endif -void ui_scrollbar_callback(Widget scrollbar, UiEventData *event, XtPointer cdata); -double ui_scrollbar_get(UiRange *range); -void ui_scrollbar_set(UiRange *range, double value); -void ui_scrollbar_setrange(UiRange *range, double min, double max); -void ui_scrollbar_setextent(UiRange *range, double extent); #ifdef __cplusplus diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/stock.c --- a/ui/motif/stock.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/stock.c Thu Dec 12 20:01:43 2024 +0100 @@ -33,44 +33,4 @@ #include "../ui/properties.h" #include -static CxMap *stock_items; -void ui_stock_init() { - stock_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 64); - - ui_add_stock_item(UI_STOCK_NEW, "New", "CtrlN", "Ctrl+N", NULL); - ui_add_stock_item(UI_STOCK_OPEN, "Open", "CtrlO", "Ctrl+O", NULL); - ui_add_stock_item(UI_STOCK_SAVE, "Save", "CtrlS", "Ctrl+S", NULL); - ui_add_stock_item(UI_STOCK_SAVE_AS, "Save as ...", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_REVERT_TO_SAVED, "Revert to saved", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_CLOSE, "Close", "CtrlW", "Ctrl+W", NULL); - ui_add_stock_item(UI_STOCK_UNDO, "Undo", "CtrlZ", "Ctrl+Z", NULL); - ui_add_stock_item(UI_STOCK_REDO, "Redo", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_GO_BACK, "Back", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_GO_FORWARD, "Forward", NULL, NULL, NULL); - ui_add_stock_item(UI_STOCK_CUT, "Cut", "CtrlX", "Ctrl+X", NULL); - ui_add_stock_item(UI_STOCK_COPY, "Copy", "CtrlC", "Ctrl+C", NULL); - ui_add_stock_item(UI_STOCK_PASTE, "Paste", "CtrlV", "Ctrl+V", NULL); - ui_add_stock_item(UI_STOCK_DELETE, "Delete", NULL, NULL, NULL); -} - -void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon) { - UiStockItem *i = malloc(sizeof(UiStockItem)); - i->label = label; - i->accelerator = accelerator; - i->accelerator_label = accelerator_label; - // TODO: icon - - cxMapPut(stock_items, id, i); -} - -UiStockItem* ui_get_stock_item(char *id) { - UiStockItem *item = cxMapGet(stock_items, id); - if(item) { - char *label = uistr_n(id); - if(label) { - item->label = label; - } - } - return item; -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/stock.h --- a/ui/motif/stock.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/stock.h Thu Dec 12 20:01:43 2024 +0100 @@ -35,18 +35,7 @@ extern "C" { #endif -typedef struct UiStockItem { - char *label; - char *accelerator; - char *accelerator_label; - // TODO: icon -} UiStockItem; - -void ui_stock_init(); -void ui_add_stock_item(char *id, char *label, char *accelerator, char *accelerator_label, void *icon); - -UiStockItem* ui_get_stock_item(char *id); #ifdef __cplusplus } diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/text.c --- a/ui/motif/text.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/text.c Thu Dec 12 20:01:43 2024 +0100 @@ -32,476 +32,3 @@ #include "text.h" #include "container.h" - -UIWIDGET ui_textarea_var(UiObject *obj, UiVar *var) { - UiContainer *ct = uic_get_current_container(obj); - int n = 0; - Arg args[16]; - - //XtSetArg(args[n], XmNeditable, TRUE); - //n++; - XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); - n++; - - Widget parent = ct->prepare(ct, args, &n, TRUE); - Widget text_area = XmCreateScrolledText(parent, "text_area", args, n); - ct->add(ct, XtParent(text_area)); - XtManageChild(text_area); - - UiTextArea *uitext = cxMalloc( - obj->ctx->allocator, - sizeof(UiTextArea)); - uitext->ctx = obj->ctx; - uitext->last_selection_state = 0; - XtAddCallback( - text_area, - XmNmotionVerifyCallback, - (XtCallbackProc)ui_text_selection_callback, - uitext); - - // bind value - if(var->value) { - UiText *value = var->value; - if(value->value.ptr) { - XmTextSetString(text_area, value->value.ptr); - value->value.free(value->value.ptr); - } - - value->set = ui_textarea_set; - value->get = ui_textarea_get; - value->getsubstr = ui_textarea_getsubstr; - value->insert = ui_textarea_insert; - value->setposition = ui_textarea_setposition; - value->position = ui_textarea_position; - value->selection = ui_textarea_selection; - value->length = ui_textarea_length; - value->value.ptr = NULL; - value->obj = text_area; - - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); - } - - XtAddCallback( - text_area, - XmNmodifyVerifyCallback, - (XtCallbackProc)ui_text_modify_callback, - var); - } - - return text_area; -} - -UIWIDGET ui_textarea(UiObject *obj, UiText *value) { - UiVar *var = malloc(sizeof(UiVar)); - var->value = value; - var->type = UI_VAR_SPECIAL; - return ui_textarea_var(obj, var); -} - -UIWIDGET ui_textarea_nv(UiObject *obj, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_TEXT); - if(var) { - return ui_textarea_var(obj, var); - } else { - // TODO: error - } - return NULL; -} - -char* ui_textarea_get(UiText *text) { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - char *str = XmTextGetString(text->obj); - text->value.ptr = str; - text->value.free = (ui_freefunc)XtFree; - return str; -} - -void ui_textarea_set(UiText *text, const char *str) { - XmTextSetString(text->obj, str); - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - text->value.ptr = NULL; -} - -char* ui_textarea_getsubstr(UiText *text, int begin, int end) { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - int length = end - begin; - char *str = XtMalloc(length + 1); - XmTextGetSubstring(text->obj, begin, length, length + 1, str); - text->value.ptr = str; - text->value.free = (ui_freefunc)XtFree; - return str; -} - -void ui_textarea_insert(UiText *text, int pos, char *str) { - text->value.ptr = NULL; - XmTextInsert(text->obj, pos, str); - if(text->value.ptr) { - text->value.free(text->value.ptr); - } -} - -void ui_textarea_setposition(UiText *text, int pos) { - XmTextSetInsertionPosition(text->obj, pos); -} - -int ui_textarea_position(UiText *text) { - long begin; - long end; - XmTextGetSelectionPosition(text->obj, &begin, &end); - text->pos = begin; - return text->pos; -} - -void ui_textarea_selection(UiText *text, int *begin, int *end) { - XmTextGetSelectionPosition(text->obj, (long*)begin, (long*)end); -} - -int ui_textarea_length(UiText *text) { - return (int)XmTextGetLastPosition(text->obj); -} - - -void ui_text_set(UiText *text, char *str) { - if(text->set) { - text->set(text, str); - } else { - if(text->value.ptr) { - text->value.free(text->value.ptr); - } - text->value.ptr = XtNewString(str); - text->value.free = (ui_freefunc)XtFree; - } -} - -char* ui_text_get(UiText *text) { - if(text->get) { - return text->get(text); - } else { - return text->value.ptr; - } -} - - -UiUndoMgr* ui_create_undomgr() { - UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr)); - mgr->begin = NULL; - mgr->end = NULL; - mgr->cur = NULL; - mgr->length = 0; - mgr->event = 1; - return mgr; -} - -void ui_destroy_undomgr(UiUndoMgr *mgr) { - UiTextBufOp *op = mgr->begin; - while(op) { - UiTextBufOp *nextOp = op->next; - if(op->text) { - free(op->text); - } - free(op); - op = nextOp; - } - free(mgr); -} - -void ui_text_selection_callback( - Widget widget, - UiTextArea *textarea, - XtPointer data) -{ - long left = 0; - long right = 0; - XmTextGetSelectionPosition(widget, &left, &right); - int sel = left < right ? 1 : 0; - if(sel != textarea->last_selection_state) { - if(sel) { - ui_set_group(textarea->ctx, UI_GROUP_SELECTION); - } else { - ui_unset_group(textarea->ctx, UI_GROUP_SELECTION); - } - } - textarea->last_selection_state = sel; -} - -void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data) { - UiText *value = var->value; - if(!value->obj) { - // TODO: bug, fix - return; - } - if(!value->undomgr) { - value->undomgr = ui_create_undomgr(); - } - - XmTextVerifyCallbackStruct *txv = (XmTextVerifyCallbackStruct*)data; - int type = txv->text->length > 0 ? UI_TEXTBUF_INSERT : UI_TEXTBUF_DELETE; - UiUndoMgr *mgr = value->undomgr; - if(!mgr->event) { - return; - } - - char *text = txv->text->ptr; - int length = txv->text->length; - - if(mgr->cur) { - UiTextBufOp *elm = mgr->cur->next; - if(elm) { - mgr->cur->next = NULL; - mgr->end = mgr->cur; - while(elm) { - elm->prev = NULL; - UiTextBufOp *next = elm->next; - ui_free_textbuf_op(elm); - elm = next; - } - } - - UiTextBufOp *last_op = mgr->cur; - if( - last_op->type == UI_TEXTBUF_INSERT && - ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) - { - // append text to last op - int ln = last_op->len; - char *newtext = malloc(ln + length + 1); - memcpy(newtext, last_op->text, ln); - memcpy(newtext+ln, text, length); - newtext[ln+length] = '\0'; - - last_op->text = newtext; - last_op->len = ln + length; - last_op->end += length; - - return; - } - } - - char *str; - if(type == UI_TEXTBUF_INSERT) { - str = malloc(length + 1); - memcpy(str, text, length); - str[length] = 0; - } else { - length = txv->endPos - txv->startPos; - str = malloc(length + 1); - XmTextGetSubstring(value->obj, txv->startPos, length, length+1, str); - } - - UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); - op->prev = NULL; - op->next = NULL; - op->type = type; - op->start = txv->startPos; - op->end = txv->endPos + 1; - op->len = length; - op->text = str; - - cx_linked_list_add( - (void**)&mgr->begin, - (void**)&mgr->end, - offsetof(UiTextBufOp, prev), - offsetof(UiTextBufOp, next), - op); - - mgr->cur = op; -} - -int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen) { - // return 1 if oldstr + newstr are one word - - int has_space = 0; - for(int i=0;i 32) { - return 1; - } - } - - return 0; -} - -void ui_free_textbuf_op(UiTextBufOp *op) { - if(op->text) { - free(op->text); - } - free(op); -} - - -void ui_text_undo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; - - if(mgr->cur) { - UiTextBufOp *op = mgr->cur; - mgr->event = 0; - switch(op->type) { - case UI_TEXTBUF_INSERT: { - XmTextReplace(value->obj, op->start, op->end, ""); - break; - } - case UI_TEXTBUF_DELETE: { - XmTextInsert(value->obj, op->start, op->text); - break; - } - } - mgr->event = 1; - mgr->cur = mgr->cur->prev; - } -} - -void ui_text_redo(UiText *value) { - UiUndoMgr *mgr = value->undomgr; - - UiTextBufOp *elm = NULL; - if(mgr->cur) { - if(mgr->cur->next) { - elm = mgr->cur->next; - } - } else if(mgr->begin) { - elm = mgr->begin; - } - - if(elm) { - UiTextBufOp *op = elm; - mgr->event = 0; - switch(op->type) { - case UI_TEXTBUF_INSERT: { - XmTextInsert(value->obj, op->start, op->text); - break; - } - case UI_TEXTBUF_DELETE: { - XmTextReplace(value->obj, op->start, op->end, ""); - break; - } - } - mgr->event = 1; - mgr->cur = elm; - } -} - - -/* ------------------------- textfield ------------------------- */ - -static UIWIDGET create_textfield(UiObject *obj, int width, UiBool frameless, UiBool password, UiString *value) { - UiContainer *ct = uic_get_current_container(obj); - int n = 0; - Arg args[16]; - XtSetArg(args[n], XmNeditMode, XmSINGLE_LINE_EDIT); - n++; - if(width > 0) { - XtSetArg(args[n], XmNcolumns, width / 2 + 1); - n++; - } - if(frameless) { - XtSetArg(args[n], XmNshadowThickness, 0); - n++; - } - if(password) { - // TODO - } - - Widget parent = ct->prepare(ct, args, &n, FALSE); - Widget textfield = XmCreateText(parent, "text_field", args, n); - ct->add(ct, textfield); - XtManageChild(textfield); - - // bind value - if(value) { - if(value->value.ptr) { - XmTextSetString(textfield, value->value.ptr); - value->value.free(value->value.ptr); - } - - value->set = ui_textfield_set; - value->get = ui_textfield_get; - value->value.ptr = NULL; - value->obj = textfield; - } - - return textfield; -} - -static UIWIDGET create_textfield_nv(UiObject *obj, int width, UiBool frameless, UiBool password, char *varname) { - UiVar *var = uic_create_var(obj->ctx, varname, UI_VAR_STRING); - if(var) { - UiString *value = var->value; - return ui_textfield(obj, value); - } else { - // TODO: error - } - return NULL; -} - -UIWIDGET ui_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, FALSE, varname); -} - -UIWIDGET ui_textfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, FALSE, value); -} - -UIWIDGET ui_textfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, FALSE, varname); -} - -UIWIDGET ui_frameless_textfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, TRUE, FALSE, value); -} - -UIWIDGET ui_frameless_textfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, TRUE, FALSE, varname); -} - -UIWIDGET ui_passwordfield(UiObject *obj, UiString *value) { - return create_textfield(obj, 0, FALSE, TRUE, value); -} - -UIWIDGET ui_passwordfield_nv(UiObject *obj, char *varname) { - return create_textfield_nv(obj, 0, FALSE, TRUE, varname); -} - -UIWIDGET ui_passwordfield_w(UiObject *obj, int width, UiString *value) { - return create_textfield(obj, width, FALSE, TRUE, value); -} - -UIWIDGET ui_passwordfield_wnv(UiObject *obj, int width, char *varname) { - return create_textfield_nv(obj, width, FALSE, TRUE, varname); -} - - -char* ui_textfield_get(UiString *str) { - if(str->value.ptr) { - str->value.free(str->value.ptr); - } - char *value = XmTextGetString(str->obj); - str->value.ptr = value; - str->value.free = (ui_freefunc)XtFree; - return value; -} - -void ui_textfield_set(UiString *str, char *value) { - XmTextSetString(str->obj, value); - if(str->value.ptr) { - str->value.free(str->value.ptr); - } - str->value.ptr = NULL; -} - diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/text.h --- a/ui/motif/text.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/text.h Thu Dec 12 20:01:43 2024 +0100 @@ -37,54 +37,6 @@ extern "C" { #endif -#define UI_TEXTBUF_INSERT 0 -#define UI_TEXTBUF_DELETE 1 -typedef struct UiTextBufOp UiTextBufOp; -struct UiTextBufOp { - UiTextBufOp *prev; - UiTextBufOp *next; - int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE - int start; - int end; - int len; - char *text; -}; - -typedef struct UiUndoMgr { - UiTextBufOp *begin; - UiTextBufOp *end; - UiTextBufOp *cur; - int length; - int event; -} UiUndoMgr; - -typedef struct UiTextArea { - UiContext *ctx; - int last_selection_state; -} UiTextArea; - -char* ui_textarea_get(UiText *text); -void ui_textarea_set(UiText *text, const char *str); -char* ui_textarea_getsubstr(UiText *text, int begin, int end); -void ui_textarea_insert(UiText *text, int pos, char *str); -void ui_textarea_setposition(UiText *text, int pos); -int ui_textarea_position(UiText *text); -void ui_textarea_selection(UiText *text, int *begin, int *end); -int ui_textarea_length(UiText *text); - -UiUndoMgr* ui_create_undomgr(); -void ui_destroy_undomgr(UiUndoMgr *mgr); -void ui_text_selection_callback( - Widget widget, - UiTextArea *textarea, - XtPointer data); -void ui_text_modify_callback(Widget widget, UiVar *var, XtPointer data); -int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen); -void ui_free_textbuf_op(UiTextBufOp *op); - -char* ui_textfield_get(UiString *str); -void ui_textfield_set(UiString *str, char *value); - #ifdef __cplusplus } #endif diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/toolbar.c --- a/ui/motif/toolbar.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/toolbar.c Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -42,340 +42,3 @@ #include "../common/context.h" -static CxMap *toolbar_items; -static CxList *defaults; - -void ui_toolbar_init() { - toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); - defaults = cxLinkedListCreateSimple(CX_STORE_POINTERS); -} - -void ui_toolitem(char *name, char *label, ui_callback f, void *userdata) { - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget; - item->label = label; - item->image = NULL; - item->callback = f; - item->userdata = userdata; - item->groups = NULL; - item->isimportant = FALSE; - - cxMapPut(toolbar_items, name, item); -} - -void ui_toolitem_st(char *name, char *stockid, ui_callback f, void *userdata) { - ui_toolitem_stgr(name, stockid, f, userdata, -1); -} - -void ui_toolitem_stgr(char *name, char *stockid, ui_callback f, void *userdata, ...) { - UiStToolItem *item = malloc(sizeof(UiStToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_widget; - item->stockid = stockid; - item->callback = f; - item->userdata = userdata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!item->groups) { - item->groups = cxArrayListCreateSimple(sizeof(int), 16); - } - cxListAdd(item->groups, &group); - } - va_end(ap); - - cxMapPut(toolbar_items, name, item); -} - -void ui_toolitem_img(char *name, char *label, char *img, ui_callback f, void *udata) { - // TODO - - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_widget; - item->label = label; - item->image = img; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - cxMapPut(toolbar_items, name, item); -} - - -void ui_toolitem_toggle_stgr(char *name, char *stockid, ui_callback f, void *udata, ...) { - // TODO - - UiStToolItem *item = malloc(sizeof(UiStToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_st_toggle_widget; - item->stockid = stockid; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, udata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!item->groups) { - item->groups = cxArrayListCreateSimple(sizeof(int), 16); - } - cxListAdd(item->groups, &group); - } - va_end(ap); - - cxMapPut(toolbar_items, name, item); -} - -void ui_toolitem_toggle_imggr(char *name, char *label, char *img, ui_callback f, void *udata, ...) { - // TODO - - UiToolItem *item = malloc(sizeof(UiToolItem)); - item->item.add_to = (ui_toolbar_add_f)add_toolitem_toggle_widget; - item->label = label; - item->image = img; - item->callback = f; - item->userdata = udata; - item->groups = NULL; - item->isimportant = FALSE; - - // add groups - va_list ap; - va_start(ap, udata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!item->groups) { - item->groups = cxArrayListCreateSimple(sizeof(int), 16); - } - cxListAdd(item->groups, &group); - } - va_end(ap); - - cxMapPut(toolbar_items, name, item); -} - -void ui_toolbar_combobox( - char *name, - UiList *list, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBox *cb = malloc(sizeof(UiToolbarComboBox)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox; - cb->list = list; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - cxMapPut(toolbar_items, name, cb); -} - -void ui_toolbar_combobox_str( - char *name, - UiList *list, - ui_callback f, - void *udata) -{ - ui_toolbar_combobox(name, list, ui_strmodel_getvalue, f, udata); -} - -void ui_toolbar_combobox_nv( - char *name, - char *listname, - ui_getvaluefunc getvalue, - ui_callback f, - void *udata) -{ - UiToolbarComboBoxNV *cb = malloc(sizeof(UiToolbarComboBoxNV)); - cb->item.add_to = (ui_toolbar_add_f)add_toolbar_combobox_nv; - cb->listname = listname; - cb->getvalue = getvalue; - cb->callback = f; - cb->userdata = udata; - - cxMapPut(toolbar_items, name, cb); -} - - -void ui_toolbar_add_default(char *name) { - char *s = strdup(name); - cxListAdd(defaults, s); -} - -Widget ui_create_toolbar(UiObject *obj, Widget parent) { - if(!defaults) { - return NULL; - } - - Arg args[8]; - XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtopAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNrightAttachment, XmATTACH_FORM); - Widget frame = XmCreateFrame(parent, "toolbar_frame", args, 5); - - XtSetArg(args[0], XmNorientation, XmHORIZONTAL); - XtSetArg(args[1], XmNpacking, XmPACK_TIGHT); - XtSetArg(args[2], XmNspacing, 1); - Widget toolbar = XmCreateRowColumn (frame, "toolbar", args, 3); - - CxIterator i = cxListIterator(defaults); - cx_foreach(char *, def, i) { - UiToolItemI *item = cxMapGet(toolbar_items, def); - if(item) { - item->add_to(toolbar, item, obj); - } else if(!strcmp(def, "@separator")) { - // TODO - } else { - fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def); - } - } - - XtManageChild(toolbar); - XtManageChild(frame); - - return frame; -} - -void add_toolitem_widget(Widget parent, UiToolItem *item, UiObject *obj) { - Arg args[4]; - - XmString label = XmStringCreateLocalized(item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_st_widget(Widget parent, UiStToolItem *item, UiObject *obj) { - Arg args[8]; - - UiStockItem *stock_item = ui_get_stock_item(item->stockid); - - XmString label = XmStringCreateLocalized(stock_item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - Widget button = XmCreatePushButton(parent, "toolbar_button", args, 3); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNactivateCallback, - (XtCallbackProc)ui_push_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_toggle_widget(Widget parent, UiToolItem *item, UiObject *obj) { - Arg args[8]; - - XmString label = XmStringCreateLocalized(item->label); - XtSetArg(args[0], XmNlabelString, label); - XtSetArg(args[1], XmNshadowThickness, 1); - XtSetArg(args[2], XmNtraversalOn, FALSE); - XtSetArg(args[3], XmNindicatorOn, XmINDICATOR_NONE); - Widget button = XmCreateToggleButton(parent, "toolbar_toggle_button", args, 4); - - XmStringFree(label); - - if(item->callback) { - UiEventData *event = cxMalloc( - obj->ctx->allocator, - sizeof(UiEventData)); - event->obj = obj; - event->userdata = item->userdata; - event->callback = item->callback; - XtAddCallback( - button, - XmNvalueChangedCallback, - (XtCallbackProc)ui_toggle_button_callback, - event); - } - - XtManageChild(button); - - if(item->groups) { - uic_add_group_widget(obj->ctx, button, (ui_enablefunc)XtSetSensitive, item->groups); - } -} - -void add_toolitem_st_toggle_widget(Widget parent, UiStToolItem *item, UiObject *obj) { - -} - -void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj) { - UiListView *listview = cxMalloc( - obj->ctx->allocator, - sizeof(UiListView)); - - UiVar *var = cxMalloc(obj->ctx->allocator, sizeof(UiVar)); - var->value = item->list; - var->type = UI_VAR_SPECIAL; - - Arg args[8]; - XtSetArg(args[0], XmNshadowThickness, 1); - XtSetArg(args[1], XmNindicatorOn, XmINDICATOR_NONE); - XtSetArg(args[2], XmNtraversalOn, FALSE); - XtSetArg(args[3], XmNwidth, 120); - Widget combobox = XmCreateDropDownList(tb, "toolbar_combobox", args, 4); - XtManageChild(combobox); - listview->widget = combobox; - listview->list = var; - listview->getvalue = item->getvalue; - - ui_listview_update(NULL, listview); - - if(item->callback) { - // TODO: - - } -} - -void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj) { - -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/toolbar.h --- a/ui/motif/toolbar.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/toolbar.h Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -37,66 +37,6 @@ extern "C" { #endif -typedef struct UiToolItemI UiToolItemI; -typedef struct UiToolItem UiToolItem; -typedef struct UiStToolItem UiStToolItem; - -typedef struct UiToolbarComboBox UiToolbarComboBox; -typedef struct UiToolbarComboBoxNV UiToolbarComboBoxNV; - -typedef void(*ui_toolbar_add_f)(Widget, UiToolItemI*, UiObject*); - -struct UiToolItemI { - ui_toolbar_add_f add_to; -}; - -struct UiToolItem { - UiToolItemI item; - char *label; - void *image; - ui_callback callback; - void *userdata; - CxList *groups; - Boolean isimportant; -}; - -struct UiStToolItem { - UiToolItemI item; - char *stockid; - ui_callback callback; - void *userdata; - CxList *groups; - Boolean isimportant; -}; - -struct UiToolbarComboBox { - UiToolItemI item; - UiList *list; - ui_getvaluefunc getvalue; - ui_callback callback; - void *userdata; -}; - -struct UiToolbarComboBoxNV { - UiToolItemI item; - char *listname; - ui_getvaluefunc getvalue; - ui_callback callback; - void *userdata; -}; - -void ui_toolbar_init(); - -Widget ui_create_toolbar(UiObject *obj, Widget parent); - -void add_toolitem_widget(Widget tb, UiToolItem *item, UiObject *obj); -void add_toolitem_st_widget(Widget tb, UiStToolItem *item, UiObject *obj); -void add_toolitem_toggle_widget(Widget tb, UiToolItem *item, UiObject *obj); -void add_toolitem_st_toggle_widget(Widget tb, UiStToolItem *item, UiObject *obj); - -void add_toolbar_combobox(Widget tb, UiToolbarComboBox *item, UiObject *obj); -void add_toolbar_combobox_nv(Widget tb, UiToolbarComboBoxNV *item, UiObject *obj); - #ifdef __cplusplus } #endif diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/toolkit.c --- a/ui/motif/toolkit.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/toolkit.c Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -33,15 +33,21 @@ #include "toolkit.h" #include "toolbar.h" +#include "container.h" #include "stock.h" +#include "../common/menu.h" +#include "../common/toolbar.h" #include "../common/document.h" #include "../common/properties.h" #include +#include +#include + static XtAppContext app; static Display *display; static Widget active_window; -static char *application_name; +static const char *application_name; static ui_callback startup_func; static void *startup_data; @@ -68,6 +74,11 @@ "*rt*fontType: FONT_IS_XFT", "*rt*fontName: Sans", "*rt*fontSize: 11", + + "*window_frame.shadowType: SHADOW_ETCHED_OUT", + "*window_frame.shadowThickness: 1", + "*togglebutton.shadowThickness: 1", + "*togglebutton.highlightThickness: 2", NULL }; @@ -76,8 +87,9 @@ read(event_pipe[0], &ptr, sizeof(void*)); } -void ui_init(char *appname, int argc, char **argv) { +void ui_init(const char *appname, int argc, char **argv) { application_name = appname; + uic_init_global_context(); XtToolkitInitialize(); XtSetLanguageProc(NULL, NULL, NULL); @@ -85,15 +97,10 @@ XtAppSetFallbackResources(app, fallback); display = XtOpenDisplay(app, NULL, appname, appname, NULL, 0, &argc, argv); - char **missing = NULL; - int nm = 0; - char *def = NULL; - XCreateFontSet(display, "-dt-interface system-medium-r-normal-s*utf*", &missing, &nm, &def); uic_docmgr_init(); - ui_toolbar_init(); - ui_stock_init(); - + uic_menu_init(); + uic_toolbar_init(); uic_load_app_properties(); if(pipe(event_pipe)) { @@ -108,11 +115,11 @@ NULL); } -char* ui_appname() { +const char* ui_appname() { return application_name; } -Display* ui_get_display() { +Display* ui_motif_get_display() { return display; } @@ -157,11 +164,12 @@ void ui_show(UiObject *obj) { uic_check_group_widgets(obj->ctx); XtRealizeWidget(obj->widget); - ui_window_dark_theme(XtDisplay(obj->widget), XtWindow(obj->widget)); // TODO: if } -// implemented in window.c -//void ui_close(UiObject *obj) +void ui_close(UiObject *obj) { + +} + void ui_set_enabled(UIWIDGET widget, int enabled) { XtSetSensitive(widget, enabled); @@ -182,17 +190,16 @@ } static Boolean ui_job_finished(void *data) { - printf("WorkProc\n"); UiJob *job = data; - - UiEvent event; - event.obj = job->obj; - event.window = job->obj->window; - event.document = job->obj->ctx->document; - event.intval = 0; - event.eventdata = NULL; - - job->finish_callback(&event, job->finish_data); + if(job->finish_callback) { + UiEvent event; + event.obj = job->obj; + event.window = job->obj->window; + event.document = job->obj->ctx->document; + event.intval = 0; + event.eventdata = NULL; + job->finish_callback(&event, job->finish_data); + } free(job); return TRUE; } @@ -201,7 +208,6 @@ UiJob *job = data; int result = job->job_func(job->job_data); if(!result) { - printf("XtAppAddWorkProc\n"); write(event_pipe[1], &job, sizeof(void*)); // hack XtAppAddWorkProc(app, ui_job_finished, job); @@ -221,7 +227,6 @@ } void ui_clipboard_set(char *str) { - printf("copy: {%s}\n", str); int length = strlen(str) + 1; Display *dp = XtDisplayOfObject(active_window); @@ -287,6 +292,9 @@ return active_window; } +/* + * doesn't work with gnome anymore + */ void ui_window_dark_theme(Display *dp, Window window) { Atom atom = XInternAtom(dp, "_GTK_THEME_VARIANT", False); Atom type = XInternAtom(dp, "UTF8_STRING", False); @@ -300,3 +308,23 @@ (const unsigned char*)"dark", 4); } + +void ui_destroy_eventdata(Widget w, XtPointer *data, XtPointer d) { + free(data); +} + +void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) { + if(!groups) { + return; + } + size_t ngroups = uic_group_array_size(groups); + ui_set_widget_ngroups(ctx, widget, groups, ngroups); +} + +void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_enabled, groups, ngroups); + ui_set_enabled(widget, FALSE); + } +} + diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/toolkit.h --- a/ui/motif/toolkit.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/toolkit.h Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -38,8 +38,6 @@ extern "C" { #endif -Display* ui_get_display(); - typedef struct UiEventData { UiObject *obj; ui_callback callback; @@ -47,6 +45,31 @@ int value; } UiEventData; +typedef struct UiEventDataExt { + UiObject *obj; + ui_callback callback; + void *userdata; + ui_callback callback2; + void *userdata2; + int value0; + int value1; + int value2; + int value3; + void *customdata0; + void *customdata1; + void *customdata2; + void *customdata3; +} UiEventDataExt; + +typedef struct UiVarEventData { + UiObject *obj; + UiVar *var; + UiObserver **observers; + ui_callback callback; + void *userdata; + int value; +} UiVarEventData; + typedef struct UiJob { UiObject *obj; ui_threadfunc job_func; @@ -60,12 +83,19 @@ void ui_exit_mainloop(); +Display* ui_motif_get_display(void); + void ui_set_active_window(Widget w); Widget ui_get_active_window(); void ui_secondary_event_loop(int *loop); void ui_window_dark_theme(Display *dp, Window window); +void ui_destroy_eventdata(Widget w, XtPointer *data, XtPointer d); + +void ui_set_widget_groups(UiContext *ctx, Widget widget, const int *groups) ; +void ui_set_widget_ngroups(UiContext *ctx, Widget widget, const int *groups, size_t ngroups); + #ifdef __cplusplus } #endif diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/window.c --- a/ui/motif/window.c Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/motif/window.c Thu Dec 12 20:01:43 2024 +0100 @@ -1,7 +1,7 @@ /* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * - * Copyright 2014 Olaf Wintermann. All rights reserved. + * 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: @@ -36,6 +36,8 @@ #include "../ui/window.h" #include "../common/context.h" +#include "Grid.h" + #include static int nwindows = 0; @@ -45,17 +47,7 @@ static void window_close_handler(Widget window, void *udata, void *cdata) { UiObject *obj = udata; - UiEvent ev; - ev.window = obj->window; - ev.document = obj->ctx->document; - ev.obj = obj; - ev.eventdata = NULL; - ev.intval = 0; - - if(obj->ctx->close_callback) { - obj->ctx->close_callback(&ev, obj->ctx->close_data); - } - // TODO: free UiObject + uic_object_destroy(obj); nwindows--; if(nwindows == 0) { @@ -63,7 +55,8 @@ } } -static UiObject* create_window(char *title, void *window_data, UiBool simple) { + +static UiObject* create_window(const char *title, void *window_data, Boolean simple) { CxMempool *mp = cxBasicMempoolCreate(256); const CxAllocator *a = mp->allocator; UiObject *obj = cxCalloc(a, 1, sizeof(UiObject)); @@ -72,23 +65,20 @@ Arg args[16]; int n = 0; - - XtSetArg(args[0], XmNtitle, title); - //XtSetArg(args[1], XmNbaseWidth, window_default_width); - //XtSetArg(args[2], XmNbaseHeight, window_default_height); - XtSetArg(args[1], XmNminWidth, 100); - XtSetArg(args[2], XmNminHeight, 50); - XtSetArg(args[3], XmNwidth, window_default_width); - XtSetArg(args[4], XmNheight, window_default_height); + XtSetArg(args[n], XmNtitle, title); n++; + XtSetArg(args[n], XmNminWidth, 100); n++; + XtSetArg(args[n], XmNminHeight, 50); n++; + XtSetArg(args[n], XmNwidth, window_default_width); n++; + XtSetArg(args[n], XmNheight, window_default_height); n++; Widget toplevel = XtAppCreateShell( - "Test123", - "abc", + ui_appname(), + "mainwindow", //applicationShellWidgetClass, vendorShellWidgetClass, - ui_get_display(), + ui_motif_get_display(), args, - 5); + n); Atom wm_delete_window; wm_delete_window = XmInternAtom( @@ -101,111 +91,30 @@ window_close_handler, obj); - // TODO: use callback - ui_set_active_window(toplevel); - Widget window = XtVaCreateManagedWidget( title, xmMainWindowWidgetClass, toplevel, NULL); - obj->widget = window; - Widget form = XtVaCreateManagedWidget( - "window_form", - xmFormWidgetClass, - window, - NULL); - Widget toolbar = NULL; - if(!simple) { - ui_create_menubar(obj); - toolbar = ui_create_toolbar(obj, form); - } - - // window content - XtSetArg(args[0], XmNshadowType, XmSHADOW_ETCHED_OUT); - XtSetArg(args[1], XmNshadowThickness, 0); - XtSetArg(args[2], XmNleftAttachment, XmATTACH_FORM); - XtSetArg(args[3], XmNrightAttachment, XmATTACH_FORM); - XtSetArg(args[4], XmNbottomAttachment, XmATTACH_FORM); - if(toolbar) { - XtSetArg(args[5], XmNtopAttachment, XmATTACH_WIDGET); - XtSetArg(args[6], XmNtopWidget, toolbar); - n = 7; - } else { - XtSetArg(args[5], XmNtopAttachment, XmATTACH_FORM); - n = 6; - } - Widget frame = XmCreateFrame(form, "content_frame", args, n); + // content frame + n = 0; + Widget frame = XmCreateFrame(window, "window_frame", args, n); XtManageChild(frame); - Widget content_form = XmCreateForm(frame, "content_form", NULL, 0); - XtManageChild(content_form); - obj->container = ui_box_container(obj, content_form, 0, 0, UI_BOX_VERTICAL); + Widget vbox = XtCreateManagedWidget("window_vbox", gridClass, frame, NULL, 0); + UiContainerX *container = ui_box_container(obj, vbox, UI_BOX_VERTICAL); + uic_object_push_container(obj, container); - XtManageChild(form); - obj->widget = toplevel; nwindows++; return obj; -} +} -UiObject* ui_window(char *title, void *window_data) { +UiObject* ui_window(const char *title, void *window_data) { return create_window(title, window_data, FALSE); } -UiObject* ui_simplewindow(char *title, void *window_data) { +UiObject* ui_simple_window(const char *title, void *window_data) { return create_window(title, window_data, TRUE); } - -void ui_close(UiObject *obj) { - XtDestroyWidget(obj->widget); - window_close_handler(obj->widget, obj, NULL); -} - -typedef struct FileDialogData { - int running; - char *file; -} FileDialogData; - -static void filedialog_select( - Widget widget, - FileDialogData *data, - XmFileSelectionBoxCallbackStruct *selection) -{ - char *path = NULL; - XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &path); - data->running = 0; - data->file = strdup(path); - XtFree(path); - XtUnmanageChild(widget); -} - -static void filedialog_cancel( - Widget widget, - FileDialogData *data, - XmFileSelectionBoxCallbackStruct *selection) - -{ - data->running = 0; - XtUnmanageChild(widget); -} - -char* ui_openfiledialog(UiObject *obj) { - Widget dialog = XmCreateFileSelectionDialog(obj->widget, "openfiledialog", NULL, 0); - XtManageChild(dialog); - - FileDialogData data; - data.running = 1; - data.file = NULL; - - XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, &data); - XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_cancel, &data); - - ui_secondary_event_loop(&data.running); - return data.file; -} - -char* ui_savefiledialog(UiObject *obj) { - return ui_openfiledialog(obj); -} diff -r b9767cb5b06b -r d2bd73d28ff1 ui/motif/window.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/motif/window.h Thu Dec 12 20:01:43 2024 +0100 @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef WINDOW_H +#define WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* WINDOW_H */ + diff -r b9767cb5b06b -r d2bd73d28ff1 ui/ui/container.h --- a/ui/ui/container.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/ui/container.h Thu Dec 12 20:01:43 2024 +0100 @@ -141,6 +141,20 @@ int alt_spacing; } UiHeaderbarArgs; +typedef struct UiSidebarArgs { + const char *name; + const char *style_class; + int margin; + int spacing; +} UiSidebarArgs; + + +struct UiContainerX { + void *container; + int close; + UiContainerX *prev; + UiContainerX *next; +}; #define UI_CTN(obj, ctn) for(ctn;ui_container_finish(obj);ui_container_begin_close(obj)) @@ -153,6 +167,7 @@ #define ui_scrolledwindow(obj, ...) for(ui_scrolledwindow_create(obj, (UiFrameArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_tabview(obj, ...) for(ui_tabview_create(obj, (UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, (UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, (UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_vbox0(obj) for(ui_vbox_create(obj, (UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_hbox0(obj) for(ui_hbox_create(obj, (UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) @@ -162,6 +177,7 @@ #define ui_scrolledwindow0(obj) for(ui_scrolledwindow_create(obj, (UiFrameArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_tabview0(obj) for(ui_tabview_create(obj, (UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar0(obj) for(ui_headerbar_create(obj, (UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) +#define ui_sidebar0(obj) for(ui_sidebar_create(obj, (UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_tab(obj, label) for(ui_tab_create(obj, label);ui_container_finish(obj);ui_container_begin_close(obj)) @@ -169,7 +185,8 @@ #define ui_headerbar_center(obj) for(ui_headerbar_center_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) #define ui_headerbar_end(obj) for(ui_headerbar_end_create(obj);ui_container_finish(obj);ui_container_begin_close(obj)) -UIEXPORT void ui_end(UiObject *obj); +UIEXPORT void ui_end(UiObject *obj); // deprecated +UIEXPORT void ui_end_new(UiObject *obj); // TODO: rename to ui_end UIEXPORT UIWIDGET ui_vbox_create(UiObject *obj, UiContainerArgs args); UIEXPORT UIWIDGET ui_hbox_create(UiObject *obj, UiContainerArgs args); @@ -189,10 +206,8 @@ UIEXPORT void ui_headerbar_center_create(UiObject *obj); UIEXPORT void ui_headerbar_end_create(UiObject *obj); +UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs args); -UIEXPORT UIWIDGET ui_scrolledwindow_deprecated(UiObject *obj); // TODO - -UIEXPORT UIWIDGET ui_sidebar(UiObject *obj); // TODO UIEXPORT UIWIDGET ui_hsplitpane(UiObject *obj, int max); // TODO UIEXPORT UIWIDGET ui_vsplitpane(UiObject *obj, int max); // TODO diff -r b9767cb5b06b -r d2bd73d28ff1 ui/ui/text.h --- a/ui/ui/text.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/ui/text.h Thu Dec 12 20:01:43 2024 +0100 @@ -71,6 +71,8 @@ const char *varname; ui_callback onchange; void *onchangedata; + ui_callback onactivate; + void *onactivatedata; const int *groups; } UiTextFieldArgs; diff -r b9767cb5b06b -r d2bd73d28ff1 ui/ui/toolkit.h --- a/ui/ui/toolkit.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/ui/toolkit.h Thu Dec 12 20:01:43 2024 +0100 @@ -33,14 +33,13 @@ #ifdef UI_COCOA +#include #ifdef __OBJC__ #import -#define UIWIDGET NSView* -#define UIMENU NSMenu* -#else -typedef void* UIWIDGET; -typedef void* UIMENU; #endif +typedef void* UIWIDGET; // NSView* +typedef void* UIWINDOW; // NSWindow* +typedef void* UIMENU; // NSMenu* #elif UI_GTK2 || UI_GTK3 || UI_GTK4 @@ -152,9 +151,10 @@ #define UI_GROUPS(...) (const int[]){ __VA_ARGS__, -1 } /* public types */ -typedef int UiBool; +typedef _Bool UiBool; typedef struct UiObject UiObject; +typedef struct UiContainerX UiContainerX; typedef struct UiEvent UiEvent; typedef struct UiMouseEvent UiMouseEvent; typedef struct UiObserver UiObserver; @@ -215,7 +215,7 @@ */ UIWIDGET widget; -#ifdef UI_WINUI +#if defined(UI_COCOA) || defined(UI_WINUI) /* * native window object */ @@ -233,11 +233,18 @@ UiContext *ctx; /* - * container interface + * container interface (deprecated) */ UiContainer *container; /* + * container list + * TODO: remove old UiContainer and rename UiContainerX to UiContainer + */ + UiContainerX *container_begin; + UiContainerX *container_end; + + /* * next container object */ UiObject *next; @@ -434,13 +441,13 @@ UIEXPORT void ui_context_destroy(UiContext *ctx); UIEXPORT void ui_object_ref(UiObject *obj); -UIEXPORT void ui_object_unref(UiObject *obj); +UIEXPORT int ui_object_unref(UiObject *obj); UIEXPORT void ui_onstartup(ui_callback f, void *userdata); UIEXPORT void ui_onopen(ui_callback f, void *userdata); UIEXPORT void ui_onexit(ui_callback f, void *userdata); -UIEXPORT void ui_main(); +UIEXPORT void ui_main(void); UIEXPORT void ui_show(UiObject *obj); UIEXPORT void ui_close(UiObject *obj); diff -r b9767cb5b06b -r d2bd73d28ff1 ui/ui/tree.h --- a/ui/ui/tree.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/ui/tree.h Thu Dec 12 20:01:43 2024 +0100 @@ -35,11 +35,15 @@ extern "C" { #endif -typedef struct UiModel UiModel; -typedef struct UiListCallbacks UiListCallbacks; -typedef struct UiListDnd UiListDnd; +typedef struct UiModel UiModel; +typedef struct UiListCallbacks UiListCallbacks; +typedef struct UiListDnd UiListDnd; -typedef struct UiListArgs UiListArgs; +typedef struct UiListArgs UiListArgs; +typedef struct UiSourceListArgs UiSourceListArgs; + +typedef struct UiSubList UiSubList; +typedef struct UiSubListItem UiSubListItem; typedef enum UiModelType { UI_STRING = 0, @@ -130,6 +134,81 @@ const int *groups; }; +typedef void (*ui_sublist_getvalue_func)(void *sublist_userdata, void *rowdata, int index, UiSubListItem *item); + +struct UiSubList { + UiList *value; + const char *varname; + const char *header; + UiBool separator; + void *userdata; +}; + +/* + * list item members must be filled by the sublist getvalue func + * all members must be allocated (by malloc, strdup, ...) the pointer + * will be passed to free + */ +struct UiSubListItem { + char *icon; + char *label; + char *button_icon; + char *button_label; + char *badge; + void *eventdata; +}; + +struct UiSourceListArgs { + UiTri fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + int colspan; + int rowspan; + const char *name; + const char *style_class; + + const int *groups; + + /* + * list of sublists + * a sublist must have a varname or a value + * + * the last entry in the list must contain all NULL values or numsublists + * must contain the number of sublists + */ + UiSubList *sublists; + /* + * optional number of sublists + * if the value is 0, it is assumed, that sublists is null-terminated + * (last item contains only NULL values) + */ + size_t numsublists; + + /* + * callback for each list item, that should fill all necessary + * UiSubListItem fields + */ + ui_sublist_getvalue_func getvalue; + + /* + * activated when a list item is selected + */ + ui_callback onactivate; + void *onactivatedata; + + /* + * activated, when the additional list item button is clicked + */ + ui_callback onbuttonclick; + void *onbuttonclickdata; +}; + +#define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ } +#define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} } + + UIEXPORT UiModel* ui_model(UiContext *ctx, ...); UIEXPORT UiModel* ui_model_copy(UiContext *ctx, UiModel* model); UIEXPORT void ui_model_free(UiContext *ctx, UiModel *mi); @@ -138,16 +217,14 @@ #define ui_table(obj, ...) ui_table_create(obj, (UiListArgs) { __VA_ARGS__ } ) #define ui_combobox(obj, ...) ui_combobox_create(obj, (UiListArgs) { __VA_ARGS__ } ) #define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, (UiListArgs) { __VA_ARGS__ } ) +#define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, (UiSourceListArgs) { __VA_ARGS__ } ) UIEXPORT UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args); UIEXPORT UIWIDGET ui_table_create(UiObject* obj, UiListArgs args); UIEXPORT UIWIDGET ui_combobox_create(UiObject* obj, UiListArgs args); UIEXPORT UIWIDGET ui_breadcrumbbar_create(UiObject* obj, UiListArgs args); -void ui_table_dragsource_deprecated(UIWIDGET tablewidget, int actions, char *target0, ...); -void ui_table_dragsource_a_deprecated(UIWIDGET tablewidget, int actions, char **targets, int nelm); -void ui_table_dragdest_deprecated(UIWIDGET tablewidget, int actions, char *target0, ...); -void ui_table_dragdest_a_deprecated(UIWIDGET tablewidget, int actions, char **targets, int nelm); +UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args); #ifdef __cplusplus diff -r b9767cb5b06b -r d2bd73d28ff1 ui/ui/window.h --- a/ui/ui/window.h Fri Nov 29 22:21:36 2024 +0100 +++ b/ui/ui/window.h Thu Dec 12 20:01:43 2024 +0100 @@ -72,9 +72,10 @@ void *onclickdata; } UiDialogWindowArgs; -UIEXPORT UiObject* ui_window(const char *title, void *window_data); -UIEXPORT UiObject* ui_simple_window(const char *title, void *window_data); -UIEXPORT UiObject* ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args); +UIEXPORT UiObject *ui_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data); +UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs args); #define ui_dialog_window(parent, ...) ui_dialog_window_create(parent, (UiDialogWindowArgs){ __VA_ARGS__ }); #define ui_dialog_window0(parent) ui_dialog_window_create(parent, (UiDialogWindowArgs){ 0 });