diff --git a/src/allegro/src/macosx/qzwindow.m b/src/allegro/src/macosx/qzwindow.m index 5f8a81904..59b8595a3 100644 --- a/src/allegro/src/macosx/qzwindow.m +++ b/src/allegro/src/macosx/qzwindow.m @@ -244,6 +244,9 @@ static void prepare_window_for_animation(int refresh_view) _unix_lock_mutex(osx_skip_events_processing_mutex); osx_skip_events_processing = FALSE; _unix_unlock_mutex(osx_skip_events_processing_mutex); + + if (_keyboard_installed) + osx_keyboard_focused(TRUE, 0); } @@ -257,6 +260,11 @@ static void prepare_window_for_animation(int refresh_view) _unix_lock_mutex(osx_skip_events_processing_mutex); osx_skip_events_processing = TRUE; _unix_unlock_mutex(osx_skip_events_processing_mutex); + + if (_keyboard_installed) { + osx_keyboard_focused(FALSE, 0); + osx_keyboard_modifiers(0); + } } @end diff --git a/src/app/file_selector.cpp b/src/app/file_selector.cpp index 85d2e7014..b773b9ce4 100644 --- a/src/app/file_selector.cpp +++ b/src/app/file_selector.cpp @@ -44,16 +44,8 @@ std::string show_file_selector(const std::string& title, std::vector tokens; base::split_string(showExtensions, tokens, ","); - std::string known; - for (const auto& tok : tokens) { - if (!known.empty()) - known.push_back(';'); - known += "*." + tok; - } - dlg->addFilter(known, "Known file types (" + known + ")"); for (const auto& tok : tokens) - dlg->addFilter("*." + tok, tok + " files (*." + tok + ")"); - dlg->addFilter("*.*", "All files (*.*)"); + dlg->addFilter(tok, tok + " files (*." + tok + ")"); if (dlg->show(she::instance()->defaultDisplay())) res = dlg->getFileName(); diff --git a/src/she/alleg4/app.mm b/src/she/alleg4/app.mm index c3d30c3c8..9c1b7ab01 100644 --- a/src/she/alleg4/app.mm +++ b/src/she/alleg4/app.mm @@ -1,11 +1,11 @@ // SHE library -// Copyright (C) 2012-2014 David Capello +// Copyright (C) 2012-2015 David Capello // // This file is released under the terms of the MIT license. // Read LICENSE.txt for more information. -#import -#import +#include +#include #include #include @@ -16,6 +16,11 @@ #include "she/event.h" #include "she/system.h" +void* get_osx_window() +{ + return osx_window; +} + @interface SheAppDelegate : AllegroAppDelegate - (BOOL)application: (NSApplication *)theApplication openFile: (NSString *)filename; @end diff --git a/src/she/alleg4/she.cpp b/src/she/alleg4/she.cpp index ee8915576..974bd4937 100644 --- a/src/she/alleg4/she.cpp +++ b/src/she/alleg4/she.cpp @@ -41,7 +41,7 @@ #define WM_MOUSEHWHEEL 0x020E #endif -#elif defined(ALLEGRO_UNIX) +#elif defined ALLEGRO_UNIX #include #ifdef None @@ -97,6 +97,8 @@ static void resize_callback(RESIZE_DISPLAY_EVENT* ev) static she::System* g_instance = nullptr; +void* get_osx_window(); + namespace she { class Alleg4EventQueue : public EventQueue { @@ -446,7 +448,8 @@ class Alleg4Display : public Display { public: Alleg4Display(int width, int height, int scale) : m_surface(NULL) - , m_scale(0) { + , m_scale(0) + , m_nativeCursor(kNoCursor) { unique_display = this; if (install_mouse() < 0) throw DisplayCreationException(allegro_error); @@ -607,6 +610,10 @@ public: return m_queue; } + NativeCursor nativeMouseCursor() override { + return m_nativeCursor; + } + bool setNativeMouseCursor(NativeCursor cursor) override { int newCursor = MOUSE_CURSOR_NONE; @@ -645,6 +652,7 @@ public: return false; } + m_nativeCursor = cursor; return (show_os_cursor(newCursor) == 0); } @@ -684,8 +692,10 @@ public: void* nativeHandle() override { #ifdef _WIN32 return reinterpret_cast(win_get_window()); +#elif defined __APPLE__ + return get_osx_window(); #else - return NULL; + return nullptr; #endif } @@ -693,6 +703,7 @@ private: Surface* m_surface; int m_scale; Alleg4EventQueue* m_queue; + NativeCursor m_nativeCursor; }; class Alleg4System : public CommonSystem { diff --git a/src/she/display.h b/src/she/display.h index b2d27b3a9..25acd16f8 100644 --- a/src/she/display.h +++ b/src/she/display.h @@ -57,6 +57,7 @@ namespace she { virtual EventQueue* getEventQueue() = 0; + virtual NativeCursor nativeMouseCursor() = 0; virtual bool setNativeMouseCursor(NativeCursor cursor) = 0; virtual void setMousePosition(const gfx::Point& position) = 0; virtual void captureMouse() = 0; diff --git a/src/she/osx/native_dialogs.mm b/src/she/osx/native_dialogs.mm index 4fe809793..8d8f5387a 100644 --- a/src/she/osx/native_dialogs.mm +++ b/src/she/osx/native_dialogs.mm @@ -6,18 +6,185 @@ #include #include +#include +#include + +#include "she/display.h" +#include "she/keys.h" +#include "she/native_cursor.h" #include "she/osx/native_dialogs.h" +#include "base/path.h" + +@interface OpenSaveHelper : NSObject +{ + NSSavePanel* panel; + she::Display* display; + int result; +} +- (id)init; +- (void)setPanel:(NSSavePanel*)panel; +- (void)setDisplay:(she::Display*)display; +- (void)runModal; +- (int)result; +@end + +@implementation OpenSaveHelper + +- (id)init +{ + if (self = [super init]) { + result = NSFileHandlingPanelCancelButton; + } + return self; +} + +- (void)setPanel:(NSSavePanel*)newPanel +{ + panel = newPanel; +} + +- (void)setDisplay:(she::Display*)newDisplay +{ + display = newDisplay; +} + +// This is executed in the main thread. +- (void)runModal +{ + she::NativeCursor oldCursor = display->nativeMouseCursor(); + display->setNativeMouseCursor(she::kArrowCursor); + + if ([panel isKindOfClass:[NSOpenPanel class]]) { + // As we're using OS X 10.4 framework, it looks like runModal + // doesn't recognize the allowedFileTypes property. So we force it + // using runModalForTypes: selector. + result = [panel runModalForTypes:[panel allowedFileTypes]]; + } + else { + result = [panel runModal]; + } + + display->setNativeMouseCursor(oldCursor); +} + +- (int)result +{ + return result; +} + +@end + namespace she { +class FileDialogOSX : public FileDialog { +public: + FileDialogOSX() + : m_save(false) + { + } + + void dispose() override { + delete this; + } + + void toOpenFile() override { + m_save = false; + } + + void toSaveFile() override { + m_save = true; + } + + void setTitle(const std::string& title) override { + m_title = title; + } + + void setDefaultExtension(const std::string& extension) override { + m_defExtension = extension; + } + + void addFilter(const std::string& extension, const std::string& description) override { + if (m_defExtension.empty()) + m_defExtension = extension; + + m_filters.push_back(std::make_pair(description, extension)); + } + + std::string getFileName() override { + return m_filename; + } + + void setFileName(const std::string& filename) override { + m_filename = filename; + } + + bool show(Display* display) override { + NSSavePanel* panel = nil; + + if (m_save) { + panel = [NSSavePanel savePanel]; + } + else { + panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:NO]; + } + + [panel setTitle:[NSString stringWithUTF8String:m_title.c_str()]]; + [panel setCanCreateDirectories:YES]; + [panel setCanChooseDirectories:NO]; + + std::string defPath = base::get_file_path(m_filename); + std::string defName = base::get_file_name(m_filename); + if (!defPath.empty()) + [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:defPath.c_str()]]]; + if (!defName.empty()) + [panel setNameFieldStringValue:[NSString stringWithUTF8String:defName.c_str()]]; + + NSMutableArray* types = [[NSMutableArray alloc] init]; + // The first extension in the array is used as the default one. + if (!m_defExtension.empty()) + [types addObject:[NSString stringWithUTF8String:m_defExtension.c_str()]]; + for (const auto& filter : m_filters) + [types addObject:[NSString stringWithUTF8String:filter.second.c_str()]]; + [panel setAllowedFileTypes:types]; + + OpenSaveHelper* helper = [[OpenSaveHelper alloc] init]; + [helper setPanel:panel]; + [helper setDisplay:display]; + [helper performSelectorOnMainThread:@selector(runModal) withObject:nil waitUntilDone:YES]; + + bool retValue; + if ([helper result] == NSFileHandlingPanelOKButton) { + NSURL* url = [panel URL]; + m_filename = [[url path] UTF8String]; + retValue = true; + } + else + retValue = false; + + [helper release]; + [types release]; + return retValue; + } + +private: + + std::vector> m_filters; + std::string m_defExtension; + std::string m_filename; + std::string m_title; + bool m_save; +}; + NativeDialogsOSX::NativeDialogsOSX() { } FileDialog* NativeDialogsOSX::createFileDialog() { - return nullptr; + return new FileDialogOSX(); } } // namespace she diff --git a/src/she/skia/skia_display.cpp b/src/she/skia/skia_display.cpp index 28a4eb6b7..bf3f5b2e0 100644 --- a/src/she/skia/skia_display.cpp +++ b/src/she/skia/skia_display.cpp @@ -17,6 +17,7 @@ SkiaDisplay::SkiaDisplay(EventQueue* queue, int width, int height, int scale) , m_window(m_queue, this) , m_surface(new SkiaSurface) , m_customSurface(false) + , m_nativeCursor(kArrowCursor) { m_surface->create(width, height); m_window.setScale(scale); @@ -116,8 +117,14 @@ EventQueue* SkiaDisplay::getEventQueue() return m_queue; } +NativeCursor SkiaDisplay::nativeMouseCursor() +{ + return m_nativeCursor; +} + bool SkiaDisplay::setNativeMouseCursor(NativeCursor cursor) { + m_nativeCursor = cursor; m_window.setNativeMouseCursor(cursor); return true; } diff --git a/src/she/skia/skia_display.h b/src/she/skia/skia_display.h index cce15b328..b65566337 100644 --- a/src/she/skia/skia_display.h +++ b/src/she/skia/skia_display.h @@ -52,6 +52,7 @@ public: bool isMaximized() const override; void setTitleBar(const std::string& title) override; EventQueue* getEventQueue() override; + NativeCursor nativeMouseCursor() override; bool setNativeMouseCursor(NativeCursor cursor) override; void setMousePosition(const gfx::Point& position) override; void captureMouse() override; diff --git a/src/she/win/native_dialogs.cpp b/src/she/win/native_dialogs.cpp index 68f84d832..a5ded1e9b 100644 --- a/src/she/win/native_dialogs.cpp +++ b/src/she/win/native_dialogs.cpp @@ -57,7 +57,7 @@ public: m_defExtension = base::from_utf8(extension); m_defFilter = 0; } - m_filters.push_back(std::make_pair(description, extension)); + m_filters.push_back(std::make_pair(extension, description)); } std::string getFileName() override { @@ -119,12 +119,37 @@ private: std::wstring getFiltersForGetOpenFileName() const { std::wstring filters; + + // A filter for all known types + filters.append(L"All known file types"); + filters.push_back('\0'); + bool first = true; for (const auto& filter : m_filters) { + if (first) + first = false; + else + filters.push_back(';'); + filters.append(L"*."); filters.append(base::from_utf8(filter.first)); - filters.push_back('\0'); + } + filters.push_back('\0'); + + // A specific filter for each type + for (const auto& filter : m_filters) { filters.append(base::from_utf8(filter.second)); filters.push_back('\0'); + filters.append(L"*."); + filters.append(base::from_utf8(filter.first)); + filters.push_back('\0'); } + + // A filter for all files + filters.append(L"All files"); + filters.push_back('\0'); + filters.append(L"*.*"); + filters.push_back('\0'); + + // End of filter string (two zeros at the end) filters.push_back('\0'); return filters; }