From 97ed67c5b0247dbaba39bda4629f7bcb6b8d3370 Mon Sep 17 00:00:00 2001 From: cxl Date: Sun, 7 Oct 2018 17:49:53 +0000 Subject: [PATCH] CtrlLib: OSX Main application menu support git-svn-id: svn://ultimatepp.org/upp/trunk@12341 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/CtrlCore/CocoProc.mm | 1 + uppsrc/CtrlCore/CocoTop.cpp | 6 +- uppsrc/CtrlCore/CocoTop.h | 9 ++ uppsrc/CtrlLib/CtrlLib.upp | 2 + uppsrc/CtrlLib/MacMenu.mm | 246 ++++++++++++++++++++++++++++++++++++ uppsrc/CtrlLib/MenuBar.cpp | 2 +- uppsrc/CtrlLib/NSMenuKeys.i | 73 +++++++++++ 7 files changed, 333 insertions(+), 6 deletions(-) create mode 100644 uppsrc/CtrlLib/MacMenu.mm create mode 100644 uppsrc/CtrlLib/NSMenuKeys.i diff --git a/uppsrc/CtrlCore/CocoProc.mm b/uppsrc/CtrlCore/CocoProc.mm index 9b6292e8e..922e32b84 100644 --- a/uppsrc/CtrlCore/CocoProc.mm +++ b/uppsrc/CtrlCore/CocoProc.mm @@ -177,6 +177,7 @@ struct MMImp { tw->placefocus = false; } Upp::Ctrl::SyncAppIcon(); + Upp::TopWindow::SyncMainMenu(false); } static void ResignKey(Upp::Ctrl *ctrl) diff --git a/uppsrc/CtrlCore/CocoTop.cpp b/uppsrc/CtrlCore/CocoTop.cpp index a69a5f5d4..c8641b764 100644 --- a/uppsrc/CtrlCore/CocoTop.cpp +++ b/uppsrc/CtrlCore/CocoTop.cpp @@ -25,11 +25,7 @@ bool TopWindow::IsTopMost() const void TopWindow::GuiPlatformConstruct() { } - -void TopWindow::GuiPlatformDestruct() -{ -} - + END_UPP_NAMESPACE #endif diff --git a/uppsrc/CtrlCore/CocoTop.h b/uppsrc/CtrlCore/CocoTop.h index bf87ffc01..0f0f38e4f 100644 --- a/uppsrc/CtrlCore/CocoTop.h +++ b/uppsrc/CtrlCore/CocoTop.h @@ -7,4 +7,13 @@ protected: dword GetMMStyle() const; virtual void MMClose() { WhenClose(); } + + static TopWindow *GetMenuTopWindow(); + static void SyncMainMenu(bool force); + + void *menubar = NULL; + +public: + void SetMainMenu(Event menu); + //$ }; \ No newline at end of file diff --git a/uppsrc/CtrlLib/CtrlLib.upp b/uppsrc/CtrlLib/CtrlLib.upp index 29e69a403..f8f482f62 100644 --- a/uppsrc/CtrlLib/CtrlLib.upp +++ b/uppsrc/CtrlLib/CtrlLib.upp @@ -65,6 +65,8 @@ file MenuImp.h, MenuItem.cpp, MenuBar.cpp, + NSMenuKeys.i, + MacMenu.mm, ToolButton.cpp, ToolBar.cpp, ToolTip.cpp, diff --git a/uppsrc/CtrlLib/MacMenu.mm b/uppsrc/CtrlLib/MacMenu.mm new file mode 100644 index 000000000..c76d13041 --- /dev/null +++ b/uppsrc/CtrlLib/MacMenu.mm @@ -0,0 +1,246 @@ +#include +#include + +namespace Upp { + +struct CocoMenuBar; + +}; + +@interface CocoMenu : NSMenu +{ +@public + Upp::Ptr ptr; + Upp::Event proc; +} +-(void)cocoMenuAction:(id)sender; +@end + +namespace Upp { + +struct CocoMenuBar : public Bar { + CocoMenu *cocomenu; + int lock = 0; + + struct Item : Bar::Item { + NSMenuItem *nsitem; + Event<> cb; + One submenu; + + virtual Item& Text(const char *text); + virtual Item& Key(dword key); + virtual Item& Image(const class Image& img); + virtual Item& Check(bool check); + virtual Item& Radio(bool check); + virtual Item& Enable(bool _enable = true); + virtual Item& Bold(bool bold = true); + + Item& Label(const char *text); + Item& RightLabel(const char *text); + + Item& Key(KeyInfo& (*key)()); + + Item() { nsitem = [NSMenuItem new]; } + ~Item() { [nsitem release]; } + }; + + Array item; + + Item& AddItem() { + Item& m = item.Add(); + [cocomenu addItem:m.nsitem]; + return m; + } + + virtual bool IsMenuBar() const { return true; } + + virtual Item& AddItem(Event<> cb) { + Item& m = AddItem(); + m.cb = cb; + m.nsitem.target = cocomenu; + m.nsitem.action = @selector(cocoMenuAction:); + return m; + } + + virtual Item& AddSubMenu(Event proc) { + Item& m = AddItem(); + m.submenu.Create(); + m.submenu->cocomenu->proc = proc; + m.nsitem.action = @selector(cocoMenuAction:); + m.nsitem.submenu = m.submenu->cocomenu; + return m; + } + + virtual void AddCtrl(Ctrl *ctrl, int gapsize) { NEVER(); } + virtual void AddCtrl(Ctrl *ctrl, Size sz) { NEVER(); } + + virtual bool IsEmpty() const; + virtual void Separator(); + + void MenuAction(id item); + + void Set(Event bar); + + CocoMenuBar() { + cocomenu = [CocoMenu new]; + cocomenu.autoenablesItems = NO; + cocomenu->ptr = this; + cocomenu.delegate = cocomenu; + } + ~CocoMenuBar() { + [cocomenu release]; + } +}; + +void CocoMenuBar::Set(Event bar) +{ + if(lock) return; + lock++; + bar(*this); + lock--; +} + +void CocoMenuBar::MenuAction(id sender) +{ + for(const Item& m : item) + if(m.nsitem == sender) { + m.cb(); + break; + } +} + +void CocoMenuBar::Separator() +{ + [cocomenu addItem:[NSMenuItem separatorItem]]; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Text(const char *text) +{ + NSString *s = [NSString stringWithUTF8String:text]; + nsitem.title = s; + if(submenu) + submenu->cocomenu.title = s; + return *this; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Key(dword key) +{ + static Tuple2 code[] = { + #include "NSMenuKeys.i" + }; + auto *v = FindTuple(code, __countof(code), key & ~(K_CTRL|K_SHIFT|K_ALT|K_OPTION)); + if(v) { + unichar chr = v->b; + nsitem.keyEquivalent = [NSString stringWithCharacters:&chr length:1]; + nsitem.keyEquivalentModifierMask = (key & K_CTRL ? NSEventModifierFlagCommand : 0) | + (key & K_SHIFT ? NSEventModifierFlagShift : 0) | + (key & K_ALT ? NSEventModifierFlagControl : 0) | + (key & K_OPTION ? NSEventModifierFlagOption : 0); + } + return *this; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Image(const class Image& img) +{ + nsitem.image = GetNSImage(img); + return *this; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Check(bool check) +{ + nsitem.state = check ? NSControlStateValueOn : NSControlStateValueOff; + return *this; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Radio(bool check) +{ + return Check(check); +} + +CocoMenuBar::Item& CocoMenuBar::Item::Enable(bool enable) +{ + nsitem.enabled = enable; + return *this; +} + +CocoMenuBar::Item& CocoMenuBar::Item::Bold(bool bold) +{ + return *this; +} + +bool CocoMenuBar::IsEmpty() const +{ + return item.GetCount() == 0; +} + +} + +@implementation CocoMenu + +-(void)cocoMenuAction:(id)sender { + if(ptr) + ptr->MenuAction(sender); +} + +- (void)menuWillOpen:(NSMenu *)menu { + CocoMenu *m = (CocoMenu *)menu; + if(m && m->ptr && proc) { + [m removeAllItems]; + proc(*m->ptr); + } +} + +-(void)submenuAction:(id)sender { + if(ptr) + proc(*ptr); + [super submenuAction:sender]; +} + +@end + +namespace Upp { + +void TopWindow::GuiPlatformDestruct() +{ + if(menubar) + delete (CocoMenuBar *)menubar; +} + +void TopWindow::SetMainMenu(Event menu) +{ + if(!menubar) + menubar = new CocoMenuBar; + CocoMenuBar& bar = *(CocoMenuBar *)menubar; + bar.Set(menu); + SyncMainMenu(true); +} + +TopWindow *TopWindow::GetMenuTopWindow() +{ + Ctrl *q = GetFocusCtrl(); + if(!q) + return NULL; + q = q->GetTopCtrl(); + while(q) { + TopWindow *w = dynamic_cast(q); + if(w && w->menubar) + return w; + q = q->GetOwner(); + } + return NULL; +} + +void TopWindow::SyncMainMenu(bool force) +{ + TopWindow *w = GetMenuTopWindow(); + static TopWindow *current; + if(w != current || force) { + current = w; + if(current) { + CocoMenuBar& bar = *(CocoMenuBar *)current->menubar; + [NSApp setMainMenu:bar.cocomenu]; + } + } +} + +}; \ No newline at end of file diff --git a/uppsrc/CtrlLib/MenuBar.cpp b/uppsrc/CtrlLib/MenuBar.cpp index 078150392..f324c7111 100644 --- a/uppsrc/CtrlLib/MenuBar.cpp +++ b/uppsrc/CtrlLib/MenuBar.cpp @@ -482,7 +482,7 @@ void MenuBar::Set(const Event menu) lock++; menu(*this); SyncBar(); - DistributeAccessKeys(); + DistributeAccessKeys(); lock--; } diff --git a/uppsrc/CtrlLib/NSMenuKeys.i b/uppsrc/CtrlLib/NSMenuKeys.i new file mode 100644 index 000000000..4db37765c --- /dev/null +++ b/uppsrc/CtrlLib/NSMenuKeys.i @@ -0,0 +1,73 @@ +{ K_BACK, NSBackspaceCharacter }, +{ K_TAB, NSTabCharacter }, +{ K_SPACE, ' ' }, +{ K_RETURN, NSCarriageReturnCharacter }, +{ K_ESCAPE, 27 }, +{ K_PAGEUP, NSPageUpFunctionKey }, +{ K_PAGEDOWN, NSPageDownFunctionKey }, +{ K_END, NSEndFunctionKey }, +{ K_HOME, NSHomeFunctionKey }, +{ K_LEFT, NSLeftArrowFunctionKey }, +{ K_UP, NSUpArrowFunctionKey }, +{ K_RIGHT, NSRightArrowFunctionKey }, +{ K_DOWN, NSDownArrowFunctionKey }, +{ K_INSERT, NSInsertFunctionKey }, // Code is this for external keyboard.. +{ K_DELETE, NSDeleteFunctionKey }, +{ K_F1, NSF1FunctionKey }, +{ K_F2, NSF2FunctionKey }, +{ K_F3, NSF3FunctionKey }, +{ K_F4, NSF4FunctionKey }, +{ K_F5, NSF5FunctionKey }, +{ K_F6, NSF6FunctionKey }, +{ K_F7, NSF7FunctionKey }, +{ K_F8, NSF8FunctionKey }, +{ K_F9, NSF9FunctionKey }, +{ K_F10, NSF10FunctionKey }, +{ K_F11, NSF11FunctionKey }, +{ K_F12, NSF12FunctionKey }, +{ K_A, 'a' }, +{ K_B, 'b' }, +{ K_C, 'c' }, +{ K_D, 'd' }, +{ K_E, 'e' }, +{ K_F, 'f' }, +{ K_G, 'g' }, +{ K_H, 'h' }, +{ K_I, 'i' }, +{ K_J, 'j' }, +{ K_K, 'k' }, +{ K_L, 'l' }, +{ K_M, 'm' }, +{ K_N, 'n' }, +{ K_O, 'o' }, +{ K_P, 'p' }, +{ K_Q, 'q' }, +{ K_R, 'r' }, +{ K_S, 's' }, +{ K_T, 't' }, +{ K_U, 'u' }, +{ K_V, 'v' }, +{ K_W, 'w' }, +{ K_X, 'x' }, +{ K_Y, 'y' }, +{ K_Z, 'z' }, +{ K_0, '0' }, +{ K_1, '1' }, +{ K_2, '2' }, +{ K_3, '3' }, +{ K_4, '4' }, +{ K_5, '5' }, +{ K_6, '6' }, +{ K_7, '7' }, +{ K_8, '8' }, +{ K_9, '9' }, + +{ K_MINUS , '-' }, +{ K_COMMA , ',' }, +{ K_PERIOD , '.' }, +{ K_SEMICOLON , ';' }, +{ K_SLASH , '/' }, +{ K_GRAVE , '\'' }, +{ K_LBRACKET , '[' }, +{ K_BACKSLASH , '\\' }, +{ K_RBRACKET , ']' },