UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2025 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #import "Toolbar.h" 30 #import "EventData.h" 31 #import "image.h" 32 #import "menu.h" 33 #import <objc/runtime.h> 34 35 #include "../common/toolbar.h" 36 37 38 void ui_toolbar_init(void) { 39 40 } 41 42 43 44 /* --------------------- UiToolbar --------------------- */ 45 46 @implementation UiToolbar 47 48 - (UiToolbar*) initWithWindow:(MainWindow*)window { 49 self = [super initWithIdentifier:@"UiToolbar"]; 50 _window = window; 51 _obj = window.obj; 52 53 allowedItems = [[NSMutableArray alloc]initWithCapacity:16]; 54 defaultItems = [[NSMutableArray alloc]initWithCapacity:16]; 55 56 if(window.sidebar) { 57 [allowedItems addObject:@"sidebar_separator"]; 58 } 59 if(window.leftPanel) { 60 [allowedItems addObject:@"splitview_separator"]; 61 } 62 63 CxMap *toolbarItems = uic_get_toolbar_items(); 64 CxMapIterator i = cxMapIteratorKeys(toolbarItems); 65 cx_foreach(CxHashKey *, key, i) { 66 NSString *s = [[NSString alloc]initWithBytes:key->data length:key->len encoding:NSUTF8StringEncoding]; 67 [allowedItems addObject:s]; 68 } 69 [allowedItems addObject: NSToolbarFlexibleSpaceItemIdentifier]; 70 [allowedItems addObject: NSToolbarSpaceItemIdentifier]; 71 72 // UI_TOOLBAR_LEFT = 0, 73 // UI_TOOLBAR_CENTER, 74 // UI_TOOLBAR_RIGHT, 75 // UI_TOOLBAR_SIDEBAR_LEFT, 76 // UI_TOOLBAR_SIDEBAR_RIGHT, 77 // UI_TOOLBAR_RIGHTPANEL_LEFT, 78 // UI_TOOLBAR_RIGHTPANEL_CENTER, 79 // UI_TOOLBAR_RIGHTPANEL_RIGHT 80 CxList *tbitems[8]; 81 for(int i=0;i<8;i++) { 82 tbitems[i] = uic_get_toolbar_defaults(i); 83 } 84 85 if(window.sidebar) { 86 CxIterator iter = cxListIterator(tbitems[UI_TOOLBAR_SIDEBAR_LEFT]); 87 cx_foreach(char *, name, iter) { 88 NSString *s = [[NSString alloc] initWithUTF8String:name]; 89 [defaultItems addObject:s]; 90 } 91 92 CxList *sidebarRight = tbitems[UI_TOOLBAR_SIDEBAR_RIGHT]; 93 if(cxListSize(sidebarRight) > 0) { 94 [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; 95 iter = cxListIterator(sidebarRight); 96 cx_foreach(char *, name, iter) { 97 NSString *s = [[NSString alloc] initWithUTF8String:name]; 98 [defaultItems addObject:s]; 99 } 100 } 101 102 [defaultItems addObject:@"sidebar_separator"]; 103 } 104 105 int start_pos = UI_TOOLBAR_LEFT; 106 for(int x=0;x<2;x++) { 107 for(int t=start_pos;t<start_pos+3;t++) { 108 CxIterator iter = cxListIterator(tbitems[t]); 109 cx_foreach(char *, name, iter) { 110 NSString *s = [[NSString alloc] initWithUTF8String:name]; 111 [defaultItems addObject:s]; 112 } 113 if(t < start_pos+2 && cxListSize(tbitems[t+1]) > 0) { 114 [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; 115 } 116 } 117 118 if(x == 0 && window.rightPanel) { 119 [defaultItems addObject:@"splitview_separator"]; 120 } 121 start_pos = UI_TOOLBAR_RIGHTPANEL_LEFT; 122 } 123 124 125 [self setDelegate:self]; 126 [self setAllowsUserCustomization:YES]; 127 return self; 128 } 129 130 // implementation of NSToolbarDelegate methods 131 132 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar { 133 return allowedItems; 134 } 135 136 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar { 137 return defaultItems; 138 } 139 140 - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar 141 itemForItemIdentifier:(NSString *)itemIdentifier 142 willBeInsertedIntoToolbar:(BOOL)flag { 143 CxMap *items = uic_get_toolbar_items(); 144 UiToolbarItemI *item = cxMapGet(items, itemIdentifier.UTF8String); 145 if(!item) { 146 if([itemIdentifier isEqualToString:@"sidebar_separator"]) { 147 NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier 148 splitView:_window.splitview 149 dividerIndex:0]; 150 return sep; 151 } else if([itemIdentifier isEqualToString:@"splitview_separator"]) { 152 int dividerIndex = _window.sidebar != nil ? 1 : 0; 153 NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier 154 splitView:_window.splitview 155 dividerIndex:dividerIndex]; 156 return sep; 157 } 158 return nil; 159 } 160 161 switch(item->type) { 162 default: return nil; 163 case UI_TOOLBAR_ITEM: { 164 return ui_nstoolbaritem_create_item(_obj, (UiToolbarItem*)item, itemIdentifier); 165 } 166 case UI_TOOLBAR_TOGGLEITEM: { 167 return ui_nstoolbaritem_create_toggle(_obj, (UiToolbarToggleItem*)item, itemIdentifier); 168 } 169 case UI_TOOLBAR_MENU: { 170 return ui_nstoolbaritem_create_menu(_obj, (UiToolbarMenuItem*)item, itemIdentifier); 171 } 172 } 173 174 return nil; 175 } 176 177 @end 178 179 @implementation UiToolbarToggleEventHandler 180 181 - (UiToolbarToggleEventHandler*)init { 182 return self; 183 } 184 185 - (void)handleEvent:(id)sender { 186 if(_callback == nil) { 187 return; 188 } 189 190 UiEvent event; 191 event.obj = _obj; 192 event.window = event.obj->window; 193 event.document = event.obj->ctx->document; 194 event.eventdata = NULL; 195 event.eventdatatype = 0; 196 event.intval = 0; 197 event.set = ui_get_setop(); 198 199 if([sender isKindOfClass:[NSSegmentedControl class]]) { 200 NSSegmentedControl *seg = (NSSegmentedControl*)sender; 201 event.intval = [seg isSelectedForSegment:0]; 202 } 203 204 _callback(&event, _userdata); 205 } 206 207 @end 208 209 NSToolbarItem* ui_nstoolbaritem_create_item(UiObject *obj, UiToolbarItem *item, NSString *identifier) { 210 NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier: identifier]; 211 button.bordered = YES; 212 213 if(item->args.label) { 214 NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; 215 button.paletteLabel = label; 216 button.label = label; 217 } 218 if(item->args.icon) { 219 button.image = ui_cocoa_named_icon(item->args.icon); 220 } 221 222 if(item->args.onclick) { 223 EventData *event = [[EventData alloc] init:item->args.onclick userdata:item->args.onclickdata]; 224 event.obj = obj; 225 button.target = event; 226 button.action = @selector(handleEvent:); 227 objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); 228 } 229 return button; 230 } 231 232 NSToolbarItem* ui_nstoolbaritem_create_toggle(UiObject *obj, UiToolbarToggleItem *item, NSString *identifier) { 233 UiToolbarToggleEventHandler *event = [[UiToolbarToggleEventHandler alloc]init]; 234 event.obj = obj; 235 event.callback = item->args.onchange; 236 event.userdata = item->args.onchangedata; 237 238 NSToolbarItem *button = [[NSToolbarItem alloc] initWithItemIdentifier:identifier]; 239 if(item->args.label) { 240 NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; 241 button.paletteLabel = label; 242 button.label = label; 243 } 244 objc_setAssociatedObject(button, "eventdata", event, OBJC_ASSOCIATION_RETAIN); 245 246 NSSegmentedControl *seg; 247 if(!item->args.icon) { 248 NSArray *labels = @[[[NSString alloc] initWithUTF8String:item->args.label]]; 249 seg = [NSSegmentedControl segmentedControlWithLabels:labels trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)]; 250 button.view = seg; 251 } else { 252 NSArray *images = @[ui_cocoa_named_icon(item->args.icon)]; 253 seg = [NSSegmentedControl segmentedControlWithImages:images trackingMode:NSSegmentSwitchTrackingSelectAny target:event action:@selector(handleEvent:)]; 254 } 255 button.view = seg; 256 257 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER); 258 if(var) { 259 event.var = var; 260 UiInteger *i = var->value; 261 if(i->get) { 262 if(ui_get(i) != 0) { 263 [seg setEnabled:YES forSegment:0]; 264 } 265 266 } 267 i->obj = (__bridge void*)seg; 268 printf("seg: %p\n", seg); 269 i->get = ui_toolbar_seg_toggleitem_get; 270 i->set = ui_toolbar_seg_toggleitem_set; 271 } 272 273 return button; 274 } 275 276 int64_t ui_toolbar_seg_toggleitem_get(UiInteger *i) { 277 NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj; 278 i->value = [seg isSelectedForSegment:0]; 279 return i->value; 280 } 281 282 void ui_toolbar_seg_toggleitem_set(UiInteger *i, int64_t value) { 283 NSSegmentedControl *seg = (__bridge NSSegmentedControl*)i->obj; 284 i->value = value; 285 [seg setSelected:value != 0 forSegment:0]; 286 } 287 288 NSToolbarItem* ui_nstoolbaritem_create_menu(UiObject *obj, UiToolbarMenuItem *item, NSString *identifier) { 289 NSMenuToolbarItem *button = [[NSMenuToolbarItem alloc] initWithItemIdentifier: identifier]; 290 button.bordered = YES; 291 292 if(item->args.label) { 293 NSString *label = [[NSString alloc] initWithUTF8String:item->args.label]; 294 button.paletteLabel = label; 295 button.label = label; 296 } 297 if(item->args.icon) { 298 button.image = ui_cocoa_named_icon(item->args.icon); 299 } 300 301 NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; 302 ui_add_menu_items(obj, menu, 0, &item->menu); 303 304 button.menu = menu; 305 306 return button; 307 } 308