mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-09 18:44:46 +00:00
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.
This commit is contained in:
parent
7826e38382
commit
744fc67b28
@ -244,6 +244,9 @@ static void prepare_window_for_animation(int refresh_view)
|
|||||||
_unix_lock_mutex(osx_skip_events_processing_mutex);
|
_unix_lock_mutex(osx_skip_events_processing_mutex);
|
||||||
osx_skip_events_processing = FALSE;
|
osx_skip_events_processing = FALSE;
|
||||||
_unix_unlock_mutex(osx_skip_events_processing_mutex);
|
_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);
|
_unix_lock_mutex(osx_skip_events_processing_mutex);
|
||||||
osx_skip_events_processing = TRUE;
|
osx_skip_events_processing = TRUE;
|
||||||
_unix_unlock_mutex(osx_skip_events_processing_mutex);
|
_unix_unlock_mutex(osx_skip_events_processing_mutex);
|
||||||
|
|
||||||
|
if (_keyboard_installed) {
|
||||||
|
osx_keyboard_focused(FALSE, 0);
|
||||||
|
osx_keyboard_modifiers(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -44,16 +44,8 @@ std::string show_file_selector(const std::string& title,
|
|||||||
|
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
base::split_string(showExtensions, 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)
|
for (const auto& tok : tokens)
|
||||||
dlg->addFilter("*." + tok, tok + " files (*." + tok + ")");
|
dlg->addFilter(tok, tok + " files (*." + tok + ")");
|
||||||
dlg->addFilter("*.*", "All files (*.*)");
|
|
||||||
|
|
||||||
if (dlg->show(she::instance()->defaultDisplay()))
|
if (dlg->show(she::instance()->defaultDisplay()))
|
||||||
res = dlg->getFileName();
|
res = dlg->getFileName();
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// SHE library
|
// 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.
|
// This file is released under the terms of the MIT license.
|
||||||
// Read LICENSE.txt for more information.
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
#import <AppKit/AppKit.h>
|
#include <AppKit/AppKit.h>
|
||||||
#import <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
|
||||||
#include <allegro.h>
|
#include <allegro.h>
|
||||||
#include <allegro/platform/alosx.h>
|
#include <allegro/platform/alosx.h>
|
||||||
@ -16,6 +16,11 @@
|
|||||||
#include "she/event.h"
|
#include "she/event.h"
|
||||||
#include "she/system.h"
|
#include "she/system.h"
|
||||||
|
|
||||||
|
void* get_osx_window()
|
||||||
|
{
|
||||||
|
return osx_window;
|
||||||
|
}
|
||||||
|
|
||||||
@interface SheAppDelegate : AllegroAppDelegate
|
@interface SheAppDelegate : AllegroAppDelegate
|
||||||
- (BOOL)application: (NSApplication *)theApplication openFile: (NSString *)filename;
|
- (BOOL)application: (NSApplication *)theApplication openFile: (NSString *)filename;
|
||||||
@end
|
@end
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#define WM_MOUSEHWHEEL 0x020E
|
#define WM_MOUSEHWHEEL 0x020E
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(ALLEGRO_UNIX)
|
#elif defined ALLEGRO_UNIX
|
||||||
|
|
||||||
#include <xalleg.h>
|
#include <xalleg.h>
|
||||||
#ifdef None
|
#ifdef None
|
||||||
@ -97,6 +97,8 @@ static void resize_callback(RESIZE_DISPLAY_EVENT* ev)
|
|||||||
|
|
||||||
static she::System* g_instance = nullptr;
|
static she::System* g_instance = nullptr;
|
||||||
|
|
||||||
|
void* get_osx_window();
|
||||||
|
|
||||||
namespace she {
|
namespace she {
|
||||||
|
|
||||||
class Alleg4EventQueue : public EventQueue {
|
class Alleg4EventQueue : public EventQueue {
|
||||||
@ -446,7 +448,8 @@ class Alleg4Display : public Display {
|
|||||||
public:
|
public:
|
||||||
Alleg4Display(int width, int height, int scale)
|
Alleg4Display(int width, int height, int scale)
|
||||||
: m_surface(NULL)
|
: m_surface(NULL)
|
||||||
, m_scale(0) {
|
, m_scale(0)
|
||||||
|
, m_nativeCursor(kNoCursor) {
|
||||||
unique_display = this;
|
unique_display = this;
|
||||||
|
|
||||||
if (install_mouse() < 0) throw DisplayCreationException(allegro_error);
|
if (install_mouse() < 0) throw DisplayCreationException(allegro_error);
|
||||||
@ -607,6 +610,10 @@ public:
|
|||||||
return m_queue;
|
return m_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NativeCursor nativeMouseCursor() override {
|
||||||
|
return m_nativeCursor;
|
||||||
|
}
|
||||||
|
|
||||||
bool setNativeMouseCursor(NativeCursor cursor) override {
|
bool setNativeMouseCursor(NativeCursor cursor) override {
|
||||||
int newCursor = MOUSE_CURSOR_NONE;
|
int newCursor = MOUSE_CURSOR_NONE;
|
||||||
|
|
||||||
@ -645,6 +652,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_nativeCursor = cursor;
|
||||||
return (show_os_cursor(newCursor) == 0);
|
return (show_os_cursor(newCursor) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,8 +692,10 @@ public:
|
|||||||
void* nativeHandle() override {
|
void* nativeHandle() override {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return reinterpret_cast<void*>(win_get_window());
|
return reinterpret_cast<void*>(win_get_window());
|
||||||
|
#elif defined __APPLE__
|
||||||
|
return get_osx_window();
|
||||||
#else
|
#else
|
||||||
return NULL;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,6 +703,7 @@ private:
|
|||||||
Surface* m_surface;
|
Surface* m_surface;
|
||||||
int m_scale;
|
int m_scale;
|
||||||
Alleg4EventQueue* m_queue;
|
Alleg4EventQueue* m_queue;
|
||||||
|
NativeCursor m_nativeCursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Alleg4System : public CommonSystem {
|
class Alleg4System : public CommonSystem {
|
||||||
|
@ -57,6 +57,7 @@ namespace she {
|
|||||||
|
|
||||||
virtual EventQueue* getEventQueue() = 0;
|
virtual EventQueue* getEventQueue() = 0;
|
||||||
|
|
||||||
|
virtual NativeCursor nativeMouseCursor() = 0;
|
||||||
virtual bool setNativeMouseCursor(NativeCursor cursor) = 0;
|
virtual bool setNativeMouseCursor(NativeCursor cursor) = 0;
|
||||||
virtual void setMousePosition(const gfx::Point& position) = 0;
|
virtual void setMousePosition(const gfx::Point& position) = 0;
|
||||||
virtual void captureMouse() = 0;
|
virtual void captureMouse() = 0;
|
||||||
|
@ -6,18 +6,185 @@
|
|||||||
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <Foundation/Foundation.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 "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 {
|
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()
|
NativeDialogsOSX::NativeDialogsOSX()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDialog* NativeDialogsOSX::createFileDialog()
|
FileDialog* NativeDialogsOSX::createFileDialog()
|
||||||
{
|
{
|
||||||
return nullptr;
|
return new FileDialogOSX();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace she
|
} // namespace she
|
||||||
|
@ -17,6 +17,7 @@ SkiaDisplay::SkiaDisplay(EventQueue* queue, int width, int height, int scale)
|
|||||||
, m_window(m_queue, this)
|
, m_window(m_queue, this)
|
||||||
, m_surface(new SkiaSurface)
|
, m_surface(new SkiaSurface)
|
||||||
, m_customSurface(false)
|
, m_customSurface(false)
|
||||||
|
, m_nativeCursor(kArrowCursor)
|
||||||
{
|
{
|
||||||
m_surface->create(width, height);
|
m_surface->create(width, height);
|
||||||
m_window.setScale(scale);
|
m_window.setScale(scale);
|
||||||
@ -116,8 +117,14 @@ EventQueue* SkiaDisplay::getEventQueue()
|
|||||||
return m_queue;
|
return m_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NativeCursor SkiaDisplay::nativeMouseCursor()
|
||||||
|
{
|
||||||
|
return m_nativeCursor;
|
||||||
|
}
|
||||||
|
|
||||||
bool SkiaDisplay::setNativeMouseCursor(NativeCursor cursor)
|
bool SkiaDisplay::setNativeMouseCursor(NativeCursor cursor)
|
||||||
{
|
{
|
||||||
|
m_nativeCursor = cursor;
|
||||||
m_window.setNativeMouseCursor(cursor);
|
m_window.setNativeMouseCursor(cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ public:
|
|||||||
bool isMaximized() const override;
|
bool isMaximized() const override;
|
||||||
void setTitleBar(const std::string& title) override;
|
void setTitleBar(const std::string& title) override;
|
||||||
EventQueue* getEventQueue() override;
|
EventQueue* getEventQueue() override;
|
||||||
|
NativeCursor nativeMouseCursor() override;
|
||||||
bool setNativeMouseCursor(NativeCursor cursor) override;
|
bool setNativeMouseCursor(NativeCursor cursor) override;
|
||||||
void setMousePosition(const gfx::Point& position) override;
|
void setMousePosition(const gfx::Point& position) override;
|
||||||
void captureMouse() override;
|
void captureMouse() override;
|
||||||
|
@ -57,7 +57,7 @@ public:
|
|||||||
m_defExtension = base::from_utf8(extension);
|
m_defExtension = base::from_utf8(extension);
|
||||||
m_defFilter = 0;
|
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 {
|
std::string getFileName() override {
|
||||||
@ -119,12 +119,37 @@ private:
|
|||||||
|
|
||||||
std::wstring getFiltersForGetOpenFileName() const {
|
std::wstring getFiltersForGetOpenFileName() const {
|
||||||
std::wstring filters;
|
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) {
|
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.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.append(base::from_utf8(filter.second));
|
||||||
filters.push_back('\0');
|
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');
|
filters.push_back('\0');
|
||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user