ui/cocoa/MainWindow.m

changeset 108
77254bd6dccb
parent 102
64ded9f6a6c6
child 109
c3dfcb8f0be7
--- a/ui/cocoa/MainWindow.m	Sat Apr 05 17:57:04 2025 +0200
+++ b/ui/cocoa/MainWindow.m	Sun Jul 20 22:04:39 2025 +0200
@@ -30,11 +30,14 @@
 #import "Container.h"
 #import "GridLayout.h"
 #import "../common/object.h"
+#import <objc/runtime.h>
+
+#import "EventData.h"
+#import "menu.h"
 
 @implementation MainWindow
 
 - (MainWindow*)init:(UiObject*)obj {
-    self.uiobj = obj;
     NSRect frame = NSMakeRect(300, 200, 600, 500);
     
     self = [self initWithContentRect:frame
@@ -63,3 +66,198 @@
 }
 
 @end
+
+
+
+@implementation MainWindowController
+
+- (MainWindowController*)initWithWindow:(UiObject*)obj window:(NSWindow*)window {
+    self = [super initWithWindow:window];
+    _uiobj = obj;
+    
+    self.checkItemStates = [[NSMutableDictionary alloc] init];
+    self.radioItems = [[NSMutableDictionary alloc] init];
+    
+    // bind all stateful menu items (checkbox, radiobuttons, lists)
+    NSArray *menuBindItems = ui_get_binding_items(); // returns all items that require binding
+    for(MenuItem *item in menuBindItems) {
+        if(item.checkItem || item.radioItem) {
+            // simple check item (ui_menu_toggleitem_create)
+            UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, item.checkItem ? item.checkItem->varname : item.radioItem->varname, UI_VAR_INTEGER);
+            // create the state object for this item/window
+            MenuItemState *state = [[MenuItemState alloc] init];
+            state.mainWindow = self;
+            state.var = var;
+            if(var) {
+                UiInteger *i = var->value;
+                if(item.checkItem) {
+                    // bind toggle item
+                    state.state = (int)i->value;
+                    i->obj = (__bridge void*)state;
+                    i->get = ui_menu_check_item_get;
+                    i->set = ui_menu_check_item_set;
+                } else {
+                    // bind radio item
+                    NSMutableArray *rgroup = nil;
+                    if(i->obj) {
+                        rgroup = (__bridge NSMutableArray*)i->obj;
+                    } else {
+                        // create a new rgroup array and register it in the window
+                        rgroup = [[NSMutableArray alloc] init];
+                        NSString *varname = [[NSString alloc] initWithUTF8String:item.radioItem->varname];
+                        [_radioItems setObject:rgroup forKey:varname];
+                        i->obj = (__bridge void*)rgroup;
+                    }
+                    i->get = ui_menu_radio_item_get;
+                    i->set = ui_menu_radio_item_set;
+                    [rgroup addObject:state]; // add this item state to the radio group
+                    // i->value can contain a non-zero value, which means a specific radiobutton
+                    // should be pre-selected
+                    if(i->value == rgroup.count) {
+                        state.state = NSControlStateValueOn;
+                    }
+                }
+            } else {
+                state.state = 0;
+            }
+            [_checkItemStates setObject:state forKey:item.itemId];
+        }
+    }
+    
+    return self;
+}
+
+- (void) windowDidLoad {
+    [self.window setNextResponder:self];
+}
+
+- (void)menuItemAction:(id)sender {
+    EventData *event = objc_getAssociatedObject(sender, "eventdata");
+    if(event) {
+        event.obj = self.uiobj; // temporary set the event object
+        [event handleEvent:sender];
+    }
+}
+
+- (void)menuCheckItemAction:(id)sender {
+    NSMenuItem *menuItem = sender;
+    MenuItem *item = objc_getAssociatedObject(sender, "menuitem");
+    if(!item || !item.checkItem) {
+        return;
+    }
+    
+    MenuItemState *state = [_checkItemStates objectForKey:item.itemId];
+    state.state = state.state == NSControlStateValueOff ? NSControlStateValueOn : NSControlStateValueOff;
+    menuItem.state = state.state;
+    
+    UiMenuCheckItem *it = item.checkItem;
+    if(it->callback) {
+        UiEvent event;
+        event.obj = _uiobj;
+        event.window = event.obj->window;
+        event.document = event.obj->ctx->document;
+        event.eventdata = state.var ? state.var->value : NULL;
+        event.intval = state.state;
+        it->callback(&event, it->userdata);
+    }
+}
+
+- (void)menuRadioItemAction:(id)sender {
+    NSMenuItem *menuItem = sender;
+    MenuItem *item = objc_getAssociatedObject(sender, "menuitem");
+    if(!item || !item.radioItem) {
+        return;
+    }
+    
+    UiMenuRadioItem *it = item.radioItem;
+    if(!it->varname) {
+        return;
+    }
+    
+    MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; // current state of this menu item
+    
+    NSString *varname = [[NSString alloc] initWithUTF8String:it->varname];
+    NSArray *radioGroup = [_radioItems objectForKey:varname];
+    if(!radioGroup) {
+        return;
+    }
+    int index = 1;
+    int value = 0;
+    for(MenuItemState *g in radioGroup) {
+        if(g == state) {
+            menuItem.state = NSControlStateValueOn;
+            g.state = NSControlStateValueOn;
+            value = index;
+        } else {
+            menuItem.state = NSControlStateValueOff;
+            g.state = NSControlStateValueOff;
+        }
+    }
+    
+    if(it->callback) {
+        UiEvent event;
+        event.obj = _uiobj;
+        event.window = event.obj->window;
+        event.document = event.obj->ctx->document;
+        event.eventdata = state.var ? state.var->value : NULL;
+        event.intval = value;
+        it->callback(&event, it->userdata);
+    }
+}
+
+
+- (BOOL) validateMenuItem:(NSMenuItem *) menuItem {
+    MenuItem *item = objc_getAssociatedObject(menuItem, "menuitem");
+    if(item) {
+        MenuItemState *state = [_checkItemStates objectForKey:item.itemId];
+        if(state) {
+            menuItem.state = state.state;
+        } else {
+            menuItem.state = NSControlStateValueOff;
+        }
+    }
+    
+    return YES;
+}
+
+@end
+
+@implementation MenuItemState
+
+@end
+
+int64_t ui_menu_check_item_get(UiInteger *i) {
+    MenuItemState *state = (__bridge MenuItemState*)i->obj;
+    i->value = state.state;
+    return i->value;
+}
+
+void ui_menu_check_item_set(UiInteger *i, int64_t value) {
+    MenuItemState *state = (__bridge MenuItemState*)i->obj;
+    i->value = value;
+    state.state = (int)value;
+}
+
+int64_t ui_menu_radio_item_get(UiInteger *i) {
+    NSArray *rgroup = (__bridge NSArray*)i->obj;
+    i->value = 0;
+    int index = 1;
+    for(MenuItemState *state in rgroup) {
+        if(state.state == NSControlStateValueOn) {
+            i->value = index;
+            break;
+        }
+        index++;
+    }
+    return i->value;
+}
+
+void ui_menu_radio_item_set(UiInteger *i, int64_t value) {
+    NSArray *rgroup = (__bridge NSArray*)i->obj;
+    i->value = 0;
+    int index = 1;
+    for(MenuItemState *state in rgroup) {
+        state.state = value == index;
+        index++;
+    }
+}

mercurial