mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-18 14:07:01 +00:00
GTK file chooser image preview support added. #2968
Thanks to Chromium code. Also fixed build on GCC / Linux.
This commit is contained in:
parent
e8b03248e1
commit
4838d1bfc9
Telegram/SourceFiles
@ -501,9 +501,9 @@ class guard {
|
||||
public:
|
||||
using return_type = typename lambda_type<Lambda>::return_type;
|
||||
|
||||
template <typename ...PointersAndLambda>
|
||||
inline guard(PointersAndLambda&&... qobjectsAndLambda) : _data(std::make_unique<guard_data<N, Lambda>>(std::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
|
||||
static_assert(sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!");
|
||||
template <typename Pointer, typename Other, typename ...PointersAndLambda>
|
||||
inline guard(Pointer &&qobject, Other &&other, PointersAndLambda&&... qobjectsAndLambda) : _data(std::make_unique<guard_data<N, Lambda>>(std::forward<Pointer>(qobject), std::forward<Other>(other), std::forward<PointersAndLambda>(qobjectsAndLambda)...)) {
|
||||
static_assert(1 + 1 + sizeof...(PointersAndLambda) == N + 1, "Wrong argument count!");
|
||||
}
|
||||
|
||||
inline guard(const guard &other) : _data(std::make_unique<guard_data<N, Lambda>>(static_cast<const guard_data<N, Lambda> &>(*other._data))) {
|
||||
|
@ -31,6 +31,19 @@ QStringList qt_make_filter_list(const QString &filter);
|
||||
|
||||
namespace Platform {
|
||||
namespace FileDialog {
|
||||
namespace {
|
||||
|
||||
// GTK file chooser image preview: thanks to Chromium
|
||||
|
||||
// The size of the preview we display for selected image files. We set height
|
||||
// larger than width because generally there is more free space vertically
|
||||
// than horiztonally (setting the preview image will alway expand the width of
|
||||
// the dialog, but usually not the height). The image's aspect ratio will always
|
||||
// be preserved.
|
||||
constexpr auto kPreviewWidth = 256;
|
||||
constexpr auto kPreviewHeight = 512;
|
||||
|
||||
} // namespace
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
@ -70,6 +83,11 @@ bool Supported() {
|
||||
&& (Libs::gtk_file_filter_new != nullptr);
|
||||
}
|
||||
|
||||
bool PreviewSupported() {
|
||||
return Supported()
|
||||
&& (Libs::gdk_pixbuf_new_from_file_at_size != nullptr);
|
||||
}
|
||||
|
||||
bool Get(QStringList &files, QByteArray &remoteContent, const QString &caption, const QString &filter, Type type, QString startFile) {
|
||||
auto parent = App::wnd() ? App::wnd()->filedialogParent() : nullptr;
|
||||
internal::GtkFileDialog dialog(parent, caption, QString(), filter);
|
||||
@ -117,7 +135,12 @@ namespace internal {
|
||||
|
||||
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
|
||||
Libs::g_signal_connect_swapped_helper(Libs::g_object_cast(gtkWidget), "response", GCallback(onResponse), this);
|
||||
Libs::g_signal_connect_helper(Libs::g_object_cast(gtkWidget), "delete-event", GCallback(Libs::gtk_widget_hide_on_delete), NULL);
|
||||
Libs::g_signal_connect_helper(Libs::g_object_cast(gtkWidget), "delete-event", GCallback(Libs::gtk_widget_hide_on_delete), nullptr);
|
||||
if (PreviewSupported()) {
|
||||
_preview = Libs::gtk_image_new();
|
||||
Libs::g_signal_connect_swapped_helper(Libs::g_object_cast(gtkWidget), "update-preview", GCallback(onUpdatePreview), this);
|
||||
Libs::gtk_file_chooser_set_preview_widget(Libs::gtk_file_chooser_cast(gtkWidget), _preview);
|
||||
}
|
||||
}
|
||||
|
||||
QGtkDialog::~QGtkDialog() {
|
||||
@ -179,6 +202,32 @@ void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
|
||||
emit dialog->reject();
|
||||
}
|
||||
|
||||
void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) {
|
||||
auto filename = Libs::gtk_file_chooser_get_preview_filename(Libs::gtk_file_chooser_cast(dialog->gtkWidget));
|
||||
if (!filename) {
|
||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't attempt to open anything which isn't a regular file. If a named pipe,
|
||||
// this may hang. See https://crbug.com/534754.
|
||||
struct stat stat_buf;
|
||||
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
|
||||
Libs::g_free(filename);
|
||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
auto pixbuf = Libs::gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
|
||||
Libs::g_free(filename);
|
||||
if (pixbuf) {
|
||||
Libs::gtk_image_set_from_pixbuf(Libs::gtk_image_cast(dialog->_preview), pixbuf);
|
||||
Libs::g_object_unref(pixbuf);
|
||||
}
|
||||
Libs::gtk_file_chooser_set_preview_widget_active(Libs::gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false);
|
||||
}
|
||||
|
||||
void QGtkDialog::onParentWindowDestroyed() {
|
||||
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
|
||||
setParent(nullptr);
|
||||
|
@ -48,42 +48,44 @@ namespace internal {
|
||||
// We need to be able to work with gtk2 and gtk3, because
|
||||
// we use gtk3 to work with appindicator3.
|
||||
class QGtkDialog : public QWindow {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QGtkDialog(GtkWidget *gtkWidget);
|
||||
~QGtkDialog();
|
||||
QGtkDialog(GtkWidget *gtkWidget);
|
||||
~QGtkDialog();
|
||||
|
||||
GtkDialog *gtkDialog() const;
|
||||
GtkDialog *gtkDialog() const;
|
||||
|
||||
void exec();
|
||||
void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
||||
void hide();
|
||||
void exec();
|
||||
void show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
||||
void hide();
|
||||
|
||||
signals:
|
||||
void accept();
|
||||
void reject();
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
protected:
|
||||
static void onResponse(QGtkDialog *dialog, int response);
|
||||
static void onResponse(QGtkDialog *dialog, int response);
|
||||
static void onUpdatePreview(QGtkDialog *dialog);
|
||||
|
||||
private slots:
|
||||
void onParentWindowDestroyed();
|
||||
void onParentWindowDestroyed();
|
||||
|
||||
private:
|
||||
GtkWidget *gtkWidget;
|
||||
GtkWidget *gtkWidget;
|
||||
GtkWidget *_preview = nullptr;
|
||||
|
||||
};
|
||||
|
||||
class GtkFileDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GtkFileDialog(QWidget *parent = Q_NULLPTR,
|
||||
const QString &caption = QString(),
|
||||
const QString &directory = QString(),
|
||||
const QString &filter = QString());
|
||||
~GtkFileDialog();
|
||||
GtkFileDialog(QWidget *parent = Q_NULLPTR,
|
||||
const QString &caption = QString(),
|
||||
const QString &directory = QString(),
|
||||
const QString &filter = QString());
|
||||
~GtkFileDialog();
|
||||
|
||||
void setVisible(bool visible) override;
|
||||
|
||||
@ -104,26 +106,26 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int exec() override;
|
||||
int exec() override;
|
||||
|
||||
bool defaultNameFilterDisables() const;
|
||||
void setDirectory(const QString &directory);
|
||||
QDir directory() const;
|
||||
void selectFile(const QString &filename);
|
||||
QStringList selectedFiles() const;
|
||||
void setFilter();
|
||||
void selectNameFilter(const QString &filter);
|
||||
QString selectedNameFilter() const;
|
||||
bool defaultNameFilterDisables() const;
|
||||
void setDirectory(const QString &directory);
|
||||
QDir directory() const;
|
||||
void selectFile(const QString &filename);
|
||||
QStringList selectedFiles() const;
|
||||
void setFilter();
|
||||
void selectNameFilter(const QString &filter);
|
||||
QString selectedNameFilter() const;
|
||||
|
||||
private slots:
|
||||
void onAccepted();
|
||||
void onAccepted();
|
||||
void onRejected();
|
||||
|
||||
private:
|
||||
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
|
||||
static void onCurrentFolderChanged(GtkFileDialog *helper);
|
||||
void applyOptions();
|
||||
void setNameFilters(const QStringList &filters);
|
||||
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
|
||||
static void onCurrentFolderChanged(GtkFileDialog *helper);
|
||||
void applyOptions();
|
||||
void setNameFilters(const QStringList &filters);
|
||||
|
||||
void showHelper(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
|
||||
void hideHelper();
|
||||
@ -137,11 +139,11 @@ private:
|
||||
QFileDialog::AcceptMode _acceptMode = QFileDialog::AcceptOpen;
|
||||
QFileDialog::FileMode _fileMode = QFileDialog::ExistingFile;
|
||||
|
||||
QString _dir;
|
||||
QStringList _selection;
|
||||
QHash<QString, GtkFileFilter*> _filters;
|
||||
QHash<GtkFileFilter*, QString> _filterNames;
|
||||
QScopedPointer<QGtkDialog> d;
|
||||
QString _dir;
|
||||
QStringList _selection;
|
||||
QHash<QString, GtkFileFilter*> _filters;
|
||||
QHash<GtkFileFilter*, QString> _filterNames;
|
||||
QScopedPointer<QGtkDialog> d;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -67,6 +67,7 @@ bool setupGtkBase(QLibrary &lib_gtk) {
|
||||
if (!load(lib_gtk, "gtk_clipboard_store", gtk_clipboard_store)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_dialog_new", gtk_file_chooser_dialog_new)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_get_type", gtk_file_chooser_get_type)) return false;
|
||||
if (!load(lib_gtk, "gtk_image_get_type", gtk_image_get_type)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_set_current_folder", gtk_file_chooser_set_current_folder)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_get_current_folder", gtk_file_chooser_get_current_folder)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_set_current_name", gtk_file_chooser_set_current_name)) return false;
|
||||
@ -84,7 +85,12 @@ bool setupGtkBase(QLibrary &lib_gtk) {
|
||||
if (!load(lib_gtk, "gtk_file_filter_set_name", gtk_file_filter_set_name)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_filter_add_pattern", gtk_file_filter_add_pattern)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_add_filter", gtk_file_chooser_add_filter)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_set_preview_widget", gtk_file_chooser_set_preview_widget)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_get_preview_filename", gtk_file_chooser_get_preview_filename)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_chooser_set_preview_widget_active", gtk_file_chooser_set_preview_widget_active)) return false;
|
||||
if (!load(lib_gtk, "gtk_file_filter_new", gtk_file_filter_new)) return false;
|
||||
if (!load(lib_gtk, "gtk_image_new", gtk_image_new)) return false;
|
||||
if (!load(lib_gtk, "gtk_image_set_from_pixbuf", gtk_image_set_from_pixbuf)) return false;
|
||||
|
||||
if (!load(lib_gtk, "gdk_window_set_modal_hint", gdk_window_set_modal_hint)) return false;
|
||||
if (!load(lib_gtk, "gdk_window_focus", gdk_window_focus)) return false;
|
||||
@ -149,6 +155,7 @@ f_gtk_clipboard_get gtk_clipboard_get = nullptr;
|
||||
f_gtk_clipboard_store gtk_clipboard_store = nullptr;
|
||||
f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new = nullptr;
|
||||
f_gtk_file_chooser_get_type gtk_file_chooser_get_type = nullptr;
|
||||
f_gtk_image_get_type gtk_image_get_type = nullptr;
|
||||
f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder = nullptr;
|
||||
f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder = nullptr;
|
||||
f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name = nullptr;
|
||||
@ -166,7 +173,12 @@ f_gtk_file_chooser_remove_filter gtk_file_chooser_remove_filter = nullptr;
|
||||
f_gtk_file_filter_set_name gtk_file_filter_set_name = nullptr;
|
||||
f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern = nullptr;
|
||||
f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter = nullptr;
|
||||
f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget = nullptr;
|
||||
f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename = nullptr;
|
||||
f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active = nullptr;
|
||||
f_gtk_file_filter_new gtk_file_filter_new = nullptr;
|
||||
f_gtk_image_new gtk_image_new = nullptr;
|
||||
f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf = nullptr;
|
||||
f_gtk_dialog_get_widget_for_response gtk_dialog_get_widget_for_response = nullptr;
|
||||
f_gtk_button_set_label gtk_button_set_label = nullptr;
|
||||
f_gtk_button_get_type gtk_button_get_type = nullptr;
|
||||
@ -185,6 +197,7 @@ f_app_indicator_set_icon_full app_indicator_set_icon_full = nullptr;
|
||||
f_gdk_init_check gdk_init_check = nullptr;
|
||||
f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data = nullptr;
|
||||
f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file = nullptr;
|
||||
f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size = nullptr;
|
||||
f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf = nullptr;
|
||||
f_gtk_status_icon_set_from_pixbuf gtk_status_icon_set_from_pixbuf = nullptr;
|
||||
f_gtk_status_icon_new_from_file gtk_status_icon_new_from_file = nullptr;
|
||||
@ -248,6 +261,7 @@ void start() {
|
||||
load(lib_gtk, "gdk_init_check", gdk_init_check);
|
||||
load(lib_gtk, "gdk_pixbuf_new_from_data", gdk_pixbuf_new_from_data);
|
||||
load(lib_gtk, "gdk_pixbuf_new_from_file", gdk_pixbuf_new_from_file);
|
||||
load(lib_gtk, "gdk_pixbuf_new_from_file_at_size", gdk_pixbuf_new_from_file_at_size);
|
||||
load(lib_gtk, "gtk_status_icon_new_from_pixbuf", gtk_status_icon_new_from_pixbuf);
|
||||
load(lib_gtk, "gtk_status_icon_set_from_pixbuf", gtk_status_icon_set_from_pixbuf);
|
||||
load(lib_gtk, "gtk_status_icon_new_from_file", gtk_status_icon_new_from_file);
|
||||
|
@ -112,13 +112,13 @@ extern f_gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new;
|
||||
typedef gboolean (*f_gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename);
|
||||
extern f_gtk_file_chooser_set_current_folder gtk_file_chooser_set_current_folder;
|
||||
|
||||
typedef gchar *(*f_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
|
||||
typedef gchar* (*f_gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser);
|
||||
extern f_gtk_file_chooser_get_current_folder gtk_file_chooser_get_current_folder;
|
||||
|
||||
typedef void (*f_gtk_file_chooser_set_current_name)(GtkFileChooser *chooser, const gchar *name);
|
||||
extern f_gtk_file_chooser_set_current_name gtk_file_chooser_set_current_name;
|
||||
|
||||
typedef gboolean (*f_gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const char *filename);
|
||||
typedef gboolean (*f_gtk_file_chooser_select_filename)(GtkFileChooser *chooser, const gchar *filename);
|
||||
extern f_gtk_file_chooser_select_filename gtk_file_chooser_select_filename;
|
||||
|
||||
typedef GSList* (*f_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
|
||||
@ -163,9 +163,24 @@ extern f_gtk_file_filter_add_pattern gtk_file_filter_add_pattern;
|
||||
typedef void (*f_gtk_file_chooser_add_filter)(GtkFileChooser *chooser, GtkFileFilter *filter);
|
||||
extern f_gtk_file_chooser_add_filter gtk_file_chooser_add_filter;
|
||||
|
||||
typedef void (*f_gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkWidget *preview_widget);
|
||||
extern f_gtk_file_chooser_set_preview_widget gtk_file_chooser_set_preview_widget;
|
||||
|
||||
typedef gchar* (*f_gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser);
|
||||
extern f_gtk_file_chooser_get_preview_filename gtk_file_chooser_get_preview_filename;
|
||||
|
||||
typedef void (*f_gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active);
|
||||
extern f_gtk_file_chooser_set_preview_widget_active gtk_file_chooser_set_preview_widget_active;
|
||||
|
||||
typedef GtkFileFilter* (*f_gtk_file_filter_new)(void);
|
||||
extern f_gtk_file_filter_new gtk_file_filter_new;
|
||||
|
||||
typedef GtkWidget* (*f_gtk_image_new)(void);
|
||||
extern f_gtk_image_new gtk_image_new;
|
||||
|
||||
typedef void (*f_gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf);
|
||||
extern f_gtk_image_set_from_pixbuf gtk_image_set_from_pixbuf;
|
||||
|
||||
typedef void (*f_gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal);
|
||||
extern f_gdk_window_set_modal_hint gdk_window_set_modal_hint;
|
||||
|
||||
@ -211,6 +226,14 @@ inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkFileChooser, Object>(obj, gtk_file_chooser_get_type());
|
||||
}
|
||||
|
||||
typedef GType (*f_gtk_image_get_type)(void) G_GNUC_CONST;
|
||||
extern f_gtk_image_get_type gtk_image_get_type;
|
||||
|
||||
template <typename Object>
|
||||
inline GtkImage *gtk_image_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkImage, Object>(obj, gtk_image_get_type());
|
||||
}
|
||||
|
||||
typedef GType (*f_gtk_button_get_type)(void) G_GNUC_CONST;
|
||||
extern f_gtk_button_get_type gtk_button_get_type;
|
||||
|
||||
@ -279,6 +302,9 @@ extern f_gdk_pixbuf_new_from_data gdk_pixbuf_new_from_data;
|
||||
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file)(const gchar *filename, GError **error);
|
||||
extern f_gdk_pixbuf_new_from_file gdk_pixbuf_new_from_file;
|
||||
|
||||
typedef GdkPixbuf* (*f_gdk_pixbuf_new_from_file_at_size)(const gchar *filename, int width, int height, GError **error);
|
||||
extern f_gdk_pixbuf_new_from_file_at_size gdk_pixbuf_new_from_file_at_size;
|
||||
|
||||
typedef GtkStatusIcon* (*f_gtk_status_icon_new_from_pixbuf)(GdkPixbuf *pixbuf);
|
||||
extern f_gtk_status_icon_new_from_pixbuf gtk_status_icon_new_from_pixbuf;
|
||||
|
||||
|
@ -378,7 +378,6 @@ bool MainWindow::psHasNativeNotifications() {
|
||||
}
|
||||
|
||||
void MainWindow::LibsLoaded() {
|
||||
auto cdesktop = Libs::CurrentDesktopStrings();
|
||||
noQtTrayIcon = !DesktopEnvironment::TryQtTrayIcon();
|
||||
tryAppIndicator = !DesktopEnvironment::PreferAppIndicatorTrayIcon();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user