Add support for GTK 4 (#3916)

* Make compact mode switch work for both GTK 3 and 4

* Implement GTK 4-specific view gesture handling

* Fix torrents view context menu on GTK 4

* Explicitly show/hide menubar on startup/teardown

* Switch from `Gtk::Pixbuf` to `Gio::Icon` for views

* Support GTK 4 exceptions based on `std::exception`

* Fix options menu setup with GTK 4

* Use `delete-event` (GTK 3) and `close-request` (GTK 4) signals to handle window clousure

* Add custom file chooser button implementation

GTK 4 drops FileChooserButton widget and suggests implementing it using
Button.

* Add helpers to set X11 hints with GTK 4

* Remove `HigWorkarea` class that's no longer used

* Make main menu shortcuts work with GTK 4

* Make drops work in main window and make dialog with GTK 4

* Remove unused `gtr_action_get_widget()` helper

* Fix text direction mark setup with GTK 4 (due to switch to enum class)

* Fix file tree font size calculation with GTK 4

* Fix crash during shutdown with GTK 4

* Switch from `RadioButton` to `CheckButton` for compatibility with GTK 4

* Fix opening files with GTK 4

* Rework torrent cell renderer to support both GTK 3 and 4

* Disable system tray icon support with GTK 4

* Fix windows positioning with GTK 4

* Fix focus event handling with GTK 4

* Adapt to tree model row/iterator changes in GTK 4

* Adapt to toplevel/root window changes in GTK 4

* Adapt to clipboard changes in GTK 4

* Adapt to icon/theme changes in GTK 4

* Adapt to file/path changes in GTK 4

* Random leftover fixes for GTK 4 compatibility

* Clean up unused code

* Move GTK 3 *.ui files into a subdirectory

* Add GTK 4 *.ui files

* Search for both GTK 3 and 4 during configuration
This commit is contained in:
Mike Gelfand
2022-10-08 15:50:03 -07:00
committed by GitHub
parent e3b871216f
commit c75c6bf5c8
63 changed files with 6329 additions and 849 deletions

View File

@@ -21,6 +21,7 @@
#include <libtransmission/makemeta.h>
#include <libtransmission/utils.h> /* tr_formatter_mem_B() */
#include "PathButton.h"
#include "MakeDialog.h"
#include "PrefsDialog.h"
#include "Session.h"
@@ -28,6 +29,11 @@
using namespace std::literals;
#if GTKMM_CHECK_VERSION(4, 0, 0)
using FileListValue = Glib::Value<GSList*>;
using FileListHandler = Glib::SListHandler<Glib::RefPtr<Gio::File>>;
#endif
namespace
{
@@ -81,10 +87,13 @@ public:
TR_DISABLE_COPY_MOVE(Impl)
private:
void onSourceToggled2(Gtk::ToggleButton* tb, Gtk::FileChooserButton* chooser);
void onChooserChosen(Gtk::FileChooserButton* chooser);
void onSourceToggled2(Gtk::CheckButton* tb, PathButton* chooser);
void onChooserChosen(PathButton* chooser);
void onResponse(int response);
#if GTKMM_CHECK_VERSION(4, 0, 0)
bool on_drag_data_received(Glib::ValueBase const& value, double x, double y);
#else
void on_drag_data_received(
Glib::RefPtr<Gdk::DragContext> const& drag_context,
int x,
@@ -92,6 +101,9 @@ private:
Gtk::SelectionData const& selection_data,
guint info,
guint time_);
#endif
bool set_dropped_source_path(std::string const& filename);
void updatePiecesLabel();
@@ -105,13 +117,13 @@ private:
MakeDialog& dialog_;
Glib::RefPtr<Session> const core_;
Gtk::RadioButton* file_radio_ = nullptr;
Gtk::FileChooserButton* file_chooser_ = nullptr;
Gtk::RadioButton* folder_radio_ = nullptr;
Gtk::FileChooserButton* folder_chooser_ = nullptr;
Gtk::CheckButton* file_radio_ = nullptr;
PathButton* file_chooser_ = nullptr;
Gtk::CheckButton* folder_radio_ = nullptr;
PathButton* folder_chooser_ = nullptr;
Gtk::Label* pieces_lb_ = nullptr;
Gtk::Scale* piece_size_scale_ = nullptr;
Gtk::FileChooserButton* destination_chooser_ = nullptr;
PathButton* destination_chooser_ = nullptr;
Gtk::CheckButton* comment_check_ = nullptr;
Gtk::Entry* comment_entry_ = nullptr;
Gtk::CheckButton* private_check_ = nullptr;
@@ -221,7 +233,7 @@ void MakeProgressDialog::onProgressDialogResponse(int response)
{
case TR_GTK_RESPONSE_TYPE(CANCEL):
builder_.cancelChecksums();
hide();
close();
break;
case TR_GTK_RESPONSE_TYPE(ACCEPT):
@@ -229,7 +241,7 @@ void MakeProgressDialog::onProgressDialogResponse(int response)
[[fallthrough]];
case TR_GTK_RESPONSE_TYPE(CLOSE):
hide();
close();
break;
default:
@@ -273,14 +285,15 @@ void MakeDialog::Impl::makeProgressDialog(std::string_view target, std::future<t
std::move(future),
target,
core_));
progress_dialog_->signal_hide().connect(
gtr_window_on_close(
*progress_dialog_,
[this]()
{
auto const success = progress_dialog_->success();
progress_dialog_.reset();
if (success)
{
dialog_.hide();
dialog_.close();
}
});
progress_dialog_->show();
@@ -322,7 +335,7 @@ void MakeDialog::Impl::onResponse(int response)
}
else if (response == TR_GTK_RESPONSE_TYPE(CLOSE))
{
dialog_.hide();
dialog_.close();
}
}
@@ -333,7 +346,7 @@ void MakeDialog::Impl::onResponse(int response)
namespace
{
void onSourceToggled(Gtk::ToggleButton* tb, Gtk::Widget* widget)
void onSourceToggled(Gtk::CheckButton* tb, Gtk::Widget* widget)
{
widget->set_sensitive(tb->get_active());
}
@@ -391,13 +404,13 @@ void MakeDialog::Impl::setFilename(std::string_view filename)
updatePiecesLabel();
}
void MakeDialog::Impl::onChooserChosen(Gtk::FileChooserButton* chooser)
void MakeDialog::Impl::onChooserChosen(PathButton* chooser)
{
chooser->set_data(FileChosenKey, GINT_TO_POINTER(true));
setFilename(chooser->get_filename());
}
void MakeDialog::Impl::onSourceToggled2(Gtk::ToggleButton* tb, Gtk::FileChooserButton* chooser)
void MakeDialog::Impl::onSourceToggled2(Gtk::CheckButton* tb, PathButton* chooser)
{
if (tb->get_active())
{
@@ -412,6 +425,47 @@ void MakeDialog::Impl::onSourceToggled2(Gtk::ToggleButton* tb, Gtk::FileChooserB
}
}
bool MakeDialog::Impl::set_dropped_source_path(std::string const& filename)
{
if (Glib::file_test(filename, TR_GLIB_FILE_TEST(IS_DIR)))
{
/* a directory was dragged onto the dialog... */
folder_radio_->set_active(true);
folder_chooser_->set_filename(filename);
return true;
}
if (Glib::file_test(filename, TR_GLIB_FILE_TEST(IS_REGULAR)))
{
/* a file was dragged on to the dialog... */
file_radio_->set_active(true);
file_chooser_->set_filename(filename);
return true;
}
return false;
}
#if GTKMM_CHECK_VERSION(4, 0, 0)
bool MakeDialog::Impl::on_drag_data_received(Glib::ValueBase const& value, double /*x*/, double /*y*/)
{
if (G_VALUE_HOLDS(value.gobj(), GDK_TYPE_FILE_LIST))
{
FileListValue files_value;
files_value.init(value.gobj());
if (auto const files = FileListHandler::slist_to_vector(files_value.get(), Glib::OwnershipType::OWNERSHIP_NONE);
!files.empty())
{
return set_dropped_source_path(files.front()->get_path());
}
}
return false;
}
#else
void MakeDialog::Impl::on_drag_data_received(
Glib::RefPtr<Gdk::DragContext> const& drag_context,
int /*x*/,
@@ -424,28 +478,14 @@ void MakeDialog::Impl::on_drag_data_received(
if (auto const uris = selection_data.get_uris(); !uris.empty())
{
auto const& uri = uris.front();
auto const filename = Glib::filename_from_uri(uri);
if (Glib::file_test(filename, TR_GLIB_FILE_TEST(IS_DIR)))
{
/* a directory was dragged onto the dialog... */
folder_radio_->set_active(true);
folder_chooser_->set_current_folder(filename);
success = true;
}
else if (Glib::file_test(filename, TR_GLIB_FILE_TEST(IS_REGULAR)))
{
/* a file was dragged on to the dialog... */
file_radio_->set_active(true);
file_chooser_->set_filename(filename);
success = true;
}
success = set_dropped_source_path(Glib::filename_from_uri(uris.front()));
}
drag_context->drag_finish(success, false, time_);
}
#endif
MakeDialog::MakeDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
@@ -468,13 +508,13 @@ std::unique_ptr<MakeDialog> MakeDialog::create(Gtk::Window& parent, Glib::RefPtr
MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core)
: dialog_(dialog)
, core_(core)
, file_radio_(gtr_get_widget<Gtk::RadioButton>(builder, "source_file_radio"))
, file_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "source_file_button"))
, folder_radio_(gtr_get_widget<Gtk::RadioButton>(builder, "source_folder_radio"))
, folder_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "source_folder_button"))
, file_radio_(gtr_get_widget<Gtk::CheckButton>(builder, "source_file_radio"))
, file_chooser_(gtr_get_widget_derived<PathButton>(builder, "source_file_button"))
, folder_radio_(gtr_get_widget<Gtk::CheckButton>(builder, "source_folder_radio"))
, folder_chooser_(gtr_get_widget_derived<PathButton>(builder, "source_folder_button"))
, pieces_lb_(gtr_get_widget<Gtk::Label>(builder, "source_size_label"))
, piece_size_scale_(gtr_get_widget<Gtk::Scale>(builder, "piece_size_scale"))
, destination_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "destination_button"))
, destination_chooser_(gtr_get_widget_derived<PathButton>(builder, "destination_button"))
, comment_check_(gtr_get_widget<Gtk::CheckButton>(builder, "comment_check"))
, comment_entry_(gtr_get_widget<Gtk::Entry>(builder, "comment_entry"))
, private_check_(gtr_get_widget<Gtk::CheckButton>(builder, "private_check"))
@@ -484,7 +524,7 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& bui
{
dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::onResponse));
destination_chooser_->set_current_folder(Glib::get_user_special_dir(TR_GLIB_USER_DIRECTORY(DESKTOP)));
destination_chooser_->set_filename(Glib::get_user_special_dir(TR_GLIB_USER_DIRECTORY(DESKTOP)));
folder_radio_->set_active(false);
folder_radio_->signal_toggled().connect([this]() { onSourceToggled2(folder_radio_, folder_chooser_); });
@@ -510,9 +550,15 @@ MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& bui
source_entry_->set_sensitive(false);
source_check_->signal_toggled().connect([this]() { onSourceToggled(source_check_, source_entry_); });
#if GTKMM_CHECK_VERSION(4, 0, 0)
auto drop_controller = Gtk::DropTarget::create(GDK_TYPE_FILE_LIST, Gdk::DragAction::COPY);
drop_controller->signal_drop().connect(sigc::mem_fun(*this, &Impl::on_drag_data_received), false);
dialog_.add_controller(drop_controller);
#else
dialog_.drag_dest_set(Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY);
dialog_.drag_dest_add_uri_targets();
dialog_.signal_drag_data_received().connect(sigc::mem_fun(*this, &Impl::on_drag_data_received));
#endif
}
void MakeDialog::Impl::onPieceSizeUpdated()