|
| 1 | +//! This example showcases a simple native custom widget that renders using |
| 2 | +//! arbitrary low-level geometry. |
| 3 | +mod rainbow { |
| 4 | + // For now, to implement a custom native widget you will need to add |
| 5 | + // `iced_native` and `iced_wgpu` to your dependencies. |
| 6 | + // |
| 7 | + // Then, you simply need to define your widget type and implement the |
| 8 | + // `iced_native::Widget` trait with the `iced_wgpu::Renderer`. |
| 9 | + // |
| 10 | + // Of course, you can choose to make the implementation renderer-agnostic, |
| 11 | + // if you wish to, by creating your own `Renderer` trait, which could be |
| 12 | + // implemented by `iced_wgpu` and other renderers. |
| 13 | + use iced_graphics::renderer::{self, Renderer}; |
| 14 | + use iced_graphics::triangle::ColoredVertex2D; |
| 15 | + use iced_graphics::{Backend, Primitive}; |
| 16 | + |
| 17 | + use iced_native::layout; |
| 18 | + use iced_native::widget::{self, Widget}; |
| 19 | + use iced_native::{ |
| 20 | + Element, Layout, Length, Point, Rectangle, Size, Vector, |
| 21 | + }; |
| 22 | + |
| 23 | + #[derive(Debug, Clone, Copy, Default)] |
| 24 | + pub struct Rainbow; |
| 25 | + |
| 26 | + pub fn rainbow() -> Rainbow { |
| 27 | + Rainbow |
| 28 | + } |
| 29 | + |
| 30 | + impl<Message, B, T> Widget<Message, Renderer<B, T>> for Rainbow |
| 31 | + where |
| 32 | + B: Backend, |
| 33 | + { |
| 34 | + fn width(&self) -> Length { |
| 35 | + Length::Fill |
| 36 | + } |
| 37 | + |
| 38 | + fn height(&self) -> Length { |
| 39 | + Length::Shrink |
| 40 | + } |
| 41 | + |
| 42 | + fn layout( |
| 43 | + &self, |
| 44 | + _renderer: &Renderer<B, T>, |
| 45 | + limits: &layout::Limits, |
| 46 | + ) -> layout::Node { |
| 47 | + let size = limits.width(Length::Fill).resolve(Size::ZERO); |
| 48 | + |
| 49 | + layout::Node::new(Size::new(size.width, size.width)) |
| 50 | + } |
| 51 | + |
| 52 | + fn draw( |
| 53 | + &self, |
| 54 | + _tree: &widget::Tree, |
| 55 | + renderer: &mut Renderer<B, T>, |
| 56 | + _theme: &T, |
| 57 | + _style: &renderer::Style, |
| 58 | + layout: Layout<'_>, |
| 59 | + cursor_position: Point, |
| 60 | + _viewport: &Rectangle, |
| 61 | + ) { |
| 62 | + use iced_graphics::triangle::Mesh2D; |
| 63 | + use iced_native::Renderer as _; |
| 64 | + |
| 65 | + let b = layout.bounds(); |
| 66 | + |
| 67 | + // R O Y G B I V |
| 68 | + let color_r = [1.0, 0.0, 0.0, 1.0]; |
| 69 | + let color_o = [1.0, 0.5, 0.0, 1.0]; |
| 70 | + let color_y = [1.0, 1.0, 0.0, 1.0]; |
| 71 | + let color_g = [0.0, 1.0, 0.0, 1.0]; |
| 72 | + let color_gb = [0.0, 1.0, 0.5, 1.0]; |
| 73 | + let color_b = [0.0, 0.2, 1.0, 1.0]; |
| 74 | + let color_i = [0.5, 0.0, 1.0, 1.0]; |
| 75 | + let color_v = [0.75, 0.0, 0.5, 1.0]; |
| 76 | + |
| 77 | + let posn_center = { |
| 78 | + if b.contains(cursor_position) { |
| 79 | + [cursor_position.x - b.x, cursor_position.y - b.y] |
| 80 | + } else { |
| 81 | + [b.width / 2.0, b.height / 2.0] |
| 82 | + } |
| 83 | + }; |
| 84 | + |
| 85 | + let posn_tl = [0.0, 0.0]; |
| 86 | + let posn_t = [b.width / 2.0, 0.0]; |
| 87 | + let posn_tr = [b.width, 0.0]; |
| 88 | + let posn_r = [b.width, b.height / 2.0]; |
| 89 | + let posn_br = [b.width, b.height]; |
| 90 | + let posn_b = [(b.width / 2.0), b.height]; |
| 91 | + let posn_bl = [0.0, b.height]; |
| 92 | + let posn_l = [0.0, b.height / 2.0]; |
| 93 | + |
| 94 | + let mesh = Primitive::SolidMesh { |
| 95 | + size: b.size(), |
| 96 | + buffers: Mesh2D { |
| 97 | + vertices: vec![ |
| 98 | + ColoredVertex2D { |
| 99 | + position: posn_center, |
| 100 | + color: [1.0, 1.0, 1.0, 1.0], |
| 101 | + }, |
| 102 | + ColoredVertex2D { |
| 103 | + position: posn_tl, |
| 104 | + color: color_r, |
| 105 | + }, |
| 106 | + ColoredVertex2D { |
| 107 | + position: posn_t, |
| 108 | + color: color_o, |
| 109 | + }, |
| 110 | + ColoredVertex2D { |
| 111 | + position: posn_tr, |
| 112 | + color: color_y, |
| 113 | + }, |
| 114 | + ColoredVertex2D { |
| 115 | + position: posn_r, |
| 116 | + color: color_g, |
| 117 | + }, |
| 118 | + ColoredVertex2D { |
| 119 | + position: posn_br, |
| 120 | + color: color_gb, |
| 121 | + }, |
| 122 | + ColoredVertex2D { |
| 123 | + position: posn_b, |
| 124 | + color: color_b, |
| 125 | + }, |
| 126 | + ColoredVertex2D { |
| 127 | + position: posn_bl, |
| 128 | + color: color_i, |
| 129 | + }, |
| 130 | + ColoredVertex2D { |
| 131 | + position: posn_l, |
| 132 | + color: color_v, |
| 133 | + }, |
| 134 | + ], |
| 135 | + indices: vec![ |
| 136 | + 0, 1, 2, // TL |
| 137 | + 0, 2, 3, // T |
| 138 | + 0, 3, 4, // TR |
| 139 | + 0, 4, 5, // R |
| 140 | + 0, 5, 6, // BR |
| 141 | + 0, 6, 7, // B |
| 142 | + 0, 7, 8, // BL |
| 143 | + 0, 8, 1, // L |
| 144 | + ], |
| 145 | + }, |
| 146 | + }; |
| 147 | + |
| 148 | + renderer.with_translation(Vector::new(b.x, b.y), |renderer| { |
| 149 | + renderer.draw_primitive(mesh); |
| 150 | + }); |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + impl<'a, Message, B, T> From<Rainbow> for Element<'a, Message, Renderer<B, T>> |
| 155 | + where |
| 156 | + B: Backend, |
| 157 | + { |
| 158 | + fn from(rainbow: Rainbow) -> Self { |
| 159 | + Self::new(rainbow) |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +use iced::widget::{column, container, scrollable}; |
| 165 | +use iced::{Element, Length, Sandbox, Settings}; |
| 166 | +use rainbow::rainbow; |
| 167 | + |
| 168 | +pub fn main() -> iced::Result { |
| 169 | + Example::run(Settings::default()) |
| 170 | +} |
| 171 | + |
| 172 | +struct Example; |
| 173 | + |
| 174 | +impl Sandbox for Example { |
| 175 | + type Message = (); |
| 176 | + |
| 177 | + fn new() -> Self { |
| 178 | + Self |
| 179 | + } |
| 180 | + |
| 181 | + fn title(&self) -> String { |
| 182 | + String::from("Custom 2D geometry - Iced") |
| 183 | + } |
| 184 | + |
| 185 | + fn update(&mut self, _: ()) {} |
| 186 | + |
| 187 | + fn view(&self) -> Element<()> { |
| 188 | + let content = column![ |
| 189 | + rainbow(), |
| 190 | + "In this example we draw a custom widget Rainbow, using \ |
| 191 | + the Mesh2D primitive. This primitive supplies a list of \ |
| 192 | + triangles, expressed as vertices and indices.", |
| 193 | + "Move your cursor over it, and see the center vertex \ |
| 194 | + follow you!", |
| 195 | + "Every Vertex2D defines its own color. You could use the \ |
| 196 | + Mesh2D primitive to render virtually any two-dimensional \ |
| 197 | + geometry for your widget.", |
| 198 | + ] |
| 199 | + .padding(20) |
| 200 | + .spacing(20) |
| 201 | + .max_width(500); |
| 202 | + |
| 203 | + let scrollable = |
| 204 | + scrollable(container(content).width(Length::Fill).center_x()); |
| 205 | + |
| 206 | + container(scrollable) |
| 207 | + .width(Length::Fill) |
| 208 | + .height(Length::Fill) |
| 209 | + .center_y() |
| 210 | + .into() |
| 211 | + } |
| 212 | +} |
0 commit comments