diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-21 09:19:21 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-21 09:19:21 +0300 |
commit | e48a9a27bd11dbef9531bd12d3c0c60cc771b2c3 (patch) | |
tree | 0044e6de2a788c061f0fe0eb239ab96d22acadd4 | |
parent | ba6d544b1237aea6901cf5b4f6562db9eb0f46c4 (diff) |
macOS: Native context menus
Popup context menus now use NSMenu. There are still has a few glitches with the navbar identity button, but most menus are working.
SDL required another little tweak to force it to update mouse button state after the synchronously handled context menu goes away. Otherwise SDL's internal mouse button state shows that the right mouse button is held down.
-rw-r--r-- | sdl2-macos-ios.diff | 70 | ||||
-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 |
12 files changed, 325 insertions, 109 deletions
diff --git a/sdl2-macos-ios.diff b/sdl2-macos-ios.diff index 04d0f2e5..ecb23824 100644 --- a/sdl2-macos-ios.diff +++ b/sdl2-macos-ios.diff | |||
@@ -1,8 +1,8 @@ | |||
1 | Only in SDL2-2.0.14/src: .DS_Store | 1 | diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c |
2 | diff -ru SDL2-2.0.14-original/src/events/SDL_mouse.c SDL2-2.0.14/src/events/SDL_mouse.c | 2 | index a776bcca8..2d5e685e3 100644 |
3 | --- SDL2-2.0.14-original/src/events/SDL_mouse.c 2020-12-21 19:44:36.000000000 +0200 | 3 | --- a/src/events/SDL_mouse.c |
4 | +++ SDL2-2.0.14/src/events/SDL_mouse.c 2021-01-06 18:31:18.000000000 +0200 | 4 | +++ b/src/events/SDL_mouse.c |
5 | @@ -647,8 +647,8 @@ | 5 | @@ -647,8 +647,8 @@ SDL_SendMouseWheel(SDL_Window * window, SDL_MouseID mouseID, float x, float y, S |
6 | event.wheel.preciseX = x; | 6 | event.wheel.preciseX = x; |
7 | event.wheel.preciseY = y; | 7 | event.wheel.preciseY = y; |
8 | #endif | 8 | #endif |
@@ -13,11 +13,11 @@ diff -ru SDL2-2.0.14-original/src/events/SDL_mouse.c SDL2-2.0.14/src/events/SDL_ | |||
13 | event.wheel.direction = (Uint32)direction; | 13 | event.wheel.direction = (Uint32)direction; |
14 | posted = (SDL_PushEvent(&event) > 0); | 14 | posted = (SDL_PushEvent(&event) > 0); |
15 | } | 15 | } |
16 | Only in SDL2-2.0.14/src/video: .DS_Store | 16 | diff --git a/src/video/cocoa/SDL_cocoamouse.m b/src/video/cocoa/SDL_cocoamouse.m |
17 | diff -ru SDL2-2.0.14-original/src/video/cocoa/SDL_cocoamouse.m SDL2-2.0.14/src/video/cocoa/SDL_cocoamouse.m | 17 | index e9d832d64..4cfa3624b 100644 |
18 | --- SDL2-2.0.14-original/src/video/cocoa/SDL_cocoamouse.m 2020-12-21 19:44:36.000000000 +0200 | 18 | --- a/src/video/cocoa/SDL_cocoamouse.m |
19 | +++ SDL2-2.0.14/src/video/cocoa/SDL_cocoamouse.m 2021-01-06 18:31:18.000000000 +0200 | 19 | +++ b/src/video/cocoa/SDL_cocoamouse.m |
20 | @@ -423,10 +423,16 @@ | 20 | @@ -463,10 +463,16 @@ + (NSCursor *)invisibleCursor |
21 | } | 21 | } |
22 | 22 | ||
23 | SDL_MouseID mouseID = mouse->mouseID; | 23 | SDL_MouseID mouseID = mouse->mouseID; |
@@ -36,10 +36,41 @@ diff -ru SDL2-2.0.14-original/src/video/cocoa/SDL_cocoamouse.m SDL2-2.0.14/src/v | |||
36 | if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) { | 36 | if ([event respondsToSelector:@selector(isDirectionInvertedFromDevice)]) { |
37 | if ([event isDirectionInvertedFromDevice] == YES) { | 37 | if ([event isDirectionInvertedFromDevice] == YES) { |
38 | direction = SDL_MOUSEWHEEL_FLIPPED; | 38 | direction = SDL_MOUSEWHEEL_FLIPPED; |
39 | Only in SDL2-2.0.14/src/video/cocoa: SDL_cocoamouse.m.orig | 39 | diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h |
40 | diff -ru SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.h SDL2-2.0.14/src/video/uikit/SDL_uikitviewcontroller.h | 40 | index 37bec665e..5e3a3995f 100644 |
41 | --- SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.h 2020-12-21 19:44:36.000000000 +0200 | 41 | --- a/src/video/cocoa/SDL_cocoawindow.h |
42 | +++ SDL2-2.0.14/src/video/uikit/SDL_uikitviewcontroller.h 2021-05-17 13:11:13.000000000 +0300 | 42 | +++ b/src/video/cocoa/SDL_cocoawindow.h |
43 | @@ -109,6 +109,8 @@ typedef enum | ||
44 | /* Touch event handling */ | ||
45 | -(void) handleTouches:(NSTouchPhase) phase withEvent:(NSEvent*) theEvent; | ||
46 | |||
47 | +-(void) syncMouseButtonState; | ||
48 | + | ||
49 | @end | ||
50 | /* *INDENT-ON* */ | ||
51 | |||
52 | diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m | ||
53 | index 7a1446f09..86db35600 100644 | ||
54 | --- a/src/video/cocoa/SDL_cocoawindow.m | ||
55 | +++ b/src/video/cocoa/SDL_cocoawindow.m | ||
56 | @@ -1073,6 +1073,13 @@ - (void)otherMouseDown:(NSEvent *)theEvent | ||
57 | [self mouseDown:theEvent]; | ||
58 | } | ||
59 | |||
60 | +- (void)syncMouseButtonState { | ||
61 | + SDL_Mouse *mouse = SDL_GetMouse(); | ||
62 | + if (mouse) { | ||
63 | + mouse->buttonstate = SDL_GetGlobalMouseState(NULL, NULL); | ||
64 | + } | ||
65 | +} | ||
66 | + | ||
67 | - (void)mouseUp:(NSEvent *)theEvent | ||
68 | { | ||
69 | const SDL_Mouse *mouse = SDL_GetMouse(); | ||
70 | diff --git a/src/video/uikit/SDL_uikitviewcontroller.h b/src/video/uikit/SDL_uikitviewcontroller.h | ||
71 | index f7f4c9de6..50c72aad0 100644 | ||
72 | --- a/src/video/uikit/SDL_uikitviewcontroller.h | ||
73 | +++ b/src/video/uikit/SDL_uikitviewcontroller.h | ||
43 | @@ -58,10 +58,13 @@ | 74 | @@ -58,10 +58,13 @@ |
44 | #if !TARGET_OS_TV | 75 | #if !TARGET_OS_TV |
45 | - (NSUInteger)supportedInterfaceOrientations; | 76 | - (NSUInteger)supportedInterfaceOrientations; |
@@ -54,10 +85,11 @@ diff -ru SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.h SDL2-2.0 | |||
54 | #endif | 85 | #endif |
55 | 86 | ||
56 | #if SDL_IPHONE_KEYBOARD | 87 | #if SDL_IPHONE_KEYBOARD |
57 | diff -ru SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.m SDL2-2.0.14/src/video/uikit/SDL_uikitviewcontroller.m | 88 | diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m |
58 | --- SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.m 2020-12-21 19:44:36.000000000 +0200 | 89 | index c51d1aed2..cd8db9517 100644 |
59 | +++ SDL2-2.0.14/src/video/uikit/SDL_uikitviewcontroller.m 2021-05-17 13:11:58.000000000 +0300 | 90 | --- a/src/video/uikit/SDL_uikitviewcontroller.m |
60 | @@ -104,6 +104,7 @@ | 91 | +++ b/src/video/uikit/SDL_uikitviewcontroller.m |
92 | @@ -105,6 +105,7 @@ - (instancetype)initWithSDLWindow:(SDL_Window *)_window | ||
61 | #endif | 93 | #endif |
62 | 94 | ||
63 | #if !TARGET_OS_TV | 95 | #if !TARGET_OS_TV |
@@ -65,7 +97,7 @@ diff -ru SDL2-2.0.14-original/src/video/uikit/SDL_uikitviewcontroller.m SDL2-2.0 | |||
65 | SDL_AddHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR, | 97 | SDL_AddHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR, |
66 | SDL_HideHomeIndicatorHintChanged, | 98 | SDL_HideHomeIndicatorHintChanged, |
67 | (__bridge void *) self); | 99 | (__bridge void *) self); |
68 | @@ -229,6 +230,17 @@ | 100 | @@ -230,6 +231,17 @@ - (BOOL)prefersHomeIndicatorAutoHidden |
69 | return hidden; | 101 | return hidden; |
70 | } | 102 | } |
71 | 103 | ||
@@ -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, |