GTK: use native file chooser dialog, if available.

This requires GTK 3.24+ and (at least for GTK 3.24) the environment
variable GTK_USE_PORTAL to be set to 1.
pull/403/head
whitequark 2019-05-08 22:53:25 +00:00
parent e7b75f19c3
commit bc3e09edbf
4 changed files with 106 additions and 40 deletions

View File

@ -81,6 +81,7 @@ Other new features:
* The "=" key is bound to "Zoom In", like "+" key.
* The numpad decimal separator key is bound to "." regardless of locale.
* On Windows, full-screen mode is implemented.
* On Linux, native file chooser dialog can be used.
Bugs fixed:
* A point in 3d constrained to any line whose length is free no longer
@ -98,7 +99,7 @@ Bugs fixed:
* Paste Transformed with a negative scale does not invert arcs.
* The tangent arc now modifies the original entities instead of deleting
them, such that their constraints are retained.
* When linking a sketch file, missing custom styles are now imported from
* When linking a sketch file, missing custom styles are now imported from
the linked file.
2.x

View File

@ -13,6 +13,14 @@ endif()
set(HAVE_SPACEWARE ${SPACEWARE_FOUND})
if(NOT WIN32 OR APPLE)
if(GTKMM_gtkmm-3.0_VERSION VERSION_LESS "3.24.0")
set(HAVE_GTK_FILECHOOSERNATIVE 0)
else()
set(HAVE_GTK_FILECHOOSERNATIVE 1)
endif()
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
${CMAKE_CURRENT_BINARY_DIR}/config.h)

View File

@ -16,4 +16,7 @@
#cmakedefine HAVE_BACKTRACE
#define BACKTRACE_HEADER @BACKTRACE_HEADER@
/* If we use GTK, can we use the native file chooser? */
#cmakedefine HAVE_GTK_FILECHOOSERNATIVE
#endif

View File

@ -15,6 +15,10 @@
#include <gtkmm/cssprovider.h>
#include <gtkmm/entry.h>
#include <gtkmm/filechooserdialog.h>
#define HAVE_GTK_FILECHOOSERNATIVE
#if defined(HAVE_GTK_FILECHOOSERNATIVE)
# include <gtkmm/filechoosernative.h>
#endif
#include <gtkmm/fixed.h>
#include <gtkmm/glarea.h>
#include <gtkmm/main.h>
@ -1187,32 +1191,27 @@ MessageDialogRef CreateMessageDialog(WindowRef parentWindow) {
// File dialogs
//-----------------------------------------------------------------------------
class FileDialogImplGtk final : public FileDialog {
class FileDialogImplGtk : public FileDialog {
public:
Gtk::FileChooserDialog gtkDialog;
Gtk::FileChooser *gtkChooser;
std::vector<std::string> extensions;
FileDialogImplGtk(Gtk::FileChooserDialog &&dialog)
: gtkDialog(std::move(dialog))
{
gtkDialog.property_filter().signal_changed().
void InitFileChooser(Gtk::FileChooser &chooser) {
gtkChooser = &chooser;
gtkChooser->property_filter().signal_changed().
connect(sigc::mem_fun(this, &FileDialogImplGtk::FilterChanged));
}
void SetTitle(std::string title) override {
gtkDialog.set_title(PrepareTitle(title));
}
void SetCurrentName(std::string name) override {
gtkDialog.set_current_name(name);
gtkChooser->set_current_name(name);
}
Platform::Path GetFilename() override {
return Path::From(gtkDialog.get_filename());
return Path::From(gtkChooser->get_filename());
}
void SetFilename(Platform::Path path) override {
gtkDialog.set_filename(path.raw);
gtkChooser->set_filename(path.raw);
}
void AddFilter(std::string name, std::vector<std::string> extensions) override {
@ -1233,13 +1232,13 @@ public:
gtkFilter->set_name(name + " (" + desc + ")");
this->extensions.push_back(extensions.front());
gtkDialog.add_filter(gtkFilter);
gtkChooser->add_filter(gtkFilter);
}
std::string GetExtension() {
auto filters = gtkDialog.list_filters();
auto filters = gtkChooser->list_filters();
size_t filterIndex =
std::find(filters.begin(), filters.end(), gtkDialog.get_filter()) -
std::find(filters.begin(), filters.end(), gtkChooser->get_filter()) -
filters.begin();
if(filterIndex < extensions.size()) {
return extensions[filterIndex];
@ -1249,14 +1248,14 @@ public:
}
void SetExtension(std::string extension) {
auto filters = gtkDialog.list_filters();
auto filters = gtkChooser->list_filters();
size_t extensionIndex =
std::find(extensions.begin(), extensions.end(), extension) -
extensions.begin();
if(extensionIndex < filters.size()) {
gtkDialog.set_filter(filters[extensionIndex]);
gtkChooser->set_filter(filters[extensionIndex]);
} else {
gtkDialog.set_filter(filters.front());
gtkChooser->set_filter(filters.front());
}
}
@ -1270,21 +1269,46 @@ public:
void FreezeChoices(SettingsRef settings, const std::string &key) override {
settings->FreezeString("Dialog_" + key + "_Folder",
gtkDialog.get_current_folder());
gtkChooser->get_current_folder());
settings->FreezeString("Dialog_" + key + "_Filter", GetExtension());
}
void ThawChoices(SettingsRef settings, const std::string &key) override {
gtkDialog.set_current_folder(settings->ThawString("Dialog_" + key + "_Folder"));
gtkChooser->set_current_folder(settings->ThawString("Dialog_" + key + "_Folder"));
SetExtension(settings->ThawString("Dialog_" + key + "_Filter"));
}
bool RunModal() override {
if(gtkDialog.get_action() == Gtk::FILE_CHOOSER_ACTION_SAVE &&
Path::From(gtkDialog.get_current_name()).FileStem().empty()) {
gtkDialog.set_current_name(std::string(_("untitled")) + "." + GetExtension());
void CheckForUntitledFile() {
if(gtkChooser->get_action() == Gtk::FILE_CHOOSER_ACTION_SAVE &&
Path::From(gtkChooser->get_current_name()).FileStem().empty()) {
gtkChooser->set_current_name(std::string(_("untitled")) + "." + GetExtension());
}
}
};
class FileDialogGtkImplGtk final : public FileDialogImplGtk {
public:
Gtk::FileChooserDialog gtkDialog;
FileDialogGtkImplGtk(Gtk::Window &gtkParent, bool isSave)
: gtkDialog(gtkParent,
isSave ? C_("title", "Save File")
: C_("title", "Open File"),
isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE
: Gtk::FILE_CHOOSER_ACTION_OPEN) {
gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
gtkDialog.add_button(isSave ? C_("button", "_Save")
: C_("button", "_Open"), Gtk::RESPONSE_OK);
gtkDialog.set_default_response(Gtk::RESPONSE_OK);
InitFileChooser(gtkDialog);
}
void SetTitle(std::string title) override {
gtkDialog.set_title(PrepareTitle(title));
}
bool RunModal() override {
CheckForUntitledFile();
if(gtkDialog.run() == Gtk::RESPONSE_OK) {
return true;
} else {
@ -1293,26 +1317,56 @@ public:
}
};
#if defined(HAVE_GTK_FILECHOOSERNATIVE)
class FileDialogNativeImplGtk final : public FileDialogImplGtk {
public:
Glib::RefPtr<Gtk::FileChooserNative> gtkNative;
FileDialogNativeImplGtk(Gtk::Window &gtkParent, bool isSave) {
gtkNative = Gtk::FileChooserNative::create(
isSave ? C_("title", "Save File")
: C_("title", "Open File"),
gtkParent,
isSave ? Gtk::FILE_CHOOSER_ACTION_SAVE
: Gtk::FILE_CHOOSER_ACTION_OPEN,
isSave ? C_("button", "_Save")
: C_("button", "_Open"),
C_("button", "_Cancel"));
// Seriously, GTK?!
InitFileChooser(*gtkNative.operator->());
}
void SetTitle(std::string title) override {
gtkNative->set_title(PrepareTitle(title));
}
bool RunModal() override {
CheckForUntitledFile();
if(gtkNative->run() == Gtk::RESPONSE_ACCEPT) {
return true;
} else {
return false;
}
}
};
#endif
#if defined(HAVE_GTK_FILECHOOSERNATIVE)
# define FILE_DIALOG_IMPL FileDialogNativeImplGtk
#else
# define FILE_DIALOG_IMPL FileDialogGtkImplGtk
#endif
FileDialogRef CreateOpenFileDialog(WindowRef parentWindow) {
Gtk::Window &gtkParent = std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow;
Gtk::FileChooserDialog gtkDialog(gtkParent, C_("title", "Open File"),
Gtk::FILE_CHOOSER_ACTION_OPEN);
gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
gtkDialog.add_button(C_("button", "_Open"), Gtk::RESPONSE_OK);
gtkDialog.set_default_response(Gtk::RESPONSE_OK);
return std::make_shared<FileDialogImplGtk>(std::move(gtkDialog));
return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/false);
}
FileDialogRef CreateSaveFileDialog(WindowRef parentWindow) {
Gtk::Window &gtkParent = std::static_pointer_cast<WindowImplGtk>(parentWindow)->gtkWindow;
Gtk::FileChooserDialog gtkDialog(gtkParent, C_("title", "Save File"),
Gtk::FILE_CHOOSER_ACTION_SAVE);
gtkDialog.set_do_overwrite_confirmation(true);
gtkDialog.add_button(C_("button", "_Cancel"), Gtk::RESPONSE_CANCEL);
gtkDialog.add_button(C_("button", "_Save"), Gtk::RESPONSE_OK);
gtkDialog.set_default_response(Gtk::RESPONSE_OK);
return std::make_shared<FileDialogImplGtk>(std::move(gtkDialog));
return std::make_shared<FILE_DIALOG_IMPL>(gtkParent, /*isSave=*/true);
}
//-----------------------------------------------------------------------------