--- a/ui/cocoa/window.m Sat Oct 04 14:54:25 2025 +0200 +++ b/ui/cocoa/window.m Sun Oct 19 21:20:08 2025 +0200 @@ -30,6 +30,8 @@ #import "MainWindow.h" #import "WindowManager.h" +#import "BoxContainer.h" +#import "EventData.h" #import <objc/runtime.h> @@ -38,20 +40,17 @@ #include "../common/context.h" #include "../common/menu.h" #include "../common/toolbar.h" +#include "../common/object.h" #include <cx/mempool.h> -static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar) { - CxMempool *mp = cxMempoolCreateSimple(256); - UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); - obj->ref = 0; +static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) { + UiObject *obj = uic_object_new_toplevel(); - obj->ctx = uic_context(obj, mp); - - MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar]; + MainWindow *window = [[MainWindow alloc] init:obj withSidebar:sidebar withSplitview:splitview]; [[WindowManager sharedWindowManager] addWindow:window]; - window.releasedWhenClosed = false; + window.releasedWhenClosed = false; // TODO: we still need a cleanup strategy obj->wobj = (__bridge void*)window; @@ -64,23 +63,27 @@ } UiObject* ui_window(const char *title, void *window_data) { - UiObject *obj = create_window(title, FALSE, FALSE); + UiObject *obj = create_window(title, FALSE, FALSE, FALSE); obj->window = window_data; return obj; } UiObject* ui_simple_window(const char *title, void *window_data) { - UiObject *obj = create_window(title, TRUE, FALSE); + UiObject *obj = create_window(title, TRUE, FALSE, FALSE); obj->window = window_data; return obj; } UiObject* ui_sidebar_window(const char *title, void *window_data) { - UiObject *obj = create_window(title, FALSE, TRUE); + UiObject *obj = create_window(title, FALSE, TRUE, FALSE); obj->window = window_data; return obj; } +UiObject* ui_splitview_window(const char *title, UiBool sidebar) { + return create_window(title, FALSE, sidebar, TRUE); +} + /* --------------------------------- File Dialogs --------------------------------- */ void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) { @@ -230,3 +233,188 @@ }]; } + +/* ------------------------------------- Dialog Window ------------------------------------- */ + +@implementation UiDialogWindow + +- (BOOL) getIsVisible { + return self.isVisible; +} + +- (void) setVisible:(BOOL)visible { + //[self makeKeyAndOrderFront:nil]; + if(visible) { + [_parent beginSheet:self completionHandler:^(NSModalResponse returnCode) { + // TODO: close event + }]; + } else { + [self.sheetParent endSheet:self returnCode:NSModalResponseCancel]; + } +} + +- (void)cancelOperation:(id)sender { + [self.sheetParent endSheet:self returnCode:NSModalResponseCancel]; + // TODO: close event +} + +@end + +UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args) { + UiObject *obj = uic_object_new_toplevel(); + UiDialogWindow *panel = [[UiDialogWindow alloc] initWithContentRect:NSMakeRect(0, 0, args->width, args->height) + styleMask:(NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskResizable | + NSWindowStyleMaskUtilityWindow) + backing:NSBackingStoreBuffered + defer:NO]; + panel.parent = (__bridge NSWindow*)parent->wobj; + panel.obj = obj; + panel.modal = args->modal; + panel.onclick = args->onclick; + panel.onclickdata = args->onclickdata; + [panel center]; + [[WindowManager sharedWindowManager] addWindow:panel]; + obj->wobj = (__bridge void*)panel; + + NSView *content = panel.contentView; + + // Create a view for the dialog window buttons (lbutton1, lbutton2, rbutton3, rbutton4) + NSView *buttonArea = [[NSView alloc]init]; + buttonArea.translatesAutoresizingMaskIntoConstraints = NO; + [content addSubview:buttonArea]; + [NSLayoutConstraint activateConstraints:@[ + [buttonArea.bottomAnchor constraintEqualToAnchor:content.bottomAnchor constant:-10], + [buttonArea.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:10], + [buttonArea.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-10], + [buttonArea.heightAnchor constraintEqualToConstant:20] + ]]; + + NSButton *lbutton1 = nil; + if(args->lbutton1) { + lbutton1 = [[NSButton alloc]init]; + lbutton1.title = [[NSString alloc]initWithUTF8String:args->lbutton1]; + lbutton1.translatesAutoresizingMaskIntoConstraints = NO; + [buttonArea addSubview:lbutton1]; + [NSLayoutConstraint activateConstraints:@[ + [lbutton1.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0], + [lbutton1.leadingAnchor constraintEqualToAnchor:buttonArea.leadingAnchor constant:0] + ]]; + + EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; + event.obj = obj; + event.value = 1; + lbutton1.target = event; + lbutton1.action = @selector(handleEvent:); + objc_setAssociatedObject(lbutton1, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + NSButton *lbutton2 = nil; + if(args->lbutton2) { + lbutton2 = [[NSButton alloc]init]; + lbutton2.title = [[NSString alloc]initWithUTF8String:args->lbutton2]; + lbutton2.translatesAutoresizingMaskIntoConstraints = NO; + [buttonArea addSubview:lbutton2]; + NSLayoutXAxisAnchor *anchor = lbutton1 != nil ? lbutton1.trailingAnchor : buttonArea.leadingAnchor; + int off = lbutton1 != nil ? 4 : 0; + [NSLayoutConstraint activateConstraints:@[ + [lbutton2.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0], + [lbutton2.leadingAnchor constraintEqualToAnchor:anchor constant:off] + ]]; + + EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; + event.obj = obj; + event.value = 2; + lbutton2.target = event; + lbutton2.action = @selector(handleEvent:); + objc_setAssociatedObject(lbutton2, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + + NSButton *rbutton4 = nil; + if(args->rbutton4) { + rbutton4 = [[NSButton alloc]init]; + rbutton4.title = [[NSString alloc]initWithUTF8String:args->rbutton4]; + rbutton4.translatesAutoresizingMaskIntoConstraints = NO; + [buttonArea addSubview:rbutton4]; + [NSLayoutConstraint activateConstraints:@[ + [rbutton4.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0], + [rbutton4.trailingAnchor constraintEqualToAnchor:buttonArea.trailingAnchor constant:0] + ]]; + + EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; + event.obj = obj; + event.value = 2; + rbutton4.target = event; + rbutton4.action = @selector(handleEvent:); + objc_setAssociatedObject(rbutton4, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + NSButton *rbutton3 = nil; + if(args->rbutton3) { + rbutton3 = [[NSButton alloc]init]; + rbutton3.title = [[NSString alloc]initWithUTF8String:args->rbutton3]; + rbutton3.translatesAutoresizingMaskIntoConstraints = NO; + [buttonArea addSubview:rbutton3]; + NSLayoutXAxisAnchor *anchor = rbutton4 != nil ? rbutton4.leadingAnchor : buttonArea.trailingAnchor; + int off = rbutton4 != nil ? -4 : 0; + [NSLayoutConstraint activateConstraints:@[ + [rbutton3.topAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0], + [rbutton3.trailingAnchor constraintEqualToAnchor:anchor constant:off] + ]]; + + EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; + event.obj = obj; + event.value = 2; + rbutton3.target = event; + rbutton3.action = @selector(handleEvent:); + objc_setAssociatedObject(rbutton3, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } + switch(args->default_button) { + default: break; + case 1: if(lbutton1 != nil) lbutton1.keyEquivalent = @"\r"; break; + case 2: if(lbutton2 != nil) lbutton2.keyEquivalent = @"\r"; break; + case 3: if(rbutton3 != nil) rbutton3.keyEquivalent = @"\r"; break; + case 4: if(rbutton4 != nil) rbutton4.keyEquivalent = @"\r"; break; + } + + // space between left and right buttons + NSView *space = [[NSView alloc]init]; + space.translatesAutoresizingMaskIntoConstraints = NO; + [buttonArea addSubview:space]; + NSLayoutXAxisAnchor *leftAnchor = buttonArea.leadingAnchor; + NSLayoutXAxisAnchor *rightAnchor = buttonArea.trailingAnchor; + if(lbutton2 != nil) { + leftAnchor = lbutton2.trailingAnchor; + } else if(lbutton1 != nil) { + leftAnchor = lbutton1.trailingAnchor; + } + + if(rbutton3 != nil) { + rightAnchor = rbutton3.leadingAnchor; + } else if(rbutton4 != nil) { + rightAnchor = rbutton4.leadingAnchor; + } + [NSLayoutConstraint activateConstraints:@[ + [space.topAnchor constraintEqualToAnchor:buttonArea.topAnchor], + [space.leadingAnchor constraintEqualToAnchor:leftAnchor constant:10], + [space.trailingAnchor constraintEqualToAnchor:rightAnchor constant:-10] + ]]; + + // dialog window main content + BoxContainer *vbox = [[BoxContainer alloc] init:NSUserInterfaceLayoutOrientationVertical spacing:0]; + vbox.translatesAutoresizingMaskIntoConstraints = NO; + [content addSubview:vbox]; + + [NSLayoutConstraint activateConstraints:@[ + [vbox.topAnchor constraintEqualToAnchor:content.topAnchor constant:0], + [vbox.leadingAnchor constraintEqualToAnchor:content.leadingAnchor], + [vbox.trailingAnchor constraintEqualToAnchor:content.trailingAnchor], + [vbox.bottomAnchor constraintEqualToAnchor:buttonArea.topAnchor constant:0] + ]]; + + UiContainerX *container = ui_create_container(obj, vbox); + vbox.container = container; + uic_object_push_container(obj, container); + + return obj; +} +