mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +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);
|
||||
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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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 @@ 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<void*>(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 {
|
||||
|
@ -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 @@ 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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user