add dialog window (Cocoa)

Sun, 19 Oct 2025 12:08:32 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 19 Oct 2025 12:08:32 +0200
changeset 864
d39301e8f962
parent 863
589bca248476
child 865
9cf0c29dde27

add dialog window (Cocoa)

make/xcode/toolkit/toolkit/main.m file | annotate | diff | comparison | revisions
ui/cocoa/EventData.m file | annotate | diff | comparison | revisions
ui/cocoa/MainWindow.h file | annotate | diff | comparison | revisions
ui/cocoa/MainWindow.m file | annotate | diff | comparison | revisions
ui/cocoa/toolkit.h file | annotate | diff | comparison | revisions
ui/cocoa/toolkit.m file | annotate | diff | comparison | revisions
ui/cocoa/window.h file | annotate | diff | comparison | revisions
ui/cocoa/window.m file | annotate | diff | comparison | revisions
--- a/make/xcode/toolkit/toolkit/main.m	Sun Oct 19 10:10:06 2025 +0200
+++ b/make/xcode/toolkit/toolkit/main.m	Sun Oct 19 12:08:32 2025 +0200
@@ -92,6 +92,25 @@
     ui_dialog(event->obj, .title = "Error", .content = "Error Message", .input = TRUE, .button1_label = "Add", .button2_label = "Remove", .closebutton_label = "Close");
 }
 
+static void toolbar_action2(UiEvent *event, void *userdata) {
+    printf("toolbar item 2\n");
+    UiObject *obj = ui_dialog_window(event->obj, .title = "Dialog Window", .lbutton1 = "Cancel", .rbutton4 = "OK", .default_button = 4);
+    
+    ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .fill = TRUE) {
+        ui_rlabel(obj, .label = "Label 1");
+        ui_textfield(obj, .value = NULL);
+        ui_newline(obj);
+        
+        ui_rlabel(obj, .label = "Label 2");
+        ui_textfield(obj, .value = NULL);
+        ui_newline(obj);
+        
+        ui_textarea(obj, .colspan = 2, .hexpand = TRUE, .hfill = TRUE, .vfill = TRUE, .vexpand = TRUE, .width = 600, .height = 300);
+    }
+    
+    ui_show(obj);
+}
+
 static void toolbar_toggle(UiEvent *event, void *userdata) {
     MyDocument *doc = event->document;
     int i = (int)(doc ? ui_get(doc->tbtoggle) : -1);
@@ -232,11 +251,13 @@
         ui_menuitem(.label = "Test1", .onclick = action_menuitem);
         ui_menu_toggleitem(.label = "Check");
     }
+    ui_toolbar_item("item5", .label = "Dialog Window", .icon = "NSImageNameAddTemplate", .onclick = toolbar_action2);
     
     ui_toolbar_add_default("item4", UI_TOOLBAR_LEFT);
     ui_toolbar_add_default("item3", UI_TOOLBAR_LEFT);
     ui_toolbar_add_default("item1", UI_TOOLBAR_LEFT);
     ui_toolbar_add_default("item2", UI_TOOLBAR_RIGHT);
+    ui_toolbar_add_default("item5", UI_TOOLBAR_RIGHT);
     
     
     ui_main();
--- a/ui/cocoa/EventData.m	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/EventData.m	Sun Oct 19 12:08:32 2025 +0200
@@ -47,6 +47,7 @@
         event.document = event.obj->ctx->document;
         event.eventdata = self.data;
         event.intval = self.value;
+        event.set = ui_get_setop();
         self.callback(&event, self.userdata);
     }
 }
@@ -58,6 +59,7 @@
     event.document = event.obj->ctx->document;
     event.eventdata = NULL;
     event.intval = 0;
+    event.set = ui_get_setop();
     if(_get_eventdata) {
         _get_eventdata(sender, _var, &event.eventdata, &event.intval);
     }
--- a/ui/cocoa/MainWindow.h	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/MainWindow.h	Sun Oct 19 12:08:32 2025 +0200
@@ -29,7 +29,7 @@
 #import "toolkit.h"
 #import "../ui/window.h"
 
-@interface MainWindow : NSWindow
+@interface MainWindow : NSWindow<UiToplevelObject>
 
 @property (strong) NSView *sidebar;
 @property (strong) NSView *leftPanel;
--- a/ui/cocoa/MainWindow.m	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/MainWindow.m	Sun Oct 19 12:08:32 2025 +0200
@@ -138,6 +138,19 @@
     return self;
 }
 
+- (BOOL) getIsVisible {
+    return [self isVisible];
+}
+
+- (void) setVisible:(BOOL)visible {
+    if(visible) {
+        [self makeKeyAndOrderFront:nil];
+    } else {
+        [self close];
+    }
+}
+
+
 @end
 
 
--- a/ui/cocoa/toolkit.h	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/toolkit.h	Sun Oct 19 12:08:32 2025 +0200
@@ -44,6 +44,13 @@
 
 @end
 
+@protocol UiToplevelObject
+
+- (BOOL) getIsVisible;
+- (void) setVisible:(BOOL)visible;
+
+@end
+
 void ui_cocoa_onstartup(void);
 void ui_cocoa_onopen(const char *file);
 void ui_cocoa_onexit(void);
--- a/ui/cocoa/toolkit.m	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/toolkit.m	Sun Oct 19 12:08:32 2025 +0200
@@ -148,18 +148,22 @@
 
 void ui_show(UiObject *obj) {
     if(obj->wobj) {
-        NSWindow *window = (__bridge NSWindow*)obj->wobj;
+        id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
         
-        if(!window.isVisible) {
+        if(![window getIsVisible]) {
             obj->ref++;
         }
         
-        [window makeKeyAndOrderFront:nil];
+        [window setVisible:YES];
     }
 }
 
 void ui_close(UiObject *obj) {
     // TODO: unref, window close, ...
+    if(obj->wobj) {
+        id<UiToplevelObject> window = (__bridge id<UiToplevelObject>)obj->wobj;
+        [window setVisible:NO];
+    }
 }
 
 /* ------------------- Job Control / Threadpool functions ------------------- */
--- a/ui/cocoa/window.h	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/window.h	Sun Oct 19 12:08:32 2025 +0200
@@ -28,3 +28,13 @@
 
 #import "toolkit.h"
 #import "../ui/window.h"
+
+@interface UiDialogWindow : NSPanel<UiToplevelObject>
+
+@property UiObject *obj;
+@property NSWindow *parent;
+@property UiTri modal;
+@property ui_callback onclick;
+@property void *onclickdata;
+
+@end
--- a/ui/cocoa/window.m	Sun Oct 19 10:10:06 2025 +0200
+++ b/ui/cocoa/window.m	Sun Oct 19 12:08:32 2025 +0200
@@ -30,6 +30,8 @@
 
 #import "MainWindow.h"
 #import "WindowManager.h"
+#import "BoxContainer.h"
+#import "EventData.h"
 
 #import <objc/runtime.h>
 
@@ -231,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;
+}
+

mercurial