├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── COPYRIGHT ├── Cargo.toml ├── LICENSE ├── README.md ├── appveyor.yml ├── build_travis.sh ├── resources ├── eye.png ├── file.png └── menu.png └── src └── bin ├── accessibility.rs ├── basic.rs ├── basic_subclass.rs ├── builder_basics.glade ├── builder_basics.rs ├── builder_signal.glade ├── builder_signal.rs ├── builders.rs ├── cairo_png.rs ├── cairo_threads.rs ├── cairotest.rs ├── child-properties.rs ├── clipboard_simple.rs ├── clock.rs ├── clone_macro.rs ├── communication_thread.rs ├── css.rs ├── drag_and_drop.rs ├── drag_and_drop_textview.rs ├── entry_completion.rs ├── gio_async_tls.rs ├── gio_futures.rs ├── gio_futures_await.rs ├── grid.glade ├── grid.rs ├── gtktest.glade ├── gtktest.rs ├── iconview_example.rs ├── list_store.rs ├── listbox_model.rs ├── menu_bar.rs ├── menu_bar_system.rs ├── multi_windows.rs ├── multithreading_context.rs ├── notebook.rs ├── overlay.rs ├── pango_attributes.rs ├── printing.glade ├── printing.rs ├── progress_tracker.rs ├── simple_treeview.rs ├── sync_widgets.glade ├── sync_widgets.rs ├── text_viewer.glade ├── text_viewer.rs ├── transparent_main_window.rs ├── tree_model_sort.rs └── treeview.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | Cargo.* eol=lf 2 | *.sh eol=lf 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: gtk-rs 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .vscode/settings.json 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: rust 3 | rust: 4 | - nightly 5 | - beta 6 | - 1.41.0 # stable 7 | - stable 8 | env: 9 | - GTK=3.14 10 | - GTK=3.18 11 | - GTK=3.22.30 12 | - GTK=3.24 13 | matrix: 14 | include: 15 | - os: osx 16 | rust: stable 17 | env: GTK=3.14 18 | - os: osx 19 | rust: stable 20 | env: GTK=3.24 21 | addons: 22 | apt: 23 | packages: 24 | - libgtk-3-dev 25 | - libmount-dev 26 | before_install: 27 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi 28 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python@2; fi 29 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libffi gtk+3 cairo atk; fi 30 | script: 31 | - ./build_travis.sh 32 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then 33 | rustup component add rustfmt; 34 | cargo fmt; 35 | git diff -R --exit-code; 36 | fi 37 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The Gtk-rs Project is copyright 2013-2016, The Gtk-rs Project Developers: 2 | 3 | Adam Crume 4 | Adolfo Ochagavía 5 | Andre Bogus 6 | Anton Konjahin 7 | Arne Dussin 8 | Boden Garman 9 | Brian Kropf 10 | Bryant Mairs 11 | Chris Greenaway 12 | Chris Palmer 13 | Corey Farwell 14 | Daniel Zalevskiy 15 | David Li 16 | Edward Shaw 17 | Edward Yang 18 | Esption 19 | Evgenii Pashkin 20 | Geoffrey French 21 | Gleb Kozyrev 22 | Glenn Watson 23 | Google Inc. 24 | Guillaume Gomez 25 | Gulshan Singh 26 | Jakob Gillich 27 | James Shepherdson 28 | Jeremy Letang 29 | John Vrbanac 30 | kennytm 31 | Laurence Tratt 32 | Lionel Flandrin 33 | Lucas Werkmeister 34 | Lukas Diekmann 35 | Mathijs Henquet 36 | Maxwell Koo 37 | mitaa 38 | Nick Herman 39 | Nicolas Koch 40 | Oliver Schneider 41 | Ömer Sinan Ağacan 42 | Ralph Giles 43 | Paul Dennis 44 | Paul Hendry 45 | Philipp Brüschweiler 46 | Raphael Nestler 47 | Robertas 48 | Romain Gauthier 49 | S.J.R. van Schaik 50 | Sebastian Schulze 51 | Silvio Fricke 52 | Simon Sapin 53 | Steve Klabnik 54 | Tobias Bales 55 | trolleyman 56 | Umur Gedik 57 | UrKr 58 | Vojtech Kral 59 | Zach Oakes 60 | Zach Ploskey 61 | 62 | The Gtk-rs Project is licensed under the MIT license, see the LICENSE file 63 | or . 64 | 65 | This project provides interoperability with various GNOME libraries but doesn't 66 | distribute any parts of them. Distributing compiled libraries and executables 67 | that link to those libraries may be subject to terms of the GNU LGPL, see the 68 | LGPL file. 69 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gtk-rs-examples" 3 | version = "0.0.1" 4 | authors = ["The Gtk-rs Project Developers"] 5 | autobins = false 6 | 7 | [dependencies] 8 | chrono = "0.4" 9 | futures = "0.3" 10 | atk = "^0" 11 | glib-sys = "^0" 12 | gobject-sys = "^0" 13 | glib = "^0" 14 | gio = "^0" 15 | gdk = "^0" 16 | gdk-pixbuf = "^0" 17 | gtk = "^0" 18 | once_cell = "^0" 19 | pango = "^0" 20 | pangocairo = "^0" 21 | cairo-rs = { version = "^0", features = ["png"] } 22 | 23 | [dependencies.async-tls] 24 | version = "0.6" 25 | optional = true 26 | 27 | [features] 28 | #default = ["gtk_3_22_30"] 29 | gtk_3_18 = ["gtk/v3_18", "gdk-pixbuf/v2_32", "gdk/v3_18", "gio/v2_46", "glib/v2_46", "pango/v1_38"] #for CI tools 30 | gtk_3_22_30 = ["gtk_3_18", "gtk/v3_22_30", "gdk-pixbuf/v2_36", "gdk/v3_22", "gio/v2_56", "glib/v2_56", "pango/v1_42"] #for CI tools 31 | gtk_3_24 = ["gtk_3_22_30", "gtk/v3_24", "atk/v2_30", "gdk-pixbuf/v2_36_8", "gdk/v3_24", "gio/v2_58", "glib/v2_58"] #for CI tools 32 | 33 | [[bin]] 34 | name = "accessibility" 35 | 36 | [[bin]] 37 | name = "basic" 38 | 39 | [[bin]] 40 | name = "basic_subclass" 41 | 42 | [[bin]] 43 | name = "builder_basics" 44 | 45 | [[bin]] 46 | name = "builder_signal" 47 | 48 | [[bin]] 49 | name = "builders" 50 | 51 | [[bin]] 52 | name = "cairo_png" 53 | 54 | [[bin]] 55 | name = "cairo_threads" 56 | 57 | [[bin]] 58 | name = "cairotest" 59 | 60 | [[bin]] 61 | name = "child-properties" 62 | 63 | [[bin]] 64 | name = "clipboard_simple" 65 | 66 | [[bin]] 67 | name = "clock" 68 | 69 | [[bin]] 70 | name = "clone_macro" 71 | 72 | [[bin]] 73 | name = "communication_thread" 74 | edition = "2018" 75 | 76 | [[bin]] 77 | name = "css" 78 | 79 | [[bin]] 80 | name = "drag_and_drop" 81 | 82 | [[bin]] 83 | name = "drag_and_drop_textview" 84 | 85 | [[bin]] 86 | name = "gio_futures" 87 | 88 | [[bin]] 89 | name = "gio_futures_await" 90 | edition = "2018" 91 | 92 | [[bin]] 93 | name = "grid" 94 | 95 | [[bin]] 96 | name = "gtktest" 97 | 98 | [[bin]] 99 | name = "iconview_example" 100 | 101 | [[bin]] 102 | name = "listbox_model" 103 | required-features = ["gtk/v3_16", "gio/v2_44"] 104 | 105 | [[bin]] 106 | name = "menu_bar" 107 | 108 | [[bin]] 109 | name = "menu_bar_system" 110 | 111 | [[bin]] 112 | name = "multi_windows" 113 | 114 | [[bin]] 115 | name = "multithreading_context" 116 | 117 | [[bin]] 118 | name = "notebook" 119 | 120 | [[bin]] 121 | name = "overlay" 122 | 123 | [[bin]] 124 | name = "pango_attributes" 125 | 126 | [[bin]] 127 | name = "progress_tracker" 128 | path = "src/bin/progress_tracker.rs" 129 | 130 | [[bin]] 131 | name = "simple_treeview" 132 | 133 | [[bin]] 134 | name = "sync_widgets" 135 | 136 | [[bin]] 137 | name = "text_viewer" 138 | 139 | [[bin]] 140 | name = "transparent_main_window" 141 | 142 | [[bin]] 143 | name = "tree_model_sort" 144 | 145 | [[bin]] 146 | name = "treeview" 147 | 148 | [[bin]] 149 | name = "list_store" 150 | 151 | [[bin]] 152 | name = "entry_completion" 153 | 154 | [[bin]] 155 | name = "printing" 156 | 157 | [[bin]] 158 | name = "gio_async_tls" 159 | required-features = ["async-tls"] 160 | edition = "2018" 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015, The Gtk-rs Project Developers. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATION NOTICE 2 | 3 | This repository is **DEPRECATED**. 4 | Use the [gtk-rs/gtk-rs](https://github.com/gtk-rs/gtk-rs) repository instead! 5 | 6 | # gtk-rs examples [![Build Status](https://travis-ci.org/gtk-rs/examples.png?branch=master)](https://travis-ci.org/gtk-rs/examples) [![Build status](https://ci.appveyor.com/api/projects/status/pi27a5xubp0ihl2d?svg=true)](https://ci.appveyor.com/project/GuillaumeGomez/examples) 7 | 8 | A few gtk-rs examples. To build, just do: 9 | 10 | ```Shell 11 | > cargo build 12 | ``` 13 | 14 | or to enable GTK 3.x depending on the version needed by the example (check Cargo.toml `[features]` to see all specific GTK compile features available): 15 | 16 | ```Shell 17 | > cargo build --features gtk_3_18 18 | > cargo build --all-features 19 | ``` 20 | 21 | And then run the executables with: 22 | 23 | ``` Shell 24 | ./target/debug/EXAMPLE-NAME 25 | ``` 26 | 27 | or with cargo run (repeating the compilation features used above), example: 28 | 29 | ``` Shell 30 | cargo run --all-features --bin EXAMPLE-NAME 31 | ``` 32 | 33 | Please be sure to have installed all the required libraries before building examples (the list is available on the [gtk-rs](https://github.com/gtk-rs/gtk/) repository). 34 | 35 | ## LICENSE 36 | The gtk-rs examples repository is licensed under the MIT license, please refer to the LICENSE and COPYRIGHT files for more information. 37 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - RUST: stable 4 | BITS: 32 5 | - RUST: stable 6 | BITS: 64 7 | 8 | install: 9 | - IF "%BITS%" == "32" SET ARCH=i686 10 | - IF "%BITS%" == "64" SET ARCH=x86_64 11 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 12 | - rustup-init.exe --default-host "%ARCH%-pc-windows-gnu" --default-toolchain %RUST% -y 13 | - SET PATH=C:\Users\appveyor\.cargo\bin;C:\msys64\mingw%BITS%\bin;%PATH%;C:\msys64\usr\bin 14 | - rustc -Vv 15 | - cargo -Vv 16 | - pacman --noconfirm -S mingw-w64-%ARCH%-gtk3 17 | 18 | build_script: 19 | - cargo build 20 | - cargo build --features gtk_3_24 21 | 22 | test: false 23 | -------------------------------------------------------------------------------- /build_travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -x 4 | set -e 5 | 6 | if [ "$GTK" = latest -o "$GTK" = "3.24" ]; then 7 | BUNDLE="gtk-3.24.0-1" 8 | if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then 9 | FEATURES=gtk_3_24,gio/v2_44 10 | else 11 | FEATURES=gtk_3_24,gio/v2_44 12 | fi 13 | elif [ "$GTK" = "3.22.30" ]; then 14 | BUNDLE="gtk-3.22.30-1" 15 | if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then 16 | FEATURES=gtk_3_22_30,gio/v2_44 17 | else 18 | FEATURES=gtk_3_22_30,gio/v2_44 19 | fi 20 | elif [ "$GTK" = "3.18" ]; then 21 | BUNDLE="gtk-3.18.1-2" 22 | if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then 23 | FEATURES=gtk_3_18,gio/v2_44 24 | else 25 | FEATURES=gtk_3_18,gio/v2_44 26 | fi 27 | fi 28 | 29 | if [ -n "$BUNDLE" ] && [ "$TRAVIS_OS_NAME" != "osx" ]; then 30 | WD="$PWD" 31 | cd "$HOME" 32 | curl -LO "https://github.com/EPashkin/gtk-bootstrap/releases/download/$BUNDLE/deps.txz" 33 | tar xf deps.txz 34 | cd "$WD" 35 | export PKG_CONFIG_PATH="$HOME/local/lib/pkgconfig" 36 | fi 37 | 38 | if [ -n "$OTHER_TARGET" ]; then 39 | PKG_CONFIG_ALLOW_CROSS=1 cargo check $OTHER_TARGET --features "$FEATURES" --jobs 1 "$@" 40 | else 41 | RUSTFLAGS="-C link-dead-code" cargo build -v --features "$FEATURES" --jobs 1 "$@" 42 | fi 43 | -------------------------------------------------------------------------------- /resources/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtk-rs/examples/765478a1b62e7a6f09a073c74c3493acb375ca24/resources/eye.png -------------------------------------------------------------------------------- /resources/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtk-rs/examples/765478a1b62e7a6f09a073c74c3493acb375ca24/resources/file.png -------------------------------------------------------------------------------- /resources/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtk-rs/examples/765478a1b62e7a6f09a073c74c3493acb375ca24/resources/menu.png -------------------------------------------------------------------------------- /src/bin/accessibility.rs: -------------------------------------------------------------------------------- 1 | //! # Accessibility example 2 | //! 3 | //! This sample demonstrates how to make an application more accessible. 4 | 5 | extern crate atk; 6 | extern crate gdk; 7 | extern crate gio; 8 | extern crate glib; 9 | extern crate gtk; 10 | 11 | use atk::prelude::*; 12 | use gio::prelude::*; 13 | use gtk::prelude::*; 14 | 15 | use std::env::args; 16 | 17 | fn build_ui(application: >k::Application) { 18 | let window = gtk::ApplicationWindow::new(application); 19 | 20 | window.set_title("Accessibility"); 21 | window.set_position(gtk::WindowPosition::Center); 22 | 23 | let button = gtk::Button::with_label("Click me!"); 24 | let label = gtk::Label::new(Some("0")); 25 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); 26 | 27 | if let (Some(button_obj), Some(label_obj)) = (button.get_accessible(), label.get_accessible()) { 28 | // We set the description 29 | button_obj.set_description("Button to increase label value"); 30 | 31 | // Then we setup the relation saying that the label is linked to the button. 32 | let relation_set = label_obj 33 | .ref_relation_set() 34 | .expect("Failed to get relation for label"); 35 | let relation = atk::Relation::new(&[button_obj], atk::RelationType::LabelFor); 36 | 37 | relation_set.add(&relation); 38 | } 39 | 40 | vbox.add(&button); 41 | vbox.add(&label); 42 | 43 | window.add(&vbox); 44 | 45 | button.connect_clicked(move |_| { 46 | let value = label.get_text().parse().unwrap_or(0) + 1; 47 | label.set_text(&value.to_string()); 48 | }); 49 | 50 | window.show_all(); 51 | } 52 | 53 | fn main() { 54 | let application = gtk::Application::new( 55 | Some("com.github.accessibility"), 56 | gio::ApplicationFlags::empty(), 57 | ) 58 | .expect("Initialization failed..."); 59 | 60 | application.connect_activate(|app| { 61 | // We build the application UI. 62 | build_ui(app); 63 | }); 64 | 65 | application.run(&args().collect::>()); 66 | } 67 | -------------------------------------------------------------------------------- /src/bin/basic.rs: -------------------------------------------------------------------------------- 1 | //! # Basic Sample 2 | //! 3 | //! This sample demonstrates how to create a toplevel `window`, set its title, size and 4 | //! position, how to add a `button` to this `window` and how to connect signals with 5 | //! actions. 6 | 7 | extern crate gio; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | 13 | use std::env::args; 14 | 15 | fn build_ui(application: >k::Application) { 16 | let window = gtk::ApplicationWindow::new(application); 17 | 18 | window.set_title("First GTK+ Program"); 19 | window.set_border_width(10); 20 | window.set_position(gtk::WindowPosition::Center); 21 | window.set_default_size(350, 70); 22 | 23 | let button = gtk::Button::with_label("Click me!"); 24 | 25 | window.add(&button); 26 | 27 | window.show_all(); 28 | } 29 | 30 | fn main() { 31 | let application = 32 | gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default()) 33 | .expect("Initialization failed..."); 34 | 35 | application.connect_activate(|app| { 36 | build_ui(app); 37 | }); 38 | 39 | application.run(&args().collect::>()); 40 | } 41 | -------------------------------------------------------------------------------- /src/bin/basic_subclass.rs: -------------------------------------------------------------------------------- 1 | //! # Basic Subclass example 2 | //! 3 | //! This file creates a `GtkApplication` and a `GtkApplicationWindow` subclass 4 | //! and showcases how you can override virtual funcitons such as `startup` 5 | //! and `activate` and how to interact with the GObjects and their private 6 | //! structs. 7 | 8 | #[macro_use] 9 | extern crate glib; 10 | extern crate gio; 11 | extern crate gtk; 12 | 13 | extern crate once_cell; 14 | 15 | use gio::prelude::*; 16 | use gtk::prelude::*; 17 | 18 | use gio::subclass::prelude::*; 19 | use gio::ApplicationFlags; 20 | use glib::subclass; 21 | use glib::subclass::prelude::*; 22 | use glib::translate::*; 23 | use gtk::subclass::prelude::*; 24 | 25 | use once_cell::unsync::OnceCell; 26 | use std::cell::Cell; 27 | 28 | #[derive(Debug)] 29 | struct WindowWidgets { 30 | headerbar: gtk::HeaderBar, 31 | increment: gtk::Button, 32 | label: gtk::Label, 33 | } 34 | 35 | // This is the private part of our `SimpleWindow` object. 36 | // Its where state and widgets are stored when they don't 37 | // need to be publicly accesible. 38 | #[derive(Debug)] 39 | pub struct SimpleWindowPrivate { 40 | widgets: OnceCell, 41 | counter: Cell, 42 | } 43 | 44 | impl ObjectSubclass for SimpleWindowPrivate { 45 | const NAME: &'static str = "SimpleWindow"; 46 | type ParentType = gtk::ApplicationWindow; 47 | type Instance = subclass::simple::InstanceStruct; 48 | type Class = subclass::simple::ClassStruct; 49 | 50 | glib_object_subclass!(); 51 | 52 | fn new() -> Self { 53 | Self { 54 | widgets: OnceCell::new(), 55 | counter: Cell::new(0), 56 | } 57 | } 58 | } 59 | 60 | impl ObjectImpl for SimpleWindowPrivate { 61 | glib_object_impl!(); 62 | 63 | // Here we are overriding the glib::Objcet::contructed 64 | // method. Its what gets called when we create our Object 65 | // and where we can initialize things. 66 | fn constructed(&self, obj: &glib::Object) { 67 | self.parent_constructed(obj); 68 | 69 | let self_ = obj.downcast_ref::().unwrap(); 70 | 71 | let headerbar = gtk::HeaderBar::new(); 72 | let increment = gtk::Button::with_label("Increment!"); 73 | let label = gtk::Label::new(Some("Press the Increment Button!")); 74 | 75 | headerbar.set_title(Some("Hello World!")); 76 | headerbar.set_show_close_button(true); 77 | headerbar.pack_start(&increment); 78 | 79 | // Connect our method `on_increment_clicked` to be called 80 | // when the increment button is clicked. 81 | increment.connect_clicked(clone!(@weak self_ => move |_| { 82 | let priv_ = SimpleWindowPrivate::from_instance(&self_); 83 | priv_.on_increment_clicked(); 84 | })); 85 | 86 | self_.add(&label); 87 | self_.set_titlebar(Some(&headerbar)); 88 | self_.set_default_size(640, 480); 89 | 90 | self.widgets 91 | .set(WindowWidgets { 92 | headerbar, 93 | label, 94 | increment, 95 | }) 96 | .expect("Failed to initialize window state"); 97 | } 98 | } 99 | 100 | impl SimpleWindowPrivate { 101 | fn on_increment_clicked(&self) { 102 | self.counter.set(self.counter.get() + 1); 103 | let w = self.widgets.get().unwrap(); 104 | w.label 105 | .set_text(&format!("Counter is {}", self.counter.get())); 106 | } 107 | } 108 | 109 | impl WidgetImpl for SimpleWindowPrivate {} 110 | impl ContainerImpl for SimpleWindowPrivate {} 111 | impl BinImpl for SimpleWindowPrivate {} 112 | impl WindowImpl for SimpleWindowPrivate {} 113 | impl ApplicationWindowImpl for SimpleWindowPrivate {} 114 | 115 | glib_wrapper! { 116 | pub struct SimpleWindow( 117 | Object, 118 | subclass::simple::ClassStruct, 119 | SimpleAppWindowClass>) 120 | @extends gtk::Widget, gtk::Container, gtk::Bin, gtk::Window, gtk::ApplicationWindow; 121 | 122 | match fn { 123 | get_type => || SimpleWindowPrivate::get_type().to_glib(), 124 | } 125 | } 126 | 127 | impl SimpleWindow { 128 | pub fn new(app: >k::Application) -> Self { 129 | glib::Object::new(Self::static_type(), &[("application", app)]) 130 | .expect("Failed to create SimpleWindow") 131 | .downcast::() 132 | .expect("Created SimpleWindow is of wrong type") 133 | } 134 | } 135 | 136 | #[derive(Debug)] 137 | pub struct SimpleApplicationPrivate { 138 | window: OnceCell, 139 | } 140 | 141 | impl ObjectSubclass for SimpleApplicationPrivate { 142 | const NAME: &'static str = "SimpleApplication"; 143 | type ParentType = gtk::Application; 144 | type Instance = subclass::simple::InstanceStruct; 145 | type Class = subclass::simple::ClassStruct; 146 | 147 | glib_object_subclass!(); 148 | 149 | fn new() -> Self { 150 | Self { 151 | window: OnceCell::new(), 152 | } 153 | } 154 | } 155 | 156 | impl ObjectImpl for SimpleApplicationPrivate { 157 | glib_object_impl!(); 158 | } 159 | 160 | // When our application starts, the `startup` signal will be fired. 161 | // This gives us a chance to perform initialisation tasks that are not directly 162 | // related to showing a new window. After this, depending on how 163 | // the application is started, either `activate` or `open` will be called next. 164 | impl ApplicationImpl for SimpleApplicationPrivate { 165 | // `gio::Application::activate` is what gets called when the 166 | // application is launched by the desktop environment and 167 | // aksed to present itself. 168 | fn activate(&self, _app: &gio::Application) { 169 | let window = self 170 | .window 171 | .get() 172 | .expect("Should always be initiliazed in gio_application_startup"); 173 | window.show_all(); 174 | window.present(); 175 | } 176 | 177 | // `gio::Application` is bit special. It does not get initialized 178 | // when `new` is called and the object created, but rather 179 | // once the `startup` signal is emitted and the `gio::Application::startup` 180 | // is called. 181 | // 182 | // Due to this, we create and initialize the `SimpleWindow` widget 183 | // here. Widgets can't be created before `startup` has been called. 184 | fn startup(&self, app: &gio::Application) { 185 | self.parent_startup(app); 186 | 187 | let app = app.downcast_ref::().unwrap(); 188 | let window = SimpleWindow::new(&app); 189 | self.window 190 | .set(window) 191 | .expect("Failed to initialize application window"); 192 | } 193 | } 194 | 195 | impl GtkApplicationImpl for SimpleApplicationPrivate {} 196 | 197 | glib_wrapper! { 198 | pub struct SimpleApplication( 199 | Object, 200 | subclass::simple::ClassStruct, 201 | SimpleApplicationClass>) 202 | @extends gio::Application, gtk::Application; 203 | 204 | match fn { 205 | get_type => || SimpleApplicationPrivate::get_type().to_glib(), 206 | } 207 | } 208 | 209 | impl SimpleApplication { 210 | pub fn new() -> Self { 211 | glib::Object::new( 212 | Self::static_type(), 213 | &[ 214 | ("application-id", &"org.gtk-rs.SimpleApplication"), 215 | ("flags", &ApplicationFlags::empty()), 216 | ], 217 | ) 218 | .expect("Failed to create SimpleApp") 219 | .downcast() 220 | .expect("Created simpleapp is of wrong type") 221 | } 222 | } 223 | 224 | fn main() { 225 | gtk::init().expect("Failed to initialize gtk"); 226 | 227 | let app = SimpleApplication::new(); 228 | 229 | let args: Vec = std::env::args().collect(); 230 | app.run(&args); 231 | } 232 | -------------------------------------------------------------------------------- /src/bin/builder_basics.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Builder Basics 6 | 320 7 | 240 8 | 9 | 10 | Big Useless Button 11 | 12 | 13 | 14 | 15 | False 16 | dialog 17 | window1 18 | 19 | 20 | msgdialog 21 | 300 22 | False 23 | Thank you for trying this example 24 | immediate 25 | vertical 26 | 2 27 | 28 | 29 | False 30 | end 31 | 32 | 33 | 34 | 35 | 36 | False 37 | True 38 | end 39 | 0 40 | 41 | 42 | 43 | 44 | True 45 | False 46 | You have pressed the button 47 | end 48 | 40 49 | 1 50 | 51 | 52 | False 53 | True 54 | 2 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/bin/builder_basics.rs: -------------------------------------------------------------------------------- 1 | //! # Builder Basics Sample 2 | //! 3 | //! This sample demonstrates how to use the builder with an imported glade file 4 | 5 | extern crate gio; 6 | extern crate glib; 7 | extern crate gtk; 8 | 9 | use gio::prelude::*; 10 | use glib::clone; 11 | use gtk::prelude::*; 12 | 13 | use gtk::{ApplicationWindow, Builder, Button, MessageDialog}; 14 | 15 | use std::env::args; 16 | 17 | fn build_ui(application: >k::Application) { 18 | let glade_src = include_str!("builder_basics.glade"); 19 | let builder = Builder::from_string(glade_src); 20 | 21 | let window: ApplicationWindow = builder.get_object("window1").expect("Couldn't get window1"); 22 | window.set_application(Some(application)); 23 | let bigbutton: Button = builder.get_object("button1").expect("Couldn't get button1"); 24 | let dialog: MessageDialog = builder 25 | .get_object("messagedialog1") 26 | .expect("Couldn't get messagedialog1"); 27 | 28 | dialog.connect_delete_event(|dialog, _| { 29 | dialog.hide(); 30 | gtk::Inhibit(true) 31 | }); 32 | 33 | bigbutton.connect_clicked(clone!(@weak dialog => move |_| dialog.show_all())); 34 | window.show_all(); 35 | } 36 | 37 | fn main() { 38 | let application = gtk::Application::new( 39 | Some("com.github.gtk-rs.examples.builder_basics"), 40 | Default::default(), 41 | ) 42 | .expect("Initialization failed..."); 43 | 44 | application.connect_activate(|app| { 45 | build_ui(app); 46 | }); 47 | 48 | application.run(&args().collect::>()); 49 | } 50 | -------------------------------------------------------------------------------- /src/bin/builder_signal.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Builder Signal 6 | 320 7 | 240 8 | 9 | 10 | Big Useless Button 11 | 12 | 13 | 14 | 15 | 16 | False 17 | dialog 18 | window1 19 | 20 | 21 | msgdialog 22 | 300 23 | False 24 | Thank you for trying this example 25 | immediate 26 | vertical 27 | 2 28 | 29 | 30 | False 31 | end 32 | 33 | 34 | 35 | 36 | 37 | False 38 | True 39 | end 40 | 0 41 | 42 | 43 | 44 | 45 | True 46 | False 47 | You have pressed the button 48 | end 49 | 40 50 | 1 51 | 52 | 53 | False 54 | True 55 | 2 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/bin/builder_signal.rs: -------------------------------------------------------------------------------- 1 | //! # Builder Signal Sample 2 | //! 3 | //! This sample demonstrates how to handle signals in builder 4 | 5 | extern crate gio; 6 | extern crate glib; 7 | extern crate gtk; 8 | 9 | use gio::prelude::*; 10 | use glib::clone; 11 | use gtk::prelude::*; 12 | 13 | use gtk::{ApplicationWindow, Builder, MessageDialog}; 14 | 15 | use std::env::args; 16 | 17 | fn build_ui(application: >k::Application) { 18 | let glade_src = include_str!("builder_signal.glade"); 19 | let builder = Builder::from_string(glade_src); 20 | 21 | let window: ApplicationWindow = builder.get_object("window1").expect("Couldn't get window1"); 22 | window.set_application(Some(application)); 23 | let dialog: MessageDialog = builder 24 | .get_object("messagedialog1") 25 | .expect("Couldn't get messagedialog1"); 26 | dialog.connect_delete_event(|dialog, _| { 27 | dialog.hide(); 28 | gtk::Inhibit(true) 29 | }); 30 | 31 | builder.connect_signals(move |_, handler_name| { 32 | // This is the one-time callback to register signals. 33 | // Here we map each handler name to its handler. 34 | 35 | if handler_name == "button1_clicked" { 36 | // Return the signal handler. 37 | Box::new(clone!(@weak dialog => @default-return None, move |_| { 38 | dialog.show_all(); 39 | None 40 | })) 41 | } else { 42 | panic!("Unknown handler name {}", handler_name) 43 | } 44 | }); 45 | 46 | window.show_all(); 47 | } 48 | 49 | fn main() { 50 | let application = gtk::Application::new( 51 | Some("com.github.gtk-rs.examples.builder_signal"), 52 | Default::default(), 53 | ) 54 | .expect("Initialization failed..."); 55 | 56 | application.connect_activate(|app| { 57 | build_ui(app); 58 | }); 59 | 60 | application.run(&args().collect::>()); 61 | } 62 | -------------------------------------------------------------------------------- /src/bin/builders.rs: -------------------------------------------------------------------------------- 1 | //! # Builders Sample 2 | //! 3 | //! This sample demonstrates how to create a widget using the builders. 4 | //! These allow to set construct-only properties and other construct 5 | //! properties when creating the widget. 6 | 7 | extern crate gio; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | 13 | use std::env::args; 14 | 15 | fn build_ui(application: >k::Application) { 16 | let window = gtk::ApplicationWindowBuilder::new() 17 | .application(application) 18 | .title("First GTK+ Program") 19 | .border_width(10) 20 | .window_position(gtk::WindowPosition::Center) 21 | .default_width(350) 22 | .default_height(70) 23 | .build(); 24 | 25 | let button = gtk::LockButtonBuilder::new() 26 | .text_lock("Lock") 27 | .text_unlock("Unlock") 28 | .build(); 29 | 30 | window.add(&button); 31 | 32 | window.show_all(); 33 | } 34 | 35 | fn main() { 36 | let application = 37 | gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default()) 38 | .expect("Initialization failed..."); 39 | 40 | application.connect_activate(|app| { 41 | build_ui(app); 42 | }); 43 | 44 | application.run(&args().collect::>()); 45 | } 46 | -------------------------------------------------------------------------------- /src/bin/cairo_png.rs: -------------------------------------------------------------------------------- 1 | //! # Cairo drawing to PNG 2 | //! 3 | //! This sample demonstrates how to create `ImageSurface`, draw on it 4 | //! and then save result to PNG file. 5 | //! Analog of C# example http://www.mgsloan.com/cairo_tut/stroke.cs 6 | 7 | extern crate cairo; 8 | 9 | use cairo::{Context, Format, ImageSurface}; 10 | use std::fs::File; 11 | 12 | fn main() { 13 | let surface = ImageSurface::create(Format::ARgb32, 120, 120).expect("Can't create surface"); 14 | let cr = Context::new(&surface); 15 | // Examples are in 1.0 x 1.0 coordinate space 16 | cr.scale(120.0, 120.0); 17 | 18 | // Drawing code goes here 19 | cr.set_line_width(0.1); 20 | cr.set_source_rgb(0.0, 0.0, 0.0); 21 | cr.rectangle(0.25, 0.25, 0.5, 0.5); 22 | cr.stroke(); 23 | 24 | let mut file = File::create("file.png").expect("Couldn't create 'file.png'"); 25 | match surface.write_to_png(&mut file) { 26 | Ok(_) => println!("file.png created"), 27 | Err(_) => println!("Error create file.png"), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/bin/cairo_threads.rs: -------------------------------------------------------------------------------- 1 | extern crate cairo; 2 | extern crate gio; 3 | extern crate glib; 4 | extern crate gtk; 5 | 6 | use std::cell::RefCell; 7 | use std::env::args; 8 | use std::rc::Rc; 9 | use std::sync::mpsc; 10 | use std::thread; 11 | use std::time::Duration; 12 | 13 | use cairo::{Context, Format, ImageSurface}; 14 | use gio::prelude::*; 15 | use glib::clone; 16 | use gtk::prelude::*; 17 | use gtk::{ApplicationWindow, DrawingArea}; 18 | 19 | const WIDTH: i32 = 200; 20 | const HEIGHT: i32 = 200; 21 | 22 | // Our custom image type. This stores a heap allocated byte array for the pixels for each of our 23 | // images, can be sent safely between threads and can be temporarily converted to a Cairo image 24 | // surface for drawing operations 25 | #[derive(Clone)] 26 | struct Image(Option>); 27 | 28 | impl Image { 29 | // Creates a new, black image 30 | fn new() -> Self { 31 | Image(Some(vec![0; 4 * WIDTH as usize * HEIGHT as usize].into())) 32 | } 33 | 34 | // Calls the given closure with a temporary Cairo image surface. After the closure has returned 35 | // there must be no further references to the surface. 36 | fn with_surface(&mut self, func: F) { 37 | // Helper struct that allows passing the pixels to the Cairo image surface and once the 38 | // image surface is destroyed the pixels will be stored in the return_location. 39 | // 40 | // This allows us to give temporary ownership of the pixels to the Cairo surface and later 41 | // retrieve them back in a safe way while ensuring that nothing else still has access to 42 | // it. 43 | struct ImageHolder { 44 | image: Option>, 45 | return_location: Rc>>>, 46 | } 47 | 48 | // This stores the pixels back into the return_location as now nothing 49 | // references the pixels anymore 50 | impl Drop for ImageHolder { 51 | fn drop(&mut self) { 52 | *self.return_location.borrow_mut() = 53 | Some(self.image.take().expect("Holding no image")); 54 | } 55 | } 56 | 57 | // Needed for ImageSurface::create_for_data() to be able to access the pixels 58 | impl AsRef<[u8]> for ImageHolder { 59 | fn as_ref(&self) -> &[u8] { 60 | self.image.as_ref().expect("Holding no image").as_ref() 61 | } 62 | } 63 | 64 | impl AsMut<[u8]> for ImageHolder { 65 | fn as_mut(&mut self) -> &mut [u8] { 66 | self.image.as_mut().expect("Holding no image").as_mut() 67 | } 68 | } 69 | 70 | // Temporary move out the pixels 71 | let image = self.0.take().expect("Empty image"); 72 | 73 | // A new return location that is then passed to our helper struct below 74 | let return_location = Rc::new(RefCell::new(None)); 75 | { 76 | let holder = ImageHolder { 77 | image: Some(image), 78 | return_location: return_location.clone(), 79 | }; 80 | 81 | // The surface will own the image for the scope of the block below 82 | let surface = 83 | ImageSurface::create_for_data(holder, Format::Rgb24, WIDTH, HEIGHT, 4 * WIDTH) 84 | .expect("Can't create surface"); 85 | func(&surface); 86 | 87 | // Now the surface will be destroyed and the pixels are stored in the return_location 88 | } 89 | 90 | // And here move the pixels back again 91 | self.0 = Some( 92 | return_location 93 | .borrow_mut() 94 | .take() 95 | .expect("Image not returned"), 96 | ); 97 | } 98 | } 99 | 100 | // This example runs four worker threads rendering parts of the image independently at different 101 | // paces in a sort of double buffered way. 102 | // 103 | // +---+---+ 104 | // | 0 | 1 | 105 | // +---+---+ 106 | // | 2 | 3 | 107 | // +---+---+ 108 | // 109 | // Each worker thread waits for an image to render into, sleeps for a while, does the drawing, then 110 | // sends the image back and waits for the next one. 111 | // 112 | // The GUI thread holds an image per image part at all times and these images are painted on a 113 | // DrawingArea in its 'draw' signal handler whenever needed. 114 | // 115 | // Additionally the GUI thread has a channel for receiving the images from the worker threads. If 116 | // there is a new image, the old image stored by the GUI thread is replaced with the new one and 117 | // the old image is sent back to the worker thread. Then the appropriate part of the DrawingArea is 118 | // invalidated prompting a redraw. 119 | // 120 | // The two images per thread are allocated and initialized once and sent back and forth repeatedly. 121 | 122 | fn build_ui(application: >k::Application) { 123 | let window = ApplicationWindow::new(application); 124 | let area = DrawingArea::new(); 125 | window.add(&area); 126 | 127 | area.set_size_request(WIDTH * 2, HEIGHT * 2); 128 | 129 | // Create the initial, green image 130 | let initial_image = draw_initial(); 131 | 132 | // This is the channel for sending results from the worker thread to the main thread 133 | // For every received image, queue the corresponding part of the DrawingArea for redrawing 134 | let (ready_tx, ready_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); 135 | 136 | let mut images = Vec::new(); 137 | let mut origins = Vec::new(); 138 | let mut workers = Vec::new(); 139 | 140 | for thread_num in 0..4 { 141 | // This is the channel for sending image from the GUI thread to the workers so that the 142 | // worker can draw the new content into them 143 | let (tx, rx) = mpsc::channel(); 144 | 145 | // Copy the initial image in two images for the workers 146 | let image0 = initial_image.clone(); 147 | let image1 = initial_image.clone(); 148 | 149 | // Store the first one in our vec. This is the one that would be drawn in the very 150 | // beginning by the DrawingArea. 151 | images.push(RefCell::new(image0)); 152 | 153 | // Send the second one immediately to the worker thread for drawing the new content 154 | let _ = tx.send(image1); 155 | 156 | // Store for each worker thread its render rectangle inside the DrawingArea 157 | origins.push(match thread_num { 158 | 0 => (0, 0), 159 | 1 => (WIDTH, 0), 160 | 2 => (0, HEIGHT), 161 | 3 => (WIDTH, HEIGHT), 162 | _ => unreachable!(), 163 | }); 164 | 165 | // And remember the sender side of the channel that allows the GUI thread to send back 166 | // images to the worker thread 167 | workers.push(tx); 168 | 169 | let x = (WIDTH - origins[thread_num].0) as f64; 170 | let y = (HEIGHT - origins[thread_num].1) as f64; 171 | let delay = Duration::from_millis((100 << thread_num) - 5); 172 | 173 | // Spawn the worker thread 174 | thread::spawn(clone!(@strong ready_tx => move || { 175 | let mut n = 0; 176 | for mut image in rx.iter() { 177 | n = (n + 1) % 0x10000; 178 | 179 | // Draw an arc with a weirdly calculated radius 180 | image.with_surface(|surface| { 181 | let cr = Context::new(surface); 182 | draw_slow(&cr, delay, x, y, 1.2_f64.powi(((n as i32) << thread_num) % 32)); 183 | surface.flush(); 184 | }); 185 | 186 | // Send the finished image back to the GUI thread 187 | let _ = ready_tx.send((thread_num, image)); 188 | } 189 | })); 190 | } 191 | 192 | // The connect-draw signal and the timeout handler closures have to be 'static, and both need 193 | // to have access to our images and related state. 194 | let workspace = Rc::new((images, origins, workers)); 195 | 196 | // Whenever the drawing area has to be redrawn, render the latest images in the correct 197 | // locations 198 | area.connect_draw( 199 | clone!(@weak workspace => @default-return Inhibit(false), move |_, cr| { 200 | let (ref images, ref origins, _) = *workspace; 201 | 202 | for (image, origin) in images.iter().zip(origins.iter()) { 203 | image.borrow_mut().with_surface(|surface| { 204 | draw_image_if_dirty(&cr, surface, *origin, (WIDTH, HEIGHT)); 205 | }); 206 | } 207 | 208 | Inhibit(false) 209 | }), 210 | ); 211 | 212 | ready_rx.attach(None, move |(thread_num, image)| { 213 | let (ref images, ref origins, ref workers) = *workspace; 214 | 215 | // Swap the newly received image with the old stored one and send the old one back to 216 | // the worker thread 217 | let tx = &workers[thread_num]; 218 | let image = images[thread_num].replace(image); 219 | let _ = tx.send(image); 220 | 221 | area.queue_draw_area(origins[thread_num].0, origins[thread_num].1, WIDTH, HEIGHT); 222 | 223 | Continue(true) 224 | }); 225 | 226 | window.show_all(); 227 | } 228 | 229 | fn main() { 230 | let application = gtk::Application::new( 231 | Some("com.github.gtk-rs.examples.cairo_threads"), 232 | Default::default(), 233 | ) 234 | .expect("Initialization failed..."); 235 | 236 | application.connect_activate(|app| { 237 | build_ui(app); 238 | }); 239 | 240 | application.run(&args().collect::>()); 241 | } 242 | 243 | // Creates a new image and fill it with green 244 | fn draw_initial() -> Image { 245 | let mut image = Image::new(); 246 | 247 | image.with_surface(|surface| { 248 | let cr = Context::new(surface); 249 | cr.set_source_rgb(0., 1., 0.); 250 | cr.paint(); 251 | }); 252 | 253 | image 254 | } 255 | 256 | // Sleep for a while and then draw an arc with a given radius 257 | fn draw_slow(cr: &Context, delay: Duration, x: f64, y: f64, radius: f64) { 258 | use std::f64::consts::PI; 259 | 260 | thread::sleep(delay); 261 | cr.set_source_rgb(0., 0., 0.); 262 | cr.paint(); 263 | cr.set_source_rgb(1., 1., 1.); 264 | cr.arc(x, y, radius, 0.0, 2. * PI); 265 | cr.stroke(); 266 | } 267 | 268 | // Render the image surface into the context at the given position 269 | fn draw_image_if_dirty( 270 | cr: &Context, 271 | image: &ImageSurface, 272 | origin: (i32, i32), 273 | dimensions: (i32, i32), 274 | ) { 275 | let x = origin.0 as f64; 276 | let y = origin.1 as f64; 277 | let w = dimensions.0 as f64; 278 | let h = dimensions.1 as f64; 279 | let (clip_x1, clip_y1, clip_x2, clip_y2) = cr.clip_extents(); 280 | if clip_x1 >= x + w || clip_y1 >= y + h || clip_x2 <= x || clip_y2 <= y { 281 | return; 282 | } 283 | cr.set_source_surface(image, x, y); 284 | cr.paint(); 285 | 286 | // Release the reference to the surface again 287 | cr.set_source_rgba(0.0, 0.0, 0.0, 0.0); 288 | } 289 | -------------------------------------------------------------------------------- /src/bin/cairotest.rs: -------------------------------------------------------------------------------- 1 | extern crate cairo; 2 | extern crate gio; 3 | extern crate gtk; 4 | 5 | use std::env::args; 6 | use std::f64::consts::PI; 7 | 8 | use gio::prelude::*; 9 | use gtk::prelude::*; 10 | use gtk::DrawingArea; 11 | 12 | use cairo::{Context, FontSlant, FontWeight}; 13 | 14 | fn build_ui(application: >k::Application) { 15 | drawable(application, 500, 500, |_, cr| { 16 | cr.set_dash(&[3., 2., 1.], 1.); 17 | assert_eq!(cr.get_dash(), (vec![3., 2., 1.], 1.)); 18 | 19 | cr.scale(500f64, 500f64); 20 | 21 | cr.set_source_rgb(250.0 / 255.0, 224.0 / 255.0, 55.0 / 255.0); 22 | cr.paint(); 23 | 24 | cr.set_line_width(0.05); 25 | 26 | // border 27 | cr.set_source_rgb(0.3, 0.3, 0.3); 28 | cr.rectangle(0.0, 0.0, 1.0, 1.0); 29 | cr.stroke(); 30 | 31 | cr.set_line_width(0.03); 32 | 33 | // draw circle 34 | cr.arc(0.5, 0.5, 0.4, 0.0, PI * 2.); 35 | cr.stroke(); 36 | 37 | // mouth 38 | let mouth_top = 0.68; 39 | let mouth_width = 0.38; 40 | 41 | let mouth_dx = 0.10; 42 | let mouth_dy = 0.10; 43 | 44 | cr.move_to(0.50 - mouth_width / 2.0, mouth_top); 45 | cr.curve_to( 46 | 0.50 - mouth_dx, 47 | mouth_top + mouth_dy, 48 | 0.50 + mouth_dx, 49 | mouth_top + mouth_dy, 50 | 0.50 + mouth_width / 2.0, 51 | mouth_top, 52 | ); 53 | 54 | println!("Extents: {:?}", cr.fill_extents()); 55 | 56 | cr.stroke(); 57 | 58 | let eye_y = 0.38; 59 | let eye_dx = 0.15; 60 | cr.arc(0.5 - eye_dx, eye_y, 0.05, 0.0, PI * 2.); 61 | cr.fill(); 62 | 63 | cr.arc(0.5 + eye_dx, eye_y, 0.05, 0.0, PI * 2.); 64 | cr.fill(); 65 | 66 | Inhibit(false) 67 | }); 68 | 69 | drawable(application, 500, 500, |_, cr| { 70 | cr.scale(500f64, 500f64); 71 | 72 | cr.select_font_face("Sans", FontSlant::Normal, FontWeight::Normal); 73 | cr.set_font_size(0.35); 74 | 75 | cr.move_to(0.04, 0.53); 76 | cr.show_text("Hello"); 77 | 78 | cr.move_to(0.27, 0.65); 79 | cr.text_path("void"); 80 | cr.set_source_rgb(0.5, 0.5, 1.0); 81 | cr.fill_preserve(); 82 | cr.set_source_rgb(0.0, 0.0, 0.0); 83 | cr.set_line_width(0.01); 84 | cr.stroke(); 85 | 86 | cr.set_source_rgba(1.0, 0.2, 0.2, 0.6); 87 | cr.arc(0.04, 0.53, 0.02, 0.0, PI * 2.); 88 | cr.arc(0.27, 0.65, 0.02, 0.0, PI * 2.); 89 | cr.fill(); 90 | 91 | Inhibit(false) 92 | }); 93 | } 94 | 95 | fn main() { 96 | let application = gtk::Application::new( 97 | Some("com.github.gtk-rs.examples.cairotest"), 98 | Default::default(), 99 | ) 100 | .expect("Initialization failed..."); 101 | 102 | application.connect_activate(|app| { 103 | build_ui(app); 104 | }); 105 | 106 | application.run(&args().collect::>()); 107 | } 108 | 109 | pub fn drawable(application: >k::Application, width: i32, height: i32, draw_fn: F) 110 | where 111 | F: Fn(&DrawingArea, &Context) -> Inhibit + 'static, 112 | { 113 | let window = gtk::ApplicationWindow::new(application); 114 | let drawing_area = Box::new(DrawingArea::new)(); 115 | 116 | drawing_area.connect_draw(draw_fn); 117 | 118 | window.set_default_size(width, height); 119 | 120 | window.add(&drawing_area); 121 | window.show_all(); 122 | } 123 | -------------------------------------------------------------------------------- /src/bin/child-properties.rs: -------------------------------------------------------------------------------- 1 | //! # Child Properties 2 | //! 3 | //! This sample demonstrates how to set child properties. 4 | 5 | #![crate_type = "bin"] 6 | 7 | extern crate gio; 8 | extern crate glib; 9 | extern crate gtk; 10 | 11 | use gio::prelude::*; 12 | use glib::clone; 13 | use gtk::prelude::*; 14 | use gtk::Orientation::Vertical; 15 | use gtk::{ApplicationWindow, Button, Label, PackType}; 16 | 17 | use std::env::args; 18 | 19 | fn build_ui(application: >k::Application) { 20 | let vbox = gtk::Box::new(Vertical, 0); 21 | 22 | let plus_button = Button::with_label("+"); 23 | vbox.add(&plus_button); 24 | // Set some child properties. 25 | // These calls need to be added after the Widget is added to the Box. 26 | vbox.set_child_expand(&plus_button, true); 27 | vbox.set_child_fill(&plus_button, true); 28 | vbox.set_child_padding(&plus_button, 50); 29 | vbox.set_child_pack_type(&plus_button, PackType::End); 30 | 31 | let counter_label = Label::new(Some("0")); 32 | vbox.add(&counter_label); 33 | 34 | let minus_button = Button::with_label("-"); 35 | vbox.add(&minus_button); 36 | 37 | minus_button.connect_clicked(clone!(@weak counter_label => move |_| { 38 | let nb = counter_label.get_text() 39 | .parse() 40 | .unwrap_or(0); 41 | if nb > 0 { 42 | counter_label.set_text(&format!("{}", nb - 1)); 43 | } 44 | })); 45 | plus_button.connect_clicked(clone!(@weak counter_label => move |_| { 46 | let nb = counter_label.get_text() 47 | .parse() 48 | .unwrap_or(0); 49 | counter_label.set_text(&format!("{}", nb + 1)); 50 | })); 51 | 52 | let window = ApplicationWindow::new(application); 53 | 54 | window.set_default_size(200, 200); 55 | window.add(&vbox); 56 | 57 | window.show_all(); 58 | } 59 | 60 | fn main() { 61 | let application = gtk::Application::new( 62 | Some("com.github.gtk-rs.examples.child_properties"), 63 | Default::default(), 64 | ) 65 | .expect("Initialization failed..."); 66 | 67 | application.connect_activate(|app| { 68 | build_ui(app); 69 | }); 70 | 71 | application.run(&args().collect::>()); 72 | } 73 | -------------------------------------------------------------------------------- /src/bin/clipboard_simple.rs: -------------------------------------------------------------------------------- 1 | //! Simple clipboard example 2 | //! 3 | //! From https://developer.gnome.org/gtkmm-tutorial/stable/sec-clipboard-examples.html.en 4 | extern crate gdk; 5 | extern crate gio; 6 | extern crate gtk; 7 | 8 | use std::cell::RefCell; 9 | use std::env::args; 10 | 11 | use gio::prelude::*; 12 | use gtk::prelude::*; 13 | 14 | struct Ui { 15 | pub button_a1: gtk::ToggleButton, 16 | pub button_a2: gtk::ToggleButton, 17 | pub button_b1: gtk::ToggleButton, 18 | pub button_b2: gtk::ToggleButton, 19 | } 20 | 21 | // Declare a new thread local storage key 22 | thread_local!( 23 | static GLOBAL: RefCell> = RefCell::new(None) 24 | ); 25 | 26 | fn build_ui(application: >k::Application) { 27 | let window = gtk::ApplicationWindow::new(application); 28 | 29 | // Create the whole window 30 | window.set_title("gtk::Clipboard Simple Example"); 31 | window.connect_delete_event(|window, _| { 32 | window.close(); 33 | Inhibit(false) 34 | }); 35 | 36 | // Create the button grid 37 | let grid = gtk::Grid::new(); 38 | grid.set_row_homogeneous(true); 39 | grid.set_column_homogeneous(true); 40 | let button_a1 = gtk::ToggleButton::with_label("A1"); 41 | grid.attach(&button_a1, 0, 0, 1, 1); 42 | let button_a2 = gtk::ToggleButton::with_label("A2"); 43 | grid.attach(&button_a2, 1, 0, 1, 1); 44 | let button_b1 = gtk::ToggleButton::with_label("B1"); 45 | grid.attach(&button_b1, 0, 1, 1, 1); 46 | let button_b2 = gtk::ToggleButton::with_label("B2"); 47 | grid.attach(&button_b2, 1, 1, 1, 1); 48 | 49 | // Add in the action buttons 50 | let copy_button = gtk::Button::with_mnemonic("_Copy"); 51 | let paste_button = gtk::Button::with_mnemonic("_Paste"); 52 | let button_box = gtk::ButtonBox::new(gtk::Orientation::Horizontal); 53 | button_box.set_layout(gtk::ButtonBoxStyle::End); 54 | button_box.pack_start(©_button, false, false, 0); 55 | button_box.pack_start(&paste_button, false, false, 0); 56 | 57 | // Pack widgets into the window and display everything 58 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); 59 | vbox.set_spacing(6); 60 | let label = gtk::Label::new(Some( 61 | "Select cells in the grid, click Copy, then \ 62 | open a second instance of this example to try \ 63 | pasting the copied data.", 64 | )); 65 | vbox.pack_start(&label, true, true, 0); 66 | vbox.pack_start(&grid, true, true, 0); 67 | vbox.pack_start(&button_box, true, true, 0); 68 | window.add(&vbox); 69 | 70 | window.show_all(); 71 | 72 | // Save out UI in thread-local storage so we can use it in callbacks later 73 | GLOBAL.with(move |global| { 74 | *global.borrow_mut() = Some(Ui { 75 | button_a1: button_a1, 76 | button_a2: button_a2, 77 | button_b1: button_b1, 78 | button_b2: button_b2, 79 | }) 80 | }); 81 | 82 | // Attach signal handlers 83 | copy_button.connect_clicked(|_| { 84 | let mut s = String::new(); 85 | GLOBAL.with(|global| { 86 | if let Some(ref ui) = *global.borrow() { 87 | if ui.button_a1.get_active() { 88 | s.push_str("1"); 89 | } else { 90 | s.push_str("0"); 91 | } 92 | if ui.button_a2.get_active() { 93 | s.push_str("1"); 94 | } else { 95 | s.push_str("0"); 96 | } 97 | if ui.button_b1.get_active() { 98 | s.push_str("1"); 99 | } else { 100 | s.push_str("0"); 101 | } 102 | if ui.button_b2.get_active() { 103 | s.push_str("1"); 104 | } else { 105 | s.push_str("0"); 106 | } 107 | } 108 | }); 109 | let clipboard = gtk::Clipboard::get(&gdk::SELECTION_CLIPBOARD); 110 | clipboard.set_text(&s); 111 | }); 112 | paste_button.connect_clicked(|_| { 113 | let clipboard = gtk::Clipboard::get(&gdk::SELECTION_CLIPBOARD); 114 | clipboard.request_text(|_, t| { 115 | if t.is_some() { 116 | let t = t.unwrap(); 117 | if t.len() >= 4 { 118 | GLOBAL.with(|global| { 119 | if let Some(ref ui) = *global.borrow() { 120 | ui.button_a1.set_active(t.chars().nth(0).unwrap() == '1'); 121 | ui.button_a2.set_active(t.chars().nth(1).unwrap() == '1'); 122 | ui.button_b1.set_active(t.chars().nth(2).unwrap() == '1'); 123 | ui.button_b2.set_active(t.chars().nth(3).unwrap() == '1'); 124 | } 125 | }); 126 | } 127 | } 128 | }); 129 | }); 130 | } 131 | 132 | fn main() { 133 | let application = gtk::Application::new( 134 | Some("org.gtk-rs.example.clipboard_simple"), 135 | gio::ApplicationFlags::NON_UNIQUE, 136 | ) 137 | .expect("Initialization failed..."); 138 | 139 | application.connect_startup(|app| { 140 | build_ui(app); 141 | }); 142 | application.connect_activate(|_| {}); 143 | 144 | application.run(&args().collect::>()); 145 | } 146 | -------------------------------------------------------------------------------- /src/bin/clock.rs: -------------------------------------------------------------------------------- 1 | //! # Clock Sample 2 | //! 3 | //! This sample demonstrates how to use gtk::timeout_add_seconds to run 4 | //! a periodic task, implementing a clock in this example. 5 | 6 | extern crate chrono; 7 | extern crate gio; 8 | extern crate gtk; 9 | 10 | use chrono::Local; 11 | use gio::prelude::*; 12 | use gtk::prelude::*; 13 | use std::env::args; 14 | 15 | fn current_time() -> String { 16 | return format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); 17 | } 18 | 19 | fn build_ui(application: >k::Application) { 20 | let window = gtk::ApplicationWindow::new(application); 21 | 22 | window.set_title("First GTK+ Clock"); 23 | window.set_border_width(10); 24 | window.set_position(gtk::WindowPosition::Center); 25 | window.set_default_size(260, 40); 26 | 27 | let time = current_time(); 28 | let label = gtk::Label::new(None); 29 | label.set_text(&time); 30 | 31 | window.add(&label); 32 | 33 | window.show_all(); 34 | 35 | // we are using a closure to capture the label (else we could also use a normal function) 36 | let tick = move || { 37 | let time = current_time(); 38 | label.set_text(&time); 39 | // we could return glib::Continue(false) to stop our clock after this tick 40 | glib::Continue(true) 41 | }; 42 | 43 | // executes the closure once every second 44 | gtk::timeout_add_seconds(1, tick); 45 | } 46 | 47 | fn main() { 48 | let application = 49 | gtk::Application::new(Some("com.github.gtk-rs.examples.clock"), Default::default()) 50 | .expect("Initialization failed..."); 51 | 52 | application.connect_activate(|app| { 53 | build_ui(app); 54 | }); 55 | 56 | application.run(&args().collect::>()); 57 | } 58 | -------------------------------------------------------------------------------- /src/bin/clone_macro.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate glib; 3 | extern crate gtk; 4 | 5 | use std::cell::RefCell; 6 | use std::rc::Rc; 7 | 8 | use gio::prelude::*; 9 | use glib::clone; 10 | use gtk::{prelude::*, Application, ApplicationWindow, Button}; 11 | 12 | #[derive(Default)] 13 | struct State { 14 | started: bool, 15 | count: i32, 16 | } 17 | 18 | impl State { 19 | fn new() -> Self { 20 | Self { 21 | started: false, 22 | count: 0, 23 | } 24 | } 25 | } 26 | 27 | fn main() { 28 | let application = 29 | Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default()) 30 | .expect("failed to initialize GTK application"); 31 | 32 | let state = Rc::new(RefCell::new(State::new())); 33 | 34 | { 35 | let state2 = Rc::new(RefCell::new(State::new())); 36 | 37 | application.connect_activate(clone!(@weak state, @strong state2 => move |app| { 38 | state.borrow_mut().started = true; 39 | 40 | let window = ApplicationWindow::new(app); 41 | window.set_title("First GTK+ Program"); 42 | window.set_default_size(350, 70); 43 | 44 | let button = Button::with_label("Click me!"); 45 | button.connect_clicked(clone!(@weak state, @weak state2 => move |_| { 46 | let mut state = state.borrow_mut(); 47 | let mut state2 = state2.borrow_mut(); 48 | println!("Clicked (started: {}): {} - {}!", state.started, state.count, state2.count); 49 | state.count += 1; 50 | state2.count += 1; 51 | })); 52 | window.add(&button); 53 | 54 | window.show_all(); 55 | })); 56 | } 57 | 58 | application.run(&[]); 59 | } 60 | -------------------------------------------------------------------------------- /src/bin/communication_thread.rs: -------------------------------------------------------------------------------- 1 | //! Example on how to use a communication thread alongside with the GUI thread. 2 | //! 3 | //! Tricks used here: 4 | //! - Use a channel to show data on the GUI. 5 | //! - Run an async function on the GUI event loop. 6 | //! - Use a separate thread to handle incoming data and put it into a channel. 7 | 8 | use futures::{channel::mpsc, StreamExt}; 9 | use gio::prelude::*; 10 | use gtk::prelude::*; 11 | use gtk::{ApplicationWindow, Label}; 12 | use std::env::args; 13 | use std::thread; 14 | 15 | fn main() { 16 | let application = gtk::Application::new( 17 | Some("com.github.gtk-rs.examples.communication_thread"), 18 | Default::default(), 19 | ) 20 | .expect("Initialization failed..."); 21 | application.connect_activate(build_ui); 22 | application.run(&args().collect::>()); 23 | } 24 | 25 | fn build_ui(application: >k::Application) { 26 | let window = ApplicationWindow::new(application); 27 | let label = Label::new(None); 28 | window.add(&label); 29 | 30 | // Create a channel between communication thread and main event loop: 31 | let (sender, receiver) = mpsc::channel(1000); 32 | 33 | spawn_local_handler(label, receiver); 34 | start_communication_thread(sender); 35 | window.show_all(); 36 | } 37 | 38 | /// Spawn channel receive task on the main event loop. 39 | fn spawn_local_handler(label: gtk::Label, mut receiver: mpsc::Receiver) { 40 | let main_context = glib::MainContext::default(); 41 | let future = async move { 42 | while let Some(item) = receiver.next().await { 43 | label.set_text(&item); 44 | } 45 | }; 46 | main_context.spawn_local(future); 47 | } 48 | 49 | /// Spawn separate thread to handle communication. 50 | fn start_communication_thread(mut sender: mpsc::Sender) { 51 | // Note that blocking I/O with threads can be prevented 52 | // by using asynchronous code, which is often a better 53 | // choice. For the sake of this example, we showcase the 54 | // way to use a thread when there is no other option. 55 | 56 | thread::spawn(move || { 57 | let mut counter = 0; 58 | loop { 59 | // Instead of a counter, your application code will 60 | // block here on TCP or serial communications. 61 | let data = format!("Counter = {}!", counter); 62 | println!("Thread received data: {}", data); 63 | match sender.try_send(data) { 64 | Ok(_) => {} 65 | Err(err) => { 66 | if err.is_full() { 67 | println!("Data is produced too fast for GUI"); 68 | } else if err.is_disconnected() { 69 | println!("GUI stopped, stopping thread."); 70 | break; 71 | } 72 | } 73 | } 74 | counter += 1; 75 | thread::sleep(std::time::Duration::from_millis(100)); 76 | } 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /src/bin/css.rs: -------------------------------------------------------------------------------- 1 | //! # CSS example 2 | //! 3 | //! This sample demonstrates how to use CSS with gtk-rs. 4 | 5 | extern crate gdk; 6 | extern crate gio; 7 | extern crate glib; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | 13 | use std::env::args; 14 | 15 | // Basic CSS: we change background color, we set font color to black and we set it as bold. 16 | const STYLE: &str = " 17 | #entry1 { 18 | background-image: -gtk-gradient (linear, 19 | 0 0, 1 0, 20 | color-stop(0, #f00), 21 | color-stop(1, #0f0)); 22 | color: blue; 23 | font-weight: bold; 24 | } 25 | 26 | button { 27 | /* If we don't put it, the yellow background won't be visible */ 28 | background-image: none; 29 | } 30 | #label1:hover { 31 | transition: 500ms; 32 | color: red; 33 | background-color: yellow; 34 | } 35 | 36 | combobox button.combo box { 37 | padding: 5px; 38 | } 39 | combobox box arrow { 40 | -gtk-icon-source: none; 41 | border-left: 5px solid transparent; 42 | border-right: 5px solid transparent; 43 | border-top: 5px solid black; 44 | }"; 45 | 46 | fn build_ui(application: >k::Application) { 47 | let window = gtk::ApplicationWindow::new(application); 48 | 49 | window.set_title("CSS"); 50 | window.set_position(gtk::WindowPosition::Center); 51 | 52 | // The container container. 53 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); 54 | 55 | let label = gtk::Button::with_label("hover me!"); 56 | // We need to name it in order to be able to use its name as a CSS label to 57 | // apply CSS on it. 58 | gtk::WidgetExt::set_widget_name(&label, "label1"); 59 | 60 | let entry = gtk::Entry::new(); 61 | // We need to name it in order to apply CSS on it. 62 | gtk::WidgetExt::set_widget_name(&entry, "entry1"); 63 | entry.set_text("Some text"); 64 | 65 | let combo = gtk::ComboBoxText::new(); 66 | combo.append_text("option 1"); 67 | combo.append_text("option 2"); 68 | combo.append_text("option 3"); 69 | combo.set_active(Some(0)); 70 | 71 | vbox.add(&label); 72 | vbox.add(&entry); 73 | vbox.add(&combo); 74 | // Then we add the container inside our window. 75 | window.add(&vbox); 76 | 77 | application.connect_activate(move |_| { 78 | window.show_all(); 79 | }); 80 | } 81 | 82 | fn main() { 83 | let application = gtk::Application::new(Some("com.github.css"), gio::ApplicationFlags::empty()) 84 | .expect("Initialization failed..."); 85 | 86 | application.connect_startup(|app| { 87 | // The CSS "magic" happens here. 88 | let provider = gtk::CssProvider::new(); 89 | provider 90 | .load_from_data(STYLE.as_bytes()) 91 | .expect("Failed to load CSS"); 92 | // We give the CssProvided to the default screen so the CSS rules we added 93 | // can be applied to our window. 94 | gtk::StyleContext::add_provider_for_screen( 95 | &gdk::Screen::get_default().expect("Error initializing gtk css provider."), 96 | &provider, 97 | gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, 98 | ); 99 | 100 | // We build the application UI. 101 | build_ui(app); 102 | }); 103 | 104 | application.run(&args().collect::>()); 105 | } 106 | -------------------------------------------------------------------------------- /src/bin/drag_and_drop.rs: -------------------------------------------------------------------------------- 1 | //! Simple drag and drop example 2 | //! 3 | //! Ported over from example code: 4 | //! https://developer.gnome.org/gtkmm-tutorial/stable/sec-dnd-example.html.en 5 | 6 | extern crate gdk; 7 | extern crate gio; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | 13 | use std::env::args; 14 | 15 | fn build_ui(application: >k::Application) { 16 | // Configure button as drag source for text 17 | let button = gtk::Button::with_label("Drag here"); 18 | let targets = vec![ 19 | gtk::TargetEntry::new("STRING", gtk::TargetFlags::SAME_APP, 0), 20 | gtk::TargetEntry::new("text/plain", gtk::TargetFlags::SAME_APP, 0), 21 | ]; 22 | button.drag_source_set( 23 | gdk::ModifierType::MODIFIER_MASK, 24 | &targets, 25 | gdk::DragAction::COPY, 26 | ); 27 | button.connect_drag_data_get(|_, _, s, _, _| { 28 | let data = "I'm data!"; 29 | s.set_text(data); 30 | }); 31 | 32 | // Configure label as drag destination to receive text 33 | let label = gtk::Label::new(Some("Drop here")); 34 | label.drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY); 35 | label.connect_drag_data_received(|w, _, _, _, s, _, _| { 36 | w.set_text(&s.get_text().expect("Couldn't get text")); 37 | }); 38 | 39 | // Stack the button and label horizontally 40 | let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0); 41 | hbox.pack_start(&button, true, true, 0); 42 | hbox.pack_start(&label, true, true, 0); 43 | 44 | // Finish populating the window and display everything 45 | let window = gtk::ApplicationWindow::new(application); 46 | window.set_title("Simple Drag and Drop Example"); 47 | window.set_default_size(200, 100); 48 | window.add(&hbox); 49 | window.show_all(); 50 | } 51 | 52 | fn main() { 53 | let application = gtk::Application::new( 54 | Some("com.github.gtk-rs.examples.drag_and_drop"), 55 | Default::default(), 56 | ) 57 | .expect("Initialization failed..."); 58 | 59 | application.connect_activate(|app| { 60 | build_ui(app); 61 | }); 62 | 63 | application.run(&args().collect::>()); 64 | } 65 | -------------------------------------------------------------------------------- /src/bin/drag_and_drop_textview.rs: -------------------------------------------------------------------------------- 1 | //! More complex drag and drop example 2 | //! 3 | //! Displays a list of filenames when they're dropped on the textview widget. 4 | 5 | extern crate gdk; 6 | extern crate gio; 7 | extern crate gtk; 8 | 9 | use std::env::args; 10 | 11 | use gdk::DragAction; 12 | use gio::prelude::*; 13 | use gtk::prelude::*; 14 | use gtk::{DestDefaults, TargetFlags}; 15 | 16 | fn build_ui(application: >k::Application) { 17 | let window = gtk::ApplicationWindow::new(application); 18 | window.set_title("Drag and Drop Example with a TextView"); 19 | 20 | // Give a nice text description for the user 21 | let label = gtk::Label::new(Some("Drag files and/or folders onto the TextView below.")); 22 | 23 | // Create scrollable text view as our drag target 24 | let text_view = gtk::TextView::new(); 25 | text_view.set_wrap_mode(gtk::WrapMode::Word); 26 | text_view.set_cursor_visible(false); 27 | let scrolled_text_view = gtk::ScrolledWindow::new(gtk::NONE_ADJUSTMENT, gtk::NONE_ADJUSTMENT); 28 | scrolled_text_view.set_policy(gtk::PolicyType::Automatic, gtk::PolicyType::Automatic); 29 | scrolled_text_view.add(&text_view); 30 | 31 | // Configure the text view to accept URI lists from other applications. This allows 32 | // dragging files & folders from a file browser program onto the textview. 33 | let targets = vec![gtk::TargetEntry::new( 34 | "text/uri-list", 35 | TargetFlags::OTHER_APP, 36 | 0, 37 | )]; 38 | text_view.drag_dest_set(DestDefaults::HIGHLIGHT, &targets, DragAction::COPY); 39 | 40 | // Process any `drag-data-received` events received by the textview. These events include 41 | // the URL list we're looking for. 42 | text_view.connect_drag_data_received(|w, _, _, _, d, _, _| { 43 | // Get the text buffer for the TextView and clear it to make it ready to accept new text. 44 | let buffer = w.get_buffer().unwrap(); 45 | buffer.set_text(""); 46 | 47 | // Since we only accept `text/uri-list`s here, we don't need to check first, we can simply 48 | // iterate through all of the accepted URIs. 49 | for file in d.get_uris() { 50 | let file = gio::File::new_for_uri(&file); 51 | let display_name = if file.is_native() { 52 | file.get_path().unwrap().display().to_string() 53 | } else { 54 | file.get_uri().into() 55 | }; 56 | let bulleted_file_path = format!(" • {}\n", &display_name); 57 | // We make sure to always insert this at the end of the text buffer so they're in 58 | // order. 59 | buffer.insert_at_cursor(&bulleted_file_path); 60 | } 61 | }); 62 | 63 | // Pack widgets vertically. 64 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); 65 | vbox.pack_start(&label, false, false, 0); 66 | vbox.pack_start(&scrolled_text_view, true, true, 0); 67 | 68 | // Create a new window 69 | window.add(&vbox); 70 | window.show_all(); 71 | } 72 | 73 | fn main() { 74 | let application = gtk::Application::new( 75 | Some("com.github.gtk-rs.examples.drag_and_drop_textview"), 76 | Default::default(), 77 | ) 78 | .expect("Initialization failed..."); 79 | 80 | application.connect_activate(|app| { 81 | build_ui(app); 82 | }); 83 | 84 | application.run(&args().collect::>()); 85 | } 86 | -------------------------------------------------------------------------------- /src/bin/entry_completion.rs: -------------------------------------------------------------------------------- 1 | //! # Entry completion example 2 | //! 3 | //! This example demonstrates how to build a list of items and use them 4 | //! to autocomplete a field as the user types in something. 5 | 6 | extern crate gdk; 7 | extern crate gio; 8 | extern crate glib; 9 | extern crate gtk; 10 | 11 | use gio::prelude::*; 12 | use glib::clone; 13 | use gtk::prelude::*; 14 | 15 | use std::env::args; 16 | 17 | struct Data { 18 | description: String, 19 | } 20 | 21 | fn create_list_model() -> gtk::ListStore { 22 | let col_types: [glib::Type; 1] = [glib::Type::String]; 23 | 24 | let data: [Data; 4] = [ 25 | Data { 26 | description: "France".to_string(), 27 | }, 28 | Data { 29 | description: "Italy".to_string(), 30 | }, 31 | Data { 32 | description: "Sweden".to_string(), 33 | }, 34 | Data { 35 | description: "Switzerland".to_string(), 36 | }, 37 | ]; 38 | let store = gtk::ListStore::new(&col_types); 39 | let col_indices: [u32; 1] = [0]; 40 | for d in data.iter() { 41 | let values: [&dyn ToValue; 1] = [&d.description]; 42 | store.set(&store.append(), &col_indices, &values); 43 | } 44 | store 45 | } 46 | 47 | fn build_ui(application: >k::Application) { 48 | // create the main window 49 | let window = gtk::ApplicationWindow::new(application); 50 | window.set_title("Entry with autocompletion"); 51 | window.set_border_width(5); 52 | window.set_position(gtk::WindowPosition::Center); 53 | window.set_default_size(840, 480); 54 | 55 | // Create a title label 56 | let win_title = gtk::Label::new(None); 57 | win_title.set_markup("Which country would you like to spend a holiday in?"); 58 | 59 | // Create an EntryCompletion widget 60 | let completion_countries = gtk::EntryCompletion::new(); 61 | // Use the first (and only) column available to set the autocompletion text 62 | completion_countries.set_text_column(0); 63 | // how many keystrokes to wait before attempting to autocomplete? 64 | completion_countries.set_minimum_key_length(1); 65 | // whether the completions should be presented in a popup window 66 | completion_countries.set_popup_completion(true); 67 | 68 | // Create a ListStore of items 69 | // These will be the source for the autocompletion 70 | // as the user types into the field 71 | // For a more evolved example of ListStore see src/bin/list_store.rs 72 | let ls = create_list_model(); 73 | completion_countries.set_model(Some(&ls)); 74 | 75 | let input_field = gtk::Entry::new(); 76 | input_field.set_completion(Some(&completion_countries)); 77 | 78 | let row = gtk::Box::new(gtk::Orientation::Vertical, 5); 79 | row.add(&win_title); 80 | row.pack_start(&input_field, false, false, 10); 81 | 82 | // window.add(&win_title); 83 | window.add(&row); 84 | 85 | // show everything 86 | window.show_all(); 87 | } 88 | 89 | fn main() { 90 | let application = gtk::Application::new( 91 | Some("com.github.gtk-rs.examples.entry-completion"), 92 | Default::default(), 93 | ) 94 | .expect("Initialization failed..."); 95 | application.connect_activate(|app| { 96 | build_ui(app); 97 | }); 98 | 99 | // When activated, shuts down the application 100 | let quit = gio::SimpleAction::new("quit", None); 101 | quit.connect_activate(clone!(@weak application => move |_action, _parameter| { 102 | application.quit(); 103 | })); 104 | application.set_accels_for_action("app.quit", &["Q"]); 105 | application.add_action(&quit); 106 | 107 | // Run the application 108 | application.run(&args().collect::>()); 109 | } 110 | -------------------------------------------------------------------------------- /src/bin/gio_async_tls.rs: -------------------------------------------------------------------------------- 1 | use futures::prelude::*; 2 | use futures::task::{Context, Poll}; 3 | use gio::prelude::*; 4 | 5 | use std::io; 6 | use std::pin::Pin; 7 | 8 | use std::error::Error; 9 | 10 | async fn run() -> Result<(), Box> { 11 | // Connect to https://www.rust-lang.org 12 | let client = gio::SocketClient::new(); 13 | let connectable = gio::NetworkAddress::new("www.rust-lang.org", 443); 14 | 15 | let connection = client.connect_async_future(&connectable).await?; 16 | let connection = connection.downcast::().unwrap(); 17 | 18 | // Get the input/output streams and convert them to the AsyncRead and AsyncWrite adapters 19 | let ostream = connection 20 | .get_output_stream() 21 | .unwrap() 22 | .dynamic_cast::() 23 | .unwrap(); 24 | let write = ostream.into_async_write().unwrap(); 25 | 26 | let istream = connection 27 | .get_input_stream() 28 | .unwrap() 29 | .dynamic_cast::() 30 | .unwrap(); 31 | let read = istream.into_async_read().unwrap(); 32 | 33 | // Wrap both in our Connection struct and start the TLS handshake on it 34 | let connection = Connection { read, write }; 35 | let connector = async_tls::TlsConnector::new(); 36 | let mut connection = connector.connect("www.rust-lang.org", connection)?.await?; 37 | 38 | // Send the HTTP request 39 | connection 40 | .write_all(&b"GET / HTTP/1.1\r\nHost: www.rust-lang.org\r\nConnection: close\r\n\r\n"[..]) 41 | .await?; 42 | 43 | // And then read the response until the end 44 | let mut buffer = [0u8; 8192]; 45 | loop { 46 | let len = connection.read(&mut buffer[..]).await?; 47 | 48 | if len == 0 { 49 | break; 50 | } 51 | 52 | print!("{}", String::from_utf8_lossy(&buffer[..len])); 53 | } 54 | println!(); 55 | 56 | connection.close().await?; 57 | 58 | Ok(()) 59 | } 60 | 61 | // Wrapper type around the AsyncRead/Write adapters that provides both at once 62 | #[derive(Debug)] 63 | struct Connection { 64 | read: gio::InputStreamAsyncRead, 65 | write: gio::OutputStreamAsyncWrite, 66 | } 67 | 68 | // Proxy to the internal AsyncRead 69 | impl AsyncRead for Connection { 70 | fn poll_read( 71 | self: Pin<&mut Self>, 72 | cx: &mut Context<'_>, 73 | buf: &mut [u8], 74 | ) -> Poll> { 75 | Pin::new(&mut Pin::get_mut(self).read).poll_read(cx, buf) 76 | } 77 | } 78 | 79 | // Proxy to the internal AsyncWrite 80 | impl AsyncWrite for Connection { 81 | fn poll_write( 82 | self: Pin<&mut Self>, 83 | cx: &mut Context<'_>, 84 | buf: &[u8], 85 | ) -> Poll> { 86 | Pin::new(&mut Pin::get_mut(self).write).poll_write(cx, buf) 87 | } 88 | 89 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 90 | Pin::new(&mut Pin::get_mut(self).write).poll_close(cx) 91 | } 92 | 93 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 94 | Pin::new(&mut Pin::get_mut(self).write).poll_flush(cx) 95 | } 96 | } 97 | 98 | fn main() -> Result<(), Box> { 99 | // Get the default main context and run our async function on it 100 | let main_context = glib::MainContext::default(); 101 | main_context.block_on(run()) 102 | } 103 | -------------------------------------------------------------------------------- /src/bin/gio_futures.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | 3 | extern crate glib; 4 | 5 | extern crate gio; 6 | use gio::prelude::*; 7 | 8 | use std::str; 9 | 10 | use futures::prelude::*; 11 | 12 | // Throughout our chained futures, we convert all errors to strings 13 | // via map_err() and print them at the very end. 14 | // 15 | // Open the file for reading, and if that succeeds read the whole file from 16 | // the resulting input stream. 17 | fn read_and_print_file( 18 | file: &gio::File, 19 | ) -> impl Future> + std::marker::Unpin { 20 | file.read_async_future(glib::PRIORITY_DEFAULT) 21 | .map_err(|err| format!("Failed to open file: {}", err)) 22 | .and_then(|strm| read_and_print_chunks(strm)) 23 | } 24 | 25 | // Read the input stream in chunks of 64 bytes, always into the same buffer 26 | // without re-allocating it all the time. Continue until the end of the file 27 | // or an error happens. 28 | fn read_and_print_chunks( 29 | strm: gio::FileInputStream, 30 | ) -> impl Future> + std::marker::Unpin { 31 | let buf = vec![0; 64]; 32 | let idx = 0; 33 | 34 | // We use unfold() here, which takes some initialization data and a 35 | // closure that is returning an item and the next state, or None to 36 | // finish the stream 37 | futures::stream::unfold(Some((buf, idx)), move |buf_and_idx| { 38 | // If None was returned from the last iteration then the last iteration 39 | // was closing the input stream or an error happened, and now we only 40 | // have to finish the stream created by unfold(). 41 | // 42 | // Otherwise we got the buffer to read to and the index of the next line 43 | // from the previous iteration. 44 | let (buf, idx) = match buf_and_idx { 45 | None => { 46 | return futures::future::Either::Left(futures::future::ready(None)); 47 | } 48 | Some(buf_and_idx) => buf_and_idx, 49 | }; 50 | 51 | // Read and print the next chunk 52 | futures::future::Either::Right(read_and_print_next_chunk(&strm, buf, idx).map(move |res| { 53 | match res { 54 | // And error happened, return the error from this stream and then finish on the 55 | // next iteration. 56 | Err(err) => Some((Err(err), None)), 57 | // The input stream was closed, return Ok(()) from this stream and then finish on 58 | // the next iteration. 59 | Ok(None) => Some((Ok(()), None)), 60 | // A chunk was successfully read and printed, return Ok(()) from this stream and 61 | // then continue with the next iteration. 62 | Ok(Some(buf)) => Some((Ok(()), Some((buf, idx + 1)))), 63 | } 64 | })) 65 | }) 66 | // Convert the stream into a simple future that collects all items and 67 | // returns Ok(()), or short-circuits on the very first error and returns it 68 | .try_for_each(|_| futures::future::ok(())) 69 | } 70 | 71 | // Read the next chunk into the buffer and print it out, or return an error. If 72 | // the input stream is finished, close the stream. 73 | // 74 | // After reading successfully we return the buffer again so it can be used in the 75 | // next iteration. 76 | fn read_and_print_next_chunk( 77 | strm: &gio::FileInputStream, 78 | buf: Vec, 79 | idx: usize, 80 | ) -> impl Future>, String>> + std::marker::Unpin { 81 | let strm_clone = strm.clone(); 82 | strm.read_async_future(buf, glib::PRIORITY_DEFAULT) 83 | .map_err(|(_buf, err)| format!("Failed to read from stream: {}", err)) 84 | .and_then(move |(buf, len)| { 85 | println!("line {}: {:?}", idx, str::from_utf8(&buf[0..len]).unwrap()); 86 | 87 | // 0 is only returned when the input stream is finished, in which case 88 | // we drop the buffer and close the stream asynchronously. 89 | // 90 | // Otherwise we simply return the buffer again so it can be read into 91 | // in the next iteration. 92 | if len == 0 { 93 | futures::future::Either::Left( 94 | strm_clone 95 | .close_async_future(glib::PRIORITY_DEFAULT) 96 | .map_err(|err| format!("Failed to close stream: {}", err)) 97 | .map_ok(|_| None), 98 | ) 99 | } else { 100 | futures::future::Either::Right(futures::future::ok(Some(buf))) 101 | } 102 | }) 103 | } 104 | 105 | fn main() { 106 | let c = glib::MainContext::default(); 107 | let l = glib::MainLoop::new(Some(&c), false); 108 | 109 | c.push_thread_default(); 110 | 111 | let file = gio::File::new_for_path("Cargo.toml"); 112 | 113 | let l_clone = l.clone(); 114 | c.spawn_local( 115 | read_and_print_file(&file) 116 | // Once all is done we quit the main loop and in case of an 117 | // error first print that error. 118 | .map(move |res| { 119 | if let Err(err) = res { 120 | eprintln!("Got error: {}", err); 121 | } 122 | 123 | l_clone.quit(); 124 | }), 125 | ); 126 | 127 | l.run(); 128 | 129 | c.pop_thread_default(); 130 | } 131 | -------------------------------------------------------------------------------- /src/bin/gio_futures_await.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate glib; 3 | use gio::prelude::*; 4 | 5 | use futures::prelude::*; 6 | 7 | use std::str; 8 | 9 | // Throughout our chained futures, we convert all errors to strings 10 | // via map_err() return them directly. 11 | async fn read_file(file: gio::File) -> Result<(), String> { 12 | // Try to open the file. 13 | let strm = file 14 | .read_async_future(glib::PRIORITY_DEFAULT) 15 | .map_err(|err| format!("Failed to open file: {}", err)) 16 | .await?; 17 | 18 | // If opening the file succeeds, we asynchronously loop and 19 | // read the file in up to 64 byte chunks and re-use the same 20 | // vec for each read. 21 | let mut buf = vec![0; 64]; 22 | let mut idx = 0; 23 | 24 | loop { 25 | let (b, len) = strm 26 | .read_async_future(buf, glib::PRIORITY_DEFAULT) 27 | .map_err(|(_buf, err)| format!("Failed to read from stream: {}", err)) 28 | .await?; 29 | 30 | // Once 0 is returned, we know that we're done with reading, otherwise 31 | // loop again and read another chunk. 32 | if len == 0 { 33 | break; 34 | } 35 | 36 | buf = b; 37 | 38 | println!("line {}: {:?}", idx, str::from_utf8(&buf[0..len]).unwrap()); 39 | 40 | idx += 1; 41 | } 42 | 43 | // Asynchronously close the stream in the end. 44 | let _ = strm 45 | .close_async_future(glib::PRIORITY_DEFAULT) 46 | .map_err(|err| format!("Failed to close stream: {}", err)) 47 | .await?; 48 | 49 | Ok(()) 50 | } 51 | 52 | fn main() { 53 | let c = glib::MainContext::default(); 54 | let l = glib::MainLoop::new(Some(&c), false); 55 | 56 | c.push_thread_default(); 57 | 58 | let file = gio::File::new_for_path("Cargo.toml"); 59 | 60 | let l_clone = l.clone(); 61 | let future = async move { 62 | match read_file(file).await { 63 | Ok(()) => (), 64 | Err(err) => eprintln!("Got error: {}", err), 65 | } 66 | l_clone.quit(); 67 | }; 68 | 69 | c.spawn_local(future); 70 | 71 | l.run(); 72 | 73 | c.pop_thread_default(); 74 | } 75 | -------------------------------------------------------------------------------- /src/bin/grid.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | Grid example 8 | 9 | 10 | True 11 | False 12 | 13 | 14 | Button 1 15 | True 16 | True 17 | True 18 | 19 | 20 | 0 21 | 0 22 | 23 | 24 | 25 | 26 | Button 2 27 | True 28 | True 29 | True 30 | 31 | 32 | 1 33 | 0 34 | 35 | 36 | 37 | 38 | Button 3 39 | True 40 | True 41 | True 42 | 43 | 44 | 2 45 | 0 46 | 47 | 48 | 49 | 50 | Button 4 51 | True 52 | True 53 | True 54 | 55 | 56 | 3 57 | 0 58 | 59 | 60 | 61 | 62 | Button 5 63 | True 64 | True 65 | True 66 | 67 | 68 | 0 69 | 1 70 | 3 71 | 72 | 73 | 74 | 75 | Button 6 76 | Press to resize 77 | True 78 | True 79 | True 80 | 81 | 82 | 3 83 | 1 84 | 2 85 | 86 | 87 | 88 | 89 | Button 7 90 | Press to move 91 | True 92 | True 93 | True 94 | 95 | 96 | 0 97 | 2 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/bin/grid.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))] 2 | 3 | extern crate gio; 4 | extern crate glib; 5 | extern crate gtk; 6 | 7 | use gio::prelude::*; 8 | use glib::clone; 9 | use gtk::prelude::*; 10 | use gtk::{ApplicationWindow, Builder, Button, Grid}; 11 | 12 | use std::env::args; 13 | 14 | fn build_ui(application: >k::Application) { 15 | let glade_src = include_str!("grid.glade"); 16 | let builder = Builder::from_string(glade_src); 17 | 18 | let window: ApplicationWindow = builder.get_object("window").expect("Couldn't get window"); 19 | window.set_application(Some(application)); 20 | let grid: Grid = builder.get_object("grid").expect("Couldn't get grid"); 21 | let button6: Button = builder.get_object("button6").expect("Couldn't get button6"); 22 | button6.connect_clicked(clone!(@weak grid => move |button| { 23 | let height = grid.get_cell_height(button); 24 | let new_height = if height == 2 { 1 } else { 2 }; 25 | grid.set_cell_height(button, new_height); 26 | })); 27 | let button7: Button = builder.get_object("button7").expect("Couldn't get button7"); 28 | button7.connect_clicked(clone!(@weak grid => move |button| { 29 | let left_attach = grid.get_cell_left_attach(button); 30 | let new_left_attach = if left_attach == 2 { 0 } else { left_attach + 1 }; 31 | grid.set_cell_left_attach(button, new_left_attach); 32 | })); 33 | 34 | window.show_all(); 35 | } 36 | 37 | fn main() { 38 | let application = 39 | gtk::Application::new(Some("com.github.gtk-rs.examples.grid"), Default::default()) 40 | .expect("Initialization failed..."); 41 | 42 | application.connect_activate(|app| { 43 | build_ui(app); 44 | }); 45 | 46 | application.run(&args().collect::>()); 47 | } 48 | -------------------------------------------------------------------------------- /src/bin/gtktest.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 100 7 | 1 8 | 10 9 | 10 | 11 | 100 12 | 1 13 | 10 14 | 15 | 16 | False 17 | dialog 18 | Glade 19 | http://gtk-rs.org 20 | James T. Kirk 21 | Spock 22 | Leonard McCoy 23 | James T. Kirk 24 | 25 | 26 | False 27 | vertical 28 | 2 29 | 30 | 31 | False 32 | end 33 | 34 | 35 | False 36 | True 37 | end 38 | 0 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | True 49 | False 50 | edit-clear 51 | 52 | 53 | False 54 | Yeah a beautiful window with gtk ! 55 | center 56 | 57 | 58 | True 59 | False 60 | 10 61 | 0 62 | none 63 | 64 | 65 | True 66 | False 67 | 12 68 | 69 | 70 | True 71 | False 72 | 5 73 | vertical 74 | 10 75 | 76 | 77 | True 78 | True 79 | False 80 | True 81 | 82 | 83 | False 84 | 6 85 | end 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | False 98 | False 99 | 0 100 | 101 | 102 | 103 | 104 | False 105 | 16 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | False 118 | False 119 | 0 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | False 128 | True 129 | 0 130 | 131 | 132 | 133 | 134 | True 135 | False 136 | 10 137 | 138 | 139 | True 140 | True 141 | 142 | 143 | False 144 | True 145 | 0 146 | 147 | 148 | 149 | 150 | True 151 | True 152 | True 153 | 154 | 155 | False 156 | True 157 | 1 158 | 159 | 160 | 161 | 162 | checkbutton 163 | True 164 | True 165 | False 166 | 0 167 | True 168 | 169 | 170 | False 171 | True 172 | 2 173 | 174 | 175 | 176 | 177 | rust-lang.org 178 | True 179 | True 180 | True 181 | none 182 | https://www.rust-lang.org/ 183 | 184 | 185 | False 186 | True 187 | 3 188 | 189 | 190 | 191 | 192 | True 193 | True 194 | spin_adjustment 195 | 1 196 | if-valid 197 | 198 | 199 | False 200 | True 201 | 4 202 | 203 | 204 | 205 | 206 | False 207 | True 208 | 1 209 | 210 | 211 | 212 | 213 | True 214 | True 215 | scale_adjustment 216 | 1 217 | 218 | 219 | False 220 | True 221 | 2 222 | 223 | 224 | 225 | 226 | True 227 | False 228 | 37 229 | 100 230 | 231 | 232 | False 233 | True 234 | 3 235 | 236 | 237 | 238 | 239 | True 240 | False 241 | start 242 | 243 | 244 | button 245 | True 246 | True 247 | True 248 | image2 249 | 250 | 251 | False 252 | True 253 | 0 254 | 255 | 256 | 257 | 258 | Whattttt a button ! 259 | True 260 | True 261 | True 262 | 263 | 264 | False 265 | True 266 | 1 267 | 268 | 269 | 270 | 271 | About? 272 | True 273 | True 274 | True 275 | 276 | 277 | False 278 | True 279 | 2 280 | 281 | 282 | 283 | 284 | Choose a font! 285 | True 286 | True 287 | True 288 | 289 | 290 | False 291 | True 292 | 3 293 | 294 | 295 | 296 | 297 | Choose a recent one ! 298 | True 299 | True 300 | True 301 | 302 | 303 | False 304 | True 305 | 4 306 | 307 | 308 | 309 | 310 | file ? 311 | True 312 | True 313 | True 314 | 315 | 316 | False 317 | True 318 | 5 319 | 320 | 321 | 322 | 323 | App ? 324 | True 325 | True 326 | True 327 | 0.50999999046325684 328 | 329 | 330 | False 331 | True 332 | 6 333 | 334 | 335 | 336 | 337 | True 338 | True 339 | True 340 | Sans 12 341 | 342 | 343 | 344 | False 345 | True 346 | 7 347 | 348 | 349 | 350 | 351 | Toggle Me ! 352 | True 353 | True 354 | True 355 | 356 | 357 | False 358 | True 359 | 8 360 | 361 | 362 | 363 | 364 | True 365 | True 366 | True 367 | 368 | 369 | False 370 | True 371 | 9 372 | 373 | 374 | 375 | 376 | True 377 | True 378 | True 379 | none 380 | False 381 | audio-volume-muted 382 | audio-volume-high 383 | audio-volume-low 384 | audio-volume-medium 385 | 386 | 387 | + 388 | True 389 | True 390 | none 391 | 392 | 393 | 394 | 395 | - 396 | True 397 | True 398 | none 399 | 400 | 401 | 402 | 403 | False 404 | True 405 | 10 406 | 407 | 408 | 409 | 410 | False 411 | True 412 | 4 413 | 414 | 415 | 416 | 417 | True 418 | False 419 | 0.69999999999999996 420 | 421 | 422 | False 423 | True 424 | 5 425 | 426 | 427 | 428 | 429 | True 430 | False 431 | 432 | 433 | False 434 | True 435 | 6 436 | 437 | 438 | 439 | 440 | True 441 | False 442 | Yeah a wonderful label too ! 443 | 444 | 445 | False 446 | True 447 | 7 448 | 449 | 450 | 451 | 452 | True 453 | True 454 | An entry with a placeholder ! 455 | 456 | 457 | False 458 | True 459 | 8 460 | 461 | 462 | 463 | 464 | True 465 | False 466 | 467 | 468 | False 469 | True 470 | 9 471 | 472 | 473 | 474 | 475 | True 476 | True 477 | edit-find-symbolic 478 | False 479 | False 480 | An Entry with a placeholder ! 481 | 482 | 483 | False 484 | True 485 | 10 486 | 487 | 488 | 489 | 490 | True 491 | False 492 | False 493 | 494 | 495 | False 496 | True 497 | 11 498 | 499 | 500 | 501 | 502 | True 503 | False 504 | resources/eye.png 505 | 506 | 507 | False 508 | True 509 | 12 510 | 511 | 512 | 513 | 514 | True 515 | False 516 | etched-out 517 | 518 | 519 | False 520 | True 521 | 13 522 | 523 | 524 | 525 | 526 | True 527 | True 528 | 2016 529 | 4 530 | 5 531 | 532 | 533 | False 534 | True 535 | 14 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | True 545 | False 546 | frame1 547 | 548 | 549 | 550 | 551 | 552 | 553 | -------------------------------------------------------------------------------- /src/bin/gtktest.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))] 2 | 3 | extern crate gdk; 4 | extern crate gio; 5 | extern crate glib; 6 | extern crate gtk; 7 | 8 | use gio::prelude::*; 9 | use glib::clone; 10 | use gtk::prelude::*; 11 | use gtk::{ 12 | AboutDialog, AppChooserDialog, ApplicationWindow, Builder, Button, Dialog, Entry, 13 | FileChooserAction, FileChooserDialog, FontChooserDialog, RecentChooserDialog, ResponseType, 14 | Scale, SpinButton, Spinner, Switch, Window, 15 | }; 16 | 17 | use std::env::args; 18 | 19 | fn about_clicked(button: &Button, dialog: &AboutDialog) { 20 | if let Some(window) = button 21 | .get_toplevel() 22 | .and_then(|w| w.downcast::().ok()) 23 | { 24 | dialog.set_transient_for(Some(&window)); 25 | } 26 | 27 | // We only want to hide the dialog when it's closed and not completely destroy it 28 | // as otherwise we can't show it again a second time. 29 | dialog.connect_delete_event(|dialog, _| { 30 | dialog.hide(); 31 | gtk::Inhibit(true) 32 | }); 33 | 34 | println!("Authors: {:?}", dialog.get_authors()); 35 | println!("Artists: {:?}", dialog.get_artists()); 36 | println!("Documenters: {:?}", dialog.get_documenters()); 37 | 38 | dialog.show_all(); 39 | } 40 | 41 | fn build_ui(application: >k::Application) { 42 | println!( 43 | "Major: {}, Minor: {}", 44 | gtk::get_major_version(), 45 | gtk::get_minor_version() 46 | ); 47 | let glade_src = include_str!("gtktest.glade"); 48 | let builder = Builder::from_string(glade_src); 49 | 50 | let spinner: Spinner = builder.get_object("spinner").expect("Couldn't get spinner"); 51 | spinner.start(); 52 | 53 | let scale: Scale = builder.get_object("scale").expect("Couldn't get scale"); 54 | scale.connect_format_value(|scale, value| { 55 | let digits = scale.get_digits() as usize; 56 | format!("<{:.*}>", digits, value) 57 | }); 58 | 59 | let spin_button: SpinButton = builder 60 | .get_object("spin_button") 61 | .expect("Couldn't get spin_button"); 62 | spin_button.connect_input(|spin_button| { 63 | let text = spin_button.get_text(); 64 | println!("spin_button_input: \"{}\"", text); 65 | match text.parse::() { 66 | Ok(value) if value >= 90. => { 67 | println!("circular right"); 68 | Some(Ok(10.)) 69 | } 70 | Ok(value) if value <= 10. => { 71 | println!("circular left"); 72 | Some(Ok(90.)) 73 | } 74 | Ok(value) => Some(Ok(value)), 75 | Err(_) => Some(Err(())), 76 | } 77 | }); 78 | 79 | let window: ApplicationWindow = builder.get_object("window").expect("Couldn't get window"); 80 | window.set_application(Some(application)); 81 | 82 | let button: Button = builder.get_object("button").expect("Couldn't get button"); 83 | let entry: Entry = builder.get_object("entry").expect("Couldn't get entry"); 84 | 85 | button.connect_clicked(clone!(@weak window, @weak entry => move |_| { 86 | let dialog = Dialog::with_buttons(Some("Hello!"), 87 | Some(&window), 88 | gtk::DialogFlags::MODAL, 89 | &[("No", ResponseType::No), 90 | ("Yes", ResponseType::Yes), 91 | ("Custom", ResponseType::Other(0))]); 92 | 93 | dialog.connect_response(clone!(@weak entry => move |dialog, response| { 94 | entry.set_text(&format!("Clicked {}", response)); 95 | dialog.close(); 96 | })); 97 | dialog.show_all(); 98 | })); 99 | 100 | let button_font: Button = builder 101 | .get_object("button_font") 102 | .expect("Couldn't get button_font"); 103 | button_font.connect_clicked(clone!(@weak window => move |_| { 104 | let dialog = FontChooserDialog::new(Some("Font chooser test"), Some(&window)); 105 | 106 | dialog.connect_response(|dialog, _| dialog.close()); 107 | dialog.show_all(); 108 | })); 109 | 110 | let button_recent: Button = builder 111 | .get_object("button_recent") 112 | .expect("Couldn't get button_recent"); 113 | button_recent.connect_clicked(clone!(@weak window => move |_| { 114 | let dialog = RecentChooserDialog::new(Some("Recent chooser test"), Some(&window)); 115 | dialog.add_buttons(&[ 116 | ("Ok", ResponseType::Ok), 117 | ("Cancel", ResponseType::Cancel) 118 | ]); 119 | 120 | dialog.connect_response(|dialog, _| dialog.close()); 121 | dialog.show_all(); 122 | })); 123 | 124 | let file_button: Button = builder 125 | .get_object("file_button") 126 | .expect("Couldn't get file_button"); 127 | file_button.connect_clicked(clone!(@weak window => move |_| { 128 | // entry.set_text("Clicked!"); 129 | let dialog = FileChooserDialog::new(Some("Choose a file"), Some(&window), 130 | FileChooserAction::Open); 131 | dialog.add_buttons(&[ 132 | ("Open", ResponseType::Ok), 133 | ("Cancel", ResponseType::Cancel) 134 | ]); 135 | 136 | dialog.set_select_multiple(true); 137 | 138 | dialog.connect_response(|dialog, response| { 139 | if response == ResponseType::Ok { 140 | let files = dialog.get_filenames(); 141 | println!("Files: {:?}", files); 142 | } 143 | dialog.close(); 144 | }); 145 | dialog.show_all(); 146 | })); 147 | 148 | let app_button: Button = builder 149 | .get_object("app_button") 150 | .expect("Couldn't get app_button"); 151 | app_button.connect_clicked(clone!(@weak window => move |_| { 152 | // entry.set_text("Clicked!"); 153 | let dialog = AppChooserDialog::new_for_content_type(Some(&window), 154 | gtk::DialogFlags::MODAL, 155 | "sh"); 156 | 157 | dialog.connect_response(|dialog, _| dialog.close()); 158 | dialog.show_all(); 159 | })); 160 | 161 | let switch: Switch = builder.get_object("switch").expect("Couldn't get switch"); 162 | switch.connect_changed_active(clone!(@weak entry => move |switch| { 163 | if switch.get_active() { 164 | entry.set_text("Switch On"); 165 | } else { 166 | entry.set_text("Switch Off"); 167 | } 168 | })); 169 | 170 | let button_about: Button = builder 171 | .get_object("button_about") 172 | .expect("Couldn't get button_about"); 173 | let dialog: AboutDialog = builder.get_object("dialog").expect("Couldn't get dialog"); 174 | button_about.connect_clicked(move |x| about_clicked(x, &dialog)); 175 | 176 | window.connect_key_press_event( 177 | clone!(@weak entry => @default-return Inhibit(false), move |_, key| { 178 | let keyval = key.get_keyval(); 179 | let keystate = key.get_state(); 180 | 181 | println!("key pressed: {} / {:?}", keyval, keystate); 182 | println!("text: {}", entry.get_text()); 183 | 184 | if keystate.intersects(gdk::ModifierType::CONTROL_MASK) { 185 | println!("You pressed Ctrl!"); 186 | } 187 | 188 | Inhibit(false) 189 | }), 190 | ); 191 | 192 | window.show_all(); 193 | } 194 | 195 | fn main() { 196 | let application = gtk::Application::new( 197 | Some("com.github.gtk-rs.examples.gtktest"), 198 | Default::default(), 199 | ) 200 | .expect("Initialization failed..."); 201 | 202 | application.connect_activate(|app| { 203 | build_ui(app); 204 | }); 205 | 206 | application.run(&args().collect::>()); 207 | } 208 | -------------------------------------------------------------------------------- /src/bin/iconview_example.rs: -------------------------------------------------------------------------------- 1 | //! # IconView Sample 2 | //! 3 | //! This sample demonstrates how to create a toplevel `window`, set its title, size and 4 | //! position, how to add a `IconView` to this `window` and how to set `model` of the `IconView` 5 | //! 6 | //! A Gtk.IconView is a widget that displays a collection of icons in a grid view. 7 | //! It supports features such as drag and drop, multiple selections and item reordering. 8 | //! Similarly to Gtk.TreeView, Gtk.IconView uses a Gtk.ListStore for its model. 9 | //! 10 | //! Instead of using cell renderers, Gtk.IconView requires that one of the columns in its 11 | //! Gtk.ListStore contains GdkPixbuf.Pixbuf objects. 12 | //! 13 | //! The example is using icons from the current icon theme. To view all icons and their names please 14 | //! install gtk3-icon-browser: https://developer.gnome.org/gtk3/stable/gtk3-icon-browser.html 15 | 16 | extern crate gio; 17 | extern crate gtk; 18 | 19 | use gio::prelude::*; 20 | use gtk::prelude::*; 21 | 22 | use std::env::args; 23 | use std::process; 24 | 25 | // Convenience Enum for IconView column types 26 | enum IconViewColumnType { 27 | TextColumn = 0, 28 | PixbufColumn = 1, 29 | } 30 | 31 | fn create_list_store_model() -> gtk::ListStore { 32 | // Initialize array of icon names, these can be found using gtk3-icon-browser app 33 | let icons: [&'static str; 3] = ["edit-cut", "edit-paste", "edit-copy"]; 34 | 35 | // Initialize an array of column types for ListStore object. Here we say that the first item 36 | // must always be of glib::Type String and the second item is of glib::Type Pixbuf. 37 | let col_types: [glib::Type; 2] = [glib::Type::String, gdk_pixbuf::Pixbuf::static_type()]; 38 | let icon_view_model = gtk::ListStore::new(&col_types); 39 | 40 | // IconTheme provides a facility for looking up icons by name and size. 41 | // 42 | // Get default icon theme 43 | let icon_theme: Option = gtk::IconTheme::get_default(); 44 | if let Some(it) = icon_theme { 45 | for x in &icons { 46 | // Looks up an icon in an icon theme, scales it to the given size and renders it into 47 | // a pixbuf. 48 | let result = it.load_icon(x, 64, gtk::IconLookupFlags::empty()); 49 | match result { 50 | Ok(r) => { 51 | // Notice how we specified the first column to be Text and second to be Pixbuf 52 | // just like in col_types var. 53 | // 54 | // The values also follow the same order, &[&String::from("Label"), &r]. 55 | // First item is text, second is pixbuf 56 | icon_view_model.insert_with_values( 57 | None, 58 | &[ 59 | IconViewColumnType::TextColumn as u32, 60 | IconViewColumnType::PixbufColumn as u32, 61 | ], 62 | &[&String::from("Label"), &r], 63 | ); 64 | } 65 | Err(err) => { 66 | println!("Error: {}", err); 67 | process::exit(1); 68 | } 69 | } 70 | } 71 | } 72 | 73 | return icon_view_model; 74 | } 75 | 76 | fn build_ui(application: >k::Application) { 77 | let window = gtk::ApplicationWindow::new(application); 78 | 79 | window.set_title("IconView Example"); 80 | window.set_border_width(10); 81 | window.set_position(gtk::WindowPosition::Center); 82 | window.set_default_size(350, 70); 83 | 84 | let icon_view = gtk::IconView::new(); 85 | icon_view.set_item_padding(0); 86 | icon_view.set_columns(3); 87 | icon_view.set_column_spacing(0); 88 | // User can select only one item at a time 89 | icon_view.set_selection_mode(gtk::SelectionMode::Single); 90 | 91 | // Create a model for our IconView 92 | let icon_view_model = create_list_store_model(); 93 | // Set IconView model 94 | icon_view.set_model(Some(&icon_view_model)); 95 | 96 | // And finally set text column and pixbuf column using enum 97 | icon_view.set_text_column(IconViewColumnType::TextColumn as i32); 98 | icon_view.set_pixbuf_column(IconViewColumnType::PixbufColumn as i32); 99 | 100 | window.add(&icon_view); 101 | window.show_all(); 102 | } 103 | 104 | fn main() { 105 | let application = gtk::Application::new( 106 | Some("com.github.gtk-rs.examples.iconview_example"), 107 | Default::default(), 108 | ) 109 | .expect("Initialization failed..."); 110 | 111 | application.connect_activate(|app| { 112 | build_ui(app); 113 | }); 114 | 115 | application.run(&args().collect::>()); 116 | } 117 | -------------------------------------------------------------------------------- /src/bin/list_store.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate gtk; 3 | 4 | use gio::prelude::*; 5 | use gtk::prelude::*; 6 | 7 | use std::env::args; 8 | use std::rc::Rc; 9 | 10 | #[derive(Debug)] 11 | #[repr(i32)] 12 | enum Columns { 13 | Fixed = 0, 14 | Number, 15 | Severity, 16 | Description, 17 | Pulse, 18 | Icon, 19 | Active, 20 | Sensitive, 21 | } 22 | 23 | fn build_ui(application: >k::Application) { 24 | let window = gtk::ApplicationWindow::new(application); 25 | 26 | window.set_title("List Store"); 27 | window.set_border_width(10); 28 | window.set_position(gtk::WindowPosition::Center); 29 | window.set_default_size(280, 250); 30 | 31 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 8); 32 | window.add(&vbox); 33 | 34 | let label = gtk::Label::new(Some( 35 | "This is the bug list (note: not based on real data, it would be \ 36 | nice to have a nice ODBC interface to bugzilla or so, though).", 37 | )); 38 | vbox.add(&label); 39 | 40 | let sw = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); 41 | sw.set_shadow_type(gtk::ShadowType::EtchedIn); 42 | sw.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic); 43 | vbox.add(&sw); 44 | 45 | let model = Rc::new(create_model()); 46 | let treeview = gtk::TreeView::with_model(&*model); 47 | treeview.set_vexpand(true); 48 | treeview.set_search_column(Columns::Description as i32); 49 | 50 | sw.add(&treeview); 51 | 52 | add_columns(&model, &treeview); 53 | 54 | window.show_all(); 55 | 56 | let model = model.clone(); 57 | timeout_add(80, move || spinner_timeout(&model)); 58 | } 59 | 60 | struct Data { 61 | fixed: bool, 62 | number: u32, 63 | severity: String, 64 | description: String, 65 | } 66 | 67 | fn create_model() -> gtk::ListStore { 68 | let col_types: [glib::Type; 8] = [ 69 | glib::Type::Bool, 70 | glib::Type::U32, 71 | glib::Type::String, 72 | glib::Type::String, 73 | glib::Type::U32, 74 | glib::Type::String, 75 | glib::Type::Bool, 76 | glib::Type::Bool, 77 | ]; 78 | 79 | let data: [Data; 14] = [ 80 | Data { 81 | fixed: false, 82 | number: 60482, 83 | severity: "Normal".to_string(), 84 | description: "scrollable notebooks and hidden tabs".to_string(), 85 | }, 86 | Data { 87 | fixed: false, 88 | number: 60620, 89 | severity: "Critical".to_string(), 90 | description: "gdk_surface_clear_area (gdksurface-win32.c) is not thread-safe" 91 | .to_string(), 92 | }, 93 | Data { 94 | fixed: false, 95 | number: 50214, 96 | severity: "Major".to_string(), 97 | description: "Xft support does not clean up correctly".to_string(), 98 | }, 99 | Data { 100 | fixed: true, 101 | number: 52877, 102 | severity: "Major".to_string(), 103 | description: "GtkFileSelection needs a refresh method. ".to_string(), 104 | }, 105 | Data { 106 | fixed: false, 107 | number: 56070, 108 | severity: "Normal".to_string(), 109 | description: "Can't click button after setting in sensitive".to_string(), 110 | }, 111 | Data { 112 | fixed: true, 113 | number: 56355, 114 | severity: "Normal".to_string(), 115 | description: "GtkLabel - Not all changes propagate correctly".to_string(), 116 | }, 117 | Data { 118 | fixed: false, 119 | number: 50055, 120 | severity: "Normal".to_string(), 121 | description: "Rework width/height computations for TreeView".to_string(), 122 | }, 123 | Data { 124 | fixed: false, 125 | number: 58278, 126 | severity: "Normal".to_string(), 127 | description: "gtk_dialog_set_response_sensitive () doesn't work".to_string(), 128 | }, 129 | Data { 130 | fixed: false, 131 | number: 55767, 132 | severity: "Normal".to_string(), 133 | description: "Getters for all setters".to_string(), 134 | }, 135 | Data { 136 | fixed: false, 137 | number: 56925, 138 | severity: "Normal".to_string(), 139 | description: "Gtkcalender size".to_string(), 140 | }, 141 | Data { 142 | fixed: false, 143 | number: 56221, 144 | severity: "Normal".to_string(), 145 | description: "Selectable label needs right-click copy menu".to_string(), 146 | }, 147 | Data { 148 | fixed: true, 149 | number: 50939, 150 | severity: "Normal".to_string(), 151 | description: "Add shift clicking to GtkTextView".to_string(), 152 | }, 153 | Data { 154 | fixed: false, 155 | number: 6112, 156 | severity: "Normal".to_string(), 157 | description: "netscape-like collapsable toolbars".to_string(), 158 | }, 159 | Data { 160 | fixed: false, 161 | number: 1, 162 | severity: "Normal".to_string(), 163 | description: "First bug :=)".to_string(), 164 | }, 165 | ]; 166 | 167 | let store = gtk::ListStore::new(&col_types); 168 | 169 | let col_indices: [u32; 8] = [0, 1, 2, 3, 4, 5, 6, 7]; 170 | 171 | for (d_idx, d) in data.iter().enumerate() { 172 | let icon_name = if d_idx == 1 || d_idx == 3 { 173 | "battery-caution-charging-symbolic" 174 | } else { 175 | "" 176 | }; 177 | 178 | let sensitive = d_idx != 3; 179 | 180 | let values: [&dyn ToValue; 8] = [ 181 | &d.fixed, 182 | &d.number, 183 | &d.severity, 184 | &d.description, 185 | &0u32, 186 | &icon_name, 187 | &false, 188 | &sensitive, 189 | ]; 190 | store.set(&store.append(), &col_indices, &values); 191 | } 192 | 193 | store 194 | } 195 | 196 | fn fixed_toggled>( 197 | model: >k::ListStore, 198 | _w: &W, 199 | path: gtk::TreePath, 200 | ) { 201 | let iter = model.get_iter(&path).unwrap(); 202 | let mut fixed = model 203 | .get_value(&iter, Columns::Fixed as i32) 204 | .get_some::() 205 | .unwrap_or_else(|err| { 206 | panic!( 207 | "ListStore value for {:?} at path {}: {}", 208 | Columns::Fixed, 209 | path, 210 | err 211 | ) 212 | }); 213 | fixed = !fixed; 214 | model.set_value(&iter, Columns::Fixed as u32, &fixed.to_value()); 215 | } 216 | 217 | fn add_columns(model: &Rc, treeview: >k::TreeView) { 218 | // Column for fixed toggles 219 | { 220 | let renderer = gtk::CellRendererToggle::new(); 221 | let model_clone = model.clone(); 222 | renderer.connect_toggled(move |w, path| fixed_toggled(&model_clone, w, path)); 223 | let column = gtk::TreeViewColumn::new(); 224 | column.pack_start(&renderer, true); 225 | column.set_title("Fixed?"); 226 | column.add_attribute(&renderer, "active", Columns::Fixed as i32); 227 | column.set_sizing(gtk::TreeViewColumnSizing::Fixed); 228 | column.set_fixed_width(50); 229 | treeview.append_column(&column); 230 | } 231 | 232 | // Column for bug numbers 233 | { 234 | let renderer = gtk::CellRendererText::new(); 235 | let column = gtk::TreeViewColumn::new(); 236 | column.pack_start(&renderer, true); 237 | column.set_title("Bug number"); 238 | column.add_attribute(&renderer, "text", Columns::Number as i32); 239 | column.set_sort_column_id(Columns::Number as i32); 240 | treeview.append_column(&column); 241 | } 242 | 243 | // Column for severities 244 | { 245 | let renderer = gtk::CellRendererText::new(); 246 | let column = gtk::TreeViewColumn::new(); 247 | column.pack_start(&renderer, true); 248 | column.set_title("Severity"); 249 | column.add_attribute(&renderer, "text", Columns::Severity as i32); 250 | column.set_sort_column_id(Columns::Severity as i32); 251 | treeview.append_column(&column); 252 | } 253 | 254 | // Column for description 255 | { 256 | let renderer = gtk::CellRendererText::new(); 257 | let column = gtk::TreeViewColumn::new(); 258 | column.pack_start(&renderer, true); 259 | column.set_title("Description"); 260 | column.add_attribute(&renderer, "text", Columns::Description as i32); 261 | column.set_sort_column_id(Columns::Description as i32); 262 | treeview.append_column(&column); 263 | } 264 | 265 | // Column for spinner 266 | { 267 | let renderer = gtk::CellRendererSpinner::new(); 268 | let column = gtk::TreeViewColumn::new(); 269 | column.pack_start(&renderer, true); 270 | column.set_title("Spinning"); 271 | column.add_attribute(&renderer, "pulse", Columns::Pulse as i32); 272 | column.add_attribute(&renderer, "active", Columns::Active as i32); 273 | treeview.append_column(&column); 274 | } 275 | 276 | // Column for symbolic icon 277 | { 278 | let renderer = gtk::CellRendererPixbuf::new(); 279 | let column = gtk::TreeViewColumn::new(); 280 | column.pack_start(&renderer, true); 281 | column.set_title("Symbolic icon"); 282 | column.add_attribute(&renderer, "icon-name", Columns::Icon as i32); 283 | column.add_attribute(&renderer, "sensitive", Columns::Sensitive as i32); 284 | column.set_sort_column_id(Columns::Icon as i32); 285 | treeview.append_column(&column); 286 | } 287 | } 288 | 289 | fn spinner_timeout(model: >k::ListStore) -> Continue { 290 | let iter = model.get_iter_first().unwrap(); 291 | let pulse = model 292 | .get_value(&iter, Columns::Pulse as i32) 293 | .get_some::() 294 | .unwrap_or_else(|err| { 295 | panic!( 296 | "ListStore value for {:?} at first entry: {}", 297 | Columns::Pulse, 298 | err 299 | ) 300 | }) 301 | .wrapping_add(1); 302 | 303 | model.set_value(&iter, Columns::Pulse as i32 as u32, &pulse.to_value()); 304 | model.set_value(&iter, Columns::Active as i32 as u32, &true.to_value()); 305 | 306 | Continue(true) 307 | } 308 | 309 | fn main() { 310 | let application = gtk::Application::new( 311 | Some("com.github.gtk-rs.examples.list-store"), 312 | Default::default(), 313 | ) 314 | .expect("Initialization failed..."); 315 | 316 | application.connect_startup(|app| { 317 | build_ui(app); 318 | }); 319 | 320 | application.connect_activate(|_| {}); 321 | 322 | application.run(&args().collect::>()); 323 | } 324 | -------------------------------------------------------------------------------- /src/bin/listbox_model.rs: -------------------------------------------------------------------------------- 1 | //! # ListBox and ListModel Sample 2 | //! 3 | //! This sample demonstrates how to use gtk::ListBox in combination with 4 | //! gio::ListStore as a model with a custom row type. 5 | //! 6 | //! It sets up a gtk::ListBox containing, per row, a label, spinbutton and 7 | //! an edit button. The edit button allows to edit the underlying data structure 8 | //! and changes are taking place immediately in the listbox by making use of GObject 9 | //! property bindings. 10 | //! 11 | //! In addition it is possible to add new rows and delete old ones. 12 | 13 | #[macro_use] 14 | extern crate glib; 15 | extern crate gio; 16 | extern crate gtk; 17 | 18 | use gio::prelude::*; 19 | use gtk::prelude::*; 20 | 21 | use gtk::ResponseType; 22 | 23 | use std::env::args; 24 | 25 | use row_data::RowData; 26 | 27 | fn build_ui(application: >k::Application) { 28 | let window = gtk::ApplicationWindow::new(application); 29 | 30 | window.set_title("ListBox Model Sample"); 31 | window.set_border_width(10); 32 | window.set_position(gtk::WindowPosition::Center); 33 | window.set_default_size(320, 480); 34 | 35 | let vbox = gtk::Box::new(gtk::Orientation::Vertical, 5); 36 | 37 | // Create our list store and specify that the type stored in the 38 | // list should be the RowData GObject we define at the bottom 39 | let model = gio::ListStore::new(RowData::static_type()); 40 | 41 | // And then create the UI part, the listbox and bind the list store 42 | // model to it. Whenever the UI needs to show a new row, e.g. because 43 | // it was notified that the model changed, it will call the callback 44 | // with the corresponding item from the model and will ask for a new 45 | // gtk::ListBoxRow that should be displayed. 46 | // 47 | // The gtk::ListBoxRow can contain any possible widgets. 48 | let listbox = gtk::ListBox::new(); 49 | listbox.bind_model(Some(&model), 50 | clone!(@weak window => @default-panic, move |item| { 51 | let box_ = gtk::ListBoxRow::new(); 52 | let item = item.downcast_ref::().expect("Row data is of wrong type"); 53 | 54 | let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5); 55 | 56 | // Create the label and spin button that shows the two values 57 | // of the item. We bind the properties for the two values to the 58 | // corresponding properties of the widgets so that they are automatically 59 | // updated whenever the item is changing. By specifying SYNC_CREATE the 60 | // widget will automatically get the initial value of the item set. 61 | // 62 | // In case of the spin button the binding is bidirectional, that is any 63 | // change of value in the spin button will be automatically reflected in 64 | // the item. 65 | let label = gtk::Label::new(None); 66 | item.bind_property("name", &label, "label") 67 | .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE) 68 | .build(); 69 | hbox.pack_start(&label, true, true, 0); 70 | 71 | let spin_button = gtk::SpinButton::with_range(0.0, 100.0, 1.0); 72 | item.bind_property("count", &spin_button, "value") 73 | .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE | glib::BindingFlags::BIDIRECTIONAL) 74 | .build(); 75 | hbox.pack_start(&spin_button, false, false, 0); 76 | 77 | // When the edit button is clicked, a new modal dialog is created for editing 78 | // the corresponding row 79 | let edit_button = gtk::Button::with_label("Edit"); 80 | edit_button.connect_clicked(clone!(@weak window, @strong item => move |_| { 81 | let dialog = gtk::Dialog::with_buttons(Some("Edit Item"), Some(&window), gtk::DialogFlags::MODAL, 82 | &[("Close", ResponseType::Close)]); 83 | dialog.set_default_response(ResponseType::Close); 84 | dialog.connect_response(|dialog, _| dialog.close()); 85 | 86 | let content_area = dialog.get_content_area(); 87 | 88 | // Similarly to the label and spin button inside the listbox, the text entry 89 | // and spin button in the edit dialog are connected via property bindings to 90 | // the item. Any changes will be immediately reflected inside the item and 91 | // by the listbox 92 | let entry = gtk::Entry::new(); 93 | item.bind_property("name", &entry, "text") 94 | .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE | glib::BindingFlags::BIDIRECTIONAL) 95 | .build(); 96 | 97 | // Activating the entry (enter) will send response `ResponseType::Close` to the dialog 98 | entry.connect_activate(clone!(@weak dialog => move |_| { 99 | dialog.response(ResponseType::Close); 100 | })); 101 | content_area.add(&entry); 102 | 103 | let spin_button = gtk::SpinButton::with_range(0.0, 100.0, 1.0); 104 | item.bind_property("count", &spin_button, "value") 105 | .flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE | glib::BindingFlags::BIDIRECTIONAL) 106 | .build(); 107 | content_area.add(&spin_button); 108 | 109 | dialog.show_all(); 110 | })); 111 | hbox.pack_start(&edit_button, false, false, 0); 112 | 113 | box_.add(&hbox); 114 | 115 | // When a row is activated (select + enter) we simply emit the clicked 116 | // signal on the corresponding edit button to open the edit dialog 117 | box_.connect_activate(clone!(@weak edit_button => move |_| { 118 | edit_button.emit_clicked(); 119 | })); 120 | 121 | box_.show_all(); 122 | 123 | box_.upcast::() 124 | })); 125 | 126 | let scrolled_window = gtk::ScrolledWindow::new(gtk::NONE_ADJUSTMENT, gtk::NONE_ADJUSTMENT); 127 | scrolled_window.add(&listbox); 128 | 129 | let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5); 130 | 131 | // The add button opens a new dialog which is basically the same as the edit 132 | // dialog, except that we don't have a corresponding item yet at that point 133 | // and only create it once the Ok button in the dialog is clicked, and only 134 | // then add it to the model. Once added to the model, it will immediately 135 | // appear in the listbox UI 136 | let add_button = gtk::Button::with_label("Add"); 137 | add_button.connect_clicked(clone!(@weak window, @weak model => move |_| { 138 | let dialog = gtk::Dialog::with_buttons(Some("Add Item"), Some(&window), gtk::DialogFlags::MODAL, 139 | &[("Ok", ResponseType::Ok), ("Cancel", ResponseType::Cancel)]); 140 | dialog.set_default_response(ResponseType::Ok); 141 | 142 | let content_area = dialog.get_content_area(); 143 | 144 | let entry = gtk::Entry::new(); 145 | entry.connect_activate(clone!(@weak dialog => move |_| { 146 | dialog.response(ResponseType::Ok); 147 | })); 148 | content_area.add(&entry); 149 | 150 | let spin_button = gtk::SpinButton::with_range(0.0, 100.0, 1.0); 151 | content_area.add(&spin_button); 152 | 153 | dialog.connect_response(clone!(@weak model, @weak entry, @weak spin_button => move |dialog, resp| { 154 | let text = entry.get_text(); 155 | if !text.is_empty() && resp == ResponseType::Ok { 156 | model.append(&RowData::new(&text, spin_button.get_value() as u32)); 157 | } 158 | dialog.close(); 159 | })); 160 | 161 | dialog.show_all(); 162 | })); 163 | 164 | hbox.add(&add_button); 165 | 166 | // Via the delete button we delete the item from the model that 167 | // is at the index of the selected row. Also deleting from the 168 | // model is immediately reflected in the listbox. 169 | let delete_button = gtk::Button::with_label("Delete"); 170 | delete_button.connect_clicked(clone!(@weak model, @weak listbox => move |_| { 171 | let selected = listbox.get_selected_row(); 172 | 173 | if let Some(selected) = selected { 174 | let idx = selected.get_index(); 175 | model.remove(idx as u32); 176 | } 177 | })); 178 | hbox.add(&delete_button); 179 | 180 | vbox.pack_start(&hbox, false, false, 0); 181 | vbox.pack_start(&scrolled_window, true, true, 0); 182 | 183 | window.add(&vbox); 184 | 185 | for i in 0..10 { 186 | model.append(&RowData::new(&format!("Name {}", i), i * 10)); 187 | } 188 | 189 | window.show_all(); 190 | } 191 | 192 | fn main() { 193 | let application = gtk::Application::new( 194 | Some("com.github.gtk-rs.examples.listbox-model"), 195 | Default::default(), 196 | ) 197 | .expect("Initialization failed..."); 198 | 199 | application.connect_activate(|app| { 200 | build_ui(app); 201 | }); 202 | 203 | application.run(&args().collect::>()); 204 | } 205 | 206 | // Our GObject subclass for carrying a name and count for the ListBox model 207 | // 208 | // Both name and count are stored in a RefCell to allow for interior mutability 209 | // and are exposed via normal GObject properties. This allows us to use property 210 | // bindings below to bind the values with what widgets display in the UI 211 | mod row_data { 212 | use super::*; 213 | 214 | use glib::subclass; 215 | use glib::subclass::prelude::*; 216 | use glib::translate::*; 217 | 218 | // Implementation sub-module of the GObject 219 | mod imp { 220 | use super::*; 221 | use std::cell::RefCell; 222 | 223 | // The actual data structure that stores our values. This is not accessible 224 | // directly from the outside. 225 | pub struct RowData { 226 | name: RefCell>, 227 | count: RefCell, 228 | } 229 | 230 | // GObject property definitions for our two values 231 | static PROPERTIES: [subclass::Property; 2] = [ 232 | subclass::Property("name", |name| { 233 | glib::ParamSpec::string( 234 | name, 235 | "Name", 236 | "Name", 237 | None, // Default value 238 | glib::ParamFlags::READWRITE, 239 | ) 240 | }), 241 | subclass::Property("count", |name| { 242 | glib::ParamSpec::uint( 243 | name, 244 | "Count", 245 | "Count", 246 | 0, 247 | 100, 248 | 0, // Allowed range and default value 249 | glib::ParamFlags::READWRITE, 250 | ) 251 | }), 252 | ]; 253 | 254 | // Basic declaration of our type for the GObject type system 255 | impl ObjectSubclass for RowData { 256 | const NAME: &'static str = "RowData"; 257 | type ParentType = glib::Object; 258 | type Instance = subclass::simple::InstanceStruct; 259 | type Class = subclass::simple::ClassStruct; 260 | 261 | glib_object_subclass!(); 262 | 263 | // Called exactly once before the first instantiation of an instance. This 264 | // sets up any type-specific things, in this specific case it installs the 265 | // properties so that GObject knows about their existence and they can be 266 | // used on instances of our type 267 | fn class_init(klass: &mut Self::Class) { 268 | klass.install_properties(&PROPERTIES); 269 | } 270 | 271 | // Called once at the very beginning of instantiation of each instance and 272 | // creates the data structure that contains all our state 273 | fn new() -> Self { 274 | Self { 275 | name: RefCell::new(None), 276 | count: RefCell::new(0), 277 | } 278 | } 279 | } 280 | 281 | // The ObjectImpl trait provides the setters/getters for GObject properties. 282 | // Here we need to provide the values that are internally stored back to the 283 | // caller, or store whatever new value the caller is providing. 284 | // 285 | // This maps between the GObject properties and our internal storage of the 286 | // corresponding values of the properties. 287 | impl ObjectImpl for RowData { 288 | glib_object_impl!(); 289 | 290 | fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) { 291 | let prop = &PROPERTIES[id]; 292 | 293 | match *prop { 294 | subclass::Property("name", ..) => { 295 | let name = value 296 | .get() 297 | .expect("type conformity checked by `Object::set_property`"); 298 | self.name.replace(name); 299 | } 300 | subclass::Property("count", ..) => { 301 | let count = value 302 | .get_some() 303 | .expect("type conformity checked by `Object::set_property`"); 304 | self.count.replace(count); 305 | } 306 | _ => unimplemented!(), 307 | } 308 | } 309 | 310 | fn get_property(&self, _obj: &glib::Object, id: usize) -> Result { 311 | let prop = &PROPERTIES[id]; 312 | 313 | match *prop { 314 | subclass::Property("name", ..) => Ok(self.name.borrow().to_value()), 315 | subclass::Property("count", ..) => Ok(self.count.borrow().to_value()), 316 | _ => unimplemented!(), 317 | } 318 | } 319 | } 320 | } 321 | 322 | // Public part of the RowData type. This behaves like a normal gtk-rs-style GObject 323 | // binding 324 | glib_wrapper! { 325 | pub struct RowData(Object, subclass::simple::ClassStruct, RowDataClass>); 326 | 327 | match fn { 328 | get_type => || imp::RowData::get_type().to_glib(), 329 | } 330 | } 331 | 332 | // Constructor for new instances. This simply calls glib::Object::new() with 333 | // initial values for our two properties and then returns the new instance 334 | impl RowData { 335 | pub fn new(name: &str, count: u32) -> RowData { 336 | glib::Object::new(Self::static_type(), &[("name", &name), ("count", &count)]) 337 | .expect("Failed to create row data") 338 | .downcast() 339 | .expect("Created row data is of wrong type") 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /src/bin/menu_bar.rs: -------------------------------------------------------------------------------- 1 | //! # MenuBar Sample 2 | //! 3 | //! This sample demonstrates how to use Menus/MenuBars and MenuItems in Windows. 4 | //! 5 | //! /!\ This is different from the system menu bar (which are preferred) available in `gio::Menu`! 6 | 7 | extern crate gio; 8 | extern crate glib; 9 | extern crate gtk; 10 | 11 | use gio::prelude::*; 12 | use glib::clone; 13 | use gtk::prelude::*; 14 | use gtk::{ 15 | AboutDialog, AccelFlags, AccelGroup, ApplicationWindow, CheckMenuItem, IconSize, Image, Label, 16 | Menu, MenuBar, MenuItem, WindowPosition, 17 | }; 18 | 19 | use std::env::args; 20 | 21 | fn build_ui(application: >k::Application) { 22 | let window = ApplicationWindow::new(application); 23 | 24 | window.set_title("MenuBar example"); 25 | window.set_position(WindowPosition::Center); 26 | window.set_size_request(400, 400); 27 | 28 | let v_box = gtk::Box::new(gtk::Orientation::Vertical, 10); 29 | 30 | let menu = Menu::new(); 31 | let accel_group = AccelGroup::new(); 32 | window.add_accel_group(&accel_group); 33 | let menu_bar = MenuBar::new(); 34 | let file = MenuItem::with_label("File"); 35 | let about = MenuItem::with_label("About"); 36 | let quit = MenuItem::with_label("Quit"); 37 | let file_item = MenuItem::new(); 38 | let file_box = gtk::Box::new(gtk::Orientation::Horizontal, 0); 39 | let file_image = Image::from_file("resources/file.png"); 40 | let file_label = Label::new(Some("File")); 41 | let folder_item = MenuItem::new(); 42 | let folder_box = gtk::Box::new(gtk::Orientation::Horizontal, 0); 43 | let folder_image = Image::from_icon_name(Some("folder-music-symbolic"), IconSize::Menu); 44 | let folder_label = Label::new(Some("Folder")); 45 | let check_item = CheckMenuItem::with_label("Click me!"); 46 | 47 | file_box.pack_start(&file_image, false, false, 0); 48 | file_box.pack_start(&file_label, true, true, 0); 49 | file_item.add(&file_box); 50 | folder_box.pack_start(&folder_image, false, false, 0); 51 | folder_box.pack_start(&folder_label, true, true, 0); 52 | folder_item.add(&folder_box); 53 | menu.append(&file_item); 54 | menu.append(&folder_item); 55 | menu.append(&check_item); 56 | menu.append(&about); 57 | menu.append(&quit); 58 | file.set_submenu(Some(&menu)); 59 | menu_bar.append(&file); 60 | 61 | let other_menu = Menu::new(); 62 | let sub_other_menu = Menu::new(); 63 | let other = MenuItem::with_label("Another"); 64 | let sub_other = MenuItem::with_label("Sub another"); 65 | let sub_other2 = MenuItem::with_label("Sub another 2"); 66 | let sub_sub_other2 = MenuItem::with_label("Sub sub another 2"); 67 | let sub_sub_other2_2 = MenuItem::with_label("Sub sub another2 2"); 68 | 69 | sub_other_menu.append(&sub_sub_other2); 70 | sub_other_menu.append(&sub_sub_other2_2); 71 | sub_other2.set_submenu(Some(&sub_other_menu)); 72 | other_menu.append(&sub_other); 73 | other_menu.append(&sub_other2); 74 | other.set_submenu(Some(&other_menu)); 75 | menu_bar.append(&other); 76 | 77 | quit.connect_activate(clone!(@weak window => move |_| { 78 | window.close(); 79 | })); 80 | 81 | // `Primary` is `Ctrl` on Windows and Linux, and `command` on macOS 82 | // It isn't available directly through gdk::ModifierType, since it has 83 | // different values on different platforms. 84 | let (key, modifier) = gtk::accelerator_parse("Q"); 85 | quit.add_accelerator("activate", &accel_group, key, modifier, AccelFlags::VISIBLE); 86 | 87 | let label = Label::new(Some("MenuBar example")); 88 | 89 | v_box.pack_start(&menu_bar, false, false, 0); 90 | v_box.pack_start(&label, true, true, 0); 91 | window.add(&v_box); 92 | window.show_all(); 93 | 94 | about.connect_activate(move |_| { 95 | let p = AboutDialog::new(); 96 | p.set_authors(&["gtk-rs developers"]); 97 | p.set_website_label(Some("gtk-rs")); 98 | p.set_website(Some("http://gtk-rs.org")); 99 | p.set_authors(&["Gtk-rs developers"]); 100 | p.set_title("About!"); 101 | p.set_transient_for(Some(&window)); 102 | p.show_all(); 103 | }); 104 | check_item.connect_toggled(|w| { 105 | w.set_label(if w.get_active() { 106 | "Checked" 107 | } else { 108 | "Unchecked" 109 | }); 110 | }); 111 | } 112 | 113 | fn main() { 114 | let application = gtk::Application::new( 115 | Some("com.github.gtk-rs.examples.menu_bar"), 116 | Default::default(), 117 | ) 118 | .expect("Initialization failed..."); 119 | 120 | application.connect_activate(|app| { 121 | build_ui(app); 122 | }); 123 | 124 | application.run(&args().collect::>()); 125 | } 126 | -------------------------------------------------------------------------------- /src/bin/menu_bar_system.rs: -------------------------------------------------------------------------------- 1 | //! # System MenuBar Sample 2 | //! 3 | //! This sample demonstrates how to create a "system" menu bar. It should always be preferred 4 | //! over the `gtk::MenuBar` since it adapts to the targetted system. 5 | 6 | extern crate gio; 7 | extern crate glib; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use glib::clone; 12 | use gtk::prelude::*; 13 | use gtk::AboutDialog; 14 | 15 | use std::env::args; 16 | 17 | fn build_system_menu(application: >k::Application) { 18 | let menu = gio::Menu::new(); 19 | let menu_bar = gio::Menu::new(); 20 | let more_menu = gio::Menu::new(); 21 | let switch_menu = gio::Menu::new(); 22 | let settings_menu = gio::Menu::new(); 23 | let submenu = gio::Menu::new(); 24 | 25 | // The first argument is the label of the menu item whereas the second is the action name. It'll 26 | // makes more sense when you'll be reading the "add_actions" function. 27 | menu.append(Some("Quit"), Some("app.quit")); 28 | 29 | switch_menu.append(Some("Switch"), Some("app.switch")); 30 | menu_bar.append_submenu(Some("_Switch"), &switch_menu); 31 | 32 | settings_menu.append(Some("Sub another"), Some("app.sub_another")); 33 | submenu.append(Some("Sub sub another"), Some("app.sub_sub_another")); 34 | submenu.append(Some("Sub sub another2"), Some("app.sub_sub_another2")); 35 | settings_menu.append_submenu(Some("Sub menu"), &submenu); 36 | menu_bar.append_submenu(Some("_Another"), &settings_menu); 37 | 38 | more_menu.append(Some("About"), Some("app.about")); 39 | menu_bar.append_submenu(Some("?"), &more_menu); 40 | 41 | application.set_app_menu(Some(&menu)); 42 | application.set_menubar(Some(&menu_bar)); 43 | } 44 | 45 | /// This function creates "actions" which connect on the declared actions from the menu items. 46 | fn add_actions( 47 | application: >k::Application, 48 | switch: >k::Switch, 49 | label: >k::Label, 50 | window: >k::ApplicationWindow, 51 | ) { 52 | // Thanks to this method, we can say that this item is actually a checkbox. 53 | let switch_action = gio::SimpleAction::new_stateful("switch", None, &false.to_variant()); 54 | switch_action.connect_activate(clone!(@weak switch => move |g, _| { 55 | let mut is_active = false; 56 | if let Some(g) = g.get_state() { 57 | is_active = g.get().expect("couldn't get bool"); 58 | // We update the state of the toggle. 59 | switch.set_active(!is_active); 60 | } 61 | // We need to change the toggle state ourselves. `gio` dark magic. 62 | g.change_state(&(!is_active).to_variant()); 63 | })); 64 | 65 | // The same goes the around way: if we update the switch state, we need to update the menu 66 | // item's state. 67 | switch.connect_property_active_notify(clone!(@weak switch_action => move |s| { 68 | switch_action.change_state(&s.get_active().to_variant()); 69 | })); 70 | 71 | let sub_another = gio::SimpleAction::new("sub_another", None); 72 | sub_another.connect_activate(clone!(@weak label => move |_, _| { 73 | label.set_text("sub another menu item clicked"); 74 | })); 75 | let sub_sub_another = gio::SimpleAction::new("sub_sub_another", None); 76 | sub_sub_another.connect_activate(clone!(@weak label => move |_, _| { 77 | label.set_text("sub sub another menu item clicked"); 78 | })); 79 | let sub_sub_another2 = gio::SimpleAction::new("sub_sub_another2", None); 80 | sub_sub_another2.connect_activate(clone!(@weak label => move |_, _| { 81 | label.set_text("sub sub another2 menu item clicked"); 82 | })); 83 | 84 | let quit = gio::SimpleAction::new("quit", None); 85 | quit.connect_activate(clone!(@weak window => move |_, _| { 86 | window.close(); 87 | })); 88 | 89 | let about = gio::SimpleAction::new("about", None); 90 | about.connect_activate(clone!(@weak window => move |_, _| { 91 | let p = AboutDialog::new(); 92 | p.set_website_label(Some("gtk-rs")); 93 | p.set_website(Some("http://gtk-rs.org")); 94 | p.set_authors(&["Gtk-rs developers"]); 95 | p.set_title("About!"); 96 | p.set_transient_for(Some(&window)); 97 | p.show_all(); 98 | })); 99 | 100 | // We need to add all the actions to the application so they can be taken into account. 101 | application.add_action(&about); 102 | application.add_action(&quit); 103 | application.add_action(&sub_another); 104 | application.add_action(&sub_sub_another); 105 | application.add_action(&sub_sub_another2); 106 | application.add_action(&switch_action); 107 | } 108 | 109 | fn add_accelerators(application: >k::Application) { 110 | application.set_accels_for_action("app.about", &["F1"]); 111 | // `Primary` is a platform-agnostic accelerator modifier. 112 | // On Windows and Linux, `Primary` maps to the `Ctrl` key, 113 | // and on macOS it maps to the `command` key. 114 | application.set_accels_for_action("app.quit", &["Q"]); 115 | } 116 | 117 | fn build_ui(application: >k::Application) { 118 | let window = gtk::ApplicationWindow::new(application); 119 | 120 | window.set_title("System menu bar"); 121 | window.set_border_width(10); 122 | window.set_position(gtk::WindowPosition::Center); 123 | window.set_default_size(350, 70); 124 | 125 | let v_box = gtk::Box::new(gtk::Orientation::Vertical, 10); 126 | let label = gtk::Label::new(Some("Nothing happened yet")); 127 | let switch = gtk::Switch::new(); 128 | 129 | v_box.pack_start(&label, false, false, 0); 130 | v_box.pack_start(&switch, true, true, 0); 131 | window.add(&v_box); 132 | 133 | build_system_menu(application); 134 | 135 | add_actions(application, &switch, &label, &window); 136 | 137 | window.show_all(); 138 | } 139 | 140 | fn main() { 141 | let application = gtk::Application::new( 142 | Some("com.github.gtk-rs.examples.menu_bar_system"), 143 | Default::default(), 144 | ) 145 | .expect("Initialization failed..."); 146 | 147 | application.connect_startup(|app| { 148 | add_accelerators(app); 149 | }); 150 | application.connect_activate(|app| { 151 | build_ui(app); 152 | }); 153 | 154 | application.run(&args().collect::>()); 155 | } 156 | -------------------------------------------------------------------------------- /src/bin/multi_windows.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate glib; 3 | extern crate gtk; 4 | 5 | use gio::prelude::*; 6 | use glib::clone; 7 | use gtk::prelude::*; 8 | 9 | use std::cell::RefCell; 10 | use std::collections::HashMap; 11 | use std::env::args; 12 | use std::rc::Rc; 13 | 14 | fn create_sub_window( 15 | application: >k::Application, 16 | title: &str, 17 | main_window_entry: >k::Entry, 18 | id: usize, 19 | windows: &Rc>>>, 20 | ) { 21 | let window = gtk::Window::new(gtk::WindowType::Toplevel); 22 | 23 | application.add_window(&window); 24 | 25 | window.set_title(title); 26 | window.set_default_size(400, 200); 27 | 28 | window.connect_delete_event( 29 | clone!(@weak windows => @default-return Inhibit(false), move |_, _| { 30 | windows.borrow_mut().remove(&id); 31 | Inhibit(false) 32 | }), 33 | ); 34 | 35 | let button = gtk::Button::with_label(&format!("Notify main window with id {}!", id)); 36 | button.connect_clicked(clone!(@weak main_window_entry => move |_| { 37 | // When the button is clicked, let's write it on the main window's entry! 38 | main_window_entry.get_buffer().set_text(&format!("sub window {} clicked", id)); 39 | })); 40 | window.add(&button); 41 | 42 | window.show_all(); 43 | // Once the new window has been created, we put it into our hashmap so we can update its 44 | // title when needed. 45 | windows.borrow_mut().insert(id, window.downgrade()); 46 | } 47 | 48 | fn create_main_window(application: >k::Application) -> gtk::ApplicationWindow { 49 | let window = gtk::ApplicationWindow::new(application); 50 | 51 | window.set_title("I'm the main window"); 52 | window.set_default_size(400, 200); 53 | window.set_position(gtk::WindowPosition::Center); 54 | 55 | window.show_all(); 56 | window 57 | } 58 | 59 | fn generate_new_id(windows: &HashMap>) -> usize { 60 | let mut id = 0; 61 | // As long as the id is already there, we just continue to increment. 62 | while windows.get(&id).is_some() { 63 | id += 1; 64 | } 65 | id 66 | } 67 | 68 | fn build_ui(application: >k::Application) { 69 | let windows: Rc>>> = 70 | Rc::new(RefCell::new(HashMap::new())); 71 | let window = create_main_window(application); 72 | 73 | // Why not changing all sub-windows' title at once? 74 | let windows_title_entry = gtk::Entry::new(); 75 | windows_title_entry.set_placeholder_text(Some("Update all sub-windows' title")); 76 | windows_title_entry.connect_changed(clone!(@weak windows => move |windows_title_entry| { 77 | // When the entry's text is updated, we update the title of every sub windows. 78 | let text = windows_title_entry.get_buffer().get_text(); 79 | for window in windows.borrow().values() { 80 | if let Some(w) = window.upgrade() { 81 | w.set_title(&text) 82 | } 83 | } 84 | })); 85 | 86 | let entry = gtk::Entry::new(); 87 | entry.set_editable(false); 88 | entry.set_placeholder_text(Some("Events notification will be sent here")); 89 | 90 | // Now let's create a button to create a looooot of new windows! 91 | let button = gtk::Button::with_label("Create new window"); 92 | button.connect_clicked( 93 | clone!(@weak windows_title_entry, @weak entry, @weak application => move |_| { 94 | let new_id = generate_new_id(&windows.borrow()); 95 | create_sub_window(&application, 96 | &windows_title_entry.get_buffer().get_text(), 97 | &entry, 98 | new_id, 99 | &windows); 100 | }), 101 | ); 102 | 103 | // Now we add a layout so we can put all widgets into it. 104 | let layout = gtk::Box::new(gtk::Orientation::Vertical, 5); 105 | layout.add(&windows_title_entry); 106 | layout.add(&button); 107 | layout.add(&entry); 108 | window.add(&layout); 109 | 110 | window.set_focus(Some(&button)); 111 | 112 | // Then we show everything. 113 | window.show_all(); 114 | } 115 | 116 | fn main() { 117 | let application = gtk::Application::new( 118 | Some("com.github.gtk-rs.examples.multi_windows"), 119 | Default::default(), 120 | ) 121 | .expect("Initialization failed..."); 122 | 123 | application.connect_activate(|app| { 124 | build_ui(app); 125 | }); 126 | 127 | application.run(&args().collect::>()); 128 | } 129 | -------------------------------------------------------------------------------- /src/bin/multithreading_context.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate glib; 3 | extern crate gtk; 4 | 5 | use gio::prelude::*; 6 | use gtk::prelude::*; 7 | 8 | use std::env::args; 9 | use std::thread; 10 | use std::time::Duration; 11 | 12 | fn build_ui(application: >k::Application) { 13 | let window = gtk::ApplicationWindow::new(application); 14 | 15 | window.set_title("Multithreading GTK+ Program"); 16 | window.set_border_width(10); 17 | window.set_position(gtk::WindowPosition::Center); 18 | window.set_default_size(600, 400); 19 | 20 | let text_view = gtk::TextView::new(); 21 | let scroll = gtk::ScrolledWindow::new(gtk::NONE_ADJUSTMENT, gtk::NONE_ADJUSTMENT); 22 | scroll.set_policy(gtk::PolicyType::Automatic, gtk::PolicyType::Automatic); 23 | scroll.add(&text_view); 24 | 25 | let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); 26 | 27 | thread::spawn(move || { 28 | for i in 1..100 { 29 | // do long work 30 | thread::sleep(Duration::from_millis(50)); 31 | // send result to channel 32 | tx.send(format!("#{} Text from another thread.", i)) 33 | .expect("Couldn't send data to channel"); 34 | // receiver will be run on the main thread 35 | } 36 | }); 37 | 38 | // Attach receiver to the main context and set the text buffer text from here 39 | let text_buffer = text_view 40 | .get_buffer() 41 | .expect("Couldn't get buffer from text_view"); 42 | rx.attach(None, move |text| { 43 | text_buffer.set_text(&text); 44 | 45 | glib::Continue(true) 46 | }); 47 | 48 | window.add(&scroll); 49 | window.show_all(); 50 | } 51 | 52 | fn main() { 53 | let application = gtk::Application::new( 54 | Some("com.github.gtk-rs.examples.multithreading_context"), 55 | Default::default(), 56 | ) 57 | .expect("Initialization failed..."); 58 | 59 | application.connect_activate(|app| { 60 | build_ui(app); 61 | }); 62 | 63 | application.run(&args().collect::>()); 64 | } 65 | -------------------------------------------------------------------------------- /src/bin/notebook.rs: -------------------------------------------------------------------------------- 1 | extern crate gio; 2 | extern crate glib; 3 | extern crate gtk; 4 | 5 | use gio::prelude::*; 6 | use glib::clone; 7 | use gtk::prelude::*; 8 | use gtk::{IconSize, Orientation, ReliefStyle, Widget}; 9 | 10 | use std::env::args; 11 | 12 | struct Notebook { 13 | notebook: gtk::Notebook, 14 | tabs: Vec, 15 | } 16 | 17 | impl Notebook { 18 | fn new() -> Notebook { 19 | Notebook { 20 | notebook: gtk::Notebook::new(), 21 | tabs: Vec::new(), 22 | } 23 | } 24 | 25 | fn create_tab(&mut self, title: &str, widget: Widget) -> u32 { 26 | let close_image = gtk::Image::from_icon_name(Some("window-close"), IconSize::Button); 27 | let button = gtk::Button::new(); 28 | let label = gtk::Label::new(Some(title)); 29 | let tab = gtk::Box::new(Orientation::Horizontal, 0); 30 | 31 | button.set_relief(ReliefStyle::None); 32 | button.set_focus_on_click(false); 33 | button.add(&close_image); 34 | 35 | tab.pack_start(&label, false, false, 0); 36 | tab.pack_start(&button, false, false, 0); 37 | tab.show_all(); 38 | 39 | let index = self.notebook.append_page(&widget, Some(&tab)); 40 | 41 | button.connect_clicked(clone!(@weak self.notebook as notebook => move |_| { 42 | let index = notebook 43 | .page_num(&widget) 44 | .expect("Couldn't get page_num from notebook"); 45 | notebook.remove_page(Some(index)); 46 | })); 47 | 48 | self.tabs.push(tab); 49 | 50 | index 51 | } 52 | } 53 | 54 | fn build_ui(application: >k::Application) { 55 | let window = gtk::ApplicationWindow::new(application); 56 | 57 | window.set_title("Notebook"); 58 | window.set_position(gtk::WindowPosition::Center); 59 | window.set_default_size(640, 480); 60 | 61 | let mut notebook = Notebook::new(); 62 | 63 | for i in 1..4 { 64 | let title = format!("sheet {}", i); 65 | let label = gtk::Label::new(Some(&*title)); 66 | notebook.create_tab(&title, label.upcast()); 67 | } 68 | 69 | window.add(¬ebook.notebook); 70 | window.show_all(); 71 | } 72 | 73 | fn main() { 74 | let application = gtk::Application::new( 75 | Some("com.github.gtk-rs.examples.notebook"), 76 | Default::default(), 77 | ) 78 | .expect("Initialization failed..."); 79 | 80 | application.connect_activate(|app| { 81 | build_ui(app); 82 | }); 83 | 84 | application.run(&args().collect::>()); 85 | } 86 | -------------------------------------------------------------------------------- /src/bin/overlay.rs: -------------------------------------------------------------------------------- 1 | //! # Overlay example 2 | //! 3 | //! This sample demonstrates how to create an element "floating" above others. 4 | 5 | extern crate gdk; 6 | extern crate gio; 7 | extern crate glib; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | 13 | use std::env::args; 14 | 15 | // Basic CSS: we change background color, we set font color to black and we set it as bold. 16 | const STYLE: &str = " 17 | #overlay-label { 18 | background-color: rgba(192, 192, 192, 0.8); 19 | color: black; 20 | font-weight: bold; 21 | }"; 22 | 23 | // upgrade weak reference or return 24 | #[macro_export] 25 | macro_rules! upgrade_weak { 26 | ($x:ident, $r:expr) => {{ 27 | match $x.upgrade() { 28 | Some(o) => o, 29 | None => return $r, 30 | } 31 | }}; 32 | ($x:ident) => { 33 | upgrade_weak!($x, ()) 34 | }; 35 | } 36 | 37 | fn button_clicked(button: >k::Button, overlay_text_weak: &glib::object::WeakRef) { 38 | let overlay_text = upgrade_weak!(overlay_text_weak); 39 | overlay_text.set_text(&button.get_label().expect("Couldn't get button label")); 40 | } 41 | 42 | fn build_ui(application: >k::Application) { 43 | let window = gtk::ApplicationWindow::new(application); 44 | 45 | window.set_title("Overlay"); 46 | window.set_position(gtk::WindowPosition::Center); 47 | 48 | // The overlay container. 49 | let overlay = gtk::Overlay::new(); 50 | 51 | // The overlay label. 52 | let overlay_text = gtk::Label::new(Some("0")); 53 | // We need to name it in order to apply CSS on it. 54 | gtk::WidgetExt::set_widget_name(&overlay_text, "overlay-label"); 55 | // We put the overlay in the top-right corner of the window. 56 | overlay_text.set_halign(gtk::Align::End); 57 | overlay_text.set_valign(gtk::Align::Start); 58 | 59 | // We add into the overlay container as the overlay element. 60 | overlay.add_overlay(&overlay_text); 61 | 62 | let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0); 63 | 64 | let but1 = gtk::Button::with_label("Click me!"); 65 | let but2 = gtk::Button::with_label("Or me!"); 66 | let but3 = gtk::Button::with_label("Why not me?"); 67 | 68 | // When a button is clicked on, we set its label to the overlay label. 69 | let overlay_text_weak = overlay_text.downgrade(); 70 | but1.connect_clicked(move |b| { 71 | button_clicked(b, &overlay_text_weak); 72 | }); 73 | let overlay_text_weak = overlay_text.downgrade(); 74 | but2.connect_clicked(move |b| { 75 | button_clicked(b, &overlay_text_weak); 76 | }); 77 | let overlay_text_weak = overlay_text.downgrade(); 78 | but3.connect_clicked(move |b| { 79 | button_clicked(b, &overlay_text_weak); 80 | }); 81 | 82 | hbox.add(&but1); 83 | hbox.add(&but2); 84 | hbox.add(&but3); 85 | 86 | // We add the horizontal box into the overlay container "normally" (so this won't be an overlay 87 | // element). 88 | overlay.add(&hbox); 89 | // Then we add the overlay container inside our window. 90 | window.add(&overlay); 91 | 92 | window.show_all(); 93 | } 94 | 95 | fn main() { 96 | let application = 97 | gtk::Application::new(Some("com.github.overlay"), gio::ApplicationFlags::empty()) 98 | .expect("Initialization failed..."); 99 | 100 | application.connect_startup(|_| { 101 | // We add a bit of CSS in order to make the overlay label easier to be seen. 102 | let provider = gtk::CssProvider::new(); 103 | provider 104 | .load_from_data(STYLE.as_bytes()) 105 | .expect("Failed to load CSS"); 106 | gtk::StyleContext::add_provider_for_screen( 107 | &gdk::Screen::get_default().expect("Error initializing gtk css provider."), 108 | &provider, 109 | gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, 110 | ); 111 | }); 112 | 113 | application.connect_activate(|app| { 114 | // We build the application UI. 115 | build_ui(app); 116 | }); 117 | 118 | application.run(&args().collect::>()); 119 | } 120 | -------------------------------------------------------------------------------- /src/bin/pango_attributes.rs: -------------------------------------------------------------------------------- 1 | //! # Pango text attributes 2 | //! 3 | //! This sample demonstrates how to use various attributes on labels text. 4 | 5 | extern crate gio; 6 | extern crate gtk; 7 | extern crate pango; 8 | 9 | use gio::prelude::*; 10 | use gtk::prelude::*; 11 | 12 | use std::env::args; 13 | 14 | fn build_ui(application: >k::Application) { 15 | let window = gtk::ApplicationWindow::new(application); 16 | 17 | window.set_title("Pango text attributes"); 18 | window.set_border_width(10); 19 | window.set_position(gtk::WindowPosition::Center); 20 | window.set_default_size(350, 70); 21 | 22 | let label = gtk::Label::new(Some("Some text")); 23 | let attr_list = pango::AttrList::new(); 24 | 25 | let mut attr = 26 | pango::Attribute::new_background(65535, 0, 0).expect("Couldn't create new background"); 27 | attr.set_start_index(0); 28 | attr.set_end_index(2); 29 | attr_list.insert(attr); 30 | 31 | let mut attr = pango::Attribute::new_underline(pango::Underline::Single) 32 | .expect("Couldn't create new underline"); 33 | attr.set_start_index(1); 34 | attr.set_end_index(4); 35 | attr_list.insert(attr); 36 | 37 | let mut attr = 38 | pango::Attribute::new_strikethrough(true).expect("Couldn't create new strikethrough"); 39 | attr.set_start_index(5); 40 | attr_list.insert(attr); 41 | 42 | let mut attr = pango::Attribute::new_scale(1.2).expect("Couldn't create new scale"); 43 | attr.set_start_index(6); 44 | attr_list.insert(attr); 45 | 46 | label.set_attributes(Some(&attr_list)); 47 | window.add(&label); 48 | 49 | window.show_all(); 50 | } 51 | 52 | fn main() { 53 | let application = gtk::Application::new( 54 | Some("com.github.gtk-rs.examples.pango_attributes"), 55 | Default::default(), 56 | ) 57 | .expect("Initialization failed..."); 58 | 59 | application.connect_activate(|app| { 60 | build_ui(app); 61 | }); 62 | 63 | application.run(&args().collect::>()); 64 | } 65 | -------------------------------------------------------------------------------- /src/bin/printing.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | True 10 | False 11 | Printing 12 | False 13 | True 14 | 15 | 16 | 17 | 18 | True 19 | False 20 | vertical 21 | 22 | 23 | True 24 | True 25 | Type 26 | 27 | 28 | False 29 | True 30 | 0 31 | 32 | 33 | 34 | 35 | True 36 | True 37 | Here 38 | 39 | 40 | False 41 | True 42 | 1 43 | 44 | 45 | 46 | 47 | Print 48 | True 49 | True 50 | True 51 | 52 | 53 | False 54 | True 55 | 2 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/bin/printing.rs: -------------------------------------------------------------------------------- 1 | //! # Printing 2 | //! 3 | //! This sample reads text from two Entry fields, 4 | //! shows a print dialog and prints both texts one below 5 | //! the other. 6 | 7 | extern crate cairo; 8 | extern crate gio; 9 | extern crate glib; 10 | extern crate gtk; 11 | extern crate pango; 12 | extern crate pangocairo; 13 | 14 | use gio::prelude::*; 15 | use glib::clone; 16 | use gtk::prelude::*; 17 | 18 | use std::env::args; 19 | 20 | fn print(window: >k::Window, value1: String, value2: String) { 21 | let print_operation = gtk::PrintOperation::new(); 22 | 23 | // Currently unused 24 | // Could be used to check whether there was a success in printing 25 | //let print_operation_result: gtk::PrintOperationResult; 26 | 27 | print_operation.connect_begin_print(move |print_operation, _| { 28 | // This sets the number of pages of the document. 29 | // You most likely will calculate this, but for this example 30 | // it's hardcoded as 1 31 | print_operation.set_n_pages(1); 32 | }); 33 | 34 | print_operation.connect_draw_page(move |_, print_context, _| { 35 | let cairo = print_context 36 | .get_cairo_context() 37 | .expect("Couldn't get cairo context"); 38 | 39 | // This allows you to get the width of the page 40 | // Currently unused in this example 41 | //let width = print_context.get_width(); 42 | 43 | //Initi pango and set a font 44 | let font_description = pango::FontDescription::from_string("sans 14"); 45 | let pango_layout = print_context 46 | .create_pango_layout() 47 | .expect("Couldn't create pango layout"); 48 | pango_layout.set_font_description(Option::from(&font_description)); 49 | 50 | // Draw text1 51 | pango_layout.set_text(&value1); 52 | cairo.move_to(10.0, 10.0); 53 | pangocairo::functions::show_layout(&cairo, &pango_layout); 54 | 55 | //Draw text2 below text1 56 | pango_layout.set_text(&value2); 57 | cairo.rel_move_to(0.0, 20.0); 58 | pangocairo::functions::show_layout(&cairo, &pango_layout); 59 | }); 60 | 61 | // Handle printing asynchronously: run() will immediately return below on 62 | // platforms where this is supported and once the dialog is finished the 63 | // "done" signal will be emitted. 64 | print_operation.set_allow_async(true); 65 | print_operation.connect_done(|_, res| { 66 | println!("printing done: {:?}", res); 67 | }); 68 | 69 | // Open Print dialog setting up main window as its parent 70 | print_operation 71 | .run(gtk::PrintOperationAction::PrintDialog, Option::from(window)) 72 | .expect("Couldn't print"); 73 | } 74 | 75 | fn build_ui(application: >k::Application) { 76 | let glade_src = include_str!("printing.glade"); 77 | let builder = gtk::Builder::from_string(glade_src); 78 | 79 | let window: gtk::Window = builder.get_object("window").expect("Couldn't get window"); 80 | window.set_application(Some(application)); 81 | let entry1: gtk::Entry = builder.get_object("entry1").expect("Couldn't get entry1"); 82 | let entry2: gtk::Entry = builder.get_object("entry2").expect("Couldn't get entry2"); 83 | let button_print: gtk::Button = builder 84 | .get_object("buttonprint") 85 | .expect("Couldn't get buttonprint"); 86 | 87 | button_print.connect_clicked(clone!(@weak window => move |_| { 88 | let text1 = entry1.get_text().to_string(); 89 | let text2 = entry2.get_text().to_string(); 90 | print(&window, text1, text2); 91 | })); 92 | 93 | window.show_all(); 94 | } 95 | 96 | fn main() { 97 | let application = gtk::Application::new( 98 | Some("com.github.gtk-rs.examples.printing"), 99 | Default::default(), 100 | ) 101 | .expect("Initialization failed..."); 102 | 103 | application.connect_activate(|app| { 104 | build_ui(app); 105 | }); 106 | 107 | application.run(&args().collect::>()); 108 | } 109 | -------------------------------------------------------------------------------- /src/bin/progress_tracker.rs: -------------------------------------------------------------------------------- 1 | //! Track progress with a background thread and a channel. 2 | 3 | extern crate gio; 4 | extern crate glib; 5 | extern crate gtk; 6 | 7 | use gio::prelude::*; 8 | use glib::clone; 9 | use gtk::prelude::*; 10 | 11 | use std::cell::{Cell, RefCell}; 12 | use std::env::args; 13 | use std::rc::Rc; 14 | use std::thread; 15 | use std::time::Duration; 16 | 17 | pub fn main() { 18 | glib::set_program_name(Some("Progress Tracker")); 19 | 20 | let application = gtk::Application::new( 21 | Some("com.github.progress-tracker"), 22 | gio::ApplicationFlags::empty(), 23 | ) 24 | .expect("initialization failed"); 25 | 26 | application.connect_startup(|app| { 27 | let application = Application::new(app); 28 | 29 | let application_container = RefCell::new(Some(application)); 30 | app.connect_shutdown(move |_| { 31 | let application = application_container 32 | .borrow_mut() 33 | .take() 34 | .expect("Shutdown called multiple times"); 35 | // Here we could do whatever we need to do for shutdown now 36 | drop(application); 37 | }); 38 | }); 39 | 40 | application.connect_activate(|_| {}); 41 | application.run(&args().collect::>()); 42 | } 43 | 44 | pub struct Application { 45 | pub widgets: Rc, 46 | } 47 | 48 | impl Application { 49 | pub fn new(app: >k::Application) -> Self { 50 | let app = Application { 51 | widgets: Rc::new(Widgets::new(app)), 52 | }; 53 | 54 | app.connect_progress(); 55 | 56 | app 57 | } 58 | 59 | fn connect_progress(&self) { 60 | let active = Rc::new(Cell::new(false)); 61 | self.widgets.main_view.button.connect_clicked( 62 | clone!(@weak self.widgets as widgets => move |_| { 63 | if active.get() { 64 | return; 65 | } 66 | 67 | active.set(true); 68 | 69 | let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); 70 | thread::spawn(move || { 71 | for v in 1..=10 { 72 | let _ = tx.send(Some(v)); 73 | thread::sleep(Duration::from_millis(500)); 74 | } 75 | let _ = tx.send(None); 76 | }); 77 | 78 | let active = active.clone(); 79 | let widgets = widgets.clone(); 80 | rx.attach(None, move |value| match value { 81 | Some(value) => { 82 | widgets 83 | .main_view 84 | .progress 85 | .set_fraction(f64::from(value) / 10.0); 86 | 87 | if value == 10 { 88 | widgets 89 | .view_stack 90 | .set_visible_child(&widgets.complete_view.container); 91 | 92 | let widgets = widgets.clone(); 93 | gtk::timeout_add(1500, move || { 94 | widgets.main_view.progress.set_fraction(0.0); 95 | widgets 96 | .view_stack 97 | .set_visible_child(&widgets.main_view.container); 98 | glib::Continue(false) 99 | }); 100 | } 101 | 102 | glib::Continue(true) 103 | } 104 | None => { 105 | active.set(false); 106 | glib::Continue(false) 107 | } 108 | }); 109 | }), 110 | ); 111 | } 112 | } 113 | 114 | pub struct Widgets { 115 | pub window: gtk::ApplicationWindow, 116 | pub header: Header, 117 | pub view_stack: gtk::Stack, 118 | pub main_view: MainView, 119 | pub complete_view: CompleteView, 120 | } 121 | 122 | impl Widgets { 123 | pub fn new(application: >k::Application) -> Self { 124 | let complete_view = CompleteView::new(); 125 | let main_view = MainView::new(); 126 | 127 | let view_stack = gtk::Stack::new(); 128 | view_stack.set_border_width(6); 129 | view_stack.set_vexpand(true); 130 | view_stack.set_hexpand(true); 131 | view_stack.add(&main_view.container); 132 | view_stack.add(&complete_view.container); 133 | 134 | let header = Header::new(); 135 | 136 | let window = gtk::ApplicationWindow::new(application); 137 | window.set_icon_name(Some("package-x-generic")); 138 | window.set_property_window_position(gtk::WindowPosition::Center); 139 | window.set_titlebar(Some(&header.container)); 140 | window.add(&view_stack); 141 | window.show_all(); 142 | window.set_default_size(500, 250); 143 | window.connect_delete_event(move |window, _| { 144 | window.close(); 145 | Inhibit(false) 146 | }); 147 | 148 | Widgets { 149 | window, 150 | header, 151 | view_stack, 152 | main_view, 153 | complete_view, 154 | } 155 | } 156 | } 157 | 158 | pub struct Header { 159 | container: gtk::HeaderBar, 160 | } 161 | 162 | impl Header { 163 | pub fn new() -> Self { 164 | let container = gtk::HeaderBar::new(); 165 | container.set_title(Some("Progress Tracker")); 166 | container.set_show_close_button(true); 167 | 168 | Header { container } 169 | } 170 | } 171 | 172 | pub struct CompleteView { 173 | pub container: gtk::Grid, 174 | } 175 | 176 | impl CompleteView { 177 | pub fn new() -> Self { 178 | let label = gtk::Label::new(None); 179 | label.set_markup("Task complete"); 180 | label.set_halign(gtk::Align::Center); 181 | label.set_valign(gtk::Align::Center); 182 | label.set_vexpand(true); 183 | label.set_hexpand(true); 184 | 185 | let container = gtk::Grid::new(); 186 | container.set_vexpand(true); 187 | container.set_hexpand(true); 188 | container.add(&label); 189 | 190 | CompleteView { container } 191 | } 192 | } 193 | 194 | pub struct MainView { 195 | pub container: gtk::Grid, 196 | pub progress: gtk::ProgressBar, 197 | pub button: gtk::Button, 198 | } 199 | 200 | impl MainView { 201 | pub fn new() -> Self { 202 | let progress = gtk::ProgressBar::new(); 203 | progress.set_text(Some("Progress Bar")); 204 | progress.set_show_text(true); 205 | progress.set_hexpand(true); 206 | 207 | let button = gtk::Button::new(); 208 | button.set_label("start"); 209 | button.set_halign(gtk::Align::Center); 210 | 211 | let container = gtk::Grid::new(); 212 | container.attach(&progress, 0, 0, 1, 1); 213 | container.attach(&button, 0, 1, 1, 1); 214 | container.set_row_spacing(12); 215 | container.set_border_width(6); 216 | container.set_vexpand(true); 217 | container.set_hexpand(true); 218 | 219 | MainView { 220 | container, 221 | progress, 222 | button, 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/bin/simple_treeview.rs: -------------------------------------------------------------------------------- 1 | //! # TreeView Sample 2 | //! 3 | //! This sample demonstrates how to create a TreeView with a ListStore. 4 | 5 | extern crate gio; 6 | extern crate gtk; 7 | 8 | use gio::prelude::*; 9 | use gtk::prelude::*; 10 | use gtk::{ 11 | ApplicationWindow, CellRendererText, Label, ListStore, Orientation, TreeView, TreeViewColumn, 12 | WindowPosition, 13 | }; 14 | 15 | use std::env::args; 16 | 17 | fn create_and_fill_model() -> ListStore { 18 | // Creation of a model with two rows. 19 | let model = ListStore::new(&[u32::static_type(), String::static_type()]); 20 | 21 | // Filling up the tree view. 22 | let entries = &["Michel", "Sara", "Liam", "Zelda", "Neo", "Octopus master"]; 23 | for (i, entry) in entries.iter().enumerate() { 24 | model.insert_with_values(None, &[0, 1], &[&(i as u32 + 1), &entry]); 25 | } 26 | model 27 | } 28 | 29 | fn append_column(tree: &TreeView, id: i32) { 30 | let column = TreeViewColumn::new(); 31 | let cell = CellRendererText::new(); 32 | 33 | column.pack_start(&cell, true); 34 | // Association of the view's column with the model's `id` column. 35 | column.add_attribute(&cell, "text", id); 36 | tree.append_column(&column); 37 | } 38 | 39 | fn create_and_setup_view() -> TreeView { 40 | // Creating the tree view. 41 | let tree = TreeView::new(); 42 | 43 | tree.set_headers_visible(false); 44 | // Creating the two columns inside the view. 45 | append_column(&tree, 0); 46 | append_column(&tree, 1); 47 | tree 48 | } 49 | 50 | fn build_ui(application: >k::Application) { 51 | let window = ApplicationWindow::new(application); 52 | 53 | window.set_title("Simple TreeView example"); 54 | window.set_position(WindowPosition::Center); 55 | 56 | // Creating a vertical layout to place both tree view and label in the window. 57 | let vertical_layout = gtk::Box::new(Orientation::Vertical, 0); 58 | 59 | // Creation of the label. 60 | let label = Label::new(None); 61 | 62 | let tree = create_and_setup_view(); 63 | 64 | let model = create_and_fill_model(); 65 | // Setting the model into the view. 66 | tree.set_model(Some(&model)); 67 | // Adding the view to the layout. 68 | vertical_layout.add(&tree); 69 | // Same goes for the label. 70 | vertical_layout.add(&label); 71 | 72 | // The closure responds to selection changes by connection to "::cursor-changed" signal, 73 | // that gets emitted when the cursor moves (focus changes). 74 | tree.connect_cursor_changed(move |tree_view| { 75 | let selection = tree_view.get_selection(); 76 | if let Some((model, iter)) = selection.get_selected() { 77 | // Now getting back the values from the row corresponding to the 78 | // iterator `iter`. 79 | // 80 | // The `get_value` method do the conversion between the gtk type and Rust. 81 | label.set_text(&format!( 82 | "Hello '{}' from row {}", 83 | model 84 | .get_value(&iter, 1) 85 | .get::() 86 | .expect("Treeview selection, column 1") 87 | .expect("Treeview selection, column 1: mandatory value not found"), 88 | model 89 | .get_value(&iter, 0) 90 | .get_some::() 91 | .expect("Treeview selection, column 0"), 92 | )); 93 | } 94 | }); 95 | 96 | // Adding the layout to the window. 97 | window.add(&vertical_layout); 98 | 99 | window.show_all(); 100 | } 101 | 102 | fn main() { 103 | let application = gtk::Application::new( 104 | Some("com.github.gtk-rs.examples.simple_treeview"), 105 | Default::default(), 106 | ) 107 | .expect("Initialization failed..."); 108 | 109 | application.connect_activate(|app| { 110 | build_ui(app); 111 | }); 112 | 113 | application.run(&args().collect::>()); 114 | } 115 | -------------------------------------------------------------------------------- /src/bin/sync_widgets.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 130 7 | 1 8 | 10 9 | 10 | 11 | 130 12 | 1 13 | 10 14 | 15 | 16 | False 17 | Enter your age 18 | center 19 | 300 20 | 20 21 | 22 | 23 | True 24 | False 25 | 5 26 | True 27 | 28 | 29 | True 30 | True 31 | spin_adjustment 32 | 33 | 34 | False 35 | True 36 | 0 37 | 38 | 39 | 40 | 41 | True 42 | True 43 | scale_adjustment 44 | 1 45 | 46 | 47 | False 48 | True 49 | 1 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/bin/sync_widgets.rs: -------------------------------------------------------------------------------- 1 | //! # Synchronizing Widgets 2 | //! 3 | //! You can use property bindings in order to synchronize the values of widgets. In this example a 4 | //! spin button and a horizontal scale will get interlocked. 5 | 6 | extern crate gio; 7 | extern crate glib; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | use gtk::Builder; 13 | 14 | use std::env::args; 15 | 16 | fn build_ui(application: >k::Application) { 17 | let glade_src = include_str!("sync_widgets.glade"); 18 | let builder = Builder::new(); 19 | builder 20 | .add_from_string(glade_src) 21 | .expect("Couldn't add from string"); 22 | 23 | let slider: gtk::Scale = builder.get_object("slider").expect("Couldn't get slider"); 24 | let spin_button: gtk::SpinButton = builder 25 | .get_object("spin_button") 26 | .expect("Couldn't get spin_button"); 27 | let slider_adj = slider.get_adjustment(); 28 | let spin_button_adj = spin_button.get_adjustment(); 29 | slider_adj 30 | .bind_property("value", &spin_button_adj, "value") 31 | .flags( 32 | glib::BindingFlags::DEFAULT 33 | | glib::BindingFlags::SYNC_CREATE 34 | | glib::BindingFlags::BIDIRECTIONAL, 35 | ) 36 | .build(); 37 | 38 | let window: gtk::ApplicationWindow = builder.get_object("window").expect("Couldn't get window"); 39 | window.set_application(Some(application)); 40 | 41 | window.show_all(); 42 | } 43 | 44 | fn main() { 45 | let application = gtk::Application::new( 46 | Some("com.github.gtk-rs.examples.sync_widgets"), 47 | Default::default(), 48 | ) 49 | .expect("Initialization failed..."); 50 | 51 | application.connect_activate(|app| { 52 | build_ui(app); 53 | }); 54 | 55 | application.run(&args().collect::>()); 56 | } 57 | -------------------------------------------------------------------------------- /src/bin/text_viewer.glade: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | True 7 | False 8 | 2 9 | document-open 10 | 11 | 12 | False 13 | Text File Viewer 14 | center 15 | 400 16 | 480 17 | 18 | 19 | True 20 | False 21 | vertical 22 | 23 | 24 | True 25 | False 26 | 27 | 28 | True 29 | False 30 | Open 31 | True 32 | Open 33 | True 34 | image1 35 | 36 | 37 | False 38 | True 39 | 40 | 41 | 42 | 43 | False 44 | True 45 | 0 46 | 47 | 48 | 49 | 50 | True 51 | True 52 | in 53 | 54 | 55 | True 56 | True 57 | 58 | 59 | 60 | 61 | True 62 | True 63 | 1 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/bin/text_viewer.rs: -------------------------------------------------------------------------------- 1 | //! # Toolbar, Scrollable Text View and File Chooser 2 | //! 3 | //! A simple text file viewer 4 | 5 | extern crate gio; 6 | extern crate glib; 7 | extern crate gtk; 8 | 9 | use std::env::args; 10 | use std::fs::File; 11 | use std::io::prelude::*; 12 | use std::io::BufReader; 13 | 14 | use gio::prelude::*; 15 | use glib::clone; 16 | use gtk::prelude::*; 17 | use gtk::Builder; 18 | 19 | pub fn build_ui(application: >k::Application) { 20 | let glade_src = include_str!("text_viewer.glade"); 21 | let builder = Builder::new(); 22 | builder 23 | .add_from_string(glade_src) 24 | .expect("Couldn't add from string"); 25 | 26 | let window: gtk::ApplicationWindow = builder.get_object("window").expect("Couldn't get window"); 27 | window.set_application(Some(application)); 28 | let open_button: gtk::ToolButton = builder 29 | .get_object("open_button") 30 | .expect("Couldn't get builder"); 31 | let text_view: gtk::TextView = builder 32 | .get_object("text_view") 33 | .expect("Couldn't get text_view"); 34 | 35 | open_button.connect_clicked(clone!(@weak window => move |_| { 36 | // TODO move this to a impl? 37 | let file_chooser = gtk::FileChooserDialog::new( 38 | Some("Open File"), 39 | Some(&window), 40 | gtk::FileChooserAction::Open, 41 | ); 42 | file_chooser.add_buttons(&[ 43 | ("Open", gtk::ResponseType::Ok), 44 | ("Cancel", gtk::ResponseType::Cancel), 45 | ]); 46 | file_chooser.connect_response(clone!(@weak text_view => move |file_chooser, response| { 47 | if response == gtk::ResponseType::Ok { 48 | let filename = file_chooser.get_filename().expect("Couldn't get filename"); 49 | let file = File::open(&filename).expect("Couldn't open file"); 50 | 51 | let mut reader = BufReader::new(file); 52 | let mut contents = String::new(); 53 | let _ = reader.read_to_string(&mut contents); 54 | 55 | text_view 56 | .get_buffer() 57 | .expect("Couldn't get window") 58 | .set_text(&contents); 59 | } 60 | file_chooser.close(); 61 | })); 62 | 63 | file_chooser.show_all(); 64 | })); 65 | 66 | window.show_all(); 67 | } 68 | 69 | fn main() { 70 | let application = gtk::Application::new( 71 | Some("com.github.gtk-rs.examples.text_viewer"), 72 | Default::default(), 73 | ) 74 | .expect("Initialization failed..."); 75 | 76 | application.connect_activate(|app| { 77 | build_ui(app); 78 | }); 79 | 80 | application.run(&args().collect::>()); 81 | } 82 | -------------------------------------------------------------------------------- /src/bin/transparent_main_window.rs: -------------------------------------------------------------------------------- 1 | //! # Transparent main window example 2 | //! 3 | //! This example demonstrates how to create a main window with a transparent background. 4 | 5 | extern crate cairo; 6 | extern crate gdk; 7 | extern crate gio; 8 | extern crate gtk; 9 | 10 | use gio::prelude::*; 11 | use gtk::prelude::*; 12 | use gtk::{ApplicationWindow, Button, Fixed}; 13 | 14 | use std::env::args; 15 | 16 | fn build_ui(application: >k::Application) { 17 | let window = ApplicationWindow::new(application); 18 | set_visual(&window, None); 19 | 20 | window.connect_screen_changed(set_visual); 21 | window.connect_draw(draw); 22 | 23 | window.set_title("Alpha Demo"); 24 | window.set_default_size(500, 500); 25 | window.set_app_paintable(true); // crucial for transparency 26 | 27 | let fixed = Fixed::new(); 28 | window.add(&fixed); 29 | let button = Button::with_label("Dummy"); 30 | button.set_size_request(100, 30); 31 | fixed.add(&button); 32 | 33 | window.show_all(); 34 | } 35 | 36 | fn main() { 37 | let application = gtk::Application::new( 38 | Some("com.github.gtk-rs.examples.transparent_main_window"), 39 | Default::default(), 40 | ) 41 | .expect("Initialization failed..."); 42 | 43 | application.connect_activate(|app| { 44 | build_ui(app); 45 | }); 46 | 47 | application.run(&args().collect::>()); 48 | } 49 | 50 | fn set_visual(window: &ApplicationWindow, _screen: Option<&gdk::Screen>) { 51 | if let Some(screen) = window.get_screen() { 52 | if let Some(ref visual) = screen.get_rgba_visual() { 53 | window.set_visual(Some(visual)); // crucial for transparency 54 | } 55 | } 56 | } 57 | 58 | fn draw(_window: &ApplicationWindow, ctx: &cairo::Context) -> Inhibit { 59 | // crucial for transparency 60 | ctx.set_source_rgba(1.0, 0.0, 0.0, 0.4); 61 | ctx.set_operator(cairo::Operator::Screen); 62 | ctx.paint(); 63 | Inhibit(false) 64 | } 65 | -------------------------------------------------------------------------------- /src/bin/tree_model_sort.rs: -------------------------------------------------------------------------------- 1 | //! # Tree Model Sort example 2 | //! 3 | //! This sample demonstrates how to use the `TreeModelSort` widget. 4 | 5 | extern crate gdk; 6 | extern crate gio; 7 | extern crate gtk; 8 | 9 | use gio::prelude::*; 10 | use gtk::prelude::*; 11 | use std::env::args; 12 | 13 | fn build_ui(application: >k::Application) { 14 | let window = gtk::ApplicationWindow::new(application); 15 | 16 | window.set_title("Tree Model Sort Window"); 17 | window.set_border_width(10); 18 | window.set_position(gtk::WindowPosition::Center); 19 | window.set_default_size(350, 70); 20 | 21 | let store = gtk::TreeStore::new(&[glib::Type::String]); 22 | store.insert_with_values(None, None, &[0], &[&"One"]); 23 | store.insert_with_values(None, None, &[0], &[&"Two"]); 24 | store.insert_with_values(None, None, &[0], &[&"Three"]); 25 | store.insert_with_values(None, None, &[0], &[&"Four"]); 26 | 27 | // We create the `TreeModelSort` and we give it the `TreeStore` as 28 | // parameter. 29 | let sortable_store = gtk::TreeModelSort::new(&store); 30 | 31 | // Then we create the `TreeView` from the `TreeModelSort`. 32 | let treeview = gtk::TreeView::with_model(&sortable_store); 33 | 34 | let column = gtk::TreeViewColumn::new(); 35 | column.set_title("Value"); 36 | column.set_clickable(true); 37 | column.set_sort_indicator(true); 38 | column.set_sort_column_id(0); 39 | 40 | let renderer = gtk::CellRendererText::new(); 41 | column.pack_end(&renderer, true); 42 | column.add_attribute(&renderer, "text", 0); 43 | 44 | treeview.append_column(&column); 45 | 46 | treeview.connect_row_activated(move |_, path, _column| { 47 | let real_path = sortable_store 48 | .convert_path_to_child_path(&path) 49 | .expect("Sorted path does not correspond to real path"); 50 | println!( 51 | "Clicked on sorted: {:?}, real: {:?}", 52 | path.get_indices(), 53 | real_path.get_indices() 54 | ); 55 | }); 56 | 57 | // We finally add the `TreeView` to the window. 58 | window.add(&treeview); 59 | window.show_all(); 60 | } 61 | 62 | fn main() { 63 | let application = 64 | gtk::Application::new(Some("com.github.basic"), gio::ApplicationFlags::empty()) 65 | .expect("Initialization failed..."); 66 | 67 | application.connect_activate(|app| { 68 | build_ui(app); 69 | }); 70 | application.run(&args().collect::>()); 71 | } 72 | -------------------------------------------------------------------------------- /src/bin/treeview.rs: -------------------------------------------------------------------------------- 1 | //! # TreeView Sample 2 | //! 3 | //! This sample demonstrates how to create a `TreeView` with either a `ListStore` or `TreeStore`. 4 | 5 | extern crate gdk_pixbuf; 6 | extern crate gio; 7 | extern crate glib; 8 | extern crate gtk; 9 | 10 | use gdk_pixbuf::Pixbuf; 11 | use gio::prelude::*; 12 | use glib::clone; 13 | use gtk::prelude::*; 14 | use gtk::{ 15 | ApplicationWindow, ButtonsType, CellRendererPixbuf, CellRendererText, DialogFlags, 16 | MessageDialog, MessageType, Orientation, TreeStore, TreeView, TreeViewColumn, WindowPosition, 17 | }; 18 | 19 | use std::env::args; 20 | 21 | fn append_text_column(tree: &TreeView) { 22 | let column = TreeViewColumn::new(); 23 | let cell = CellRendererText::new(); 24 | 25 | column.pack_start(&cell, true); 26 | column.add_attribute(&cell, "text", 0); 27 | tree.append_column(&column); 28 | } 29 | 30 | fn build_ui(application: >k::Application) { 31 | let window = ApplicationWindow::new(application); 32 | 33 | window.set_title("TreeView Sample"); 34 | window.set_position(WindowPosition::Center); 35 | 36 | // left pane 37 | let left_tree = TreeView::new(); 38 | let left_store = TreeStore::new(&[String::static_type()]); 39 | 40 | left_tree.set_model(Some(&left_store)); 41 | left_tree.set_headers_visible(false); 42 | append_text_column(&left_tree); 43 | 44 | for i in 0..10 { 45 | // insert_with_values takes two slices: column indices and ToValue 46 | // trait objects. ToValue is implemented for strings, numeric types, 47 | // bool and Object descendants 48 | let iter = left_store.insert_with_values(None, None, &[0], &[&format!("Hello {}", i)]); 49 | 50 | for _ in 0..i { 51 | left_store.insert_with_values(Some(&iter), None, &[0], &[&"I'm a child node"]); 52 | } 53 | } 54 | 55 | // right pane 56 | let right_tree = TreeView::new(); 57 | let right_column_types = [Pixbuf::static_type(), String::static_type()]; 58 | let right_store = TreeStore::new(&right_column_types); 59 | let renderer = CellRendererPixbuf::new(); 60 | let col = TreeViewColumn::new(); 61 | 62 | col.set_title("Picture"); 63 | col.pack_start(&renderer, false); 64 | 65 | col.add_attribute(&renderer, "pixbuf", 0); 66 | 67 | let renderer2 = CellRendererText::new(); 68 | col.pack_start(&renderer2, true); 69 | col.add_attribute(&renderer2, "text", 1); 70 | let image = Pixbuf::from_file("./resources/eye.png") 71 | .or_else(|err| { 72 | let mut msg = err.to_string(); 73 | if err.kind() == Some(glib::FileError::Noent) { 74 | msg.push_str( 75 | "\nRelaunch this example from the same level \ 76 | as the `resources` folder", 77 | ); 78 | } 79 | 80 | gtk::idle_add( 81 | clone!(@weak window => @default-return glib::Continue(false), move || { 82 | let dialog = MessageDialog::new(Some(&window), DialogFlags::MODAL, 83 | MessageType::Error, ButtonsType::Ok, &msg); 84 | dialog.connect_response(|dialog, _| dialog.close()); 85 | dialog.show_all(); 86 | Continue(false) 87 | }), 88 | ); 89 | 90 | Err(()) 91 | }) 92 | .ok(); 93 | 94 | right_tree.append_column(&col); 95 | right_tree.set_model(Some(&right_store)); 96 | right_tree.set_headers_visible(true); 97 | 98 | for _ in 0..10 { 99 | right_store.insert_with_values( 100 | None, 101 | None, 102 | &[0, 1], 103 | &[&image, &"I'm a child node with an image"], 104 | ); 105 | } 106 | 107 | // selection and path manipulation 108 | 109 | let left_selection = left_tree.get_selection(); 110 | left_selection.connect_changed(clone!(@weak right_tree => move |tree_selection| { 111 | let (left_model, iter) = tree_selection.get_selected().expect("Couldn't get selected"); 112 | let mut path = left_model.get_path(&iter).expect("Couldn't get path"); 113 | // get the top-level element path 114 | while path.get_depth() > 1 { 115 | path.up(); 116 | } 117 | right_tree.get_selection().select_path(&path); 118 | })); 119 | 120 | // display the panes 121 | 122 | let split_pane = gtk::Box::new(Orientation::Horizontal, 10); 123 | 124 | split_pane.set_size_request(-1, -1); 125 | split_pane.add(&left_tree); 126 | split_pane.add(&right_tree); 127 | 128 | window.add(&split_pane); 129 | window.show_all(); 130 | } 131 | 132 | fn main() { 133 | let application = gtk::Application::new( 134 | Some("com.github.gtk-rs.examples.treeview"), 135 | Default::default(), 136 | ) 137 | .expect("Initialization failed..."); 138 | 139 | application.connect_activate(|app| { 140 | build_ui(app); 141 | }); 142 | 143 | application.run(&args().collect::>()); 144 | } 145 | --------------------------------------------------------------------------------