Permalink
Browse files

Implement native open/save dialog on OS X (issue #321)

There were some problems detecting the release of Cmd+O keys after
opening a NSSavePanel. We fixed those problems calling
osx_keyboard_focused/modifiers(0) when we receive a windowDidResignKey
notification.
  • Loading branch information...
dacap committed May 28, 2015
1 parent 7826e38 commit 744fc67b281df6b18394794070fc6e16f4a48a54
@@ -244,6 +244,9 @@ - (void)windowDidBecomeKey:(NSNotification *)notification
_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 @@ - (void)windowDidResignKey:(NSNotification *)notification
_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
View
@@ -44,16 +44,8 @@ std::string show_file_selector(const std::string& title,
std::vector<std::string> 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();
View
@@ -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 <AppKit/AppKit.h>
-#import <IOKit/hid/IOHIDLib.h>
+#include <AppKit/AppKit.h>
+#include <IOKit/hid/IOHIDLib.h>
#include <allegro.h>
#include <allegro/platform/alosx.h>
@@ -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
View
@@ -41,7 +41,7 @@
#define WM_MOUSEHWHEEL 0x020E
#endif
-#elif defined(ALLEGRO_UNIX)
+#elif defined ALLEGRO_UNIX
#include <xalleg.h>
#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 @@ class Alleg4Display : public Display {
return m_queue;
}
+ NativeCursor nativeMouseCursor() override {
+ return m_nativeCursor;
+ }
+
bool setNativeMouseCursor(NativeCursor cursor) override {
int newCursor = MOUSE_CURSOR_NONE;
@@ -645,6 +652,7 @@ class Alleg4Display : public Display {
return false;
}
+ m_nativeCursor = cursor;
return (show_os_cursor(newCursor) == 0);
}
@@ -684,15 +692,18 @@ class Alleg4Display : public Display {
void* nativeHandle() override {
#ifdef _WIN32
return reinterpret_cast<void*>(win_get_window());
+#elif defined __APPLE__
+ return get_osx_window();
#else
- return NULL;
+ return nullptr;
#endif
}
private:
Surface* m_surface;
int m_scale;
Alleg4EventQueue* m_queue;
+ NativeCursor m_nativeCursor;
};
class Alleg4System : public CommonSystem {
View
@@ -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;
@@ -6,18 +6,185 @@
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
+#include <AppKit/AppKit.h>
+#include <vector>
+
+#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<std::pair<std::string, std::string>> 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
@@ -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;
}
@@ -52,6 +52,7 @@ class SkiaDisplay : public Display {
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;
Oops, something went wrong.

0 comments on commit 744fc67

Please sign in to comment.