aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/osx/gui/Menu.hpp65
-rw-r--r--include/cru/osx/gui/UiApplication.hpp2
-rw-r--r--include/cru/platform/gui/Menu.hpp28
-rw-r--r--include/cru/platform/gui/UiApplication.hpp4
-rw-r--r--src/osx/gui/CMakeLists.txt1
-rw-r--r--src/osx/gui/Menu.mm148
-rw-r--r--src/osx/gui/MenuPrivate.h63
-rw-r--r--src/osx/gui/UiApplication.mm3
-rw-r--r--src/platform/gui/CMakeLists.txt1
-rw-r--r--src/platform/gui/Menu.cpp11
-rw-r--r--src/platform/gui/UiApplication.cpp4
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