├── .github └── workflows │ └── conrod.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── fonts │ └── NotoSans │ │ ├── LICENSE-2.0.txt │ │ ├── NotoSans-Bold.ttf │ │ ├── NotoSans-BoldItalic.ttf │ │ ├── NotoSans-Italic.ttf │ │ └── NotoSans-Regular.ttf └── images │ ├── rust.png │ ├── rust_hover.png │ └── rust_press.png ├── backends ├── conrod_example_shared │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── conrod_gfx │ ├── Cargo.toml │ ├── examples │ │ └── all_winit_gfx.rs │ └── src │ │ └── lib.rs ├── conrod_glium │ ├── Cargo.toml │ ├── examples │ │ ├── all_winit_glium.rs │ │ ├── all_winit_glium_threaded.rs │ │ ├── canvas.rs │ │ ├── counter.rs │ │ ├── custom_widget.rs │ │ ├── file_navigator.rs │ │ ├── graph.rs │ │ ├── hello_world.rs │ │ ├── image.rs │ │ ├── image_button.rs │ │ ├── list.rs │ │ ├── list_select.rs │ │ ├── old_demo.rs │ │ ├── plot_path.rs │ │ ├── primitives.rs │ │ ├── range_slider.rs │ │ ├── support │ │ │ └── mod.rs │ │ ├── text.rs │ │ ├── text_edit.rs │ │ ├── triangles.rs │ │ └── tutorial │ │ │ └── chapter3_hello_world.rs │ └── src │ │ └── lib.rs ├── conrod_piston │ ├── Cargo.toml │ ├── examples │ │ └── all_piston_window.rs │ └── src │ │ ├── draw.rs │ │ ├── event.rs │ │ └── lib.rs ├── conrod_rendy │ ├── Cargo.toml │ ├── examples │ │ └── all_winit_rendy.rs │ └── src │ │ ├── lib.rs │ │ ├── shaders │ │ ├── frag.spv │ │ ├── shader.frag │ │ ├── shader.vert │ │ └── vert.spv │ │ └── vertex.rs ├── conrod_vulkano │ ├── Cargo.toml │ ├── examples │ │ ├── all_winit_vulkano.rs │ │ ├── list_select_vulkano.rs │ │ └── support │ │ │ └── mod.rs │ └── src │ │ ├── lib.rs │ │ └── shaders │ │ ├── frag.spv │ │ ├── shader.frag │ │ ├── shader.vert │ │ └── vert.spv ├── conrod_wgpu │ ├── Cargo.toml │ ├── examples │ │ └── all_winit_wgpu.rs │ └── src │ │ ├── lib.rs │ │ └── shaders │ │ ├── frag.wgsl │ │ └── vert.wgsl └── conrod_winit │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── macros.rs │ ├── v020.rs │ ├── v021.rs │ ├── v022.rs │ └── v023.rs ├── conrod_core ├── .travis.yml ├── Cargo.toml └── src │ ├── border.rs │ ├── color.rs │ ├── cursor.rs │ ├── event.rs │ ├── graph │ ├── algo.rs │ ├── depth_order.rs │ └── mod.rs │ ├── guide │ ├── chapter_1.rs │ ├── chapter_2.rs │ ├── chapter_3.rs │ └── mod.rs │ ├── image.rs │ ├── input │ ├── global.rs │ ├── mod.rs │ ├── state.rs │ └── widget.rs │ ├── label.rs │ ├── lib.rs │ ├── mesh.rs │ ├── position │ ├── matrix.rs │ ├── mod.rs │ ├── range.rs │ └── rect.rs │ ├── render.rs │ ├── tests │ ├── color.rs │ ├── global_input.rs │ ├── mod.rs │ ├── ui.rs │ └── widget_input.rs │ ├── text.rs │ ├── theme.rs │ ├── ui.rs │ ├── utils.rs │ └── widget │ ├── bordered_rectangle.rs │ ├── builder.rs │ ├── button.rs │ ├── canvas.rs │ ├── collapsible_area.rs │ ├── drop_down_list.rs │ ├── envelope_editor.rs │ ├── file_navigator │ ├── directory_view.rs │ └── mod.rs │ ├── graph │ ├── mod.rs │ └── node.rs │ ├── grid.rs │ ├── id.rs │ ├── list.rs │ ├── list_select.rs │ ├── matrix.rs │ ├── mod.rs │ ├── number_dialer.rs │ ├── plot_path.rs │ ├── primitive │ ├── image.rs │ ├── line.rs │ ├── mod.rs │ ├── point_path.rs │ ├── shape │ │ ├── circle.rs │ │ ├── mod.rs │ │ ├── oval.rs │ │ ├── polygon.rs │ │ ├── rectangle.rs │ │ └── triangles.rs │ └── text.rs │ ├── range_slider.rs │ ├── rounded_rectangle.rs │ ├── scroll.rs │ ├── scrollbar.rs │ ├── slider.rs │ ├── tabs.rs │ ├── text_box.rs │ ├── text_edit.rs │ ├── title_bar.rs │ ├── toggle.rs │ └── xy_pad.rs ├── conrod_derive ├── .gitignore ├── Cargo.toml └── src │ ├── common.rs │ ├── lib.rs │ ├── style.rs │ └── utils.rs └── scripts ├── id_rsa.enc └── travis-doc-upload.cfg /.github/workflows/conrod.yml: -------------------------------------------------------------------------------- 1 | name: conrod 2 | on: [push, pull_request] 3 | jobs: 4 | rustfmt-check: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Install stable 9 | uses: actions-rs/toolchain@v1 10 | with: 11 | profile: minimal 12 | toolchain: stable 13 | override: true 14 | components: rustfmt 15 | - name: Run rustfmt 16 | uses: actions-rs/cargo@v1 17 | with: 18 | command: fmt 19 | args: --all -- --check 20 | 21 | cargo-test-core: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Update apt 26 | run: sudo apt update 27 | - name: Install libxcb dev tools 28 | run: sudo apt-get install libxcb-composite0-dev 29 | - name: Install stable 30 | uses: actions-rs/toolchain@v1 31 | with: 32 | profile: minimal 33 | toolchain: stable 34 | override: true 35 | - name: Run default features 36 | uses: actions-rs/cargo@v1 37 | with: 38 | command: test 39 | args: -p conrod_core --verbose 40 | - name: Test docs 41 | uses: actions-rs/cargo@v1 42 | with: 43 | command: test 44 | args: -p conrod_core --doc --verbose 45 | 46 | cargo-check-gfx: 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v2 50 | - name: Update apt 51 | run: sudo apt update 52 | - name: Install libxcb dev tools 53 | run: sudo apt-get install libxcb-composite0-dev 54 | - name: Install stable 55 | uses: actions-rs/toolchain@v1 56 | with: 57 | profile: minimal 58 | toolchain: stable 59 | override: true 60 | - name: Run check 61 | uses: actions-rs/cargo@v1 62 | with: 63 | command: check 64 | args: -p conrod_gfx --examples --verbose 65 | 66 | cargo-check-glium: 67 | runs-on: ubuntu-latest 68 | steps: 69 | - uses: actions/checkout@v2 70 | - name: Update apt 71 | run: sudo apt update 72 | - name: Install libxcb dev tools 73 | run: sudo apt-get install libxcb-composite0-dev 74 | - name: Install stable 75 | uses: actions-rs/toolchain@v1 76 | with: 77 | profile: minimal 78 | toolchain: stable 79 | override: true 80 | - name: Run check 81 | uses: actions-rs/cargo@v1 82 | with: 83 | command: check 84 | args: -p conrod_glium --examples --verbose 85 | 86 | cargo-check-piston: 87 | runs-on: ubuntu-latest 88 | steps: 89 | - uses: actions/checkout@v2 90 | - name: Update apt 91 | run: sudo apt update 92 | - name: Install libxcb dev tools 93 | run: sudo apt-get install libxcb-composite0-dev 94 | - name: Install stable 95 | uses: actions-rs/toolchain@v1 96 | with: 97 | profile: minimal 98 | toolchain: stable 99 | override: true 100 | - name: Run check 101 | uses: actions-rs/cargo@v1 102 | with: 103 | command: check 104 | args: -p conrod_piston --examples --verbose 105 | 106 | cargo-check-rendy: 107 | runs-on: ubuntu-latest 108 | steps: 109 | - uses: actions/checkout@v2 110 | - name: Update apt 111 | run: sudo apt update 112 | - name: Install libxcb dev tools 113 | run: sudo apt-get install libxcb-composite0-dev 114 | - name: Install stable 115 | uses: actions-rs/toolchain@v1 116 | with: 117 | profile: minimal 118 | toolchain: stable 119 | override: true 120 | - name: Run check 121 | uses: actions-rs/cargo@v1 122 | with: 123 | command: check 124 | args: -p conrod_rendy --examples --verbose 125 | 126 | cargo-check-vulkano: 127 | runs-on: ubuntu-latest 128 | steps: 129 | - uses: actions/checkout@v2 130 | - name: Update apt 131 | run: sudo apt update 132 | - name: Install libxcb dev tools 133 | run: sudo apt-get install libxcb-composite0-dev 134 | - name: Install stable 135 | uses: actions-rs/toolchain@v1 136 | with: 137 | profile: minimal 138 | toolchain: stable 139 | override: true 140 | - name: Run check 141 | uses: actions-rs/cargo@v1 142 | with: 143 | command: check 144 | args: -p conrod_vulkano --examples --verbose 145 | 146 | cargo-check-wgpu: 147 | runs-on: ubuntu-latest 148 | steps: 149 | - uses: actions/checkout@v2 150 | - name: Update apt 151 | run: sudo apt update 152 | - name: Install libxcb dev tools 153 | run: sudo apt-get install libxcb-composite0-dev 154 | - name: Install stable 155 | uses: actions-rs/toolchain@v1 156 | with: 157 | profile: minimal 158 | toolchain: stable 159 | override: true 160 | - name: Run check 161 | uses: actions-rs/cargo@v1 162 | with: 163 | command: check 164 | args: -p conrod_wgpu --examples --verbose 165 | 166 | cargo-publish: 167 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 168 | env: 169 | CRATESIO_TOKEN: ${{ secrets.CRATESIO_TOKEN }} 170 | runs-on: ubuntu-latest 171 | steps: 172 | - uses: actions/checkout@v2 173 | - name: Update apt 174 | run: sudo apt update 175 | - name: Install libxcb dev tools 176 | run: sudo apt-get install libxcb-composite0-dev 177 | - name: Install stable 178 | uses: actions-rs/toolchain@v1 179 | with: 180 | profile: minimal 181 | toolchain: stable 182 | override: true 183 | - name: Cargo publish conrod_derive 184 | continue-on-error: true 185 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path conrod_derive/Cargo.toml 186 | - name: wait for crates.io 187 | run: sleep 30 188 | - name: Cargo publish conrod_core 189 | continue-on-error: true 190 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path conrod_core/Cargo.toml 191 | - name: Cargo publish conrod_winit 192 | continue-on-error: true 193 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_winit/Cargo.toml 194 | - name: wait for crates.io 195 | run: sleep 30 196 | - name: Cargo publish conrod_example_shared 197 | continue-on-error: true 198 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_example_shared/Cargo.toml 199 | - name: wait for crates.io 200 | run: sleep 30 201 | - name: Cargo publish conrod_gfx 202 | continue-on-error: true 203 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_gfx/Cargo.toml 204 | - name: Cargo publish conrod_glium 205 | continue-on-error: true 206 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_glium/Cargo.toml 207 | - name: Cargo publish conrod_piston 208 | continue-on-error: true 209 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_piston/Cargo.toml 210 | - name: Cargo publish conrod_rendy 211 | continue-on-error: true 212 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_rendy/Cargo.toml 213 | - name: Cargo publish conrod_vulkano 214 | continue-on-error: true 215 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_vulkano/Cargo.toml 216 | - name: Cargo publish conrod_wgpu 217 | continue-on-error: true 218 | run: cargo publish --token $CRATESIO_TOKEN --manifest-path backends/conrod_wgpu/Cargo.toml 219 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /bin/main 15 | /bin/test-internal 16 | /bin/test-external 17 | /doc/ 18 | target/ 19 | /build/ 20 | /.rust/ 21 | rusti.sh 22 | /examples/**/target 23 | 24 | Cargo.lock 25 | 26 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "conrod_core", 4 | "conrod_derive", 5 | "backends/conrod_example_shared", 6 | "backends/conrod_winit", 7 | "backends/conrod_gfx", 8 | "backends/conrod_glium", 9 | "backends/conrod_piston", 10 | "backends/conrod_rendy", 11 | "backends/conrod_vulkano", 12 | "backends/conrod_wgpu", 13 | ] 14 | 15 | # wgpu requires this feature resolver version. 16 | resolver = "2" 17 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2016 PistonDevelopers 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PistonDevelopers 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. -------------------------------------------------------------------------------- /assets/fonts/NotoSans/NotoSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/fonts/NotoSans/NotoSans-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSans/NotoSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/fonts/NotoSans/NotoSans-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSans/NotoSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/fonts/NotoSans/NotoSans-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/NotoSans/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/fonts/NotoSans/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /assets/images/rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/images/rust.png -------------------------------------------------------------------------------- /assets/images/rust_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/images/rust_hover.png -------------------------------------------------------------------------------- /assets/images/rust_press.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/assets/images/rust_press.png -------------------------------------------------------------------------------- /backends/conrod_example_shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_example_shared" 3 | version = "0.76.1" 4 | authors = ["mitchmindtree "] 5 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 6 | description = "A small crate for sharing common code between conrod examples." 7 | license = "MIT OR Apache-2.0" 8 | readme = "../../README.md" 9 | repository = "https://github.com/pistondevelopers/conrod.git" 10 | homepage = "https://github.com/pistondevelopers/conrod" 11 | categories = ["gui"] 12 | 13 | [dependencies] 14 | conrod_core = { path = "../../conrod_core", version = "0.76" } 15 | rand = "0.7" 16 | -------------------------------------------------------------------------------- /backends/conrod_gfx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_gfx" 3 | version = "0.76.1" 4 | authors = ["Mitchell Nordine "] 5 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 6 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 7 | license = "MIT OR Apache-2.0" 8 | readme = "../../README.md" 9 | repository = "https://github.com/pistondevelopers/conrod.git" 10 | homepage = "https://github.com/pistondevelopers/conrod" 11 | categories = ["gui"] 12 | 13 | [lib] 14 | name = "conrod_gfx" 15 | path = "./src/lib.rs" 16 | 17 | [dependencies] 18 | conrod_core = { path = "../../conrod_core", version = "0.76" } 19 | gfx = { version = "0.18" } 20 | gfx_core = { version = "0.9" } 21 | 22 | [dev-dependencies] 23 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 24 | conrod_winit = { path = "../conrod_winit", version = "0.76" } 25 | find_folder = "0.3.0" 26 | image = "0.22" 27 | petgraph = "0.4" 28 | ## glutin_gfx.rs example dependencies 29 | gfx_device_gl = "0.16" 30 | old_school_gfx_glutin_ext = "0.25" 31 | glutin = "0.25" 32 | winit = "0.23" 33 | -------------------------------------------------------------------------------- /backends/conrod_glium/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_glium" 3 | version = "0.76.1" 4 | authors = ["Mitchell Nordine "] 5 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 6 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 7 | license = "MIT OR Apache-2.0" 8 | readme = "../../README.md" 9 | repository = "https://github.com/pistondevelopers/conrod.git" 10 | homepage = "https://github.com/pistondevelopers/conrod" 11 | categories = ["gui"] 12 | 13 | [lib] 14 | name = "conrod_glium" 15 | path = "./src/lib.rs" 16 | 17 | [dependencies] 18 | conrod_core = { path = "../../conrod_core", version = "0.76" } 19 | glium = "0.28" 20 | 21 | [dev-dependencies] 22 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 23 | conrod_winit = { path = "../conrod_winit", version = "0.76" } 24 | find_folder = "0.3.0" 25 | image = "0.22" 26 | petgraph = "0.4" 27 | rand = "0.7" 28 | winit = "0.23" 29 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/all_winit_glium.rs: -------------------------------------------------------------------------------- 1 | //! A demonstration using winit to provide events and glium for drawing the Ui. 2 | extern crate conrod_core; 3 | extern crate conrod_example_shared; 4 | extern crate conrod_glium; 5 | extern crate conrod_winit; 6 | extern crate find_folder; 7 | extern crate glium; 8 | extern crate image; 9 | 10 | mod support; 11 | 12 | use conrod_example_shared::{WIN_H, WIN_W}; 13 | use conrod_glium::Renderer; 14 | use glium::Surface; 15 | 16 | fn main() { 17 | // Build the window. 18 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 19 | let window = glium::glutin::window::WindowBuilder::new() 20 | .with_title("Conrod with glium!") 21 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIN_W, WIN_H)); 22 | let context = glium::glutin::ContextBuilder::new() 23 | .with_vsync(true) 24 | .with_multisampling(4); 25 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 26 | 27 | // Construct our `Ui`. 28 | let mut ui = conrod_core::UiBuilder::new([WIN_W as f64, WIN_H as f64]) 29 | .theme(conrod_example_shared::theme()) 30 | .build(); 31 | 32 | // The `widget::Id` of each widget instantiated in `conrod_example_shared::gui`. 33 | let ids = conrod_example_shared::Ids::new(ui.widget_id_generator()); 34 | 35 | // Add a `Font` to the `Ui`'s `font::Map` from file. 36 | let assets = find_folder::Search::KidsThenParents(3, 5) 37 | .for_folder("assets") 38 | .unwrap(); 39 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 40 | ui.fonts.insert_from_file(font_path).unwrap(); 41 | 42 | // Load the Rust logo from our assets folder to use as an example image. 43 | fn load_rust_logo(display: &glium::Display) -> glium::texture::Texture2d { 44 | let assets = find_folder::Search::ParentsThenKids(3, 3) 45 | .for_folder("assets") 46 | .unwrap(); 47 | let path = assets.join("images/rust.png"); 48 | let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba(); 49 | let image_dimensions = rgba_image.dimensions(); 50 | let raw_image = glium::texture::RawImage2d::from_raw_rgba_reversed( 51 | &rgba_image.into_raw(), 52 | image_dimensions, 53 | ); 54 | let texture = glium::texture::Texture2d::new(display, raw_image).unwrap(); 55 | texture 56 | } 57 | 58 | let mut image_map = conrod_core::image::Map::new(); 59 | let rust_logo = image_map.insert(load_rust_logo(&display)); 60 | 61 | // A demonstration of some app state that we want to control with the conrod GUI. 62 | let mut app = conrod_example_shared::DemoApp::new(rust_logo); 63 | 64 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 65 | // for drawing to the glium `Surface`. 66 | // 67 | // Internally, the `Renderer` maintains: 68 | // - a `backend::glium::GlyphCache` for caching text onto a `glium::texture::Texture2d`. 69 | // - a `glium::Program` to use as the shader program when drawing to the `glium::Surface`. 70 | // - a `Vec` for collecting `backend::glium::Vertex`s generated when translating the 71 | // `conrod_core::render::Primitive`s. 72 | // - a `Vec` of commands that describe how to draw the vertices. 73 | let mut renderer = Renderer::new(&display).unwrap(); 74 | 75 | // Start the loop: 76 | // 77 | // - Send available events to the `Ui`. 78 | // - Update the widgets via the `conrod_example_shared::gui` fn. 79 | // - Render the current state of the `Ui`. 80 | // - Repeat. 81 | support::run_loop(display, event_loop, move |request, display| { 82 | match request { 83 | support::Request::Event { 84 | event, 85 | should_update_ui, 86 | should_exit, 87 | } => { 88 | // Use the `winit` backend feature to convert the winit event to a conrod one. 89 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 90 | ui.handle_event(event); 91 | *should_update_ui = true; 92 | } 93 | 94 | match event { 95 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 96 | // Break from the loop upon `Escape`. 97 | glium::glutin::event::WindowEvent::CloseRequested 98 | | glium::glutin::event::WindowEvent::KeyboardInput { 99 | input: 100 | glium::glutin::event::KeyboardInput { 101 | virtual_keycode: 102 | Some(glium::glutin::event::VirtualKeyCode::Escape), 103 | .. 104 | }, 105 | .. 106 | } => *should_exit = true, 107 | _ => {} 108 | }, 109 | _ => {} 110 | } 111 | } 112 | support::Request::SetUi { needs_redraw } => { 113 | // Instantiate a GUI demonstrating every widget type provided by conrod. 114 | conrod_example_shared::gui(&mut ui.set_widgets(), &ids, &mut app); 115 | 116 | *needs_redraw = ui.has_changed(); 117 | } 118 | support::Request::Redraw => { 119 | // Render the `Ui` and then display it on the screen. 120 | let primitives = ui.draw(); 121 | 122 | renderer.fill(display, primitives, &image_map); 123 | let mut target = display.draw(); 124 | target.clear_color(0.0, 0.0, 0.0, 1.0); 125 | renderer.draw(display, &mut target, &image_map).unwrap(); 126 | target.finish().unwrap(); 127 | } 128 | } 129 | }) 130 | } 131 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/counter.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate conrod_glium; 4 | #[macro_use] 5 | extern crate conrod_winit; 6 | extern crate find_folder; 7 | extern crate glium; 8 | 9 | use conrod_core::{widget, Labelable, Positionable, Sizeable, Widget}; 10 | use glium::Surface; 11 | 12 | mod support; 13 | 14 | fn main() { 15 | const WIDTH: u32 = 200; 16 | const HEIGHT: u32 = 200; 17 | 18 | // Build the window. 19 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 20 | let window = glium::glutin::window::WindowBuilder::new() 21 | .with_title("Click me!") 22 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 23 | let context = glium::glutin::ContextBuilder::new() 24 | .with_vsync(true) 25 | .with_multisampling(4); 26 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 27 | 28 | // construct our `Ui`. 29 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 30 | 31 | // Generate the widget identifiers. 32 | widget_ids!(struct Ids { canvas, counter }); 33 | let ids = Ids::new(ui.widget_id_generator()); 34 | 35 | // Add a `Font` to the `Ui`'s `font::Map` from file. 36 | let assets = find_folder::Search::KidsThenParents(3, 5) 37 | .for_folder("assets") 38 | .unwrap(); 39 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 40 | ui.fonts.insert_from_file(font_path).unwrap(); 41 | 42 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 43 | // for drawing to the glium `Surface`. 44 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 45 | 46 | // The image map describing each of our widget->image mappings (in our case, none). 47 | let image_map = conrod_core::image::Map::::new(); 48 | 49 | let mut count = 0; 50 | 51 | // Poll events from the window. 52 | support::run_loop(display, event_loop, move |request, display| { 53 | match request { 54 | support::Request::Event { 55 | event, 56 | should_update_ui, 57 | should_exit, 58 | } => { 59 | // Use the `winit` backend feature to convert the winit event to a conrod one. 60 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 61 | ui.handle_event(event); 62 | *should_update_ui = true; 63 | } 64 | 65 | match event { 66 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 67 | // Break from the loop upon `Escape`. 68 | glium::glutin::event::WindowEvent::CloseRequested 69 | | glium::glutin::event::WindowEvent::KeyboardInput { 70 | input: 71 | glium::glutin::event::KeyboardInput { 72 | virtual_keycode: 73 | Some(glium::glutin::event::VirtualKeyCode::Escape), 74 | .. 75 | }, 76 | .. 77 | } => *should_exit = true, 78 | _ => {} 79 | }, 80 | _ => {} 81 | } 82 | } 83 | support::Request::SetUi { needs_redraw } => { 84 | // Instantiate all widgets in the GUI. 85 | let ui = &mut ui.set_widgets(); 86 | 87 | // Create a background canvas upon which we'll place the button. 88 | widget::Canvas::new().pad(40.0).set(ids.canvas, ui); 89 | 90 | // Draw the button and increment `count` if pressed. 91 | for _click in widget::Button::new() 92 | .middle_of(ids.canvas) 93 | .w_h(80.0, 80.0) 94 | .label(&count.to_string()) 95 | .set(ids.counter, ui) 96 | { 97 | count += 1; 98 | } 99 | 100 | *needs_redraw = ui.has_changed(); 101 | } 102 | support::Request::Redraw => { 103 | // Render the `Ui` and then display it on the screen. 104 | let primitives = ui.draw(); 105 | 106 | renderer.fill(display, primitives, &image_map); 107 | let mut target = display.draw(); 108 | target.clear_color(0.0, 0.0, 0.0, 1.0); 109 | renderer.draw(display, &mut target, &image_map).unwrap(); 110 | target.finish().unwrap(); 111 | } 112 | } 113 | }) 114 | } 115 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/file_navigator.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate conrod_glium; 4 | extern crate conrod_winit; 5 | extern crate find_folder; 6 | extern crate glium; 7 | 8 | mod support; 9 | 10 | use glium::Surface; 11 | 12 | const WIDTH: u32 = 600; 13 | const HEIGHT: u32 = 300; 14 | 15 | fn main() { 16 | // Build the window. 17 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 18 | let window = glium::glutin::window::WindowBuilder::new() 19 | .with_title("Conrod with glium!") 20 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 21 | let context = glium::glutin::ContextBuilder::new() 22 | .with_vsync(true) 23 | .with_multisampling(4); 24 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 25 | 26 | // Construct our `Ui`. 27 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 28 | 29 | // A unique identifier for each widget. 30 | widget_ids!(struct Ids { canvas, file_navigator }); 31 | let ids = Ids::new(ui.widget_id_generator()); 32 | 33 | // Add a `Font` to the `Ui`'s `font::Map` from file. 34 | let assets = find_folder::Search::KidsThenParents(3, 5) 35 | .for_folder("assets") 36 | .unwrap(); 37 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 38 | ui.fonts.insert_from_file(font_path).unwrap(); 39 | 40 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 41 | // for drawing to the glium `Surface`. 42 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 43 | 44 | // The image map describing each of our widget->image mappings (in our case, none). 45 | let image_map = conrod_core::image::Map::::new(); 46 | 47 | let directory = find_folder::Search::KidsThenParents(3, 5) 48 | .for_folder("conrod") 49 | .unwrap(); 50 | 51 | // Poll events from the window. 52 | support::run_loop(display, event_loop, move |request, display| { 53 | match request { 54 | support::Request::Event { 55 | event, 56 | should_update_ui, 57 | should_exit, 58 | } => { 59 | // Use the `winit` backend feature to convert the winit event to a conrod one. 60 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 61 | ui.handle_event(event); 62 | *should_update_ui = true; 63 | } 64 | 65 | match event { 66 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 67 | // Break from the loop upon `Escape`. 68 | glium::glutin::event::WindowEvent::CloseRequested 69 | | glium::glutin::event::WindowEvent::KeyboardInput { 70 | input: 71 | glium::glutin::event::KeyboardInput { 72 | virtual_keycode: 73 | Some(glium::glutin::event::VirtualKeyCode::Escape), 74 | .. 75 | }, 76 | .. 77 | } => *should_exit = true, 78 | _ => {} 79 | }, 80 | _ => {} 81 | } 82 | } 83 | support::Request::SetUi { needs_redraw } => { 84 | // Instantiate the conrod widgets. 85 | use conrod_core::{widget, Colorable, Positionable, Sizeable, Widget}; 86 | let ui = &mut ui.set_widgets(); 87 | 88 | widget::Canvas::new() 89 | .color(conrod_core::color::DARK_CHARCOAL) 90 | .set(ids.canvas, ui); 91 | 92 | // Navigate the conrod directory only showing `.rs` and `.toml` files. 93 | for event in widget::FileNavigator::with_extension(&directory, &["rs", "toml"]) 94 | .color(conrod_core::color::LIGHT_BLUE) 95 | .font_size(16) 96 | .wh_of(ids.canvas) 97 | .middle_of(ids.canvas) 98 | //.show_hidden_files(true) // Use this to show hidden files 99 | .set(ids.file_navigator, ui) 100 | { 101 | println!("{:?}", event); 102 | } 103 | 104 | *needs_redraw = ui.has_changed(); 105 | } 106 | support::Request::Redraw => { 107 | // Render the `Ui` and then display it on the screen. 108 | let primitives = ui.draw(); 109 | 110 | renderer.fill(display, primitives, &image_map); 111 | let mut target = display.draw(); 112 | target.clear_color(0.0, 0.0, 0.0, 1.0); 113 | renderer.draw(display, &mut target, &image_map).unwrap(); 114 | target.finish().unwrap(); 115 | } 116 | } 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | //! A simple example that demonstrates using conrod within a basic `winit` window loop, using 2 | //! `glium` to render the `conrod_core::render::Primitives` to screen. 3 | 4 | #[macro_use] 5 | extern crate conrod_core; 6 | extern crate conrod_glium; 7 | #[macro_use] 8 | extern crate conrod_winit; 9 | extern crate find_folder; 10 | extern crate glium; 11 | 12 | mod support; 13 | 14 | use conrod_core::{widget, Colorable, Positionable, Widget}; 15 | use glium::Surface; 16 | 17 | const WIDTH: u32 = 400; 18 | const HEIGHT: u32 = 200; 19 | 20 | fn main() { 21 | // Build the window. 22 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 23 | let window = glium::glutin::window::WindowBuilder::new() 24 | .with_title("Hello Conrod!") 25 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 26 | let context = glium::glutin::ContextBuilder::new() 27 | .with_vsync(true) 28 | .with_multisampling(4); 29 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 30 | 31 | // construct our `Ui`. 32 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 33 | 34 | // Generate the widget identifiers. 35 | widget_ids!(struct Ids { text }); 36 | let ids = Ids::new(ui.widget_id_generator()); 37 | 38 | // Add a `Font` to the `Ui`'s `font::Map` from file. 39 | let assets = find_folder::Search::KidsThenParents(3, 5) 40 | .for_folder("assets") 41 | .unwrap(); 42 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 43 | ui.fonts.insert_from_file(font_path).unwrap(); 44 | 45 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 46 | // for drawing to the glium `Surface`. 47 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 48 | 49 | // The image map describing each of our widget->image mappings (in our case, none). 50 | let image_map = conrod_core::image::Map::::new(); 51 | 52 | let mut should_update_ui = true; 53 | event_loop.run(move |event, _, control_flow| { 54 | // Break from the loop upon `Escape` or closed window. 55 | match &event { 56 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 57 | // Break from the loop upon `Escape`. 58 | glium::glutin::event::WindowEvent::CloseRequested 59 | | glium::glutin::event::WindowEvent::KeyboardInput { 60 | input: 61 | glium::glutin::event::KeyboardInput { 62 | virtual_keycode: Some(glium::glutin::event::VirtualKeyCode::Escape), 63 | .. 64 | }, 65 | .. 66 | } => *control_flow = glium::glutin::event_loop::ControlFlow::Exit, 67 | _ => {} 68 | }, 69 | _ => {} 70 | } 71 | 72 | // Use the `winit` backend feature to convert the winit event to a conrod one. 73 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 74 | ui.handle_event(event); 75 | should_update_ui = true; 76 | } 77 | 78 | match &event { 79 | glium::glutin::event::Event::MainEventsCleared => { 80 | if should_update_ui { 81 | should_update_ui = false; 82 | 83 | // Set the widgets. 84 | let ui = &mut ui.set_widgets(); 85 | 86 | // "Hello World!" in the middle of the screen. 87 | widget::Text::new("Hello World!") 88 | .middle_of(ui.window) 89 | .color(conrod_core::color::WHITE) 90 | .font_size(32) 91 | .set(ids.text, ui); 92 | 93 | // Request redraw if the `Ui` has changed. 94 | display.gl_window().window().request_redraw(); 95 | } 96 | } 97 | glium::glutin::event::Event::RedrawRequested(_) => { 98 | // Draw the `Ui` if it has changed. 99 | let primitives = ui.draw(); 100 | 101 | renderer.fill(&display, primitives, &image_map); 102 | let mut target = display.draw(); 103 | target.clear_color(0.0, 0.0, 0.0, 1.0); 104 | renderer.draw(&display, &mut target, &image_map).unwrap(); 105 | target.finish().unwrap(); 106 | } 107 | _ => {} 108 | } 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/image.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A simple demonstration of how to instantiate an `Image` widget. 3 | //! 4 | 5 | #[macro_use] 6 | extern crate conrod_core; 7 | extern crate conrod_glium; 8 | extern crate glium; 9 | #[macro_use] 10 | extern crate conrod_winit; 11 | extern crate find_folder; 12 | extern crate image; 13 | 14 | mod support; 15 | 16 | use conrod_core::{color, widget, Colorable, Positionable, Sizeable, Widget}; 17 | use glium::Surface; 18 | 19 | const WIDTH: u32 = 800; 20 | const HEIGHT: u32 = 600; 21 | 22 | fn main() { 23 | // Build the window. 24 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 25 | let window = glium::glutin::window::WindowBuilder::new() 26 | .with_title("Image Widget Demonstration") 27 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 28 | let context = glium::glutin::ContextBuilder::new() 29 | .with_vsync(true) 30 | .with_multisampling(4); 31 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 32 | 33 | // construct our `Ui`. 34 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 35 | 36 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 37 | // for drawing to the glium `Surface`. 38 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 39 | 40 | // The `WidgetId` for our background and `Image` widgets. 41 | widget_ids!(struct Ids { background, rust_logo }); 42 | let ids = Ids::new(ui.widget_id_generator()); 43 | 44 | // Create our `conrod_core::image::Map` which describes each of our widget->image mappings. 45 | let rust_logo = load_rust_logo(&display); 46 | let (w, h) = (rust_logo.get_width(), rust_logo.get_height().unwrap()); 47 | let mut image_map = conrod_core::image::Map::new(); 48 | let rust_logo = image_map.insert(rust_logo); 49 | 50 | // Poll events from the window. 51 | support::run_loop(display, event_loop, move |request, display| { 52 | match request { 53 | support::Request::Event { 54 | event, 55 | should_update_ui, 56 | should_exit, 57 | } => { 58 | // Use the `winit` backend feature to convert the winit event to a conrod one. 59 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 60 | ui.handle_event(event); 61 | *should_update_ui = true; 62 | } 63 | 64 | match event { 65 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 66 | // Break from the loop upon `Escape`. 67 | glium::glutin::event::WindowEvent::CloseRequested 68 | | glium::glutin::event::WindowEvent::KeyboardInput { 69 | input: 70 | glium::glutin::event::KeyboardInput { 71 | virtual_keycode: 72 | Some(glium::glutin::event::VirtualKeyCode::Escape), 73 | .. 74 | }, 75 | .. 76 | } => *should_exit = true, 77 | _ => {} 78 | }, 79 | _ => {} 80 | } 81 | } 82 | support::Request::SetUi { needs_redraw } => { 83 | // Instantiate the widgets. 84 | let ui = &mut ui.set_widgets(); 85 | // Draw a light blue background. 86 | widget::Canvas::new() 87 | .color(color::LIGHT_BLUE) 88 | .set(ids.background, ui); 89 | // Instantiate the `Image` at its full size in the middle of the window. 90 | widget::Image::new(rust_logo) 91 | .w_h(w as f64, h as f64) 92 | .middle() 93 | .set(ids.rust_logo, ui); 94 | 95 | *needs_redraw = ui.has_changed(); 96 | } 97 | support::Request::Redraw => { 98 | // Render the `Ui` and then display it on the screen. 99 | let primitives = ui.draw(); 100 | 101 | renderer.fill(display, primitives, &image_map); 102 | let mut target = display.draw(); 103 | target.clear_color(0.0, 0.0, 0.0, 1.0); 104 | renderer.draw(display, &mut target, &image_map).unwrap(); 105 | target.finish().unwrap(); 106 | } 107 | } 108 | }) 109 | } 110 | 111 | // Load the Rust logo from our assets folder to use as an example image. 112 | fn load_rust_logo(display: &glium::Display) -> glium::texture::Texture2d { 113 | let assets = find_folder::Search::ParentsThenKids(5, 3) 114 | .for_folder("assets") 115 | .unwrap(); 116 | let path = assets.join("images/rust.png"); 117 | let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba(); 118 | let image_dimensions = rgba_image.dimensions(); 119 | let raw_image = glium::texture::RawImage2d::from_raw_rgba_reversed( 120 | &rgba_image.into_raw(), 121 | image_dimensions, 122 | ); 123 | let texture = glium::texture::Texture2d::new(display, raw_image).unwrap(); 124 | texture 125 | } 126 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/image_button.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! A demonstration of all non-primitive widgets available in Conrod. 3 | //! 4 | //! 5 | //! Don't be put off by the number of method calls, they are only for demonstration and almost all 6 | //! of them are optional. Conrod supports `Theme`s, so if you don't give it an argument, it will 7 | //! check the current `Theme` within the `Ui` and retrieve defaults from there. 8 | //! 9 | 10 | #[macro_use] 11 | extern crate conrod_core; 12 | extern crate conrod_glium; 13 | #[macro_use] 14 | extern crate conrod_winit; 15 | extern crate find_folder; 16 | extern crate glium; 17 | extern crate image; 18 | extern crate rand; // for making a random color. 19 | 20 | mod support; 21 | 22 | use conrod_core::{color, widget, Borderable, Colorable, Positionable, Sizeable, Widget}; 23 | use glium::Surface; 24 | 25 | const WIDTH: u32 = 1100; 26 | const HEIGHT: u32 = 560; 27 | 28 | fn main() { 29 | // Build the window. 30 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 31 | let window = glium::glutin::window::WindowBuilder::new() 32 | .with_title("Image Button Demonstration") 33 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 34 | let context = glium::glutin::ContextBuilder::new() 35 | .with_vsync(true) 36 | .with_multisampling(4); 37 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 38 | 39 | // construct our `Ui`. 40 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 41 | 42 | // Add a `Font` to the `Ui`'s `font::Map` from file. 43 | let assets = find_folder::Search::KidsThenParents(3, 5) 44 | .for_folder("assets") 45 | .unwrap(); 46 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 47 | ui.fonts.insert_from_file(font_path).unwrap(); 48 | 49 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 50 | // for drawing to the glium `Surface`. 51 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 52 | 53 | // Declare the ID for each of our widgets. 54 | widget_ids!(struct Ids { canvas, button, rust_logo }); 55 | let ids = Ids::new(ui.widget_id_generator()); 56 | 57 | // Create our `conrod_core::image::Map` which describes each of our widget->image mappings. 58 | let mut image_map = conrod_core::image::Map::new(); 59 | 60 | struct ImageIds { 61 | normal: conrod_core::image::Id, 62 | hover: conrod_core::image::Id, 63 | press: conrod_core::image::Id, 64 | } 65 | 66 | // Load the images into our `ImageIds` type for easy access. 67 | let image_path = assets.join("images"); 68 | let rust_logo = load_image(&display, image_path.join("rust.png")); 69 | let (w, h) = (rust_logo.get_width(), rust_logo.get_height().unwrap()); 70 | let image_ids = ImageIds { 71 | normal: image_map.insert(rust_logo), 72 | hover: image_map.insert(load_image(&display, image_path.join("rust_hover.png"))), 73 | press: image_map.insert(load_image(&display, image_path.join("rust_press.png"))), 74 | }; 75 | 76 | // We'll change the background colour with the image button. 77 | let mut bg_color = conrod_core::color::LIGHT_BLUE; 78 | 79 | // Poll events from the window. 80 | support::run_loop(display, event_loop, move |request, display| { 81 | match request { 82 | support::Request::Event { 83 | event, 84 | should_update_ui, 85 | should_exit, 86 | } => { 87 | // Use the `winit` backend feature to convert the winit event to a conrod one. 88 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 89 | ui.handle_event(event); 90 | *should_update_ui = true; 91 | } 92 | 93 | match event { 94 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 95 | // Break from the loop upon `Escape`. 96 | glium::glutin::event::WindowEvent::CloseRequested 97 | | glium::glutin::event::WindowEvent::KeyboardInput { 98 | input: 99 | glium::glutin::event::KeyboardInput { 100 | virtual_keycode: 101 | Some(glium::glutin::event::VirtualKeyCode::Escape), 102 | .. 103 | }, 104 | .. 105 | } => *should_exit = true, 106 | _ => {} 107 | }, 108 | _ => {} 109 | } 110 | } 111 | support::Request::SetUi { needs_redraw } => { 112 | let ui = &mut ui.set_widgets(); 113 | 114 | // We can use this `Canvas` as a parent Widget upon which we can place other widgets. 115 | widget::Canvas::new() 116 | .pad(30.0) 117 | .color(bg_color) 118 | .set(ids.canvas, ui); 119 | 120 | // Button widget example button. 121 | if widget::Button::image(image_ids.normal) 122 | .hover_image(image_ids.hover) 123 | .press_image(image_ids.press) 124 | .w_h(w as conrod_core::Scalar, h as conrod_core::Scalar) 125 | .middle_of(ids.canvas) 126 | .border(0.0) 127 | .set(ids.button, ui) 128 | .was_clicked() 129 | { 130 | bg_color = color::rgb(rand::random(), rand::random(), rand::random()); 131 | } 132 | 133 | *needs_redraw = ui.has_changed(); 134 | } 135 | support::Request::Redraw => { 136 | // Render the `Ui` and then display it on the screen. 137 | let primitives = ui.draw(); 138 | 139 | renderer.fill(display, primitives, &image_map); 140 | let mut target = display.draw(); 141 | target.clear_color(0.0, 0.0, 0.0, 1.0); 142 | renderer.draw(display, &mut target, &image_map).unwrap(); 143 | target.finish().unwrap(); 144 | } 145 | } 146 | }) 147 | } 148 | 149 | // Load an image from our assets folder as a texture we can draw to the screen. 150 | fn load_image

(display: &glium::Display, path: P) -> glium::texture::SrgbTexture2d 151 | where 152 | P: AsRef, 153 | { 154 | let path = path.as_ref(); 155 | let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba(); 156 | let image_dimensions = rgba_image.dimensions(); 157 | let raw_image = glium::texture::RawImage2d::from_raw_rgba_reversed( 158 | &rgba_image.into_raw(), 159 | image_dimensions, 160 | ); 161 | let texture = glium::texture::SrgbTexture2d::new(display, raw_image).unwrap(); 162 | texture 163 | } 164 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/list.rs: -------------------------------------------------------------------------------- 1 | //! A simple example demonstrating the `List` widget. 2 | 3 | #[macro_use] 4 | extern crate conrod_core; 5 | extern crate conrod_glium; 6 | extern crate conrod_winit; 7 | extern crate find_folder; 8 | extern crate glium; 9 | 10 | mod support; 11 | 12 | use glium::Surface; 13 | 14 | const WIDTH: u32 = 150; 15 | const HEIGHT: u32 = 600; 16 | 17 | widget_ids! { 18 | struct Ids { canvas, list } 19 | } 20 | 21 | fn main() { 22 | // Build the window. 23 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 24 | let window = glium::glutin::window::WindowBuilder::new() 25 | .with_title("List Demo") 26 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 27 | let context = glium::glutin::ContextBuilder::new() 28 | .with_vsync(true) 29 | .with_multisampling(4); 30 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 31 | 32 | // Construct our `Ui`. 33 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 34 | 35 | // Unique identifier for each widget. 36 | let ids = Ids::new(ui.widget_id_generator()); 37 | 38 | // Add a `Font` to the `Ui`'s `font::Map` from file. 39 | let assets = find_folder::Search::KidsThenParents(3, 5) 40 | .for_folder("assets") 41 | .unwrap(); 42 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 43 | ui.fonts.insert_from_file(font_path).unwrap(); 44 | 45 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 46 | // for drawing to the glium `Surface`. 47 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 48 | 49 | // The image map describing each of our widget->image mappings (in our case, none). 50 | let image_map = conrod_core::image::Map::::new(); 51 | 52 | let mut list = vec![true; 16]; 53 | 54 | // Poll events from the window. 55 | support::run_loop(display, event_loop, move |request, display| { 56 | match request { 57 | support::Request::Event { 58 | event, 59 | should_update_ui, 60 | should_exit, 61 | } => { 62 | // Use the `winit` backend feature to convert the winit event to a conrod one. 63 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 64 | ui.handle_event(event); 65 | *should_update_ui = true; 66 | } 67 | 68 | match event { 69 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 70 | // Break from the loop upon `Escape`. 71 | glium::glutin::event::WindowEvent::CloseRequested 72 | | glium::glutin::event::WindowEvent::KeyboardInput { 73 | input: 74 | glium::glutin::event::KeyboardInput { 75 | virtual_keycode: 76 | Some(glium::glutin::event::VirtualKeyCode::Escape), 77 | .. 78 | }, 79 | .. 80 | } => *should_exit = true, 81 | _ => {} 82 | }, 83 | _ => {} 84 | } 85 | } 86 | support::Request::SetUi { needs_redraw } => { 87 | set_ui(ui.set_widgets(), &mut list, &ids); 88 | 89 | *needs_redraw = ui.has_changed(); 90 | } 91 | support::Request::Redraw => { 92 | // Render the `Ui` and then display it on the screen. 93 | let primitives = ui.draw(); 94 | 95 | renderer.fill(display, primitives, &image_map); 96 | let mut target = display.draw(); 97 | target.clear_color(0.0, 0.0, 0.0, 1.0); 98 | renderer.draw(display, &mut target, &image_map).unwrap(); 99 | target.finish().unwrap(); 100 | } 101 | } 102 | }) 103 | } 104 | 105 | // Declare the `WidgetId`s and instantiate the widgets. 106 | fn set_ui(ref mut ui: conrod_core::UiCell, list: &mut [bool], ids: &Ids) { 107 | use conrod_core::{widget, Colorable, Labelable, Positionable, Sizeable, Widget}; 108 | 109 | widget::Canvas::new() 110 | .color(conrod_core::color::DARK_CHARCOAL) 111 | .set(ids.canvas, ui); 112 | 113 | let (mut items, scrollbar) = widget::List::flow_down(list.len()) 114 | .item_size(50.0) 115 | .scrollbar_on_top() 116 | .middle_of(ids.canvas) 117 | .wh_of(ids.canvas) 118 | .set(ids.list, ui); 119 | 120 | while let Some(item) = items.next(ui) { 121 | let i = item.i; 122 | let label = format!("item {}: {}", i, list[i]); 123 | let toggle = widget::Toggle::new(list[i]) 124 | .label(&label) 125 | .label_color(conrod_core::color::WHITE) 126 | .color(conrod_core::color::LIGHT_BLUE); 127 | for v in item.set(toggle, ui) { 128 | list[i] = v; 129 | } 130 | } 131 | 132 | if let Some(s) = scrollbar { 133 | s.set(ui) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/plot_path.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate glium; 4 | 5 | extern crate conrod_glium; 6 | extern crate find_folder; 7 | #[macro_use] 8 | extern crate conrod_winit; 9 | 10 | mod support; 11 | 12 | use glium::Surface; 13 | 14 | const WIDTH: u32 = 720; 15 | const HEIGHT: u32 = 360; 16 | 17 | widget_ids! { 18 | struct Ids { canvas, grid, plot } 19 | } 20 | 21 | fn main() { 22 | // Build the window. 23 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 24 | let window = glium::glutin::window::WindowBuilder::new() 25 | .with_title("PlotPath Demo") 26 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 27 | let context = glium::glutin::ContextBuilder::new() 28 | .with_vsync(true) 29 | .with_multisampling(4); 30 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 31 | 32 | // Construct our `Ui`. 33 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 34 | 35 | // A unique identifier for each widget. 36 | let ids = Ids::new(ui.widget_id_generator()); 37 | 38 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 39 | // for drawing to the glium `Surface`. 40 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 41 | 42 | // The image map describing each of our widget->image mappings (in our case, none). 43 | let image_map = conrod_core::image::Map::::new(); 44 | 45 | // Poll events from the window. 46 | support::run_loop(display, event_loop, move |request, display| { 47 | match request { 48 | support::Request::Event { 49 | event, 50 | should_update_ui, 51 | should_exit, 52 | } => { 53 | // Use the `winit` backend feature to convert the winit event to a conrod one. 54 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 55 | ui.handle_event(event); 56 | *should_update_ui = true; 57 | } 58 | 59 | match event { 60 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 61 | // Break from the loop upon `Escape`. 62 | glium::glutin::event::WindowEvent::CloseRequested 63 | | glium::glutin::event::WindowEvent::KeyboardInput { 64 | input: 65 | glium::glutin::event::KeyboardInput { 66 | virtual_keycode: 67 | Some(glium::glutin::event::VirtualKeyCode::Escape), 68 | .. 69 | }, 70 | .. 71 | } => *should_exit = true, 72 | _ => {} 73 | }, 74 | _ => {} 75 | } 76 | } 77 | support::Request::SetUi { needs_redraw } => { 78 | // Instantiate the widgets. 79 | use conrod_core::{color, widget, Colorable, Positionable, Sizeable, Widget}; 80 | 81 | let ui = &mut ui.set_widgets(); 82 | 83 | widget::Canvas::new() 84 | .color(color::DARK_CHARCOAL) 85 | .set(ids.canvas, ui); 86 | 87 | let min_x = 0.0; 88 | let max_x = std::f64::consts::PI * 2.0; 89 | let min_y = -1.0; 90 | let max_y = 1.0; 91 | 92 | let quarter_lines = widget::grid::Lines::step(0.5_f64).thickness(2.0); 93 | let sixteenth_lines = widget::grid::Lines::step(0.125_f64).thickness(1.0); 94 | let lines = &[ 95 | quarter_lines.x(), 96 | quarter_lines.y(), 97 | sixteenth_lines.x(), 98 | sixteenth_lines.y(), 99 | ]; 100 | 101 | widget::Grid::new(min_x, max_x, min_y, max_y, lines.iter().cloned()) 102 | .color(color::rgb(0.1, 0.12, 0.15)) 103 | .wh_of(ids.canvas) 104 | .middle_of(ids.canvas) 105 | .set(ids.grid, ui); 106 | widget::PlotPath::new(min_x, max_x, min_y, max_y, f64::sin) 107 | .color(color::LIGHT_BLUE) 108 | .thickness(2.0) 109 | .wh_of(ids.canvas) 110 | .middle_of(ids.canvas) 111 | .set(ids.plot, ui); 112 | *needs_redraw = ui.has_changed(); 113 | } 114 | support::Request::Redraw => { 115 | // Render the `Ui` and then display it on the screen. 116 | let primitives = ui.draw(); 117 | 118 | renderer.fill(display, primitives, &image_map); 119 | let mut target = display.draw(); 120 | target.clear_color(0.0, 0.0, 0.0, 1.0); 121 | renderer.draw(display, &mut target, &image_map).unwrap(); 122 | target.finish().unwrap(); 123 | } 124 | } 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/primitives.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate conrod_glium; 4 | #[macro_use] 5 | extern crate conrod_winit; 6 | extern crate find_folder; 7 | extern crate glium; 8 | 9 | mod support; 10 | 11 | use glium::Surface; 12 | 13 | // Generate a type that will produce a unique `widget::Id` for each widget. 14 | widget_ids! { 15 | struct Ids { 16 | canvas, 17 | line, 18 | point_path, 19 | rectangle_fill, 20 | rectangle_outline, 21 | trapezoid, 22 | oval_fill, 23 | oval_outline, 24 | circle, 25 | } 26 | } 27 | 28 | fn main() { 29 | const WIDTH: u32 = 400; 30 | const HEIGHT: u32 = 720; 31 | 32 | // Build the window. 33 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 34 | let window = glium::glutin::window::WindowBuilder::new() 35 | .with_title("Primitive Widgets Demo") 36 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 37 | let context = glium::glutin::ContextBuilder::new() 38 | .with_vsync(true) 39 | .with_multisampling(4); 40 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 41 | 42 | // construct our `Ui`. 43 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 44 | 45 | // A unique identifier for each widget. 46 | let ids = Ids::new(ui.widget_id_generator()); 47 | 48 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 49 | // for drawing to the glium `Surface`. 50 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 51 | 52 | // The image map describing each of our widget->image mappings (in our case, none). 53 | let image_map = conrod_core::image::Map::::new(); 54 | 55 | // Poll events from the window. 56 | support::run_loop(display, event_loop, move |request, display| { 57 | match request { 58 | support::Request::Event { 59 | event, 60 | should_update_ui, 61 | should_exit, 62 | } => { 63 | // Use the `winit` backend feature to convert the winit event to a conrod one. 64 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 65 | ui.handle_event(event); 66 | *should_update_ui = true; 67 | } 68 | 69 | match event { 70 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 71 | // Break from the loop upon `Escape`. 72 | glium::glutin::event::WindowEvent::CloseRequested 73 | | glium::glutin::event::WindowEvent::KeyboardInput { 74 | input: 75 | glium::glutin::event::KeyboardInput { 76 | virtual_keycode: 77 | Some(glium::glutin::event::VirtualKeyCode::Escape), 78 | .. 79 | }, 80 | .. 81 | } => *should_exit = true, 82 | _ => {} 83 | }, 84 | _ => {} 85 | } 86 | } 87 | support::Request::SetUi { needs_redraw } => { 88 | set_ui(ui.set_widgets(), &ids); 89 | 90 | *needs_redraw = ui.has_changed(); 91 | } 92 | support::Request::Redraw => { 93 | // Render the `Ui` and then display it on the screen. 94 | let primitives = ui.draw(); 95 | 96 | renderer.fill(display, primitives, &image_map); 97 | let mut target = display.draw(); 98 | target.clear_color(0.0, 0.0, 0.0, 1.0); 99 | renderer.draw(display, &mut target, &image_map).unwrap(); 100 | target.finish().unwrap(); 101 | } 102 | } 103 | }) 104 | } 105 | 106 | fn set_ui(ref mut ui: conrod_core::UiCell, ids: &Ids) { 107 | use conrod_core::widget::{Canvas, Circle, Line, Oval, PointPath, Polygon, Rectangle}; 108 | use conrod_core::{Positionable, Widget}; 109 | use std::iter::once; 110 | 111 | // The background canvas upon which we'll place our widgets. 112 | Canvas::new().pad(80.0).set(ids.canvas, ui); 113 | 114 | Line::centred([-40.0, -40.0], [40.0, 40.0]) 115 | .top_left_of(ids.canvas) 116 | .set(ids.line, ui); 117 | 118 | let left = [-40.0, -40.0]; 119 | let top = [0.0, 40.0]; 120 | let right = [40.0, -40.0]; 121 | let points = once(left).chain(once(top)).chain(once(right)); 122 | PointPath::centred(points) 123 | .down(80.0) 124 | .set(ids.point_path, ui); 125 | 126 | Rectangle::fill([80.0, 80.0]) 127 | .down(80.0) 128 | .set(ids.rectangle_fill, ui); 129 | 130 | Rectangle::outline([80.0, 80.0]) 131 | .down(80.0) 132 | .set(ids.rectangle_outline, ui); 133 | 134 | let bl = [-40.0, -40.0]; 135 | let tl = [-20.0, 40.0]; 136 | let tr = [20.0, 40.0]; 137 | let br = [40.0, -40.0]; 138 | let points = once(bl).chain(once(tl)).chain(once(tr)).chain(once(br)); 139 | Polygon::centred_fill(points) 140 | .right_from(ids.line, 80.0) 141 | .set(ids.trapezoid, ui); 142 | 143 | Oval::fill([40.0, 80.0]) 144 | .down(80.0) 145 | .align_middle_x() 146 | .set(ids.oval_fill, ui); 147 | 148 | Oval::outline([80.0, 40.0]) 149 | .down(100.0) 150 | .align_middle_x() 151 | .set(ids.oval_outline, ui); 152 | 153 | Circle::fill(40.0) 154 | .down(100.0) 155 | .align_middle_x() 156 | .set(ids.circle, ui); 157 | } 158 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/range_slider.rs: -------------------------------------------------------------------------------- 1 | //! A simple example demonstrating the `RangeSlider` widget. 2 | 3 | #[macro_use] 4 | extern crate conrod_core; 5 | extern crate conrod_glium; 6 | #[macro_use] 7 | extern crate conrod_winit; 8 | extern crate find_folder; 9 | extern crate glium; 10 | 11 | mod support; 12 | 13 | use glium::Surface; 14 | 15 | widget_ids! { 16 | struct Ids { canvas, oval, range_slider } 17 | } 18 | 19 | fn main() { 20 | const WIDTH: u32 = 360; 21 | const HEIGHT: u32 = 360; 22 | 23 | // Build the window. 24 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 25 | let window = glium::glutin::window::WindowBuilder::new() 26 | .with_title("RangeSlider Demo") 27 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 28 | let context = glium::glutin::ContextBuilder::new() 29 | .with_vsync(true) 30 | .with_multisampling(4); 31 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 32 | 33 | // Construct our `Ui`. 34 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 35 | 36 | // A unique identifier for each widget. 37 | let ids = Ids::new(ui.widget_id_generator()); 38 | 39 | // Add a `Font` to the `Ui`'s `font::Map` from file. 40 | let assets = find_folder::Search::KidsThenParents(3, 5) 41 | .for_folder("assets") 42 | .unwrap(); 43 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 44 | ui.fonts.insert_from_file(font_path).unwrap(); 45 | 46 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 47 | // for drawing to the glium `Surface`. 48 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 49 | 50 | // The image map describing each of our widget->image mappings (in our case, none). 51 | let image_map = conrod_core::image::Map::::new(); 52 | 53 | let mut oval_range = (0.25, 0.75); 54 | 55 | // Poll events from the window. 56 | support::run_loop(display, event_loop, move |request, display| { 57 | match request { 58 | support::Request::Event { 59 | event, 60 | should_update_ui, 61 | should_exit, 62 | } => { 63 | // Use the `winit` backend feature to convert the winit event to a conrod one. 64 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 65 | ui.handle_event(event); 66 | *should_update_ui = true; 67 | } 68 | 69 | match event { 70 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 71 | // Break from the loop upon `Escape`. 72 | glium::glutin::event::WindowEvent::CloseRequested 73 | | glium::glutin::event::WindowEvent::KeyboardInput { 74 | input: 75 | glium::glutin::event::KeyboardInput { 76 | virtual_keycode: 77 | Some(glium::glutin::event::VirtualKeyCode::Escape), 78 | .. 79 | }, 80 | .. 81 | } => *should_exit = true, 82 | _ => {} 83 | }, 84 | _ => {} 85 | } 86 | } 87 | support::Request::SetUi { needs_redraw } => { 88 | set_ui(ui.set_widgets(), &ids, &mut oval_range); 89 | 90 | *needs_redraw = ui.has_changed(); 91 | } 92 | support::Request::Redraw => { 93 | // Render the `Ui` and then display it on the screen. 94 | let primitives = ui.draw(); 95 | 96 | renderer.fill(display, primitives, &image_map); 97 | let mut target = display.draw(); 98 | target.clear_color(0.0, 0.0, 0.0, 1.0); 99 | renderer.draw(display, &mut target, &image_map).unwrap(); 100 | target.finish().unwrap(); 101 | } 102 | } 103 | }) 104 | } 105 | 106 | // Declare the `WidgetId`s and instantiate the widgets. 107 | fn set_ui( 108 | ref mut ui: conrod_core::UiCell, 109 | ids: &Ids, 110 | oval_range: &mut (conrod_core::Scalar, conrod_core::Scalar), 111 | ) { 112 | use conrod_core::{color, widget, Colorable, Positionable, Sizeable, Widget}; 113 | 114 | widget::Canvas::new() 115 | .color(color::DARK_CHARCOAL) 116 | .set(ids.canvas, ui); 117 | 118 | const PAD: conrod_core::Scalar = 20.0; 119 | let (ref mut start, ref mut end) = *oval_range; 120 | let min = 0.0; 121 | let max = 1.0; 122 | for (edge, value) in widget::RangeSlider::new(*start, *end, min, max) 123 | .color(color::LIGHT_BLUE) 124 | .padded_w_of(ids.canvas, PAD) 125 | .h(30.0) 126 | .mid_top_with_margin_on(ids.canvas, PAD) 127 | .set(ids.range_slider, ui) 128 | { 129 | match edge { 130 | widget::range_slider::Edge::Start => *start = value, 131 | widget::range_slider::Edge::End => *end = value, 132 | } 133 | } 134 | 135 | let range_slider_w = ui.w_of(ids.range_slider).unwrap(); 136 | let w = (*end - *start) * range_slider_w; 137 | let h = 200.0; 138 | widget::Oval::fill([w, h]) 139 | .mid_left_with_margin_on(ids.canvas, PAD + *start * range_slider_w) 140 | .color(color::LIGHT_BLUE) 141 | .down(50.0) 142 | .set(ids.oval, ui); 143 | } 144 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/support/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use glium::{ 4 | glutin::{event, event_loop}, 5 | Display, 6 | }; 7 | 8 | pub enum Request<'a, 'b: 'a> { 9 | Event { 10 | event: &'a event::Event<'b, ()>, 11 | should_update_ui: &'a mut bool, 12 | should_exit: &'a mut bool, 13 | }, 14 | SetUi { 15 | needs_redraw: &'a mut bool, 16 | }, 17 | Redraw, 18 | } 19 | 20 | /// In most of the examples the `glutin` crate is used for providing the window context and 21 | /// events while the `glium` crate is used for displaying `conrod_core::render::Primitives` to the 22 | /// screen. 23 | /// 24 | /// This function simplifies some of the boilerplate involved in limiting the redraw rate in the 25 | /// glutin+glium event loop. 26 | pub fn run_loop(display: Display, event_loop: event_loop::EventLoop<()>, mut callback: F) -> ! 27 | where 28 | F: 'static + FnMut(Request, &Display), 29 | { 30 | let sixteen_ms = std::time::Duration::from_millis(16); 31 | let mut next_update = None; 32 | let mut ui_update_needed = false; 33 | event_loop.run(move |event, _, control_flow| { 34 | { 35 | let mut should_update_ui = false; 36 | let mut should_exit = false; 37 | callback( 38 | Request::Event { 39 | event: &event, 40 | should_update_ui: &mut should_update_ui, 41 | should_exit: &mut should_exit, 42 | }, 43 | &display, 44 | ); 45 | ui_update_needed |= should_update_ui; 46 | if should_exit { 47 | *control_flow = event_loop::ControlFlow::Exit; 48 | return; 49 | } 50 | } 51 | 52 | // We don't want to draw any faster than 60 FPS, so set the UI only on every 16ms, unless: 53 | // - this is the very first event, or 54 | // - we didn't request update on the last event and new events have arrived since then. 55 | let should_set_ui_on_main_events_cleared = next_update.is_none() && ui_update_needed; 56 | match (&event, should_set_ui_on_main_events_cleared) { 57 | (event::Event::NewEvents(event::StartCause::Init { .. }), _) 58 | | (event::Event::NewEvents(event::StartCause::ResumeTimeReached { .. }), _) 59 | | (event::Event::MainEventsCleared, true) => { 60 | next_update = Some(std::time::Instant::now() + sixteen_ms); 61 | ui_update_needed = false; 62 | 63 | let mut needs_redraw = false; 64 | callback( 65 | Request::SetUi { 66 | needs_redraw: &mut needs_redraw, 67 | }, 68 | &display, 69 | ); 70 | if needs_redraw { 71 | display.gl_window().window().request_redraw(); 72 | } else { 73 | // We don't need to redraw anymore until more events arrives. 74 | next_update = None; 75 | } 76 | } 77 | _ => {} 78 | } 79 | if let Some(next_update) = next_update { 80 | *control_flow = event_loop::ControlFlow::WaitUntil(next_update); 81 | } else { 82 | *control_flow = event_loop::ControlFlow::Wait; 83 | } 84 | 85 | // Request redraw if needed. 86 | match &event { 87 | event::Event::RedrawRequested(_) => { 88 | callback(Request::Redraw, &display); 89 | } 90 | _ => {} 91 | } 92 | }) 93 | } 94 | 95 | // Conversion functions for converting between types from glium's version of `winit` and 96 | // `conrod_core`. 97 | conrod_winit::v023_conversion_fns!(); 98 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/text_edit.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate conrod_glium; 4 | #[macro_use] 5 | extern crate conrod_winit; 6 | extern crate find_folder; 7 | extern crate glium; 8 | 9 | mod support; 10 | 11 | use glium::Surface; 12 | 13 | widget_ids! { 14 | struct Ids { canvas, text_edit, scrollbar } 15 | } 16 | 17 | fn main() { 18 | const WIDTH: u32 = 360; 19 | const HEIGHT: u32 = 720; 20 | 21 | // Build the window. 22 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 23 | let window = glium::glutin::window::WindowBuilder::new() 24 | .with_title("TextEdit Demo") 25 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 26 | let context = glium::glutin::ContextBuilder::new() 27 | .with_vsync(true) 28 | .with_multisampling(4); 29 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 30 | 31 | // Construct our `Ui`. 32 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 33 | 34 | // A unique identifier for each widget. 35 | let ids = Ids::new(ui.widget_id_generator()); 36 | 37 | // Add a `Font` to the `Ui`'s `font::Map` from file. 38 | let assets = find_folder::Search::KidsThenParents(3, 5) 39 | .for_folder("assets") 40 | .unwrap(); 41 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 42 | ui.fonts.insert_from_file(font_path).unwrap(); 43 | 44 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 45 | // for drawing to the glium `Surface`. 46 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 47 | 48 | // The image map describing each of our widget->image mappings (in our case, none). 49 | let image_map = conrod_core::image::Map::::new(); 50 | 51 | // Some starting text to edit. 52 | let mut demo_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ 53 | Mauris aliquet porttitor tellus vel euismod. Integer lobortis volutpat bibendum. Nulla \ 54 | finibus odio nec elit condimentum, rhoncus fermentum purus lacinia. Interdum et malesuada \ 55 | fames ac ante ipsum primis in faucibus. Cras rhoncus nisi nec dolor bibendum pellentesque. \ 56 | Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. \ 57 | Quisque commodo nibh hendrerit nunc sollicitudin sodales. Cras vitae tempus ipsum. Nam \ 58 | magna est, efficitur suscipit dolor eu, consectetur consectetur urna." 59 | .to_owned(); 60 | 61 | // Poll events from the window. 62 | support::run_loop(display, event_loop, move |request, display| { 63 | match request { 64 | support::Request::Event { 65 | event, 66 | should_update_ui, 67 | should_exit, 68 | } => { 69 | // Use the `winit` backend feature to convert the winit event to a conrod one. 70 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 71 | ui.handle_event(event); 72 | *should_update_ui = true; 73 | } 74 | 75 | match event { 76 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 77 | // Break from the loop upon `Escape`. 78 | glium::glutin::event::WindowEvent::CloseRequested 79 | | glium::glutin::event::WindowEvent::KeyboardInput { 80 | input: 81 | glium::glutin::event::KeyboardInput { 82 | virtual_keycode: 83 | Some(glium::glutin::event::VirtualKeyCode::Escape), 84 | .. 85 | }, 86 | .. 87 | } => *should_exit = true, 88 | _ => {} 89 | }, 90 | _ => {} 91 | } 92 | } 93 | support::Request::SetUi { needs_redraw } => { 94 | // Instantiate all widgets in the GUI. 95 | set_ui(ui.set_widgets(), &ids, &mut demo_text); 96 | 97 | // Get the underlying winit window and update the mouse cursor as set by conrod. 98 | display 99 | .gl_window() 100 | .window() 101 | .set_cursor_icon(support::convert_mouse_cursor(ui.mouse_cursor())); 102 | 103 | *needs_redraw = ui.has_changed(); 104 | } 105 | support::Request::Redraw => { 106 | // Render the `Ui` and then display it on the screen. 107 | let primitives = ui.draw(); 108 | 109 | renderer.fill(display, primitives, &image_map); 110 | let mut target = display.draw(); 111 | target.clear_color(0.0, 0.0, 0.0, 1.0); 112 | renderer.draw(display, &mut target, &image_map).unwrap(); 113 | target.finish().unwrap(); 114 | } 115 | } 116 | }) 117 | } 118 | 119 | // Declare the `WidgetId`s and instantiate the widgets. 120 | fn set_ui(ref mut ui: conrod_core::UiCell, ids: &Ids, demo_text: &mut String) { 121 | use conrod_core::{color, widget, Colorable, Positionable, Sizeable, Widget}; 122 | 123 | widget::Canvas::new() 124 | .scroll_kids_vertically() 125 | .color(color::DARK_CHARCOAL) 126 | .set(ids.canvas, ui); 127 | 128 | for edit in widget::TextEdit::new(demo_text) 129 | .color(color::WHITE) 130 | .padded_w_of(ids.canvas, 20.0) 131 | .mid_top_of(ids.canvas) 132 | .center_justify() 133 | .line_spacing(2.5) 134 | //.restrict_to_height(false) // Let the height grow infinitely and scroll. 135 | .set(ids.text_edit, ui) 136 | { 137 | *demo_text = edit; 138 | } 139 | 140 | widget::Scrollbar::y_axis(ids.canvas) 141 | .auto_hide(true) 142 | .set(ids.scrollbar, ui); 143 | } 144 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/triangles.rs: -------------------------------------------------------------------------------- 1 | //! A simple example demonstrating the `Triangles` widget. 2 | 3 | #[macro_use] 4 | extern crate conrod_core; 5 | extern crate conrod_glium; 6 | extern crate glium; 7 | #[macro_use] 8 | extern crate conrod_winit; 9 | extern crate find_folder; 10 | 11 | mod support; 12 | 13 | use conrod_core::widget::triangles::Triangle; 14 | use conrod_core::{color, widget, Widget}; 15 | use glium::Surface; 16 | 17 | fn main() { 18 | const WIDTH: u32 = 700; 19 | const HEIGHT: u32 = 400; 20 | 21 | // Build the window. 22 | let event_loop = glium::glutin::event_loop::EventLoop::new(); 23 | let window = glium::glutin::window::WindowBuilder::new() 24 | .with_title("Triangles!") 25 | .with_inner_size(glium::glutin::dpi::LogicalSize::new(WIDTH, HEIGHT)); 26 | let context = glium::glutin::ContextBuilder::new() 27 | .with_vsync(true) 28 | .with_multisampling(4); 29 | let display = glium::Display::new(window, context, &event_loop).unwrap(); 30 | 31 | // construct our `Ui`. 32 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 33 | 34 | // Generate the widget identifiers. 35 | widget_ids!(struct Ids { triangles }); 36 | let ids = Ids::new(ui.widget_id_generator()); 37 | 38 | // Add a `Font` to the `Ui`'s `font::Map` from file. 39 | let assets = find_folder::Search::KidsThenParents(3, 5) 40 | .for_folder("assets") 41 | .unwrap(); 42 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 43 | ui.fonts.insert_from_file(font_path).unwrap(); 44 | 45 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 46 | // for drawing to the glium `Surface`. 47 | let mut renderer = conrod_glium::Renderer::new(&display).unwrap(); 48 | 49 | // The image map describing each of our widget->image mappings (in our case, none). 50 | let image_map = conrod_core::image::Map::::new(); 51 | 52 | support::run_loop(display, event_loop, move |request, display| { 53 | match request { 54 | support::Request::Event { 55 | event, 56 | should_update_ui, 57 | should_exit, 58 | } => { 59 | // Use the `winit` backend feature to convert the winit event to a conrod one. 60 | if let Some(event) = support::convert_event(&event, &display.gl_window().window()) { 61 | ui.handle_event(event); 62 | *should_update_ui = true; 63 | } 64 | 65 | match event { 66 | glium::glutin::event::Event::WindowEvent { event, .. } => match event { 67 | // Break from the loop upon `Escape`. 68 | glium::glutin::event::WindowEvent::CloseRequested 69 | | glium::glutin::event::WindowEvent::KeyboardInput { 70 | input: 71 | glium::glutin::event::KeyboardInput { 72 | virtual_keycode: 73 | Some(glium::glutin::event::VirtualKeyCode::Escape), 74 | .. 75 | }, 76 | .. 77 | } => *should_exit = true, 78 | _ => {} 79 | }, 80 | _ => {} 81 | } 82 | } 83 | support::Request::SetUi { needs_redraw } => { 84 | // Set the triangle widget. 85 | let ui = &mut ui.set_widgets(); 86 | let rect = ui.rect_of(ui.window).unwrap(); 87 | let (l, r, b, t) = rect.l_r_b_t(); 88 | let (c1, c2, c3) = ( 89 | color::RED.to_rgb(), 90 | color::GREEN.to_rgb(), 91 | color::BLUE.to_rgb(), 92 | ); 93 | 94 | let triangles = [ 95 | Triangle([([l, b], c1), ([l, t], c2), ([r, t], c3)]), 96 | Triangle([([r, t], c1), ([r, b], c2), ([l, b], c3)]), 97 | ]; 98 | 99 | widget::Triangles::multi_color(triangles.iter().cloned()) 100 | .with_bounding_rect(rect) 101 | .set(ids.triangles, ui); 102 | 103 | *needs_redraw = ui.has_changed(); 104 | } 105 | support::Request::Redraw => { 106 | // Draw the `Ui` if it has changed. 107 | let primitives = ui.draw(); 108 | 109 | renderer.fill(display, primitives, &image_map); 110 | let mut target = display.draw(); 111 | target.clear_color(0.0, 0.0, 0.0, 1.0); 112 | renderer.draw(display, &mut target, &image_map).unwrap(); 113 | target.finish().unwrap(); 114 | } 115 | } 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /backends/conrod_glium/examples/tutorial/chapter3_hello_world.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate conrod_core; 3 | extern crate conrod_glium; 4 | #[macro_use] 5 | extern crate conrod_winit; 6 | extern crate find_folder; 7 | extern crate glium; 8 | 9 | use conrod_core::{widget, Positionable, Colorable, Widget}; 10 | use glium::Surface; 11 | 12 | fn main() { 13 | const WIDTH: u32 = 400; 14 | const HEIGHT: u32 = 200; 15 | 16 | // Build the window. 17 | let mut events_loop = glium::glutin::EventsLoop::new(); 18 | let window = glium::glutin::WindowBuilder::new() 19 | .with_title("Hello Conrod") 20 | .with_dimensions((WIDTH, HEIGHT).into()); 21 | let context = glium::glutin::ContextBuilder::new() 22 | .with_vsync(true) 23 | .with_multisampling(4); 24 | let display = glium::Display::new(window, context, &events_loop).unwrap(); 25 | let display = GliumDisplayWinitWrapper(display); 26 | 27 | // construct our `Ui`. 28 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); 29 | 30 | // Generate the widget identifiers. 31 | widget_ids!(struct Ids { text }); 32 | let ids = Ids::new(ui.widget_id_generator()); 33 | 34 | // Add a `Font` to the `Ui`'s `font::Map` from file. 35 | let assets = find_folder::Search::KidsThenParents(3, 5) 36 | .for_folder("assets") 37 | .unwrap(); 38 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 39 | ui.fonts.insert_from_file(font_path).unwrap(); 40 | 41 | // The image map describing each of our widget->image mappings (in our case, none). 42 | let image_map = conrod_core::image::Map::::new(); 43 | 44 | // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used 45 | // for drawing to the glium `Surface`. 46 | let mut renderer = conrod_glium::Renderer::new(&display.0).unwrap(); 47 | 48 | let mut event_loop = EventLoop::new(); 49 | 'main: loop { 50 | 51 | let mut events = Vec::new(); 52 | events_loop.poll_events(|event| events.push(event)); 53 | 54 | for event in event_loop.next(&mut events_loop) { 55 | 56 | // Use the `winit` backend feature to convert the winit event to a conrod one. 57 | if let Some(event) = conrod_winit::convert_event(event.clone(), &display) { 58 | ui.handle_event(event); 59 | event_loop.needs_update(); 60 | } 61 | 62 | match event { 63 | glium::glutin::Event::WindowEvent { event, .. } => { 64 | match event { 65 | glium::glutin::WindowEvent::CloseRequested | 66 | glium::glutin::WindowEvent::KeyboardInput { 67 | input: glium::glutin::KeyboardInput { 68 | virtual_keycode: Some(glium::glutin::VirtualKeyCode::Escape), .. 69 | }, 70 | .. 71 | } => break 'main, 72 | _ => (), 73 | } 74 | } 75 | _ => (), 76 | } 77 | } 78 | 79 | // Instantiate all widgets in the GUI. 80 | { 81 | let ui = &mut ui.set_widgets(); 82 | 83 | // "Hello World!" in the middle of the screen. 84 | widget::Text::new("Hello World!") 85 | .middle_of(ui.window) 86 | .color(conrod_core::color::WHITE) 87 | .font_size(32) 88 | .set(ids.text, ui); 89 | } 90 | 91 | 92 | // Render the `Ui` and then display it on the screen. 93 | if let Some(primitives) = ui.draw_if_changed() { 94 | renderer.fill(&display.0, primitives, &image_map); 95 | let mut target = display.0.draw(); 96 | target.clear_color(0.0, 1.0, 0.0, 1.0); 97 | renderer.draw(&display.0, &mut target, &image_map).unwrap(); 98 | target.finish().unwrap(); 99 | } 100 | } 101 | } 102 | 103 | pub struct EventLoop { 104 | ui_needs_update: bool, 105 | last_update: std::time::Instant, 106 | } 107 | 108 | impl EventLoop { 109 | pub fn new() -> Self { 110 | EventLoop { 111 | last_update: std::time::Instant::now(), 112 | ui_needs_update: true, 113 | } 114 | } 115 | 116 | /// Produce an iterator yielding all available events. 117 | pub fn next( 118 | &mut self, 119 | events_loop: &mut glium::glutin::EventsLoop, 120 | ) -> Vec { 121 | 122 | // We don't want to loop any faster than 60 FPS, so wait until it has been at least 16ms 123 | // since the last yield. 124 | let last_update = self.last_update; 125 | let sixteen_ms = std::time::Duration::from_millis(16); 126 | let duration_since_last_update = std::time::Instant::now().duration_since(last_update); 127 | if duration_since_last_update < sixteen_ms { 128 | std::thread::sleep(sixteen_ms - duration_since_last_update); 129 | } 130 | 131 | // Collect all pending events. 132 | let mut events = Vec::new(); 133 | events_loop.poll_events(|event| events.push(event)); 134 | 135 | // If there are no events and the `Ui` does not need updating, wait for the next event. 136 | if events.is_empty() && !self.ui_needs_update { 137 | events_loop.run_forever(|event| { 138 | events.push(event); 139 | glium::glutin::ControlFlow::Break 140 | }); 141 | } 142 | 143 | self.ui_needs_update = false; 144 | self.last_update = std::time::Instant::now(); 145 | 146 | events 147 | } 148 | 149 | /// Notifies the event loop that the `Ui` requires another update whether or not there are any 150 | /// pending events. 151 | /// 152 | /// This is primarily used on the occasion that some part of the `Ui` is still animating and 153 | /// requires further updates to do so. 154 | pub fn needs_update(&mut self) { 155 | self.ui_needs_update = true; 156 | } 157 | } 158 | 159 | pub struct GliumDisplayWinitWrapper(pub glium::Display); 160 | 161 | impl conrod_winit::WinitWindow for GliumDisplayWinitWrapper { 162 | fn get_inner_size(&self) -> Option<(u32, u32)> { 163 | self.0.gl_window().get_inner_size().map(Into::into) 164 | } 165 | fn hidpi_factor(&self) -> f32 { 166 | self.0.gl_window().get_hidpi_factor() as _ 167 | } 168 | } 169 | 170 | // Generate functions for converting between types from glium's version of `winit` and 171 | // `conrod_core`. 172 | conrod_winit::conversion_fns!(); 173 | -------------------------------------------------------------------------------- /backends/conrod_piston/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_piston" 3 | version = "0.76.1" 4 | authors = [ 5 | "Mitchell Nordine ", 6 | "Sven Nilsen " 7 | ] 8 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 9 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 10 | license = "MIT OR Apache-2.0" 11 | readme = "../../README.md" 12 | repository = "https://github.com/pistondevelopers/conrod.git" 13 | homepage = "https://github.com/pistondevelopers/conrod" 14 | categories = ["gui"] 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [lib] 20 | name = "conrod_piston" 21 | path = "./src/lib.rs" 22 | 23 | [dependencies] 24 | conrod_core = { path = "../../conrod_core", version = "0.76" } 25 | piston2d-graphics = { version = "0.40" } 26 | pistoncore-input = "1.0.0" 27 | 28 | [dev-dependencies] 29 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 30 | find_folder = "0.3.0" 31 | image = "0.23" 32 | petgraph = "0.4" 33 | piston_window = "0.120" 34 | -------------------------------------------------------------------------------- /backends/conrod_piston/examples/all_piston_window.rs: -------------------------------------------------------------------------------- 1 | //! An example demonstrating all widgets in a long, vertically scrollable window. 2 | 3 | extern crate conrod_core; 4 | extern crate conrod_example_shared; 5 | extern crate conrod_piston; 6 | extern crate find_folder; 7 | extern crate piston_window; 8 | 9 | use self::piston_window::texture::UpdateTexture; 10 | use self::piston_window::OpenGL; 11 | use self::piston_window::{Flip, G2d, G2dTexture, Texture, TextureSettings}; 12 | use self::piston_window::{PistonWindow, UpdateEvent, Window, WindowSettings}; 13 | 14 | pub fn main() { 15 | const WIDTH: u32 = conrod_example_shared::WIN_W; 16 | const HEIGHT: u32 = conrod_example_shared::WIN_H; 17 | 18 | // Construct the window. 19 | let mut window: PistonWindow = 20 | WindowSettings::new("All Widgets - Piston Backend", [WIDTH, HEIGHT]) 21 | .graphics_api(OpenGL::V3_2) // If not working, try `OpenGL::V2_1`. 22 | .samples(4) 23 | .exit_on_esc(true) 24 | .vsync(true) 25 | .build() 26 | .unwrap(); 27 | 28 | // construct our `Ui`. 29 | let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]) 30 | .theme(conrod_example_shared::theme()) 31 | .build(); 32 | 33 | // Add a `Font` to the `Ui`'s `font::Map` from file. 34 | let assets = find_folder::Search::KidsThenParents(3, 5) 35 | .for_folder("assets") 36 | .unwrap(); 37 | let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); 38 | ui.fonts.insert_from_file(font_path).unwrap(); 39 | 40 | // Create texture context to perform operations on textures. 41 | let mut texture_context = window.create_texture_context(); 42 | 43 | // Create a texture to use for efficiently caching text on the GPU. 44 | let mut text_vertex_data = Vec::new(); 45 | let (mut glyph_cache, mut text_texture_cache) = { 46 | const SCALE_TOLERANCE: f32 = 0.1; 47 | const POSITION_TOLERANCE: f32 = 0.1; 48 | let cache = conrod_core::text::GlyphCache::builder() 49 | .dimensions(WIDTH, HEIGHT) 50 | .scale_tolerance(SCALE_TOLERANCE) 51 | .position_tolerance(POSITION_TOLERANCE) 52 | .build(); 53 | let buffer_len = WIDTH as usize * HEIGHT as usize; 54 | let init = vec![128; buffer_len]; 55 | let settings = TextureSettings::new(); 56 | let texture = 57 | G2dTexture::from_memory_alpha(&mut texture_context, &init, WIDTH, HEIGHT, &settings) 58 | .unwrap(); 59 | (cache, texture) 60 | }; 61 | 62 | // Instantiate the generated list of widget identifiers. 63 | let ids = conrod_example_shared::Ids::new(ui.widget_id_generator()); 64 | 65 | // Load the rust logo from file to a piston_window texture. 66 | let rust_logo: G2dTexture = { 67 | let assets = find_folder::Search::ParentsThenKids(5, 3) 68 | .for_folder("assets") 69 | .unwrap(); 70 | let path = assets.join("images/rust.png"); 71 | let settings = TextureSettings::new(); 72 | Texture::from_path(&mut texture_context, &path, Flip::None, &settings).unwrap() 73 | }; 74 | 75 | // Create our `conrod_core::image::Map` which describes each of our widget->image mappings. 76 | let mut image_map = conrod_core::image::Map::new(); 77 | let rust_logo = image_map.insert(rust_logo); 78 | 79 | // A demonstration of some state that we'd like to control with the App. 80 | let mut app = conrod_example_shared::DemoApp::new(rust_logo); 81 | 82 | // Poll events from the window. 83 | while let Some(event) = window.next() { 84 | // Convert the src event to a conrod event. 85 | let size = window.size(); 86 | let (win_w, win_h) = ( 87 | size.width as conrod_core::Scalar, 88 | size.height as conrod_core::Scalar, 89 | ); 90 | if let Some(e) = conrod_piston::event::convert(event.clone(), win_w, win_h) { 91 | ui.handle_event(e); 92 | } 93 | 94 | event.update(|_| { 95 | let mut ui = ui.set_widgets(); 96 | conrod_example_shared::gui(&mut ui, &ids, &mut app); 97 | }); 98 | 99 | window.draw_2d(&event, |context, graphics, device| { 100 | if let Some(primitives) = ui.draw_if_changed() { 101 | // A function used for caching glyphs to the texture cache. 102 | let cache_queued_glyphs = |_graphics: &mut G2d, 103 | cache: &mut G2dTexture, 104 | rect: conrod_core::text::rt::Rect, 105 | data: &[u8]| { 106 | let offset = [rect.min.x, rect.min.y]; 107 | let size = [rect.width(), rect.height()]; 108 | let format = piston_window::texture::Format::Rgba8; 109 | text_vertex_data.clear(); 110 | text_vertex_data.extend(data.iter().flat_map(|&b| vec![255, 255, 255, b])); 111 | UpdateTexture::update( 112 | cache, 113 | &mut texture_context, 114 | format, 115 | &text_vertex_data[..], 116 | offset, 117 | size, 118 | ) 119 | .expect("failed to update texture") 120 | }; 121 | 122 | // Specify how to get the drawable texture from the image. In this case, the image 123 | // *is* the texture. 124 | fn texture_from_image(img: &T) -> &T { 125 | img 126 | } 127 | 128 | // Draw the conrod `render::Primitives`. 129 | conrod_piston::draw::primitives( 130 | primitives, 131 | context, 132 | graphics, 133 | &mut text_texture_cache, 134 | &mut glyph_cache, 135 | &image_map, 136 | cache_queued_glyphs, 137 | texture_from_image, 138 | ); 139 | 140 | texture_context.encoder.flush(device); 141 | } 142 | }); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /backends/conrod_piston/src/event.rs: -------------------------------------------------------------------------------- 1 | //! A backend for converting src events to conrod's `Input` type. 2 | 3 | use conrod_core::{event, input, Point, Scalar}; 4 | pub use piston_input::{GenericEvent, UpdateEvent}; 5 | 6 | /// Converts any `GenericEvent` to an `Input` event for conrod. 7 | /// 8 | /// The given `width` and `height` must be `Scalar` (DPI agnostic) values. 9 | pub fn convert(event: E, win_w: Scalar, win_h: Scalar) -> Option 10 | where 11 | E: GenericEvent, 12 | { 13 | // Translate the coordinates from top-left-origin-with-y-down to centre-origin-with-y-up. 14 | let translate_coords = |xy: Point| (xy[0] - win_w / 2.0, -(xy[1] - win_h / 2.0)); 15 | 16 | if let Some(xy) = event.mouse_cursor_args() { 17 | let (x, y) = translate_coords(xy); 18 | return Some(event::Input::Motion(input::Motion::MouseCursor { 19 | x: x, 20 | y: y, 21 | })); 22 | } 23 | 24 | if let Some(rel_xy) = event.mouse_relative_args() { 25 | let (rel_x, rel_y) = translate_coords(rel_xy); 26 | return Some(event::Input::Motion(input::Motion::MouseRelative { 27 | x: rel_x, 28 | y: rel_y, 29 | })); 30 | } 31 | 32 | if let Some(xy) = event.mouse_scroll_args() { 33 | // Invert the scrolling of the *y* axis as *y* is up in conrod. 34 | let (x, y) = (xy[0], -xy[1]); 35 | return Some(event::Input::Motion(input::Motion::Scroll { x: x, y: y })); 36 | } 37 | 38 | if let Some(args) = event.controller_axis_args() { 39 | return Some(event::Input::Motion(input::Motion::ControllerAxis(args))); 40 | } 41 | 42 | if let Some(args) = event.touch_args() { 43 | let id = input::touch::Id::new(args.id as u64); 44 | let xy = args.position(); 45 | let phase = match args.touch { 46 | ::piston_input::Touch::Start => input::touch::Phase::Start, 47 | ::piston_input::Touch::Move => input::touch::Phase::Move, 48 | ::piston_input::Touch::Cancel => input::touch::Phase::Cancel, 49 | ::piston_input::Touch::End => input::touch::Phase::End, 50 | }; 51 | let touch = input::Touch { 52 | id: id, 53 | xy: xy, 54 | phase: phase, 55 | }; 56 | return Some(event::Input::Touch(touch)); 57 | } 58 | 59 | if let Some(button) = event.press_args() { 60 | return Some(event::Input::Press(button)); 61 | } 62 | 63 | if let Some(button) = event.release_args() { 64 | return Some(event::Input::Release(button)); 65 | } 66 | 67 | if let Some(text) = event.text_args() { 68 | return Some(event::Input::Text(text)); 69 | } 70 | 71 | if let Some(args) = event.resize_args() { 72 | return Some(event::Input::Resize( 73 | args.window_size[0], 74 | args.window_size[1], 75 | )); 76 | } 77 | 78 | if let Some(b) = event.focus_args() { 79 | return Some(event::Input::Focus(b)); 80 | } 81 | 82 | None 83 | } 84 | -------------------------------------------------------------------------------- /backends/conrod_piston/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Functionality for simplifying the work involved when using conrod along-side src. 2 | 3 | extern crate conrod_core; 4 | extern crate graphics as piston_graphics; 5 | extern crate input as piston_input; 6 | 7 | pub mod draw; 8 | pub mod event; 9 | -------------------------------------------------------------------------------- /backends/conrod_rendy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_rendy" 3 | version = "0.76.1" 4 | authors = [ 5 | "David Partouche ", 6 | "mitchmindtree ", 7 | ] 8 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 9 | description = "A rendy backend for conrod." 10 | license = "MIT OR Apache-2.0" 11 | readme = "../../README.md" 12 | repository = "https://github.com/pistondevelopers/conrod.git" 13 | homepage = "https://github.com/pistondevelopers/conrod" 14 | categories = ["gui"] 15 | edition = "2018" 16 | 17 | [dependencies] 18 | conrod_core = { path = "../../conrod_core", version = "0.76" } 19 | lazy_static = "1.4.0" 20 | rendy = { version = "0.5.1", default-features = false, features = ["base", "texture"] } 21 | 22 | [features] 23 | empty = ["rendy/empty"] 24 | dx12 = ["rendy/dx12"] 25 | gl = ["rendy/gl"] 26 | metal = ["rendy/metal"] 27 | vulkan = ["rendy/vulkan"] 28 | init-winit = ["rendy/init-winit"] 29 | no-slow-safety-checks = ["rendy/no-slow-safety-checks"] 30 | profiler = ["rendy/profiler"] 31 | 32 | [dev-dependencies] 33 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 34 | conrod_winit = { path = "../conrod_winit", version = "0.76" } 35 | find_folder = "0.3.0" 36 | image = "0.22" 37 | 38 | [[example]] 39 | name = "all_winit_rendy" 40 | required-features = ["init-winit"] 41 | -------------------------------------------------------------------------------- /backends/conrod_rendy/src/shaders/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/backends/conrod_rendy/src/shaders/frag.spv -------------------------------------------------------------------------------- /backends/conrod_rendy/src/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | // NOTE: This shader requires being manually compiled to SPIR-V in order to 2 | // avoid having downstream users require building shaderc and compiling the 3 | // shader themselves. If you update this shader, be sure to also re-compile it 4 | // and update `frag.spv`. You can do so using `glslangValidator` with the 5 | // following command: `glslangValidator -V -o frag.spv shader.frag` 6 | 7 | #version 450 8 | 9 | layout(set = 0, binding = 0) uniform sampler2D t_TextColor; 10 | layout(set = 0, binding = 1) uniform sampler2D t_ImgColor; 11 | 12 | layout(location = 0) in vec2 v_Uv; 13 | layout(location = 1) in vec4 v_Color; 14 | layout(location = 2) flat in uint v_Mode; 15 | 16 | layout(location = 0) out vec4 Target0; 17 | 18 | void main() { 19 | // Text 20 | if (v_Mode == uint(0)) { 21 | Target0 = v_Color * vec4(1.0, 1.0, 1.0, texture(t_TextColor, v_Uv).r); 22 | 23 | // Image 24 | } else if (v_Mode == uint(1)) { 25 | Target0 = texture(t_ImgColor, v_Uv); 26 | 27 | // 2D Geometry 28 | } else if (v_Mode == uint(2)) { 29 | Target0 = v_Color; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backends/conrod_rendy/src/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | // NOTE: This shader requires being manually compiled to SPIR-V in order to 2 | // avoid having downstream users require building shaderc and compiling the 3 | // shader themselves. If you update this shader, be sure to also re-compile it 4 | // and update `vert.spv`. You can do so using `glslangValidator` with the 5 | // following command: `glslangValidator -V -o vert.spv shader.vert` 6 | 7 | #version 450 8 | 9 | layout(location = 0) in vec2 pos; 10 | layout(location = 1) in vec2 uv; 11 | layout(location = 2) in vec4 color; 12 | layout(location = 3) in uint mode; 13 | 14 | layout(location = 0) out vec2 v_Uv; 15 | layout(location = 1) out vec4 v_Color; 16 | layout(location = 2) flat out uint v_Mode; 17 | 18 | void main() { 19 | v_Uv = uv; 20 | v_Color = color; 21 | gl_Position = vec4(pos, 0.0, 1.0); 22 | v_Mode = mode; 23 | } 24 | -------------------------------------------------------------------------------- /backends/conrod_rendy/src/shaders/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/backends/conrod_rendy/src/shaders/vert.spv -------------------------------------------------------------------------------- /backends/conrod_rendy/src/vertex.rs: -------------------------------------------------------------------------------- 1 | use rendy::{ 2 | hal::format::Format, 3 | mesh::{AsAttribute, AsVertex, VertexFormat}, 4 | }; 5 | 6 | /// The position of the vertex within vector space. 7 | /// 8 | /// [-1.0, 1.0] is the leftmost, bottom position of the display. 9 | /// [1.0, -1.0] is the rightmost, top position of the display. 10 | #[repr(transparent)] 11 | #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] 12 | pub struct Position(pub [f32; 2]); 13 | 14 | /// The coordinates of the texture used by this `Vertex`. 15 | /// 16 | /// [0.0, 0.0] is the leftmost, top position of the texture. 17 | /// [1.0, 1.0] is the rightmost, bottom position of the texture. 18 | #[repr(transparent)] 19 | #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] 20 | pub struct TexCoords(pub [f32; 2]); 21 | 22 | /// A color associated with the `Vertex`. 23 | /// 24 | /// The way that the color is used depends on the `mode`. 25 | #[repr(transparent)] 26 | #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] 27 | pub struct Color(pub [f32; 4]); 28 | 29 | /// The mode with which the `Vertex` will be drawn within the fragment shader. 30 | /// 31 | /// `0` for rendering text. 32 | /// `1` for rendering an image. 33 | /// `2` for rendering non-textured 2D geometry. 34 | /// 35 | /// If any other value is given, the fragment shader will not output any color. 36 | #[repr(transparent)] 37 | #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] 38 | pub struct Mode(pub u32); 39 | 40 | /// The `Vertex` type passed to the vertex shader. 41 | #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] 42 | #[repr(C)] 43 | pub struct Vertex { 44 | pub pos: Position, 45 | pub uv: TexCoords, 46 | pub color: Color, 47 | pub mode: Mode, 48 | } 49 | 50 | impl From<[f32; 2]> for Position { 51 | fn from(v: [f32; 2]) -> Self { 52 | Position(v) 53 | } 54 | } 55 | 56 | impl From<[f32; 2]> for TexCoords { 57 | fn from(v: [f32; 2]) -> Self { 58 | TexCoords(v) 59 | } 60 | } 61 | 62 | impl From<[f32; 4]> for Color { 63 | fn from(v: [f32; 4]) -> Self { 64 | Color(v) 65 | } 66 | } 67 | 68 | impl From for Mode { 69 | fn from(v: u32) -> Self { 70 | Mode(v) 71 | } 72 | } 73 | 74 | impl AsAttribute for Position { 75 | const NAME: &'static str = "pos"; 76 | const FORMAT: Format = Format::Rg32Sfloat; 77 | } 78 | 79 | impl AsAttribute for TexCoords { 80 | const NAME: &'static str = "uv"; 81 | const FORMAT: Format = Format::Rg32Sfloat; 82 | } 83 | 84 | impl AsAttribute for Color { 85 | const NAME: &'static str = "color"; 86 | const FORMAT: Format = Format::Rgba32Sfloat; 87 | } 88 | 89 | impl AsAttribute for Mode { 90 | const NAME: &'static str = "mode"; 91 | const FORMAT: Format = Format::R32Uint; 92 | } 93 | 94 | impl AsVertex for Vertex { 95 | fn vertex() -> VertexFormat { 96 | VertexFormat::new(( 97 | Position::vertex(), 98 | TexCoords::vertex(), 99 | Color::vertex(), 100 | Mode::vertex(), 101 | )) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /backends/conrod_vulkano/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_vulkano" 3 | version = "0.76.1" 4 | authors = [ 5 | "mitchmindtree ", 6 | "Kurble", 7 | "Abendstolz", 8 | ] 9 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 10 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 11 | license = "MIT OR Apache-2.0" 12 | readme = "../../README.md" 13 | repository = "https://github.com/pistondevelopers/conrod.git" 14 | homepage = "https://github.com/pistondevelopers/conrod" 15 | categories = ["gui"] 16 | edition = "2018" 17 | 18 | [dependencies] 19 | conrod_core = { path = "../../conrod_core", version = "0.76" } 20 | vulkano = "0.26" 21 | 22 | [dev-dependencies] 23 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 24 | conrod_winit = { path = "../conrod_winit", version = "0.76" } 25 | find_folder = "0.3" 26 | image = "0.23" 27 | vulkano-win = "0.26" 28 | winit = "0.25" 29 | -------------------------------------------------------------------------------- /backends/conrod_vulkano/examples/support/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use vulkano::{ 3 | self, 4 | device::{Device, Queue}, 5 | image::SwapchainImage, 6 | instance::Instance, 7 | swapchain::{ColorSpace, Surface, Swapchain, SwapchainCreationError}, 8 | Version, 9 | }; 10 | 11 | use vulkano::device::physical::PhysicalDevice; 12 | use vulkano::format::NumericType; 13 | use vulkano::image::ImageUsage; 14 | use vulkano_win::{self, VkSurfaceBuild}; 15 | 16 | pub struct Window { 17 | pub surface: Arc>, 18 | pub swapchain: Arc>, 19 | pub queue: Arc, 20 | pub device: Arc, 21 | pub images: Vec>>, 22 | } 23 | 24 | impl Window { 25 | pub fn new( 26 | width: u32, 27 | height: u32, 28 | title: &str, 29 | event_loop: &winit::event_loop::EventLoop, 30 | ) -> Self { 31 | let size = winit::dpi::LogicalSize::new(width as f64, height as f64); 32 | let (width, height): (u32, u32) = size.into(); 33 | 34 | let instance: Arc = { 35 | let extensions = vulkano_win::required_extensions(); 36 | Instance::new( 37 | None, 38 | Version { 39 | major: 1, 40 | minor: 0, 41 | patch: 0, 42 | }, 43 | &extensions, 44 | None, 45 | ) 46 | .expect("failed to create Vulkan instance") 47 | }; 48 | 49 | let cloned_instance = instance.clone(); 50 | 51 | let physical: PhysicalDevice = PhysicalDevice::enumerate(&cloned_instance) 52 | .next() 53 | .expect("no device available"); 54 | 55 | let surface = winit::window::WindowBuilder::new() 56 | .with_inner_size(size) 57 | .with_title(title) 58 | .build_vk_surface(event_loop, instance.clone()) 59 | .unwrap(); 60 | 61 | let queue = physical 62 | .queue_families() 63 | .find(|&q| q.supports_graphics() && surface.is_supported(q).unwrap_or(false)) 64 | .expect("couldn't find a graphical queue family"); 65 | 66 | let (device, mut queues) = { 67 | let device_ext = vulkano::device::DeviceExtensions { 68 | khr_swapchain: true, 69 | ..vulkano::device::DeviceExtensions::none() 70 | }; 71 | 72 | Device::new( 73 | physical, 74 | physical.supported_features(), 75 | &device_ext, 76 | [(queue, 0.5)].iter().cloned(), 77 | ) 78 | .expect("failed to create device") 79 | }; 80 | 81 | let queue = queues.next().unwrap(); 82 | let ((swapchain, images), _surface_dimensions) = { 83 | let caps = surface 84 | .capabilities(physical) 85 | .expect("failed to get surface capabilities"); 86 | 87 | let surface_dimensions = caps.current_extent.unwrap_or([width, height]); 88 | let _alpha = caps.supported_composite_alpha.iter().next().unwrap(); 89 | let format = caps 90 | .supported_formats 91 | .iter() 92 | .filter(|&&(fmt, cs)| { 93 | fmt.type_color() == Some(NumericType::SRGB) && cs == ColorSpace::SrgbNonLinear 94 | }) 95 | .map(|&(fmt, _)| fmt) 96 | .next() 97 | .expect("failed to find sRGB format"); 98 | 99 | ( 100 | Swapchain::start(device.clone(), surface.clone()) 101 | .num_images(caps.min_image_count) 102 | .format(format) 103 | .dimensions(surface_dimensions) 104 | .usage(ImageUsage::color_attachment()) 105 | .sharing_mode(&queue) 106 | .composite_alpha(caps.supported_composite_alpha.iter().next().unwrap()) 107 | .build() 108 | .expect("failed to create swapchain"), 109 | surface_dimensions, 110 | ) 111 | }; 112 | 113 | Self { 114 | surface, 115 | swapchain, 116 | queue, 117 | device, 118 | images, 119 | } 120 | } 121 | 122 | pub fn get_dimensions(&self) -> Option<(u32, u32)> { 123 | let inner_size = self.surface.window().inner_size(); 124 | Some(inner_size.into()) 125 | } 126 | 127 | pub fn handle_resize(&mut self) -> () { 128 | // Get the new dimensions for the viewport/framebuffers. 129 | let new_dimensions = self 130 | .surface 131 | .capabilities(self.device.physical_device()) 132 | .expect("failed to get surface capabilities") 133 | .current_extent 134 | .unwrap(); 135 | let (new_swapchain, new_images) = 136 | match self.swapchain.recreate().dimensions(new_dimensions).build() { 137 | Ok(r) => r, 138 | // This error tends to happen when the user is manually resizing the window. 139 | // Simply restarting the loop is the easiest way to fix this issue. 140 | Err(SwapchainCreationError::UnsupportedDimensions) => return self.handle_resize(), 141 | Err(err) => panic!("Window couldn't be resized! {:?}", err), 142 | }; 143 | 144 | self.swapchain = new_swapchain; 145 | self.images = new_images; 146 | } 147 | } 148 | 149 | // Implement the `WinitWindow` trait for `WindowRef` to allow for generating compatible conversion 150 | // functions. 151 | impl conrod_winit::WinitWindow for Window { 152 | fn get_inner_size(&self) -> Option<(u32, u32)> { 153 | Some(winit::window::Window::inner_size(self.surface.window()).into()) 154 | } 155 | fn hidpi_factor(&self) -> f32 { 156 | winit::window::Window::scale_factor(self.surface.window()) as _ 157 | } 158 | } 159 | 160 | // Generate the winit <-> conrod type conversion fns. 161 | conrod_winit::v023_conversion_fns!(); 162 | -------------------------------------------------------------------------------- /backends/conrod_vulkano/src/shaders/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/backends/conrod_vulkano/src/shaders/frag.spv -------------------------------------------------------------------------------- /backends/conrod_vulkano/src/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | // NOTE: This shader requires being manually compiled to SPIR-V in order to 2 | // avoid having downstream users require building shaderc and compiling the 3 | // shader themselves. If you update this shader, be sure to also re-compile it 4 | // and update `frag.spv`. You can do so using `glslangValidator` with the 5 | // following command: `glslangValidator -V -o frag.spv shader.frag` 6 | 7 | #version 450 8 | layout(set = 0, binding = 0) uniform sampler2D t_Color; 9 | layout(location = 0) in vec2 v_Uv; 10 | layout(location = 1) in vec4 v_Color; 11 | layout(location = 2) flat in uint v_Mode; 12 | layout(location = 0) out vec4 Target0; 13 | void main() { 14 | // Text 15 | if (v_Mode == uint(0)) { 16 | Target0 = v_Color * vec4(1.0, 1.0, 1.0, texture(t_Color, v_Uv).r); 17 | // Image 18 | } else if (v_Mode == uint(1)) { 19 | Target0 = texture(t_Color, v_Uv); 20 | // 2D Geometry 21 | } else if (v_Mode == uint(2)) { 22 | Target0 = v_Color; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backends/conrod_vulkano/src/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | // NOTE: This shader requires being manually compiled to SPIR-V in order to 2 | // avoid having downstream users require building shaderc and compiling the 3 | // shader themselves. If you update this shader, be sure to also re-compile it 4 | // and update `vert.spv`. You can do so using `glslangValidator` with the 5 | // following command: `glslangValidator -V -o vert.spv shader.vert` 6 | 7 | #version 450 8 | layout(location = 0) in vec2 position; 9 | layout(location = 1) in vec2 tex_coords; 10 | layout(location = 2) in vec4 rgba; 11 | layout(location = 3) in uint mode; 12 | layout(location = 0) out vec2 v_Uv; 13 | layout(location = 1) out vec4 v_Color; 14 | layout(location = 2) flat out uint v_Mode; 15 | void main() { 16 | v_Uv = tex_coords; 17 | v_Color = rgba; 18 | gl_Position = vec4(position, 0.0, 1.0); 19 | v_Mode = mode; 20 | } -------------------------------------------------------------------------------- /backends/conrod_vulkano/src/shaders/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/backends/conrod_vulkano/src/shaders/vert.spv -------------------------------------------------------------------------------- /backends/conrod_wgpu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_wgpu" 3 | version = "0.76.1" 4 | authors = [ 5 | "mitchmindtree ", 6 | ] 7 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 8 | description = "A crate to assist with rendering conrod UIs via wgpu." 9 | license = "MIT OR Apache-2.0" 10 | readme = "../../README.md" 11 | repository = "https://github.com/pistondevelopers/conrod.git" 12 | homepage = "https://github.com/pistondevelopers/conrod" 13 | categories = ["gui"] 14 | edition = "2018" 15 | 16 | [dependencies] 17 | conrod_core = { path = "../../conrod_core", version = "0.76" } 18 | wgpu = "0.11" 19 | 20 | [dev-dependencies] 21 | conrod_example_shared = { path = "../conrod_example_shared", version = "0.76" } 22 | conrod_winit = { path = "../conrod_winit", version = "0.76" } 23 | find_folder = "0.3" 24 | futures = "0.3" 25 | image = "0.23" 26 | winit = "0.25" 27 | -------------------------------------------------------------------------------- /backends/conrod_wgpu/src/shaders/frag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragmentOutput { 2 | [[location(0)]] color: vec4; 3 | }; 4 | 5 | [[group(0), binding(0)]] 6 | var text_texture: texture_2d; 7 | [[group(0), binding(1)]] 8 | var image_sampler: sampler; 9 | [[group(0), binding(2)]] 10 | var image_texture: texture_2d; 11 | 12 | [[stage(fragment)]] 13 | fn main( 14 | [[location(0)]] uv: vec2, 15 | [[location(1)]] color: vec4, 16 | [[location(2)]] mode: u32, 17 | ) -> FragmentOutput { 18 | var text_color: vec4 = textureSample(text_texture, image_sampler, uv); 19 | var image_color: vec4 = textureSample(image_texture, image_sampler, uv); 20 | var text_alpha: f32 = text_color.x; 21 | var out_color: vec4 = vec4(0.5, 0.0, 0.0, 1.0); 22 | if (mode == u32(0)) { 23 | out_color = color * vec4(1.0, 1.0, 1.0, text_alpha); 24 | } else { 25 | if (mode == u32(1)) { 26 | out_color = image_color; 27 | } else { 28 | if (mode == u32(2)) { 29 | out_color = color; 30 | } 31 | } 32 | } 33 | return FragmentOutput(out_color); 34 | } 35 | -------------------------------------------------------------------------------- /backends/conrod_wgpu/src/shaders/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | [[location(0)]] uv: vec2; 3 | [[location(1)]] color: vec4; 4 | [[location(2)]] mode: u32; 5 | [[builtin(position)]] pos: vec4; 6 | }; 7 | 8 | [[stage(vertex)]] 9 | fn main( 10 | [[location(0)]] pos: vec2, 11 | [[location(1)]] uv: vec2, 12 | [[location(2)]] color: vec4, 13 | [[location(3)]] mode: u32, 14 | ) -> VertexOutput { 15 | let out_pos = vec4(pos * vec2(1.0, -1.0), 0.0, 1.0); 16 | return VertexOutput(uv, color, mode, out_pos); 17 | } 18 | -------------------------------------------------------------------------------- /backends/conrod_winit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_winit" 3 | version = "0.76.1" 4 | authors = [ 5 | "Mitchell Nordine ", 6 | "Sven Nilsen " 7 | ] 8 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 9 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 10 | license = "MIT OR Apache-2.0" 11 | readme = "../../README.md" 12 | repository = "https://github.com/pistondevelopers/conrod.git" 13 | homepage = "https://github.com/pistondevelopers/conrod" 14 | categories = ["gui"] 15 | 16 | [lib] 17 | name = "conrod_winit" 18 | path = "./src/lib.rs" 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /backends/conrod_winit/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A function for converting a `winit::Event` to a `conrod::event::Input`. 2 | 3 | pub mod macros; 4 | pub mod v020; 5 | pub mod v021; 6 | pub mod v022; 7 | pub mod v023; 8 | 9 | /// Types that have access to a `winit::Window` and can provide the necessary dimensions and hidpi 10 | /// factor for converting `winit::Event`s to `conrod::event::Input`, as well as set the mouse 11 | /// cursor. 12 | /// 13 | /// This allows users to pass references to window types like `glium::Display`, 14 | /// `glium::glutin::Window` or `winit::Window` 15 | pub trait WinitWindow { 16 | /// Return the inner size of the window in logical pixels. 17 | fn get_inner_size(&self) -> Option<(u32, u32)>; 18 | /// Return the window's DPI factor so that we can convert from pixel values to scalar values. 19 | fn hidpi_factor(&self) -> f32; 20 | } 21 | -------------------------------------------------------------------------------- /backends/conrod_winit/src/v022.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! v022_convert_key { 3 | ($keycode:expr) => {{ 4 | $crate::v021_convert_key!($keycode) 5 | }}; 6 | } 7 | 8 | /// Maps winit's mouse button to conrod's mouse button. 9 | /// 10 | /// Expects a `winit::MouseButton` as input and returns a `conrod_core::input::MouseButton` as 11 | /// output. 12 | /// 13 | /// Requires that both the `conrod_core` and `winit` crates are in the crate root. 14 | #[macro_export] 15 | macro_rules! v022_convert_mouse_button { 16 | ($mouse_button:expr) => {{ 17 | $crate::v021_convert_mouse_button!($mouse_button) 18 | }}; 19 | } 20 | 21 | /// A macro for converting a `winit::WindowEvent` to a `Option`. 22 | /// 23 | /// Expects a `winit::WindowEvent` and a reference to a window implementing `WinitWindow`. 24 | /// Returns an `Option`. 25 | #[macro_export] 26 | macro_rules! v022_convert_window_event { 27 | ($event:expr, $window:expr) => {{ 28 | $crate::v021_convert_window_event!($event, $window) 29 | }}; 30 | } 31 | 32 | /// A macro for converting a `winit::Event` to a `conrod_core::event::Input`. 33 | /// 34 | /// Expects a `winit::Event` and a reference to a window implementing `WinitWindow`. 35 | /// Returns an `Option`. 36 | /// 37 | /// Invocations of this macro require that a version of the `winit` and `conrod_core` crates are 38 | /// available in the crate root. 39 | #[macro_export] 40 | macro_rules! v022_convert_event { 41 | ($event:expr, $window:expr) => {{ 42 | $crate::v021_convert_event!($event, $window) 43 | }}; 44 | } 45 | 46 | /// Convert a given conrod mouse cursor to the corresponding winit cursor type. 47 | /// 48 | /// Expects a `conrod_core::cursor::MouseCursor`, returns a `winit::MouseCursor`. 49 | /// 50 | /// Requires that both the `conrod_core` and `winit` crates are in the crate root. 51 | #[macro_export] 52 | macro_rules! v022_convert_mouse_cursor { 53 | ($cursor:expr) => {{ 54 | $crate::v021_convert_mouse_cursor!($cursor) 55 | }}; 56 | } 57 | 58 | #[macro_export] 59 | macro_rules! v022_conversion_fns { 60 | () => { 61 | $crate::v021_conversion_fns!(); 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /conrod_core/.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | notifications: 9 | irc: "irc.mozilla.org#src-internals" 10 | 11 | os: 12 | - linux 13 | 14 | addons: 15 | apt: 16 | packages: 17 | - libxxf86vm-dev 18 | - libosmesa6-dev 19 | 20 | script: 21 | - cargo build --verbose 22 | - cargo test --verbose 23 | # TODO move to appropriate render backend 24 | #- cargo test --features "winit glium" --verbose 25 | #- cargo test --features "src" --verbose 26 | #- cargo test --features "gfx_rs" --verbose 27 | #- cargo test --all-features --verbose 28 | #- cargo doc --all-features --verbose 29 | 30 | after_success: 31 | - curl http://docs.src.rs/travis-doc-upload.sh | sh 32 | -------------------------------------------------------------------------------- /conrod_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_core" 3 | version = "0.76.1" 4 | authors = [ 5 | "Mitchell Nordine ", 6 | "Sven Nilsen " 7 | ] 8 | keywords = ["ui", "widgets", "gui", "interface", "graphics"] 9 | description = "An easy-to-use, 100% Rust, extensible 2D GUI library." 10 | license = "MIT OR Apache-2.0" 11 | readme = "../README.md" 12 | repository = "https://github.com/pistondevelopers/conrod.git" 13 | homepage = "https://github.com/pistondevelopers/conrod" 14 | categories = ["gui"] 15 | 16 | [package.metadata.docs.rs] 17 | all-features = true 18 | 19 | [features] 20 | stdweb = [ "instant/stdweb" ] 21 | wasm-bindgen = [ "instant/wasm-bindgen" ] 22 | 23 | [dependencies] 24 | conrod_derive = { path = "../conrod_derive", version = "0.76" } 25 | daggy = "0.5" 26 | fnv = "1.0" 27 | num = "0.3" 28 | pistoncore-input = "1.0.0" 29 | rusttype = { version = "0.8.3", features = ["gpu_cache"] } 30 | instant = "0.1" 31 | copypasta = "0.6" 32 | -------------------------------------------------------------------------------- /conrod_core/src/border.rs: -------------------------------------------------------------------------------- 1 | use color::{hsl, hsla, rgb, rgba, Color}; 2 | 3 | /// To be used as a parameter for defining the aesthetic 4 | /// of the widget border. 5 | #[derive(Copy, Clone)] 6 | pub enum Bordering { 7 | /// Border width and color. 8 | Border(f64, Color), 9 | /// No border. 10 | NoBorder, 11 | } 12 | 13 | /// Widgets that may display a border. 14 | pub trait Borderable: Sized { 15 | /// Set the width of the widget's border. 16 | fn border(self, width: f64) -> Self; 17 | 18 | /// Set the color of the widget's border. 19 | fn border_color(self, color: Color) -> Self; 20 | 21 | /// Set the color of the widget's border with rgba values. 22 | fn border_rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self { 23 | self.border_color(rgba(r, g, b, a)) 24 | } 25 | 26 | /// Set the color of the widget's border with rgb values. 27 | fn border_rgb(self, r: f32, g: f32, b: f32) -> Self { 28 | self.border_color(rgb(r, g, b)) 29 | } 30 | 31 | /// Set the color of the widget's border with hsla values. 32 | fn border_hsla(self, h: f32, s: f32, l: f32, a: f32) -> Self { 33 | self.border_color(hsla(h, s, l, a)) 34 | } 35 | 36 | /// Set the color of the widget's border with hsl values. 37 | fn border_hsl(self, h: f32, s: f32, l: f32) -> Self { 38 | self.border_color(hsl(h, s, l)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /conrod_core/src/cursor.rs: -------------------------------------------------------------------------------- 1 | //! Contains an extendable enum of supported mouse cursor types. 2 | //! 3 | //! Use this module to map from the conrod's mouse cursor types to the types known to the window 4 | //! backend you are using. A lot of these are already implemented in `conrod::backend`. Unless you 5 | //! are using custom mouse cursor types not provided here, then using one of the implementations in 6 | //! `conrod::backend` should be sufficient. 7 | 8 | /// This enum specifies cursor types used by internal widgets. For custom widgets using custom 9 | /// cursor types, you can still use this enum by specifying a numbered custom variant. 10 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 11 | pub enum MouseCursor { 12 | /// Default mouse cursor. 13 | Arrow, 14 | /// Text input curosr. 15 | Text, 16 | /// Text input for vertical text. 17 | VerticalText, 18 | /// Open hand with index finger pointing up. 19 | Hand, 20 | /// Open hand. 21 | Grab, 22 | /// Closed hand. 23 | Grabbing, 24 | /// Vertical resize cursor. 25 | ResizeVertical, 26 | /// Horizontal resize cursor. 27 | ResizeHorizontal, 28 | /// Diagonal resize cursor pointing to top left and bottom right corners. 29 | ResizeTopLeftBottomRight, 30 | /// Diagonal resize cursor pointing to top right to bottom left corners. 31 | ResizeTopRightBottomLeft, 32 | /// Custom cursor variant. Encode your favourite cursor with a u8. 33 | Custom(u8), 34 | } 35 | -------------------------------------------------------------------------------- /conrod_core/src/graph/depth_order.rs: -------------------------------------------------------------------------------- 1 | //! Types and functionality related to the calculation of a **Graph**'s rendering depth order. 2 | 3 | use super::{Graph, Node}; 4 | use daggy::Walker; 5 | use fnv; 6 | use std; 7 | use widget; 8 | 9 | /// Contains Node indices in order of depth, starting with the deepest. 10 | #[derive(Debug)] 11 | pub struct DepthOrder { 12 | /// The primary **Vec** storing the **DepthOrder**'s ordered indices. 13 | pub indices: Vec, 14 | /// Used for storing indices of "floating" widgets during depth sorting so that they may be 15 | /// visited after widgets of the root tree. 16 | floating: Vec, 17 | } 18 | 19 | impl DepthOrder { 20 | /// Construct a new empty **DepthOrder**. 21 | pub fn new() -> DepthOrder { 22 | DepthOrder { 23 | indices: Vec::new(), 24 | floating: Vec::new(), 25 | } 26 | } 27 | 28 | /// Construct a new empty **DepthOrder**. 29 | /// 30 | /// There can be at most two indices per widget (the widget and the widget's scrollbar). Thus 31 | /// we'll reserve double the number of nodes given. 32 | pub fn with_node_capacity(n_nodes: usize) -> DepthOrder { 33 | let n_indices = n_nodes * 2; 34 | DepthOrder { 35 | indices: Vec::with_capacity(n_indices), 36 | floating: Vec::with_capacity(n_nodes), 37 | } 38 | } 39 | 40 | /// Update the **DepthOrder** (starting with the deepest) for all nodes in the given **Graph**. 41 | /// 42 | /// FIXME: 43 | /// This likely needs to be re-written, and will probably fail for graphs with many floating 44 | /// widgets instantiated upon other floating widgets. 45 | /// 46 | /// The proper algorithm should be a full toposort where the neighbours of each node are 47 | /// visited in the order specified within `visit_by_depth`. 48 | /// 49 | /// The `visit_by_depth` algorithm should not be recursive and instead use either looping, 50 | /// walking or iteration. 51 | pub fn update( 52 | &mut self, 53 | graph: &Graph, 54 | root: widget::Id, 55 | updated_widgets: &fnv::FnvHashSet, 56 | ) { 57 | let DepthOrder { 58 | ref mut indices, 59 | ref mut floating, 60 | } = *self; 61 | 62 | // Clear the buffers and ensure they've enough memory allocated. 63 | let num_nodes = graph.node_count(); 64 | indices.clear(); 65 | indices.reserve(num_nodes); 66 | floating.clear(); 67 | floating.reserve(num_nodes); 68 | 69 | // Visit each node in order of depth and add their indices to depth_order. 70 | // If the widget is floating, then store it in the floating deque instead. 71 | visit_by_depth(graph, root, updated_widgets, indices, floating); 72 | 73 | // Sort the floating widgets so that the ones clicked last come last. 74 | floating.sort_by(|&a, &b| match (&graph[a], &graph[b]) { 75 | (&Node::Widget(ref a), &Node::Widget(ref b)) => { 76 | let a_floating = a.maybe_floating.expect("Not floating"); 77 | let b_floating = b.maybe_floating.expect("Not floating"); 78 | a_floating 79 | .time_last_clicked 80 | .cmp(&b_floating.time_last_clicked) 81 | } 82 | _ => std::cmp::Ordering::Equal, 83 | }); 84 | 85 | // Visit all of the floating widgets last. 86 | while !floating.is_empty() { 87 | let idx = floating.remove(0); 88 | visit_by_depth(graph, idx, updated_widgets, indices, floating); 89 | } 90 | } 91 | } 92 | 93 | /// Recursive function for visiting all nodes within the dag. 94 | fn visit_by_depth( 95 | graph: &Graph, 96 | idx: widget::Id, 97 | updated_widgets: &fnv::FnvHashSet, 98 | depth_order: &mut Vec, 99 | floating_deque: &mut Vec, 100 | ) { 101 | // First, if the current node is a widget and it was set in the current `set_widgets` stage, 102 | // store its index. 103 | match graph.widget(idx).is_some() && updated_widgets.contains(&idx) { 104 | true => depth_order.push(idx), 105 | // If the current node is not an updated widget, we're done with this branch. 106 | false => return, 107 | } 108 | 109 | // Sort the children of the current node by their `.depth` members. 110 | // FIXME: We should remove these allocations by storing a `child_sorter` buffer in each Widget 111 | // node (perhaps in the `Container`). 112 | let mut child_sorter: Vec = 113 | graph.depth_children(idx).iter(&graph).nodes().collect(); 114 | 115 | child_sorter.sort_by(|&a, &b| { 116 | use std::cmp::Ordering; 117 | 118 | if let (&Node::Widget(ref a), &Node::Widget(ref b)) = (&graph[a], &graph[b]) { 119 | match b.depth.partial_cmp(&a.depth).expect("Depth was NaN!") { 120 | Ordering::Equal => a.instantiation_order_idx.cmp(&b.instantiation_order_idx), 121 | ordering => ordering, 122 | } 123 | } else { 124 | Ordering::Equal 125 | } 126 | }); 127 | 128 | // Then, visit each of the child widgets. If we come across any floating widgets, we'll store 129 | // those in the floating deque so that we can visit them following the current tree. 130 | for child_idx in child_sorter.into_iter() { 131 | // Determine whether or not the node is a floating widget. 132 | let maybe_is_floating = graph.widget(child_idx).map(|w| w.maybe_floating.is_some()); 133 | 134 | // Store floating widgets int he floating_deque for visiting after the current tree. 135 | match maybe_is_floating { 136 | Some(true) => floating_deque.push(child_idx), 137 | _ => visit_by_depth( 138 | graph, 139 | child_idx, 140 | updated_widgets, 141 | depth_order, 142 | floating_deque, 143 | ), 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /conrod_core/src/guide/chapter_2.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | **Getting Started** 4 | 5 | 6 | In this chapter we'll make sure that your environment is setup correctly for using Conrod. 7 | 8 | 9 | ## Installing Rust and Cargo 10 | 11 | Conrod is a Rust library (aka crate), so you'll need Rust! Conrod tracks the stable branch, so you 12 | can be assured that we'll always be compatible with the latest stable version of rustc. 13 | 14 | We also rely on the Rust package manager [Cargo](https://crates.io/) for managing dependencies 15 | and hosting the latest version of conrod. 16 | 17 | The easiest way to acquire both of these is by downloading the Rust installer from [the Rust 18 | homepage][rust-lang]. This installer will install the latest stable version of both rustc and 19 | cargo. 20 | 21 | Once installed, you can test that rustc and cargo are working by entering `rustc --version` and 22 | `cargo --version` into your command line. 23 | 24 | If you're brand new to Rust, we recommend first checking out [The Official Rust Book], or at least 25 | keeping it on hand as a reference. It also contains a [Getting Started][rust getting started] guide 26 | with more details on installing Rust, which may be useful in the case that you run into any issues 27 | with the above steps. 28 | 29 | 30 | ## Running the Conrod Examples 31 | 32 | You can view the examples by cloning the github repository and running the examples. 33 | First, open up the command line on your system and follow these steps: 34 | 35 | 1. Clone the repo 36 | 37 | ```txt 38 | git clone https://github.com/PistonDevelopers/conrod.git 39 | ``` 40 | 41 | 2. Change to the `conrod` directory that we just cloned 42 | 43 | ```txt 44 | cd conrod 45 | ``` 46 | 47 | 3. Build and run an example (with --release optimisations turned on)! 48 | 49 | ```txt 50 | cargo run --release --example all_winit_glium 51 | cargo run --release --example canvas 52 | cargo run --release --example primitives 53 | cargo run --release --example text 54 | ``` 55 | 56 | Hint: You can get a list of all available examples by running: 57 | 58 | ```txt 59 | cargo run --example 60 | ``` 61 | 62 | If you ran into any issues with these steps, please let us know by filing an issue at the Conrod 63 | [issue tracker]. Be sure to search for your issue first, as another user may have already 64 | encountered your problem. 65 | 66 | Otherwise, you're now ready to use conrod! 67 | 68 | [rust-lang]: https://www.rust-lang.org/ "The Rust Homepage" 69 | [The Official Rust Book]: https://doc.rust-lang.org/book/ "The Official Rust Book" 70 | [rust getting started]: https://doc.rust-lang.org/book/getting-started.html "Getting Started with Rust" 71 | [issue tracker]: https://github.com/PistonDevelopers/conrod/issues "Conrod issue tracker" 72 | 73 | */ 74 | -------------------------------------------------------------------------------- /conrod_core/src/guide/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | **The Conrod Guide** 4 | 5 | ## Table of Contents 6 | 7 | 1. [**What is Conrod?**][1] 8 | - [A Brief Summary][1.1] 9 | - [Screenshots and Videos][1.2] 10 | - [Feature Overview][1.3] 11 | - [Available Widgets][1.4] 12 | - [Primitive Widgets][1.4.1] 13 | - [Common Use Widgets][1.4.2] 14 | - [Immediate Mode][1.5] 15 | - [What is it?][1.5.1] 16 | - [Why use it?][1.5.2] 17 | - [Is Conrod Immediate or Retained?][1.5.3] 18 | - [The Builder Pattern][1.6] 19 | 2. [**Getting Started**][2] 20 | - [Installing Rust and Cargo][2.1] 21 | - [Running the Conrod Examples][2.2] 22 | 3. [**Hello World**][3] 23 | - [Creating a new project][3.1] 24 | - [Conrod Setup][3.2] 25 | -[Back ends][3.2.1] 26 | - [Creating a Window][3.3] 27 | - [Event Handling][3.4] 28 | 4. **Using and Customising Themes** 29 | - What is a `Theme`? 30 | - Custom Themes 31 | - Serializing Themes 32 | 5. **Designing Custom Widgets (using the Widget trait)** 33 | - The `Widget` trait 34 | - The `widget_style!` macro 35 | - The `builder_methods!` macro 36 | - Making a `Button` widget 37 | 6. **Custom Graphics and Window Backends** 38 | - Demonstration of Backend Implementation (using glium and glutin) 39 | 7. **Internals** 40 | - The `Ui`'s Widget `Graph` 41 | - `Ui::set_widgets` - How does it work? 42 | 8. **FAQ** 43 | 44 | 45 | [1]: ./chapter_1/index.html 46 | [1.1]: ./chapter_1/index.html#a-brief-history 47 | [1.2]: ./chapter_1/index.html#screenshots-and-videos 48 | [1.3]: ./chapter_1/index.html#feature-overview 49 | [1.4]: ./chapter_1/index.html#available-widgets 50 | [1.4.1]: ./chapter_1/index.html#primitive-widgets 51 | [1.4.2]: ./chapter_1/index.html#common-use-widgets 52 | [1.5]: ./chapter_1/index.html#immediate-mode 53 | [1.5.1]: ./chapter_1/index.html#what-is-it 54 | [1.5.2]: ./chapter_1/index.html#why-use-it 55 | [1.5.3]: ./chapter_1/index.html#is-conrod-immediate-or-retained 56 | [1.6]: ./chapter_1/index.html#the-builder-pattern 57 | [2]: ./chapter_2/index.html 58 | [2.1]: ./chapter_2/index.html#installing-rust-and-cargo 59 | [2.2]: ./chapter_2/index.html#running-the-conrod-examples 60 | [3]: ./chapter_3/index.html 61 | [3.1]: ./chapter_3/index.html#creating-a-new-project 62 | [3.2]: ./chapter_3/index.html#setting-up-conrod 63 | [3.2.1]: ./chapter_3/index.html#backends 64 | [3.3]: ./chapter_3/index.html#creating-a-window 65 | [3.4]: ./chapter_3/index.html#handling-events 66 | 67 | 68 | */ 69 | 70 | pub mod chapter_1; 71 | pub mod chapter_2; 72 | pub mod chapter_3; 73 | -------------------------------------------------------------------------------- /conrod_core/src/image.rs: -------------------------------------------------------------------------------- 1 | //! A type used to manage a user's image data and map them to `Image` widgets: 2 | //! 3 | //! - [Map](./struct.Map.html) 4 | 5 | use fnv; 6 | use std; 7 | 8 | /// Unique image identifier. 9 | /// 10 | /// Throughout conrod, images are referred to via their unique `Id`. By referring to images via 11 | /// `Id`s, conrod can remain agnostic of the actual image or texture types used to represent each 12 | /// image. 13 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] 14 | pub struct Id(u32); 15 | 16 | /// A type used to map the `widget::Id` of `Image` widgets to their associated `Img` data. 17 | /// 18 | /// The `image::Map` type is usually instantiated and loaded during the "setup" stage of the 19 | /// application before the main loop begins. 20 | pub struct Map { 21 | next_index: u32, 22 | map: HashMap, 23 | /// Whether or not the `image::Map` will trigger a redraw the next time `Ui::draw` is called. 24 | /// 25 | /// This is automatically set to `true` when any method that takes `&mut self` is called. 26 | pub trigger_redraw: std::cell::Cell, 27 | } 28 | 29 | /// The type of `std::collections::HashMap` with `fnv::FnvHasher` used within the `image::Map`. 30 | pub type HashMap = fnv::FnvHashMap; 31 | 32 | /// An iterator yielding an `Id` for each new `Img` inserted into the `Map` via the `extend` 33 | /// method. 34 | pub struct NewIds { 35 | index_range: std::ops::Range, 36 | } 37 | 38 | impl std::ops::Deref for Map { 39 | type Target = HashMap; 40 | fn deref(&self) -> &Self::Target { 41 | &self.map 42 | } 43 | } 44 | 45 | impl Map { 46 | /// Construct a new, empty `image::Map`. 47 | pub fn new() -> Self { 48 | Map { 49 | next_index: 0, 50 | map: HashMap::::default(), 51 | trigger_redraw: std::cell::Cell::new(true), 52 | } 53 | } 54 | 55 | // Calling any of the following methods will trigger a redraw when using `Ui::draw_if_changed`. 56 | 57 | /// Uniquely borrow the `Img` associated with the given widget. 58 | /// 59 | /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called. 60 | pub fn get_mut(&mut self, id: Id) -> Option<&mut Img> { 61 | self.trigger_redraw.set(true); 62 | self.map.get_mut(&id) 63 | } 64 | 65 | /// Inserts the given image into the map, returning its associated `image::Id`. The user *must* 66 | /// store the returned `image::Id` in order to use, modify or remove the inserted image. 67 | /// 68 | /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called. 69 | pub fn insert(&mut self, img: Img) -> Id { 70 | self.trigger_redraw.set(true); 71 | let index = self.next_index; 72 | self.next_index = index.wrapping_add(1); 73 | let id = Id(index); 74 | self.map.insert(id, img); 75 | id 76 | } 77 | 78 | /// Replaces the given image in the map if it exists. Returns the image or None. 79 | /// 80 | /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called. 81 | pub fn replace(&mut self, id: Id, img: Img) -> Option { 82 | self.trigger_redraw.set(true); 83 | self.map.insert(id, img) 84 | } 85 | 86 | /// Removes the given image from the map if it exists. Returns the image or None. 87 | /// 88 | /// Any future use of the given `image::Id` will be invalid. 89 | /// 90 | /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called. 91 | pub fn remove(&mut self, id: Id) -> Option { 92 | self.trigger_redraw.set(true); 93 | self.map.remove(&id) 94 | } 95 | 96 | /// Insert each of the images yielded by the given iterator and produce an iterator yielding 97 | /// their generated `Ids` in the same order. 98 | /// 99 | /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called. 100 | pub fn extend(&mut self, images: I) -> NewIds 101 | where 102 | I: IntoIterator, 103 | { 104 | self.trigger_redraw.set(true); 105 | let start_index = self.next_index; 106 | let mut end_index = start_index; 107 | for image in images { 108 | self.map.insert(Id(end_index), image); 109 | end_index += 1; 110 | } 111 | NewIds { 112 | index_range: start_index..end_index, 113 | } 114 | } 115 | } 116 | 117 | impl Iterator for NewIds { 118 | type Item = Id; 119 | fn next(&mut self) -> Option { 120 | self.index_range.next().map(|i| Id(i)) 121 | } 122 | fn size_hint(&self) -> (usize, Option) { 123 | (0, Some(self.len())) 124 | } 125 | } 126 | 127 | impl ExactSizeIterator for NewIds { 128 | fn len(&self) -> usize { 129 | self.index_range.len() 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /conrod_core/src/input/global.rs: -------------------------------------------------------------------------------- 1 | //! Handles all of the global input events and state. 2 | //! The core of this module is the `Global` struct. It is responsible for aggregating 3 | //! and interpreting raw input events into high-level semantic events. 4 | 5 | use event; 6 | use input; 7 | use std; 8 | 9 | /// Global input event handler that also implements `input::Provider`. The `Ui` passes all events 10 | /// to it's `Global` instance, which aggregates and interprets the events to provide so-called 11 | /// 'high-level' events to widgets. This input gets reset after every update by the `Ui`. 12 | #[derive(Debug)] 13 | pub struct Global { 14 | /// The `input::State` as it was at the end of the last update cycle. 15 | pub start: input::State, 16 | /// The most recent `input::State`, with updates from handling all the events 17 | /// this update cycle 18 | pub current: input::State, 19 | /// The events that have occurred between two consecutive updates. 20 | events: Vec, 21 | /// Tracks the last click that occurred and the time at which it occurred in order to create 22 | /// double-click events. 23 | pub last_click: Option<(instant::Instant, event::Click)>, 24 | } 25 | 26 | /// Iterator over all global `event::Event`s that have occurred since the last time 27 | /// `Ui::set_widgets` was called. 28 | #[derive(Clone)] 29 | pub struct Events<'a> { 30 | iter: std::slice::Iter<'a, event::Event>, 31 | } 32 | 33 | /// An iterator yielding all `event::Ui`s that have occurred since the last time `Ui::set_widgets` 34 | /// was called. 35 | #[derive(Clone)] 36 | pub struct UiEvents<'a> { 37 | events: Events<'a>, 38 | } 39 | 40 | impl Global { 41 | /// Returns a fresh new `Global` 42 | pub fn new() -> Global { 43 | Global { 44 | events: Vec::new(), 45 | start: input::State::new(), 46 | current: input::State::new(), 47 | last_click: None, 48 | } 49 | } 50 | 51 | /// Returns an iterator yielding all events that have occurred since the last time 52 | /// `Ui::set_widgets` was called. 53 | pub fn events(&self) -> Events { 54 | Events { 55 | iter: self.events.iter(), 56 | } 57 | } 58 | 59 | /// Add the new event to the stack. 60 | pub fn push_event(&mut self, event: event::Event) { 61 | self.events.push(event); 62 | } 63 | 64 | /// Called at the end of every update cycle in order to prepare the `Global` to 65 | /// handle events for the next one. 66 | pub fn clear_events_and_update_start_state(&mut self) { 67 | self.events.clear(); 68 | self.start = self.current.clone(); 69 | } 70 | } 71 | 72 | impl<'a> Events<'a> { 73 | /// Converts the `Events` into a `UiEvents`, yielding only the `event::Ui`s that have occurred 74 | /// since the last time `Ui::set_widgets` was called. 75 | pub fn ui(self) -> UiEvents<'a> { 76 | UiEvents { events: self } 77 | } 78 | } 79 | 80 | impl<'a> Iterator for Events<'a> { 81 | type Item = &'a event::Event; 82 | fn next(&mut self) -> Option { 83 | self.iter.next() 84 | } 85 | } 86 | 87 | impl<'a> Iterator for UiEvents<'a> { 88 | type Item = &'a event::Ui; 89 | fn next(&mut self) -> Option { 90 | while let Some(event) = self.events.next() { 91 | if let event::Event::Ui(ref ui_event) = *event { 92 | return Some(ui_event); 93 | } 94 | } 95 | None 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /conrod_core/src/input/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains all the logic for handling input events and providing them to widgets. 2 | //! 3 | //! All user input is provided to the `Ui` in the form of `input::Input` events, which are received 4 | //! via the `Ui::handle_event` method. These raw input events tend to be fairly low level. The `Ui` 5 | //! stores each of these `Input` events in it's `GlobalInput`, which keeps track of the state of 6 | //! input for the entire `Ui`. `GlobalInput` will also aggregate the low level events into higher 7 | //! level ones. For instance, two events indicating that a mouse button was pressed then released 8 | //! would cause a new `UiEvent::MouseClick` to be generated. This saves individual widgets from 9 | //! having to interpret these themselves, thus freeing them from also having to store input state. 10 | //! 11 | //! Whenever there's an update, all of the events that have occurred since the last update will be 12 | //! available for widgets to process. `WidgetInput` is used to provide input events to a specific 13 | //! widget. It filters events that do not apply to the widget. All events provided by `WidgetIput` 14 | //! will have all coordinates in the widget's own local coordinate system, where `(0, 0)` is the 15 | //! middle of the widget's bounding `Rect`. `GlobalInput`, on the other hand, will never filter out 16 | //! any events, and will always provide them with coordinates relative to the window. 17 | 18 | pub mod global; 19 | pub mod state; 20 | pub mod widget; 21 | 22 | pub use self::global::Global; 23 | pub use self::state::State; 24 | pub use self::touch::Touch; 25 | pub use self::widget::Widget; 26 | use Scalar; 27 | 28 | #[doc(inline)] 29 | pub use piston_input::keyboard::ModifierKey; 30 | #[doc(inline)] 31 | pub use piston_input::{ 32 | keyboard, Button, ControllerAxisArgs, ControllerButton, Key, MouseButton, RenderArgs, 33 | }; 34 | 35 | /// Sources from which user input may be received. 36 | /// 37 | /// We use these to track which sources of input are being captured by which widget. 38 | #[derive(Copy, Clone, PartialEq, Debug)] 39 | pub enum Source { 40 | /// Mouse input (i.e. movement, buttons). 41 | Mouse, 42 | /// Keyboard input. 43 | Keyboard, 44 | /// Input from a finger on a touch screen/surface. 45 | Touch(self::touch::Id), 46 | } 47 | 48 | /// Different kinds of motion input. 49 | #[allow(missing_docs)] 50 | #[derive(Copy, Clone, Debug, PartialEq)] 51 | pub enum Motion { 52 | /// Absolute cursor position within the window. 53 | /// 54 | /// For more details on co-ordinate orientation etc, see the `Input` docs. 55 | MouseCursor { x: Scalar, y: Scalar }, 56 | /// Relative mouse movement. 57 | MouseRelative { x: Scalar, y: Scalar }, 58 | /// x and y in scroll ticks. 59 | Scroll { x: Scalar, y: Scalar }, 60 | /// controller axis move event. 61 | ControllerAxis(ControllerAxisArgs), 62 | } 63 | 64 | /// Touch-related items. 65 | pub mod touch { 66 | use Point; 67 | 68 | /// A type for uniquely identifying the source of a touch interaction. 69 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 70 | pub struct Id(u64); 71 | 72 | /// The stage of the touch interaction. 73 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 74 | pub enum Phase { 75 | /// The start of a touch interaction. 76 | Start, 77 | /// A touch moving across a surface. 78 | Move, 79 | /// The touch interaction was cancelled. 80 | Cancel, 81 | /// The end of a touch interaction. 82 | End, 83 | } 84 | 85 | /// Represents a touch interaction. 86 | /// 87 | /// Each time a user touches the surface with a new finger, a new series of `Touch` events 88 | /// `Start`, each with a unique identifier. 89 | /// 90 | /// For every `Id` there should be at least 2 events with `Start` and `End` (or `Cancel`led) 91 | /// `Phase`s. 92 | /// 93 | /// A `Start` input received with the same `Id` as a previously received `End` does *not* 94 | /// indicate that the same finger was used. `Id`s are only used to distinguish between 95 | /// overlapping touch interactions. 96 | #[derive(Copy, Clone, Debug, PartialEq)] 97 | pub struct Touch { 98 | /// The stage of the touch interaction. 99 | pub phase: Phase, 100 | /// A unique identifier associated with the source of the touch interaction. 101 | pub id: Id, 102 | /// The location of the touch on the surface/screen. See `Input` docs for information on 103 | /// the co-ordinate system. 104 | pub xy: Point, 105 | } 106 | 107 | impl Id { 108 | /// Construct a new identifier. 109 | pub fn new(id: u64) -> Self { 110 | Id(id) 111 | } 112 | } 113 | 114 | impl Touch { 115 | /// Returns a copy of the `Touch` relative to the given `xy`. 116 | pub fn relative_to(&self, xy: Point) -> Self { 117 | Touch { 118 | xy: [self.xy[0] - xy[0], self.xy[1] - xy[1]], 119 | ..*self 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /conrod_core/src/label.rs: -------------------------------------------------------------------------------- 1 | use color::{hsl, hsla, rgb, rgba, Color}; 2 | use ui::Ui; 3 | 4 | /// Font size used throughout Conrod. 5 | pub type FontSize = u32; 6 | 7 | /// Widgets that may display some label. 8 | pub trait Labelable<'a>: Sized { 9 | /// Set the label for the widget. 10 | fn label(self, text: &'a str) -> Self; 11 | 12 | /// Set the color of the widget's label. 13 | fn label_color(self, color: Color) -> Self; 14 | 15 | /// Set the color of the widget's label from rgba values. 16 | fn label_rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self { 17 | self.label_color(rgba(r, g, b, a)) 18 | } 19 | 20 | /// Set the color of the widget's label from rgb values. 21 | fn label_rgb(self, r: f32, g: f32, b: f32) -> Self { 22 | self.label_color(rgb(r, g, b)) 23 | } 24 | 25 | /// Set the color of the widget's label from hsla values. 26 | fn label_hsla(self, h: f32, s: f32, l: f32, a: f32) -> Self { 27 | self.label_color(hsla(h, s, l, a)) 28 | } 29 | 30 | /// Set the color of the widget's label from hsl values. 31 | fn label_hsl(self, h: f32, s: f32, l: f32) -> Self { 32 | self.label_color(hsl(h, s, l)) 33 | } 34 | 35 | /// Set the font size for the widget's label. 36 | fn label_font_size(self, size: FontSize) -> Self; 37 | 38 | /// Set a "small" font size for the widget's label. 39 | fn small_font(self, ui: &Ui) -> Self { 40 | self.label_font_size(ui.theme.font_size_small) 41 | } 42 | 43 | /// Set a "medium" font size for the widget's label. 44 | fn medium_font(self, ui: &Ui) -> Self { 45 | self.label_font_size(ui.theme.font_size_medium) 46 | } 47 | 48 | /// Set a "large" font size for the widget's label. 49 | fn large_font(self, ui: &Ui) -> Self { 50 | self.label_font_size(ui.theme.font_size_large) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /conrod_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Conrod 2 | //! 3 | //! An easy-to-use, immediate-mode, 2D GUI library featuring a range of useful widgets. 4 | //! 5 | //! If you are new to Conrod, we recommend checking out [The Guide](./guide/index.html). 6 | 7 | #![deny(unsafe_code)] 8 | #![deny(missing_copy_implementations)] 9 | #![warn(missing_docs)] 10 | 11 | #[macro_use] 12 | extern crate conrod_derive; 13 | extern crate copypasta; 14 | extern crate daggy; 15 | extern crate fnv; 16 | extern crate input as piston_input; 17 | extern crate num; 18 | extern crate rusttype; 19 | 20 | pub use border::{Borderable, Bordering}; 21 | pub use color::{Color, Colorable}; 22 | pub use conrod_derive::*; 23 | pub use label::{FontSize, Labelable}; 24 | pub use position::{Dimensions, Point, Position, Positionable, Range, Rect, Scalar, Sizeable}; 25 | pub use theme::Theme; 26 | pub use ui::{Ui, UiBuilder, UiCell}; 27 | pub use widget::{scroll, Widget}; 28 | 29 | mod border; 30 | pub mod color; 31 | pub mod cursor; 32 | pub mod event; 33 | pub mod graph; 34 | pub mod guide; 35 | pub mod image; 36 | pub mod input; 37 | mod label; 38 | pub mod mesh; 39 | pub mod position; 40 | pub mod render; 41 | pub mod text; 42 | pub mod theme; 43 | mod ui; 44 | pub mod utils; 45 | pub mod widget; 46 | 47 | #[cfg(test)] 48 | mod tests; 49 | -------------------------------------------------------------------------------- /conrod_core/src/position/matrix.rs: -------------------------------------------------------------------------------- 1 | use {Backend, CharacterCache, Ui}; 2 | use super::{Depth, Dimension, Dimensions, Point, Position, Positionable, Scalar, Sizeable}; 3 | use ui; 4 | use widget; 5 | 6 | pub type WidgetNum = usize; 7 | pub type ColNum = usize; 8 | pub type RowNum = usize; 9 | pub type Width = f64; 10 | pub type Height = f64; 11 | pub type PosX = f64; 12 | pub type PosY = f64; 13 | 14 | /// A type to simplify placement of various widgets in a matrix or grid layout. 15 | #[derive(Copy, Clone, Debug)] 16 | pub struct Matrix { 17 | cols: usize, 18 | rows: usize, 19 | maybe_x_position: Option, 20 | maybe_y_position: Option, 21 | maybe_x_dimension: Option, 22 | maybe_y_dimension: Option, 23 | cell_pad_w: Scalar, 24 | cell_pad_h: Scalar, 25 | } 26 | 27 | impl Matrix { 28 | 29 | /// Start building a new position **Matrix**. 30 | pub fn new(cols: usize, rows: usize) -> Matrix { 31 | Matrix { 32 | cols: cols, 33 | rows: rows, 34 | maybe_x_position: None, 35 | maybe_y_position: None, 36 | maybe_x_dimension: None, 37 | maybe_y_dimension: None, 38 | cell_pad_w: 0.0, 39 | cell_pad_h: 0.0, 40 | } 41 | } 42 | 43 | /// Produce the matrix with the given cell padding. 44 | pub fn cell_padding(mut self, w: Scalar, h: Scalar) -> Matrix { 45 | self.cell_pad_w = w; 46 | self.cell_pad_h = h; 47 | self 48 | } 49 | 50 | /// Call the given function for every element in the Matrix. 51 | pub fn each_widget(self, ui: &mut Ui, mut f: F) where 52 | C: CharacterCache, 53 | F: FnMut(&mut Ui, WidgetNum, ColNum, RowNum, Point, Dimensions), 54 | { 55 | use utils::map_range; 56 | 57 | let x_pos = self.get_x_position(ui); 58 | let y_pos = self.get_y_position(ui); 59 | let dim = self.get_wh(ui).unwrap_or([0.0, 0.0]); 60 | 61 | // If we can infer some new current parent from the position, set that as the current 62 | // parent within the given `Ui`. 63 | let parent_idx = ui::infer_parent_unchecked(ui, x_pos, y_pos); 64 | ui::set_current_parent_idx(ui, parent_idx); 65 | 66 | let xy = ui.calc_xy(None, x_pos, y_pos, dim, true); 67 | let (half_w, half_h) = (dim[0] / 2.0, dim[1] / 2.0); 68 | let widget_w = dim[0] / self.cols as f64; 69 | let widget_h = dim[1] / self.rows as f64; 70 | let x_min = -half_w + widget_w / 2.0; 71 | let x_max = half_w + widget_w / 2.0; 72 | let y_min = -half_h - widget_h / 2.0; 73 | let y_max = half_h - widget_h / 2.0; 74 | let mut widget_num = 0; 75 | for col in 0..self.cols { 76 | for row in 0..self.rows { 77 | let x = xy[0] + map_range(col as f64, 0.0, self.cols as f64, x_min, x_max); 78 | let y = xy[1] + map_range(row as f64, 0.0, self.rows as f64, y_max, y_min); 79 | let w = widget_w - self.cell_pad_w * 2.0; 80 | let h = widget_h - self.cell_pad_h * 2.0; 81 | f(ui, widget_num, col, row, [x, y], [w, h]); 82 | widget_num += 1; 83 | } 84 | } 85 | } 86 | 87 | } 88 | 89 | impl Positionable for Matrix 90 | where B: Backend, 91 | { 92 | #[inline] 93 | fn x_position(mut self, pos: Position) -> Self { 94 | self.maybe_x_position = Some(pos); 95 | self 96 | } 97 | #[inline] 98 | fn y_position(mut self, pos: Position) -> Self { 99 | self.maybe_y_position = Some(pos); 100 | self 101 | } 102 | #[inline] 103 | fn get_x_position(&self, ui: &Ui) -> Position { 104 | self.maybe_x_position.unwrap_or(ui.theme.x_position) 105 | } 106 | #[inline] 107 | fn get_y_position(&self, ui: &Ui) -> Position { 108 | self.maybe_y_position.unwrap_or(ui.theme.y_position) 109 | } 110 | #[inline] 111 | fn depth(self, _: Depth) -> Self { 112 | unimplemented!(); 113 | } 114 | #[inline] 115 | fn get_depth(&self) -> Depth { 116 | unimplemented!(); 117 | } 118 | } 119 | 120 | impl Sizeable for Matrix 121 | where B: Backend, 122 | { 123 | #[inline] 124 | fn x_dimension(mut self, w: Dimension) -> Self { 125 | self.maybe_x_dimension = Some(w); 126 | self 127 | } 128 | #[inline] 129 | fn y_dimension(mut self, h: Dimension) -> Self { 130 | self.maybe_y_dimension = Some(h); 131 | self 132 | } 133 | #[inline] 134 | fn get_x_dimension(&self, ui: &Ui) -> Dimension { 135 | const DEFAULT_WIDTH: Dimension = Dimension::Absolute(256.0); 136 | self.maybe_x_dimension.or_else(|| { 137 | ui.theme.widget_style::(widget::matrix::KIND) 138 | .map(|default| default.common.maybe_x_dimension.unwrap_or(DEFAULT_WIDTH)) 139 | }).unwrap_or(DEFAULT_WIDTH) 140 | } 141 | #[inline] 142 | fn get_y_dimension(&self, ui: &Ui) -> Dimension { 143 | const DEFAULT_HEIGHT: Dimension = Dimension::Absolute(256.0); 144 | self.maybe_y_dimension.or_else(|| { 145 | ui.theme.widget_style::(widget::matrix::KIND) 146 | .map(|default| default.common.maybe_y_dimension.unwrap_or(DEFAULT_HEIGHT)) 147 | }).unwrap_or(DEFAULT_HEIGHT) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /conrod_core/src/tests/color.rs: -------------------------------------------------------------------------------- 1 | use color::{hsl_to_rgb, rgb_to_hsl}; 2 | use std::cmp::Ordering::Equal; 3 | 4 | ///// Test assist code. 5 | 6 | fn convert_rgb_to_hsl_to_rgb(expected_r: f32, expected_g: f32, expected_b: f32) -> (f32, f32, f32) { 7 | let (h, s, l) = rgb_to_hsl(expected_r, expected_g, expected_b); 8 | hsl_to_rgb(h, s, l) 9 | } 10 | 11 | fn compare_rgb_pairs(expected: (f32, f32, f32), actual: (f32, f32, f32)) -> bool { 12 | let (expected_r, expected_g, expected_b) = expected; 13 | let (actual_r, actual_g, actual_b) = actual; 14 | let r_comp = expected_r.partial_cmp(&actual_r).unwrap(); 15 | let g_comp = expected_g.partial_cmp(&actual_g).unwrap(); 16 | let b_comp = expected_b.partial_cmp(&actual_b).unwrap(); 17 | r_comp == Equal && g_comp == Equal && b_comp == Equal 18 | } 19 | 20 | ///// Actual tests. 21 | 22 | #[test] 23 | fn rgb_to_hsl_black() { 24 | // black (0,0,0) should convert to hsl (0.0, 0.0, 0.0) 25 | let (r, g, b) = (0.0, 0.0, 0.0); 26 | let actual = convert_rgb_to_hsl_to_rgb(r, g, b); 27 | assert!(compare_rgb_pairs((r, g, b), actual)) 28 | } 29 | 30 | #[test] 31 | fn rgb_to_hsl_white() { 32 | // white (255,255,255) should convert to hsl (0.0, 0.0, 1.0) 33 | let (r, g, b) = (1.0, 1.0, 1.0); 34 | let actual = convert_rgb_to_hsl_to_rgb(r, g, b); 35 | assert!(compare_rgb_pairs((r, g, b), actual)) 36 | } 37 | 38 | #[test] 39 | fn rgb_to_hsl_gray() { 40 | // gray rgb (128,128,128) should convert to hsl (0.0, 0.0, 0.5) 41 | let (r, g, b) = (0.5, 0.5, 0.5); 42 | let actual = convert_rgb_to_hsl_to_rgb(r, g, b); 43 | assert!(compare_rgb_pairs((r, g, b), actual)); 44 | } 45 | 46 | #[test] 47 | fn rgb_to_hsl_purple() { 48 | // purple rgb (128,0,128) should convert to hsl (5.23598766, 1.0, 0.25) 49 | let (r, g, b) = (0.5, 0.0, 0.5); 50 | let actual = convert_rgb_to_hsl_to_rgb(r, g, b); 51 | assert!(compare_rgb_pairs((r, g, b), actual)); 52 | } 53 | 54 | #[test] 55 | fn rgb_to_hsl_silver() { 56 | // silver rgb (191,191,191) should convert to hsl (0.0, 0.0, 0.75) 57 | let (r, g, b) = (0.75, 0.75, 0.75); 58 | let actual = convert_rgb_to_hsl_to_rgb(r, g, b); 59 | assert!(compare_rgb_pairs((r, g, b), actual)); 60 | } 61 | -------------------------------------------------------------------------------- /conrod_core/src/tests/global_input.rs: -------------------------------------------------------------------------------- 1 | use event::{self, Input}; 2 | use input::Button::Keyboard; 3 | use input::Button::Mouse; 4 | use input::{self, Key, Motion, MouseButton}; 5 | use position::Scalar; 6 | 7 | // Pushes an event onto the given global input with a default drag threshold. 8 | fn push_event(input: &mut input::Global, event: event::Event) { 9 | input.push_event(event); 10 | } 11 | 12 | fn mouse_move_event(x: Scalar, y: Scalar) -> event::Event { 13 | event::Event::Raw(Input::Motion(Motion::MouseRelative { x: x, y: y })) 14 | } 15 | 16 | #[test] 17 | fn resetting_input_should_set_starting_state_to_current_state() { 18 | let mut input = input::Global::new(); 19 | push_event( 20 | &mut input, 21 | event::Event::Raw(Input::Press(Keyboard(Key::LShift))), 22 | ); 23 | push_event( 24 | &mut input, 25 | event::Event::Raw(Input::Motion(Motion::Scroll { x: 0.0, y: 50.0 })), 26 | ); 27 | 28 | let expected_start = input.current.clone(); 29 | input.clear_events_and_update_start_state(); 30 | assert_eq!(expected_start, input.start); 31 | } 32 | 33 | #[test] 34 | fn resetting_input_should_clear_out_events() { 35 | let mut input = input::Global::new(); 36 | push_event( 37 | &mut input, 38 | event::Event::Raw(Input::Press(Keyboard(Key::LShift))), 39 | ); 40 | push_event( 41 | &mut input, 42 | event::Event::Raw(Input::Motion(Motion::Scroll { x: 0.0, y: 50.0 })), 43 | ); 44 | input.clear_events_and_update_start_state(); 45 | assert!(input.events().next().is_none()); 46 | } 47 | 48 | #[test] 49 | fn no_events_should_be_returned_after_reset_is_called() { 50 | let mut input = input::Global::new(); 51 | push_event( 52 | &mut input, 53 | event::Event::Raw(Input::Press(Keyboard(Key::RShift))), 54 | ); 55 | push_event( 56 | &mut input, 57 | event::Event::Raw(Input::Motion(Motion::Scroll { x: 7.0, y: 88.5 })), 58 | ); 59 | push_event( 60 | &mut input, 61 | event::Event::Raw(Input::Press(Mouse(MouseButton::Left))), 62 | ); 63 | push_event(&mut input, mouse_move_event(60.0, 30.0)); 64 | push_event( 65 | &mut input, 66 | event::Event::Raw(Input::Release(Mouse(MouseButton::Left))), 67 | ); 68 | 69 | input.clear_events_and_update_start_state(); 70 | 71 | assert!(input.events().next().is_none()); 72 | } 73 | 74 | #[test] 75 | fn events_should_return_all_inputs_in_order() { 76 | let mut input = input::Global::new(); 77 | 78 | let evt1 = event::Event::Raw(Input::Press(Keyboard(Key::Z))); 79 | push_event(&mut input, evt1.clone()); 80 | let evt2 = event::Event::Raw(Input::Press(Keyboard(Key::A))); 81 | push_event(&mut input, evt2.clone()); 82 | 83 | let results = input.events().collect::>(); 84 | assert_eq!(2, results.len()); 85 | assert_eq!(evt1, *results[0]); 86 | assert_eq!(evt2, *results[1]); 87 | } 88 | -------------------------------------------------------------------------------- /conrod_core/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod color; 2 | mod global_input; 3 | mod ui; 4 | mod widget_input; 5 | -------------------------------------------------------------------------------- /conrod_core/src/tests/ui.rs: -------------------------------------------------------------------------------- 1 | use event::{self, Input}; 2 | use input::keyboard::ModifierKey; 3 | use input::{self, Button, Key, Motion, MouseButton}; 4 | use position::Point; 5 | use widget; 6 | use {Color, Colorable, Labelable, Positionable, Sizeable, Ui, UiBuilder, Widget}; 7 | 8 | ///// Test assist code. 9 | 10 | fn left_click_mouse(ui: &mut Ui) { 11 | press_mouse_button(MouseButton::Left, ui); 12 | release_mouse_button(MouseButton::Left, ui); 13 | } 14 | 15 | fn release_mouse_button(button: MouseButton, ui: &mut Ui) { 16 | let event = Input::Release(Button::Mouse(button)); 17 | ui.handle_event(event); 18 | } 19 | 20 | fn press_mouse_button(button: MouseButton, ui: &mut Ui) { 21 | let event = Input::Press(Button::Mouse(button)); 22 | ui.handle_event(event); 23 | } 24 | 25 | fn move_mouse_to_widget(widget_id: widget::Id, ui: &mut Ui) { 26 | ui.xy_of(widget_id).map(|point| { 27 | let abs_xy = to_window_coordinates(point, ui); 28 | move_mouse_to_abs_coordinates(abs_xy[0], abs_xy[1], ui); 29 | }); 30 | } 31 | 32 | fn move_mouse_to_abs_coordinates(x: f64, y: f64, ui: &mut Ui) { 33 | ui.handle_event(Input::Motion(Motion::MouseCursor { x: x, y: y })); 34 | } 35 | 36 | fn test_handling_basic_input_event(ui: &mut Ui, event: Input) { 37 | ui.handle_event(event.clone()); 38 | assert_event_was_pushed(ui, event::Event::Raw(event)); 39 | } 40 | 41 | fn assert_event_was_pushed(ui: &Ui, event: event::Event) { 42 | let found = ui.global_input().events().find(|evt| **evt == event); 43 | assert!( 44 | found.is_some(), 45 | format!( 46 | "expected to find event: {:?} in: \nevents: {:?}", 47 | event, 48 | ui.global_input().events().collect::>() 49 | ) 50 | ); 51 | } 52 | 53 | fn to_window_coordinates(xy: Point, ui: &Ui) -> Point { 54 | let x = (ui.win_w / 2.0) + xy[0]; 55 | let y = (ui.win_h / 2.0) - xy[1]; 56 | [x, y] 57 | } 58 | 59 | fn windowless_ui() -> Ui { 60 | UiBuilder::new([800.0, 600.0]).build() 61 | } 62 | 63 | ///// Actual tests. 64 | 65 | #[test] 66 | fn ui_should_reset_global_input_after_widget_are_set() { 67 | let ui = &mut windowless_ui(); 68 | ui.win_w = 250.0; 69 | ui.win_h = 300.0; 70 | 71 | let (canvas, button) = { 72 | let mut id_generator = ui.widget_id_generator(); 73 | (id_generator.next(), id_generator.next()) 74 | }; 75 | 76 | move_mouse_to_widget(button, ui); 77 | for _ in 0..2 { 78 | left_click_mouse(ui); 79 | let ui = &mut ui.set_widgets(); 80 | 81 | assert!(ui.global_input().events().next().is_some()); 82 | 83 | widget::Canvas::new() 84 | .color(Color::Rgba(1.0, 1.0, 1.0, 1.0)) 85 | .set(canvas, ui); 86 | widget::Button::new() 87 | .w_h(100.0, 200.0) 88 | .label("MyButton") 89 | .bottom_right_of(canvas) 90 | .set(button, ui); 91 | } 92 | 93 | assert!(ui.global_input().events().next().is_none()); 94 | } 95 | 96 | #[test] 97 | fn drag_delta_xy_should_add_up_to_total_delta_xy() { 98 | let ui = &mut windowless_ui(); 99 | ui.theme.mouse_drag_threshold = 2.0; 100 | let long_distance = 10.0; // Initial movement to trigger drag 101 | let small_distance = 1.0; // Subsequent smaller movements below drag threshold 102 | // Move mouse to (0,0) 103 | test_handling_basic_input_event(ui, Input::Motion(Motion::MouseCursor { x: 0.0, y: 0.0 })); 104 | // Press left mouse button 105 | test_handling_basic_input_event(ui, Input::Press(Button::Mouse(MouseButton::Left))); 106 | 107 | // Move mouse (above drag threshold) 108 | test_handling_basic_input_event( 109 | ui, 110 | Input::Motion(Motion::MouseCursor { 111 | x: long_distance, 112 | y: 0.0, 113 | }), 114 | ); 115 | assert_event_was_pushed( 116 | ui, 117 | event::Event::Ui(event::Ui::Drag( 118 | None, 119 | event::Drag { 120 | button: MouseButton::Left, 121 | origin: [0.0, 0.0], 122 | from: [0.0, 0.0], 123 | to: [long_distance, 0.0], 124 | delta_xy: [long_distance, 0.0], 125 | total_delta_xy: [long_distance, 0.0], 126 | modifiers: Default::default(), 127 | }, 128 | )), 129 | ); 130 | 131 | // Move mouse a bunch more, below the drag threshold. This should still trigger drag events 132 | // anyway because we are already dragging 133 | for i in 0..3 { 134 | let from_x = long_distance + (i as f64) * small_distance; 135 | let to_x = long_distance + (i + 1) as f64 * small_distance; 136 | test_handling_basic_input_event(ui, Input::Motion(Motion::MouseCursor { x: to_x, y: 0.0 })); 137 | assert_event_was_pushed( 138 | ui, 139 | event::Event::Ui(event::Ui::Drag( 140 | None, 141 | event::Drag { 142 | button: MouseButton::Left, 143 | origin: [0.0, 0.0], 144 | from: [from_x, 0.0], 145 | to: [to_x, 0.0], 146 | delta_xy: [small_distance, 0.0], 147 | total_delta_xy: [to_x, 0.0], 148 | modifiers: Default::default(), 149 | }, 150 | )), 151 | ); 152 | } 153 | } 154 | 155 | #[test] 156 | fn ui_should_push_input_events_to_aggregator() { 157 | let ui = &mut windowless_ui(); 158 | 159 | test_handling_basic_input_event(ui, Input::Press(input::Button::Keyboard(Key::LCtrl))); 160 | test_handling_basic_input_event(ui, Input::Release(input::Button::Keyboard(Key::LCtrl))); 161 | test_handling_basic_input_event(ui, Input::Text("my string".to_string())); 162 | test_handling_basic_input_event(ui, Input::Resize(55.0, 99.0)); 163 | test_handling_basic_input_event(ui, Input::Focus(true)); 164 | } 165 | 166 | #[test] 167 | fn high_level_scroll_event_should_be_created_from_a_raw_mouse_scroll() { 168 | let mut ui = windowless_ui(); 169 | ui.handle_event(Input::Motion(Motion::Scroll { x: 10.0, y: 33.0 })); 170 | 171 | let expected_scroll = event::Scroll { 172 | x: 10.0, 173 | y: 33.0, 174 | modifiers: ModifierKey::default(), 175 | }; 176 | let event = ui 177 | .global_input() 178 | .events() 179 | .next() 180 | .expect("expected a scroll event"); 181 | if let event::Event::Ui(event::Ui::Scroll(_, scroll)) = *event { 182 | assert_eq!(expected_scroll, scroll); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /conrod_core/src/tests/widget_input.rs: -------------------------------------------------------------------------------- 1 | use event::{self, Input}; 2 | use input::keyboard::ModifierKey; 3 | use input::{self, Button, Motion, MouseButton}; 4 | use position::Rect; 5 | use widget; 6 | 7 | // Pushes an event onto the given global input with a default drag threshold. 8 | fn push_event(input: &mut input::Global, event: event::Event) { 9 | input.push_event(event); 10 | } 11 | 12 | #[test] 13 | fn mouse_should_return_none_if_another_widget_is_capturing_mouse() { 14 | let widget_area = Rect::from_corners([10.0, 10.0], [50.0, 50.0]); 15 | let mut global_input = input::Global::new(); 16 | let source = input::Source::Mouse; 17 | push_event( 18 | &mut global_input, 19 | event::Ui::WidgetCapturesInputSource(widget::Id::new(999), source).into(), 20 | ); 21 | push_event( 22 | &mut global_input, 23 | event::Event::Raw(Input::Motion(Motion::MouseRelative { x: 30.0, y: 30. })), 24 | ); 25 | push_event( 26 | &mut global_input, 27 | event::Event::Raw(Input::Press(Button::Mouse(MouseButton::Left))), 28 | ); 29 | 30 | let widget_input = input::Widget::for_widget(widget::Id::new(2), widget_area, &global_input); 31 | 32 | assert!(widget_input.mouse().is_none()); 33 | } 34 | 35 | #[test] 36 | fn widget_input_should_provide_any_mouse_events_over_the_widgets_area_if_nothing_is_capturing_mouse( 37 | ) { 38 | let mut global_input = input::Global::new(); 39 | let widget = widget::Id::new(4); 40 | 41 | push_event( 42 | &mut global_input, 43 | event::Ui::Click( 44 | Some(widget), 45 | event::Click { 46 | button: MouseButton::Left, 47 | xy: [10.0, 10.0], 48 | modifiers: ModifierKey::NO_MODIFIER, 49 | }, 50 | ) 51 | .into(), 52 | ); 53 | assert!(global_input.current.widget_capturing_mouse.is_none()); 54 | 55 | let widget_area = Rect::from_corners([0.0, 0.0], [40.0, 40.0]); 56 | let widget_input = input::Widget::for_widget(widget, widget_area, &global_input); 57 | 58 | widget_input 59 | .clicks() 60 | .left() 61 | .next() 62 | .expect("Expected to get a mouse click event"); 63 | 64 | let another_widget = widget::Id::new(7); 65 | let another_area = Rect::from_corners([-20.0, -20.0], [0.0, 0.0]); 66 | let another_widget_input = 67 | input::Widget::for_widget(another_widget, another_area, &global_input); 68 | 69 | assert!(another_widget_input.clicks().left().next().is_none()); 70 | } 71 | -------------------------------------------------------------------------------- /conrod_core/src/theme.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Types a functionality for handling Canvas and Widget theming. 3 | //! 4 | 5 | use color::{Color, BLACK, WHITE}; 6 | use fnv; 7 | use position::{Align, Direction, Padding, Position, Relative}; 8 | use std; 9 | use std::any::Any; 10 | use text; 11 | use widget; 12 | use Scalar; 13 | 14 | /// `std::collections::HashMap` with `fnv::FnvHasher` for unique styling 15 | /// of each widget, index-able by the **Widget::kind**. 16 | pub type StyleMap = fnv::FnvHashMap; 17 | 18 | /// A serializable collection of canvas and widget styling defaults. 19 | #[derive(Debug)] 20 | pub struct Theme { 21 | /// A name for the theme used for identification. 22 | pub name: String, 23 | /// Padding for Canvas layout and positioning. 24 | pub padding: Padding, 25 | /// A default widget position along the *x* axis. 26 | pub x_position: Position, 27 | /// A default widget position along the *y* axis. 28 | pub y_position: Position, 29 | /// A default background for the theme. 30 | pub background_color: Color, 31 | /// A default color for widget shapes. 32 | pub shape_color: Color, 33 | /// A default color for widget borders. 34 | pub border_color: Color, 35 | /// A default width for widget borders. 36 | pub border_width: Scalar, 37 | /// A default color for widget labels. 38 | pub label_color: Color, 39 | /// The `Id` of the default font used for text widgets when one is not specified. 40 | pub font_id: Option, 41 | /// A default "large" font size. 42 | pub font_size_large: u32, 43 | /// A default "medium" font size. 44 | pub font_size_medium: u32, 45 | /// A default "small" font size. 46 | pub font_size_small: u32, 47 | /// `StyleMap` for unique styling 48 | /// of each widget, index-able by the **Widget::kind**. 49 | pub widget_styling: StyleMap, 50 | /// Mouse Drag distance threshold determines the minimum distance from the mouse-down point 51 | /// that the mouse must move before starting a drag operation. 52 | pub mouse_drag_threshold: Scalar, 53 | /// Once the `Duration` that separates two consecutive `Click`s is greater than this value, a 54 | /// `DoubleClick` event will no longer be generated. 55 | pub double_click_threshold: std::time::Duration, 56 | } 57 | 58 | /// The defaults for a specific widget. 59 | #[derive(Debug)] 60 | pub struct WidgetDefault { 61 | /// The unique style of a widget. 62 | pub style: Box, 63 | /// The attributes commonly shared between widgets. 64 | pub common: widget::CommonStyle, 65 | } 66 | 67 | /// A **WidgetDefault** downcast to a **Widget**'s unique **Style** type. 68 | #[derive(Copy, Clone, Debug)] 69 | pub struct UniqueDefault<'a, T: 'a> { 70 | /// The unique style for the widget. 71 | pub style: &'a T, 72 | /// Attributes that are common to all widgets. 73 | pub common: &'a widget::CommonStyle, 74 | } 75 | 76 | impl WidgetDefault { 77 | /// Constructor for a WidgetDefault. 78 | pub fn new(style: Box) -> WidgetDefault { 79 | WidgetDefault { 80 | style: style, 81 | common: widget::CommonStyle::default(), 82 | } 83 | } 84 | } 85 | 86 | impl Theme { 87 | /// The default theme if not loading from file. 88 | pub fn default() -> Theme { 89 | Theme { 90 | name: "Demo Theme".to_string(), 91 | padding: Padding::none(), 92 | x_position: Position::Relative(Relative::Align(Align::Start), None), 93 | y_position: Position::Relative(Relative::Direction(Direction::Backwards, 20.0), None), 94 | background_color: BLACK, 95 | shape_color: WHITE, 96 | border_color: BLACK, 97 | border_width: 1.0, 98 | label_color: BLACK, 99 | font_id: None, 100 | font_size_large: 26, 101 | font_size_medium: 18, 102 | font_size_small: 12, 103 | widget_styling: fnv::FnvHashMap::default(), 104 | mouse_drag_threshold: 0.0, 105 | double_click_threshold: std::time::Duration::from_millis(500), 106 | } 107 | } 108 | 109 | /// Retrieve the unique default styling for a widget. 110 | /// 111 | /// Attempts to cast the `Box` to the **Widget**'s unique associated style **T**. 112 | pub fn widget_style(&self) -> Option> 113 | where 114 | T: widget::Style, 115 | { 116 | let style_id = std::any::TypeId::of::(); 117 | self.widget_styling 118 | .get(&style_id) 119 | .and_then(|boxed_default| { 120 | boxed_default.style.downcast_ref().map(|style| { 121 | let common = &boxed_default.common; 122 | UniqueDefault { 123 | style: style, 124 | common: common, 125 | } 126 | }) 127 | }) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /conrod_core/src/widget/plot_path.rs: -------------------------------------------------------------------------------- 1 | //! A widget for plotting a series of lines using the given function *x -> y*. 2 | 3 | use graph; 4 | use num; 5 | use utils; 6 | use widget; 7 | use {Color, Colorable, Point, Positionable, Scalar, Sizeable, Theme, Widget}; 8 | 9 | /// A widget that plots a series of lines using the given function *x -> y*. 10 | /// 11 | /// The function is sampled once per pixel and the result is mapped to the widget's height. 12 | /// 13 | /// The resulting "path" is drawn using conrod's `PointPath` primitive widget. 14 | #[derive(WidgetCommon_)] 15 | pub struct PlotPath { 16 | #[conrod(common_builder)] 17 | common: widget::CommonBuilder, 18 | style: Style, 19 | min_x: X, 20 | max_x: X, 21 | min_y: Y, 22 | max_y: Y, 23 | f: F, 24 | } 25 | 26 | /// Unique styling parameters for the `PlotPath` widget. 27 | #[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)] 28 | pub struct Style { 29 | /// The thickness of the plotted line. 30 | #[conrod(default = "1.0")] 31 | pub thickness: Option, 32 | /// The color of the line. 33 | #[conrod(default = "theme.shape_color")] 34 | pub color: Option, 35 | } 36 | 37 | widget_ids! { 38 | struct Ids { 39 | point_path, 40 | } 41 | } 42 | 43 | /// Unique state stored between updates for the `PlotPath` widget. 44 | pub struct State { 45 | ids: Ids, 46 | } 47 | 48 | impl PlotPath { 49 | /// Begin building a new `PlotPath` widget instance. 50 | pub fn new(min_x: X, max_x: X, min_y: Y, max_y: Y, f: F) -> Self { 51 | PlotPath { 52 | common: widget::CommonBuilder::default(), 53 | style: Style::default(), 54 | min_x: min_x, 55 | max_x: max_x, 56 | min_y: min_y, 57 | max_y: max_y, 58 | f: f, 59 | } 60 | } 61 | 62 | /// The thickness of the point path used to draw the plot. 63 | pub fn thickness(mut self, thickness: Scalar) -> Self { 64 | self.style.thickness = Some(thickness); 65 | self 66 | } 67 | } 68 | 69 | impl Widget for PlotPath 70 | where 71 | X: num::NumCast + Clone, 72 | Y: num::NumCast + Clone, 73 | F: FnMut(X) -> Y, 74 | { 75 | type State = State; 76 | type Style = Style; 77 | type Event = (); 78 | 79 | fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { 80 | State { 81 | ids: Ids::new(id_gen), 82 | } 83 | } 84 | 85 | fn style(&self) -> Self::Style { 86 | self.style.clone() 87 | } 88 | 89 | fn is_over(&self) -> widget::IsOverFn { 90 | fn is_over_widget(widget: &graph::Container, _: Point, _: &Theme) -> widget::IsOver { 91 | let unique = widget.state_and_style::().unwrap(); 92 | unique.state.ids.point_path.into() 93 | } 94 | is_over_widget 95 | } 96 | 97 | /// Update the state of the PlotPath. 98 | fn update(self, args: widget::UpdateArgs) -> Self::Event { 99 | let widget::UpdateArgs { 100 | id, 101 | state, 102 | style, 103 | rect, 104 | ui, 105 | .. 106 | } = args; 107 | let PlotPath { 108 | min_x, 109 | max_x, 110 | min_y, 111 | max_y, 112 | mut f, 113 | .. 114 | } = self; 115 | 116 | let y_to_scalar = 117 | |y| utils::map_range(y, min_y.clone(), max_y.clone(), rect.bottom(), rect.top()); 118 | let scalar_to_x = 119 | |s| utils::map_range(s, rect.left(), rect.right(), min_x.clone(), max_x.clone()); 120 | 121 | let point_iter = (0..rect.w() as usize).map(|x_scalar| { 122 | let x_scalar = x_scalar as Scalar + rect.x.start; 123 | let x = scalar_to_x(x_scalar); 124 | let y = f(x); 125 | let y_scalar = y_to_scalar(y); 126 | [x_scalar, y_scalar] 127 | }); 128 | 129 | let thickness = style.thickness(ui.theme()); 130 | let color = style.color(ui.theme()); 131 | widget::PointPath::new(point_iter) 132 | .wh(rect.dim()) 133 | .xy(rect.xy()) 134 | .color(color) 135 | .thickness(thickness) 136 | .parent(id) 137 | .graphics_for(id) 138 | .set(state.ids.point_path, ui); 139 | } 140 | } 141 | 142 | impl Colorable for PlotPath { 143 | builder_method!(color { style.color = Some(Color) }); 144 | } 145 | -------------------------------------------------------------------------------- /conrod_core/src/widget/primitive/image.rs: -------------------------------------------------------------------------------- 1 | //! A simple, non-interactive widget for drawing an `Image`. 2 | 3 | use image; 4 | use position::{Dimension, Rect}; 5 | use widget; 6 | use {Color, Ui, Widget}; 7 | 8 | /// A primitive and basic widget for drawing an `Image`. 9 | #[derive(Copy, Clone, WidgetCommon_)] 10 | pub struct Image { 11 | /// Data necessary and common for all widget builder types. 12 | #[conrod(common_builder)] 13 | pub common: widget::CommonBuilder, 14 | /// The unique identifier for the image that will be drawn. 15 | pub image_id: image::Id, 16 | /// The rectangle area of the original source image that should be used. 17 | pub src_rect: Option, 18 | /// Unique styling. 19 | pub style: Style, 20 | } 21 | 22 | /// Unique `State` to be stored between updates for the `Image`. 23 | #[derive(Copy, Clone)] 24 | pub struct State { 25 | /// The rectangular area of the image that we wish to display. 26 | /// 27 | /// If `None`, the entire image will be used. 28 | pub src_rect: Option, 29 | /// The unique identifier for the image's associated data that will be drawn. 30 | pub image_id: image::Id, 31 | } 32 | 33 | /// Unique styling for the `Image` widget. 34 | #[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)] 35 | pub struct Style { 36 | /// Optionally specify a single color to use for the image. 37 | #[conrod(default = "None")] 38 | pub maybe_color: Option>, 39 | } 40 | 41 | impl Image { 42 | /// Construct a new `Image`. 43 | /// 44 | /// Note that the `Image` widget does not require borrowing or owning any image data directly. 45 | /// Instead, image data is stored within a `conrod::image::Map` where `image::Id`s are mapped 46 | /// to their associated data. 47 | /// 48 | /// This is done for a few reasons: 49 | /// 50 | /// - To avoid requiring that the widget graph owns an instance of each image 51 | /// - To avoid requiring that the user passes the image data to the `Image` every update 52 | /// unnecessarily 53 | /// - To make it easier for users to borrow and mutate their images without needing to index 54 | /// into the `Ui`'s widget graph (which also requires casting types). 55 | /// 56 | /// During rendering, conrod will take the `image::Map`, retrieve the data associated with each 57 | /// image and yield it via the `render::Primitive::Image` variant. 58 | /// 59 | /// Note: this implies that the type must be the same for all `Image` widgets instantiated via 60 | /// the same `Ui`. In the case that you require multiple different types of images, we 61 | /// recommend that you either: 62 | /// 63 | /// 1. use an enum with a variant for each type 64 | /// 2. use a trait object, where the trait is implemented for each of your image types or 65 | /// 3. use an index type which may be mapped to your various image types. 66 | pub fn new(image_id: image::Id) -> Self { 67 | Image { 68 | common: widget::CommonBuilder::default(), 69 | image_id: image_id, 70 | src_rect: None, 71 | style: Style::default(), 72 | } 73 | } 74 | 75 | /// The rectangular area of the image that we wish to display. 76 | /// 77 | /// If this method is not called, the entire image will be used. 78 | pub fn source_rectangle(mut self, rect: Rect) -> Self { 79 | self.src_rect = Some(rect); 80 | self 81 | } 82 | 83 | builder_methods! { 84 | pub color { style.maybe_color = Some(Option) } 85 | } 86 | } 87 | 88 | impl Widget for Image { 89 | type State = State; 90 | type Style = Style; 91 | type Event = (); 92 | 93 | fn init_state(&self, _: widget::id::Generator) -> Self::State { 94 | State { 95 | src_rect: None, 96 | image_id: self.image_id, 97 | } 98 | } 99 | 100 | fn style(&self) -> Self::Style { 101 | self.style.clone() 102 | } 103 | 104 | fn default_x_dimension(&self, ui: &Ui) -> Dimension { 105 | match self.src_rect.as_ref() { 106 | Some(rect) => Dimension::Absolute(rect.w()), 107 | None => widget::default_x_dimension(self, ui), 108 | } 109 | } 110 | 111 | fn default_y_dimension(&self, ui: &Ui) -> Dimension { 112 | match self.src_rect.as_ref() { 113 | Some(rect) => Dimension::Absolute(rect.h()), 114 | None => widget::default_y_dimension(self, ui), 115 | } 116 | } 117 | 118 | fn update(self, args: widget::UpdateArgs) -> Self::Event { 119 | let widget::UpdateArgs { state, .. } = args; 120 | let Image { 121 | image_id, src_rect, .. 122 | } = self; 123 | 124 | if state.image_id != image_id { 125 | state.update(|state| state.image_id = image_id); 126 | } 127 | if state.src_rect != src_rect { 128 | state.update(|state| state.src_rect = src_rect); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /conrod_core/src/widget/primitive/mod.rs: -------------------------------------------------------------------------------- 1 | //! Primitive widgets are special in that they are built into conrod's `render`ing logic. 2 | //! 3 | //! By providing a set of foundational graphics widgets, we avoid the need for other widgets to 4 | //! define their own methods for rendering. Instead, conrod graphics backends only need to define 5 | //! rendering methods for a small set of primitives. 6 | 7 | pub mod image; 8 | pub mod line; 9 | pub mod point_path; 10 | pub mod shape; 11 | pub mod text; 12 | 13 | use {Point, Range, Rect}; 14 | 15 | /// Find the bounding rect for the given series of points. 16 | pub fn bounding_box_for_points(mut points: I) -> Rect 17 | where 18 | I: Iterator, 19 | { 20 | points 21 | .next() 22 | .map(|first| { 23 | let start_rect = Rect { 24 | x: Range { 25 | start: first[0], 26 | end: first[0], 27 | }, 28 | y: Range { 29 | start: first[1], 30 | end: first[1], 31 | }, 32 | }; 33 | points.fold(start_rect, Rect::stretch_to_point) 34 | }) 35 | .unwrap_or_else(|| Rect::from_xy_dim([0.0, 0.0], [0.0, 0.0])) 36 | } 37 | -------------------------------------------------------------------------------- /conrod_core/src/widget/primitive/shape/circle.rs: -------------------------------------------------------------------------------- 1 | //! An adaptation of the **Oval** type where the width and height are equal. 2 | 3 | use super::oval::{Full, Oval}; 4 | use super::Style; 5 | use widget; 6 | use {Color, Dimensions, Scalar}; 7 | 8 | /// A tiny wrapper around the **Oval** widget type. 9 | #[derive(Copy, Clone, Debug)] 10 | pub struct Circle; 11 | 12 | fn rad_to_dim(radius: Scalar) -> Dimensions { 13 | let side = radius * 2.0; 14 | [side, side] 15 | } 16 | 17 | impl Circle { 18 | /// Build a circular **Oval** with the given dimensions and style. 19 | pub fn styled(radius: Scalar, style: Style) -> Oval { 20 | Oval::styled(rad_to_dim(radius), style) 21 | } 22 | 23 | /// Build a new **Fill**ed circular **Oval**. 24 | pub fn fill(radius: Scalar) -> Oval { 25 | Oval::fill(rad_to_dim(radius)) 26 | } 27 | 28 | /// Build a new circular **Oval** **Fill**ed with the given color. 29 | pub fn fill_with(radius: Scalar, color: Color) -> Oval { 30 | Oval::fill_with(rad_to_dim(radius), color) 31 | } 32 | 33 | /// Build a new circular **Outline**d **Oval** widget. 34 | pub fn outline(radius: Scalar) -> Oval { 35 | Oval::outline(rad_to_dim(radius)) 36 | } 37 | 38 | /// Build a new circular **Oval** **Outline**d with the given style. 39 | pub fn outline_styled(radius: Scalar, line_style: widget::line::Style) -> Oval { 40 | Oval::outline_styled(rad_to_dim(radius), line_style) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /conrod_core/src/widget/primitive/shape/mod.rs: -------------------------------------------------------------------------------- 1 | //! A module encompassing the primitive 2D shape widgets. 2 | 3 | use color::Color; 4 | use theme::Theme; 5 | use widget; 6 | 7 | pub mod circle; 8 | pub mod oval; 9 | pub mod polygon; 10 | pub mod rectangle; 11 | pub mod triangles; 12 | 13 | /// The style for some 2D shape. 14 | #[derive(Copy, Clone, Debug, PartialEq)] 15 | pub enum Style { 16 | /// The outline of the shape with this style. 17 | Outline(widget::line::Style), 18 | /// A rectangle filled with this color. 19 | Fill(Option), 20 | } 21 | 22 | impl Style { 23 | /// A default `Fill` style. 24 | pub fn fill() -> Self { 25 | Style::Fill(None) 26 | } 27 | 28 | /// A `Fill` style with some given `Color`. 29 | pub fn fill_with(color: Color) -> Self { 30 | Style::Fill(Some(color)) 31 | } 32 | 33 | /// A default `Outline` style. 34 | pub fn outline() -> Self { 35 | Style::Outline(widget::line::Style::new()) 36 | } 37 | 38 | /// A default `Outline` style. 39 | pub fn outline_styled(line_style: widget::line::Style) -> Self { 40 | Style::Outline(line_style) 41 | } 42 | 43 | /// The style with some given Color. 44 | pub fn color(mut self, color: Color) -> Self { 45 | self.set_color(color); 46 | self 47 | } 48 | 49 | /// Set the color for the style. 50 | pub fn set_color(&mut self, color: Color) { 51 | match *self { 52 | Style::Fill(ref mut maybe_color) => *maybe_color = Some(color), 53 | Style::Outline(ref mut line_style) => line_style.set_color(color), 54 | } 55 | } 56 | 57 | /// Get the color of the Rectangle. 58 | pub fn get_color(&self, theme: &Theme) -> Color { 59 | match *self { 60 | Style::Fill(maybe_color) => maybe_color.unwrap_or(theme.shape_color), 61 | Style::Outline(style) => style.get_color(theme), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /conrod_core/src/widget/primitive/shape/rectangle.rs: -------------------------------------------------------------------------------- 1 | //! A simple, non-interactive rectangle shape widget. 2 | //! 3 | //! Due to the frequency of its use in GUIs, the `Rectangle` gets its own widget to allow backends 4 | //! to specialise their rendering implementations. 5 | 6 | use super::Style; 7 | use widget; 8 | use widget::triangles::Triangle; 9 | use {Color, Colorable, Dimensions, Point, Rect, Sizeable, Widget}; 10 | 11 | /// A basic, non-interactive rectangle shape widget. 12 | #[derive(Copy, Clone, Debug, WidgetCommon_)] 13 | pub struct Rectangle { 14 | /// Data necessary and common for all widget builder types. 15 | #[conrod(common_builder)] 16 | pub common: widget::CommonBuilder, 17 | /// Unique styling for the **Rectangle**. 18 | pub style: Style, 19 | } 20 | 21 | /// Unique state for the Rectangle. 22 | #[derive(Copy, Clone, Debug, PartialEq)] 23 | pub struct State { 24 | kind: Kind, 25 | } 26 | 27 | /// Whether the rectangle is drawn as an outline or a filled color. 28 | #[derive(Copy, Clone, Debug, PartialEq)] 29 | pub enum Kind { 30 | /// Only the outline of the rectangle is drawn. 31 | Outline, 32 | /// The rectangle area is filled with some color. 33 | Fill, 34 | } 35 | 36 | impl Rectangle { 37 | /// Build a rectangle with the dimensions and style. 38 | pub fn styled(dim: Dimensions, style: Style) -> Self { 39 | Rectangle { 40 | common: widget::CommonBuilder::default(), 41 | style: style, 42 | } 43 | .wh(dim) 44 | } 45 | 46 | /// Build a new filled rectangle. 47 | pub fn fill(dim: Dimensions) -> Self { 48 | Rectangle::styled(dim, Style::fill()) 49 | } 50 | 51 | /// Build a new filled rectangle widget filled with the given color. 52 | pub fn fill_with(dim: Dimensions, color: Color) -> Self { 53 | Rectangle::styled(dim, Style::fill_with(color)) 54 | } 55 | 56 | /// Build a new outlined rectangle widget. 57 | pub fn outline(dim: Dimensions) -> Self { 58 | Rectangle::styled(dim, Style::outline()) 59 | } 60 | 61 | /// Build an outlined rectangle rather than a filled one. 62 | pub fn outline_styled(dim: Dimensions, line_style: widget::line::Style) -> Self { 63 | Rectangle::styled(dim, Style::outline_styled(line_style)) 64 | } 65 | } 66 | 67 | impl Widget for Rectangle { 68 | type State = State; 69 | type Style = Style; 70 | type Event = (); 71 | 72 | fn init_state(&self, _: widget::id::Generator) -> Self::State { 73 | State { kind: Kind::Fill } 74 | } 75 | 76 | fn style(&self) -> Self::Style { 77 | self.style.clone() 78 | } 79 | 80 | /// Update the state of the Rectangle. 81 | fn update(self, args: widget::UpdateArgs) -> Self::Event { 82 | let widget::UpdateArgs { state, style, .. } = args; 83 | 84 | let kind = match *style { 85 | Style::Fill(_) => Kind::Fill, 86 | Style::Outline(_) => Kind::Outline, 87 | }; 88 | 89 | if state.kind != kind { 90 | state.update(|state| state.kind = kind); 91 | } 92 | } 93 | } 94 | 95 | impl Colorable for Rectangle { 96 | fn color(mut self, color: Color) -> Self { 97 | self.style.set_color(color); 98 | self 99 | } 100 | } 101 | 102 | /// The two triangles that describe the given `Rect`. 103 | pub fn triangles(rect: Rect) -> (Triangle, Triangle) { 104 | let (l, r, b, t) = rect.l_r_b_t(); 105 | let quad = [[l, t], [r, t], [r, b], [l, b]]; 106 | widget::triangles::from_quad(quad) 107 | } 108 | -------------------------------------------------------------------------------- /conrod_core/src/widget/text_box.rs: -------------------------------------------------------------------------------- 1 | //! A widget for displaying and mutating a one-line field of text. 2 | 3 | use event; 4 | use input; 5 | use position::{Range, Rect, Scalar}; 6 | use text; 7 | use widget; 8 | use {Borderable, Color, Colorable, FontSize, Positionable, Sizeable, Widget}; 9 | 10 | /// A widget for displaying and mutating a small, one-line field of text, given by the user in the 11 | /// form of a `String`. 12 | /// 13 | /// It's reaction is triggered upon pressing of the `Enter`/`Return` key. 14 | #[derive(WidgetCommon_)] 15 | pub struct TextBox<'a> { 16 | #[conrod(common_builder)] 17 | common: widget::CommonBuilder, 18 | text: &'a str, 19 | style: Style, 20 | } 21 | 22 | /// Unique graphical styling for the TextBox. 23 | #[derive(Copy, Clone, Debug, Default, PartialEq, WidgetStyle_)] 24 | pub struct Style { 25 | /// The length of the gap between the bounding rectangle's border and the edge of the text. 26 | #[conrod(default = "5.0")] 27 | pub text_padding: Option, 28 | /// Color of the rectangle behind the text. 29 | /// 30 | /// If you don't want to see the rectangle, either set the color with a zeroed alpha or use 31 | /// the `TextEdit` widget directly. 32 | #[conrod(default = "theme.shape_color")] 33 | pub color: Option, 34 | /// The width of the bounding `BorderedRectangle` border. 35 | #[conrod(default = "theme.border_width")] 36 | pub border: Option, 37 | /// The color of the `BorderedRecangle`'s border. 38 | #[conrod(default = "theme.border_color")] 39 | pub border_color: Option, 40 | /// The color of the `TextEdit` widget. 41 | #[conrod(default = "theme.label_color")] 42 | pub text_color: Option, 43 | /// The font size for the text. 44 | #[conrod(default = "theme.font_size_medium")] 45 | pub font_size: Option, 46 | /// The typographic alignment of the text. 47 | #[conrod(default = "text::Justify::Left")] 48 | pub justify: Option, 49 | /// The font used for the `Text`. 50 | #[conrod(default = "theme.font_id")] 51 | pub font_id: Option>, 52 | } 53 | 54 | widget_ids! { 55 | struct Ids { 56 | text_edit, 57 | rectangle, 58 | } 59 | } 60 | 61 | /// The `State` of the `TextBox` widget that will be cached within the `Ui`. 62 | pub struct State { 63 | ids: Ids, 64 | } 65 | 66 | impl<'a> TextBox<'a> { 67 | /// Construct a TextBox widget. 68 | pub fn new(text: &'a str) -> Self { 69 | TextBox { 70 | common: widget::CommonBuilder::default(), 71 | style: Style::default(), 72 | text: text, 73 | } 74 | } 75 | 76 | /// Align the text to the left of its bounding **Rect**'s *x* axis range. 77 | pub fn left_justify(self) -> Self { 78 | self.justify(text::Justify::Left) 79 | } 80 | 81 | /// Align the text to the middle of its bounding **Rect**'s *x* axis range. 82 | pub fn center_justify(self) -> Self { 83 | self.justify(text::Justify::Center) 84 | } 85 | 86 | /// Align the text to the right of its bounding **Rect**'s *x* axis range. 87 | pub fn right_justify(self) -> Self { 88 | self.justify(text::Justify::Right) 89 | } 90 | 91 | /// Specify the font used for displaying the text. 92 | pub fn font_id(mut self, font_id: text::font::Id) -> Self { 93 | self.style.font_id = Some(Some(font_id)); 94 | self 95 | } 96 | 97 | builder_methods! { 98 | pub text_color { style.text_color = Some(Color) } 99 | pub font_size { style.font_size = Some(FontSize) } 100 | pub justify { style.justify = Some(text::Justify) } 101 | pub pad_text { style.text_padding = Some(Scalar) } 102 | } 103 | } 104 | 105 | /// Events produced by the `TextBox`. 106 | #[derive(Clone, Debug)] 107 | pub enum Event { 108 | /// The `String` was updated. 109 | Update(String), 110 | /// The `Return` or `Enter` key was pressed. 111 | Enter, 112 | } 113 | 114 | impl<'a> Widget for TextBox<'a> { 115 | type State = State; 116 | type Style = Style; 117 | type Event = Vec; 118 | 119 | fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { 120 | State { 121 | ids: Ids::new(id_gen), 122 | } 123 | } 124 | 125 | fn style(&self) -> Self::Style { 126 | self.style.clone() 127 | } 128 | 129 | /// Update the state of the TextEdit. 130 | fn update(self, args: widget::UpdateArgs) -> Self::Event { 131 | let widget::UpdateArgs { 132 | id, 133 | state, 134 | rect, 135 | style, 136 | ui, 137 | .. 138 | } = args; 139 | let TextBox { text, .. } = self; 140 | 141 | let font_size = style.font_size(ui.theme()); 142 | let border = style.border(ui.theme()); 143 | let text_padding = style.text_padding(ui.theme()); 144 | let justify = style.justify(ui.theme()); 145 | 146 | let text_rect = { 147 | let w = rect.x.pad(border + text_padding).len(); 148 | let h = font_size as Scalar + 1.0; 149 | let x = Range::new(0.0, w).align_middle_of(rect.x); 150 | let y = Range::new(0.0, h).align_middle_of(rect.y); 151 | Rect { x: x, y: y } 152 | }; 153 | 154 | let color = style.color(ui.theme()); 155 | let border_color = style.border_color(ui.theme()); 156 | widget::BorderedRectangle::new(rect.dim()) 157 | .xy(rect.xy()) 158 | .graphics_for(id) 159 | .parent(id) 160 | .border(border) 161 | .color(color) 162 | .border_color(border_color) 163 | .set(state.ids.rectangle, ui); 164 | 165 | let mut events = Vec::new(); 166 | 167 | let text_color = style.text_color(ui.theme()); 168 | let font_id = style.font_id(&ui.theme).or(ui.fonts.ids().next()); 169 | if let Some(new_string) = widget::TextEdit::new(text) 170 | .and_then(font_id, widget::TextEdit::font_id) 171 | .wh(text_rect.dim()) 172 | .xy(text_rect.xy()) 173 | .font_size(font_size) 174 | .color(text_color) 175 | .justify(justify) 176 | .parent(id) 177 | .set(state.ids.text_edit, ui) 178 | { 179 | events.push(Event::Update(new_string)); 180 | } 181 | 182 | // Produce an event for any `Enter`/`Return` presses. 183 | // 184 | // TODO: We should probably be doing this via the `TextEdit` widget. 185 | for widget_event in ui.widget_input(state.ids.text_edit).events() { 186 | match widget_event { 187 | event::Widget::Press(press) => match press.button { 188 | event::Button::Keyboard(key) => match key { 189 | input::Key::Return => events.push(Event::Enter), 190 | _ => (), 191 | }, 192 | _ => (), 193 | }, 194 | _ => (), 195 | } 196 | } 197 | 198 | events 199 | } 200 | } 201 | 202 | impl<'a> Borderable for TextBox<'a> { 203 | builder_methods! { 204 | border { style.border = Some(Scalar) } 205 | border_color { style.border_color = Some(Color) } 206 | } 207 | } 208 | 209 | impl<'a> Colorable for TextBox<'a> { 210 | builder_method!(color { style.color = Some(Color) }); 211 | } 212 | -------------------------------------------------------------------------------- /conrod_derive/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /bin/main 15 | /bin/test-internal 16 | /bin/test-external 17 | /doc/ 18 | /target/ 19 | /build/ 20 | /.rust/ 21 | rusti.sh 22 | /examples/**/target 23 | 24 | Cargo.lock 25 | -------------------------------------------------------------------------------- /conrod_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conrod_derive" 3 | version = "0.76.1" 4 | authors = ["mitchmindtree "] 5 | description = "A crate providing procedural macros for the conrod library" 6 | license = "MIT OR Apache-2.0" 7 | keywords = ["conrod", "gui", "derive", "procedural", "macro"] 8 | repository = "https://github.com/pistondevelopers/conrod.git" 9 | homepage = "https://github.com/pistondevelopers/conrod" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1" 16 | quote = "1" 17 | syn = { version = "1", features = ["extra-traits", "full"] } 18 | -------------------------------------------------------------------------------- /conrod_derive/src/common.rs: -------------------------------------------------------------------------------- 1 | use std; 2 | 3 | use proc_macro2; 4 | use syn; 5 | 6 | // The implementation for `WidgetCommon`. 7 | pub fn impl_widget_common(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { 8 | let ident = &ast.ident; 9 | let common_field = common_builder_field(ast).unwrap(); 10 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 11 | let dummy_const = syn::Ident::new( 12 | &format!("_IMPL_WIDGET_COMMON_FOR_{}", ident), 13 | proc_macro2::Span::call_site(), 14 | ); 15 | 16 | let impl_item = quote! { 17 | impl #impl_generics _conrod::widget::Common for #ident #ty_generics #where_clause { 18 | fn common(&self) -> &_conrod::widget::CommonBuilder { 19 | &self.#common_field 20 | } 21 | fn common_mut(&mut self) -> &mut _conrod::widget::CommonBuilder { 22 | &mut self.#common_field 23 | } 24 | } 25 | }; 26 | 27 | quote! { 28 | #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] 29 | const #dummy_const: () = { 30 | extern crate conrod_core as _conrod; 31 | #impl_item 32 | }; 33 | } 34 | } 35 | 36 | // The implementation for `WidgetCommon_`. 37 | // 38 | // The same as `WidgetCommon` but only for use within the conrod crate itself. 39 | pub fn impl_widget_common_(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { 40 | let ident = &ast.ident; 41 | let common_field = common_builder_field(ast).unwrap(); 42 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 43 | let dummy_const = syn::Ident::new( 44 | &format!("_IMPL_WIDGET_COMMON_FOR_{}", ident), 45 | proc_macro2::Span::call_site(), 46 | ); 47 | 48 | let impl_item = quote! { 49 | impl #impl_generics ::widget::Common for #ident #ty_generics #where_clause { 50 | fn common(&self) -> &::widget::CommonBuilder { 51 | &self.#common_field 52 | } 53 | fn common_mut(&mut self) -> &mut ::widget::CommonBuilder { 54 | &mut self.#common_field 55 | } 56 | } 57 | }; 58 | 59 | quote! { 60 | #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] 61 | const #dummy_const: () = { 62 | #impl_item 63 | }; 64 | } 65 | } 66 | 67 | // Find the name of the struct and the field with the `CommonBuilder` attribute. 68 | fn common_builder_field(ast: &syn::DeriveInput) -> Result<&syn::Ident, Error> { 69 | // Ensure we are deriving for a struct. 70 | let body = match ast.data { 71 | syn::Data::Struct(ref body) => body, 72 | _ => return Err(Error::NotStruct), 73 | }; 74 | 75 | // We can only derive `WidgetCommon` for structs with fields. 76 | match body.fields { 77 | syn::Fields::Named(_) => {} 78 | syn::Fields::Unnamed(_) => return Err(Error::TupleStruct), 79 | syn::Fields::Unit => return Err(Error::UnitStruct), 80 | }; 81 | 82 | // Find the field on the struct with the `WidgetCommon` attribute. 83 | // 84 | // We need this to know what field to use for the `common` and `common_mut` accessor methods. 85 | let mut common_field = None; 86 | for field in body.fields.iter() { 87 | // First, search for the attribute. 88 | for attr in &field.attrs { 89 | if let Ok(_meta) = attr.parse_meta() { 90 | let mut is_conrod = false; 91 | let mut has_common_builder = false; 92 | if let syn::Meta::List(_metalist) = _meta { 93 | if _metalist.path.is_ident("conrod") { 94 | is_conrod = true; 95 | } 96 | 97 | has_common_builder = _metalist.nested.iter().any(|v| match *v { 98 | syn::NestedMeta::Meta(syn::Meta::Path(ref p)) 99 | if p.is_ident("common_builder") => 100 | { 101 | true 102 | } 103 | _ => false, 104 | }); 105 | } 106 | if is_conrod && has_common_builder { 107 | // There should only be one `CommonBuilder` attribute. 108 | if common_field.is_some() { 109 | return Err(Error::MultipleCommonBuilderFields); 110 | } 111 | common_field = match field.ident.as_ref() { 112 | Some(ident) => Some(ident), 113 | None => return Err(Error::UnnamedCommonBuilderField), 114 | }; 115 | } 116 | } 117 | } 118 | } 119 | 120 | // Panic if we couldn't find a field with the `CommonBuilder` attribute. 121 | let common_field = match common_field { 122 | Some(field) => field, 123 | None => return Err(Error::NoCommonBuilderField), 124 | }; 125 | 126 | Ok(common_field) 127 | } 128 | 129 | // Errors that might be returned from `name_and_common_builder_field`. 130 | #[derive(Debug)] 131 | enum Error { 132 | NotStruct, 133 | TupleStruct, 134 | UnitStruct, 135 | MultipleCommonBuilderFields, 136 | UnnamedCommonBuilderField, 137 | NoCommonBuilderField, 138 | } 139 | 140 | impl std::error::Error for Error {} 141 | 142 | impl std::fmt::Display for Error { 143 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 144 | let s = match *self { 145 | Error::NotStruct => "#[derive(WidgetCommon)] is only defined for structs", 146 | Error::TupleStruct => "#[derive(WidgetCommon)] is not defined for tuple structs", 147 | Error::UnitStruct => "#[derive(WidgetCommon)] is not defined for unit structs", 148 | Error::MultipleCommonBuilderFields => { 149 | "Found multiple #[conrod(common_builder)] attributes when only one is required" 150 | } 151 | Error::UnnamedCommonBuilderField => { 152 | "Cannot use #[conrod(common_builder)] attribute on unnamed fields" 153 | } 154 | Error::NoCommonBuilderField => { 155 | "`#[derive(WidgetCommon)]` requires a struct with one field of type \ 156 | `conrod::widget::CommonBuilder` that has the `#[conrod(common_builder)]` \ 157 | attribute" 158 | } 159 | }; 160 | write!(f, "{}", s) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /conrod_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | extern crate proc_macro2; 4 | #[macro_use] 5 | extern crate quote; 6 | extern crate syn; 7 | 8 | mod common; 9 | mod style; 10 | mod utils; 11 | 12 | use proc_macro::TokenStream; 13 | 14 | // The implementation for the `WidgetCommon` trait derivation (aka `conrod_core::widget::Common`). 15 | #[proc_macro_derive(WidgetCommon, attributes(conrod, common_builder))] 16 | pub fn widget_common(input: TokenStream) -> TokenStream { 17 | impl_derive(input, common::impl_widget_common) 18 | } 19 | 20 | // The implementation for the `WidgetCommon_` trait derivation (aka `conrod_core::widget::Common`). 21 | // 22 | // Note that this is identical to the `WidgetCommon` trait, but only for use within the conrod 23 | // crate itself. 24 | #[proc_macro_derive(WidgetCommon_, attributes(conrod, common_builder))] 25 | pub fn widget_common_(input: TokenStream) -> TokenStream { 26 | impl_derive(input, common::impl_widget_common_) 27 | } 28 | 29 | // The implementation for the `WidgetStyle` trait derivation (aka `conrod_core::widget::Style`). 30 | #[proc_macro_derive(WidgetStyle, attributes(conrod, default))] 31 | pub fn widget_style(input: TokenStream) -> TokenStream { 32 | impl_derive(input, style::impl_widget_style) 33 | } 34 | 35 | // The implementation for the `WidgetStyle_` trait derivation (aka `conrod_core::widget::Style`). 36 | // 37 | // Note that this is identical to the `WidgetStyle_` trait, but only for use within the conrod 38 | // crate itself. 39 | #[proc_macro_derive(WidgetStyle_, attributes(conrod, default))] 40 | pub fn widget_style_(input: TokenStream) -> TokenStream { 41 | impl_derive(input, style::impl_widget_style_) 42 | } 43 | 44 | // Use the given function to generate a TokenStream for the derive implementation. 45 | fn impl_derive( 46 | input: TokenStream, 47 | generate_derive: fn(&syn::DeriveInput) -> proc_macro2::TokenStream, 48 | ) -> TokenStream { 49 | // Parse the input TokenStream representation. 50 | let ast = syn::parse(input).unwrap(); 51 | 52 | // Build the implementation. 53 | let gen = generate_derive(&ast); 54 | // Return the generated impl. 55 | gen.into() 56 | } 57 | -------------------------------------------------------------------------------- /conrod_derive/src/utils.rs: -------------------------------------------------------------------------------- 1 | use syn; 2 | 3 | // An iterator yielding all conrod attributes in the given attributes. 4 | pub struct ConrodAttrs { 5 | attrs: I, 6 | } 7 | 8 | pub fn conrod_attrs<'a, I>(attrs: I) -> ConrodAttrs 9 | where 10 | I: IntoIterator, 11 | { 12 | ConrodAttrs { 13 | attrs: attrs.into_iter(), 14 | } 15 | } 16 | 17 | impl<'a, I> Iterator for ConrodAttrs 18 | where 19 | I: Iterator, 20 | { 21 | type Item = Vec; 22 | fn next(&mut self) -> Option { 23 | while let Some(attr) = self.attrs.next() { 24 | if let Ok(_meta) = attr.parse_meta() { 25 | if let &syn::Meta::List(ref _metalist) = &_meta { 26 | if _metalist.path.is_ident("conrod") { 27 | let j = _metalist 28 | .nested 29 | .clone() 30 | .into_pairs() 31 | .map(|pair| pair.into_value()) 32 | .collect::>(); 33 | return Some(j); 34 | } 35 | } 36 | } 37 | } 38 | None 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /scripts/id_rsa.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/conrod/8b4e0d3dfbe5cab76858562df4dc1bd65c7ad67b/scripts/id_rsa.enc -------------------------------------------------------------------------------- /scripts/travis-doc-upload.cfg: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=conrod 2 | DOCS_REPO=PistonDevelopers/docs.git 3 | SSH_KEY_TRAVIS_ID=7e632a3a6c1c 4 | --------------------------------------------------------------------------------