### Meson Build Setup and Installation Commands Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/meson.md Commands to set up the build directory and install the application using Meson. Note that on Linux, modifying the default installation directory might require root privileges. ```bash meson setup builddir meson install -C builddir ``` -------------------------------- ### Run Specific Example: hello_world_3 Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/listings/README.md Example command to run the 'hello_world_3' listing. ```bash cargo run --bin hello_world_3 ``` -------------------------------- ### Run Specific Listing Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/README.md Executes a specific example binary from the 'listings' directory. ```bash cargo run --bin listing_name ``` -------------------------------- ### Main Application Setup with Custom Buttons Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/accessibility.md Set up the main GTK application window and load custom CSS. This example demonstrates adding two custom buttons to a vertical box. ```rust fn main() { // Initialize the GTK application. let app = Application::builder().application_id("org.gtk_rs.example").build(); // Connect to the activate signal to build the UI. app.connect_activate(build_ui); app.run(); } fn build_ui(app: &Application) { // Load the custom CSS file. let provider = CssProvider::new(); provider.load_from_data( r#"*:focus { outline-color: #5294e2; outline-style: solid; outline-width: 1px; outline-offset: 1px; }"#, ); // Add the CSS provider to the default screen. if let Some(display) = gdk::Display::default() { StyleContext::add_provider_for_display( &display, &provider, STYLE_PROVIDER_PRIORITY_APPLICATION, ); } // Create two custom buttons. let button1 = CustomButton::new("Button 1"); let button2 = CustomButton::new("Button 2"); // Create a vertical box to hold the buttons. let box = Box::builder() .orientation(Orientation::Vertical) .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .spacing(6) .build(); // Add the buttons to the box. box.append(&button1); box.append(&button2); // Create the main window. let window = ApplicationWindow::builder() .application(app) .title("Custom Buttons") .child(&box) .build(); // Present the window. window.present(); } ``` -------------------------------- ### Run a GTK 4 Rust Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/examples/README.md Instructions on how to run any example from the command line using Cargo. Change the directory to the example's folder and execute the command. ```bash cargo run --bin [example_name] ``` ```bash cargo run --bin basics ``` -------------------------------- ### Build and Install Application Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/i18n.md Commands to set up the build directory, configure the project with development profile and a specific prefix, and then install the application. ```bash meson setup builddir -Dprofile=development --prefix=~/.local meson install -C builddir ``` -------------------------------- ### Build Examples Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/README.md Compiles the code examples in the 'listings' directory to check for build errors. ```bash cargo build ``` -------------------------------- ### Run ListBox Model Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/examples/list_box_model/README.md Execute the example application using Cargo. ```bash cargo run --bin list_box_model ``` -------------------------------- ### Run ListBox Sort StringList Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/examples/list_box_sort_stringlist/README.md Execute this command in your terminal to run the example. Ensure you have the `v4_14` feature enabled. ```bash cargo run --bin list_box_sort_stringlist --features v4_14 ``` -------------------------------- ### Install Application Data Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/listings/README.md Run this command in the current working directory to install application data. ```bash cargo xtask install ``` -------------------------------- ### Example Console Output Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/main_event_loop.md This is an example of the console output expected after pressing a button in a GTK application. ```text Status: 200 OK ``` -------------------------------- ### Install mdbook Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/README.md Installs the mdbook tool, which is used for building the documentation. ```bash cargo install mdbook ``` -------------------------------- ### Setup SignalListItemFactory Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/list_widgets.md Configures the SignalListItemFactory to create widgets. The 'setup' signal is emitted when new widgets are needed, and a Label is created for each. ```rust let factory = SignalListItemFactory::new(); factory.connect_setup(|_, list_item| { // It has to be `list_item.child()` that is cast to `Label` let label = Label::new(None); list_item.set_child(Some(&label)); }); ``` -------------------------------- ### Setup Factory with Expressions Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/list_widgets.md Use expressions to bind properties, simplifying the setup process by removing the explicit 'bind' step. This allows for dynamic updates as list items change. ```rust let factory = SignalListItemFactory::new(); factory.connect_setup(|_, list_item| { let label = Label::new(None); list_item .data() .set("label", label.clone()) .unwrap(); // The expression below binds the 'number' property of the item // to the 'label' property of the label widget. list_item .item() .expression( "item.number", &Expression::property("label", Some(&label), None), ) .unwrap(); }); ``` -------------------------------- ### Icon Installation Configuration Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/meson.md Installs application icons, including a development version, into the appropriate system directories. This ensures the application has a visual representation in the desktop environment. ```meson install_data('org.gtk_rs.Todo9.svg', install_dir: get_option('datadir') / 'icons' / 'hicolor' / 'scalable' / 'apps') if host_machine.system() == 'linux' install_data('org.gtk_rs.Todo9.Devel.svg', install_dir: get_option('datadir') / 'icons' / 'hicolor' / 'scalable' / 'apps') endif ``` -------------------------------- ### Set up GTK UI and Run Main Event Loop Source: https://github.com/gtk-rs/gtk4-rs/blob/main/gtk4/README.md Sets up a GTK application with a window and a button, then runs the main event loop. This example shows how to create widgets, connect signal handlers, and display the UI. ```rust use gtk4 as gtk; use gtk::prelude::*; use gtk::{glib, Application, ApplicationWindow, Button}; fn main() -> glib::ExitCode { let application = Application::builder() .application_id("com.example.FirstGtkApp") .build(); application.connect_activate(|app| { let window = ApplicationWindow::builder() .application(app) .title("First GTK Program") .default_width(350) .default_height(70) .build(); let button = Button::with_label("Click me!"); button.connect_clicked(|_| { eprintln!("Clicked!"); }); window.set_child(Some(&button)); window.present(); }); application.run() } ``` -------------------------------- ### Install Meson and Ninja with pip Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_windows.md Installs the Meson build system and Ninja build tool using pip. These are required for building GTK 4 manually. ```powershell pip install meson ninja ``` -------------------------------- ### Hello World App using Builder Pattern Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/composite_templates.md This is an example of constructing pre-defined widgets using the builder pattern in GTK-RS. ```rust use gtk::prelude::*;function main() { let app = gtk::Application::builder().application_id("com.example.hello_world").build(); app.connect_activate(|app| { let window = gtk::ApplicationWindow::builder() .application(app) .title("Hello World") .default_width(300) .default_height(100) .build(); let button = gtk::Button::with_label("Click me!"); button.connect_clicked(|_| { println!("Button clicked!"); }); window.set_child(Some(&button)); window.present(); }); app.run(); } ``` -------------------------------- ### Run Code Listings Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/listings/README.md Execute this command to run a specific code listing, replacing '[example_name]_[number]' with the desired example. ```bash cargo run --bin [example_name]_[number] ``` -------------------------------- ### Initialize GTK and Run "Hello, World!" Application Source: https://github.com/gtk-rs/gtk4-rs/blob/main/gtk4/README.md Initializes the GTK application and creates a main window. GTK must be initialized before use, which is handled by creating an Application. This snippet demonstrates basic application setup and window presentation. ```rust use gtk4 as gtk; use gtk::prelude::*; use gtk::{glib, Application, ApplicationWindow}; fn main() -> glib::ExitCode { let app = Application::builder() .application_id("org.example.HelloWorld") .build(); app.connect_activate(|app| { // We create the main window. let window = ApplicationWindow::builder() .application(app) .default_width(320) .default_height(200) .title("Hello, World!") .build(); // Show the window. window.present(); }); app.run() } ``` -------------------------------- ### StringList Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/list_widgets.md Demonstrates how to use `gtk::StringList` for displaying a list of strings. It shows how to add strings to the model and connect them to UI elements using expressions. ```rust let string_list = StringList::new(); string_list.append("Hello"); string_list.append("World"); string_list.append("!"); ``` -------------------------------- ### Install GTK 4 on Fedora Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_linux.md Installs GTK 4 development files, Libadwaita, Meson, and essential build tools on Fedora and its derivatives. ```bash sudo dnf install gtk4-devel libadwaita-devel meson desktop-file-utils gcc glib-compile-resources gtk4-update-icon-cache update-desktop-database ``` -------------------------------- ### Compile and Install GTK 4 Dependencies Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_windows.md Compiles and installs GTK 4 and its dependencies (libxml2, librsvg) using MSVC. This involves cloning repositories, configuring with Meson and CMake, and using `nmake`. ```cmd cd /\ngit clone https://gitlab.gnome.org/GNOME/gtk.git --depth 1\ngit clone https://gitlab.gnome.org/GNOME/libxml2.git --depth 1\ngit clone https://gitlab.gnome.org/GNOME/librsvg.git --depth 1 :: Make sure that cmd finds pkg-config-lite when searching for pkg-config where pkg-config :: Make sure that setuptools is available. pip install setuptools cd gtk meson setup builddir --prefix=C:/gnome -Dbuild-tests=false -Dmedia-gstreamer=disabled meson install -C builddir cd / cd libxml2 cmake -S . -B build -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=C:\gnome -D LIBXML2_WITH_ICONV=OFF -D LIBXML2_WITH_LZMA=OFF -D LIBXML2_WITH_PYTHON=OFF -D LIBXML2_WITH_ZLIB=OFF cmake --build build --config Release cmake --install build cd / cd librsvg/win32 nmake /f generate-msvc.mak generate-nmake-files nmake /f Makefile.vc CFG=release install PREFIX=C:\gnome cd / ``` -------------------------------- ### Install GTK 4, Libadwaita, and Meson on macOS Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_macos.md Execute this command in your terminal to install the required GTK 4, Libadwaita, and Meson build system dependencies using Homebrew. ```bash brew install gtk4 libadwaita meson desktop-file-utils ``` -------------------------------- ### Setup Collections List Store Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_4.md Initializes the `collections` list store and connects a handler to reflect changes in the UI. This ensures that modifications to the collections are saved and visually updated. ```rust fn setup_collections(&self, obj: &Self::Type) { self.collections.connect_notify_local( "-item-changed", glib::clone!(@weak obj => move |_, _| { obj.save_collections(); glib::Propagation::Stop }), ); } ``` -------------------------------- ### Main Meson Build Configuration Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/meson.md Configures GResource compilation and Linux-specific installations for a GTK application. Installs desktop files, GSettings schemas, and icons only on Linux systems. ```meson project('todo', 'c', 'cpp', 'rust', version: '0.1.0', default_options: ['warning_level=2', 'rust_link_rlib=true']) if host_machine.system() == 'linux' desktop_file = configuration_data() desktop_file.set('APP_ID', 'org.gtk_rs.Todo9') configure_file(input: '../org.gtk_rs.Todo9.desktop.in', output: configure_user_paths(get_option('datadir') / 'applications' / '@OUTPUT@'), configuration: desktop_file) service_file = configuration_data() service_file.set('APP_ID', 'org.gtk_rs.Todo9') configure_file(input: '../org.gtk_rs.Todo9.service.in', output: configure_user_paths(get_option('datadir') / 'dbus-1' / 'services' / '@OUTPUT@'), configuration: service_file) schema_file = configuration_data() schema_file.set('APP_ID', 'org.gtk_rs.Todo9') configure_file(input: '../org.gtk_rs.Todo9.gschema.xml.in', output: configure_user_paths(get_option('datadir') / 'glib-2.0' / 'schemas' / '@OUTPUT@'), configuration: schema_file) endif subdir('icons') subdir('resources') meson.add_install_script('meson_post_install.py') ``` -------------------------------- ### Create and Use a glib::Variant for i32 Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/g_object_values.md Demonstrates converting an `i32` to a `glib::Variant` and back. This is a basic example of `Variant` usage for serialization. ```rust let variant = glib::Variant::from(10i32); let retrieved_value = variant.get::().expect("Failed to get i32 from Variant"); assert_eq!(retrieved_value, 10); ``` -------------------------------- ### Install GTK 4 and Dependencies using MSYS2 Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_windows.md Execute this command in the MSYS2 MinGW 64-bit terminal to install GTK 4, gettext, libxml2, librsvg, pkgconf, and gcc. ```sh pacman -S mingw-w64-x86_64-gtk4 mingw-w64-x86_64-gettext mingw-w64-x86_64-libxml2 mingw-w64-x86_64-librsvg mingw-w64-x86_64-pkgconf mingw-w64-x86_64-gcc ``` -------------------------------- ### Setup Shortcuts for Todo App Actions Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_2.md Binds keyboard shortcuts to application actions using set_accels_for_action. Requires a gtk::Application instance. ```rust pub fn setup_shortcuts(app: >k::Application) { app.set_accels_for_action("win.filter", &["f", "f"]); app.set_accels_for_action("win.close", &["w"]); } ``` -------------------------------- ### Install and Set Rust GNU Toolchain Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_windows.md Install the stable GNU toolchain for Rust and set it as the default. This command may change in the future; refer to the project's issue tracker if it fails. ```sh rustup toolchain install stable-gnu ``` ```sh rustup default stable-gnu ``` -------------------------------- ### Install Async Action for New Collection Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_4.md Installs an asynchronous action named 'new-collection' to a widget's class. This is typically used to trigger dialogs or other UI elements when a button or menu item is activated. ```rust object_subclass! { // ... fn class_init(klass: &mut Self::Klass) { // ... klass.install_action_async("new-collection", |widget, _args| async { // ... Self::new_collection(widget).await; // ... }); // ... } // ... } ``` -------------------------------- ### Desktop File Template Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/i18n.md This is an example of a desktop file template used for internationalization. Translatable keys are prefixed with an underscore. ```ini {{#include ../listings/todo/10/data/org.gtk_rs.Todo10.desktop.in.in}} ``` -------------------------------- ### Setup Tasks Model with Filtering Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_2.md Initializes the filter_model with the current filter state and updates it whenever the 'filter' setting changes. ```rust let filter_model = gtk::FilterListModel::new(Some(&tasks), Some(&filter_model)); settings .connect_changed(Some("filter"), move |_, _| { filter_model.set_filter(Some(&filter(settings))); }); window.set_filter_model(filter_model); ``` -------------------------------- ### Manually Build libadwaita with MSVC Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/libadwaita.md Manually build libadwaita using MSVC on Windows. This involves cloning the repository, setting up the build directory with Meson, and installing the library. ```bash cd / git clone --branch libadwaita-1-3 https://gitlab.gnome.org/GNOME/libadwaita.git --depth 1 cd libadwaita meson setup builddir -Dprefix=C:/gnome -Dintrospection=disabled -Dvapi=false meson install -C builddir ``` -------------------------------- ### StringList Factory Setup Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/list_widgets.md Sets up a factory for `StringList` to connect labels to the string content. It utilizes `StringObject` and expressions to bind the 'string' property of the object to the label's text. ```rust let factory = SignalListItemFactory::new(); factory.connect_setup(|_, list_item| { let label = Label::new(None); list_item .data() .set("label", label.clone()) .unwrap(); // Bind the 'string' property of the StringObject to the label's text. list_item .item() .expression( "item.string", &Expression::property("label", Some(&label), None), ) .unwrap(); }); ``` -------------------------------- ### Add ApplicationWindow to GTK Application Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/hello_world.md Adds a `gtk::ApplicationWindow` to the application and connects it to the 'activate' signal, ensuring the window is displayed when the application starts. ```rust use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; fn main() { let app = Application::builder().application_id("org.gtk_rs.example").build(); // Connect to the activate signal of the application app.connect_activate(|app| { // Use `ApplicationWindow::builder` to create a new window let window = ApplicationWindow::builder() .application(app) .default_width(300) .default_height(200) .title("My GTK App") .build(); // Present the window window.present(); }); app.run(); } ``` -------------------------------- ### Example Serialized Task Data (JSON) Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_2.md Illustrates the JSON format for a list of tasks, showing how 'completed' and 'content' fields are represented. This format is generated by Serde_JSON. ```json [ { "completed": true, "content": "Task Number Two" }, { "completed": false, "content": "Task Number Five" }, { "completed": true, "content": "Task Number Six" }, { "completed": false, "content": "Task Number Seven" }, { "completed": false, "content": "Task Number Eight" } ] ``` -------------------------------- ### Translation File (.po) Example Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/i18n.md A sample translation file in the .po format. Each entry contains a comment indicating the source location, the original string (msgid), and the translated string (msgstr). ```po #: data/resources/window.ui:134 msgid "Main Menu" msgstr "Hauptmenü" #: data/resources/window.ui:153 msgid "Enter a Task…" msgstr "Aufgabe eingeben…" ``` -------------------------------- ### Setup Callbacks for Collection and Filter Changes Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_4.md Sets up various callbacks to manage the application's state based on user interactions and data changes. This includes filtering the task model when settings change, updating the stack when collection items change, and handling the selection of collections. ```rust fn setup_callbacks(&self) { // ... self.filter_vbox.connect_property_notify( "filter", glib::clone::clone(&self.filter_vbox, move |filter_vbox| { // ... filter_vbox.filter_tasks(); // ... }), ); self.collections_list.connect_items_changed( glib::clone::clone(&self.collections_list, move |collections_list| { // ... self.set_stack(); // ... }), ); self.collections_list.connect_selected_rows_changed( glib::clone::clone(&self.collections_list, move |collections_list| { // ... self.current_collection = collections_list.selected_item().cloned(); // ... }), ); // ... } ``` -------------------------------- ### Create and Use a glib::Value for i32 Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/g_object_values.md Demonstrates creating a `glib::Value` to hold an `i32` and retrieving its value. Ensure the correct type is used when getting the value. ```rust let value = glib::Value::from_i32(10); let retrieved_value = value.get::().expect("Failed to get i32 value"); assert_eq!(retrieved_value, 10); ``` -------------------------------- ### Run Async Functions from External Crates (ashpd) Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/main_event_loop.md Example demonstrating how to use the `ashpd` crate to request user information asynchronously within a GTK4 application. Requires a Linux desktop environment. ```rust async fn callback() { // ... let native = button.native().unwrap(); let identifier = ashpd::WindowIdentifier::from(native); let user_info = ashpd::desktop::account::UserInformation::new(identifier) .request() // This returns a future .await; // ... } ``` -------------------------------- ### D-Bus Service File Configuration Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/meson.md Enables D-Bus activation for the application, allowing GNOME to start the app via a D-Bus message. The `--gapplication-service` flag ensures GApplication runs as a D-Bus service. ```ini [D-BUS Service] Name=org.gtk_rs.Todo9 Exec=todo --gapplication-service ``` -------------------------------- ### Window setup_callbacks method Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_1.md Connects to the "activate" signal of the entry to handle new task creation when the Enter key is pressed. ```rust use gtk::glib; use gtk::prelude::*; use gtk::ApplicationWindow; use crate::task_object::TaskObject; glib::wrapper! { pub struct Window(ObjectSubclass) @extends ApplicationWindow; } impl Window { pub fn new(app: >k::Application) -> Self { let window: Self = gtk::ApplicationWindow::builder() .application(app) .title("GTK Todo") .default_width(350) .default_height(700) .build(); window } pub fn tasks(&self) -> gtk::gio::ListStore { self.imp().tasks.clone() } pub fn setup_tasks(&self) { let tasks = gtk::gio::ListStore::new(); self.imp().tasks.set(tasks.clone()); let list_view = gtk::ListView::builder().model(&tasks).build(); self.set_child(Some(&list_view)); } // Connect to the "activate" signal of the entry pub fn setup_callbacks(&self) { let entry = self.imp().entry.get(); entry.connect_activate(move |entry| { let buffer = entry.buffer(); let content = buffer.text().to_string(); buffer.set_text(""); let task = TaskObject::new(content); self.tasks().append(&task); }); } pub fn setup_factory(&self) { let factory = gtk::SignalListItemFactory::new(); factory.connect_setup(move |_, list_item| { let task_row = TaskRow::new(); list_item.set_child(Some(&task_row.upcast::())).unwrap(); }); factory.connect_bind(move |_, list_item| { let task = list_item.item().unwrap().downcast::().unwrap(); let task_row = list_item.child().unwrap().downcast::().unwrap(); task_row.bind(&task); }); factory.connect_unbind(move |_, list_item| { let task_row = list_item.child().unwrap().downcast::().unwrap(); task_row.unbind(); }); let list_view = self.imp().list_view.get(); list_view.set_factory(Some(&factory)); } } impl ApplicationWindowImpl for WindowImpl {} ``` -------------------------------- ### Setup Filter and Remove Done Tasks Actions Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_2.md This Rust code sets up the 'filter' action using settings and installs the 'remove-done-tasks' action using `install_action`. The 'filter' action is stateful and linked to the settings, while 'remove-done-tasks' is a simpler action for removing completed tasks. ```rust pub fn setup_actions(window: &Window) { let action_filter = gtk::gio::SimpleAction::new_stateful("filter", None, &glib::Variant::new("All")); action_filter.connect_activate(glib::clone!(@weak window => move |action, parameter| { let filter = parameter.unwrap().get_string().unwrap(); window.set_filter(filter); action.set_state(&glib::Variant::new(&filter)); })); window.add_action(&action_filter); let action_remove_done = gtk::gio::SimpleAction::new("remove-done-tasks", None); action_remove_done.connect_activate(glib::clone!(@weak window => move |_action, _parameter| { window.remove_done_tasks(); })); window.add_action(&action_remove_done); } ``` -------------------------------- ### Custom GTK Window with State Management Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/saving_window_state.md Defines a custom window that includes convenience methods for getting and setting window state (height, width, maximized status) using gio::Settings. It also handles the initialization of settings. ```rust use gtk::glib; use gtk::prelude::*; use gio::Settings; use std::cell::OnceCell; mod imp { use super::*; use gtk::subclass::window::WindowImpl; use glib::{ParamFlags, Value}; use gtk::glib::ParamSpec; #[derive(Default)] pub struct CustomWindow { pub settings: OnceCell, } #[glib::object_subclass] impl ObjectImpl for CustomWindow { fn constructed(&self, obj: &Self::Type) { let obj = obj.downcast_ref::().unwrap(); // Initialize settings let app_id = obj.application_id().or_else(|| { obj.name() }).expect("application id or name is needed"); let settings = Settings::new(app_id); obj.imp.settings.set(settings).unwrap(); // Load window state let settings = obj.settings(); let height = settings.get_int("height"); let width = settings.get_int("width"); let is_maximized = settings.get_bool("is-maximized"); obj.set_default_size(width, height); if is_maximized { obj.maximize(); } } fn close_request(&self, obj: &Self::Type) -> glib::Propagation { let obj = obj.downcast_ref::().unwrap(); // Save window state let settings = obj.settings(); let (width, height) = obj.default_size(); let is_maximized = obj.is_maximized(); settings.set_int("width", width as i32); settings.set_int("height", height as i32); settings.set_bool("is-maximized", is_maximized); // Allow the window to close glib::Propagation::Proceed } } impl WindowImpl for CustomWindow {} } glib::wrapper! { pub struct CustomWindow(ObjectSubclass) @extends gtk::Window; } impl CustomWindow { pub fn new>(_application_id: S) -> Self { gtk::Window::new() } fn settings(&self) -> &Settings { self.imp.settings.get_or_init(|| { let app_id = self.application_id().or_else(|| { self.name() }).expect("application id or name is needed"); Settings::new(app_id) }) } } ``` -------------------------------- ### Main Application Structure with Actions - Rust Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/actions.md Sets up the main application window and calls `build_ui` to add actions. It also demonstrates setting keyboard accelerators for actions using `set_accels_for_action`. ```rust fn main() { let app = Application::builder() .application_id("com.example.Gtk-rs") .build(); app.connect_activate(|app| { let window = ApplicationWindow::builder() .application(app) .title("Actions") .default_width(350) .build(); build_ui(&window); window.set_accels_for_action("close", &["W"]); window.present(); }); app.run(); } ``` -------------------------------- ### Window setup_factory method Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_1.md Sets up the SignalListItemFactory for the gtk::ListView, handling the creation, binding, and unbinding of TaskRow widgets. ```rust use gtk::glib; use gtk::prelude::*; use gtk::ApplicationWindow; use crate::task_object::TaskObject; glib::wrapper! { pub struct Window(ObjectSubclass) @extends ApplicationWindow; } impl Window { pub fn new(app: >k::Application) -> Self { let window: Self = gtk::ApplicationWindow::builder() .application(app) .title("GTK Todo") .default_width(350) .default_height(700) .build(); window } pub fn tasks(&self) -> gtk::gio::ListStore { self.imp().tasks.clone() } pub fn setup_tasks(&self) { let tasks = gtk::gio::ListStore::new(); self.imp().tasks.set(tasks.clone()); let list_view = gtk::ListView::builder().model(&tasks).build(); self.set_child(Some(&list_view)); } pub fn setup_callbacks(&self) { let entry = self.imp().entry.get(); entry.connect_activate(move |entry| { let buffer = entry.buffer(); let content = buffer.text().to_string(); buffer.set_text(""); let task = TaskObject::new(content); self.tasks().append(&task); }); } // Set up the SignalListItemFactory pub fn setup_factory(&self) { let factory = gtk::SignalListItemFactory::new(); // Called when the list item is created factory.connect_setup(move |_, list_item| { let task_row = TaskRow::new(); list_item.set_child(Some(&task_row.upcast::())).unwrap(); }); // Called when the list item is bound to a task object factory.connect_bind(move |_, list_item| { let task = list_item.item().unwrap().downcast::().unwrap(); let task_row = list_item.child().unwrap().downcast::().unwrap(); task_row.bind(&task); }); // Called when the list item is unbound from a task object factory.connect_unbind(move |_, list_item| { let task_row = list_item.child().unwrap().downcast::().unwrap(); task_row.unbind(); }); let list_view = self.imp().list_view.get(); list_view.set_factory(Some(&factory)); } } impl ApplicationWindowImpl for WindowImpl {} ``` -------------------------------- ### Install GTK 4 on Arch Linux Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_linux.md Installs GTK 4, Libadwaita, Meson, and essential build tools on Arch Linux and its derivatives. ```bash sudo pacman -S gtk4 libadwaita meson desktop-file-utils gcc ``` -------------------------------- ### Main Application Entry Point Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_2.md The main function sets up the GTK application, loads resources, and initializes the main window and shortcuts. ```rust use gtk::prelude::*; mod window; fn main() { let app = gtk::Application::builder() .application_id("org.gtk_rs.Todo") .resource_base_path("/org/gtk_rs/gtk4_rs/todo") .build(); app.connect_activate(window::win::activate); app.run(); } ``` -------------------------------- ### Install GTK 4 on Debian Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/installation_linux.md Installs GTK 4 development files, Libadwaita, Meson, and essential build tools on Debian and its derivatives. ```bash sudo apt install libgtk-4-dev libadwaita-1-dev meson desktop-file-utils gcc gtk-update-icon-cache ``` -------------------------------- ### Serve GTK-Rust Book Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/README.md Builds and serves the GTK-Rust book locally for viewing. ```bash mdbook serve ``` -------------------------------- ### Initialize Application with Libadwaita Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_3.md Replace `gtk::Application` with `adw::Application` to automatically set up translations, types, stylesheets, and icons for Libadwaita. This also handles loading stylesheets from resources. ```rust use adw::prelude::*; use adw::Application; use gtk::glib; // Your application code fn main() -> glib::ExitCode { let app = Application::builder().application_id("com.example.Todo").build(); app.connect_activate(|app| { let window = adw::ApplicationWindow::builder() .application(app) .default_width(400) .default_height(300) .build(); // Your window code window.present(); }); app.run() } ``` -------------------------------- ### Setup Labeled Entry and Hidden Error Label Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/accessibility.md Sets up a GTK labeled entry widget and a hidden label for displaying error messages. This is part of the UI setup for form validation. ```rust let email_entry = gtk::Entry::with_label("Email:"); let error_label = gtk::Label::builder().label("Please enter a valid email address").css_classes(vec!["error"]).visible(false).build(); ``` -------------------------------- ### Main Application Entry Point Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_1.md The main function to set up and run the GTK application, including loading resources. ```rust use gtk::glib; mod window; use window::Window; const APP_ID: &str = "org.gtk_rs.Todo1"; fn main() -> glib::ExitCode { let app = gtk::Application::builder().application_id(APP_ID).build(); app.connect_activate(build_ui); app.run() } fn build_ui(app: >k::Application) { let window = Window::new(app); window.present(); } ``` -------------------------------- ### Create a New Window with Libadwaita Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_3.md Demonstrates creating a new application window using `adw::ApplicationWindow`. This window will automatically adopt the styling and theming provided by Libadwaita. ```rust pub fn new(app: &Application) -> ApplicationWindow { let window = ApplicationWindow::builder() .application(app) .default_width(400) .default_height(300) .build(); // Set the header bar let header_bar = HeaderBar::new(); window.set_titlebar(Some(&header_bar)); // Add widgets to the window // ... window } ``` -------------------------------- ### Desktop File Configuration Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/meson.md Defines how the application is displayed and launched by the system. Uses placeholders like `@APP_ID@` for dynamic substitution during the build process. ```ini [Desktop Entry] Name=Todo Comment=A simple todo list application Exec=todo Icon=org.gtk_rs.Todo9 Terminal=false Type=Application Categories=GTK;GNOME;Utility; Keywords=todo; StartupNotify=true DBusActivatable=true ``` -------------------------------- ### Spawn Reqwest GET Request in Tokio Runtime Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/main_event_loop.md Spawns an asynchronous `reqwest` GET request within the Tokio runtime when a GTK button is clicked. The response is sent back via a channel. ```rust # use std::sync::OnceLock; # # use glib::clone; # use gtk::glib; # use gtk::prelude::*; # use gtk::{Application, ApplicationWindow, Button}; # use tokio::runtime::Runtime; # # const APP_ID: &str = "org.gtk_rs.MainEventLoop0"; # static RUNTIME: OnceLock = OnceLock::new(); fn runtime() -> &'static Runtime { RUNTIME.get_or_init(|| { Runtime::new().expect("Setting up tokio runtime needs to succeed.") }) } # fn build_ui(app: &Application) { // Create a button let button = Button::builder() .label("Press me!") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); // ANCHOR: callback let (sender, receiver) = async_channel::bounded(1); // Connect to "clicked" signal of `button` button.connect_clicked(move |_| { runtime().spawn(clone!(#[strong] sender, async move { let response = reqwest::get("https://www.gtk-rs.org").await; sender.send(response).await.expect("The channel needs to be open."); })); }); // The main loop executes the asynchronous block glib::spawn_future_local(async move { while let Ok(response) = receiver.recv().await { if let Ok(response) = response { println!("Status: {}", response.status()); } else { println!("Could not make a `GET` request."); } } }); // ANCHOR_END: callback // Create a window let window = ApplicationWindow::builder() .application(app) .title("My GTK App") .child(&button) .build(); // Present window window.present(); } # # fn main() -> glib::ExitCode { # // Create a new application # let app = Application::builder().application_id(APP_ID).build(); # # // Connect to "activate" signal of `app` # app.connect_activate(build_ui); # # // Run the application # app.run() # } ``` -------------------------------- ### Initialize Application and Settings Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/settings.md Initializes the GTK application and creates a gio::Settings object using the application ID. This object will be used to manage application settings. ```rust use gio::Settings; use gtk::Application; fn main() { let app = Application::builder() .application_id("org.gtk_rs.Settings1") .build(); let settings = Settings::new("org.gtk_rs.Settings1"); app.connect_activate(move |app| { // ... create window and widgets ... }); app.run(); } ``` -------------------------------- ### Implementing Action Connections in Rust Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/actions.md Implement the `setup_actions` method to connect actions to widgets. This method is typically called within the `constructed` lifecycle method. ```rust fn setup_actions(&self) { let action = self.imp.action.get(); let action = action.get().expect("Action not found"); let button = self.imp.button.get(); button.set_action_name(Some("app.action_name")); button.set_action_target(Some(&42i32)); self.add_action(&action); } ``` -------------------------------- ### Check GTK 4 Version Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/project_setup.md Determine the installed GTK 4 version on your system using pkg-config. ```bash pkg-config --modversion gtk4 ``` -------------------------------- ### Test Application with Specific Language Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/i18n.md How to test your installed application with a specific language by setting the LANGUAGE environment variable. ```bash LANGUAGE=de todo-10 ``` -------------------------------- ### Create Stateful Actions from Settings Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/actions.md Use `SettingsExt::create_action` to create stateful actions directly from schema keys and add them to the action group. ```rust let actions = &self.imp().settings().actions(); let action_group = self.imp().action_group(); action_group.add_action("button-frame", actions["button-frame"].clone()); action_group.add_action("orientation", actions["orientation"].clone()); ``` -------------------------------- ### Assemble Widgets and Present Window Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/accessibility.md Assembles the created widgets (entry and error label) into a window and presents it to the user. This is the final step in setting up the UI. ```rust let vbox = gtk::Box::builder().orientation(gtk::Orientation::Vertical).margin_top(12).margin_start(12).margin_end(12).margin_bottom(12).spacing(6).build(); vbox.append(&email_entry); vbox.append(&error_label); let window = gtk::ApplicationWindow::builder() .title("Form Validation") .default_width(300) .child(&vbox) .build(); app.add_window(&window); window.present(); ``` -------------------------------- ### Integrate Custom Button into GTK Application Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/g_object_subclassing.md Replace the standard `gtk::Button` with the custom `CustomButton` in the application's UI setup. ```rust use gtk::prelude::*; mod custom_button; fn build_ui(app: >k::Application) { let button = gtk::Button::with_label("Click Me!"); button.connect_clicked(move |_| { println!("Clicked!"); }); let window = gtk::ApplicationWindow::builder() .application(app) .title("Hello World") .child(&button) .build(); window.present(); } ``` -------------------------------- ### Initialize Settings in Constructed Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/actions.md Ensure that `gio::Settings` are initialized and settings are bound to actions within the `constructed` method of the window. ```rust fn constructed(&self) { let settings = Settings::new("org.gtk_rs.Actions7").unwrap(); self.imp().settings.set(settings).unwrap(); let action_group = self.imp().action_group(); self.imp().bind_settings(&action_group); // ... other setup } ``` -------------------------------- ### Rust: Object Subclass for AdwApplicationWindow Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/todo_4.md Defines the object subclass for the window, inheriting from `adw::ApplicationWindow`. This is a boilerplate setup for custom widgets in GTK. ```rust object_subclass! { // The main window of the application pub struct Window; impl ObjectSubclass for Window { const NAME: &'static str = "TodoWindow"; type Type = Window; type ParentType = adw::ApplicationWindow; fn class_init(klass: &mut Self::Class) { // For this example we don't need to do anything in class_init // or super::class_init. } } } ``` -------------------------------- ### Create and Navigate to New GTK App Directory Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/project_setup.md Use Cargo to create a new Rust project and then change the current directory into the newly created project folder. ```bash cargo new my-gtk-app cd my-gtk-app ``` -------------------------------- ### Removing Cargo Dependencies Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/main_event_loop.md Command to remove unused dependencies from a Cargo project. This is useful for cleaning up the project after initial setup or when dependencies are no longer needed. ```bash cargo remove tokio reqwest ashpd ``` -------------------------------- ### Collapsible Section Container Setup Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/src/accessibility.md Set up the initial container for a collapsible section. This involves creating a vertical box to hold the toggle button and the content. ```rust let container = Box::builder() .orientation(Orientation::Vertical) .spacing(6) .build(); ``` -------------------------------- ### Navigate to Listings Directory Source: https://github.com/gtk-rs/gtk4-rs/blob/main/book/README.md Changes the current directory to the 'listings' folder within the project. ```bash cd listings ```