зеркало из https://github.com/electron/electron.git
refactor: use `//ui/shell_dialogs` on Linux (#42110)
This commit is contained in:
Родитель
0864b87b56
Коммит
4d988dd217
15
BUILD.gn
15
BUILD.gn
|
@ -81,18 +81,11 @@ if (is_linux) {
|
|||
]
|
||||
}
|
||||
|
||||
# Generates electron_gtk_stubs.h header which contains
|
||||
# stubs for extracting function ptrs from the gtk library.
|
||||
# Function signatures for which stubs are required should be
|
||||
# declared in electron_gtk.sigs, currently this file contains
|
||||
# signatures for the functions used with native file chooser
|
||||
# implementation. In future, this file can be extended to contain
|
||||
# gtk4 stubs to switch gtk version in runtime.
|
||||
# Generates headers which contain stubs for extracting function ptrs
|
||||
# from the gtk library. Function signatures for which stubs are
|
||||
# required should be declared in the sig files.
|
||||
generate_stubs("electron_gtk_stubs") {
|
||||
sigs = [
|
||||
"shell/browser/ui/electron_gdk_pixbuf.sigs",
|
||||
"shell/browser/ui/electron_gtk.sigs",
|
||||
]
|
||||
sigs = [ "shell/browser/ui/electron_gdk_pixbuf.sigs" ]
|
||||
extra_header = "shell/browser/ui/electron_gtk.fragment"
|
||||
output_name = "electron_gtk_stubs"
|
||||
public_deps = [ "//ui/gtk:gtk_config" ]
|
||||
|
|
|
@ -34,7 +34,7 @@ filenames = {
|
|||
"shell/browser/notifications/linux/notification_presenter_linux.h",
|
||||
"shell/browser/relauncher_linux.cc",
|
||||
"shell/browser/ui/electron_desktop_window_tree_host_linux.cc",
|
||||
"shell/browser/ui/file_dialog_gtk.cc",
|
||||
"shell/browser/ui/file_dialog_linux.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.cc",
|
||||
"shell/browser/ui/gtk/menu_gtk.h",
|
||||
"shell/browser/ui/gtk/menu_util.cc",
|
||||
|
|
|
@ -131,3 +131,4 @@ fix_getcursorscreenpoint_wrongly_returns_0_0.patch
|
|||
fix_add_support_for_skipping_first_2_no-op_refreshes_in_thumb_cap.patch
|
||||
refactor_expose_file_system_access_blocklist.patch
|
||||
cherry-pick-013961609785.patch
|
||||
feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch
|
||||
|
|
|
@ -0,0 +1,243 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Sun, 5 May 2024 09:17:17 +0000
|
||||
Subject: feat: add support for missing dialog features to //shell_dialogs
|
||||
|
||||
This CL adds support for the following features to //shell_dialogs:
|
||||
* buttonLabel - Custom label for the confirmation button.
|
||||
* showHiddenFiles - Show hidden files in dialog.
|
||||
* showOverwriteConfirmation - Whether the user will be presented a confirmation dialog if the user types a file name that already exists.
|
||||
|
||||
This may be partially upstreamed to Chromium in the future.
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.cc b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
index 698f97b23f851c02af037880585c82d4494da63f..0a3b701a701289a2124214e7421a6383ff7f0320 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.cc
|
||||
@@ -243,6 +243,10 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
|
||||
std::string title_string = base::UTF16ToUTF8(title);
|
||||
|
||||
+ ExtraSettings extra_settings;
|
||||
+ if (params)
|
||||
+ extra_settings = *(static_cast<ExtraSettings*>(params));
|
||||
+
|
||||
set_file_type_index(file_type_index);
|
||||
if (file_types)
|
||||
set_file_types(*file_types);
|
||||
@@ -261,23 +265,23 @@ void SelectFileDialogLinuxGtk::SelectFileImpl(
|
||||
case SELECT_UPLOAD_FOLDER:
|
||||
case SELECT_EXISTING_FOLDER:
|
||||
dialog = CreateSelectFolderDialog(type, title_string, default_path,
|
||||
- owning_window);
|
||||
+ owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFolderDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_FILE:
|
||||
- dialog = CreateFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
case SELECT_OPEN_MULTI_FILE:
|
||||
dialog =
|
||||
- CreateMultiFileOpenDialog(title_string, default_path, owning_window);
|
||||
+ CreateMultiFileOpenDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectMultiFileDialogResponse);
|
||||
break;
|
||||
case SELECT_SAVEAS_FILE:
|
||||
- dialog = CreateSaveAsDialog(title_string, default_path, owning_window);
|
||||
+ dialog = CreateSaveAsDialog(title_string, default_path, owning_window, extra_settings);
|
||||
connect("response",
|
||||
&SelectFileDialogLinuxGtk::OnSelectSingleFileDialogResponse);
|
||||
break;
|
||||
@@ -412,10 +416,14 @@ void SelectFileDialogLinuxGtk::FileNotSelected(GtkWidget* dialog) {
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetOpenLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, GetCancelLabel(),
|
||||
- GTK_RESPONSE_CANCEL, GetOpenLabel(), GTK_RESPONSE_ACCEPT);
|
||||
+ GTK_RESPONSE_CANCEL, button_label, GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
AddFilters(GTK_FILE_CHOOSER(dialog));
|
||||
|
||||
@@ -431,6 +439,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenHelper(
|
||||
GtkFileChooserSetCurrentFolder(GTK_FILE_CHOOSER(dialog),
|
||||
*last_opened_path());
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog),
|
||||
+ settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@@ -438,7 +448,8 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string = title;
|
||||
if (title_string.empty()) {
|
||||
title_string =
|
||||
@@ -446,11 +457,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
? l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE)
|
||||
: l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
|
||||
}
|
||||
- std::string accept_button_label =
|
||||
- (type == SELECT_UPLOAD_FOLDER)
|
||||
- ? l10n_util::GetStringUTF8(
|
||||
- IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
- : GetOpenLabel();
|
||||
+
|
||||
+ std::string accept_button_label = settings.button_label;
|
||||
+ if (accept_button_label.empty()) {
|
||||
+ accept_button_label = (type == SELECT_UPLOAD_FOLDER)
|
||||
+ ? l10n_util::GetStringUTF8(
|
||||
+ IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)
|
||||
+ : GetOpenLabel();
|
||||
+ }
|
||||
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
@@ -472,19 +486,21 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSelectFolderDialog(
|
||||
gtk_file_filter_add_mime_type(only_folders, "inode/directory");
|
||||
gtk_file_filter_add_mime_type(only_folders, "text/directory");
|
||||
gtk_file_chooser_add_filter(chooser, only_folders);
|
||||
- gtk_file_chooser_set_select_multiple(chooser, FALSE);
|
||||
+ gtk_file_chooser_set_select_multiple(chooser, settings.allow_multiple_selection);
|
||||
+ gtk_file_chooser_set_show_hidden(chooser, settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -492,12 +508,14 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE);
|
||||
|
||||
- GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
|
||||
+ GtkWidget* dialog =
|
||||
+ CreateFileOpenHelper(title_string, default_path, parent, settings);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
|
||||
return dialog;
|
||||
}
|
||||
@@ -505,14 +523,17 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateMultiFileOpenDialog(
|
||||
GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent) {
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings) {
|
||||
std::string title_string =
|
||||
!title.empty() ? title
|
||||
: l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
|
||||
-
|
||||
+ const char* button_label = settings.button_label.empty()
|
||||
+ ? GetSaveLabel()
|
||||
+ : settings.button_label.c_str();
|
||||
GtkWidget* dialog = GtkFileChooserDialogNew(
|
||||
title_string.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
- GetCancelLabel(), GTK_RESPONSE_CANCEL, GetSaveLabel(),
|
||||
+ GetCancelLabel(), GTK_RESPONSE_CANCEL, button_label,
|
||||
GTK_RESPONSE_ACCEPT);
|
||||
SetGtkTransientForAura(dialog, parent);
|
||||
|
||||
@@ -538,9 +559,10 @@ GtkWidget* SelectFileDialogLinuxGtk::CreateSaveAsDialog(
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
||||
// Overwrite confirmation is always enabled in GTK4.
|
||||
if (!GtkCheckVersion(4)) {
|
||||
- gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
|
||||
- TRUE);
|
||||
+ gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
+ GTK_FILE_CHOOSER(dialog), settings.show_overwrite_confirmation);
|
||||
}
|
||||
+ gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), settings.show_hidden);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
diff --git a/ui/gtk/select_file_dialog_linux_gtk.h b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
index 53ae15f14c45ee72abdae172fc4555c9e4b3ff9a..af181afd9db1351cd886ba24dd651c7bf2f8a716 100644
|
||||
--- a/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
+++ b/ui/gtk/select_file_dialog_linux_gtk.h
|
||||
@@ -15,6 +15,13 @@
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+struct ExtraSettings {
|
||||
+ std::string button_label;
|
||||
+ bool show_overwrite_confirmation = true;
|
||||
+ bool show_hidden = false;
|
||||
+ bool allow_multiple_selection = false;
|
||||
+};
|
||||
+
|
||||
// Implementation of SelectFileDialog that shows a Gtk common dialog for
|
||||
// choosing a file or folder. This acts as a modal dialog.
|
||||
class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
@@ -90,19 +97,23 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
GtkWidget* CreateSelectFolderDialog(Type type,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateMultiFileOpenDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
GtkWidget* CreateSaveAsDialog(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Removes and returns the |params| associated with |dialog| from
|
||||
// |params_map_|.
|
||||
@@ -121,7 +132,8 @@ class SelectFileDialogLinuxGtk : public ui::SelectFileDialogLinux,
|
||||
// Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog.
|
||||
GtkWidget* CreateFileOpenHelper(const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
- gfx::NativeWindow parent);
|
||||
+ gfx::NativeWindow parent,
|
||||
+ const ExtraSettings& settings);
|
||||
|
||||
// Callback for when the user responds to a Save As or Open File dialog.
|
||||
void OnSelectSingleFileDialogResponse(GtkWidget* dialog, int response_id);
|
|
@ -1,13 +1,13 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
|
||||
Subject: Make gtk::GetLibGdkPixbuf public
|
||||
|
||||
Allows embedders to get a handle to the gtk and
|
||||
gdk_pixbuf libraries already loaded in the process.
|
||||
Allows embedders to get a handle to the gdk_pixbuf
|
||||
library already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411dbcbd96457 100644
|
||||
index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..e410998c4197c98947d2e1ad8bebe12c70379358 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -66,11 +66,6 @@ void* GetLibGio() {
|
||||
|
@ -22,20 +22,7 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411db
|
|||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
return libgtk4;
|
||||
}
|
||||
|
||||
-void* GetLibGtk() {
|
||||
- if (GtkCheckVersion(4))
|
||||
- return GetLibGtk4();
|
||||
- return GetLibGtk3();
|
||||
-}
|
||||
-
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -134,6 +123,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -134,6 +129,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -43,29 +30,20 @@ index 3a4b856ec5c2f5c3ede6e8f6db7858498b737702..a308249c66c01856df84e64bda0411db
|
|||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGtk() {
|
||||
+ if (GtkCheckVersion(4))
|
||||
+ return GetLibGtk4();
|
||||
+ return GetLibGtk3();
|
||||
+}
|
||||
+
|
||||
bool LoadGtk() {
|
||||
static bool loaded = LoadGtkImpl();
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 19f73cc179d82a3729c5fe37883460ac05f4d0c3..cdb67c98291f145f89f76a50b31bf00318648518 100644
|
||||
index 19f73cc179d82a3729c5fe37883460ac05f4d0c3..17aa0b95bd6158ed02c03095c1687185a057fe62 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -41,6 +41,12 @@ using SkColor = uint32_t;
|
||||
@@ -41,6 +41,9 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gtk library in the process.
|
||||
+void* GetLibGtk();
|
||||
+
|
||||
// Loads libgtk and related libraries and returns true on success.
|
||||
bool LoadGtk();
|
||||
|
|
|
@ -398,12 +398,6 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
|||
CHECK(linux_ui);
|
||||
linux_ui_getter_ = std::make_unique<LinuxUiGetterImpl>();
|
||||
|
||||
// Try loading gtk symbols used by Electron.
|
||||
electron::InitializeElectron_gtk(gtk::GetLibGtk());
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
electron::UninitializeElectron_gtk();
|
||||
}
|
||||
|
||||
electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
|
|
@ -1,395 +0,0 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "ui/base/glib/scoped_gsignal.h"
|
||||
#include "ui/gtk/gtk_ui.h" // nogncheck
|
||||
#include "ui/gtk/gtk_util.h" // nogncheck
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
static const int kPreviewWidth = 256;
|
||||
static const int kPreviewHeight = 512;
|
||||
|
||||
std::string MakeCaseInsensitivePattern(const std::string& extension) {
|
||||
// If the extension is the "all files" extension, no change needed.
|
||||
if (extension == "*")
|
||||
return extension;
|
||||
|
||||
std::string pattern("*.");
|
||||
for (char ch : extension) {
|
||||
if (!base::IsAsciiAlpha(ch)) {
|
||||
pattern.push_back(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.push_back('[');
|
||||
pattern.push_back(base::ToLowerASCII(ch));
|
||||
pattern.push_back(base::ToUpperASCII(ch));
|
||||
pattern.push_back(']');
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
class FileChooserDialog {
|
||||
public:
|
||||
FileChooserDialog(GtkFileChooserAction action, const DialogSettings& settings)
|
||||
: parent_(
|
||||
static_cast<electron::NativeWindowViews*>(settings.parent_window)),
|
||||
filters_(settings.filters) {
|
||||
auto label = settings.button_label;
|
||||
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_native_new(
|
||||
settings.title.c_str(), nullptr, action,
|
||||
label.empty() ? nullptr : label.c_str(), nullptr));
|
||||
} else {
|
||||
const char* confirm_text = gtk_util::GetOkLabel();
|
||||
if (!label.empty())
|
||||
confirm_text = label.c_str();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
confirm_text = gtk_util::GetSaveLabel();
|
||||
else if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
confirm_text = gtk_util::GetOpenLabel();
|
||||
|
||||
dialog_ = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new(
|
||||
settings.title.c_str(), nullptr, action, gtk_util::GetCancelLabel(),
|
||||
GTK_RESPONSE_CANCEL, confirm_text, GTK_RESPONSE_ACCEPT, nullptr));
|
||||
}
|
||||
|
||||
if (parent_) {
|
||||
parent_->SetEnabled(false);
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_set_modal(GTK_NATIVE_DIALOG(dialog_), TRUE);
|
||||
} else {
|
||||
gtk::SetGtkTransientForAura(GTK_WIDGET(dialog_),
|
||||
parent_->GetNativeWindow());
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(dialog_, TRUE);
|
||||
if (action != GTK_FILE_CHOOSER_ACTION_OPEN)
|
||||
gtk_file_chooser_set_create_folders(dialog_, TRUE);
|
||||
|
||||
if (!settings.default_path.empty()) {
|
||||
electron::ScopedAllowBlockingForElectron allow_blocking;
|
||||
if (base::DirectoryExists(settings.default_path)) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.value().c_str());
|
||||
} else {
|
||||
if (settings.default_path.IsAbsolute()) {
|
||||
gtk_file_chooser_set_current_folder(
|
||||
dialog_, settings.default_path.DirName().value().c_str());
|
||||
}
|
||||
|
||||
gtk_file_chooser_set_current_name(
|
||||
GTK_FILE_CHOOSER(dialog_),
|
||||
settings.default_path.BaseName().value().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (!settings.filters.empty())
|
||||
AddFilters(settings.filters);
|
||||
|
||||
// GtkFileChooserNative does not support preview widgets through the
|
||||
// org.freedesktop.portal.FileChooser portal. In the case of running through
|
||||
// the org.freedesktop.portal.FileChooser portal, anything having to do with
|
||||
// the update-preview signal or the preview widget will just be ignored.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
preview_ = gtk_image_new();
|
||||
signals_.emplace_back(
|
||||
dialog_, "update-preview",
|
||||
base::BindRepeating(&FileChooserDialog::OnUpdatePreview,
|
||||
base::Unretained(this)));
|
||||
gtk_file_chooser_set_preview_widget(dialog_, preview_);
|
||||
}
|
||||
}
|
||||
|
||||
~FileChooserDialog() {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_destroy(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_destroy(GTK_WIDGET(dialog_));
|
||||
}
|
||||
|
||||
if (parent_)
|
||||
parent_->SetEnabled(true);
|
||||
}
|
||||
|
||||
// disable copy
|
||||
FileChooserDialog(const FileChooserDialog&) = delete;
|
||||
FileChooserDialog& operator=(const FileChooserDialog&) = delete;
|
||||
|
||||
void SetupOpenProperties(int properties) {
|
||||
const auto hasProp = [properties](OpenFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_select_multiple(file_chooser,
|
||||
hasProp(OPEN_DIALOG_MULTI_SELECTIONS));
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(OPEN_DIALOG_SHOW_HIDDEN_FILES));
|
||||
}
|
||||
|
||||
void SetupSaveProperties(int properties) {
|
||||
const auto hasProp = [properties](SaveFileDialogProperty prop) {
|
||||
return gboolean((properties & prop) != 0);
|
||||
};
|
||||
auto* file_chooser = dialog();
|
||||
gtk_file_chooser_set_show_hidden(file_chooser,
|
||||
hasProp(SAVE_DIALOG_SHOW_HIDDEN_FILES));
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(
|
||||
file_chooser, hasProp(SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION));
|
||||
}
|
||||
|
||||
void RunAsynchronous() {
|
||||
signals_.emplace_back(
|
||||
GTK_WIDGET(dialog_), "response",
|
||||
base::BindRepeating(&FileChooserDialog::OnFileDialogResponse,
|
||||
base::Unretained(this)));
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_show(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog_));
|
||||
gtk::GtkUi::GetPlatform()->ShowGtkWindow(GTK_WINDOW(dialog_));
|
||||
}
|
||||
}
|
||||
|
||||
void RunSaveAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
save_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
void RunOpenAsynchronous(
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
open_promise_ =
|
||||
std::make_unique<gin_helper::Promise<gin_helper::Dictionary>>(
|
||||
std::move(promise));
|
||||
RunAsynchronous();
|
||||
}
|
||||
|
||||
base::FilePath GetFileName() const {
|
||||
gchar* filename = gtk_file_chooser_get_filename(dialog_);
|
||||
const base::FilePath path(filename);
|
||||
g_free(filename);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<base::FilePath> GetFileNames() const {
|
||||
std::vector<base::FilePath> paths;
|
||||
auto* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog_));
|
||||
for (auto* iter = filenames; iter != nullptr; iter = iter->next) {
|
||||
auto* filename = static_cast<char*>(iter->data);
|
||||
paths.emplace_back(filename);
|
||||
g_free(filename);
|
||||
}
|
||||
g_slist_free(filenames);
|
||||
return paths;
|
||||
}
|
||||
|
||||
void OnFileDialogResponse(GtkWidget* widget, int response);
|
||||
|
||||
GtkFileChooser* dialog() const { return dialog_; }
|
||||
|
||||
private:
|
||||
void AddFilters(const Filters& filters);
|
||||
|
||||
raw_ptr<electron::NativeWindowViews> parent_;
|
||||
|
||||
RAW_PTR_EXCLUSION GtkFileChooser* dialog_;
|
||||
RAW_PTR_EXCLUSION GtkWidget* preview_;
|
||||
|
||||
Filters filters_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> save_promise_;
|
||||
std::unique_ptr<gin_helper::Promise<gin_helper::Dictionary>> open_promise_;
|
||||
|
||||
// Callback for when we update the preview for the selection.
|
||||
void OnUpdatePreview(GtkFileChooser* chooser);
|
||||
|
||||
std::vector<ScopedGSignal> signals_;
|
||||
};
|
||||
|
||||
void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) {
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
gtk_native_dialog_hide(GTK_NATIVE_DIALOG(dialog_));
|
||||
} else {
|
||||
gtk_widget_hide(GTK_WIDGET(dialog_));
|
||||
}
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
if (save_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(save_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePath", GetFileName());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePath", base::FilePath());
|
||||
}
|
||||
save_promise_->Resolve(dict);
|
||||
} else if (open_promise_) {
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(open_promise_->isolate());
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", GetFileNames());
|
||||
} else {
|
||||
dict.Set("canceled", true);
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
open_promise_->Resolve(dict);
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileChooserDialog::AddFilters(const Filters& filters) {
|
||||
for (const auto& filter : filters) {
|
||||
GtkFileFilter* gtk_filter = gtk_file_filter_new();
|
||||
|
||||
for (const auto& extension : filter.second) {
|
||||
std::string pattern = MakeCaseInsensitivePattern(extension);
|
||||
gtk_file_filter_add_pattern(gtk_filter, pattern.c_str());
|
||||
}
|
||||
|
||||
gtk_file_filter_set_name(gtk_filter, filter.first.c_str());
|
||||
gtk_file_chooser_add_filter(dialog_, gtk_filter);
|
||||
}
|
||||
}
|
||||
|
||||
bool CanPreview(const struct stat& st) {
|
||||
// Only preview regular files; pipes may hang.
|
||||
// See https://crbug.com/534754.
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't preview huge files; they may crash.
|
||||
// https://github.com/electron/electron/issues/31630
|
||||
// Setting an arbitrary filesize max t at 100 MB here.
|
||||
constexpr off_t ArbitraryMax = 100000000ULL;
|
||||
return st.st_size < ArbitraryMax;
|
||||
}
|
||||
|
||||
void FileChooserDialog::OnUpdatePreview(GtkFileChooser* chooser) {
|
||||
CHECK(!electron::IsElectron_gtkInitialized());
|
||||
gchar* filename = gtk_file_chooser_get_preview_filename(chooser);
|
||||
if (!filename) {
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat sb;
|
||||
if (stat(filename, &sb) != 0 || !CanPreview(sb)) {
|
||||
g_free(filename);
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth,
|
||||
kPreviewHeight, nullptr);
|
||||
g_free(filename);
|
||||
if (pixbuf) {
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
gtk_file_chooser_set_preview_widget_active(chooser, pixbuf ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowFileDialog(const FileChooserDialog& dialog) {
|
||||
// gtk_native_dialog_run() will call gtk_native_dialog_show() for us.
|
||||
if (!electron::IsElectron_gtkInitialized()) {
|
||||
gtk_widget_show_all(GTK_WIDGET(dialog.dialog()));
|
||||
}
|
||||
}
|
||||
|
||||
int RunFileDialog(const FileChooserDialog& dialog) {
|
||||
int response = 0;
|
||||
if (electron::IsElectron_gtkInitialized()) {
|
||||
response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog.dialog()));
|
||||
} else {
|
||||
response = gtk_dialog_run(GTK_DIALOG(dialog.dialog()));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog open_dialog(action, settings);
|
||||
open_dialog.SetupOpenProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(open_dialog);
|
||||
|
||||
const int response = RunFileDialog(open_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*paths = open_dialog.GetFileNames();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
if (settings.properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||
FileChooserDialog* open_dialog = new FileChooserDialog(action, settings);
|
||||
open_dialog->SetupOpenProperties(settings.properties);
|
||||
open_dialog->RunOpenAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog.SetupSaveProperties(settings.properties);
|
||||
|
||||
ShowFileDialog(save_dialog);
|
||||
|
||||
const int response = RunFileDialog(save_dialog);
|
||||
if (response == GTK_RESPONSE_ACCEPT) {
|
||||
*path = save_dialog.GetFileName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* save_dialog =
|
||||
new FileChooserDialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings);
|
||||
save_dialog->RunSaveAsynchronous(std::move(promise));
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright (c) 2024 Microsoft, GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/file_dialog.h"
|
||||
#include "shell/common/gin_converters/callback_converter.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "ui/gtk/select_file_dialog_linux_gtk.h" // nogncheck
|
||||
#include "ui/shell_dialogs/select_file_dialog.h"
|
||||
#include "ui/shell_dialogs/selected_file_info.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
DialogSettings::DialogSettings() = default;
|
||||
DialogSettings::DialogSettings(const DialogSettings&) = default;
|
||||
DialogSettings::~DialogSettings() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
ui::SelectFileDialog::Type GetDialogType(int properties) {
|
||||
if (properties & OPEN_DIALOG_OPEN_DIRECTORY)
|
||||
return ui::SelectFileDialog::SELECT_FOLDER;
|
||||
|
||||
if (properties & OPEN_DIALOG_MULTI_SELECTIONS)
|
||||
return ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
|
||||
|
||||
return ui::SelectFileDialog::SELECT_OPEN_FILE;
|
||||
}
|
||||
|
||||
ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) {
|
||||
ui::SelectFileDialog::FileTypeInfo file_type_info;
|
||||
|
||||
for (const auto& [name, extension_group] : filters) {
|
||||
file_type_info.extension_description_overrides.push_back(
|
||||
base::UTF8ToUTF16(name));
|
||||
|
||||
const bool has_all_files_wildcard = base::ranges::any_of(
|
||||
extension_group, [](const auto& ext) { return ext == "*"; });
|
||||
if (has_all_files_wildcard) {
|
||||
file_type_info.include_all_files = true;
|
||||
} else {
|
||||
file_type_info.extensions.emplace_back(extension_group);
|
||||
}
|
||||
}
|
||||
|
||||
return file_type_info;
|
||||
}
|
||||
|
||||
class FileChooserDialog : public ui::SelectFileDialog::Listener {
|
||||
public:
|
||||
enum class DialogType { OPEN, SAVE };
|
||||
|
||||
FileChooserDialog() { dialog_ = ui::SelectFileDialog::Create(this, nullptr); }
|
||||
|
||||
~FileChooserDialog() override = default;
|
||||
|
||||
gtk::ExtraSettings GetExtraSettings(const DialogSettings& settings) {
|
||||
gtk::ExtraSettings extra;
|
||||
extra.button_label = settings.button_label;
|
||||
extra.show_overwrite_confirmation =
|
||||
settings.properties & SAVE_DIALOG_SHOW_OVERWRITE_CONFIRMATION;
|
||||
extra.allow_multiple_selection =
|
||||
settings.properties & OPEN_DIALOG_MULTI_SELECTIONS;
|
||||
if (type_ == DialogType::SAVE) {
|
||||
extra.show_hidden = settings.properties & SAVE_DIALOG_SHOW_HIDDEN_FILES;
|
||||
} else {
|
||||
extra.show_hidden = settings.properties & OPEN_DIALOG_SHOW_HIDDEN_FILES;
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
void RunSaveDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::SAVE;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
ui::SelectFileDialog::SELECT_SAVEAS_FILE,
|
||||
base::UTF8ToUTF16(settings.title), settings.default_path,
|
||||
&file_info /* file_types */, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunSaveDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunSaveDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunSaveDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialogImpl(const DialogSettings& settings) {
|
||||
type_ = DialogType::OPEN;
|
||||
ui::SelectFileDialog::FileTypeInfo file_info =
|
||||
GetFilterInfo(settings.filters);
|
||||
auto extra_settings = GetExtraSettings(settings);
|
||||
dialog_->SelectFile(
|
||||
GetDialogType(settings.properties), base::UTF8ToUTF16(settings.title),
|
||||
settings.default_path, &file_info, 0 /* file_type_index */,
|
||||
base::FilePath::StringType() /* default_extension */,
|
||||
settings.parent_window ? settings.parent_window->GetNativeWindow()
|
||||
: nullptr,
|
||||
static_cast<void*>(&extra_settings));
|
||||
}
|
||||
|
||||
void RunOpenDialog(gin_helper::Promise<gin_helper::Dictionary> promise,
|
||||
const DialogSettings& settings) {
|
||||
promise_ = std::move(promise);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void RunOpenDialog(base::OnceCallback<void(gin_helper::Dictionary)> callback,
|
||||
const DialogSettings& settings) {
|
||||
callback_ = std::move(callback);
|
||||
RunOpenDialogImpl(settings);
|
||||
}
|
||||
|
||||
void FileSelected(const ui::SelectedFileInfo& file,
|
||||
int index,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", file.file_path);
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>{file.file_path});
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MultiFilesSelected(const std::vector<ui::SelectedFileInfo>& files,
|
||||
void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", false);
|
||||
dict.Set("filePaths", ui::SelectedFileInfoListToFilePathList(files));
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FileSelectionCanceled(void* params) override {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("canceled", true);
|
||||
if (type_ == DialogType::SAVE) {
|
||||
dict.Set("filePath", base::FilePath());
|
||||
} else {
|
||||
dict.Set("filePaths", std::vector<base::FilePath>());
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
std::move(callback_).Run(dict);
|
||||
} else {
|
||||
promise_.Resolve(dict);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
DialogType type_;
|
||||
scoped_refptr<ui::SelectFileDialog> dialog_;
|
||||
base::OnceCallback<void(gin_helper::Dictionary)> callback_;
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ShowOpenDialogSync(const DialogSettings& settings,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePaths", file_paths);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), paths);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(cb), settings);
|
||||
|
||||
run_loop.Run();
|
||||
return !paths->empty();
|
||||
}
|
||||
|
||||
void ShowOpenDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunOpenDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
|
||||
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
|
||||
auto cb = base::BindOnce(
|
||||
[](base::RepeatingClosure cb, base::FilePath* file_path,
|
||||
gin_helper::Dictionary result) {
|
||||
result.Get("filePath", file_path);
|
||||
std::move(cb).Run();
|
||||
},
|
||||
run_loop.QuitClosure(), path);
|
||||
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
run_loop.Run();
|
||||
return !path->empty();
|
||||
}
|
||||
|
||||
void ShowSaveDialog(const DialogSettings& settings,
|
||||
gin_helper::Promise<gin_helper::Dictionary> promise) {
|
||||
FileChooserDialog* dialog = new FileChooserDialog();
|
||||
dialog->RunSaveDialog(std::move(promise), settings);
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
Загрузка…
Ссылка в новой задаче