diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/macos.h | 4 | ||||
-rw-r--r-- | src/macos.m | 161 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 4 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 15 | ||||
-rw-r--r-- | src/ui/root.c | 1 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 37 | ||||
-rw-r--r-- | src/ui/util.c | 122 | ||||
-rw-r--r-- | src/ui/util.h | 14 | ||||
-rw-r--r-- | src/ui/widget.c | 3 | ||||
-rw-r--r-- | src/ui/widget.h | 1 |
11 files changed, 274 insertions, 90 deletions
@@ -129,7 +129,7 @@ iLocalDef void postCommandString_Root(iRoot *d, const iString *command) { | |||
129 | } | 129 | } |
130 | } | 130 | } |
131 | iLocalDef void postCommand_App(const char *command) { | 131 | iLocalDef void postCommand_App(const char *command) { |
132 | postCommandf_App(command); | 132 | postCommand_Root(NULL, command); |
133 | } | 133 | } |
134 | 134 | ||
135 | iDocumentWidget * document_Command (const char *cmd); | 135 | iDocumentWidget * document_Command (const char *cmd); |
diff --git a/src/macos.h b/src/macos.h index 20b95943..22a6dfff 100644 --- a/src/macos.h +++ b/src/macos.h | |||
@@ -24,7 +24,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | #include "ui/util.h" | 25 | #include "ui/util.h" |
26 | 26 | ||
27 | iDeclareType(MenuItem) | ||
27 | iDeclareType(Window) | 28 | iDeclareType(Window) |
29 | iDeclareType(Widget) | ||
28 | 30 | ||
29 | /* Platform-specific functionality for macOS */ | 31 | /* Platform-specific functionality for macOS */ |
30 | 32 | ||
@@ -40,3 +42,5 @@ void enableMenu_MacOS (const char *menuLabel, iBool enable); | |||
40 | void enableMenuItem_MacOS (const char *menuItemCommand, iBool enable); | 42 | void enableMenuItem_MacOS (const char *menuItemCommand, iBool enable); |
41 | void enableMenuItemsByKey_MacOS (int key, int kmods, iBool enable); | 43 | void enableMenuItemsByKey_MacOS (int key, int kmods, iBool enable); |
42 | void handleCommand_MacOS (const char *cmd); | 44 | void handleCommand_MacOS (const char *cmd); |
45 | |||
46 | void showPopupMenu_MacOS (iWidget *source, iInt2 windowCoord, const iMenuItem *items, size_t n); | ||
diff --git a/src/macos.m b/src/macos.m index 298db0f8..cec53a7d 100644 --- a/src/macos.m +++ b/src/macos.m | |||
@@ -77,6 +77,14 @@ iBool shouldDefaultToMetalRenderer_MacOS(void) { | |||
77 | return ver.x > 10 || ver.y > 13;*/ | 77 | return ver.x > 10 || ver.y > 13;*/ |
78 | } | 78 | } |
79 | 79 | ||
80 | static void ignoreImmediateKeyDownEvents_(void) { | ||
81 | /* SDL ignores menu key equivalents so the keydown events will be posted regardless. | ||
82 | However, we shouldn't double-activate menu items when a shortcut key is used in our | ||
83 | widgets. Quite a kludge: take advantage of Window's focus-acquisition threshold to | ||
84 | ignore the immediately following key down events. */ | ||
85 | get_Window()->focusGainedAt = SDL_GetTicks(); | ||
86 | } | ||
87 | |||
80 | /*----------------------------------------------------------------------------------------------*/ | 88 | /*----------------------------------------------------------------------------------------------*/ |
81 | 89 | ||
82 | @interface CommandButton : NSCustomTouchBarItem { | 90 | @interface CommandButton : NSCustomTouchBarItem { |
@@ -146,11 +154,60 @@ iBool shouldDefaultToMetalRenderer_MacOS(void) { | |||
146 | 154 | ||
147 | /*----------------------------------------------------------------------------------------------*/ | 155 | /*----------------------------------------------------------------------------------------------*/ |
148 | 156 | ||
157 | @interface MenuCommands : NSObject { | ||
158 | NSMutableDictionary<NSString *, NSString *> *commands; | ||
159 | iWidget *source; | ||
160 | } | ||
161 | @end | ||
162 | |||
163 | @implementation MenuCommands | ||
164 | |||
165 | - (id)init { | ||
166 | commands = [[NSMutableDictionary<NSString *, NSString *> alloc] init]; | ||
167 | source = NULL; | ||
168 | return self; | ||
169 | } | ||
170 | |||
171 | - (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem { | ||
172 | [commands setObject:command forKey:[menuItem title]]; | ||
173 | } | ||
174 | |||
175 | - (void)setSource:(iWidget *)widget { | ||
176 | source = widget; | ||
177 | } | ||
178 | |||
179 | - (void)clear { | ||
180 | [commands removeAllObjects]; | ||
181 | } | ||
182 | |||
183 | - (NSString *)commandForMenuItem:(NSMenuItem *)menuItem { | ||
184 | return [commands objectForKey:[menuItem title]]; | ||
185 | } | ||
186 | |||
187 | - (void)postMenuItemCommand:(id)sender { | ||
188 | NSString *command = [commands objectForKey:[(NSMenuItem *)sender title]]; | ||
189 | if (command) { | ||
190 | const char *cstr = [command cStringUsingEncoding:NSUTF8StringEncoding]; | ||
191 | if (source) { | ||
192 | postCommand_Widget(source, "%s", cstr); | ||
193 | } | ||
194 | else { | ||
195 | postCommand_Root(NULL, cstr); | ||
196 | } | ||
197 | ignoreImmediateKeyDownEvents_(); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | @end | ||
202 | |||
203 | /*----------------------------------------------------------------------------------------------*/ | ||
204 | |||
149 | @interface MyDelegate : NSResponder<NSApplicationDelegate, NSTouchBarDelegate> { | 205 | @interface MyDelegate : NSResponder<NSApplicationDelegate, NSTouchBarDelegate> { |
150 | enum iTouchBarVariant touchBarVariant; | 206 | enum iTouchBarVariant touchBarVariant; |
151 | NSString *currentAppearanceName; | 207 | NSString *currentAppearanceName; |
152 | NSObject<NSApplicationDelegate> *sdlDelegate; | 208 | NSObject<NSApplicationDelegate> *sdlDelegate; |
153 | NSMutableDictionary<NSString *, NSString*> *menuCommands; | 209 | //NSMutableDictionary<NSString *, NSString*> *menuCommands; |
210 | MenuCommands *menuCommands; | ||
154 | } | 211 | } |
155 | - (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl; | 212 | - (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl; |
156 | - (NSTouchBar *)makeTouchBar; | 213 | - (NSTouchBar *)makeTouchBar; |
@@ -165,7 +222,7 @@ iBool shouldDefaultToMetalRenderer_MacOS(void) { | |||
165 | - (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl { | 222 | - (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl { |
166 | [super init]; | 223 | [super init]; |
167 | currentAppearanceName = nil; | 224 | currentAppearanceName = nil; |
168 | menuCommands = [[NSMutableDictionary<NSString *, NSString *> alloc] init]; | 225 | menuCommands = [[MenuCommands alloc] init]; |
169 | touchBarVariant = default_TouchBarVariant; | 226 | touchBarVariant = default_TouchBarVariant; |
170 | sdlDelegate = sdl; | 227 | sdlDelegate = sdl; |
171 | return self; | 228 | return self; |
@@ -182,6 +239,14 @@ iBool shouldDefaultToMetalRenderer_MacOS(void) { | |||
182 | self.touchBar = nil; | 239 | self.touchBar = nil; |
183 | } | 240 | } |
184 | 241 | ||
242 | - (MenuCommands *)menuCommands { | ||
243 | return menuCommands; | ||
244 | } | ||
245 | |||
246 | - (void)postMenuItemCommand:(id)sender { | ||
247 | [menuCommands postMenuItemCommand:sender]; | ||
248 | } | ||
249 | |||
185 | static void appearanceChanged_MacOS_(NSString *name) { | 250 | static void appearanceChanged_MacOS_(NSString *name) { |
186 | const iBool isDark = [name containsString:@"Dark"]; | 251 | const iBool isDark = [name containsString:@"Dark"]; |
187 | const iBool isHighContrast = [name containsString:@"HighContrast"]; | 252 | const iBool isHighContrast = [name containsString:@"HighContrast"]; |
@@ -198,10 +263,6 @@ static void appearanceChanged_MacOS_(NSString *name) { | |||
198 | } | 263 | } |
199 | } | 264 | } |
200 | 265 | ||
201 | - (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem { | ||
202 | [menuCommands setObject:command forKey:[menuItem title]]; | ||
203 | } | ||
204 | |||
205 | - (BOOL)application:(NSApplication *)app openFile:(NSString *)filename { | 266 | - (BOOL)application:(NSApplication *)app openFile:(NSString *)filename { |
206 | return [sdlDelegate application:app openFile:filename]; | 267 | return [sdlDelegate application:app openFile:filename]; |
207 | } | 268 | } |
@@ -258,31 +319,11 @@ static void appearanceChanged_MacOS_(NSString *name) { | |||
258 | ignoreImmediateKeyDownEvents_(); | 319 | ignoreImmediateKeyDownEvents_(); |
259 | } | 320 | } |
260 | 321 | ||
261 | static void ignoreImmediateKeyDownEvents_(void) { | ||
262 | /* SDL ignores menu key equivalents so the keydown events will be posted regardless. | ||
263 | However, we shouldn't double-activate menu items when a shortcut key is used in our | ||
264 | widgets. Quite a kludge: take advantage of Window's focus-acquisition threshold to | ||
265 | ignore the immediately following key down events. */ | ||
266 | get_Window()->focusGainedAt = SDL_GetTicks(); | ||
267 | } | ||
268 | |||
269 | - (void)closeTab { | 322 | - (void)closeTab { |
270 | postCommand_App("tabs.close"); | 323 | postCommand_App("tabs.close"); |
271 | ignoreImmediateKeyDownEvents_(); | 324 | ignoreImmediateKeyDownEvents_(); |
272 | } | 325 | } |
273 | 326 | ||
274 | - (NSString *)commandForItem:(NSMenuItem *)menuItem { | ||
275 | return [menuCommands objectForKey:[menuItem title]]; | ||
276 | } | ||
277 | |||
278 | - (void)postMenuItemCommand:(id)sender { | ||
279 | NSString *command = [menuCommands objectForKey:[(NSMenuItem *)sender title]]; | ||
280 | if (command) { | ||
281 | postCommand_App([command cStringUsingEncoding:NSUTF8StringEncoding]); | ||
282 | ignoreImmediateKeyDownEvents_(); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | - (void)sidebarModePressed:(id)sender { | 327 | - (void)sidebarModePressed:(id)sender { |
287 | NSSegmentedControl *seg = sender; | 328 | NSSegmentedControl *seg = sender; |
288 | postCommandf_App("sidebar.mode arg:%d toggle:1", (int) [seg selectedSegment]); | 329 | postCommandf_App("sidebar.mode arg:%d toggle:1", (int) [seg selectedSegment]); |
@@ -403,7 +444,7 @@ void enableMenuItem_MacOS(const char *menuItemCommand, iBool enable) { | |||
403 | NSMenu *menu = mainMenuItem.submenu; | 444 | NSMenu *menu = mainMenuItem.submenu; |
404 | if (menu) { | 445 | if (menu) { |
405 | for (NSMenuItem *menuItem in menu.itemArray) { | 446 | for (NSMenuItem *menuItem in menu.itemArray) { |
406 | NSString *command = [myDel commandForItem:menuItem]; | 447 | NSString *command = [[myDel menuCommands] commandForMenuItem:menuItem]; |
407 | if (command) { | 448 | if (command) { |
408 | if (!iCmpStr([command cStringUsingEncoding:NSUTF8StringEncoding], | 449 | if (!iCmpStr([command cStringUsingEncoding:NSUTF8StringEncoding], |
409 | menuItemCommand)) { | 450 | menuItemCommand)) { |
@@ -483,18 +524,8 @@ void removeMenu_MacOS(int atIndex) { | |||
483 | [appMenu removeItemAtIndex:atIndex]; | 524 | [appMenu removeItemAtIndex:atIndex]; |
484 | } | 525 | } |
485 | 526 | ||
486 | void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem *items, size_t count) { | 527 | static void makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) { |
487 | NSApplication *app = [NSApplication sharedApplication]; | 528 | for (size_t i = 0; i < n && items[i].label; ++i) { |
488 | MyDelegate *myDel = (MyDelegate *) app.delegate; | ||
489 | NSMenu *appMenu = [app mainMenu]; | ||
490 | menuLabel = translateCStr_Lang(menuLabel); | ||
491 | NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] | ||
492 | action:nil | ||
493 | keyEquivalent:@"" | ||
494 | atIndex:atIndex]; | ||
495 | NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; | ||
496 | [menu setAutoenablesItems:NO]; | ||
497 | for (size_t i = 0; i < count; ++i) { | ||
498 | const char *label = translateCStr_Lang(items[i].label); | 529 | const char *label = translateCStr_Lang(items[i].label); |
499 | if (label[0] == '\v') { | 530 | if (label[0] == '\v') { |
500 | /* Skip the formatting escape. */ | 531 | /* Skip the formatting escape. */ |
@@ -505,13 +536,19 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * | |||
505 | } | 536 | } |
506 | else { | 537 | else { |
507 | const iBool hasCommand = (items[i].command && items[i].command[0]); | 538 | const iBool hasCommand = (items[i].command && items[i].command[0]); |
508 | NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:label] | 539 | iString itemTitle; |
540 | initCStr_String(&itemTitle, label); | ||
541 | removeIconPrefix_String(&itemTitle); | ||
542 | NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:cstr_String(&itemTitle)] | ||
509 | action:(hasCommand ? @selector(postMenuItemCommand:) : nil) | 543 | action:(hasCommand ? @selector(postMenuItemCommand:) : nil) |
510 | keyEquivalent:@""]; | 544 | keyEquivalent:@""]; |
545 | deinit_String(&itemTitle); | ||
546 | [item setTarget:commands]; | ||
511 | int key = items[i].key; | 547 | int key = items[i].key; |
512 | int kmods = items[i].kmods; | 548 | int kmods = items[i].kmods; |
513 | if (hasCommand) { | 549 | if (hasCommand) { |
514 | [myDel setCommand:[NSString stringWithUTF8String:items[i].command] forMenuItem:item]; | 550 | [commands setCommand:[NSString stringWithUTF8String:items[i].command] |
551 | forMenuItem:item]; | ||
515 | /* Bindings may have a different key. */ | 552 | /* Bindings may have a different key. */ |
516 | const iBinding *bind = findCommand_Keys(items[i].command); | 553 | const iBinding *bind = findCommand_Keys(items[i].command); |
517 | if (bind && bind->id < builtIn_BindingId) { | 554 | if (bind && bind->id < builtIn_BindingId) { |
@@ -522,6 +559,20 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * | |||
522 | setShortcut_NSMenuItem_(item, key, kmods); | 559 | setShortcut_NSMenuItem_(item, key, kmods); |
523 | } | 560 | } |
524 | } | 561 | } |
562 | } | ||
563 | |||
564 | void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem *items, size_t count) { | ||
565 | NSApplication *app = [NSApplication sharedApplication]; | ||
566 | MyDelegate *myDel = (MyDelegate *) app.delegate; | ||
567 | NSMenu *appMenu = [app mainMenu]; | ||
568 | menuLabel = translateCStr_Lang(menuLabel); | ||
569 | NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] | ||
570 | action:nil | ||
571 | keyEquivalent:@"" | ||
572 | atIndex:atIndex]; | ||
573 | NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; | ||
574 | [menu setAutoenablesItems:NO]; | ||
575 | makeMenuItems_(menu, [myDel menuCommands], items, count); | ||
525 | [mainItem setSubmenu:menu]; | 576 | [mainItem setSubmenu:menu]; |
526 | [menu release]; | 577 | [menu release]; |
527 | } | 578 | } |
@@ -542,7 +593,7 @@ void handleCommand_MacOS(const char *cmd) { | |||
542 | if (menu) { | 593 | if (menu) { |
543 | int itemIndex = 0; | 594 | int itemIndex = 0; |
544 | for (NSMenuItem *menuItem in menu.itemArray) { | 595 | for (NSMenuItem *menuItem in menu.itemArray) { |
545 | NSString *command = [myDel commandForItem:menuItem]; | 596 | NSString *command = [[myDel menuCommands] commandForMenuItem:menuItem]; |
546 | if (!command && mainIndex == 6 && itemIndex == 0) { | 597 | if (!command && mainIndex == 6 && itemIndex == 0) { |
547 | /* Window > Close */ | 598 | /* Window > Close */ |
548 | command = @"tabs.close"; | 599 | command = @"tabs.close"; |
@@ -568,3 +619,29 @@ void handleCommand_MacOS(const char *cmd) { | |||
568 | void log_MacOS(const char *msg) { | 619 | void log_MacOS(const char *msg) { |
569 | NSLog(@"%s", msg); | 620 | NSLog(@"%s", msg); |
570 | } | 621 | } |
622 | |||
623 | void showPopupMenu_MacOS(iWidget *source, iInt2 windowCoord, const iMenuItem *items, size_t n) { | ||
624 | NSMenu * menu = [[NSMenu alloc] init]; | ||
625 | MenuCommands *menuCommands = [[MenuCommands alloc] init]; | ||
626 | iWindow * window = as_Window(mainWindow_App()); | ||
627 | NSWindow * nsWindow = nsWindow_(window->win); | ||
628 | /* View coordinates are flipped. */ | ||
629 | windowCoord.y = window->size.y - windowCoord.y; | ||
630 | windowCoord = divf_I2(windowCoord, window->pixelRatio); | ||
631 | NSPoint screenPoint = [nsWindow convertPointToScreen:(CGPoint){ windowCoord.x, windowCoord.y }]; | ||
632 | makeMenuItems_(menu, menuCommands, items, n); | ||
633 | [menuCommands setSource:source]; | ||
634 | [menu popUpMenuPositioningItem:nil atLocation:screenPoint inView:nil]; | ||
635 | [menu release]; | ||
636 | [menuCommands release]; | ||
637 | /* The right mouse button has now been released so let SDL know about it. The button up event | ||
638 | was consumed by the popup menu so it got never passed to SDL. */ | ||
639 | SEL sel = NSSelectorFromString(@"syncMouseButtonState"); /* custom method */ | ||
640 | if ([[nsWindow delegate] respondsToSelector:sel]) { | ||
641 | NSInvocation *call = [NSInvocation invocationWithMethodSignature: | ||
642 | [NSMethodSignature signatureWithObjCTypes:"v@:"]]; | ||
643 | [call setSelector:sel]; | ||
644 | [call invokeWithTarget:[nsWindow delegate]]; | ||
645 | } | ||
646 | } | ||
647 | |||
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index f02bf408..37d655b7 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -2365,8 +2365,8 @@ static void draw_InputWidget_(const iInputWidget *d) { | |||
2365 | cursorChar.start = charPos_InputWidget_(d, d->cursor); | 2365 | cursorChar.start = charPos_InputWidget_(d, d->cursor); |
2366 | iChar ch = 0; | 2366 | iChar ch = 0; |
2367 | int n = decodeBytes_MultibyteChar(cursorChar.start, | 2367 | int n = decodeBytes_MultibyteChar(cursorChar.start, |
2368 | constEnd_String(&constCursorLine_InputWidget_(d)->text), | 2368 | constEnd_String(&constCursorLine_InputWidget_(d)->text), |
2369 | &ch); | 2369 | &ch); |
2370 | cursorChar.end = cursorChar.start + iMax(n, 0); | 2370 | cursorChar.end = cursorChar.start + iMax(n, 0); |
2371 | if (ch) { | 2371 | if (ch) { |
2372 | if (d->inFlags & isSensitive_InputWidgetFlag) { | 2372 | if (d->inFlags & isSensitive_InputWidgetFlag) { |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 30eb1d36..c8748efa 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -611,19 +611,8 @@ iBool checkIcon_LabelWidget(iLabelWidget *d) { | |||
611 | d->icon = 0; | 611 | d->icon = 0; |
612 | return iFalse; | 612 | return iFalse; |
613 | } | 613 | } |
614 | iStringConstIterator iter; | 614 | d->icon = removeIconPrefix_String(&d->label); |
615 | init_StringConstIterator(&iter, &d->label); | 615 | return d->icon != 0; |
616 | const iChar icon = iter.value; | ||
617 | next_StringConstIterator(&iter); | ||
618 | if (iter.value == ' ' && icon >= 0x100) { | ||
619 | d->icon = icon; | ||
620 | remove_Block(&d->label.chars, 0, iter.next - constBegin_String(&d->label)); | ||
621 | return iTrue; | ||
622 | } | ||
623 | else { | ||
624 | d->icon = 0; | ||
625 | } | ||
626 | return iFalse; | ||
627 | } | 616 | } |
628 | 617 | ||
629 | iChar icon_LabelWidget(const iLabelWidget *d) { | 618 | iChar icon_LabelWidget(const iLabelWidget *d) { |
diff --git a/src/ui/root.c b/src/ui/root.c index 9e290b05..595184cc 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -435,6 +435,7 @@ static void updateNavBarIdentity_(iWidget *navBar) { | |||
435 | setOutline_LabelWidget(toolButton, ident == NULL); | 435 | setOutline_LabelWidget(toolButton, ident == NULL); |
436 | /* Update menu. */ | 436 | /* Update menu. */ |
437 | iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); | 437 | iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); |
438 | if (!idItem) return; | ||
438 | const iString *subjectName = ident ? name_GmIdentity(ident) : NULL; | 439 | const iString *subjectName = ident ? name_GmIdentity(ident) : NULL; |
439 | setTextCStr_LabelWidget( | 440 | setTextCStr_LabelWidget( |
440 | idItem, | 441 | idItem, |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index fe33c540..fdfb5300 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -1457,40 +1457,25 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1457 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { | 1457 | if (d->mode == bookmarks_SidebarMode && d->contextItem) { |
1458 | const iBookmark *bm = get_Bookmarks(bookmarks_App(), d->contextItem->id); | 1458 | const iBookmark *bm = get_Bookmarks(bookmarks_App(), d->contextItem->id); |
1459 | if (bm) { | 1459 | if (bm) { |
1460 | iLabelWidget *menuItem = findMenuItem_Widget(d->menu, | 1460 | updateMenuItemLabel_Widget(d->menu, "bookmark.tag tag:homepage", |
1461 | "bookmark.tag tag:homepage"); | 1461 | hasTag_Bookmark(bm, homepage_BookmarkTag) |
1462 | if (menuItem) { | 1462 | ? home_Icon " ${bookmark.untag.home}" |
1463 | setTextCStr_LabelWidget(menuItem, | 1463 | : home_Icon " ${bookmark.tag.home}"); |
1464 | hasTag_Bookmark(bm, homepage_BookmarkTag) | 1464 | updateMenuItemLabel_Widget(d->menu, "bookmark.tag tag:subscribed", |
1465 | ? home_Icon " ${bookmark.untag.home}" | 1465 | hasTag_Bookmark(bm, subscribed_BookmarkTag) |
1466 | : home_Icon " ${bookmark.tag.home}"); | ||
1467 | checkIcon_LabelWidget(menuItem); | ||
1468 | } | ||
1469 | menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:subscribed"); | ||
1470 | if (menuItem) { | ||
1471 | setTextCStr_LabelWidget(menuItem, | ||
1472 | hasTag_Bookmark(bm, subscribed_BookmarkTag) | ||
1473 | ? star_Icon " ${bookmark.untag.sub}" | 1466 | ? star_Icon " ${bookmark.untag.sub}" |
1474 | : star_Icon " ${bookmark.tag.sub}"); | 1467 | : star_Icon " ${bookmark.tag.sub}"); |
1475 | checkIcon_LabelWidget(menuItem); | 1468 | updateMenuItemLabel_Widget(d->menu, "bookmark.tag tag:remotesource", |
1476 | } | 1469 | hasTag_Bookmark(bm, remoteSource_BookmarkTag) |
1477 | menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource"); | ||
1478 | if (menuItem) { | ||
1479 | setTextCStr_LabelWidget(menuItem, | ||
1480 | hasTag_Bookmark(bm, remoteSource_BookmarkTag) | ||
1481 | ? downArrowBar_Icon " ${bookmark.untag.remote}" | 1470 | ? downArrowBar_Icon " ${bookmark.untag.remote}" |
1482 | : downArrowBar_Icon " ${bookmark.tag.remote}"); | 1471 | : downArrowBar_Icon " ${bookmark.tag.remote}"); |
1483 | checkIcon_LabelWidget(menuItem); | ||
1484 | } | ||
1485 | } | 1472 | } |
1486 | } | 1473 | } |
1487 | else if (d->mode == feeds_SidebarMode && d->contextItem) { | 1474 | else if (d->mode == feeds_SidebarMode && d->contextItem) { |
1488 | iLabelWidget *menuItem = findMenuItem_Widget(d->menu, "feed.entry.toggleread"); | ||
1489 | const iBool isRead = d->contextItem->indent == 0; | 1475 | const iBool isRead = d->contextItem->indent == 0; |
1490 | setTextCStr_LabelWidget(menuItem, | 1476 | updateMenuItemLabel_Widget(d->menu, "feed.entry.toggleread", |
1491 | isRead ? circle_Icon " ${feeds.entry.markunread}" | 1477 | isRead ? circle_Icon " ${feeds.entry.markunread}" |
1492 | : circleWhite_Icon " ${feeds.entry.markread}"); | 1478 | : circleWhite_Icon " ${feeds.entry.markread}"); |
1493 | checkIcon_LabelWidget(menuItem); | ||
1494 | } | 1479 | } |
1495 | else if (d->mode == identities_SidebarMode) { | 1480 | else if (d->mode == identities_SidebarMode) { |
1496 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); | 1481 | const iGmIdentity *ident = constHoverIdentity_SidebarWidget_(d); |
diff --git a/src/ui/util.c b/src/ui/util.c index 38977b96..0baf541d 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -44,6 +44,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
44 | # include "../ios.h" | 44 | # include "../ios.h" |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | #if defined (iPlatformAppleDesktop) | ||
48 | # include "macos.h" | ||
49 | #endif | ||
50 | |||
47 | #include <the_Foundation/math.h> | 51 | #include <the_Foundation/math.h> |
48 | #include <the_Foundation/path.h> | 52 | #include <the_Foundation/path.h> |
49 | #include <SDL_timer.h> | 53 | #include <SDL_timer.h> |
@@ -749,10 +753,65 @@ void makeMenuItems_Widget(iWidget *menu, const iMenuItem *items, size_t n) { | |||
749 | } | 753 | } |
750 | } | 754 | } |
751 | } | 755 | } |
752 | }} | 756 | } |
757 | } | ||
758 | |||
759 | static iArray *deepCopyMenuItems_(iWidget *menu, const iMenuItem *items, size_t n) { | ||
760 | iArray *array = new_Array(sizeof(iMenuItem)); | ||
761 | iString cmd; | ||
762 | init_String(&cmd); | ||
763 | for (size_t i = 0; i < n; i++) { | ||
764 | const iMenuItem *item = &items[i]; | ||
765 | const char *itemCommand = item->command; | ||
766 | #if 0 | ||
767 | if (itemCommand) { | ||
768 | /* Make it appear the command is coming from the right widget. */ | ||
769 | setCStr_String(&cmd, itemCommand); | ||
770 | if (!hasLabel_Command(itemCommand, "ptr")) { | ||
771 | size_t firstSpace = indexOf_String(&cmd, ' '); | ||
772 | iBlock ptr; | ||
773 | init_Block(&ptr, 0); | ||
774 | printf_Block(&ptr, " ptr:%p", menu); | ||
775 | if (firstSpace != iInvalidPos) { | ||
776 | insertData_Block(&cmd.chars, firstSpace, data_Block(&ptr), size_Block(&ptr)); | ||
777 | } | ||
778 | else { | ||
779 | append_Block(&cmd.chars, &ptr); | ||
780 | } | ||
781 | deinit_Block(&ptr); | ||
782 | } | ||
783 | itemCommand = cstr_String(&cmd); | ||
784 | } | ||
785 | #endif | ||
786 | pushBack_Array(array, &(iMenuItem){ | ||
787 | item->label ? strdup(item->label) : NULL, | ||
788 | item->key, | ||
789 | item->kmods, | ||
790 | itemCommand ? strdup(itemCommand) : NULL /* NOTE: Only works with string commands. */ | ||
791 | }); | ||
792 | } | ||
793 | deinit_String(&cmd); | ||
794 | return array; | ||
795 | } | ||
796 | |||
797 | static void deleteMenuItems_(iArray *items) { | ||
798 | iForEach(Array, i, items) { | ||
799 | iMenuItem *item = i.value; | ||
800 | free((void *) item->label); | ||
801 | free((void *) item->command); | ||
802 | } | ||
803 | delete_Array(items); | ||
804 | } | ||
753 | 805 | ||
754 | iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | 806 | iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { |
755 | iWidget *menu = new_Widget(); | 807 | iWidget *menu = new_Widget(); |
808 | #if defined (iHaveNativeMenus) | ||
809 | setFlags_Widget(menu, hidden_WidgetFlag | nativeMenu_WidgetFlag, iTrue); | ||
810 | setUserData_Object(menu, deepCopyMenuItems_(menu, items, n)); | ||
811 | addChild_Widget(parent, menu); | ||
812 | iRelease(menu); /* owned by parent now */ | ||
813 | #else | ||
814 | /* Non-native custom popup menu. This may still be displayed inside a separate window. */ | ||
756 | setDrawBufferEnabled_Widget(menu, iTrue); | 815 | setDrawBufferEnabled_Widget(menu, iTrue); |
757 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); | 816 | setBackgroundColor_Widget(menu, uiBackgroundMenu_ColorId); |
758 | if (deviceType_App() != desktop_AppDeviceType) { | 817 | if (deviceType_App() != desktop_AppDeviceType) { |
@@ -777,6 +836,7 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
777 | iWidget *cancel = addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel"); | 836 | iWidget *cancel = addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel"); |
778 | setId_Widget(cancel, "menu.cancel"); | 837 | setId_Widget(cancel, "menu.cancel"); |
779 | setFlags_Widget(cancel, disabled_WidgetFlag, iTrue); | 838 | setFlags_Widget(cancel, disabled_WidgetFlag, iTrue); |
839 | #endif | ||
780 | return menu; | 840 | return menu; |
781 | } | 841 | } |
782 | 842 | ||
@@ -812,11 +872,52 @@ static void updateMenuItemFonts_Widget_(iWidget *d) { | |||
812 | } | 872 | } |
813 | } | 873 | } |
814 | 874 | ||
875 | void updateMenuItemLabel_Widget(iWidget *menu, const char *command, const char *newLabel) { | ||
876 | if (~flags_Widget(menu) & nativeMenu_WidgetFlag) { | ||
877 | iLabelWidget *menuItem = findMenuItem_Widget(menu, command); | ||
878 | if (menuItem) { | ||
879 | setTextCStr_LabelWidget(menuItem, newLabel); | ||
880 | checkIcon_LabelWidget(menuItem); | ||
881 | } | ||
882 | } | ||
883 | else { | ||
884 | iArray *items = userData_Object(menu); | ||
885 | iAssert(items); | ||
886 | iForEach(Array, i, items) { | ||
887 | iMenuItem *item = i.value; | ||
888 | if (item->command && !iCmpStr(item->command, command)) { | ||
889 | free((void *) item->label); | ||
890 | item->label = strdup(newLabel); | ||
891 | break; | ||
892 | } | ||
893 | } | ||
894 | } | ||
895 | } | ||
896 | |||
815 | iLocalDef iBool isUsingMenuPopupWindows_(void) { | 897 | iLocalDef iBool isUsingMenuPopupWindows_(void) { |
816 | return deviceType_App() == desktop_AppDeviceType; | 898 | return deviceType_App() == desktop_AppDeviceType; |
817 | } | 899 | } |
818 | 900 | ||
901 | void releaseNativeMenu_Widget(iWidget *d) { | ||
902 | #if defined (iHaveNativeMenus) | ||
903 | iArray *items = userData_Object(d); | ||
904 | iAssert(flags_Widget(d) & nativeMenu_WidgetFlag); | ||
905 | iAssert(items); | ||
906 | deleteMenuItems_(items); | ||
907 | setUserData_Object(d, NULL); | ||
908 | #else | ||
909 | iUnused(d); | ||
910 | #endif | ||
911 | } | ||
912 | |||
819 | void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { | 913 | void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { |
914 | #if defined (iHaveNativeMenus) | ||
915 | const iArray *items = userData_Object(d); | ||
916 | iAssert(flags_Widget(d) & nativeMenu_WidgetFlag); | ||
917 | iAssert(items); | ||
918 | showPopupMenu_MacOS(d, mouseCoord_Window(get_Window(), 0), | ||
919 | constData_Array(items), size_Array(items)); | ||
920 | #else | ||
820 | const iRect rootRect = rect_Root(d->root); | 921 | const iRect rootRect = rect_Root(d->root); |
821 | const iInt2 rootSize = rootRect.size; | 922 | const iInt2 rootSize = rootRect.size; |
822 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); | 923 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); |
@@ -904,9 +1005,13 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { | |||
904 | postCommand_Widget(d, "menu.opened"); | 1005 | postCommand_Widget(d, "menu.opened"); |
905 | } | 1006 | } |
906 | setupMenuTransition_Mobile(d, iTrue); | 1007 | setupMenuTransition_Mobile(d, iTrue); |
1008 | #endif | ||
907 | } | 1009 | } |
908 | 1010 | ||
909 | void closeMenu_Widget(iWidget *d) { | 1011 | void closeMenu_Widget(iWidget *d) { |
1012 | if (flags_Widget(d) & nativeMenu_WidgetFlag) { | ||
1013 | return; /* Handled natively. */ | ||
1014 | } | ||
910 | if (d == NULL || flags_Widget(d) & hidden_WidgetFlag) { | 1015 | if (d == NULL || flags_Widget(d) & hidden_WidgetFlag) { |
911 | return; /* Already closed. */ | 1016 | return; /* Already closed. */ |
912 | } | 1017 | } |
@@ -1780,6 +1885,21 @@ size_t findWidestLabel_MenuItem(const iMenuItem *items, size_t num) { | |||
1780 | return widestPos; | 1885 | return widestPos; |
1781 | } | 1886 | } |
1782 | 1887 | ||
1888 | iChar removeIconPrefix_String(iString *d) { | ||
1889 | if (isEmpty_String(d)) { | ||
1890 | return 0; | ||
1891 | } | ||
1892 | iStringConstIterator iter; | ||
1893 | init_StringConstIterator(&iter, d); | ||
1894 | iChar icon = iter.value; | ||
1895 | next_StringConstIterator(&iter); | ||
1896 | if (iter.value == ' ' && icon >= 0x100) { | ||
1897 | remove_Block(&d->chars, 0, iter.next - constBegin_String(d)); | ||
1898 | return icon; | ||
1899 | } | ||
1900 | return 0; | ||
1901 | } | ||
1902 | |||
1783 | iWidget *makeDialog_Widget(const char *id, | 1903 | iWidget *makeDialog_Widget(const char *id, |
1784 | const iMenuItem *itemsNullTerminated, | 1904 | const iMenuItem *itemsNullTerminated, |
1785 | const iMenuItem *actions, size_t numActions) { | 1905 | const iMenuItem *actions, size_t numActions) { |
diff --git a/src/ui/util.h b/src/ui/util.h index 3dd4e153..d929143f 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -226,16 +226,20 @@ struct Impl_MenuItem { | |||
226 | }; | 226 | }; |
227 | }; | 227 | }; |
228 | 228 | ||
229 | iWidget * makeMenu_Widget (iWidget *parent, const iMenuItem *items, size_t n); /* returns no ref */ | 229 | iWidget * makeMenu_Widget (iWidget *parent, const iMenuItem *items, size_t n); /* returns no ref */ |
230 | void makeMenuItems_Widget(iWidget *menu, const iMenuItem *items, size_t n); | 230 | void makeMenuItems_Widget (iWidget *menu, const iMenuItem *items, size_t n); |
231 | void openMenu_Widget (iWidget *, iInt2 windowCoord); | 231 | void openMenu_Widget (iWidget *, iInt2 windowCoord); |
232 | void openMenuFlags_Widget(iWidget *, iInt2 windowCoord, iBool postCommands); | 232 | void openMenuFlags_Widget (iWidget *, iInt2 windowCoord, iBool postCommands); |
233 | void closeMenu_Widget (iWidget *); | 233 | void closeMenu_Widget (iWidget *); |
234 | void releaseNativeMenu_Widget(iWidget *); | ||
234 | 235 | ||
235 | size_t findWidestLabel_MenuItem (const iMenuItem *items, size_t num); | 236 | size_t findWidestLabel_MenuItem (const iMenuItem *items, size_t num); |
236 | 237 | ||
238 | iChar removeIconPrefix_String (iString *); | ||
239 | |||
237 | iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); | 240 | iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); |
238 | void setMenuItemDisabled_Widget (iWidget *menu, const char *command, iBool disable); | 241 | void setMenuItemDisabled_Widget (iWidget *menu, const char *command, iBool disable); |
242 | void updateMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel); | ||
239 | 243 | ||
240 | int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */ | 244 | int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */ |
241 | 245 | ||
diff --git a/src/ui/widget.c b/src/ui/widget.c index 7b33a752..6b9ee11d 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -163,6 +163,9 @@ void deinit_Widget(iWidget *d) { | |||
163 | if (win->hover == d) { | 163 | if (win->hover == d) { |
164 | win->hover = NULL; | 164 | win->hover = NULL; |
165 | } | 165 | } |
166 | if (d->flags & nativeMenu_WidgetFlag) { | ||
167 | releaseNativeMenu_Widget(d); | ||
168 | } | ||
166 | widgetDestroyed_Touch(d); | 169 | widgetDestroyed_Touch(d); |
167 | } | 170 | } |
168 | 171 | ||
diff --git a/src/ui/widget.h b/src/ui/widget.h index 0eab69c1..9243c00a 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -121,6 +121,7 @@ enum iWidgetFlag { | |||
121 | #define destroyPending_WidgetFlag iBit64(61) | 121 | #define destroyPending_WidgetFlag iBit64(61) |
122 | #define leftEdgeDraggable_WidgetFlag iBit64(62) | 122 | #define leftEdgeDraggable_WidgetFlag iBit64(62) |
123 | #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ | 123 | #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ |
124 | #define nativeMenu_WidgetFlag iBit64(64) | ||
124 | 125 | ||
125 | enum iWidgetAddPos { | 126 | enum iWidgetAddPos { |
126 | back_WidgetAddPos, | 127 | back_WidgetAddPos, |