summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-21 11:51:55 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-21 11:51:55 +0300
commit282e1376091e850565b5c5166755ae94afe56ad2 (patch)
tree8c4334f0dbe18bb1fa23939ff800ce9051c070d6
parente48a9a27bd11dbef9531bd12d3c0c60cc771b2c3 (diff)
macOS: Improving native menu behavior
Selected items and dynamic label updates.
-rw-r--r--src/macos.m35
-rw-r--r--src/ui/util.c66
-rw-r--r--src/ui/util.h2
3 files changed, 98 insertions, 5 deletions
diff --git a/src/macos.m b/src/macos.m
index cec53a7d..d9ee71e7 100644
--- a/src/macos.m
+++ b/src/macos.m
@@ -524,26 +524,53 @@ void removeMenu_MacOS(int atIndex) {
524 [appMenu removeItemAtIndex:atIndex]; 524 [appMenu removeItemAtIndex:atIndex];
525} 525}
526 526
527enum iColorId removeColorEscapes_String(iString *d) {
528 enum iColorId color = none_ColorId;
529 for (;;) {
530 const char *esc = strchr(cstr_String(d), '\v');
531 if (esc) {
532 const char *ptr = esc + 1;
533 color = 0;
534 if (*ptr == '\v') {
535 color += asciiExtended_ColorEscape;
536 ptr++;
537 }
538 color += *ptr - asciiBase_ColorEscape;
539 ptr++;
540 remove_Block(&d->chars, esc - cstr_String(d), ptr - esc);
541 }
542 else break;
543 }
544 return color;
545}
546
527static void makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) { 547static void makeMenuItems_(NSMenu *menu, MenuCommands *commands, const iMenuItem *items, size_t n) {
528 for (size_t i = 0; i < n && items[i].label; ++i) { 548 for (size_t i = 0; i < n && items[i].label; ++i) {
529 const char *label = translateCStr_Lang(items[i].label); 549 const char *label = translateCStr_Lang(items[i].label);
530 if (label[0] == '\v') {
531 /* Skip the formatting escape. */
532 label += 2;
533 }
534 if (equal_CStr(label, "---")) { 550 if (equal_CStr(label, "---")) {
535 [menu addItem:[NSMenuItem separatorItem]]; 551 [menu addItem:[NSMenuItem separatorItem]];
536 } 552 }
537 else { 553 else {
538 const iBool hasCommand = (items[i].command && items[i].command[0]); 554 const iBool hasCommand = (items[i].command && items[i].command[0]);
555 iBool isChecked = iFalse;
556 if (startsWith_CStr(label, "###")) {
557 isChecked = iTrue;
558 label += 3;
559 }
539 iString itemTitle; 560 iString itemTitle;
540 initCStr_String(&itemTitle, label); 561 initCStr_String(&itemTitle, label);
541 removeIconPrefix_String(&itemTitle); 562 removeIconPrefix_String(&itemTitle);
563 if (removeColorEscapes_String(&itemTitle) == uiTextCaution_ColorId) {
564// prependCStr_String(&itemTitle, "\u26a0\ufe0f ");
565 }
542 NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:cstr_String(&itemTitle)] 566 NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:cstr_String(&itemTitle)]
543 action:(hasCommand ? @selector(postMenuItemCommand:) : nil) 567 action:(hasCommand ? @selector(postMenuItemCommand:) : nil)
544 keyEquivalent:@""]; 568 keyEquivalent:@""];
545 deinit_String(&itemTitle); 569 deinit_String(&itemTitle);
546 [item setTarget:commands]; 570 [item setTarget:commands];
571 if (isChecked) {
572 [item setState:NSControlStateValueOn];
573 }
547 int key = items[i].key; 574 int key = items[i].key;
548 int kmods = items[i].kmods; 575 int kmods = items[i].kmods;
549 if (hasCommand) { 576 if (hasCommand) {
diff --git a/src/ui/util.c b/src/ui/util.c
index 0baf541d..a5b1cfb3 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -872,6 +872,36 @@ static void updateMenuItemFonts_Widget_(iWidget *d) {
872 } 872 }
873} 873}
874 874
875iMenuItem *findNativeMenuItem_Widget(iWidget *menu, const char *commandSuffix) {
876 iAssert(flags_Widget(menu) & nativeMenu_WidgetFlag);
877 iForEach(Array, i, userData_Object(menu)) {
878 iMenuItem *item = i.value;
879 if (item->command && endsWith_Rangecc(range_CStr(item->command), commandSuffix)) {
880 return item;
881 }
882 }
883 return NULL;
884}
885
886void setSelected_NativeMenuItem(iMenuItem *item, iBool isSelected) {
887 if (!item->label) {
888 return;
889 }
890 const iBool hasPrefix = startsWith_CStr(item->label, "###");
891 if (hasPrefix && !isSelected) {
892 char *label = strdup(item->label + 3);
893 free((char *) item->label);
894 item->label = label;
895 }
896 else if (!hasPrefix && isSelected) {
897 char *label = malloc(strlen(item->label) + 4);
898 memcpy(label, "###", 3);
899 strcpy(label + 3, item->label);
900 free((char *) item->label);
901 item->label = label;
902 }
903}
904
875void updateMenuItemLabel_Widget(iWidget *menu, const char *command, const char *newLabel) { 905void updateMenuItemLabel_Widget(iWidget *menu, const char *command, const char *newLabel) {
876 if (~flags_Widget(menu) & nativeMenu_WidgetFlag) { 906 if (~flags_Widget(menu) & nativeMenu_WidgetFlag) {
877 iLabelWidget *menuItem = findMenuItem_Widget(menu, command); 907 iLabelWidget *menuItem = findMenuItem_Widget(menu, command);
@@ -894,6 +924,14 @@ void updateMenuItemLabel_Widget(iWidget *menu, const char *command, const char *
894 } 924 }
895} 925}
896 926
927void unselectAllNativeMenuItems_Widget(iWidget *menu) {
928 iArray *items = userData_Object(menu);
929 iAssert(items);
930 iForEach(Array, i, items) {
931 setSelected_NativeMenuItem(i.value, iFalse);
932 }
933}
934
897iLocalDef iBool isUsingMenuPopupWindows_(void) { 935iLocalDef iBool isUsingMenuPopupWindows_(void) {
898 return deviceType_App() == desktop_AppDeviceType; 936 return deviceType_App() == desktop_AppDeviceType;
899} 937}
@@ -915,7 +953,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) {
915 const iArray *items = userData_Object(d); 953 const iArray *items = userData_Object(d);
916 iAssert(flags_Widget(d) & nativeMenu_WidgetFlag); 954 iAssert(flags_Widget(d) & nativeMenu_WidgetFlag);
917 iAssert(items); 955 iAssert(items);
918 showPopupMenu_MacOS(d, mouseCoord_Window(get_Window(), 0), 956 showPopupMenu_MacOS(d, windowCoord, //mouseCoord_Window(get_Window(), 0),
919 constData_Array(items), size_Array(items)); 957 constData_Array(items), size_Array(items));
920#else 958#else
921 const iRect rootRect = rect_Root(d->root); 959 const iRect rootRect = rect_Root(d->root);
@@ -1075,8 +1113,34 @@ iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *ite
1075 return button; 1113 return button;
1076} 1114}
1077 1115
1116const iString *removeMenuItemLabelPrefixes_String(const iString *d) {
1117 iString *str = copy_String(d);
1118 for (;;) {
1119 if (startsWith_String(str, "###")) {
1120 remove_Block(&str->chars, 0, 3);
1121 continue;
1122 }
1123 if (startsWith_String(str, "```")) {
1124 remove_Block(&str->chars, 0, 3);
1125 continue;
1126 }
1127 break;
1128 }
1129 return collect_String(str);
1130}
1131
1078void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *selectedCommand) { 1132void updateDropdownSelection_LabelWidget(iLabelWidget *dropButton, const char *selectedCommand) {
1079 iWidget *menu = findChild_Widget(as_Widget(dropButton), "menu"); 1133 iWidget *menu = findChild_Widget(as_Widget(dropButton), "menu");
1134 if (flags_Widget(menu) & nativeMenu_WidgetFlag) {
1135 unselectAllNativeMenuItems_Widget(menu);
1136 iMenuItem *item = findNativeMenuItem_Widget(menu, selectedCommand);
1137 if (item) {
1138 setSelected_NativeMenuItem(item, iTrue);
1139 updateText_LabelWidget(dropButton,
1140 removeMenuItemLabelPrefixes_String(collectNewCStr_String(item->label)));
1141 }
1142 return;
1143 }
1080 iForEach(ObjectList, i, children_Widget(menu)) { 1144 iForEach(ObjectList, i, children_Widget(menu)) {
1081 if (isInstance_Object(i.object, &Class_LabelWidget)) { 1145 if (isInstance_Object(i.object, &Class_LabelWidget)) {
1082 iLabelWidget *item = i.object; 1146 iLabelWidget *item = i.object;
diff --git a/src/ui/util.h b/src/ui/util.h
index d929143f..a1914e2a 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -234,10 +234,12 @@ void closeMenu_Widget (iWidget *);
234void releaseNativeMenu_Widget(iWidget *); 234void releaseNativeMenu_Widget(iWidget *);
235 235
236size_t findWidestLabel_MenuItem (const iMenuItem *items, size_t num); 236size_t findWidestLabel_MenuItem (const iMenuItem *items, size_t num);
237void setSelected_NativeMenuItem (iMenuItem *item, iBool isSelected);
237 238
238iChar removeIconPrefix_String (iString *); 239iChar removeIconPrefix_String (iString *);
239 240
240iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); 241iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command);
242iMenuItem * findNativeMenuItem_Widget (iWidget *menu, const char *commandSuffix);
241void setMenuItemDisabled_Widget (iWidget *menu, const char *command, iBool disable); 243void setMenuItemDisabled_Widget (iWidget *menu, const char *command, iBool disable);
242void updateMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel); 244void updateMenuItemLabel_Widget (iWidget *menu, const char *command, const char *newLabel);
243 245