diff options
-rw-r--r-- | include/cru/osx/gui/Menu.hpp | 65 | ||||
-rw-r--r-- | include/cru/osx/gui/UiApplication.hpp | 2 | ||||
-rw-r--r-- | include/cru/platform/gui/Menu.hpp | 28 | ||||
-rw-r--r-- | include/cru/platform/gui/UiApplication.hpp | 4 | ||||
-rw-r--r-- | src/osx/gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/osx/gui/Menu.mm | 148 | ||||
-rw-r--r-- | src/osx/gui/MenuPrivate.h | 63 | ||||
-rw-r--r-- | src/osx/gui/UiApplication.mm | 3 | ||||
-rw-r--r-- | src/platform/gui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/platform/gui/Menu.cpp | 11 | ||||
-rw-r--r-- | src/platform/gui/UiApplication.cpp | 4 |
11 files changed, 329 insertions, 1 deletions
diff --git a/include/cru/osx/gui/Menu.hpp b/include/cru/osx/gui/Menu.hpp new file mode 100644 index 00000000..058f9cb3 --- /dev/null +++ b/include/cru/osx/gui/Menu.hpp @@ -0,0 +1,65 @@ +#pragma once +#include "Resource.hpp" + +#include "cru/platform/gui/Menu.hpp" + +namespace cru::platform::gui::osx { +namespace details { +struct OsxMenuItemPrivate; +struct OsxMenuPrivate; +} // namespace details + +class OsxMenu; + +class OsxMenuItem : public OsxGuiResource, public virtual IMenuItem { + friend OsxMenu; + friend details::OsxMenuPrivate; + + private: + explicit OsxMenuItem(IUiApplication* ui_application); + + public: + CRU_DELETE_COPY(OsxMenuItem) + CRU_DELETE_MOVE(OsxMenuItem) + + ~OsxMenuItem() override; + + public: + String GetTitle() override; + void SetTitle(String title) override; + bool IsEnabled() override; + void SetEnabled(bool enabled) override; + IMenu* GetParentMenu() override; + IMenu* GetSubmenu() override; + void SetOnClickHandler(std::function<void()> handler) override; + + private: + details::OsxMenuItemPrivate* p_; +}; + +class OsxMenu : public OsxGuiResource, public virtual IMenu { + friend OsxMenuItem; + friend details::OsxMenuPrivate; + friend details::OsxMenuItemPrivate; + + private: + explicit OsxMenu(IUiApplication* ui_application); + + public: + static OsxMenu* CreateOrGetApplicationMenu(IUiApplication* ui_application); + + CRU_DELETE_COPY(OsxMenu) + CRU_DELETE_MOVE(OsxMenu) + + ~OsxMenu() override; + + public: + IMenuItem* GetItemAt(int index) override; + int GetItemCount() override; + IMenuItem* CreateItemAt(int index) override; + void RemoveItemAt(int index) override; + + private: + details::OsxMenuPrivate* p_; +}; +} // namespace cru::platform::gui::osx diff --git a/include/cru/osx/gui/UiApplication.hpp b/include/cru/osx/gui/UiApplication.hpp index 09547b38..a7c76fe4 100644 --- a/include/cru/osx/gui/UiApplication.hpp +++ b/include/cru/osx/gui/UiApplication.hpp @@ -49,6 +49,8 @@ class OsxUiApplication : public OsxGuiResource, public virtual IUiApplication { IClipboard* GetClipboard() override; + IMenu* GetApplicationMenu() override; + private: void UnregisterWindow(OsxWindow* window); diff --git a/include/cru/platform/gui/Menu.hpp b/include/cru/platform/gui/Menu.hpp new file mode 100644 index 00000000..31fbc068 --- /dev/null +++ b/include/cru/platform/gui/Menu.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "Base.hpp" + +#include <functional> + +namespace cru::platform::gui { +struct IMenu; + +struct CRU_PLATFORM_GUI_API IMenuItem : virtual IPlatformResource { + virtual String GetTitle() = 0; + virtual void SetTitle(String title) = 0; + virtual bool IsEnabled() = 0; + virtual void SetEnabled(bool enabled) = 0; + virtual IMenu* GetParentMenu() = 0; + virtual IMenu* GetSubmenu() = 0; + virtual void SetOnClickHandler(std::function<void()> handler) = 0; +}; + +struct CRU_PLATFORM_GUI_API IMenu : virtual IPlatformResource { + virtual IMenuItem* GetItemAt(int index) = 0; + virtual int GetItemCount() = 0; + virtual IMenuItem* CreateItemAt(int index) = 0; + virtual void RemoveItemAt(int index) = 0; + + virtual std::vector<IMenuItem*> GetItems(); + bool Empty() { return GetItemCount() == 0; } +}; +} // namespace cru::platform::gui diff --git a/include/cru/platform/gui/UiApplication.hpp b/include/cru/platform/gui/UiApplication.hpp index f450ba7d..5c22bd94 100644 --- a/include/cru/platform/gui/UiApplication.hpp +++ b/include/cru/platform/gui/UiApplication.hpp @@ -2,6 +2,7 @@ #include "Base.hpp" #include "cru/common/Bitmask.hpp" +#include "cru/platform/gui/Menu.hpp" #include <chrono> #include <functional> @@ -56,5 +57,8 @@ struct CRU_PLATFORM_GUI_API IUiApplication : public virtual IPlatformResource { virtual ICursorManager* GetCursorManager() = 0; virtual IClipboard* GetClipboard() = 0; + + // If return nullptr, it means the menu is not supported. + virtual IMenu* GetApplicationMenu(); }; } // namespace cru::platform::gui diff --git a/src/osx/gui/CMakeLists.txt b/src/osx/gui/CMakeLists.txt index 27efbe0e..5446c9df 100644 --- a/src/osx/gui/CMakeLists.txt +++ b/src/osx/gui/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(cru_osx_gui SHARED Cursor.mm InputMethod.mm Keyboard.mm + Menu.mm Resource.cpp UiApplication.mm Window.mm diff --git a/src/osx/gui/Menu.mm b/src/osx/gui/Menu.mm new file mode 100644 index 00000000..a980b7f6 --- /dev/null +++ b/src/osx/gui/Menu.mm @@ -0,0 +1,148 @@ +#import "MenuPrivate.h" + +#include "cru/common/platform/osx/Convert.hpp" + +#import <AppKit/NSApplication.h> + +namespace cru::platform::gui::osx { +using platform::osx::Convert; + +namespace { +std::unique_ptr<OsxMenu> application_menu = nullptr; +} + +namespace details { +OsxMenuItemPrivate::OsxMenuItemPrivate(OsxMenuItem* d) { + d_ = d; + sub_menu_ = new OsxMenu(d->GetUiApplication()); + handler_ = [[CruOsxMenuItemClickHandler alloc] init:this]; +} + +OsxMenuItemPrivate::~OsxMenuItemPrivate() { delete sub_menu_; } + +void OsxMenuItemPrivate::AttachToNative(NSMenuItem* native_menu_item, bool check_submenu) { + Expects(sub_menu_); + + menu_item_ = native_menu_item; + [native_menu_item setTarget:handler_]; + [native_menu_item setAction:@selector(handleClick)]; + if (check_submenu && [native_menu_item hasSubmenu]) { + sub_menu_->p_->AttachToNative([native_menu_item submenu]); + } +} + +OsxMenuPrivate::OsxMenuPrivate(OsxMenu* d) { d_ = d; } + +OsxMenuPrivate::~OsxMenuPrivate() { + for (auto item : items_) { + delete item; + } +} + +void OsxMenuPrivate::AttachToNative(NSMenu* native_menu) { + menu_ = native_menu; + + auto item_count = [native_menu numberOfItems]; + for (int i = 0; i < item_count; i++) { + auto native_item = [native_menu itemAtIndex:i]; + auto item = new OsxMenuItem(d_->GetUiApplication()); + item->p_->SetParentMenu(d_); + item->p_->AttachToNative(native_item, true); + items_.push_back(item); + } +} +} + +OsxMenuItem::OsxMenuItem(IUiApplication* ui_application) : OsxGuiResource(ui_application) { + p_ = new details::OsxMenuItemPrivate(this); +} + +OsxMenuItem::~OsxMenuItem() { delete p_; } + +String OsxMenuItem::GetTitle() { return Convert((CFStringRef)[p_->menu_item_ title]); } + +void OsxMenuItem::SetTitle(String title) { [p_->menu_item_ setTitle:(NSString*)Convert(title)]; } + +bool OsxMenuItem::IsEnabled() { return [p_->menu_item_ isEnabled]; } + +void OsxMenuItem::SetEnabled(bool enabled) { [p_->menu_item_ setEnabled:enabled]; } + +IMenu* OsxMenuItem::GetParentMenu() { return p_->parent_menu_; } + +IMenu* OsxMenuItem::GetSubmenu() { return p_->sub_menu_; } + +void OsxMenuItem::SetOnClickHandler(std::function<void()> handler) { + p_->on_click_handler_ = std::move(handler); +} + +OsxMenu* OsxMenu::CreateOrGetApplicationMenu(IUiApplication* ui_application) { + if (application_menu) { + return application_menu.get(); + } + + application_menu.reset(new OsxMenu(ui_application)); + application_menu->p_->AttachToNative([NSApp mainMenu]); + + return application_menu.get(); +} + +OsxMenu::OsxMenu(IUiApplication* ui_application) : OsxGuiResource(ui_application) { + p_ = new details::OsxMenuPrivate(this); +} + +OsxMenu::~OsxMenu() { delete p_; } + +IMenuItem* OsxMenu::GetItemAt(int index) { + if (index < 0 || index >= p_->items_.size()) { + return nullptr; + } + + return p_->items_[index]; +} + +int OsxMenu::GetItemCount() { return p_->items_.size(); } + +IMenuItem* OsxMenu::CreateItemAt(int index) { + if (index < 0) index = 0; + if (index > p_->items_.size()) index = p_->items_.size(); + + auto native_item = [[NSMenuItem alloc] init]; + [p_->menu_ insertItem:native_item atIndex:index]; + + auto item = new OsxMenuItem(GetUiApplication()); + item->p_->SetParentMenu(this); + item->p_->AttachToNative(native_item, false); + p_->items_.insert(p_->items_.begin() + index, item); + + return item; +} + +void OsxMenu::RemoveItemAt(int index) { + if (index < 0 || index >= p_->items_.size()) { + return; + } + + auto item = p_->items_[index]; + [p_->menu_ removeItem:item->p_->GetNative()]; + p_->items_.erase(p_->items_.begin() + index); + + delete item; +} +} + +@implementation CruOsxMenuItemClickHandler { + cru::platform::gui::osx::details::OsxMenuItemPrivate* p_; +} + +- (id)init:(cru::platform::gui::osx::details::OsxMenuItemPrivate*)p { + p_ = p; + return self; +} + +- (void)handleClick:(id)sender { + if (p_->GetOnClickHandler()) { + p_->GetOnClickHandler()(); + } +} + +@end diff --git a/src/osx/gui/MenuPrivate.h b/src/osx/gui/MenuPrivate.h new file mode 100644 index 00000000..433f246d --- /dev/null +++ b/src/osx/gui/MenuPrivate.h @@ -0,0 +1,63 @@ +#pragma once +#include "cru/osx/gui/Menu.hpp" + +#import <AppKit/NSMenu.h> +#import <AppKit/NSMenuItem.h> + +@interface CruOsxMenuItemClickHandler : NSObject +- init:(cru::platform::gui::osx::details::OsxMenuItemPrivate*)p; +- (void)handleClick:(id)sender; +@end + +namespace cru::platform::gui::osx { +namespace details { + +class OsxMenuItemPrivate { + friend OsxMenuItem; + + public: + explicit OsxMenuItemPrivate(OsxMenuItem* d); + + CRU_DELETE_COPY(OsxMenuItemPrivate) + CRU_DELETE_MOVE(OsxMenuItemPrivate) + + ~OsxMenuItemPrivate(); + + public: + NSMenuItem* GetNative() { return menu_item_; } + void SetParentMenu(OsxMenu* menu) { parent_menu_ = menu; } + void AttachToNative(NSMenuItem* native_menu_item, bool check_submenu); + + const std::function<void()> GetOnClickHandler() const { return on_click_handler_; } + + private: + OsxMenuItem* d_; + OsxMenu* parent_menu_ = nullptr; + NSMenuItem* menu_item_ = nullptr; + OsxMenu* sub_menu_ = nullptr; + std::function<void()> on_click_handler_; + CruOsxMenuItemClickHandler* handler_; +}; + +class OsxMenuPrivate { + friend OsxMenu; + + public: + explicit OsxMenuPrivate(OsxMenu* d); + + CRU_DELETE_COPY(OsxMenuPrivate) + CRU_DELETE_MOVE(OsxMenuPrivate) + + ~OsxMenuPrivate(); + + public: + void AttachToNative(NSMenu* native_menu); + + private: + OsxMenu* d_; + NSMenu* menu_ = nullptr; + std::vector<OsxMenuItem*> items_; +}; +} // namespace details + +} // namespace cru::platform::gui::osx diff --git a/src/osx/gui/UiApplication.mm b/src/osx/gui/UiApplication.mm index 7ec2a049..b923742e 100644 --- a/src/osx/gui/UiApplication.mm +++ b/src/osx/gui/UiApplication.mm @@ -5,6 +5,7 @@ #include "cru/osx/graphics/quartz/Factory.hpp" #include "cru/osx/gui/Clipboard.hpp" #include "cru/osx/gui/Cursor.hpp" +#include "cru/osx/gui/Menu.hpp" #include "cru/osx/gui/Window.hpp" #include "cru/platform/graphics/Factory.hpp" #include "cru/platform/gui/Base.hpp" @@ -172,6 +173,8 @@ ICursorManager* OsxUiApplication::GetCursorManager() { return p_->cursor_manager IClipboard* OsxUiApplication::GetClipboard() { return p_->clipboard_.get(); } +IMenu* OsxUiApplication::GetApplicationMenu() { return OsxMenu::CreateOrGetApplicationMenu(this); } + graphics::IGraphicsFactory* OsxUiApplication::GetGraphicsFactory() { return p_->quartz_graphics_factory_.get(); } diff --git a/src/platform/gui/CMakeLists.txt b/src/platform/gui/CMakeLists.txt index 81be85d9..bd899ecd 100644 --- a/src/platform/gui/CMakeLists.txt +++ b/src/platform/gui/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(cru_platform_gui SHARED Keyboard.cpp + Menu.cpp UiApplication.cpp ) target_link_libraries(cru_platform_gui PUBLIC cru_platform_graphics) diff --git a/src/platform/gui/Menu.cpp b/src/platform/gui/Menu.cpp new file mode 100644 index 00000000..7b02a8a4 --- /dev/null +++ b/src/platform/gui/Menu.cpp @@ -0,0 +1,11 @@ +#include "cru/platform/gui/Menu.hpp" + +namespace cru::platform::gui { +std::vector<IMenuItem*> IMenu::GetItems() { + std::vector<IMenuItem*> items; + for (int i = 0; i < GetItemCount(); ++i) { + items.push_back(GetItemAt(i)); + } + return items; +} +} // namespace cru::platform::gui diff --git a/src/platform/gui/UiApplication.cpp b/src/platform/gui/UiApplication.cpp index f095361e..1f77653f 100644 --- a/src/platform/gui/UiApplication.cpp +++ b/src/platform/gui/UiApplication.cpp @@ -7,9 +7,11 @@ IUiApplication::IUiApplication() { if (instance) { throw std::runtime_error("An ui application has already been created."); } - + instance = this; } IUiApplication::~IUiApplication() { instance = nullptr; } + +IMenu* IUiApplication::GetApplicationMenu() { return nullptr; } } // namespace cru::platform::gui |