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 /src/ui/util.c | |
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.
Diffstat (limited to 'src/ui/util.c')
-rw-r--r-- | src/ui/util.c | 122 |
1 files changed, 121 insertions, 1 deletions
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) { |