Add native file dialog for Linux using zenity (fix #1953, fix #2354)

The Zenity utility is not perfect, there are several issues to improve
the UX, but it's good enough to offer a native file dialog on Linux
without depending on huge dynamic GUI libraries (GTK+, Qt, etc.).
This commit is contained in:
David Capello 2023-06-27 14:28:32 -03:00
parent 3e171d0f6a
commit 556c621eeb
5 changed files with 87 additions and 44 deletions

View File

@ -109,16 +109,6 @@ if(NOT LAF_OS_BACKEND)
endif()
endif()
# Check valid gtk + libpng combination
if(LAF_OS_WITH_GTK)
if(NOT USE_SHARED_LIBPNG)
message(FATAL_ERROR "Cannot compile with gtk and static libpng, set USE_SHARED_LIBPNG=ON")
endif()
if(NOT USE_SHARED_HARFBUZZ)
message(FATAL_ERROR "Cannot compile with gtk and static HarfBuzz, set USE_SHARED_HARFBUZZ=ON")
endif()
endif()
if(ENABLE_DRM AND NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/drm/CMakeLists.txt)
message(FATAL_ERROR "Your Aseprite repository is incomplete, clone the drm repository")
endif()

2
laf

@ -1 +1 @@
Subproject commit f89c72e5bfdc6d8923fe0b3fbef15ec38333630a
Subproject commit 5e936d76335172a4605dc09556a3bf8d54252b05

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,6 +14,7 @@
#include "app/app.h"
#include "app/pref/preferences.h"
#include "app/ui/file_selector.h"
#include "base/fs.h"
#include "os/native_dialogs.h"
#include "os/system.h"
#include "os/window.h"
@ -37,7 +38,21 @@ bool show_file_selector(
if (dlg) {
dlg->setTitle(title);
// Must be set before setFileName() as the Linux impl might
// require the default extension to fix the initial file name
// with the default extension.
if (!defExtension.empty()) {
dlg->setDefaultExtension(defExtension);
}
#if LAF_LINUX // As the X11 version doesn't store the default path to
// start navigating, we use our own
// get_initial_path_to_select_filename()
dlg->setFileName(get_initial_path_to_select_filename(initialPath));
#else // !LAF_LINUX
dlg->setFileName(initialPath);
#endif
os::FileDialog::Type nativeType = os::FileDialog::Type::OpenFile;
switch (type) {
@ -56,17 +71,30 @@ bool show_file_selector(
for (const auto& ext : extensions)
dlg->addFilter(ext, ext + " files (*." + ext + ")");
if (!defExtension.empty())
dlg->setDefaultExtension(defExtension);
auto res = dlg->show(os::instance()->defaultWindow());
if (res != os::FileDialog::Result::Error) {
if (res == os::FileDialog::Result::OK) {
if (type == FileSelectorType::OpenMultiple)
dlg->getMultipleFileNames(output);
else
output.push_back(dlg->fileName());
bool res = dlg->show(os::instance()->defaultWindow());
if (res) {
if (type == FileSelectorType::OpenMultiple)
dlg->getMultipleFileNames(output);
else
output.push_back(dlg->fileName());
#if LAF_LINUX // Save the path in the configuration file
if (!output.empty()) {
set_current_dir_for_file_selector(base::get_file_path(output[0]));
}
#endif
return true;
}
else {
return false;
}
}
else {
// Fallback to default file selector if we weren't able to
// open the native dialog...
}
return res;
}
}
@ -78,4 +106,38 @@ bool show_file_selector(
return fileSelector.show(title, initialPath, extensions, output);
}
std::string get_initial_path_to_select_filename(const std::string& initialFilename)
{
std::string path = base::get_file_path(initialFilename);
// If initialFilename doesn't contain a path/directory.
if (path.empty()) {
// Put the saved 'path' from the configuration file.
path = base::join_path(get_current_dir_for_file_selector(),
base::get_file_name(initialFilename));
}
else {
path = initialFilename;
}
path = base::fix_path_separators(path);
return path;
}
std::string get_current_dir_for_file_selector()
{
std::string path = Preferences::instance().fileSelector.currentFolder();
// If it's empty or the folder doesn't exist anymore, starts from
// the docs folder by default.
if (path.empty() ||
path == "<empty>" ||
!base::is_directory(path)) {
path = base::get_user_docs_folder();
}
return path;
}
void set_current_dir_for_file_selector(const std::string& dirPath)
{
Preferences::instance().fileSelector.currentFolder(dirPath);
}
} // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -28,6 +29,14 @@ namespace app {
FileSelectorType type,
base::paths& output);
std::string get_initial_path_to_select_filename(
const std::string& initialFilename);
// Get/set the directory used for the file selector (custom one, and
// the X11 native one).
std::string get_current_dir_for_file_selector();
void set_current_dir_for_file_selector(const std::string& dirPath);
} // namespace app
#endif

View File

@ -410,28 +410,10 @@ bool FileSelector::show(
fs->refresh();
// we have to find where the user should begin to browse files (start_folder)
std::string start_folder_path;
IFileItem* start_folder = nullptr;
// If initialPath doesn't contain a path.
if (base::get_file_path(initialPath).empty()) {
// Get the saved `path' in the configuration file.
std::string path = Preferences::instance().fileSelector.currentFolder();
if (path == "<empty>") {
start_folder_path = base::get_user_docs_folder();
path = base::join_path(start_folder_path, initialPath);
}
start_folder = fs->getFileItemFromPath(path);
}
else {
// Remove the filename.
start_folder_path = base::join_path(base::get_file_path(initialPath), "");
}
start_folder_path = base::fix_path_separators(start_folder_path);
if (!start_folder)
start_folder = fs->getFileItemFromPath(start_folder_path);
// We have to find where the user should begin to browse files
std::string start_folder_path =
base::get_file_path(get_initial_path_to_select_filename(initialPath));
IFileItem* start_folder = fs->getFileItemFromPath(start_folder_path);
FILESEL_TRACE("FILESEL: Start folder '%s' (%p)\n", start_folder_path.c_str(), start_folder);
@ -703,7 +685,7 @@ again:
// save the path in the configuration file
std::string lastpath = folder->keyName();
Preferences::instance().fileSelector.currentFolder(lastpath);
set_current_dir_for_file_selector(lastpath);
}
Preferences::instance().fileSelector.zoom(m_fileList->zoom());