aboutsummaryrefslogtreecommitdiff
path: root/src/platform/gui/osx/UiApplication.mm
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-05-15 14:08:06 +0800
committercrupest <crupest@outlook.com>2022-05-15 14:08:06 +0800
commit8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6 (patch)
tree77e41cc14264060517c0f7ed95837012afb8342e /src/platform/gui/osx/UiApplication.mm
parent9e0c9d3499bc50c3534b4dc500d8b5d0b5f22752 (diff)
downloadcru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.tar.gz
cru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.tar.bz2
cru-8ad2966933957ac5d6ff8dcd5e732736fd5e4dc6.zip
...
Diffstat (limited to 'src/platform/gui/osx/UiApplication.mm')
-rw-r--r--src/platform/gui/osx/UiApplication.mm260
1 files changed, 260 insertions, 0 deletions
diff --git a/src/platform/gui/osx/UiApplication.mm b/src/platform/gui/osx/UiApplication.mm
new file mode 100644
index 00000000..ef62af58
--- /dev/null
+++ b/src/platform/gui/osx/UiApplication.mm
@@ -0,0 +1,260 @@
+#include "cru/platform/gui/osx/UiApplication.h"
+
+#include "ClipboardPrivate.h"
+#include "cru/common/log/Logger.h"
+#include "cru/common/platform/osx/Convert.h"
+#include "cru/platform/graphics/quartz/Factory.h"
+#include "cru/platform/gui/osx/Clipboard.h"
+#include "cru/platform/gui/osx/Cursor.h"
+#include "cru/platform/gui/osx/Menu.h"
+#include "cru/platform/gui/osx/Window.h"
+#include "cru/platform/graphics/Factory.h"
+#include "cru/platform/gui/Base.h"
+#include "cru/platform/gui/UiApplication.h"
+#include "cru/platform/gui/Window.h"
+
+#include <AppKit/NSApplication.h>
+#include <Foundation/NSRunLoop.h>
+#include <UniformTypeIdentifiers/UTType.h>
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+@interface CruAppDelegate : NSObject <NSApplicationDelegate>
+- (id)init:(cru::platform::gui::osx::details::OsxUiApplicationPrivate*)p;
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
+- (void)applicationWillTerminate:(NSNotification*)notification;
+@end
+
+namespace cru::platform::gui::osx {
+
+using cru::platform::osx::Convert;
+
+namespace details {
+class OsxUiApplicationPrivate {
+ friend OsxUiApplication;
+
+ public:
+ explicit OsxUiApplicationPrivate(OsxUiApplication* osx_ui_application)
+ : osx_ui_application_(osx_ui_application) {
+ app_delegate_ = [[CruAppDelegate alloc] init:this];
+ }
+
+ CRU_DELETE_COPY(OsxUiApplicationPrivate)
+ CRU_DELETE_MOVE(OsxUiApplicationPrivate)
+
+ ~OsxUiApplicationPrivate() = default;
+
+ void CallQuitHandlers();
+
+ private:
+ OsxUiApplication* osx_ui_application_;
+ CruAppDelegate* app_delegate_;
+ std::vector<std::function<void()>> quit_handlers_;
+ bool quit_on_all_window_closed_ = true;
+
+ long long current_timer_id_ = 1;
+ std::unordered_map<long long, std::function<void()>> next_tick_;
+ std::unordered_map<long long, NSTimer*> timers_;
+
+ std::vector<OsxWindow*> windows_;
+
+ std::unique_ptr<OsxCursorManager> cursor_manager_;
+
+ std::unique_ptr<OsxClipboard> clipboard_;
+
+ std::unique_ptr<platform::graphics::quartz::QuartzGraphicsFactory> quartz_graphics_factory_;
+};
+
+void OsxUiApplicationPrivate::CallQuitHandlers() {
+ for (const auto& handler : quit_handlers_) {
+ handler();
+ }
+}
+}
+
+OsxUiApplication::OsxUiApplication()
+ : OsxGuiResource(this), p_(new details::OsxUiApplicationPrivate(this)) {
+ [NSApplication sharedApplication];
+
+ [NSApp setDelegate:p_->app_delegate_];
+ p_->quartz_graphics_factory_ = std::make_unique<graphics::quartz::QuartzGraphicsFactory>();
+ p_->cursor_manager_ = std::make_unique<OsxCursorManager>(this);
+ p_->clipboard_ = std::make_unique<OsxClipboard>(
+ this, std::make_unique<details::OsxClipboardPrivate>([NSPasteboard generalPasteboard]));
+}
+
+OsxUiApplication::~OsxUiApplication() {}
+
+int OsxUiApplication::Run() {
+ [NSApp run];
+ return 0;
+}
+
+void OsxUiApplication::RequestQuit(int quit_code) {
+ [NSApp terminate:[NSNumber numberWithInteger:quit_code]];
+}
+
+void OsxUiApplication::AddOnQuitHandler(std::function<void()> handler) {
+ p_->quit_handlers_.push_back(std::move(handler));
+}
+
+bool OsxUiApplication::IsQuitOnAllWindowClosed() { return p_->quit_on_all_window_closed_; }
+
+void OsxUiApplication::SetQuitOnAllWindowClosed(bool quit_on_all_window_closed) {
+ p_->quit_on_all_window_closed_ = quit_on_all_window_closed;
+}
+
+long long OsxUiApplication::SetImmediate(std::function<void()> action) {
+ const long long id = p_->current_timer_id_++;
+ p_->next_tick_.emplace(id, std::move(action));
+
+ [[NSRunLoop mainRunLoop] performBlock:^{
+ const auto i = p_->next_tick_.find(id);
+ if (i != p_->next_tick_.cend()) {
+ i->second();
+ }
+ p_->next_tick_.erase(i);
+ }];
+
+ return id;
+}
+
+long long OsxUiApplication::SetTimeout(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) {
+ long long id = p_->current_timer_id_++;
+ p_->timers_.emplace(id, [NSTimer scheduledTimerWithTimeInterval:milliseconds.count() / 1000.0
+ repeats:false
+ block:^(NSTimer* timer) {
+ action();
+ p_->timers_.erase(id);
+ }]);
+
+ return id;
+}
+
+long long OsxUiApplication::SetInterval(std::chrono::milliseconds milliseconds,
+ std::function<void()> action) {
+ long long id = p_->current_timer_id_++;
+ p_->timers_.emplace(id, [NSTimer scheduledTimerWithTimeInterval:milliseconds.count() / 1000.0
+ repeats:true
+ block:^(NSTimer* timer) {
+ action();
+ }]);
+
+ return id;
+}
+
+void OsxUiApplication::CancelTimer(long long id) {
+ p_->next_tick_.erase(id);
+ auto i = p_->timers_.find(id);
+ if (i != p_->timers_.cend()) {
+ [i->second invalidate];
+ p_->timers_.erase(i);
+ }
+}
+
+std::vector<INativeWindow*> OsxUiApplication::GetAllWindow() {
+ std::vector<INativeWindow*> result;
+ std::transform(p_->windows_.cbegin(), p_->windows_.cend(), std::back_inserter(result),
+ [](OsxWindow* w) { return static_cast<INativeWindow*>(w); });
+ return result;
+}
+
+INativeWindow* OsxUiApplication::CreateWindow() {
+ auto window = new OsxWindow(this);
+ p_->windows_.push_back(window);
+ return window;
+}
+
+ICursorManager* OsxUiApplication::GetCursorManager() { return p_->cursor_manager_.get(); }
+
+IClipboard* OsxUiApplication::GetClipboard() { return p_->clipboard_.get(); }
+
+IMenu* OsxUiApplication::GetApplicationMenu() { return OsxMenu::CreateOrGetApplicationMenu(this); }
+
+graphics::IGraphicsFactory* OsxUiApplication::GetGraphicsFactory() {
+ return p_->quartz_graphics_factory_.get();
+}
+
+std::optional<String> OsxUiApplication::ShowSaveDialog(SaveDialogOptions options) {
+ NSSavePanel* panel = [NSSavePanel savePanel];
+ [panel setTitle:(NSString*)Convert(options.title)];
+ [panel setPrompt:(NSString*)Convert(options.prompt)];
+ [panel setMessage:(NSString*)Convert(options.message)];
+
+ NSMutableArray* allowed_content_types = [NSMutableArray array];
+
+ for (const auto& file_type : options.allowed_file_types) {
+ [allowed_content_types
+ addObject:[UTType typeWithFilenameExtension:(NSString*)Convert(file_type)]];
+ }
+
+ [panel setAllowedContentTypes:allowed_content_types];
+ [panel setAllowsOtherFileTypes:options.allow_all_file_types];
+
+ auto model_result = [panel runModal];
+ if (model_result == NSModalResponseOK) {
+ return Convert((CFStringRef)[[panel URL] path]);
+ } else {
+ return std::nullopt;
+ }
+}
+
+std::optional<std::vector<String>> OsxUiApplication::ShowOpenDialog(OpenDialogOptions options) {
+ NSOpenPanel* panel = [NSOpenPanel openPanel];
+ [panel setTitle:(NSString*)Convert(options.title)];
+ [panel setPrompt:(NSString*)Convert(options.prompt)];
+ [panel setMessage:(NSString*)Convert(options.message)];
+
+ NSMutableArray* allowed_content_types = [NSMutableArray array];
+
+ for (const auto& file_type : options.allowed_file_types) {
+ [allowed_content_types
+ addObject:[UTType typeWithFilenameExtension:(NSString*)Convert(file_type)]];
+ }
+
+ [panel setAllowedContentTypes:allowed_content_types];
+ [panel setAllowsOtherFileTypes:options.allow_all_file_types];
+
+ [panel setCanChooseFiles:options.can_choose_files];
+ [panel setCanChooseDirectories:options.can_choose_directories];
+ [panel setAllowsMultipleSelection:options.allow_mulitple_selection];
+
+ auto model_result = [panel runModal];
+ if (model_result == NSModalResponseOK) {
+ std::vector<String> result;
+ for (NSURL* url in [panel URLs]) {
+ result.push_back(Convert((CFStringRef)[url path]));
+ }
+ return result;
+ } else {
+ return std::nullopt;
+ }
+}
+
+void OsxUiApplication::UnregisterWindow(OsxWindow* window) {
+ p_->windows_.erase(
+ std::remove(p_->windows_.begin(), p_->windows_.end(), static_cast<INativeWindow*>(window)),
+ p_->windows_.cend());
+}
+}
+
+@implementation CruAppDelegate {
+ cru::platform::gui::osx::details::OsxUiApplicationPrivate* _p;
+}
+
+- (id)init:(cru::platform::gui::osx::details::OsxUiApplicationPrivate*)p {
+ _p = p;
+ return self;
+}
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender {
+ return NSApplicationTerminateReply::NSTerminateNow;
+}
+- (void)applicationWillTerminate:(NSNotification*)notification {
+ _p->CallQuitHandlers();
+}
+@end