summaryrefslogtreecommitdiff
path: root/src/ui/util.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-09-21 09:19:21 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-09-21 09:19:21 +0300
commite48a9a27bd11dbef9531bd12d3c0c60cc771b2c3 (patch)
tree0044e6de2a788c061f0fe0eb239ab96d22acadd4 /src/ui/util.c
parentba6d544b1237aea6901cf5b4f6562db9eb0f46c4 (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.c122
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
759static 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
797static 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
754iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { 806iWidget *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
875void 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
815iLocalDef iBool isUsingMenuPopupWindows_(void) { 897iLocalDef iBool isUsingMenuPopupWindows_(void) {
816 return deviceType_App() == desktop_AppDeviceType; 898 return deviceType_App() == desktop_AppDeviceType;
817} 899}
818 900
901void 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
819void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { 913void 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
909void closeMenu_Widget(iWidget *d) { 1011void 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
1888iChar 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
1783iWidget *makeDialog_Widget(const char *id, 1903iWidget *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) {