diff -r a2df724b4cb9 -r f6e92de49959 ui/cocoa/MainWindow.m --- a/ui/cocoa/MainWindow.m Thu Apr 17 17:51:07 2025 +0200 +++ b/ui/cocoa/MainWindow.m Thu Apr 17 18:49:25 2025 +0200 @@ -75,31 +75,52 @@ self = [super initWithWindow:window]; _uiobj = obj; + self.checkItemStates = [[NSMutableDictionary alloc] init]; + self.radioItems = [[NSMutableDictionary alloc] init]; + // bind all stateful menu items (checkbox, radiobuttons, lists) - self.checkItemStates = [[NSMutableDictionary alloc] init]; NSArray *menuBindItems = ui_get_binding_items(); // returns all items that require binding for(MenuItem *item in menuBindItems) { - if(item.checkItem) { + if(item.checkItem || item.radioItem) { // simple check item (ui_menu_toggleitem_create) - UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, item.checkItem->varname, UI_VAR_INTEGER); + 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 - MenuCheckItem *state = [[MenuCheckItem alloc] init]; + MenuItemState *state = [[MenuItemState alloc] init]; state.mainWindow = self; state.var = var; if(var) { - // bind toggle item UiInteger *i = var->value; - state.state = (int)i->value; - i->obj = (__bridge void*)state; - i->get = ui_menu_check_item_get; - i->set = ui_menu_check_item_set; + 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]; - } else if(item.radioItem) { - // bind radio item - } } @@ -124,12 +145,12 @@ if(!item || !item.checkItem) { return; } - UiMenuCheckItem *it = item.checkItem; - MenuCheckItem *state = [_checkItemStates objectForKey:item.itemId]; + 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; @@ -141,10 +162,54 @@ } } +- (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) { - MenuCheckItem *state = [_checkItemStates objectForKey:item.itemId]; + MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; if(state) { menuItem.state = state.state; } else { @@ -157,18 +222,42 @@ @end -@implementation MenuCheckItem +@implementation MenuItemState @end int64_t ui_menu_check_item_get(UiInteger *i) { - MenuCheckItem *state = (__bridge MenuCheckItem*)i->obj; + MenuItemState *state = (__bridge MenuItemState*)i->obj; i->value = state.state; return i->value; } void ui_menu_check_item_set(UiInteger *i, int64_t value) { - MenuCheckItem *state = (__bridge MenuCheckItem*)i->obj; + 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++; + } +}