Skip to content

Commit ab00537

Browse files
committed
[dlgs] Move the native FileDialog stuff to laf-dlgs library
1 parent b78acc4 commit ab00537

21 files changed

+243
-319
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ endif()
6565
add_subdirectory(third_party)
6666
add_subdirectory(base)
6767
add_subdirectory(gfx)
68+
add_subdirectory(dlgs)
6869
if(FREETYPE_LIBRARIES AND HARFBUZZ_LIBRARIES)
6970
add_subdirectory(ft)
7071
endif()

dlgs/CMakeLists.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# laf-dlgs
2+
# Copyright (C) 2024 Igara Studio S.A.
3+
4+
add_library(laf-dlgs file_dialog.cpp)
5+
target_link_libraries(laf-dlgs laf-base)
6+
7+
if(WIN32)
8+
target_sources(laf-dlgs PRIVATE file_dialog_win.cpp)
9+
elseif(APPLE)
10+
target_sources(laf-dlgs PRIVATE file_dialog_osx.mm)
11+
else()
12+
target_sources(laf-dlgs PRIVATE file_dialog_x11.cpp)
13+
endif()

dlgs/file_dialog.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// laf-dlgs
2+
// Copyright (C) 2020-2024 Igara Studio S.A.
3+
// Copyright (C) 2017 David Capello
4+
//
5+
// This file is released under the terms of the MIT license.
6+
// Read LICENSE.txt for more information.
7+
8+
#ifdef HAVE_CONFIG_H
9+
#include "config.h"
10+
#endif
11+
12+
#include "dlgs/file_dialog.h"
13+
14+
namespace dlgs {
15+
16+
void FileDialog::setType(const Type type)
17+
{
18+
m_type = type;
19+
}
20+
21+
void FileDialog::setTitle(const std::string& title)
22+
{
23+
m_title = title;
24+
}
25+
26+
void FileDialog::setDefaultExtension(const std::string& extension)
27+
{
28+
m_defExtension = extension;
29+
}
30+
31+
void FileDialog::addFilter(const std::string& extension,
32+
const std::string& description)
33+
{
34+
if (m_defExtension.empty())
35+
m_defExtension = extension;
36+
37+
m_filters.push_back(std::make_pair(extension, description));
38+
}
39+
40+
FileDialogRef FileDialog::make(const Spec& spec)
41+
{
42+
#if LAF_WINDOWS
43+
return FileDialog::makeWin(spec);
44+
#elif LAF_MACOS
45+
return FileDialog::makeOSX(spec);
46+
#elif LAF_LINUX
47+
return FileDialog::makeX11(spec);
48+
#else
49+
return nullptr;
50+
#endif
51+
}
52+
53+
} // namespace dlgs

dlgs/file_dialog.h

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// laf-dlgs
2+
// Copyright (c) 2020-2024 Igara Studio S.A.
3+
// Copyright (c) 2015-2018 David Capello
4+
//
5+
// This file is released under the terms of the MIT license.
6+
// Read LICENSE.txt for more information.
7+
8+
#ifndef DLGS_FILE_DIALOG_H_INCLUDED
9+
#define DLGS_FILE_DIALOG_H_INCLUDED
10+
#pragma once
11+
12+
#include "base/paths.h"
13+
#include "base/ref.h"
14+
15+
#include <string>
16+
#include <utility>
17+
#include <vector>
18+
19+
namespace dlgs {
20+
class FileDialog;
21+
using FileDialogRef = base::Ref<FileDialog>;
22+
23+
class FileDialog : public base::RefCount {
24+
public:
25+
enum class Type {
26+
OpenFile,
27+
OpenFiles,
28+
OpenFolder,
29+
SaveFile,
30+
};
31+
32+
enum class Result {
33+
Error = -1, // This happens when we cannot open the native dialog
34+
Cancel = 0, // The user canceled the dialog
35+
OK = 1, // The user selected some file(s)
36+
};
37+
38+
struct Spec {
39+
#if LAF_MACOS
40+
// Indicates which is the "Edit" menu (NSMenuItem*) with
41+
// Undo/Redo/Cut/Copy/Paste/etc. commands. Used by the
42+
// FileDialogOSX impl to completely replace the "Edit" menu with a
43+
// standard one to make these
44+
// Undo/Redo/Cut/Copy/Paste/etc. keyboard shortcuts work.
45+
//
46+
// The specific details are not beautiful or important, but you
47+
// can search for "OSXEditMenuHack" to know why this is needed.
48+
void* editNSMenuItem = nullptr;
49+
#endif
50+
51+
#if LAF_LINUX
52+
// Connection to the X11 server (the Display* pointer returned by
53+
// XOpenDisplay()).
54+
void* x11display = nullptr;
55+
#endif
56+
};
57+
58+
static FileDialogRef make(const Spec& spec);
59+
#if LAF_WINDOWS
60+
static FileDialogRef makeWin(const Spec& spec);
61+
#elif LAF_MACOS
62+
static FileDialogRef makeOSX(const Spec& spec);
63+
#elif LAF_LINUX
64+
static FileDialogRef makeX11(const Spec& spec);
65+
#endif
66+
67+
virtual ~FileDialog() { }
68+
69+
void setType(const Type type);
70+
void setTitle(const std::string& title);
71+
void setDefaultExtension(const std::string& extension);
72+
void addFilter(const std::string& extension,
73+
const std::string& description);
74+
75+
virtual std::string fileName() = 0;
76+
virtual void getMultipleFileNames(base::paths& output) = 0;
77+
virtual void setFileName(const std::string& filename) = 0;
78+
79+
// The native window handle is an HWND on Windows, a NSWindow* on
80+
// macOS, or an X11 Window on Linux.
81+
virtual Result show(void* windowNative) = 0;
82+
83+
protected:
84+
Type m_type = Type::OpenFile;
85+
std::string m_title;
86+
std::string m_defExtension;
87+
std::vector<std::pair<std::string, std::string>> m_filters;
88+
};
89+
90+
} // namespace dlgs
91+
92+
#endif

os/osx/native_dialogs.mm renamed to dlgs/file_dialog_osx.mm

+40-42
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// LAF OS Library
2-
// Copyright (C) 2020-2022 Igara Studio S.A.
1+
// laf-dlgs
2+
// Copyright (C) 2020-2024 Igara Studio S.A.
33
// Copyright (C) 2012-2018 David Capello
44
//
55
// This file is released under the terms of the MIT license.
@@ -8,12 +8,9 @@
88
#include <Cocoa/Cocoa.h>
99
#include <vector>
1010

11+
#include "dlgs/file_dialog.h"
12+
1113
#include "base/fs.h"
12-
#include "os/common/file_dialog.h"
13-
#include "os/keys.h"
14-
#include "os/native_cursor.h"
15-
#include "os/osx/native_dialogs.h"
16-
#include "os/window.h"
1714

1815
#include <map>
1916

@@ -26,22 +23,23 @@
2623
// etc.
2724
//
2825
// So what we have to do if the main menu doesn't provide the standard
29-
// Edit selectors? (which is our case with our MenuItemOSX and MenuOSX
30-
// implementations):
26+
// "Edit" selectors? Which can be our case with laf-os MenuItemOSX and
27+
// MenuOSX implementations:
3128
//
32-
// 1. Before we open the NSSavePanel, find the Edit menu that was
33-
// specified by the user with os::MenuItem::setAsStandardEditMenuItem()
34-
// 2. Replace the Edit menu with a custom made one with the
35-
// standard Edit menu prepared especially with selectors
36-
// (undo:, redo:, cut:, etc.)
29+
// 1. Before we open the NSSavePanel, this class receives the current
30+
// "Edit" menu that was specified by the user from
31+
// FileDialog::Spec::editNSMenuItem.
32+
// 2. Replaces the "Edit" menu with a custom made one with the
33+
// standard Edit menu prepared especially with selectors (undo:,
34+
// redo:, cut:, etc.)
3735
// 3. Each menu item that contains a standard keyboard shortcut
3836
// (Command+C, Command+V, etc.) must be modified, because those
3937
// shortcuts are now used by this new Edit menu. So we remove the
4038
// keyEquivalent of each one. This is necessary only when one of
4139
// those items are outside the replaced Edit menu (e.g. if we have
4240
// Command+A to select all in other menu like Select > All, instead
4341
// of Edit > Select All)
44-
// 4. After the NSSavePanel is closed, we restore all keyEquivalent
42+
// 4. After the NSSavePanel is closed, it restores all keyEquivalent
4543
// shortcuts and the old Edit menu.
4644
//
4745
class OSXEditMenuHack {
@@ -121,20 +119,16 @@ void disableMenuItemsWithKeyEquivalent(NSMenu* menu,
121119

122120
} // anonymous namespace
123121

124-
namespace os {
125-
extern NSMenuItem* g_standardEditMenuItem;
126-
}
127-
128122
@interface OpenSaveHelper : NSObject {
129123
@private
130124
NSSavePanel* panel;
131-
os::Window* window;
125+
NSWindow* window;
132126
int result;
133127
std::function<void()> fileTypeChangeObserver;
134128
}
135129
- (id)init;
136130
- (void)setPanel:(NSSavePanel*)panel;
137-
- (void)setWindow:(os::Window*)window;
131+
- (void)setWindow:(NSWindow*)window;
138132
- (void)runModal;
139133
- (int)result;
140134
- (void)fileTypeChange;
@@ -146,6 +140,8 @@ @implementation OpenSaveHelper
146140
- (id)init
147141
{
148142
if (self = [super init]) {
143+
panel = nil;
144+
window = nil;
149145
result = NSFileHandlingPanelCancelButton;
150146
}
151147
return self;
@@ -156,18 +152,14 @@ - (void)setPanel:(NSSavePanel*)newPanel
156152
panel = newPanel;
157153
}
158154

159-
- (void)setWindow:(os::Window*)newWindow
155+
- (void)setWindow:(NSWindow*)newWindow
160156
{
161157
window = newWindow;
162158
}
163159

164160
// This is executed in the main thread.
165161
- (void)runModal
166162
{
167-
os::NativeCursor oldCursor = window->nativeCursor();
168-
window->setCursor(os::NativeCursor::Arrow);
169-
170-
OSXEditMenuHack hack(os::g_standardEditMenuItem);
171163
[[[NSApplication sharedApplication] mainMenu] setAutoenablesItems:NO];
172164

173165
#ifndef __MAC_10_6 // runModalForTypes is deprecated in 10.6
@@ -184,10 +176,11 @@ - (void)runModal
184176
result = [panel runModal];
185177
}
186178

187-
window->setCursor(oldCursor);
188-
NSWindow* nsWindow = (__bridge NSWindow *)window->nativeHandle();
189-
[nsWindow makeKeyAndOrderFront:nil];
190179
[[[NSApplication sharedApplication] mainMenu] setAutoenablesItems:YES];
180+
181+
// Re-active the parent key window.
182+
if (window)
183+
[window makeKeyAndOrderFront:nil];
191184
}
192185

193186
- (int)result
@@ -208,11 +201,13 @@ - (void)setFileTypeChangeObserver:(std::function<void()>)observer
208201

209202
@end
210203

211-
namespace os {
204+
namespace dlgs {
212205

213-
class FileDialogOSX : public CommonFileDialog {
206+
class FileDialogOSX : public FileDialog {
214207
public:
215-
FileDialogOSX() {
208+
FileDialogOSX(const Spec& spec) {
209+
if (spec.editNSMenuItem)
210+
m_editMenuItem = (__bridge NSMenuItem*)spec.editNSMenuItem;
216211
}
217212

218213
std::string fileName() override {
@@ -227,7 +222,7 @@ void setFileName(const std::string& filename) override {
227222
m_filename = filename;
228223
}
229224

230-
Result show(Window* window) override {
225+
Result show(void* window) override {
231226
Result retValue = Result::Cancel;
232227
@autoreleasepool {
233228
NSSavePanel* panel = nil;
@@ -282,7 +277,8 @@ Result show(Window* window) override {
282277

283278
OpenSaveHelper* helper = [OpenSaveHelper new];
284279
[helper setPanel:panel];
285-
[helper setWindow:window];
280+
if (window)
281+
[helper setWindow:(__bridge NSWindow*)window];
286282

287283
// Configure the file type popup/combobox.
288284
if (m_popup) {
@@ -323,7 +319,10 @@ Result show(Window* window) override {
323319
}];
324320
}
325321

326-
[helper performSelectorOnMainThread:@selector(runModal) withObject:nil waitUntilDone:YES];
322+
{
323+
OSXEditMenuHack hack(m_editMenuItem);
324+
[helper performSelectorOnMainThread:@selector(runModal) withObject:nil waitUntilDone:YES];
325+
}
327326

328327
if ([helper result] == NSFileHandlingPanelOKButton) {
329328
if (m_type == Type::OpenFiles) {
@@ -385,15 +384,14 @@ Result show(Window* window) override {
385384
// easily (configuring its target/action properties + accessing its
386385
// selected item).
387386
NSPopUpButton* m_popup = nullptr;
388-
};
389387

390-
NativeDialogsOSX::NativeDialogsOSX()
391-
{
392-
}
388+
// Pointer to the "Edit" menu used with OSXEditMenuHack.
389+
NSMenuItem* m_editMenuItem = nullptr;
390+
};
393391

394-
FileDialogRef NativeDialogsOSX::makeFileDialog()
392+
FileDialogRef FileDialog::makeOSX(const Spec& spec)
395393
{
396-
return make_ref<FileDialogOSX>();
394+
return base::make_ref<FileDialogOSX>(spec);
397395
}
398396

399-
} // namespace os
397+
} // namespace dlgs

0 commit comments

Comments
 (0)