From 03b34931383e701c39c653a7662a616fe21a0947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 16:07:22 +0700 Subject: [PATCH 01/92] Remove trait-specific draw logic in `iced_native` --- examples/custom_widget/src/main.rs | 27 ++- examples/geometry/src/main.rs | 183 +++++++-------- examples/integration_opengl/src/main.rs | 26 ++- examples/integration_wgpu/src/main.rs | 30 +-- glow/src/backend.rs | 12 +- glow/src/window/compositor.rs | 10 +- glutin/src/application.rs | 24 +- graphics/src/lib.rs | 2 +- graphics/src/overlay/menu.rs | 106 +-------- graphics/src/renderer.rs | 92 +------- graphics/src/widget/button.rs | 92 +------- graphics/src/widget/canvas.rs | 49 ++-- graphics/src/widget/checkbox.rs | 56 +---- graphics/src/widget/column.rs | 46 +--- graphics/src/widget/container.rs | 61 +---- graphics/src/widget/image.rs | 18 +- graphics/src/widget/image/viewer.rs | 55 +---- graphics/src/widget/pane_grid.rs | 272 +---------------------- graphics/src/widget/pick_list.rs | 81 +------ graphics/src/widget/progress_bar.rs | 53 +---- graphics/src/widget/qr_code.rs | 97 ++++---- graphics/src/widget/radio.rs | 58 +---- graphics/src/widget/row.rs | 46 +--- graphics/src/widget/rule.rs | 57 +---- graphics/src/widget/scrollable.rs | 87 +------- graphics/src/widget/slider.rs | 101 +-------- graphics/src/widget/space.rs | 14 -- graphics/src/widget/svg.rs | 18 +- graphics/src/widget/text.rs | 43 +--- graphics/src/widget/text_input.rs | 182 +-------------- graphics/src/widget/toggler.rs | 80 +------ graphics/src/widget/tooltip.rs | 151 +------------ graphics/src/window/compositor.rs | 18 +- graphics/src/window/gl_compositor.rs | 9 +- native/src/element.rs | 28 +-- native/src/layout.rs | 2 - native/src/layout/debugger.rs | 24 -- native/src/lib.rs | 2 +- native/src/overlay.rs | 2 +- native/src/overlay/element.rs | 4 +- native/src/overlay/menu.rs | 79 ++----- native/src/program/state.rs | 17 +- native/src/renderer.rs | 15 +- native/src/renderer/null.rs | 191 +--------------- native/src/user_interface.rs | 34 ++- native/src/widget.rs | 2 +- native/src/widget/button.rs | 25 +-- native/src/widget/checkbox.rs | 74 +++--- native/src/widget/column.rs | 37 +-- native/src/widget/container.rs | 24 +- native/src/widget/image.rs | 7 +- native/src/widget/image/viewer.rs | 47 +--- native/src/widget/pane_grid.rs | 170 +++++--------- native/src/widget/pane_grid/content.rs | 53 ++--- native/src/widget/pane_grid/title_bar.rs | 56 ++--- native/src/widget/pick_list.rs | 27 +-- native/src/widget/progress_bar.rs | 25 +-- native/src/widget/radio.rs | 74 +++--- native/src/widget/row.rs | 44 +--- native/src/widget/rule.rs | 18 +- native/src/widget/scrollable.rs | 117 ++++------ native/src/widget/slider.rs | 43 ++-- native/src/widget/space.rs | 15 +- native/src/widget/svg.rs | 8 +- native/src/widget/text.rs | 40 +--- native/src/widget/text_input.rs | 89 +++----- native/src/widget/toggler.rs | 88 +++----- native/src/widget/tooltip.rs | 43 +--- wgpu/src/backend.rs | 11 +- wgpu/src/window/compositor.rs | 29 +-- winit/src/application.rs | 27 +-- 71 files changed, 681 insertions(+), 3166 deletions(-) delete mode 100644 native/src/layout/debugger.rs diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index c9ad1905b1..648e729548 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -11,8 +11,7 @@ mod circle { // implemented by `iced_wgpu` and other renderers. use iced_graphics::{Backend, Defaults, Primitive, Renderer}; use iced_native::{ - layout, mouse, Background, Color, Element, Hasher, Layout, Length, - Point, Rectangle, Size, Widget, + layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; pub struct Circle { @@ -55,20 +54,20 @@ mod circle { &self, _renderer: &mut Renderer, _defaults: &Defaults, - layout: Layout<'_>, + _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> (Primitive, mouse::Interaction) { - ( - Primitive::Quad { - bounds: layout.bounds(), - background: Background::Color(Color::BLACK), - border_radius: self.radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - mouse::Interaction::default(), - ) + ) { + // ( + // Primitive::Quad { + // bounds: layout.bounds(), + // background: Background::Color(Color::BLACK), + // border_radius: self.radius, + // border_width: 0.0, + // border_color: Color::TRANSPARENT, + // }, + // mouse::Interaction::default(), + // ) } } diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index e51154930b..0745739a43 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -55,98 +55,99 @@ mod rainbow { &self, _renderer: &mut Renderer, _defaults: &Defaults, - layout: Layout<'_>, - cursor_position: Point, + _layout: Layout<'_>, + _cursor_position: Point, _viewport: &Rectangle, - ) -> (Primitive, mouse::Interaction) { - let b = layout.bounds(); - - // R O Y G B I V - let color_r = [1.0, 0.0, 0.0, 1.0]; - let color_o = [1.0, 0.5, 0.0, 1.0]; - let color_y = [1.0, 1.0, 0.0, 1.0]; - let color_g = [0.0, 1.0, 0.0, 1.0]; - let color_gb = [0.0, 1.0, 0.5, 1.0]; - let color_b = [0.0, 0.2, 1.0, 1.0]; - let color_i = [0.5, 0.0, 1.0, 1.0]; - let color_v = [0.75, 0.0, 0.5, 1.0]; - - let posn_center = { - if b.contains(cursor_position) { - [cursor_position.x - b.x, cursor_position.y - b.y] - } else { - [b.width / 2.0, b.height / 2.0] - } - }; - - let posn_tl = [0.0, 0.0]; - let posn_t = [b.width / 2.0, 0.0]; - let posn_tr = [b.width, 0.0]; - let posn_r = [b.width, b.height / 2.0]; - let posn_br = [b.width, b.height]; - let posn_b = [(b.width / 2.0), b.height]; - let posn_bl = [0.0, b.height]; - let posn_l = [0.0, b.height / 2.0]; - - ( - Primitive::Translate { - translation: Vector::new(b.x, b.y), - content: Box::new(Primitive::Mesh2D { - size: b.size(), - buffers: Mesh2D { - vertices: vec![ - Vertex2D { - position: posn_center, - color: [1.0, 1.0, 1.0, 1.0], - }, - Vertex2D { - position: posn_tl, - color: color_r, - }, - Vertex2D { - position: posn_t, - color: color_o, - }, - Vertex2D { - position: posn_tr, - color: color_y, - }, - Vertex2D { - position: posn_r, - color: color_g, - }, - Vertex2D { - position: posn_br, - color: color_gb, - }, - Vertex2D { - position: posn_b, - color: color_b, - }, - Vertex2D { - position: posn_bl, - color: color_i, - }, - Vertex2D { - position: posn_l, - color: color_v, - }, - ], - indices: vec![ - 0, 1, 2, // TL - 0, 2, 3, // T - 0, 3, 4, // TR - 0, 4, 5, // R - 0, 5, 6, // BR - 0, 6, 7, // B - 0, 7, 8, // BL - 0, 8, 1, // L - ], - }, - }), - }, - mouse::Interaction::default(), - ) + ) { + // let b = layout.bounds(); + + // // R O Y G B I V + // let color_r = [1.0, 0.0, 0.0, 1.0]; + // let color_o = [1.0, 0.5, 0.0, 1.0]; + // let color_y = [1.0, 1.0, 0.0, 1.0]; + // let color_g = [0.0, 1.0, 0.0, 1.0]; + // let color_gb = [0.0, 1.0, 0.5, 1.0]; + // let color_b = [0.0, 0.2, 1.0, 1.0]; + // let color_i = [0.5, 0.0, 1.0, 1.0]; + // let color_v = [0.75, 0.0, 0.5, 1.0]; + + // let posn_center = { + // if b.contains(cursor_position) { + // [cursor_position.x - b.x, cursor_position.y - b.y] + // } else { + // [b.width / 2.0, b.height / 2.0] + // } + // }; + + // let posn_tl = [0.0, 0.0]; + // let posn_t = [b.width / 2.0, 0.0]; + // let posn_tr = [b.width, 0.0]; + // let posn_r = [b.width, b.height / 2.0]; + // let posn_br = [b.width, b.height]; + // let posn_b = [(b.width / 2.0), b.height]; + // let posn_bl = [0.0, b.height]; + // let posn_l = [0.0, b.height / 2.0]; + + // ( + // Primitive::Translate { + // translation: Vector::new(b.x, b.y), + // content: Box::new(Primitive::Mesh2D { + // size: b.size(), + // buffers: Mesh2D { + // vertices: vec![ + // Vertex2D { + // position: posn_center, + // color: [1.0, 1.0, 1.0, 1.0], + // }, + // Vertex2D { + // position: posn_tl, + // color: color_r, + // }, + // Vertex2D { + // position: posn_t, + // color: color_o, + // }, + // Vertex2D { + // position: posn_tr, + // color: color_y, + // }, + // Vertex2D { + // position: posn_r, + // color: color_g, + // }, + // Vertex2D { + // position: posn_br, + // color: color_gb, + // }, + // Vertex2D { + // position: posn_b, + // color: color_b, + // }, + // Vertex2D { + // position: posn_bl, + // color: color_i, + // }, + // Vertex2D { + // position: posn_l, + // color: color_v, + // }, + // ], + // indices: vec![ + // 0, 1, 2, // TL + // 0, 2, 3, // T + // 0, 3, 4, // TR + // 0, 4, 5, // R + // 0, 5, 6, // BR + // 0, 6, 7, // B + // 0, 7, 8, // BL + // 0, 8, 1, // L + // ], + // }, + // }), + // }, + // mouse::Interaction::default(), + // ) + // TODO } } diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs index f80915d2c4..bab6331e62 100644 --- a/examples/integration_opengl/src/main.rs +++ b/examples/integration_opengl/src/main.rs @@ -160,18 +160,22 @@ pub fn main() { } // And then iced on top - let mouse_interaction = renderer.backend_mut().draw( - &gl, - &viewport, - state.primitive(), - &debug.overlay(), - ); + renderer.present(|backend, primitive| { + backend.present( + &gl, + primitive, + &viewport, + &debug.overlay(), + ); + }); + // Update the mouse cursor - windowed_context.window().set_cursor_icon( - iced_winit::conversion::mouse_interaction( - mouse_interaction, - ), - ); + // TODO + // windowed_context.window().set_cursor_icon( + // iced_winit::conversion::mouse_interaction( + // mouse_interaction, + // ), + // ); windowed_context.swap_buffers().unwrap(); } diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 7ef148bc5d..9980acc209 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -195,26 +195,28 @@ pub fn main() { } // And then iced on top - let mouse_interaction = renderer.backend_mut().draw( - &mut device, - &mut staging_belt, - &mut encoder, - &view, - &viewport, - state.primitive(), - &debug.overlay(), - ); + renderer.present(|backend, primitive| { + backend.present( + &mut device, + &mut staging_belt, + &mut encoder, + &view, + primitive, + &viewport, + &debug.overlay(), + ); + }); // Then we submit the work staging_belt.finish(); queue.submit(Some(encoder.finish())); // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - mouse_interaction, - ), - ); + // window.set_cursor_icon( + // iced_winit::conversion::mouse_interaction( + // mouse_interaction, + // ), + // ); // And recall staging buffers local_pool diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 9a9457cfc2..b8e4bd4b95 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -5,10 +5,8 @@ use crate::{Settings, Transformation, Viewport}; use iced_graphics::backend; use iced_graphics::font; -use iced_graphics::Layer; -use iced_graphics::Primitive; +use iced_graphics::{Layer, Primitive}; use iced_native::alignment; -use iced_native::mouse; use iced_native::{Font, Size}; /// A [`glow`] graphics backend for [`iced`]. @@ -47,13 +45,13 @@ impl Backend { /// /// The text provided as overlay will be rendered on top of the primitives. /// This is useful for rendering debug information. - pub fn draw>( + pub fn present>( &mut self, gl: &glow::Context, + primitive: &Primitive, viewport: &Viewport, - (primitive, mouse_interaction): &(Primitive, mouse::Interaction), overlay_text: &[T], - ) -> mouse::Interaction { + ) { let viewport_size = viewport.physical_size(); let scale_factor = viewport.scale_factor() as f32; let projection = viewport.projection(); @@ -70,8 +68,6 @@ impl Backend { viewport_size.height, ); } - - *mouse_interaction } fn flush( diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs index b8157f78e2..36e591cd0c 100644 --- a/glow/src/window/compositor.rs +++ b/glow/src/window/compositor.rs @@ -3,7 +3,6 @@ use crate::{Backend, Color, Error, Renderer, Settings, Viewport}; use core::ffi::c_void; use glow::HasContext; use iced_graphics::{Antialiasing, Size}; -use iced_native::mouse; /// A window graphics backend for iced powered by `glow`. #[allow(missing_debug_implementations)] @@ -59,14 +58,13 @@ impl iced_graphics::window::GLCompositor for Compositor { } } - fn draw>( + fn present>( &mut self, renderer: &mut Self::Renderer, viewport: &Viewport, color: Color, - output: &::Output, overlay: &[T], - ) -> mouse::Interaction { + ) { let gl = &self.gl; let [r, g, b, a] = color.into_linear(); @@ -76,6 +74,8 @@ impl iced_graphics::window::GLCompositor for Compositor { gl.clear(glow::COLOR_BUFFER_BIT); } - renderer.backend_mut().draw(gl, viewport, output, overlay) + renderer.present(|backend, primitive| { + backend.present(gl, primitive, viewport, overlay); + }); } } diff --git a/glutin/src/application.rs b/glutin/src/application.rs index fa2192d81d..508dfe040d 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -1,5 +1,5 @@ //! Create interactive, native cross-platform applications. -use crate::{mouse, Error, Executor, Runtime}; +use crate::{Error, Executor, Runtime}; pub use iced_winit::Application; @@ -179,9 +179,8 @@ async fn run_instance( &mut debug, )); - let mut primitive = - user_interface.draw(&mut renderer, state.cursor_position()); - let mut mouse_interaction = mouse::Interaction::default(); + // TODO + // let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); @@ -246,8 +245,7 @@ async fn run_instance( } debug.draw_started(); - primitive = - user_interface.draw(&mut renderer, state.cursor_position()); + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); context.window().request_redraw(); @@ -291,8 +289,7 @@ async fn run_instance( debug.layout_finished(); debug.draw_started(); - primitive = user_interface - .draw(&mut renderer, state.cursor_position()); + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); context.resize(glutin::dpi::PhysicalSize::new( @@ -305,11 +302,10 @@ async fn run_instance( viewport_version = current_viewport_version; } - let new_mouse_interaction = compositor.draw( + compositor.present( &mut renderer, state.viewport(), state.background_color(), - &primitive, &debug.overlay(), ); @@ -317,14 +313,6 @@ async fn run_instance( debug.render_finished(); - if new_mouse_interaction != mouse_interaction { - context.window().set_cursor_icon( - conversion::mouse_interaction(new_mouse_interaction), - ); - - mouse_interaction = new_mouse_interaction; - } - // TODO: Handle animations! // Maybe we can use `ControlFlow::WaitUntil` for this. } diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 54cdcb773f..9c113da61f 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -4,7 +4,7 @@ //! ![The native path of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true) //! //! [`iced`]: https://github.com/hecrj/iced -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![deny(unsafe_code)] diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs index 53f47984e1..7dfb48b9e5 100644 --- a/graphics/src/overlay/menu.rs +++ b/graphics/src/overlay/menu.rs @@ -1,9 +1,8 @@ //! Build and show dropdown menus. -use crate::alignment; use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; +use crate::Renderer; -use iced_native::{mouse, overlay, Color, Font, Padding, Point, Rectangle}; +use iced_native::overlay; pub use iced_style::menu::Style; @@ -12,105 +11,4 @@ where B: Backend + backend::Text, { type Style = Style; - - fn decorate( - &mut self, - bounds: Rectangle, - _cursor_position: Point, - style: &Style, - (primitives, mouse_cursor): Self::Output, - ) -> Self::Output { - ( - Primitive::Group { - primitives: vec![ - Primitive::Quad { - bounds, - background: style.background, - border_color: style.border_color, - border_width: style.border_width, - border_radius: 0.0, - }, - primitives, - ], - }, - mouse_cursor, - ) - } - - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - viewport: &Rectangle, - options: &[T], - hovered_option: Option, - padding: Padding, - text_size: u16, - font: Font, - style: &Style, - ) -> Self::Output { - use std::f32; - - let is_mouse_over = bounds.contains(cursor_position); - let option_height = (text_size + padding.vertical()) as usize; - - let mut primitives = Vec::new(); - - let offset = viewport.y - bounds.y; - let start = (offset / option_height as f32) as usize; - let end = - ((offset + viewport.height) / option_height as f32).ceil() as usize; - - let visible_options = &options[start..end.min(options.len())]; - - for (i, option) in visible_options.iter().enumerate() { - let i = start + i; - let is_selected = hovered_option == Some(i); - - let bounds = Rectangle { - x: bounds.x, - y: bounds.y + (option_height * i) as f32, - width: bounds.width, - height: f32::from(text_size + padding.vertical()), - }; - - if is_selected { - primitives.push(Primitive::Quad { - bounds, - background: style.selected_background, - border_color: Color::TRANSPARENT, - border_width: 0.0, - border_radius: 0.0, - }); - } - - primitives.push(Primitive::Text { - content: option.to_string(), - bounds: Rectangle { - x: bounds.x + padding.left as f32, - y: bounds.center_y(), - width: f32::INFINITY, - ..bounds - }, - size: f32::from(text_size), - font, - color: if is_selected { - style.selected_text_color - } else { - style.text_color - }, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - }); - } - - ( - Primitive::Group { primitives }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index fa63991b88..cedffe7e5d 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,30 +1,29 @@ use crate::{Backend, Defaults, Primitive}; -use iced_native::layout::{self, Layout}; -use iced_native::mouse; -use iced_native::{ - Background, Color, Element, Point, Rectangle, Vector, Widget, -}; +use iced_native::layout; +use iced_native::{Element, Rectangle}; /// A backend-agnostic renderer that supports all the built-in widgets. #[derive(Debug)] pub struct Renderer { backend: B, + primitive: Primitive, } impl Renderer { /// Creates a new [`Renderer`] from the given [`Backend`]. pub fn new(backend: B) -> Self { - Self { backend } + Self { + backend, + primitive: Primitive::None, + } } - /// Returns a reference to the [`Backend`] of the [`Renderer`]. pub fn backend(&self) -> &B { &self.backend } - /// Returns a mutable reference to the [`Backend`] of the [`Renderer`]. - pub fn backend_mut(&mut self) -> &mut B { - &mut self.backend + pub fn present(&mut self, f: impl FnOnce(&mut B, &Primitive)) { + f(&mut self.backend, &self.primitive); } } @@ -32,7 +31,6 @@ impl iced_native::Renderer for Renderer where B: Backend, { - type Output = (Primitive, mouse::Interaction); type Defaults = Defaults; fn layout<'a, Message>( @@ -47,75 +45,5 @@ where layout } - fn overlay( - &mut self, - (base_primitive, base_cursor): (Primitive, mouse::Interaction), - (overlay_primitives, overlay_cursor): (Primitive, mouse::Interaction), - overlay_bounds: Rectangle, - ) -> (Primitive, mouse::Interaction) { - ( - Primitive::Group { - primitives: vec![ - base_primitive, - Primitive::Clip { - bounds: Rectangle { - width: overlay_bounds.width + 0.5, - height: overlay_bounds.height + 0.5, - ..overlay_bounds - }, - offset: Vector::new(0, 0), - content: Box::new(overlay_primitives), - }, - ], - }, - if base_cursor > overlay_cursor { - base_cursor - } else { - overlay_cursor - }, - ) - } -} - -impl layout::Debugger for Renderer -where - B: Backend, -{ - fn explain( - &mut self, - defaults: &Defaults, - widget: &dyn Widget, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - color: Color, - ) -> Self::Output { - let (primitive, cursor) = - widget.draw(self, defaults, layout, cursor_position, viewport); - - let mut primitives = Vec::new(); - - explain_layout(layout, color, &mut primitives); - primitives.push(primitive); - - (Primitive::Group { primitives }, cursor) - } -} - -fn explain_layout( - layout: Layout<'_>, - color: Color, - primitives: &mut Vec, -) { - primitives.push(Primitive::Quad { - bounds: layout.bounds(), - background: Background::Color(Color::TRANSPARENT), - border_radius: 0.0, - border_width: 1.0, - border_color: [0.6, 0.6, 0.6, 0.5].into(), - }); - - for child in layout.children() { - explain_layout(child, color, primitives); - } + fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} } diff --git a/graphics/src/widget/button.rs b/graphics/src/widget/button.rs index 60400ed812..990aac9f92 100644 --- a/graphics/src/widget/button.rs +++ b/graphics/src/widget/button.rs @@ -1,12 +1,8 @@ //! Allow your users to perform actions by pressing a button. //! //! A [`Button`] has some local [`State`]. -use crate::defaults::{self, Defaults}; -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; -use iced_native::{ - Background, Color, Element, Layout, Padding, Point, Rectangle, Vector, -}; +use crate::{Backend, Renderer}; +use iced_native::Padding; pub use iced_native::button::State; pub use iced_style::button::{Style, StyleSheet}; @@ -24,88 +20,4 @@ where const DEFAULT_PADDING: Padding = Padding::new(5); type Style = Box; - - fn draw( - &mut self, - _defaults: &Defaults, - bounds: Rectangle, - cursor_position: Point, - is_disabled: bool, - is_pressed: bool, - style: &Box, - content: &Element<'_, Message, Self>, - content_layout: Layout<'_>, - ) -> Self::Output { - let is_mouse_over = bounds.contains(cursor_position); - - let styling = if is_disabled { - style.disabled() - } else if is_mouse_over { - if is_pressed { - style.pressed() - } else { - style.hovered() - } - } else { - style.active() - }; - - let (content, _) = content.draw( - self, - &Defaults { - text: defaults::Text { - color: styling.text_color, - }, - }, - content_layout, - cursor_position, - &bounds, - ); - - ( - if styling.background.is_some() || styling.border_width > 0.0 { - let background = Primitive::Quad { - bounds, - background: styling - .background - .unwrap_or(Background::Color(Color::TRANSPARENT)), - border_radius: styling.border_radius, - border_width: styling.border_width, - border_color: styling.border_color, - }; - - if styling.shadow_offset == Vector::default() { - Primitive::Group { - primitives: vec![background, content], - } - } else { - // TODO: Implement proper shadow support - let shadow = Primitive::Quad { - bounds: Rectangle { - x: bounds.x + styling.shadow_offset.x, - y: bounds.y + styling.shadow_offset.y, - ..bounds - }, - background: Background::Color( - [0.0, 0.0, 0.0, 0.5].into(), - ), - border_radius: styling.border_radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }; - - Primitive::Group { - primitives: vec![shadow, background, content], - } - } - } else { - content - }, - if is_mouse_over && !is_disabled { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 7897c8ec7e..7bf00ca521 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -3,12 +3,10 @@ //! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a //! [`Frame`]. It can be used for animation, data visualization, game graphics, //! and more! -use crate::{Backend, Defaults, Primitive, Renderer}; +use crate::{Backend, Defaults, Renderer}; use iced_native::layout; -use iced_native::mouse; use iced_native::{ - Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, - Widget, + Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; use std::hash::Hash; use std::marker::PhantomData; @@ -190,28 +188,29 @@ where &self, _renderer: &mut Renderer, _defaults: &Defaults, - layout: Layout<'_>, - cursor_position: Point, + _layout: Layout<'_>, + _cursor_position: Point, _viewport: &Rectangle, - ) -> (Primitive, mouse::Interaction) { - let bounds = layout.bounds(); - let translation = Vector::new(bounds.x, bounds.y); - let cursor = Cursor::from_window_position(cursor_position); - - ( - Primitive::Translate { - translation, - content: Box::new(Primitive::Group { - primitives: self - .program - .draw(bounds, cursor) - .into_iter() - .map(Geometry::into_primitive) - .collect(), - }), - }, - self.program.mouse_interaction(bounds, cursor), - ) + ) { + // let bounds = layout.bounds(); + // let translation = Vector::new(bounds.x, bounds.y); + // let cursor = Cursor::from_window_position(cursor_position); + + // ( + // Primitive::Translate { + // translation, + // content: Box::new(Primitive::Group { + // primitives: self + // .program + // .draw(bounds, cursor) + // .into_iter() + // .map(Geometry::into_primitive) + // .collect(), + // }), + // }, + // self.program.mouse_interaction(bounds, cursor), + // ) + // TODO } fn hash_layout(&self, state: &mut Hasher) { diff --git a/graphics/src/widget/checkbox.rs b/graphics/src/widget/checkbox.rs index 620bfc9e2e..3b75652539 100644 --- a/graphics/src/widget/checkbox.rs +++ b/graphics/src/widget/checkbox.rs @@ -1,10 +1,8 @@ //! Show toggle controls using checkboxes. -use crate::alignment; use crate::backend::{self, Backend}; -use crate::{Primitive, Rectangle, Renderer}; +use crate::Renderer; use iced_native::checkbox; -use iced_native::mouse; pub use iced_style::checkbox::{Style, StyleSheet}; @@ -22,56 +20,4 @@ where const DEFAULT_SIZE: u16 = 20; const DEFAULT_SPACING: u16 = 15; - - fn draw( - &mut self, - bounds: Rectangle, - is_checked: bool, - is_mouse_over: bool, - (label, _): Self::Output, - style_sheet: &Self::Style, - ) -> Self::Output { - let style = if is_mouse_over { - style_sheet.hovered(is_checked) - } else { - style_sheet.active(is_checked) - }; - - let checkbox = Primitive::Quad { - bounds, - background: style.background, - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }; - - ( - Primitive::Group { - primitives: if is_checked { - let check = Primitive::Text { - content: B::CHECKMARK_ICON.to_string(), - font: B::ICON_FONT, - size: bounds.height * 0.7, - bounds: Rectangle { - x: bounds.center_x(), - y: bounds.center_y(), - ..bounds - }, - color: style.checkmark_color, - horizontal_alignment: alignment::Horizontal::Center, - vertical_alignment: alignment::Vertical::Center, - }; - - vec![checkbox, check, label] - } else { - vec![checkbox, label] - }, - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/column.rs b/graphics/src/widget/column.rs index 0cf56842bd..567529e5d6 100644 --- a/graphics/src/widget/column.rs +++ b/graphics/src/widget/column.rs @@ -1,49 +1,5 @@ -use crate::{Backend, Primitive, Renderer}; -use iced_native::column; -use iced_native::mouse; -use iced_native::{Element, Layout, Point, Rectangle}; +use crate::Renderer; /// A container that distributes its contents vertically. pub type Column<'a, Message, Backend> = iced_native::Column<'a, Message, Renderer>; - -impl column::Renderer for Renderer -where - B: Backend, -{ - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output { - let mut mouse_interaction = mouse::Interaction::default(); - - ( - Primitive::Group { - primitives: content - .iter() - .zip(layout.children()) - .map(|(child, layout)| { - let (primitive, new_mouse_interaction) = child.draw( - self, - defaults, - layout, - cursor_position, - viewport, - ); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - primitive - }) - .collect(), - }, - mouse_interaction, - ) - } -} diff --git a/graphics/src/widget/container.rs b/graphics/src/widget/container.rs index aae3e1d85f..811a0c7f5b 100644 --- a/graphics/src/widget/container.rs +++ b/graphics/src/widget/container.rs @@ -1,8 +1,6 @@ //! Decorate content and apply alignment. use crate::container; -use crate::defaults::{self, Defaults}; -use crate::{Backend, Primitive, Renderer}; -use iced_native::{Background, Color, Element, Layout, Point, Rectangle}; +use crate::{Backend, Renderer}; pub use iced_style::container::{Style, StyleSheet}; @@ -18,61 +16,4 @@ where B: Backend, { type Style = Box; - - fn draw( - &mut self, - defaults: &Defaults, - bounds: Rectangle, - cursor_position: Point, - viewport: &Rectangle, - style_sheet: &Self::Style, - content: &Element<'_, Message, Self>, - content_layout: Layout<'_>, - ) -> Self::Output { - let style = style_sheet.style(); - - let defaults = Defaults { - text: defaults::Text { - color: style.text_color.unwrap_or(defaults.text.color), - }, - }; - - let (content, mouse_interaction) = content.draw( - self, - &defaults, - content_layout, - cursor_position, - viewport, - ); - - if let Some(background) = background(bounds, &style) { - ( - Primitive::Group { - primitives: vec![background, content], - }, - mouse_interaction, - ) - } else { - (content, mouse_interaction) - } - } -} - -pub(crate) fn background( - bounds: Rectangle, - style: &container::Style, -) -> Option { - if style.background.is_some() || style.border_width > 0.0 { - Some(Primitive::Quad { - bounds, - background: style - .background - .unwrap_or(Background::Color(Color::TRANSPARENT)), - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }) - } else { - None - } } diff --git a/graphics/src/widget/image.rs b/graphics/src/widget/image.rs index bdf03de302..b55ba32fe5 100644 --- a/graphics/src/widget/image.rs +++ b/graphics/src/widget/image.rs @@ -3,10 +3,8 @@ pub mod viewer; use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; +use crate::Renderer; use iced_native::image; -use iced_native::mouse; -use iced_native::Layout; pub use iced_native::image::{Handle, Image, Viewer}; @@ -17,18 +15,4 @@ where fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { self.backend().dimensions(handle) } - - fn draw( - &mut self, - handle: image::Handle, - layout: Layout<'_>, - ) -> Self::Output { - ( - Primitive::Image { - handle, - bounds: layout.bounds(), - }, - mouse::Interaction::default(), - ) - } } diff --git a/graphics/src/widget/image/viewer.rs b/graphics/src/widget/image/viewer.rs index 28dffc4f34..ea7d859136 100644 --- a/graphics/src/widget/image/viewer.rs +++ b/graphics/src/widget/image/viewer.rs @@ -1,55 +1,2 @@ //! Zoom and pan on an image. -use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; - -use iced_native::image; -use iced_native::image::viewer; -use iced_native::mouse; -use iced_native::{Rectangle, Size, Vector}; - -impl viewer::Renderer for Renderer -where - B: Backend + backend::Image, -{ - fn draw( - &mut self, - state: &viewer::State, - bounds: Rectangle, - image_size: Size, - translation: Vector, - handle: image::Handle, - is_mouse_over: bool, - ) -> Self::Output { - ( - { - Primitive::Clip { - bounds, - content: Box::new(Primitive::Translate { - translation, - content: Box::new(Primitive::Image { - handle, - bounds: Rectangle { - x: bounds.x, - y: bounds.y, - ..Rectangle::with_size(image_size) - }, - }), - }), - offset: Vector::new(0, 0), - } - }, - { - if state.is_cursor_grabbed() { - mouse::Interaction::Grabbing - } else if is_mouse_over - && (image_size.width > bounds.width - || image_size.height > bounds.height) - { - mouse::Interaction::Grab - } else { - mouse::Interaction::Idle - } - }, - ) - } -} +pub use iced_native::image::Viewer; diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index 92cdbb773d..8c6b0f82a3 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -7,12 +7,8 @@ //! drag and drop, and hotkey support. //! //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid -use crate::defaults; -use crate::{Backend, Color, Primitive, Renderer}; -use iced_native::container; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::pane_grid; -use iced_native::{Element, Layout, Point, Rectangle, Vector}; pub use iced_native::pane_grid::{ Axis, Configuration, Content, Direction, DragEvent, Node, Pane, @@ -35,270 +31,4 @@ where B: Backend, { type Style = Box; - - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[(Pane, Content<'_, Message, Self>)], - dragging: Option<(Pane, Point)>, - resizing: Option<(Axis, Rectangle, bool)>, - layout: Layout<'_>, - style_sheet: &::Style, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output { - let pane_cursor_position = if dragging.is_some() { - // TODO: Remove once cursor availability is encoded in the type - // system - Point::new(-1.0, -1.0) - } else { - cursor_position - }; - - let mut mouse_interaction = mouse::Interaction::default(); - let mut dragged_pane = None; - - let mut panes: Vec<_> = content - .iter() - .zip(layout.children()) - .enumerate() - .map(|(i, ((id, pane), layout))| { - let (primitive, new_mouse_interaction) = pane.draw( - self, - defaults, - layout, - pane_cursor_position, - viewport, - ); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - if let Some((dragging, origin)) = dragging { - if *id == dragging { - dragged_pane = Some((i, layout, origin)); - } - } - - primitive - }) - .collect(); - - let mut primitives = if let Some((index, layout, origin)) = dragged_pane - { - let pane = panes.remove(index); - let bounds = layout.bounds(); - - // TODO: Fix once proper layering is implemented. - // This is a pretty hacky way to achieve layering. - let clip = Primitive::Clip { - bounds: Rectangle { - x: cursor_position.x - origin.x, - y: cursor_position.y - origin.y, - width: bounds.width + 0.5, - height: bounds.height + 0.5, - }, - offset: Vector::new(0, 0), - content: Box::new(Primitive::Translate { - translation: Vector::new( - cursor_position.x - bounds.x - origin.x, - cursor_position.y - bounds.y - origin.y, - ), - content: Box::new(pane), - }), - }; - - panes.push(clip); - - panes - } else { - panes - }; - - let (primitives, mouse_interaction) = - if let Some((axis, split_region, is_picked)) = resizing { - let highlight = if is_picked { - style_sheet.picked_split() - } else { - style_sheet.hovered_split() - }; - - if let Some(highlight) = highlight { - primitives.push(Primitive::Quad { - bounds: match axis { - Axis::Horizontal => Rectangle { - x: split_region.x, - y: (split_region.y - + (split_region.height - highlight.width) - / 2.0) - .round(), - width: split_region.width, - height: highlight.width, - }, - Axis::Vertical => Rectangle { - x: (split_region.x - + (split_region.width - highlight.width) - / 2.0) - .round(), - y: split_region.y, - width: highlight.width, - height: split_region.height, - }, - }, - background: highlight.color.into(), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }); - } - - ( - primitives, - match axis { - Axis::Horizontal => { - mouse::Interaction::ResizingVertically - } - Axis::Vertical => { - mouse::Interaction::ResizingHorizontally - } - }, - ) - } else { - (primitives, mouse_interaction) - }; - - ( - Primitive::Group { primitives }, - if dragging.is_some() { - mouse::Interaction::Grabbing - } else { - mouse_interaction - }, - ) - } - - fn draw_pane( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - style_sheet: &::Style, - title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>, - body: (&Element<'_, Message, Self>, Layout<'_>), - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output { - let style = style_sheet.style(); - let (body, body_layout) = body; - - let (body_primitive, body_interaction) = - body.draw(self, defaults, body_layout, cursor_position, viewport); - - let background = crate::widget::container::background(bounds, &style); - - if let Some((title_bar, title_bar_layout)) = title_bar { - let show_controls = bounds.contains(cursor_position); - let is_over_pick_area = - title_bar.is_over_pick_area(title_bar_layout, cursor_position); - - let (title_bar_primitive, title_bar_interaction) = title_bar.draw( - self, - defaults, - title_bar_layout, - cursor_position, - viewport, - show_controls, - ); - - ( - Primitive::Group { - primitives: vec![ - background.unwrap_or(Primitive::None), - title_bar_primitive, - body_primitive, - ], - }, - if title_bar_interaction > body_interaction { - title_bar_interaction - } else if is_over_pick_area { - mouse::Interaction::Grab - } else { - body_interaction - }, - ) - } else { - ( - if let Some(background) = background { - Primitive::Group { - primitives: vec![background, body_primitive], - } - } else { - body_primitive - }, - body_interaction, - ) - } - } - - fn draw_title_bar( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - style_sheet: &::Style, - content: (&Element<'_, Message, Self>, Layout<'_>), - controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output { - let style = style_sheet.style(); - let (title_content, title_layout) = content; - - let defaults = Self::Defaults { - text: defaults::Text { - color: style.text_color.unwrap_or(defaults.text.color), - }, - }; - - let background = crate::widget::container::background(bounds, &style); - - let (title_primitive, title_interaction) = title_content.draw( - self, - &defaults, - title_layout, - cursor_position, - viewport, - ); - - if let Some((controls, controls_layout)) = controls { - let (controls_primitive, controls_interaction) = controls.draw( - self, - &defaults, - controls_layout, - cursor_position, - viewport, - ); - - ( - Primitive::Group { - primitives: vec![ - background.unwrap_or(Primitive::None), - title_primitive, - controls_primitive, - ], - }, - controls_interaction.max(title_interaction), - ) - } else { - ( - if let Some(background) = background { - Primitive::Group { - primitives: vec![background, title_primitive], - } - } else { - title_primitive - }, - title_interaction, - ) - } - } } diff --git a/graphics/src/widget/pick_list.rs b/graphics/src/widget/pick_list.rs index 532840b866..54f42cde09 100644 --- a/graphics/src/widget/pick_list.rs +++ b/graphics/src/widget/pick_list.rs @@ -1,9 +1,8 @@ //! Display a dropdown list of selectable values. -use crate::alignment; use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; +use crate::Renderer; -use iced_native::{mouse, Font, Padding, Point, Rectangle}; +use iced_native::Padding; use iced_style::menu; pub use iced_native::pick_list::State; @@ -24,80 +23,4 @@ where fn menu_style(style: &Box) -> menu::Style { style.menu() } - - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - selected: Option, - placeholder: Option<&str>, - padding: Padding, - text_size: u16, - font: Font, - style: &Box, - ) -> Self::Output { - let is_mouse_over = bounds.contains(cursor_position); - let is_selected = selected.is_some(); - - let style = if is_mouse_over { - style.hovered() - } else { - style.active() - }; - - let background = Primitive::Quad { - bounds, - background: style.background, - border_color: style.border_color, - border_width: style.border_width, - border_radius: style.border_radius, - }; - - let arrow_down = Primitive::Text { - content: B::ARROW_DOWN_ICON.to_string(), - font: B::ICON_FONT, - size: bounds.height * style.icon_size, - bounds: Rectangle { - x: bounds.x + bounds.width - f32::from(padding.horizontal()), - y: bounds.center_y(), - ..bounds - }, - color: style.text_color, - horizontal_alignment: alignment::Horizontal::Right, - vertical_alignment: alignment::Vertical::Center, - }; - - ( - Primitive::Group { - primitives: if let Some(label) = - selected.or_else(|| placeholder.map(str::to_string)) - { - let label = Primitive::Text { - content: label, - size: f32::from(text_size), - font, - color: is_selected - .then(|| style.text_color) - .unwrap_or(style.placeholder_color), - bounds: Rectangle { - x: bounds.x + f32::from(padding.left), - y: bounds.center_y(), - ..bounds - }, - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - }; - - vec![background, label, arrow_down] - } else { - vec![background, arrow_down] - }, - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/progress_bar.rs b/graphics/src/widget/progress_bar.rs index 32ee42c625..3d21b7b9d3 100644 --- a/graphics/src/widget/progress_bar.rs +++ b/graphics/src/widget/progress_bar.rs @@ -2,10 +2,8 @@ //! //! A [`ProgressBar`] has a range of possible values and a current value, //! as well as a length, height and style. -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::progress_bar; -use iced_native::{Color, Rectangle}; pub use iced_style::progress_bar::{Style, StyleSheet}; @@ -22,53 +20,4 @@ where type Style = Box; const DEFAULT_HEIGHT: u16 = 30; - - fn draw( - &self, - bounds: Rectangle, - range: std::ops::RangeInclusive, - value: f32, - style_sheet: &Self::Style, - ) -> Self::Output { - let style = style_sheet.style(); - let (range_start, range_end) = range.into_inner(); - - let active_progress_width = if range_start >= range_end { - 0.0 - } else { - bounds.width * (value - range_start) / (range_end - range_start) - }; - - let background = Primitive::Group { - primitives: vec![Primitive::Quad { - bounds: Rectangle { ..bounds }, - background: style.background, - border_radius: style.border_radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }], - }; - - ( - if active_progress_width > 0.0 { - let bar = Primitive::Quad { - bounds: Rectangle { - width: active_progress_width, - ..bounds - }, - background: style.bar, - border_radius: style.border_radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }; - - Primitive::Group { - primitives: vec![background, bar], - } - } else { - background - }, - mouse::Interaction::default(), - ) - } } diff --git a/graphics/src/widget/qr_code.rs b/graphics/src/widget/qr_code.rs index b3a01dd775..a809d99fa0 100644 --- a/graphics/src/widget/qr_code.rs +++ b/graphics/src/widget/qr_code.rs @@ -1,10 +1,10 @@ //! Encode and display information in a QR code. use crate::canvas; -use crate::{Backend, Defaults, Primitive, Renderer, Vector}; +use crate::{Backend, Defaults, Renderer}; use iced_native::{ - layout, mouse, Color, Element, Hasher, Layout, Length, Point, Rectangle, - Size, Widget, + layout, Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, + Widget, }; use thiserror::Error; @@ -82,53 +82,54 @@ where &self, _renderer: &mut Renderer, _defaults: &Defaults, - layout: Layout<'_>, + _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> (Primitive, mouse::Interaction) { - let bounds = layout.bounds(); - let side_length = self.state.width + 2 * QUIET_ZONE; - - // Reuse cache if possible - let geometry = self.state.cache.draw(bounds.size(), |frame| { - // Scale units to cell size - frame.scale(f32::from(self.cell_size)); - - // Draw background - frame.fill_rectangle( - Point::ORIGIN, - Size::new(side_length as f32, side_length as f32), - self.light, - ); - - // Avoid drawing on the quiet zone - frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32)); - - // Draw contents - self.state - .contents - .iter() - .enumerate() - .filter(|(_, value)| **value == qrcode::Color::Dark) - .for_each(|(index, _)| { - let row = index / self.state.width; - let column = index % self.state.width; - - frame.fill_rectangle( - Point::new(column as f32, row as f32), - Size::UNIT, - self.dark, - ); - }); - }); - - ( - Primitive::Translate { - translation: Vector::new(bounds.x, bounds.y), - content: Box::new(geometry.into_primitive()), - }, - mouse::Interaction::default(), - ) + ) { + // let bounds = layout.bounds(); + // let side_length = self.state.width + 2 * QUIET_ZONE; + + // // Reuse cache if possible + // let geometry = self.state.cache.draw(bounds.size(), |frame| { + // // Scale units to cell size + // frame.scale(f32::from(self.cell_size)); + + // // Draw background + // frame.fill_rectangle( + // Point::ORIGIN, + // Size::new(side_length as f32, side_length as f32), + // self.light, + // ); + + // // Avoid drawing on the quiet zone + // frame.translate(Vector::new(QUIET_ZONE as f32, QUIET_ZONE as f32)); + + // // Draw contents + // self.state + // .contents + // .iter() + // .enumerate() + // .filter(|(_, value)| **value == qrcode::Color::Dark) + // .for_each(|(index, _)| { + // let row = index / self.state.width; + // let column = index % self.state.width; + + // frame.fill_rectangle( + // Point::new(column as f32, row as f32), + // Size::UNIT, + // self.dark, + // ); + // }); + // }); + + // ( + // Primitive::Translate { + // translation: Vector::new(bounds.x, bounds.y), + // content: Box::new(geometry.into_primitive()), + // }, + // mouse::Interaction::default(), + // ) + // TODO } } diff --git a/graphics/src/widget/radio.rs b/graphics/src/widget/radio.rs index fd3d81458e..cd83f2ff29 100644 --- a/graphics/src/widget/radio.rs +++ b/graphics/src/widget/radio.rs @@ -1,8 +1,6 @@ //! Create choices using radio buttons. -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::radio; -use iced_native::{Background, Color, Rectangle}; pub use iced_style::radio::{Style, StyleSheet}; @@ -21,58 +19,4 @@ where const DEFAULT_SIZE: u16 = 28; const DEFAULT_SPACING: u16 = 15; - - fn draw( - &mut self, - bounds: Rectangle, - is_selected: bool, - is_mouse_over: bool, - (label, _): Self::Output, - style_sheet: &Self::Style, - ) -> Self::Output { - let style = if is_mouse_over { - style_sheet.hovered() - } else { - style_sheet.active() - }; - - let size = bounds.width; - let dot_size = size / 2.0; - - let radio = Primitive::Quad { - bounds, - background: style.background, - border_radius: size / 2.0, - border_width: style.border_width, - border_color: style.border_color, - }; - - ( - Primitive::Group { - primitives: if is_selected { - let radio_circle = Primitive::Quad { - bounds: Rectangle { - x: bounds.x + dot_size / 2.0, - y: bounds.y + dot_size / 2.0, - width: bounds.width - dot_size, - height: bounds.height - dot_size, - }, - background: Background::Color(style.dot_color), - border_radius: dot_size / 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }; - - vec![radio, radio_circle, label] - } else { - vec![radio, label] - }, - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/row.rs b/graphics/src/widget/row.rs index 397d80bfc4..55960c0403 100644 --- a/graphics/src/widget/row.rs +++ b/graphics/src/widget/row.rs @@ -1,49 +1,5 @@ -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; -use iced_native::row; -use iced_native::{Element, Layout, Point, Rectangle}; +use crate::Renderer; /// A container that distributes its contents horizontally. pub type Row<'a, Message, Backend> = iced_native::Row<'a, Message, Renderer>; - -impl row::Renderer for Renderer -where - B: Backend, -{ - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output { - let mut mouse_interaction = mouse::Interaction::default(); - - ( - Primitive::Group { - primitives: content - .iter() - .zip(layout.children()) - .map(|(child, layout)| { - let (primitive, new_mouse_interaction) = child.draw( - self, - defaults, - layout, - cursor_position, - viewport, - ); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - primitive - }) - .collect(), - }, - mouse_interaction, - ) - } -} diff --git a/graphics/src/widget/rule.rs b/graphics/src/widget/rule.rs index 835ebed87e..62766f825f 100644 --- a/graphics/src/widget/rule.rs +++ b/graphics/src/widget/rule.rs @@ -1,9 +1,7 @@ //! Display a horizontal or vertical rule for dividing content. -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::rule; -use iced_native::{Background, Color, Rectangle}; pub use iced_style::rule::{FillMode, Style, StyleSheet}; @@ -17,57 +15,4 @@ where B: Backend, { type Style = Box; - - fn draw( - &mut self, - bounds: Rectangle, - style_sheet: &Self::Style, - is_horizontal: bool, - ) -> Self::Output { - let style = style_sheet.style(); - - let line = if is_horizontal { - let line_y = (bounds.y + (bounds.height / 2.0) - - (style.width as f32 / 2.0)) - .round(); - - let (offset, line_width) = style.fill_mode.fill(bounds.width); - let line_x = bounds.x + offset; - - Primitive::Quad { - bounds: Rectangle { - x: line_x, - y: line_y, - width: line_width, - height: style.width as f32, - }, - background: Background::Color(style.color), - border_radius: style.radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - } - } else { - let line_x = (bounds.x + (bounds.width / 2.0) - - (style.width as f32 / 2.0)) - .round(); - - let (offset, line_height) = style.fill_mode.fill(bounds.height); - let line_y = bounds.y + offset; - - Primitive::Quad { - bounds: Rectangle { - x: line_x, - y: line_y, - width: style.width as f32, - height: line_height, - }, - background: Background::Color(style.color), - border_radius: style.radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - } - }; - - (line, mouse::Interaction::default()) - } } diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index 2220e4b84e..f1fe0d2de2 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -1,8 +1,7 @@ //! Navigate an endless amount of content with a scrollbar. -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::scrollable; -use iced_native::{Background, Color, Rectangle, Vector}; +use iced_native::Rectangle; pub use iced_native::scrollable::State; pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; @@ -73,86 +72,4 @@ where None } } - - fn draw( - &mut self, - state: &scrollable::State, - bounds: Rectangle, - _content_bounds: Rectangle, - is_mouse_over: bool, - is_mouse_over_scrollbar: bool, - scrollbar: Option, - offset: u32, - style_sheet: &Self::Style, - (content, mouse_interaction): Self::Output, - ) -> Self::Output { - ( - if let Some(scrollbar) = scrollbar { - let clip = Primitive::Clip { - bounds, - offset: Vector::new(0, offset), - content: Box::new(content), - }; - - let style = if state.is_scroller_grabbed() { - style_sheet.dragging() - } else if is_mouse_over_scrollbar { - style_sheet.hovered() - } else { - style_sheet.active() - }; - - let is_scrollbar_visible = - style.background.is_some() || style.border_width > 0.0; - - let scroller = if is_mouse_over - || state.is_scroller_grabbed() - || is_scrollbar_visible - { - Primitive::Quad { - bounds: scrollbar.scroller.bounds, - background: Background::Color(style.scroller.color), - border_radius: style.scroller.border_radius, - border_width: style.scroller.border_width, - border_color: style.scroller.border_color, - } - } else { - Primitive::None - }; - - let scrollbar = if is_scrollbar_visible { - Primitive::Quad { - bounds: scrollbar.bounds, - background: style - .background - .unwrap_or(Background::Color(Color::TRANSPARENT)), - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - } - } else { - Primitive::None - }; - - let scroll = Primitive::Clip { - bounds, - offset: Vector::new(0, 0), - content: Box::new(Primitive::Group { - primitives: vec![scrollbar, scroller], - }), - }; - - Primitive::Group { - primitives: vec![clip, scroll], - } - } else { - content - }, - if is_mouse_over_scrollbar || state.is_scroller_grabbed() { - mouse::Interaction::Idle - } else { - mouse_interaction - }, - ) - } } diff --git a/graphics/src/widget/slider.rs b/graphics/src/widget/slider.rs index aeceec3f55..5125d66cf8 100644 --- a/graphics/src/widget/slider.rs +++ b/graphics/src/widget/slider.rs @@ -1,10 +1,8 @@ //! Display an interactive selector of a single value from a range of values. //! //! A [`Slider`] has some local [`State`]. -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; +use crate::{Backend, Renderer}; use iced_native::slider; -use iced_native::{Background, Color, Point, Rectangle}; pub use iced_native::slider::State; pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; @@ -23,101 +21,4 @@ where type Style = Box; const DEFAULT_HEIGHT: u16 = 22; - - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - range: std::ops::RangeInclusive, - value: f32, - is_dragging: bool, - style_sheet: &Self::Style, - ) -> Self::Output { - let is_mouse_over = bounds.contains(cursor_position); - - let style = if is_dragging { - style_sheet.dragging() - } else if is_mouse_over { - style_sheet.hovered() - } else { - style_sheet.active() - }; - - let rail_y = bounds.y + (bounds.height / 2.0).round(); - - let (rail_top, rail_bottom) = ( - Primitive::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y, - width: bounds.width, - height: 2.0, - }, - background: Background::Color(style.rail_colors.0), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y + 2.0, - width: bounds.width, - height: 2.0, - }, - background: Background::Color(style.rail_colors.1), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - ); - - let (handle_width, handle_height, handle_border_radius) = match style - .handle - .shape - { - HandleShape::Circle { radius } => { - (radius * 2.0, radius * 2.0, radius) - } - HandleShape::Rectangle { - width, - border_radius, - } => (f32::from(width), f32::from(bounds.height), border_radius), - }; - - let (range_start, range_end) = range.into_inner(); - - let handle_offset = if range_start >= range_end { - 0.0 - } else { - (bounds.width - handle_width) * (value - range_start) - / (range_end - range_start) - }; - - let handle = Primitive::Quad { - bounds: Rectangle { - x: bounds.x + handle_offset.round(), - y: rail_y - handle_height / 2.0, - width: handle_width, - height: handle_height, - }, - background: Background::Color(style.handle.color), - border_radius: handle_border_radius, - border_width: style.handle.border_width, - border_color: style.handle.border_color, - }; - - ( - Primitive::Group { - primitives: vec![rail_top, rail_bottom, handle], - }, - if is_dragging { - mouse::Interaction::Grabbing - } else if is_mouse_over { - mouse::Interaction::Grab - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/space.rs b/graphics/src/widget/space.rs index 1f31eabedf..a4d60d4be5 100644 --- a/graphics/src/widget/space.rs +++ b/graphics/src/widget/space.rs @@ -1,15 +1 @@ -use crate::{Backend, Primitive, Renderer}; -use iced_native::mouse; -use iced_native::space; -use iced_native::Rectangle; - pub use iced_native::Space; - -impl space::Renderer for Renderer -where - B: Backend, -{ - fn draw(&mut self, _bounds: Rectangle) -> Self::Output { - (Primitive::None, mouse::Interaction::default()) - } -} diff --git a/graphics/src/widget/svg.rs b/graphics/src/widget/svg.rs index 8b5ed66a54..b74a0abb35 100644 --- a/graphics/src/widget/svg.rs +++ b/graphics/src/widget/svg.rs @@ -1,7 +1,7 @@ //! Display vector graphics in your application. use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; -use iced_native::{mouse, svg, Layout}; +use crate::Renderer; +use iced_native::svg; pub use iced_native::svg::{Handle, Svg}; @@ -12,18 +12,4 @@ where fn dimensions(&self, handle: &svg::Handle) -> (u32, u32) { self.backend().viewport_dimensions(handle) } - - fn draw( - &mut self, - handle: svg::Handle, - layout: Layout<'_>, - ) -> Self::Output { - ( - Primitive::Svg { - handle, - bounds: layout.bounds(), - }, - mouse::Interaction::default(), - ) - } } diff --git a/graphics/src/widget/text.rs b/graphics/src/widget/text.rs index d6d446d320..2ccd18a10e 100644 --- a/graphics/src/widget/text.rs +++ b/graphics/src/widget/text.rs @@ -1,10 +1,8 @@ //! Write some text for your users to read. use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; -use iced_native::alignment; -use iced_native::mouse; +use crate::Renderer; use iced_native::text; -use iced_native::{Color, Font, Point, Rectangle, Size}; +use iced_native::{Font, Point, Size}; /// A paragraph of text. /// @@ -52,41 +50,4 @@ where nearest_only, ) } - - fn draw( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - content: &str, - size: u16, - font: Font, - color: Option, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - ) -> Self::Output { - let x = match horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.center_x(), - alignment::Horizontal::Right => bounds.x + bounds.width, - }; - - let y = match vertical_alignment { - alignment::Vertical::Top => bounds.y, - alignment::Vertical::Center => bounds.center_y(), - alignment::Vertical::Bottom => bounds.y + bounds.height, - }; - - ( - Primitive::Text { - content: content.to_string(), - size: f32::from(size), - bounds: Rectangle { x, y, ..bounds }, - color: color.unwrap_or(defaults.text.color), - font, - horizontal_alignment, - vertical_alignment, - }, - mouse::Interaction::default(), - ) - } } diff --git a/graphics/src/widget/text_input.rs b/graphics/src/widget/text_input.rs index 1516b00769..e9dbf056a7 100644 --- a/graphics/src/widget/text_input.rs +++ b/graphics/src/widget/text_input.rs @@ -1,14 +1,9 @@ //! Display fields that can be filled with text. //! //! A [`TextInput`] has some local [`State`]. -use crate::alignment; use crate::backend::{self, Backend}; -use crate::{ - Background, Color, Font, Point, Primitive, Rectangle, Renderer, Size, - Vector, -}; +use crate::{Font, Rectangle, Renderer, Size}; -use iced_native::mouse; use iced_native::text_input::{self, cursor}; use std::f32; @@ -66,181 +61,6 @@ where 0.0 } } - - fn draw( - &mut self, - bounds: Rectangle, - text_bounds: Rectangle, - cursor_position: Point, - font: Font, - size: u16, - placeholder: &str, - value: &text_input::Value, - state: &text_input::State, - style_sheet: &Self::Style, - ) -> Self::Output { - let is_mouse_over = bounds.contains(cursor_position); - - let style = if state.is_focused() { - style_sheet.focused() - } else if is_mouse_over { - style_sheet.hovered() - } else { - style_sheet.active() - }; - - let input = Primitive::Quad { - bounds, - background: style.background, - border_radius: style.border_radius, - border_width: style.border_width, - border_color: style.border_color, - }; - - let text = value.to_string(); - - let text_value = Primitive::Text { - content: if text.is_empty() { - placeholder.to_string() - } else { - text.clone() - }, - color: if text.is_empty() { - style_sheet.placeholder_color() - } else { - style_sheet.value_color() - }, - font, - bounds: Rectangle { - y: text_bounds.center_y(), - width: f32::INFINITY, - ..text_bounds - }, - size: f32::from(size), - horizontal_alignment: alignment::Horizontal::Left, - vertical_alignment: alignment::Vertical::Center, - }; - - let (contents_primitive, offset) = if state.is_focused() { - let cursor = state.cursor(); - - let (cursor_primitive, offset) = match cursor.state(value) { - cursor::State::Index(position) => { - let (text_value_width, offset) = - measure_cursor_and_scroll_offset( - self, - text_bounds, - value, - size, - position, - font, - ); - - ( - Primitive::Quad { - bounds: Rectangle { - x: text_bounds.x + text_value_width, - y: text_bounds.y, - width: 1.0, - height: text_bounds.height, - }, - background: Background::Color( - style_sheet.value_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - offset, - ) - } - cursor::State::Selection { start, end } => { - let left = start.min(end); - let right = end.max(start); - - let (left_position, left_offset) = - measure_cursor_and_scroll_offset( - self, - text_bounds, - value, - size, - left, - font, - ); - - let (right_position, right_offset) = - measure_cursor_and_scroll_offset( - self, - text_bounds, - value, - size, - right, - font, - ); - - let width = right_position - left_position; - - ( - Primitive::Quad { - bounds: Rectangle { - x: text_bounds.x + left_position, - y: text_bounds.y, - width, - height: text_bounds.height, - }, - background: Background::Color( - style_sheet.selection_color(), - ), - border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - if end == right { - right_offset - } else { - left_offset - }, - ) - } - }; - - ( - Primitive::Group { - primitives: vec![cursor_primitive, text_value], - }, - Vector::new(offset as u32, 0), - ) - } else { - (text_value, Vector::new(0, 0)) - }; - - let text_width = self.measure_value( - if text.is_empty() { placeholder } else { &text }, - size, - font, - ); - - let contents = if text_width > text_bounds.width { - Primitive::Clip { - bounds: text_bounds, - offset, - content: Box::new(contents_primitive), - } - } else { - contents_primitive - }; - - ( - Primitive::Group { - primitives: vec![input, contents], - }, - if is_mouse_over { - mouse::Interaction::Text - } else { - mouse::Interaction::default() - }, - ) - } } fn measure_cursor_and_scroll_offset( diff --git a/graphics/src/widget/toggler.rs b/graphics/src/widget/toggler.rs index 852d18ee8e..cd072f7c58 100644 --- a/graphics/src/widget/toggler.rs +++ b/graphics/src/widget/toggler.rs @@ -1,19 +1,10 @@ //! Show toggle controls using togglers. use crate::backend::{self, Backend}; -use crate::{Primitive, Renderer}; -use iced_native::mouse; +use crate::Renderer; use iced_native::toggler; -use iced_native::Rectangle; pub use iced_style::toggler::{Style, StyleSheet}; -/// Makes sure that the border radius of the toggler looks good at every size. -const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0; - -/// The space ratio between the background Quad and the Toggler bounds, and -/// between the background Quad and foreground Quad. -const SPACE_RATIO: f32 = 0.05; - /// A toggler that can be toggled. /// /// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`. @@ -27,73 +18,4 @@ where type Style = Box; const DEFAULT_SIZE: u16 = 20; - - fn draw( - &mut self, - bounds: Rectangle, - is_active: bool, - is_mouse_over: bool, - label: Option, - style_sheet: &Self::Style, - ) -> Self::Output { - let style = if is_mouse_over { - style_sheet.hovered(is_active) - } else { - style_sheet.active(is_active) - }; - - let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO; - let space = SPACE_RATIO * bounds.height as f32; - - let toggler_background_bounds = Rectangle { - x: bounds.x + space, - y: bounds.y + space, - width: bounds.width - (2.0 * space), - height: bounds.height - (2.0 * space), - }; - - let toggler_background = Primitive::Quad { - bounds: toggler_background_bounds, - background: style.background.into(), - border_radius, - border_width: 1.0, - border_color: style.background_border.unwrap_or(style.background), - }; - - let toggler_foreground_bounds = Rectangle { - x: bounds.x - + if is_active { - bounds.width - 2.0 * space - (bounds.height - (4.0 * space)) - } else { - 2.0 * space - }, - y: bounds.y + (2.0 * space), - width: bounds.height - (4.0 * space), - height: bounds.height - (4.0 * space), - }; - - let toggler_foreground = Primitive::Quad { - bounds: toggler_foreground_bounds, - background: style.foreground.into(), - border_radius, - border_width: 1.0, - border_color: style.foreground_border.unwrap_or(style.foreground), - }; - - ( - Primitive::Group { - primitives: match label { - Some((l, _)) => { - vec![l, toggler_background, toggler_foreground] - } - None => vec![toggler_background, toggler_foreground], - }, - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } } diff --git a/graphics/src/widget/tooltip.rs b/graphics/src/widget/tooltip.rs index 493a638980..d55d61b224 100644 --- a/graphics/src/widget/tooltip.rs +++ b/graphics/src/widget/tooltip.rs @@ -1,11 +1,6 @@ //! Decorate content and apply alignment. use crate::backend::{self, Backend}; -use crate::defaults::{self, Defaults}; -use crate::{Primitive, Renderer, Vector}; - -use iced_native::container; -use iced_native::layout::{self, Layout}; -use iced_native::{Element, Padding, Point, Rectangle, Size, Text}; +use crate::Renderer; /// An element decorating some content. /// @@ -21,148 +16,4 @@ where B: Backend + backend::Text, { const DEFAULT_PADDING: u16 = 5; - - fn draw( - &mut self, - defaults: &Defaults, - cursor_position: Point, - content_layout: Layout<'_>, - viewport: &Rectangle, - content: &Element<'_, Message, Self>, - tooltip: &Text, - position: Position, - style_sheet: &::Style, - gap: u16, - padding: u16, - ) -> Self::Output { - let (content, mouse_interaction) = content.draw( - self, - &defaults, - content_layout, - cursor_position, - viewport, - ); - - let bounds = content_layout.bounds(); - - if bounds.contains(cursor_position) { - use iced_native::Widget; - - let gap = f32::from(gap); - let style = style_sheet.style(); - - let defaults = Defaults { - text: defaults::Text { - color: style.text_color.unwrap_or(defaults.text.color), - }, - }; - - let text_layout = Widget::<(), Self>::layout( - tooltip, - self, - &layout::Limits::new(Size::ZERO, viewport.size()) - .pad(Padding::new(padding)), - ); - - let padding = f32::from(padding); - let text_bounds = text_layout.bounds(); - let x_center = bounds.x + (bounds.width - text_bounds.width) / 2.0; - let y_center = - bounds.y + (bounds.height - text_bounds.height) / 2.0; - - let mut tooltip_bounds = { - let offset = match position { - Position::Top => Vector::new( - x_center, - bounds.y - text_bounds.height - gap - padding, - ), - Position::Bottom => Vector::new( - x_center, - bounds.y + bounds.height + gap + padding, - ), - Position::Left => Vector::new( - bounds.x - text_bounds.width - gap - padding, - y_center, - ), - Position::Right => Vector::new( - bounds.x + bounds.width + gap + padding, - y_center, - ), - Position::FollowCursor => Vector::new( - cursor_position.x, - cursor_position.y - text_bounds.height, - ), - }; - - Rectangle { - x: offset.x - padding, - y: offset.y - padding, - width: text_bounds.width + padding * 2.0, - height: text_bounds.height + padding * 2.0, - } - }; - - if tooltip_bounds.x < viewport.x { - tooltip_bounds.x = viewport.x; - } else if viewport.x + viewport.width - < tooltip_bounds.x + tooltip_bounds.width - { - tooltip_bounds.x = - viewport.x + viewport.width - tooltip_bounds.width; - } - - if tooltip_bounds.y < viewport.y { - tooltip_bounds.y = viewport.y; - } else if viewport.y + viewport.height - < tooltip_bounds.y + tooltip_bounds.height - { - tooltip_bounds.y = - viewport.y + viewport.height - tooltip_bounds.height; - } - - let (tooltip, _) = Widget::<(), Self>::draw( - tooltip, - self, - &defaults, - Layout::with_offset( - Vector::new( - tooltip_bounds.x + padding, - tooltip_bounds.y + padding, - ), - &text_layout, - ), - cursor_position, - viewport, - ); - - ( - Primitive::Group { - primitives: vec![ - content, - Primitive::Clip { - bounds: *viewport, - offset: Vector::new(0, 0), - content: Box::new( - if let Some(background) = - crate::container::background( - tooltip_bounds, - &style, - ) - { - Primitive::Group { - primitives: vec![background, tooltip], - } - } else { - tooltip - }, - ), - }, - ], - }, - mouse_interaction, - ) - } else { - (content, mouse_interaction) - } - } } diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs index 37edef1d5e..9ea040cd1b 100644 --- a/graphics/src/window/compositor.rs +++ b/graphics/src/window/compositor.rs @@ -1,7 +1,5 @@ use crate::{Color, Error, Viewport}; -use iced_native::mouse; - use raw_window_handle::HasRawWindowHandle; use thiserror::Error; @@ -30,9 +28,8 @@ pub trait Compositor: Sized { window: &W, ) -> Self::Surface; - /// Crates a new [`SwapChain`] for the given [`Surface`]. + /// Configures a new [`Surface`] with the given dimensions. /// - /// [`SwapChain`]: Self::SwapChain /// [`Surface`]: Self::Surface fn configure_surface( &mut self, @@ -41,18 +38,17 @@ pub trait Compositor: Sized { height: u32, ); - /// Draws the output primitives to the next frame of the given [`SwapChain`]. + /// Presents the [`Renderer`] primitives to the next frame of the given [`Surface`]. /// /// [`SwapChain`]: Self::SwapChain - fn draw>( + fn present>( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - output: &::Output, overlay: &[T], - ) -> Result; + ) -> Result<(), SurfaceError>; } /// Result of an unsuccessful call to [`Compositor::draw`]. @@ -63,13 +59,13 @@ pub enum SurfaceError { "A timeout was encountered while trying to acquire the next frame" )] Timeout, - /// The underlying surface has changed, and therefore the swap chain must be updated. + /// The underlying surface has changed, and therefore the surface must be updated. #[error( - "The underlying surface has changed, and therefore the swap chain must be updated." + "The underlying surface has changed, and therefore the surface must be updated." )] Outdated, /// The swap chain has been lost and needs to be recreated. - #[error("The swap chain has been lost and needs to be recreated")] + #[error("The surface has been lost and needs to be recreated")] Lost, /// There is no more memory left to allocate a new frame. #[error("There is no more memory left to allocate a new frame")] diff --git a/graphics/src/window/gl_compositor.rs b/graphics/src/window/gl_compositor.rs index 34d70be318..b1b995f1f5 100644 --- a/graphics/src/window/gl_compositor.rs +++ b/graphics/src/window/gl_compositor.rs @@ -1,5 +1,4 @@ use crate::{Color, Error, Size, Viewport}; -use iced_native::mouse; use core::ffi::c_void; @@ -49,15 +48,15 @@ pub trait GLCompositor: Sized { /// Resizes the viewport of the [`GLCompositor`]. fn resize_viewport(&mut self, physical_size: Size); - /// Draws the provided output with the given [`Renderer`]. + /// Presents the primitives of the [`Renderer`] to the next frame of the + /// [`GLCompositor`]. /// /// [`Renderer`]: crate::Renderer - fn draw>( + fn present>( &mut self, renderer: &mut Self::Renderer, viewport: &Viewport, background_color: Color, - output: &::Output, overlay: &[T], - ) -> mouse::Interaction; + ); } diff --git a/native/src/element.rs b/native/src/element.rs index 5c84a3887f..35e1b4f4b8 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -189,7 +189,7 @@ where ) -> Element<'a, Message, Renderer> where Message: 'static, - Renderer: 'a + layout::Debugger, + Renderer: 'a, { Element { widget: Box::new(Explain::new(self, color.into())), @@ -245,7 +245,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { + ) { self.widget .draw(renderer, defaults, layout, cursor_position, viewport) } @@ -340,7 +340,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { + ) { self.widget .draw(renderer, defaults, layout, cursor_position, viewport) } @@ -378,7 +378,7 @@ where impl<'a, Message, Renderer> Widget for Explain<'a, Message, Renderer> where - Renderer: crate::Renderer + layout::Debugger, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.element.widget.width() @@ -417,20 +417,12 @@ where fn draw( &self, - renderer: &mut Renderer, - defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Renderer::Output { - renderer.explain( - defaults, - self.element.widget.as_ref(), - layout, - cursor_position, - viewport, - self.color, - ) + _renderer: &mut Renderer, + _defaults: &Renderer::Defaults, + _layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { } fn hash_layout(&self, state: &mut Hasher) { diff --git a/native/src/layout.rs b/native/src/layout.rs index b4b4a02166..04954fb9b2 100644 --- a/native/src/layout.rs +++ b/native/src/layout.rs @@ -1,11 +1,9 @@ //! Position your widgets properly. -mod debugger; mod limits; mod node; pub mod flex; -pub use debugger::Debugger; pub use limits::Limits; pub use node::Node; diff --git a/native/src/layout/debugger.rs b/native/src/layout/debugger.rs deleted file mode 100644 index 0759613f4a..0000000000 --- a/native/src/layout/debugger.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{Color, Layout, Point, Rectangle, Renderer, Widget}; - -/// A renderer able to graphically explain a [`Layout`]. -pub trait Debugger: Renderer { - /// Explains the [`Layout`] of an [`Element`] for debugging purposes. - /// - /// This will be called when [`Element::explain`] has been used. It should - /// _explain_ the given [`Layout`] graphically. - /// - /// A common approach consists in recursively rendering the bounds of the - /// [`Layout`] and its children. - /// - /// [`Element`]: crate::Element - /// [`Element::explain`]: crate::Element::explain - fn explain( - &mut self, - defaults: &Self::Defaults, - widget: &dyn Widget, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - color: Color, - ) -> Self::Output; -} diff --git a/native/src/lib.rs b/native/src/lib.rs index 170a588bec..55265fbfbd 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -28,7 +28,7 @@ //! [`druid`]: https://github.com/xi-editor/druid //! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle //! [renderer]: crate::renderer -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(unsafe_code)] diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 84145e7fad..7054ee693c 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -35,7 +35,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, - ) -> Renderer::Output; + ); /// Computes the _layout_ hash of the [`Overlay`]. /// diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index e48190376b..081b62cec4 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -74,7 +74,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, - ) -> Renderer::Output { + ) { self.overlay .draw(renderer, defaults, layout, cursor_position) } @@ -145,7 +145,7 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, - ) -> Renderer::Output { + ) { self.content .draw(renderer, defaults, layout, cursor_position) } diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index f62dcb46e7..f90a9f7bf0 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -239,21 +239,22 @@ where defaults: &Renderer::Defaults, layout: Layout<'_>, cursor_position: Point, - ) -> Renderer::Output { - let primitives = self.container.draw( - renderer, - defaults, - layout, - cursor_position, - &layout.bounds(), - ); - - renderer.decorate( - layout.bounds(), - cursor_position, - &self.style, - primitives, - ) + ) { + // TODO + // let primitives = self.container.draw( + // renderer, + // defaults, + // layout, + // cursor_position, + // &layout.bounds(), + // ); + + // renderer.decorate( + // layout.bounds(), + // cursor_position, + // &self.style, + // primitives, + // ) } } @@ -378,24 +379,13 @@ where fn draw( &self, - renderer: &mut Renderer, + _renderer: &mut Renderer, _defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Renderer::Output { - self::Renderer::draw( - renderer, - layout.bounds(), - cursor_position, - viewport, - self.options, - *self.hovered_option, - self.padding, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - &self.style, - ) + _layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + // TODO } } @@ -410,31 +400,6 @@ pub trait Renderer: { /// The [`Menu`] style supported by this renderer. type Style: Default + Clone; - - /// Decorates a the list of options of a [`Menu`]. - /// - /// This method can be used to draw a background for the [`Menu`]. - fn decorate( - &mut self, - bounds: Rectangle, - cursor_position: Point, - style: &::Style, - primitive: Self::Output, - ) -> Self::Output; - - /// Draws the list of options of a [`Menu`]. - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - viewport: &Rectangle, - options: &[T], - hovered_option: Option, - padding: Padding, - text_size: u16, - font: Self::Font, - style: &::Style, - ) -> Self::Output; } impl<'a, T, Message, Renderer> Into> diff --git a/native/src/program/state.rs b/native/src/program/state.rs index 3f5f606978..55308a927b 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,5 +1,5 @@ use crate::{ - Cache, Clipboard, Command, Debug, Event, Point, Program, Renderer, Size, + Cache, Clipboard, Command, Debug, Event, Point, Program, Size, UserInterface, }; @@ -12,7 +12,6 @@ where { program: P, cache: Option, - primitive: ::Output, queued_events: Vec, queued_messages: Vec, } @@ -38,16 +37,11 @@ where debug, ); - debug.draw_started(); - let primitive = user_interface.draw(renderer, cursor_position); - debug.draw_finished(); - let cache = Some(user_interface.into_cache()); State { program, cache, - primitive, queued_events: Vec::new(), queued_messages: Vec::new(), } @@ -58,11 +52,6 @@ where &self.program } - /// Returns a reference to the current rendering primitive of the [`State`]. - pub fn primitive(&self) -> &::Output { - &self.primitive - } - /// Queues an event in the [`State`] for processing during an [`update`]. /// /// [`update`]: Self::update @@ -120,7 +109,7 @@ where if messages.is_empty() { debug.draw_started(); - self.primitive = user_interface.draw(renderer, cursor_position); + user_interface.draw(renderer, cursor_position); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); @@ -151,7 +140,7 @@ where ); debug.draw_started(); - self.primitive = user_interface.draw(renderer, cursor_position); + user_interface.draw(renderer, cursor_position); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 39a6cff19e..3784ff24d0 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -30,12 +30,6 @@ use crate::{layout, Element, Rectangle}; /// A component that can take the state of a user interface and produce an /// output for its users. pub trait Renderer: Sized { - /// The type of output of the [`Renderer`]. - /// - /// If you are implementing a graphical renderer, your output will most - /// likely be a tree of visual primitives. - type Output; - /// The default styling attributes of the [`Renderer`]. /// /// This type can be leveraged to implement style inheritance. @@ -53,12 +47,5 @@ pub trait Renderer: Sized { element.layout(self, limits) } - /// Overlays the `overlay` output with the given bounds on top of the `base` - /// output. - fn overlay( - &mut self, - base: Self::Output, - overlay: Self::Output, - overlay_bounds: Rectangle, - ) -> Self::Output; + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index b5921582bb..1ffca5c97c 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,20 +1,15 @@ -use crate::alignment; use crate::button; use crate::checkbox; -use crate::column; use crate::container; use crate::pane_grid; use crate::progress_bar; use crate::radio; -use crate::row; use crate::scrollable; use crate::slider; use crate::text; use crate::text_input; use crate::toggler; -use crate::{ - Color, Element, Font, Layout, Padding, Point, Rectangle, Renderer, Size, -}; +use crate::{Font, Padding, Point, Rectangle, Renderer, Size}; /// A renderer that does nothing. /// @@ -30,35 +25,9 @@ impl Null { } impl Renderer for Null { - type Output = (); type Defaults = (); - fn overlay(&mut self, _base: (), _overlay: (), _overlay_bounds: Rectangle) { - } -} - -impl column::Renderer for Null { - fn draw( - &mut self, - _defaults: &Self::Defaults, - _content: &[Element<'_, Message, Self>], - _layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - } -} - -impl row::Renderer for Null { - fn draw( - &mut self, - _defaults: &Self::Defaults, - _content: &[Element<'_, Message, Self>], - _layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - } + fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} } impl text::Renderer for Null { @@ -89,19 +58,6 @@ impl text::Renderer for Null { ) -> Option { None } - - fn draw( - &mut self, - _defaults: &Self::Defaults, - _bounds: Rectangle, - _content: &str, - _size: u16, - _font: Font, - _color: Option, - _horizontal_alignment: alignment::Horizontal, - _vertical_alignment: alignment::Vertical, - ) { - } } impl scrollable::Renderer for Null { @@ -118,20 +74,6 @@ impl scrollable::Renderer for Null { ) -> Option { None } - - fn draw( - &mut self, - _scrollable: &scrollable::State, - _bounds: Rectangle, - _content_bounds: Rectangle, - _is_mouse_over: bool, - _is_mouse_over_scrollbar: bool, - _scrollbar: Option, - _offset: u32, - _style: &Self::Style, - _content: Self::Output, - ) { - } } impl text_input::Renderer for Null { @@ -151,39 +93,12 @@ impl text_input::Renderer for Null { ) -> f32 { 0.0 } - - fn draw( - &mut self, - _bounds: Rectangle, - _text_bounds: Rectangle, - _cursor_position: Point, - _font: Font, - _size: u16, - _placeholder: &str, - _value: &text_input::Value, - _state: &text_input::State, - _style: &Self::Style, - ) -> Self::Output { - } } impl button::Renderer for Null { const DEFAULT_PADDING: Padding = Padding::ZERO; type Style = (); - - fn draw( - &mut self, - _defaults: &Self::Defaults, - _bounds: Rectangle, - _cursor_position: Point, - _is_disabled: bool, - _is_pressed: bool, - _style: &Self::Style, - _content: &Element<'_, Message, Self>, - _content_layout: Layout<'_>, - ) -> Self::Output { - } } impl radio::Renderer for Null { @@ -191,16 +106,6 @@ impl radio::Renderer for Null { const DEFAULT_SIZE: u16 = 20; const DEFAULT_SPACING: u16 = 15; - - fn draw( - &mut self, - _bounds: Rectangle, - _is_selected: bool, - _is_mouse_over: bool, - _label: Self::Output, - _style: &Self::Style, - ) { - } } impl checkbox::Renderer for Null { @@ -208,122 +113,30 @@ impl checkbox::Renderer for Null { const DEFAULT_SIZE: u16 = 20; const DEFAULT_SPACING: u16 = 15; - - fn draw( - &mut self, - _bounds: Rectangle, - _is_checked: bool, - _is_mouse_over: bool, - _label: Self::Output, - _style: &Self::Style, - ) { - } } impl slider::Renderer for Null { type Style = (); const DEFAULT_HEIGHT: u16 = 30; - - fn draw( - &mut self, - _bounds: Rectangle, - _cursor_position: Point, - _range: std::ops::RangeInclusive, - _value: f32, - _is_dragging: bool, - _style_sheet: &Self::Style, - ) { - } } impl progress_bar::Renderer for Null { type Style = (); const DEFAULT_HEIGHT: u16 = 30; - - fn draw( - &self, - _bounds: Rectangle, - _range: std::ops::RangeInclusive, - _value: f32, - _style: &Self::Style, - ) { - } } impl container::Renderer for Null { type Style = (); - - fn draw( - &mut self, - _defaults: &Self::Defaults, - _bounds: Rectangle, - _cursor_position: Point, - _viewport: &Rectangle, - _style: &Self::Style, - _content: &Element<'_, Message, Self>, - _content_layout: Layout<'_>, - ) { - } } impl pane_grid::Renderer for Null { type Style = (); - - fn draw( - &mut self, - _defaults: &Self::Defaults, - _content: &[(pane_grid::Pane, pane_grid::Content<'_, Message, Self>)], - _dragging: Option<(pane_grid::Pane, Point)>, - _resizing: Option<(pane_grid::Axis, Rectangle, bool)>, - _layout: Layout<'_>, - _style: &::Style, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - } - - fn draw_pane( - &mut self, - _defaults: &Self::Defaults, - _bounds: Rectangle, - _style: &::Style, - _title_bar: Option<( - &pane_grid::TitleBar<'_, Message, Self>, - Layout<'_>, - )>, - _body: (&Element<'_, Message, Self>, Layout<'_>), - _cursor_position: Point, - _viewport: &Rectangle, - ) { - } - - fn draw_title_bar( - &mut self, - _defaults: &Self::Defaults, - _bounds: Rectangle, - _style: &::Style, - _content: (&Element<'_, Message, Self>, Layout<'_>), - _controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - } } impl toggler::Renderer for Null { type Style = (); const DEFAULT_SIZE: u16 = 20; - - fn draw( - &mut self, - _bounds: Rectangle, - _is_checked: bool, - _is_mouse_over: bool, - _label: Option, - _style: &Self::Style, - ) { - } } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 8e0d7d1cbd..187c469aae 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -329,11 +329,7 @@ where /// // Flush rendering operations... /// } /// ``` - pub fn draw( - &mut self, - renderer: &mut Renderer, - cursor_position: Point, - ) -> Renderer::Output { + pub fn draw(&mut self, renderer: &mut Renderer, cursor_position: Point) { let viewport = Rectangle::with_size(self.bounds); let overlay = if let Some(mut overlay) = @@ -348,40 +344,36 @@ where let overlay_bounds = layer.layout.bounds(); - let overlay_primitives = overlay.draw( - renderer, - &Renderer::Defaults::default(), - Layout::new(&layer.layout), - cursor_position, - ); + renderer.with_layer(overlay_bounds, |renderer| { + overlay.draw( + renderer, + &Renderer::Defaults::default(), + Layout::new(&layer.layout), + cursor_position, + ); + }); self.overlay = Some(layer); - Some((overlay_primitives, overlay_bounds)) + Some(overlay_bounds) } else { None }; - if let Some((overlay_primitives, overlay_bounds)) = overlay { + if let Some(overlay_bounds) = overlay { let base_cursor = if overlay_bounds.contains(cursor_position) { Point::new(-1.0, -1.0) } else { cursor_position }; - let base_primitives = self.root.widget.draw( + self.root.widget.draw( renderer, &Renderer::Defaults::default(), Layout::new(&self.base.layout), base_cursor, &viewport, ); - - renderer.overlay( - base_primitives, - overlay_primitives, - overlay_bounds, - ) } else { self.root.widget.draw( renderer, @@ -389,7 +381,7 @@ where Layout::new(&self.base.layout), cursor_position, &viewport, - ) + ); } } diff --git a/native/src/widget.rs b/native/src/widget.rs index 43c1b023d7..01c5bed390 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -135,7 +135,7 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output; + ); /// Computes the _layout_ hash of the [`Widget`]. /// diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index c469a0e55a..1c7f80ef27 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -248,17 +248,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - defaults, - layout.bounds(), - cursor_position, - self.on_press.is_none(), - self.state.is_pressed, - &self.style, - &self.content, - layout.children().next().unwrap(), - ) + ) { } fn hash_layout(&self, state: &mut Hasher) { @@ -289,19 +279,6 @@ pub trait Renderer: crate::Renderer + Sized { /// The style supported by this renderer. type Style: Default; - - /// Draws a [`Button`]. - fn draw( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - cursor_position: Point, - is_disabled: bool, - is_pressed: bool, - style: &Self::Style, - content: &Element<'_, Message, Self>, - content_layout: Layout<'_>, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 8bdb6b78e6..579e4ee39d 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -5,7 +5,6 @@ use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; use crate::mouse; -use crate::row; use crate::text; use crate::touch; use crate::{ @@ -121,7 +120,7 @@ impl impl Widget for Checkbox where - Renderer: self::Renderer + text::Renderer + row::Renderer, + Renderer: self::Renderer + text::Renderer, { fn width(&self) -> Length { self.width @@ -187,36 +186,29 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - let bounds = layout.bounds(); - let mut children = layout.children(); - - let checkbox_layout = children.next().unwrap(); - let label_layout = children.next().unwrap(); - let checkbox_bounds = checkbox_layout.bounds(); - - let label = text::Renderer::draw( - renderer, - defaults, - label_layout.bounds(), - &self.label, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - self.text_color, - alignment::Horizontal::Left, - alignment::Vertical::Center, - ); - - let is_mouse_over = bounds.contains(cursor_position); - - self::Renderer::draw( - renderer, - checkbox_bounds, - self.is_checked, - is_mouse_over, - label, - &self.style, - ) + ) { + // let bounds = layout.bounds(); + // let mut children = layout.children(); + + // let checkbox_layout = children.next().unwrap(); + // let label_layout = children.next().unwrap(); + // let checkbox_bounds = checkbox_layout.bounds(); + + // let label = text::Renderer::draw( + // renderer, + // defaults, + // label_layout.bounds(), + // &self.label, + // self.text_size.unwrap_or(renderer.default_size()), + // self.font, + // self.text_color, + // alignment::Horizontal::Left, + // alignment::Vertical::Center, + // ); + + // let is_mouse_over = bounds.contains(cursor_position); + + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -242,28 +234,12 @@ pub trait Renderer: crate::Renderer { /// The default spacing of a [`Checkbox`]. const DEFAULT_SPACING: u16; - - /// Draws a [`Checkbox`]. - /// - /// It receives: - /// * the bounds of the [`Checkbox`] - /// * whether the [`Checkbox`] is selected or not - /// * whether the mouse is over the [`Checkbox`] or not - /// * the drawn label of the [`Checkbox`] - fn draw( - &mut self, - bounds: Rectangle, - is_checked: bool, - is_mouse_over: bool, - label: Self::Output, - style: &Self::Style, - ) -> Self::Output; } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + text::Renderer + row::Renderer, + Renderer: 'a + self::Renderer + text::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 30cf0781e5..61b2361f22 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -105,7 +105,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget for Column<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -169,14 +169,8 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - defaults, - &self.children, - layout, - cursor_position, - viewport, - ) + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -208,33 +202,10 @@ where } } -/// The renderer of a [`Column`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Column`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// Draws a [`Column`]. - /// - /// It receives: - /// - the children of the [`Column`] - /// - the [`Layout`] of the [`Column`] and its children - /// - the cursor position - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 0e86ab62cc..f43de2a59e 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -179,16 +179,8 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - defaults, - layout.bounds(), - cursor_position, - viewport, - &self.style, - &self.content, - layout.children().next().unwrap(), - ) + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -221,18 +213,6 @@ where pub trait Renderer: crate::Renderer { /// The style supported by this renderer. type Style: Default; - - /// Draws a [`Container`]. - fn draw( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - cursor_position: Point, - viewport: &Rectangle, - style: &Self::Style, - content: &Element<'_, Message, Self>, - content_layout: Layout<'_>, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 4d8e0a3f39..1607a5960e 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -96,8 +96,8 @@ where layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw(self.handle.clone(), layout) + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -225,9 +225,6 @@ impl std::fmt::Debug for Data { pub trait Renderer: crate::Renderer { /// Returns the dimensions of an [`Image`] located on the given path. fn dimensions(&self, handle: &Handle) -> (u32, u32); - - /// Draws an [`Image`]. - fn draw(&mut self, handle: Handle, layout: Layout<'_>) -> Self::Output; } impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs index 405daf0053..d483ed617a 100644 --- a/native/src/widget/image/viewer.rs +++ b/native/src/widget/image/viewer.rs @@ -88,7 +88,7 @@ impl<'a> Viewer<'a> { /// will be respected. fn image_size(&self, renderer: &Renderer, bounds: Size) -> Size where - Renderer: self::Renderer + image::Renderer, + Renderer: image::Renderer, { let (width, height) = renderer.dimensions(&self.handle); @@ -115,7 +115,7 @@ impl<'a> Viewer<'a> { impl<'a, Message, Renderer> Widget for Viewer<'a> where - Renderer: self::Renderer + image::Renderer, + Renderer: image::Renderer, { fn width(&self) -> Length { self.width @@ -287,7 +287,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { + ) { let bounds = layout.bounds(); let image_size = self.image_size(renderer, bounds.size()); @@ -302,16 +302,6 @@ where }; let is_mouse_over = bounds.contains(cursor_position); - - self::Renderer::draw( - renderer, - &self.state, - bounds, - image_size, - translation, - self.handle.clone(), - is_mouse_over, - ) } fn hash_layout(&self, state: &mut Hasher) { @@ -373,38 +363,9 @@ impl State { } } -/// The renderer of an [`Viewer`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Viewer`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// Draws the [`Viewer`]. - /// - /// It receives: - /// - the [`State`] of the [`Viewer`] - /// - the bounds of the [`Viewer`] widget - /// - the [`Size`] of the scaled [`Viewer`] image - /// - the translation of the clipped image - /// - the [`Handle`] to the underlying image - /// - whether the mouse is over the [`Viewer`] or not - /// - /// [`Handle`]: image::Handle - fn draw( - &mut self, - state: &State, - bounds: Rectangle, - image_size: Size, - translation: Vector, - handle: image::Handle, - is_mouse_over: bool, - ) -> Self::Output; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + image::Renderer, + Renderer: 'a + image::Renderer, Message: 'a, { fn from(viewer: Viewer<'a>) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 26a724098c..b4a8cdb139 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -32,7 +32,6 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; -use crate::row; use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, @@ -480,66 +479,56 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { - let picked_split = self - .state - .picked_split() - .and_then(|(split, axis)| { - let bounds = layout.bounds(); - - let splits = self - .state - .split_regions(f32::from(self.spacing), bounds.size()); - - let (_axis, region, ratio) = splits.get(&split)?; - - let region = axis.split_line_bounds( - *region, - *ratio, - f32::from(self.spacing), - ); - - Some((axis, region + Vector::new(bounds.x, bounds.y), true)) - }) - .or_else(|| match self.on_resize { - Some((leeway, _)) => { - let bounds = layout.bounds(); - - let relative_cursor = Point::new( - cursor_position.x - bounds.x, - cursor_position.y - bounds.y, - ); - - let splits = self - .state - .split_regions(f32::from(self.spacing), bounds.size()); - - let (_split, axis, region) = hovered_split( - splits.iter(), - f32::from(self.spacing + leeway), - relative_cursor, - )?; - - Some(( - axis, - region + Vector::new(bounds.x, bounds.y), - false, - )) - } - None => None, - }); - - self::Renderer::draw( - renderer, - defaults, - &self.elements, - self.state.picked_pane(), - picked_split, - layout, - &self.style, - cursor_position, - viewport, - ) + ) { + // let picked_split = self + // .state + // .picked_split() + // .and_then(|(split, axis)| { + // let bounds = layout.bounds(); + + // let splits = self + // .state + // .split_regions(f32::from(self.spacing), bounds.size()); + + // let (_axis, region, ratio) = splits.get(&split)?; + + // let region = axis.split_line_bounds( + // *region, + // *ratio, + // f32::from(self.spacing), + // ); + + // Some((axis, region + Vector::new(bounds.x, bounds.y), true)) + // }) + // .or_else(|| match self.on_resize { + // Some((leeway, _)) => { + // let bounds = layout.bounds(); + + // let relative_cursor = Point::new( + // cursor_position.x - bounds.x, + // cursor_position.y - bounds.y, + // ); + + // let splits = self + // .state + // .split_regions(f32::from(self.spacing), bounds.size()); + + // let (_split, axis, region) = hovered_split( + // splits.iter(), + // f32::from(self.spacing + leeway), + // relative_cursor, + // )?; + + // Some(( + // axis, + // region + Vector::new(bounds.x, bounds.y), + // false, + // )) + // } + // None => None, + // }); + + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -578,69 +567,12 @@ where pub trait Renderer: crate::Renderer + container::Renderer + Sized { /// The style supported by this renderer. type Style: Default; - - /// Draws a [`PaneGrid`]. - /// - /// It receives: - /// - the elements of the [`PaneGrid`] - /// - the [`Pane`] that is currently being dragged - /// - the [`Axis`] that is currently being resized - /// - the [`Layout`] of the [`PaneGrid`] and its elements - /// - the cursor position - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[(Pane, Content<'_, Message, Self>)], - dragging: Option<(Pane, Point)>, - resizing: Option<(Axis, Rectangle, bool)>, - layout: Layout<'_>, - style: &::Style, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output; - - /// Draws a [`Pane`]. - /// - /// It receives: - /// - the [`TitleBar`] of the [`Pane`], if any - /// - the [`Content`] of the [`Pane`] - /// - the [`Layout`] of the [`Pane`] and its elements - /// - the cursor position - fn draw_pane( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - style: &::Style, - title_bar: Option<(&TitleBar<'_, Message, Self>, Layout<'_>)>, - body: (&Element<'_, Message, Self>, Layout<'_>), - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output; - - /// Draws a [`TitleBar`]. - /// - /// It receives: - /// - the bounds, style of the [`TitleBar`] - /// - the style of the [`TitleBar`] - /// - the content of the [`TitleBar`] with its layout - /// - the controls of the [`TitleBar`] with their [`Layout`], if any - /// - the cursor position - fn draw_title_bar( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - style: &::Style, - content: (&Element<'_, Message, Self>, Layout<'_>), - controls: Option<(&Element<'_, Message, Self>, Layout<'_>)>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output; } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + row::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index bac9fdd4d7..e94955f7d9 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -61,32 +61,33 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { - if let Some(title_bar) = &self.title_bar { - let mut children = layout.children(); - let title_bar_layout = children.next().unwrap(); - let body_layout = children.next().unwrap(); - - renderer.draw_pane( - defaults, - layout.bounds(), - &self.style, - Some((title_bar, title_bar_layout)), - (&self.body, body_layout), - cursor_position, - viewport, - ) - } else { - renderer.draw_pane( - defaults, - layout.bounds(), - &self.style, - None, - (&self.body, layout), - cursor_position, - viewport, - ) - } + ) { + // TODO + // if let Some(title_bar) = &self.title_bar { + // let mut children = layout.children(); + // let title_bar_layout = children.next().unwrap(); + // let body_layout = children.next().unwrap(); + + // renderer.draw_pane( + // defaults, + // layout.bounds(), + // &self.style, + // Some((title_bar, title_bar_layout)), + // (&self.body, body_layout), + // cursor_position, + // viewport, + // ) + // } else { + // renderer.draw_pane( + // defaults, + // layout.bounds(), + // &self.style, + // None, + // (&self.body, layout), + // cursor_position, + // viewport, + // ) + // } } /// Returns whether the [`Content`] with the given [`Layout`] can be picked diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 070010f853..9b0b7c7836 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -90,34 +90,34 @@ where cursor_position: Point, viewport: &Rectangle, show_controls: bool, - ) -> Renderer::Output { - let mut children = layout.children(); - let padded = children.next().unwrap(); - - let mut children = padded.children(); - let title_layout = children.next().unwrap(); - - let controls = if let Some(controls) = &self.controls { - let controls_layout = children.next().unwrap(); - - if show_controls || self.always_show_controls { - Some((controls, controls_layout)) - } else { - None - } - } else { - None - }; - - renderer.draw_title_bar( - defaults, - layout.bounds(), - &self.style, - (&self.content, title_layout), - controls, - cursor_position, - viewport, - ) + ) { + // let mut children = layout.children(); + // let padded = children.next().unwrap(); + + // let mut children = padded.children(); + // let title_layout = children.next().unwrap(); + + // let controls = if let Some(controls) = &self.controls { + // let controls_layout = children.next().unwrap(); + + // if show_controls || self.always_show_controls { + // Some((controls, controls_layout)) + // } else { + // None + // } + // } else { + // None + // }; + + // renderer.draw_title_bar( + // defaults, + // layout.bounds(), + // &self.style, + // (&self.content, title_layout), + // controls, + // cursor_position, + // viewport, + // ) } /// Returns whether the mouse cursor is over the pick area of the diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index d779200053..7154a57247 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -327,18 +327,8 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - self::Renderer::draw( - renderer, - layout.bounds(), - cursor_position, - self.selected.as_ref().map(ToString::to_string), - self.placeholder.as_ref().map(String::as_str), - self.padding, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - &self.style, - ) + ) { + // TODO } fn overlay( @@ -387,19 +377,6 @@ pub trait Renderer: text::Renderer + menu::Renderer { fn menu_style( style: &::Style, ) -> ::Style; - - /// Draws a [`PickList`]. - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - selected: Option, - placeholder: Option<&str>, - padding: Padding, - text_size: u16, - font: Self::Font, - style: &::Style, - ) -> Self::Output; } impl<'a, T: 'a, Message, Renderer> Into> diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index d294f19855..d8cf53769b 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -97,13 +97,8 @@ where layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - layout.bounds(), - self.range.clone(), - self.value, - &self.style, - ) + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -127,22 +122,6 @@ pub trait Renderer: crate::Renderer { /// The default height of a [`ProgressBar`]. const DEFAULT_HEIGHT: u16; - - /// Draws a [`ProgressBar`]. - /// - /// It receives: - /// * the bounds of the [`ProgressBar`] - /// * the range of values of the [`ProgressBar`] - /// * the current value of the [`ProgressBar`] - /// * maybe a specific background of the [`ProgressBar`] - /// * maybe a specific active color of the [`ProgressBar`] - fn draw( - &self, - bounds: Rectangle, - range: RangeInclusive, - value: f32, - style: &Self::Style, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 513b2fce63..c6955079e8 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -5,7 +5,6 @@ use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; use crate::mouse; -use crate::row; use crate::text; use crate::touch; use crate::{ @@ -136,7 +135,7 @@ where impl Widget for Radio where Message: Clone, - Renderer: self::Renderer + text::Renderer + row::Renderer, + Renderer: self::Renderer + text::Renderer, { fn width(&self) -> Length { self.width @@ -199,36 +198,37 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - let bounds = layout.bounds(); - let mut children = layout.children(); + ) { + // TODO + // let bounds = layout.bounds(); + // let mut children = layout.children(); - let radio_layout = children.next().unwrap(); - let label_layout = children.next().unwrap(); - let radio_bounds = radio_layout.bounds(); + // let radio_layout = children.next().unwrap(); + // let label_layout = children.next().unwrap(); + // let radio_bounds = radio_layout.bounds(); - let label = text::Renderer::draw( - renderer, - defaults, - label_layout.bounds(), - &self.label, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - self.text_color, - alignment::Horizontal::Left, - alignment::Vertical::Center, - ); + // let label = text::Renderer::draw( + // renderer, + // defaults, + // label_layout.bounds(), + // &self.label, + // self.text_size.unwrap_or(renderer.default_size()), + // self.font, + // self.text_color, + // alignment::Horizontal::Left, + // alignment::Vertical::Center, + // ); - let is_mouse_over = bounds.contains(cursor_position); + // let is_mouse_over = bounds.contains(cursor_position); - self::Renderer::draw( - renderer, - radio_bounds, - self.is_selected, - is_mouse_over, - label, - &self.style, - ) + // self::Renderer::draw( + // renderer, + // radio_bounds, + // self.is_selected, + // is_mouse_over, + // label, + // &self.style, + // ) } fn hash_layout(&self, state: &mut Hasher) { @@ -254,29 +254,13 @@ pub trait Renderer: crate::Renderer { /// The default spacing of a [`Radio`] button. const DEFAULT_SPACING: u16; - - /// Draws a [`Radio`] button. - /// - /// It receives: - /// * the bounds of the [`Radio`] - /// * whether the [`Radio`] is selected or not - /// * whether the mouse is over the [`Radio`] or not - /// * the drawn label of the [`Radio`] - fn draw( - &mut self, - bounds: Rectangle, - is_selected: bool, - is_mouse_over: bool, - label: Self::Output, - style: &Self::Style, - ) -> Self::Output; } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer + row::Renderer + text::Renderer, + Renderer: 'a + self::Renderer + text::Renderer, { fn from(radio: Radio) -> Element<'a, Message, Renderer> { Element::new(radio) diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 1923f213d7..04a1f8648f 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -104,7 +104,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget for Row<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -168,14 +168,15 @@ where layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - defaults, - &self.children, - layout, - cursor_position, - viewport, - ) + ) { + // TODO + // renderer.draw( + // defaults, + // &self.children, + // layout, + // cursor_position, + // viewport, + // ) } fn hash_layout(&self, state: &mut Hasher) { @@ -207,33 +208,10 @@ where } } -/// The renderer of a [`Row`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Row`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// Draws a [`Row`]. - /// - /// It receives: - /// - the children of the [`Row`] - /// - the [`Layout`] of the [`Row`] and its children - /// - the cursor position - fn draw( - &mut self, - defaults: &Self::Defaults, - children: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Self::Output; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, Message: 'a, { fn from(row: Row<'a, Message, Renderer>) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs index 18c886584b..1fab77bcf1 100644 --- a/native/src/widget/rule.rs +++ b/native/src/widget/rule.rs @@ -72,8 +72,9 @@ where layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw(layout.bounds(), &self.style, self.is_horizontal) + ) { + // TODO + // renderer.draw(layout.bounds(), &self.style, self.is_horizontal) } fn hash_layout(&self, state: &mut Hasher) { @@ -89,19 +90,6 @@ where pub trait Renderer: crate::Renderer { /// The style supported by this renderer. type Style: Default; - - /// Draws a [`Rule`]. - /// - /// It receives: - /// * the bounds of the [`Rule`] - /// * the style of the [`Rule`] - /// * whether the [`Rule`] is horizontal (true) or vertical (false) - fn draw( - &mut self, - bounds: Rectangle, - style: &Self::Style, - is_horizontal: bool, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index a8e467d36e..1c897dc5ab 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -1,5 +1,4 @@ //! Navigate an endless amount of content with a scrollbar. -use crate::column; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -381,57 +380,45 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - let bounds = layout.bounds(); - let content_layout = layout.children().next().unwrap(); - let content_bounds = content_layout.bounds(); - let offset = self.state.offset(bounds, content_bounds); - let scrollbar = renderer.scrollbar( - bounds, - content_bounds, - offset, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - ); - - let is_mouse_over = bounds.contains(cursor_position); - let is_mouse_over_scrollbar = scrollbar - .as_ref() - .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) - .unwrap_or(false); - - let content = { - let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { - Point::new(cursor_position.x, cursor_position.y + offset as f32) - } else { - Point::new(cursor_position.x, -1.0) - }; - - self.content.draw( - renderer, - defaults, - content_layout, - cursor_position, - &Rectangle { - y: bounds.y + offset as f32, - ..bounds - }, - ) - }; - - self::Renderer::draw( - renderer, - &self.state, - bounds, - content_layout.bounds(), - is_mouse_over, - is_mouse_over_scrollbar, - scrollbar, - offset, - &self.style, - content, - ) + ) { + // TODO + // let bounds = layout.bounds(); + // let content_layout = layout.children().next().unwrap(); + // let content_bounds = content_layout.bounds(); + // let offset = self.state.offset(bounds, content_bounds); + // let scrollbar = renderer.scrollbar( + // bounds, + // content_bounds, + // offset, + // self.scrollbar_width, + // self.scrollbar_margin, + // self.scroller_width, + // ); + + // let is_mouse_over = bounds.contains(cursor_position); + // let is_mouse_over_scrollbar = scrollbar + // .as_ref() + // .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) + // .unwrap_or(false); + + // let content = { + // let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { + // Point::new(cursor_position.x, cursor_position.y + offset as f32) + // } else { + // Point::new(cursor_position.x, -1.0) + // }; + + // self.content.draw( + // renderer, + // defaults, + // content_layout, + // cursor_position, + // &Rectangle { + // y: bounds.y + offset as f32, + // ..bounds + // }, + // ) + // }; } fn hash_layout(&self, state: &mut Hasher) { @@ -635,7 +622,7 @@ pub struct Scroller { /// able to use a [`Scrollable`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: column::Renderer + Sized { +pub trait Renderer: crate::Renderer + Sized { /// The style supported by this renderer. type Style: Default; @@ -650,30 +637,6 @@ pub trait Renderer: column::Renderer + Sized { scrollbar_margin: u16, scroller_width: u16, ) -> Option; - - /// Draws the [`Scrollable`]. - /// - /// It receives: - /// - the [`State`] of the [`Scrollable`] - /// - the bounds of the [`Scrollable`] widget - /// - the bounds of the [`Scrollable`] content - /// - whether the mouse is over the [`Scrollable`] or not - /// - whether the mouse is over the [`Scrollbar`] or not - /// - a optional [`Scrollbar`] to be rendered - /// - the scrolling offset - /// - the drawn content - fn draw( - &mut self, - scrollable: &State, - bounds: Rectangle, - content_bounds: Rectangle, - is_mouse_over: bool, - is_mouse_over_scrollbar: bool, - scrollbar: Option, - offset: u32, - style: &Self::Style, - content: Self::Output, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 2a74d5a313..bcf811fee3 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -250,18 +250,19 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - let start = *self.range.start(); - let end = *self.range.end(); - - renderer.draw( - layout.bounds(), - cursor_position, - start.into() as f32..=end.into() as f32, - self.value.into() as f32, - self.state.is_dragging, - &self.style, - ) + ) { + // TODO + // let start = *self.range.start(); + // let end = *self.range.end(); + + // renderer.draw( + // layout.bounds(), + // cursor_position, + // start.into() as f32..=end.into() as f32, + // self.value.into() as f32, + // self.state.is_dragging, + // &self.style, + // ) } fn hash_layout(&self, state: &mut Hasher) { @@ -284,24 +285,6 @@ pub trait Renderer: crate::Renderer { /// The default height of a [`Slider`]. const DEFAULT_HEIGHT: u16; - - /// Draws a [`Slider`]. - /// - /// It receives: - /// * the current cursor position - /// * the bounds of the [`Slider`] - /// * the local state of the [`Slider`] - /// * the range of values of the [`Slider`] - /// * the current value of the [`Slider`] - fn draw( - &mut self, - bounds: Rectangle, - cursor_position: Point, - range: RangeInclusive, - value: f32, - is_dragging: bool, - style: &Self::Style, - ) -> Self::Output; } impl<'a, T, Message, Renderer> From> diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index 6b34ece889..9a70dab14f 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -39,7 +39,7 @@ impl Space { impl Widget for Space where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -66,8 +66,7 @@ where layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw(layout.bounds()) + ) { } fn hash_layout(&self, state: &mut Hasher) { @@ -78,17 +77,9 @@ where } } -/// The renderer of an amount of [`Space`]. -pub trait Renderer: crate::Renderer { - /// Draws an amount of empty [`Space`]. - /// - /// You should most likely return an empty primitive here. - fn draw(&mut self, bounds: Rectangle) -> Self::Output; -} - impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, Message: 'a, { fn from(space: Space) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 9cd619182c..737dd8d40c 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -94,8 +94,9 @@ where layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw(self.handle.clone(), layout) + ) { + // TODO + // renderer.draw(self.handle.clone(), layout) } fn hash_layout(&self, state: &mut Hasher) { @@ -187,9 +188,6 @@ impl std::fmt::Debug for Data { pub trait Renderer: crate::Renderer { /// Returns the default dimensions of an [`Svg`] for the given [`Handle`]. fn dimensions(&self, handle: &Handle) -> (u32, u32); - - /// Draws an [`Svg`]. - fn draw(&mut self, handle: Handle, layout: Layout<'_>) -> Self::Output; } impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index 168d49c2dc..9915a6e980 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -133,22 +133,13 @@ where fn draw( &self, - renderer: &mut Renderer, - defaults: &Renderer::Defaults, - layout: Layout<'_>, + _renderer: &mut Renderer, + _defaults: &Renderer::Defaults, + _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - renderer.draw( - defaults, - layout.bounds(), - &self.content, - self.size.unwrap_or(renderer.default_size()), - self.font, - self.color, - self.horizontal_alignment, - self.vertical_alignment, - ) + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -201,27 +192,6 @@ pub trait Renderer: crate::Renderer { point: Point, nearest_only: bool, ) -> Option; - - /// Draws a [`Text`] fragment. - /// - /// It receives: - /// * the bounds of the [`Text`] - /// * the contents of the [`Text`] - /// * the size of the [`Text`] - /// * the color of the [`Text`] - /// * the [`HorizontalAlignment`] of the [`Text`] - /// * the [`VerticalAlignment`] of the [`Text`] - fn draw( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - content: &str, - size: u16, - font: Self::Font, - color: Option, - horizontal_alignment: alignment::Horizontal, - vertical_alignment: alignment::Vertical, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 84d171beb6..fe95eb955b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -170,38 +170,39 @@ where layout: Layout<'_>, cursor_position: Point, value: Option<&Value>, - ) -> Renderer::Output { - let value = value.unwrap_or(&self.value); - let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); - - if self.is_secure { - self::Renderer::draw( - renderer, - bounds, - text_bounds, - cursor_position, - self.font, - self.size.unwrap_or(renderer.default_size()), - &self.placeholder, - &value.secure(), - &self.state, - &self.style, - ) - } else { - self::Renderer::draw( - renderer, - bounds, - text_bounds, - cursor_position, - self.font, - self.size.unwrap_or(renderer.default_size()), - &self.placeholder, - value, - &self.state, - &self.style, - ) - } + ) { + // TODO + // let value = value.unwrap_or(&self.value); + // let bounds = layout.bounds(); + // let text_bounds = layout.children().next().unwrap().bounds(); + + // if self.is_secure { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // &value.secure(), + // &self.state, + // &self.style, + // ) + // } else { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // value, + // &self.state, + // &self.style, + // ) + // } } } @@ -630,7 +631,7 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { + ) { self.draw(renderer, layout, cursor_position, None) } @@ -673,28 +674,6 @@ pub trait Renderer: text::Renderer + Sized { state: &State, ) -> f32; - /// Draws a [`TextInput`]. - /// - /// It receives: - /// - the bounds of the [`TextInput`] - /// - the bounds of the text (i.e. the current value) - /// - the cursor position - /// - the placeholder to show when the value is empty - /// - the current [`Value`] - /// - the current [`State`] - fn draw( - &mut self, - bounds: Rectangle, - text_bounds: Rectangle, - cursor_position: Point, - font: Self::Font, - size: u16, - placeholder: &str, - value: &Value, - state: &State, - style: &Self::Style, - ) -> Self::Output; - /// Computes the position of the text cursor at the given X coordinate of /// a [`TextInput`]. fn find_cursor_position( diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index c624be4c59..4a1f1f5d7b 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -5,7 +5,6 @@ use crate::alignment; use crate::event; use crate::layout; use crate::mouse; -use crate::row; use crate::text; use crate::{ Alignment, Clipboard, Element, Event, Hasher, Layout, Length, Point, @@ -119,7 +118,7 @@ impl impl Widget for Toggler where - Renderer: self::Renderer + text::Renderer + row::Renderer, + Renderer: self::Renderer + text::Renderer, { fn width(&self) -> Length { self.width @@ -190,43 +189,35 @@ where layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, - ) -> Renderer::Output { - let bounds = layout.bounds(); - let mut children = layout.children(); - - let label = match &self.label { - Some(label) => { - let label_layout = children.next().unwrap(); - - Some(text::Renderer::draw( - renderer, - defaults, - label_layout.bounds(), - &label, - self.text_size.unwrap_or(renderer.default_size()), - self.font, - None, - self.text_alignment, - alignment::Vertical::Center, - )) - } - - None => None, - }; - - let toggler_layout = children.next().unwrap(); - let toggler_bounds = toggler_layout.bounds(); - - let is_mouse_over = bounds.contains(cursor_position); - - self::Renderer::draw( - renderer, - toggler_bounds, - self.is_active, - is_mouse_over, - label, - &self.style, - ) + ) { + // TODO + // let bounds = layout.bounds(); + // let mut children = layout.children(); + + // let label = match &self.label { + // Some(label) => { + // let label_layout = children.next().unwrap(); + + // Some(text::Renderer::draw( + // renderer, + // defaults, + // label_layout.bounds(), + // &label, + // self.text_size.unwrap_or(renderer.default_size()), + // self.font, + // None, + // self.text_alignment, + // alignment::Vertical::Center, + // )) + // } + + // None => None, + // }; + + // let toggler_layout = children.next().unwrap(); + // let toggler_bounds = toggler_layout.bounds(); + + // let is_mouse_over = bounds.contains(cursor_position); } fn hash_layout(&self, state: &mut Hasher) { @@ -249,29 +240,12 @@ pub trait Renderer: crate::Renderer { /// The default size of a [`Toggler`]. const DEFAULT_SIZE: u16; - - /// Draws a [`Toggler`]. - /// - /// It receives: - /// * the bounds of the [`Toggler`] - /// * whether the [`Toggler`] is activated or not - /// * whether the mouse is over the [`Toggler`] or not - /// * the drawn label of the [`Toggler`] - /// * the style of the [`Toggler`] - fn draw( - &mut self, - bounds: Rectangle, - is_active: bool, - is_mouse_over: bool, - label: Option, - style: &Self::Style, - ) -> Self::Output; } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + text::Renderer + row::Renderer, + Renderer: 'a + self::Renderer + text::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 276afd4102..83d04675b0 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -136,25 +136,13 @@ where fn draw( &self, - renderer: &mut Renderer, - defaults: &Renderer::Defaults, - layout: Layout<'_>, - cursor_position: Point, - viewport: &Rectangle, - ) -> Renderer::Output { - self::Renderer::draw( - renderer, - defaults, - cursor_position, - layout, - viewport, - &self.content, - &self.tooltip, - self.position, - &self.style, - self.gap, - self.padding, - ) + _renderer: &mut Renderer, + _defaults: &Renderer::Defaults, + _layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + // TODO } fn hash_layout(&self, state: &mut Hasher) { @@ -177,23 +165,6 @@ pub trait Renderer: { /// The default padding of a [`Tooltip`] drawn by this renderer. const DEFAULT_PADDING: u16; - - /// Draws a [`Tooltip`]. - /// - /// [`Tooltip`]: struct.Tooltip.html - fn draw( - &mut self, - defaults: &Self::Defaults, - cursor_position: Point, - content_layout: Layout<'_>, - viewport: &Rectangle, - content: &Element<'_, Message, Self>, - tooltip: &Text, - position: Position, - style: &::Style, - gap: u16, - padding: u16, - ) -> Self::Output; } impl<'a, Message, Renderer> From> diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 73fa5c5f4f..56de553f15 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -8,7 +8,6 @@ use iced_graphics::font; use iced_graphics::layer::Layer; use iced_graphics::{Primitive, Viewport}; use iced_native::alignment; -use iced_native::mouse; use iced_native::{Font, Size}; #[cfg(any(feature = "image_rs", feature = "svg"))] @@ -28,6 +27,7 @@ pub struct Backend { image_pipeline: image::Pipeline, default_text_size: u16, + primitive: Primitive, } impl Backend { @@ -60,6 +60,7 @@ impl Backend { image_pipeline, default_text_size: settings.default_text_size, + primitive: Primitive::None, } } @@ -67,16 +68,16 @@ impl Backend { /// /// The text provided as overlay will be rendered on top of the primitives. /// This is useful for rendering debug information. - pub fn draw>( + pub fn present>( &mut self, device: &wgpu::Device, staging_belt: &mut wgpu::util::StagingBelt, encoder: &mut wgpu::CommandEncoder, frame: &wgpu::TextureView, + primitive: &Primitive, viewport: &Viewport, - (primitive, mouse_interaction): &(Primitive, mouse::Interaction), overlay_text: &[T], - ) -> mouse::Interaction { + ) { log::debug!("Drawing"); let target_size = viewport.physical_size(); @@ -102,8 +103,6 @@ impl Backend { #[cfg(any(feature = "image_rs", feature = "svg"))] self.image_pipeline.trim_cache(); - - *mouse_interaction } fn flush( diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index eca54b6f0e..f29a50f5f4 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -1,7 +1,7 @@ use crate::{Backend, Color, Error, Renderer, Settings, Viewport}; use futures::task::SpawnExt; -use iced_native::{futures, mouse}; +use iced_native::futures; use raw_window_handle::HasRawWindowHandle; /// A window graphics backend for iced powered by `wgpu`. @@ -132,15 +132,14 @@ impl iced_graphics::window::Compositor for Compositor { ); } - fn draw>( + fn present>( &mut self, renderer: &mut Self::Renderer, surface: &mut Self::Surface, viewport: &Viewport, background_color: Color, - output: &::Output, overlay: &[T], - ) -> Result { + ) -> Result<(), iced_graphics::window::SurfaceError> { match surface.get_current_frame() { Ok(frame) => { let mut encoder = self.device.create_command_encoder( @@ -180,15 +179,17 @@ impl iced_graphics::window::Compositor for Compositor { depth_stencil_attachment: None, }); - let mouse_interaction = renderer.backend_mut().draw( - &mut self.device, - &mut self.staging_belt, - &mut encoder, - view, - viewport, - output, - overlay, - ); + renderer.present(|backend, primitive| { + backend.present( + &mut self.device, + &mut self.staging_belt, + &mut encoder, + view, + primitive, + viewport, + overlay, + ); + }); // Submit work self.staging_belt.finish(); @@ -202,7 +203,7 @@ impl iced_graphics::window::Compositor for Compositor { self.local_pool.run_until_stalled(); - Ok(mouse_interaction) + Ok(()) } Err(error) => match error { wgpu::SurfaceError::Timeout => { diff --git a/winit/src/application.rs b/winit/src/application.rs index ab9a70a0ed..70562627df 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -5,7 +5,6 @@ pub use state::State; use crate::clipboard::{self, Clipboard}; use crate::conversion; -use crate::mouse; use crate::{ Color, Command, Debug, Error, Executor, Mode, Proxy, Runtime, Settings, Size, Subscription, @@ -253,9 +252,8 @@ async fn run_instance( &mut debug, )); - let mut primitive = - user_interface.draw(&mut renderer, state.cursor_position()); - let mut mouse_interaction = mouse::Interaction::default(); + // TODO + // let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); @@ -319,8 +317,7 @@ async fn run_instance( } debug.draw_started(); - primitive = - user_interface.draw(&mut renderer, state.cursor_position()); + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); window.request_redraw(); @@ -359,8 +356,7 @@ async fn run_instance( debug.layout_finished(); debug.draw_started(); - primitive = user_interface - .draw(&mut renderer, state.cursor_position()); + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); compositor.configure_surface( @@ -372,27 +368,16 @@ async fn run_instance( viewport_version = current_viewport_version; } - match compositor.draw( + match compositor.present( &mut renderer, &mut surface, state.viewport(), state.background_color(), - &primitive, &debug.overlay(), ) { - Ok(new_mouse_interaction) => { + Ok(()) => { debug.render_finished(); - if new_mouse_interaction != mouse_interaction { - window.set_cursor_icon( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); - - mouse_interaction = new_mouse_interaction; - } - // TODO: Handle animations! // Maybe we can use `ControlFlow::WaitUntil` for this. } From 3a0c503db99eb3d45ac971132904df419ee566b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 16:59:19 +0700 Subject: [PATCH 02/92] Implement `Widget::draw` for `Text` --- glow/src/backend.rs | 4 +-- graphics/src/backend.rs | 1 - graphics/src/layer.rs | 16 ++++++----- graphics/src/renderer.rs | 55 +++++++++++++++++++++++++++++++----- graphics/src/widget/text.rs | 2 -- native/src/renderer.rs | 8 +++++- native/src/renderer/null.rs | 11 ++++++-- native/src/renderer/text.rs | 20 +++++++++++++ native/src/user_interface.rs | 3 ++ native/src/widget/text.rs | 34 +++++++++++++++++----- wgpu/src/backend.rs | 4 +-- 11 files changed, 127 insertions(+), 31 deletions(-) create mode 100644 native/src/renderer/text.rs diff --git a/glow/src/backend.rs b/glow/src/backend.rs index b8e4bd4b95..9212761e3d 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -48,7 +48,7 @@ impl Backend { pub fn present>( &mut self, gl: &glow::Context, - primitive: &Primitive, + primitives: &[Primitive], viewport: &Viewport, overlay_text: &[T], ) { @@ -56,7 +56,7 @@ impl Backend { let scale_factor = viewport.scale_factor() as f32; let projection = viewport.projection(); - let mut layers = Layer::generate(primitive, viewport); + let mut layers = Layer::generate(primitives, viewport); layers.push(Layer::overlay(overlay_text, viewport)); for layer in layers { diff --git a/graphics/src/backend.rs b/graphics/src/backend.rs index 7e0af2cc49..b8ff5d2103 100644 --- a/graphics/src/backend.rs +++ b/graphics/src/backend.rs @@ -16,7 +16,6 @@ pub trait Backend { fn trim_measurements(&mut self) {} } -/// A graphics backend that supports text rendering. pub trait Text { /// The icon font of the backend. const ICON_FONT: Font; diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index 9653a2e477..e5cb64c308 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -74,7 +74,7 @@ impl<'a> Layer<'a> { /// Distributes the given [`Primitive`] and generates a list of layers based /// on its contents. pub fn generate( - primitive: &'a Primitive, + primitives: &'a [Primitive], viewport: &Viewport, ) -> Vec { let first_layer = @@ -82,12 +82,14 @@ impl<'a> Layer<'a> { let mut layers = vec![first_layer]; - Self::process_primitive( - &mut layers, - Vector::new(0.0, 0.0), - primitive, - 0, - ); + for primitive in primitives { + Self::process_primitive( + &mut layers, + Vector::new(0.0, 0.0), + primitive, + 0, + ); + } layers } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index cedffe7e5d..a708cb6791 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,12 +1,14 @@ -use crate::{Backend, Defaults, Primitive}; +use crate::backend::{self, Backend}; +use crate::{Defaults, Primitive, Vector}; use iced_native::layout; -use iced_native::{Element, Rectangle}; +use iced_native::renderer; +use iced_native::{Color, Element, Font, Rectangle}; /// A backend-agnostic renderer that supports all the built-in widgets. #[derive(Debug)] pub struct Renderer { backend: B, - primitive: Primitive, + primitives: Vec, } impl Renderer { @@ -14,7 +16,7 @@ impl Renderer { pub fn new(backend: B) -> Self { Self { backend, - primitive: Primitive::None, + primitives: Vec::new(), } } @@ -22,8 +24,8 @@ impl Renderer { &self.backend } - pub fn present(&mut self, f: impl FnOnce(&mut B, &Primitive)) { - f(&mut self.backend, &self.primitive); + pub fn present(&mut self, f: impl FnOnce(&mut B, &[Primitive])) { + f(&mut self.backend, &self.primitives); } } @@ -45,5 +47,44 @@ where layout } - fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { + let current_primitives = + std::mem::replace(&mut self.primitives, Vec::new()); + + f(self); + + let layer_primitives = + std::mem::replace(&mut self.primitives, current_primitives); + + self.primitives.push(Primitive::Clip { + bounds, + offset: Vector::new(0, 0), + content: Box::new(Primitive::Group { + primitives: layer_primitives, + }), + }); + } + + fn clear(&mut self) { + self.primitives.clear(); + } +} + +impl renderer::Text for Renderer +where + B: Backend + backend::Text, +{ + type Font = Font; + + fn fill_text(&mut self, text: renderer::text::Section<'_, Self::Font>) { + self.primitives.push(Primitive::Text { + content: text.content.to_string(), + bounds: text.bounds, + size: text.size.unwrap_or(f32::from(self.backend.default_size())), + color: text.color.unwrap_or(Color::BLACK), + font: text.font, + horizontal_alignment: text.horizontal_alignment, + vertical_alignment: text.vertical_alignment, + }); + } } diff --git a/graphics/src/widget/text.rs b/graphics/src/widget/text.rs index 2ccd18a10e..4ee2c61664 100644 --- a/graphics/src/widget/text.rs +++ b/graphics/src/widget/text.rs @@ -15,8 +15,6 @@ impl text::Renderer for Renderer where B: Backend + backend::Text, { - type Font = Font; - fn default_size(&self) -> u16 { self.backend().default_size() } diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 3784ff24d0..ca0aececf0 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -19,13 +19,17 @@ //! [`text::Renderer`]: crate::widget::text::Renderer //! [`Checkbox`]: crate::widget::Checkbox //! [`checkbox::Renderer`]: crate::widget::checkbox::Renderer +pub mod text; + +pub use text::Text; #[cfg(debug_assertions)] mod null; #[cfg(debug_assertions)] pub use null::Null; -use crate::{layout, Element, Rectangle}; +use crate::layout; +use crate::{Element, Rectangle}; /// A component that can take the state of a user interface and produce an /// output for its users. @@ -48,4 +52,6 @@ pub trait Renderer: Sized { } fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); + + fn clear(&mut self); } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 1ffca5c97c..da758ebb24 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -4,12 +4,13 @@ use crate::container; use crate::pane_grid; use crate::progress_bar; use crate::radio; +use crate::renderer::{self, Renderer}; use crate::scrollable; use crate::slider; use crate::text; use crate::text_input; use crate::toggler; -use crate::{Font, Padding, Point, Rectangle, Renderer, Size}; +use crate::{Font, Padding, Point, Rectangle, Size}; /// A renderer that does nothing. /// @@ -28,11 +29,17 @@ impl Renderer for Null { type Defaults = (); fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} + + fn clear(&mut self) {} } -impl text::Renderer for Null { +impl renderer::Text for Null { type Font = Font; + fn fill_text(&mut self, _text: renderer::text::Section<'_, Self::Font>) {} +} + +impl text::Renderer for Null { fn default_size(&self) -> u16 { 20 } diff --git a/native/src/renderer/text.rs b/native/src/renderer/text.rs new file mode 100644 index 0000000000..5c189d89ce --- /dev/null +++ b/native/src/renderer/text.rs @@ -0,0 +1,20 @@ +use crate::alignment; +use crate::{Color, Rectangle, Renderer}; + +pub trait Text: Renderer { + /// The font type used. + type Font: Default + Copy; + + fn fill_text(&mut self, section: Section<'_, Self::Font>); +} + +#[derive(Debug, Clone, Copy)] +pub struct Section<'a, Font> { + pub content: &'a str, + pub bounds: Rectangle, + pub size: Option, + pub color: Option, + pub font: Font, + pub horizontal_alignment: alignment::Horizontal, + pub vertical_alignment: alignment::Vertical, +} diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 187c469aae..c80aaf449f 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -330,6 +330,9 @@ where /// } /// ``` pub fn draw(&mut self, renderer: &mut Renderer, cursor_position: Point) { + // TODO: Move to shell level (?) + renderer.clear(); + let viewport = Rectangle::with_size(self.bounds); let overlay = if let Some(mut overlay) = diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index 9915a6e980..ea1ba7acdf 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -1,6 +1,7 @@ //! Write some text for your users to read. use crate::alignment; use crate::layout; +use crate::renderer; use crate::{ Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; @@ -133,13 +134,35 @@ where fn draw( &self, - _renderer: &mut Renderer, + renderer: &mut Renderer, _defaults: &Renderer::Defaults, - _layout: Layout<'_>, + layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { - // TODO + let bounds = layout.bounds(); + + let x = match self.horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => bounds.center_x(), + alignment::Horizontal::Right => bounds.x + bounds.width, + }; + + let y = match self.vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => bounds.center_y(), + alignment::Vertical::Bottom => bounds.y + bounds.height, + }; + + renderer.fill_text(renderer::text::Section { + content: &self.content, + size: self.size.map(f32::from), + bounds: Rectangle { x, y, ..bounds }, + color: self.color, + font: self.font, + horizontal_alignment: self.horizontal_alignment, + vertical_alignment: self.vertical_alignment, + }); } fn hash_layout(&self, state: &mut Hasher) { @@ -159,10 +182,7 @@ where /// able to use [`Text`] in your user interface. /// /// [renderer]: crate::Renderer -pub trait Renderer: crate::Renderer { - /// The font type used for [`Text`]. - type Font: Default + Copy; - +pub trait Renderer: renderer::Text { /// Returns the default size of [`Text`]. fn default_size(&self) -> u16; diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 56de553f15..2597cef33f 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -74,7 +74,7 @@ impl Backend { staging_belt: &mut wgpu::util::StagingBelt, encoder: &mut wgpu::CommandEncoder, frame: &wgpu::TextureView, - primitive: &Primitive, + primitives: &[Primitive], viewport: &Viewport, overlay_text: &[T], ) { @@ -84,7 +84,7 @@ impl Backend { let scale_factor = viewport.scale_factor() as f32; let transformation = viewport.projection(); - let mut layers = Layer::generate(primitive, viewport); + let mut layers = Layer::generate(primitives, viewport); layers.push(Layer::overlay(overlay_text, viewport)); for layer in layers { From 1ab2afe72d377d8fa9a5b2d71cc23f3a729351a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 17:05:08 +0700 Subject: [PATCH 03/92] Implement `Widget::draw` for `Column` --- native/src/widget/column.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 61b2361f22..7cf6d34522 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -170,7 +170,9 @@ where cursor_position: Point, viewport: &Rectangle, ) { - // TODO + for (child, layout) in self.children.iter().zip(layout.children()) { + child.draw(renderer, defaults, layout, cursor_position, viewport); + } } fn hash_layout(&self, state: &mut Hasher) { From f8895a42b05b93f28416597c8115f7b598885ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 17:05:37 +0700 Subject: [PATCH 04/92] Implement `Widget::draw` for `Row` --- native/src/widget/row.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 04a1f8648f..7eb5ab5556 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -169,14 +169,9 @@ where cursor_position: Point, viewport: &Rectangle, ) { - // TODO - // renderer.draw( - // defaults, - // &self.children, - // layout, - // cursor_position, - // viewport, - // ) + for (child, layout) in self.children.iter().zip(layout.children()) { + child.draw(renderer, defaults, layout, cursor_position, viewport); + } } fn hash_layout(&self, state: &mut Hasher) { From 7c4738735b6cd5eafb544de01dbf5dd5091dc188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 17:05:47 +0700 Subject: [PATCH 05/92] Implement `Widget::draw` for `Container` --- native/src/widget/container.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index f43de2a59e..af40f2abd3 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -180,7 +180,13 @@ where cursor_position: Point, viewport: &Rectangle, ) { - // TODO + self.content.draw( + renderer, + defaults, + layout.children().next().unwrap(), + cursor_position, + viewport, + ); } fn hash_layout(&self, state: &mut Hasher) { From dfceee99aad9462f09ca61081e68e1decb2fed92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 14 Oct 2021 17:15:29 +0700 Subject: [PATCH 06/92] Implement `Widget::draw` for `Scrollable` Rendering the scroller is still WIP --- graphics/src/renderer.rs | 11 ++- native/src/renderer.rs | 9 ++- native/src/renderer/null.rs | 10 ++- native/src/user_interface.rs | 22 +++--- native/src/widget/scrollable.rs | 126 ++++++++++++++++++++++---------- 5 files changed, 125 insertions(+), 53 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index a708cb6791..91f6b5506b 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -47,7 +47,12 @@ where layout } - fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { + fn with_layer( + &mut self, + bounds: Rectangle, + offset: Vector, + f: impl FnOnce(&mut Self), + ) { let current_primitives = std::mem::replace(&mut self.primitives, Vec::new()); @@ -58,7 +63,7 @@ where self.primitives.push(Primitive::Clip { bounds, - offset: Vector::new(0, 0), + offset, content: Box::new(Primitive::Group { primitives: layer_primitives, }), @@ -77,6 +82,8 @@ where type Font = Font; fn fill_text(&mut self, text: renderer::text::Section<'_, Self::Font>) { + dbg!(text); + self.primitives.push(Primitive::Text { content: text.content.to_string(), bounds: text.bounds, diff --git a/native/src/renderer.rs b/native/src/renderer.rs index ca0aececf0..5a7a006766 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -29,7 +29,7 @@ mod null; pub use null::Null; use crate::layout; -use crate::{Element, Rectangle}; +use crate::{Element, Rectangle, Vector}; /// A component that can take the state of a user interface and produce an /// output for its users. @@ -51,7 +51,12 @@ pub trait Renderer: Sized { element.layout(self, limits) } - fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); + fn with_layer( + &mut self, + bounds: Rectangle, + offset: Vector, + f: impl FnOnce(&mut Self), + ); fn clear(&mut self); } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index da758ebb24..b8e01b80c0 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -10,7 +10,7 @@ use crate::slider; use crate::text; use crate::text_input; use crate::toggler; -use crate::{Font, Padding, Point, Rectangle, Size}; +use crate::{Font, Padding, Point, Rectangle, Size, Vector}; /// A renderer that does nothing. /// @@ -28,7 +28,13 @@ impl Null { impl Renderer for Null { type Defaults = (); - fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} + fn with_layer( + &mut self, + _bounds: Rectangle, + _offset: Vector, + _f: impl FnOnce(&mut Self), + ) { + } fn clear(&mut self) {} } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index c80aaf449f..eb57670d49 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,7 +1,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; -use crate::{Clipboard, Element, Layout, Point, Rectangle, Size}; +use crate::{Clipboard, Element, Layout, Point, Rectangle, Size, Vector}; use std::hash::Hasher; @@ -347,14 +347,18 @@ where let overlay_bounds = layer.layout.bounds(); - renderer.with_layer(overlay_bounds, |renderer| { - overlay.draw( - renderer, - &Renderer::Defaults::default(), - Layout::new(&layer.layout), - cursor_position, - ); - }); + renderer.with_layer( + overlay_bounds, + Vector::new(0, 0), + |renderer| { + overlay.draw( + renderer, + &Renderer::Defaults::default(), + Layout::new(&layer.layout), + cursor_position, + ); + }, + ); self.overlay = Some(layer); diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 1c897dc5ab..ba3d3dd629 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -381,44 +381,94 @@ where cursor_position: Point, _viewport: &Rectangle, ) { - // TODO - // let bounds = layout.bounds(); - // let content_layout = layout.children().next().unwrap(); - // let content_bounds = content_layout.bounds(); - // let offset = self.state.offset(bounds, content_bounds); - // let scrollbar = renderer.scrollbar( - // bounds, - // content_bounds, - // offset, - // self.scrollbar_width, - // self.scrollbar_margin, - // self.scroller_width, - // ); - - // let is_mouse_over = bounds.contains(cursor_position); - // let is_mouse_over_scrollbar = scrollbar - // .as_ref() - // .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) - // .unwrap_or(false); - - // let content = { - // let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { - // Point::new(cursor_position.x, cursor_position.y + offset as f32) - // } else { - // Point::new(cursor_position.x, -1.0) - // }; - - // self.content.draw( - // renderer, - // defaults, - // content_layout, - // cursor_position, - // &Rectangle { - // y: bounds.y + offset as f32, - // ..bounds - // }, - // ) - // }; + let bounds = layout.bounds(); + let content_layout = layout.children().next().unwrap(); + let content_bounds = content_layout.bounds(); + let offset = self.state.offset(bounds, content_bounds); + let scrollbar = renderer.scrollbar( + bounds, + content_bounds, + offset, + self.scrollbar_width, + self.scrollbar_margin, + self.scroller_width, + ); + + let is_mouse_over = bounds.contains(cursor_position); + let is_mouse_over_scrollbar = scrollbar + .as_ref() + .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) + .unwrap_or(false); + + let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { + Point::new(cursor_position.x, cursor_position.y + offset as f32) + } else { + Point::new(cursor_position.x, -1.0) + }; + + if let Some(scrollbar) = scrollbar { + renderer.with_layer(bounds, Vector::new(0, offset), |renderer| { + self.content.draw( + renderer, + defaults, + content_layout, + cursor_position, + &Rectangle { + y: bounds.y + offset as f32, + ..bounds + }, + ); + }); + + // TODO: Draw scroller + // let style = if self.state.is_scroller_grabbed() { + // style_sheet.dragging() + // } else if is_mouse_over_scrollbar { + // style_sheet.hovered() + // } else { + // style_sheet.active() + // }; + + // let is_scrollbar_visible = + // style.background.is_some() || style.border_width > 0.0; + + // if is_mouse_over + // || self.state.is_scroller_grabbed() + // || is_scrollbar_visible + // { + // // Primitive::Quad { + // // bounds: scrollbar.scroller.bounds, + // // background: Background::Color(style.scroller.color), + // // border_radius: style.scroller.border_radius, + // // border_width: style.scroller.border_width, + // // border_color: style.scroller.border_color, + // // } + // }; + + // TODO: Draw scrollbar + // if is_scrollbar_visible { + // Primitive::Quad { + // bounds: scrollbar.bounds, + // background: style + // .background + // .unwrap_or(Background::Color(Color::TRANSPARENT)), + // border_radius: style.border_radius, + // border_width: style.border_width, + // border_color: style.border_color, + // } + //} + } else { + self.content.draw( + renderer, + defaults, + content_layout, + cursor_position, + &Rectangle { + y: bounds.y + offset as f32, + ..bounds + }, + ); + } } fn hash_layout(&self, state: &mut Hasher) { From a4f4d831615899046d36c96e6a580d5386aa25bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 14:47:49 +0700 Subject: [PATCH 07/92] Introduce `fill_rectangle` to `Renderer` trait --- graphics/src/renderer.rs | 10 ++++++++++ native/src/renderer.rs | 13 ++++++++++++- native/src/renderer/null.rs | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 91f6b5506b..7ed061519e 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -70,6 +70,16 @@ where }); } + fn fill_rectangle(&mut self, quad: renderer::Quad) { + self.primitives.push(Primitive::Quad { + bounds: quad.bounds, + background: quad.background, + border_radius: quad.border_radius, + border_width: quad.border_width, + border_color: quad.border_color, + }); + } + fn clear(&mut self) { self.primitives.clear(); } diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 5a7a006766..382edb61dd 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -29,7 +29,7 @@ mod null; pub use null::Null; use crate::layout; -use crate::{Element, Rectangle, Vector}; +use crate::{Background, Color, Element, Rectangle, Vector}; /// A component that can take the state of a user interface and produce an /// output for its users. @@ -59,4 +59,15 @@ pub trait Renderer: Sized { ); fn clear(&mut self); + + fn fill_rectangle(&mut self, quad: Quad); +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Quad { + pub bounds: Rectangle, + pub background: Background, + pub border_radius: f32, + pub border_width: f32, + pub border_color: Color, } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index b8e01b80c0..1c026fde4e 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -37,6 +37,8 @@ impl Renderer for Null { } fn clear(&mut self) {} + + fn fill_rectangle(&mut self, _quad: renderer::Quad) {} } impl renderer::Text for Null { From 54a9a232f8110364972a8eef966b7b7477573f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 14:48:33 +0700 Subject: [PATCH 08/92] Draw scrollbar in `Widget::draw` for `Scrollable` --- examples/scrollable/src/main.rs | 2 +- examples/scrollable/src/style.rs | 4 +- examples/styling/src/main.rs | 6 +- graphics/src/widget/scrollable.rs | 64 +-------- native/Cargo.toml | 4 + native/src/overlay/menu.rs | 4 +- native/src/renderer/null.rs | 16 --- native/src/widget/pick_list.rs | 3 +- native/src/widget/scrollable.rs | 207 ++++++++++++++++-------------- style/src/scrollable.rs | 13 +- web/src/widget/scrollable.rs | 11 +- 11 files changed, 135 insertions(+), 199 deletions(-) diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 3416b83ded..272f0ce290 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -95,7 +95,7 @@ impl Sandbox for ScrollableDemo { .on_scroll(move |offset| { Message::Scrolled(i, offset) }) - .style(*theme) + .style(theme.clone().into()) .push(Text::new(variant.title)) .push( Button::new( diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs index ae449141dc..d955f52d41 100644 --- a/examples/scrollable/src/style.rs +++ b/examples/scrollable/src/style.rs @@ -34,11 +34,11 @@ impl From for Box { } } -impl From for Box { +impl From for &'static dyn scrollable::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), + Theme::Dark => &dark::Scrollable, } } } diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 81d33ad32d..1746d7b4eb 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -98,7 +98,7 @@ impl Sandbox for Styling { let scrollable = Scrollable::new(&mut self.scroll) .width(Length::Fill) .height(Length::Units(100)) - .style(self.theme) + .style(self.theme.into()) .push(Text::new("Scroll me!")) .push(Space::with_height(Length::Units(800))) .push(Text::new("You did it!")); @@ -212,11 +212,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn scrollable::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), + Theme::Dark => &dark::Scrollable, } } } diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index f1fe0d2de2..61eae5872b 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -1,7 +1,5 @@ //! Navigate an endless amount of content with a scrollbar. -use crate::{Backend, Renderer}; -use iced_native::scrollable; -use iced_native::Rectangle; +use crate::Renderer; pub use iced_native::scrollable::State; pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; @@ -13,63 +11,3 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; /// `Renderer`. pub type Scrollable<'a, Message, Backend> = iced_native::Scrollable<'a, Message, Renderer>; - -impl scrollable::Renderer for Renderer -where - B: Backend, -{ - type Style = Box; - - fn scrollbar( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - scrollbar_width: u16, - scrollbar_margin: u16, - scroller_width: u16, - ) -> Option { - if content_bounds.height > bounds.height { - let outer_width = - scrollbar_width.max(scroller_width) + 2 * scrollbar_margin; - - let outer_bounds = Rectangle { - x: bounds.x + bounds.width - outer_width as f32, - y: bounds.y, - width: outer_width as f32, - height: bounds.height, - }; - - let scrollbar_bounds = Rectangle { - x: bounds.x + bounds.width - - f32::from(outer_width / 2 + scrollbar_width / 2), - y: bounds.y, - width: scrollbar_width as f32, - height: bounds.height, - }; - - let ratio = bounds.height / content_bounds.height; - let scroller_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; - - let scroller_bounds = Rectangle { - x: bounds.x + bounds.width - - f32::from(outer_width / 2 + scroller_width / 2), - y: scrollbar_bounds.y + y_offset, - width: scroller_width as f32, - height: scroller_height, - }; - - Some(scrollable::Scrollbar { - outer_bounds, - bounds: scrollbar_bounds, - margin: scrollbar_margin, - scroller: scrollable::Scroller { - bounds: scroller_bounds, - }, - }) - } else { - None - } - } -} diff --git a/native/Cargo.toml b/native/Cargo.toml index a3134ef4d4..5de99b2ecf 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -23,3 +23,7 @@ path = "../core" version = "0.3" path = "../futures" features = ["thread-pool"] + +[dependencies.iced_style] +version = "0.3" +path = "../style" diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index f90a9f7bf0..c4f64f8550 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -395,9 +395,7 @@ where /// able to use a [`Menu`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: - scrollable::Renderer + container::Renderer + text::Renderer -{ +pub trait Renderer: container::Renderer + text::Renderer { /// The [`Menu`] style supported by this renderer. type Style: Default + Clone; } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 1c026fde4e..960a5727b8 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -75,22 +75,6 @@ impl text::Renderer for Null { } } -impl scrollable::Renderer for Null { - type Style = (); - - fn scrollbar( - &self, - _bounds: Rectangle, - _content_bounds: Rectangle, - _offset: u32, - _scrollbar_width: u16, - _scrollbar_margin: u16, - _scroller_width: u16, - ) -> Option { - None - } -} - impl text_input::Renderer for Null { type Style = (); diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 7154a57247..73b01b0115 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -5,7 +5,6 @@ use crate::layout; use crate::mouse; use crate::overlay; use crate::overlay::menu::{self, Menu}; -use crate::scrollable; use crate::text; use crate::touch; use crate::{ @@ -145,7 +144,7 @@ where T: Clone + ToString + Eq, [T]: ToOwned>, Message: 'static, - Renderer: self::Renderer + scrollable::Renderer + 'a, + Renderer: self::Renderer + 'a, { fn width(&self) -> Length { self.width diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index ba3d3dd629..9835792843 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -3,18 +3,21 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; +use crate::renderer; use crate::touch; use crate::{ - Alignment, Clipboard, Column, Element, Hasher, Layout, Length, Padding, - Point, Rectangle, Size, Vector, Widget, + Alignment, Background, Clipboard, Color, Column, Element, Hasher, Layout, + Length, Padding, Point, Rectangle, Size, Vector, Widget, }; use std::{f32, hash::Hash, u32}; +pub use iced_style::scrollable::StyleSheet; + /// A widget that can vertically display an infinite amount of content with a /// scrollbar. #[allow(missing_debug_implementations)] -pub struct Scrollable<'a, Message, Renderer: self::Renderer> { +pub struct Scrollable<'a, Message, Renderer> { state: &'a mut State, height: Length, max_height: u32, @@ -23,10 +26,10 @@ pub struct Scrollable<'a, Message, Renderer: self::Renderer> { scroller_width: u16, content: Column<'a, Message, Renderer>, on_scroll: Option Message>>, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } -impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { +impl<'a, Message, Renderer: crate::Renderer> Scrollable<'a, Message, Renderer> { /// Creates a new [`Scrollable`] with the given [`State`]. pub fn new(state: &'a mut State) -> Self { Scrollable { @@ -38,7 +41,7 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { scroller_width: 10, content: Column::new(), on_scroll: None, - style: Renderer::Style::default(), + style_sheet: Default::default(), } } @@ -119,8 +122,11 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { } /// Sets the style of the [`Scrollable`] . - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style<'b>(mut self, style_sheet: &'b dyn StyleSheet) -> Self + where + 'b: 'a, + { + self.style_sheet = style_sheet; self } @@ -150,12 +156,63 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { )); } } + + fn scrollbar( + &self, + bounds: Rectangle, + content_bounds: Rectangle, + ) -> Option { + let offset = self.state.offset(bounds, content_bounds); + + if content_bounds.height > bounds.height { + let outer_width = self.scrollbar_width.max(self.scroller_width) + + 2 * self.scrollbar_margin; + + let outer_bounds = Rectangle { + x: bounds.x + bounds.width - outer_width as f32, + y: bounds.y, + width: outer_width as f32, + height: bounds.height, + }; + + let scrollbar_bounds = Rectangle { + x: bounds.x + bounds.width + - f32::from(outer_width / 2 + self.scrollbar_width / 2), + y: bounds.y, + width: self.scrollbar_width as f32, + height: bounds.height, + }; + + let ratio = bounds.height / content_bounds.height; + let scroller_height = bounds.height * ratio; + let y_offset = offset as f32 * ratio; + + let scroller_bounds = Rectangle { + x: bounds.x + bounds.width + - f32::from(outer_width / 2 + self.scroller_width / 2), + y: scrollbar_bounds.y + y_offset, + width: self.scroller_width as f32, + height: scroller_height, + }; + + Some(Scrollbar { + outer_bounds, + bounds: scrollbar_bounds, + margin: self.scrollbar_margin, + scroller: Scroller { + bounds: scroller_bounds, + }, + }) + } else { + None + } + } } impl<'a, Message, Renderer> Widget for Scrollable<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { Widget::::width(&self.content) @@ -201,15 +258,7 @@ where let content = layout.children().next().unwrap(); let content_bounds = content.bounds(); - let offset = self.state.offset(bounds, content_bounds); - let scrollbar = renderer.scrollbar( - bounds, - content_bounds, - offset, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - ); + let scrollbar = self.scrollbar(bounds, content_bounds); let is_mouse_over_scrollbar = scrollbar .as_ref() .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) @@ -385,14 +434,7 @@ where let content_layout = layout.children().next().unwrap(); let content_bounds = content_layout.bounds(); let offset = self.state.offset(bounds, content_bounds); - let scrollbar = renderer.scrollbar( - bounds, - content_bounds, - offset, - self.scrollbar_width, - self.scrollbar_margin, - self.scroller_width, - ); + let scrollbar = self.scrollbar(bounds, content_bounds); let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over_scrollbar = scrollbar @@ -420,43 +462,43 @@ where ); }); - // TODO: Draw scroller - // let style = if self.state.is_scroller_grabbed() { - // style_sheet.dragging() - // } else if is_mouse_over_scrollbar { - // style_sheet.hovered() - // } else { - // style_sheet.active() - // }; - - // let is_scrollbar_visible = - // style.background.is_some() || style.border_width > 0.0; - - // if is_mouse_over - // || self.state.is_scroller_grabbed() - // || is_scrollbar_visible - // { - // // Primitive::Quad { - // // bounds: scrollbar.scroller.bounds, - // // background: Background::Color(style.scroller.color), - // // border_radius: style.scroller.border_radius, - // // border_width: style.scroller.border_width, - // // border_color: style.scroller.border_color, - // // } - // }; - - // TODO: Draw scrollbar - // if is_scrollbar_visible { - // Primitive::Quad { - // bounds: scrollbar.bounds, - // background: style - // .background - // .unwrap_or(Background::Color(Color::TRANSPARENT)), - // border_radius: style.border_radius, - // border_width: style.border_width, - // border_color: style.border_color, - // } - //} + let style = if self.state.is_scroller_grabbed() { + self.style_sheet.dragging() + } else if is_mouse_over_scrollbar { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + let is_scrollbar_visible = + style.background.is_some() || style.border_width > 0.0; + + renderer.with_layer(bounds, Vector::new(0, 0), |renderer| { + if is_scrollbar_visible { + renderer.fill_rectangle(renderer::Quad { + bounds: scrollbar.bounds, + background: style + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + } + + if is_mouse_over + || self.state.is_scroller_grabbed() + || is_scrollbar_visible + { + renderer.fill_rectangle(renderer::Quad { + bounds: scrollbar.scroller.bounds, + background: Background::Color(style.scroller.color), + border_radius: style.scroller.border_radius, + border_width: style.scroller.border_width, + border_color: style.scroller.border_color, + }); + } + }); } else { self.content.draw( renderer, @@ -614,19 +656,19 @@ impl State { /// The scrollbar of a [`Scrollable`]. #[derive(Debug)] -pub struct Scrollbar { +struct Scrollbar { /// The outer bounds of the scrollable, including the [`Scrollbar`] and /// [`Scroller`]. - pub outer_bounds: Rectangle, + outer_bounds: Rectangle, /// The bounds of the [`Scrollbar`]. - pub bounds: Rectangle, + bounds: Rectangle, /// The margin within the [`Scrollbar`]. - pub margin: u16, + margin: u16, /// The bounds of the [`Scroller`]. - pub scroller: Scroller, + scroller: Scroller, } impl Scrollbar { @@ -661,38 +703,15 @@ impl Scrollbar { /// The handle of a [`Scrollbar`]. #[derive(Debug, Clone, Copy)] -pub struct Scroller { +struct Scroller { /// The bounds of the [`Scroller`]. - pub bounds: Rectangle, -} - -/// The renderer of a [`Scrollable`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Scrollable`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// The style supported by this renderer. - type Style: Default; - - /// Returns the [`Scrollbar`] given the bounds and content bounds of a - /// [`Scrollable`]. - fn scrollbar( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - scrollbar_width: u16, - scrollbar_margin: u16, - scroller_width: u16, - ) -> Option; + bounds: Rectangle, } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, Message: 'a, { fn from( diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index 65da980380..741d9d39c5 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -60,17 +60,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index 847bf5a045..595eb26efb 100644 --- a/web/src/widget/scrollable.rs +++ b/web/src/widget/scrollable.rs @@ -14,7 +14,7 @@ pub struct Scrollable<'a, Message> { max_height: u32, content: Column<'a, Message>, #[allow(dead_code)] - style: Box, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message> Scrollable<'a, Message> { @@ -27,7 +27,7 @@ impl<'a, Message> Scrollable<'a, Message> { height: Length::Shrink, max_height: u32::MAX, content: Column::new(), - style: Default::default(), + style_sheet: Default::default(), } } @@ -78,8 +78,11 @@ impl<'a, Message> Scrollable<'a, Message> { } /// Sets the style of the [`Scrollable`] . - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + pub fn style<'b>(mut self, style_sheet: &'b dyn StyleSheet) -> Self + where + 'b: 'a, + { + self.style_sheet = style_sheet; self } From edea093350e1b576e2b7db50c525e7fa5c3bea9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 15:19:04 +0700 Subject: [PATCH 09/92] Move `Defaults` from `iced_graphics` to `iced_native` --- examples/custom_widget/src/main.rs | 5 +- examples/geometry/src/main.rs | 13 ++-- graphics/src/defaults.rs | 32 --------- graphics/src/lib.rs | 4 +- graphics/src/renderer.rs | 12 ++-- graphics/src/widget/canvas.rs | 6 +- graphics/src/widget/qr_code.rs | 9 +-- native/src/element.rs | 11 +-- native/src/overlay.rs | 3 +- native/src/overlay/element.rs | 11 ++- native/src/overlay/menu.rs | 5 +- native/src/renderer.rs | 20 ++++-- native/src/renderer/null.rs | 3 - native/src/renderer/text.rs | 4 +- native/src/user_interface.rs | 7 +- native/src/widget.rs | 3 +- native/src/widget/button.rs | 3 +- native/src/widget/checkbox.rs | 8 +-- native/src/widget/column.rs | 5 +- native/src/widget/container.rs | 5 +- native/src/widget/image.rs | 3 +- native/src/widget/image/viewer.rs | 3 +- native/src/widget/pane_grid.rs | 3 +- native/src/widget/pane_grid/content.rs | 3 +- native/src/widget/pane_grid/title_bar.rs | 3 +- native/src/widget/pick_list.rs | 3 +- native/src/widget/progress_bar.rs | 8 +-- native/src/widget/radio.rs | 8 +-- native/src/widget/row.rs | 5 +- native/src/widget/rule.rs | 9 ++- native/src/widget/scrollable.rs | 6 +- native/src/widget/slider.rs | 6 +- native/src/widget/space.rs | 10 +-- native/src/widget/svg.rs | 11 ++- native/src/widget/text.rs | 6 +- native/src/widget/text_input.rs | 85 ++++++++++-------------- native/src/widget/toggler.rs | 3 +- native/src/widget/tooltip.rs | 10 +-- wgpu/src/lib.rs | 4 +- 39 files changed, 166 insertions(+), 192 deletions(-) delete mode 100644 graphics/src/defaults.rs diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 648e729548..c8188f105e 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -9,7 +9,8 @@ mod circle { // Of course, you can choose to make the implementation renderer-agnostic, // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. - use iced_graphics::{Backend, Defaults, Primitive, Renderer}; + use iced_graphics::renderer::{self, Renderer}; + use iced_graphics::Backend; use iced_native::{ layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; @@ -53,7 +54,7 @@ mod circle { fn draw( &self, _renderer: &mut Renderer, - _defaults: &Defaults, + _style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 0745739a43..de157267b2 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -10,13 +10,11 @@ mod rainbow { // Of course, you can choose to make the implementation renderer-agnostic, // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. - use iced_graphics::{ - triangle::{Mesh2D, Vertex2D}, - Backend, Defaults, Primitive, Renderer, - }; + use iced_graphics::renderer::{self, Renderer}; + use iced_graphics::Backend; + use iced_native::{ - layout, mouse, Element, Hasher, Layout, Length, Point, Rectangle, Size, - Vector, Widget, + layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; pub struct Rainbow; @@ -54,11 +52,12 @@ mod rainbow { fn draw( &self, _renderer: &mut Renderer, - _defaults: &Defaults, + _style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { + // use iced_graphics::triangle::{Mesh2D, Vertex2D}; // let b = layout.bounds(); // // R O Y G B I V diff --git a/graphics/src/defaults.rs b/graphics/src/defaults.rs deleted file mode 100644 index 11718a8717..0000000000 --- a/graphics/src/defaults.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Use default styling attributes to inherit styles. -use iced_native::Color; - -/// Some default styling attributes. -#[derive(Debug, Clone, Copy)] -pub struct Defaults { - /// Text styling - pub text: Text, -} - -impl Default for Defaults { - fn default() -> Defaults { - Defaults { - text: Text::default(), - } - } -} - -/// Some default text styling attributes. -#[derive(Debug, Clone, Copy)] -pub struct Text { - /// The default color of text - pub color: Color, -} - -impl Default for Text { - fn default() -> Text { - Text { - color: Color::BLACK, - } - } -} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 9c113da61f..dbd94e9910 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -13,15 +13,14 @@ mod antialiasing; mod error; mod primitive; -mod renderer; mod transformation; mod viewport; pub mod backend; -pub mod defaults; pub mod font; pub mod layer; pub mod overlay; +pub mod renderer; pub mod triangle; pub mod widget; pub mod window; @@ -31,7 +30,6 @@ pub use widget::*; pub use antialiasing::Antialiasing; pub use backend::Backend; -pub use defaults::Defaults; pub use error::Error; pub use layer::Layer; pub use primitive::Primitive; diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 7ed061519e..8d623868c7 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -1,8 +1,10 @@ use crate::backend::{self, Backend}; -use crate::{Defaults, Primitive, Vector}; +use crate::{Primitive, Vector}; use iced_native::layout; use iced_native::renderer; -use iced_native::{Color, Element, Font, Rectangle}; +use iced_native::{Element, Font, Rectangle}; + +pub use iced_native::renderer::Style; /// A backend-agnostic renderer that supports all the built-in widgets. #[derive(Debug)] @@ -33,8 +35,6 @@ impl iced_native::Renderer for Renderer where B: Backend, { - type Defaults = Defaults; - fn layout<'a, Message>( &mut self, element: &Element<'a, Message, Self>, @@ -97,8 +97,8 @@ where self.primitives.push(Primitive::Text { content: text.content.to_string(), bounds: text.bounds, - size: text.size.unwrap_or(f32::from(self.backend.default_size())), - color: text.color.unwrap_or(Color::BLACK), + size: text.size, + color: text.color, font: text.font, horizontal_alignment: text.horizontal_alignment, vertical_alignment: text.vertical_alignment, diff --git a/graphics/src/widget/canvas.rs b/graphics/src/widget/canvas.rs index 7bf00ca521..3990c2b9c0 100644 --- a/graphics/src/widget/canvas.rs +++ b/graphics/src/widget/canvas.rs @@ -3,7 +3,9 @@ //! A [`Canvas`] widget can be used to draw different kinds of 2D shapes in a //! [`Frame`]. It can be used for animation, data visualization, game graphics, //! and more! -use crate::{Backend, Defaults, Renderer}; +use crate::renderer::{self, Renderer}; +use crate::Backend; + use iced_native::layout; use iced_native::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, @@ -187,7 +189,7 @@ where fn draw( &self, _renderer: &mut Renderer, - _defaults: &Defaults, + _style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/graphics/src/widget/qr_code.rs b/graphics/src/widget/qr_code.rs index a809d99fa0..364b636ba8 100644 --- a/graphics/src/widget/qr_code.rs +++ b/graphics/src/widget/qr_code.rs @@ -1,10 +1,11 @@ //! Encode and display information in a QR code. use crate::canvas; -use crate::{Backend, Defaults, Renderer}; +use crate::renderer::{self, Renderer}; +use crate::Backend; +use iced_native::layout; use iced_native::{ - layout, Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, - Widget, + Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; use thiserror::Error; @@ -81,7 +82,7 @@ where fn draw( &self, _renderer: &mut Renderer, - _defaults: &Defaults, + _style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/element.rs b/native/src/element.rs index 35e1b4f4b8..3ecd8e2648 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -1,6 +1,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{ Clipboard, Color, Hasher, Layout, Length, Point, Rectangle, Widget, }; @@ -241,13 +242,13 @@ where pub fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.widget - .draw(renderer, defaults, layout, cursor_position, viewport) + .draw(renderer, style, layout, cursor_position, viewport) } /// Computes the _layout_ hash of the [`Element`]. @@ -336,13 +337,13 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.widget - .draw(renderer, defaults, layout, cursor_position, viewport) + .draw(renderer, style, layout, cursor_position, viewport) } fn hash_layout(&self, state: &mut Hasher) { @@ -418,7 +419,7 @@ where fn draw( &self, _renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 7054ee693c..25b49adf3f 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -8,6 +8,7 @@ pub use menu::Menu; use crate::event::{self, Event}; use crate::layout; +use crate::renderer; use crate::{Clipboard, Hasher, Layout, Point, Size}; /// An interactive component that can be displayed on top of other widgets. @@ -32,7 +33,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ); diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index 081b62cec4..bf3fb3f0bd 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -2,6 +2,7 @@ pub use crate::Overlay; use crate::event::{self, Event}; use crate::layout; +use crate::renderer; use crate::{Clipboard, Hasher, Layout, Point, Size, Vector}; /// A generic [`Overlay`]. @@ -71,12 +72,11 @@ where pub fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ) { - self.overlay - .draw(renderer, defaults, layout, cursor_position) + self.overlay.draw(renderer, style, layout, cursor_position) } /// Computes the _layout_ hash of the [`Element`]. @@ -142,12 +142,11 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ) { - self.content - .draw(renderer, defaults, layout, cursor_position) + self.content.draw(renderer, style, layout, cursor_position) } fn hash_layout(&self, state: &mut Hasher, position: Point) { diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index c4f64f8550..f2367ae7d4 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -4,6 +4,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; +use crate::renderer; use crate::scrollable; use crate::text; use crate::touch; @@ -236,7 +237,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, ) { @@ -380,7 +381,7 @@ where fn draw( &self, _renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + _style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 382edb61dd..e48c701d0e 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -34,11 +34,6 @@ use crate::{Background, Color, Element, Rectangle, Vector}; /// A component that can take the state of a user interface and produce an /// output for its users. pub trait Renderer: Sized { - /// The default styling attributes of the [`Renderer`]. - /// - /// This type can be leveraged to implement style inheritance. - type Defaults: Default; - /// Lays out the elements of a user interface. /// /// You should override this if you need to perform any operations before or @@ -71,3 +66,18 @@ pub struct Quad { pub border_width: f32, pub border_color: Color, } + +/// The styling attributes of a [`Renderer`]. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Style { + /// The text color + pub text_color: Color, +} + +impl Default for Style { + fn default() -> Self { + Style { + text_color: Color::BLACK, + } + } +} diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 960a5727b8..268248fde4 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -5,7 +5,6 @@ use crate::pane_grid; use crate::progress_bar; use crate::radio; use crate::renderer::{self, Renderer}; -use crate::scrollable; use crate::slider; use crate::text; use crate::text_input; @@ -26,8 +25,6 @@ impl Null { } impl Renderer for Null { - type Defaults = (); - fn with_layer( &mut self, _bounds: Rectangle, diff --git a/native/src/renderer/text.rs b/native/src/renderer/text.rs index 5c189d89ce..9234c5874e 100644 --- a/native/src/renderer/text.rs +++ b/native/src/renderer/text.rs @@ -12,8 +12,8 @@ pub trait Text: Renderer { pub struct Section<'a, Font> { pub content: &'a str, pub bounds: Rectangle, - pub size: Option, - pub color: Option, + pub size: f32, + pub color: Color, pub font: Font, pub horizontal_alignment: alignment::Horizontal, pub vertical_alignment: alignment::Vertical, diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index eb57670d49..2f76c08499 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,6 +1,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{Clipboard, Element, Layout, Point, Rectangle, Size, Vector}; use std::hash::Hasher; @@ -353,7 +354,7 @@ where |renderer| { overlay.draw( renderer, - &Renderer::Defaults::default(), + &renderer::Style::default(), Layout::new(&layer.layout), cursor_position, ); @@ -376,7 +377,7 @@ where self.root.widget.draw( renderer, - &Renderer::Defaults::default(), + &renderer::Style::default(), Layout::new(&self.base.layout), base_cursor, &viewport, @@ -384,7 +385,7 @@ where } else { self.root.widget.draw( renderer, - &Renderer::Defaults::default(), + &renderer::Style::default(), Layout::new(&self.base.layout), cursor_position, &viewport, diff --git a/native/src/widget.rs b/native/src/widget.rs index 01c5bed390..9a494771cb 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -81,6 +81,7 @@ pub use tooltip::Tooltip; use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{Clipboard, Hasher, Layout, Length, Point, Rectangle}; /// A component that displays information and allows interaction. @@ -131,7 +132,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 1c7f80ef27..2aeffd034d 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -5,6 +5,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; +use crate::renderer; use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, @@ -244,7 +245,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 579e4ee39d..52113322fb 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -1,15 +1,15 @@ //! Show toggle controls using checkboxes. use std::hash::Hash; -use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; use crate::mouse; +use crate::renderer; use crate::text; use crate::touch; use crate::{ - Clipboard, Color, Element, Hasher, Layout, Length, Point, Rectangle, Row, - Text, Widget, + Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, + Rectangle, Row, Text, Widget, }; /// A box that can be checked. @@ -182,7 +182,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 7cf6d34522..e7ce004172 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{ Alignment, Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, Widget, @@ -165,13 +166,13 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { for (child, layout) in self.children.iter().zip(layout.children()) { - child.draw(renderer, defaults, layout, cursor_position, viewport); + child.draw(renderer, style, layout, cursor_position, viewport); } } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index af40f2abd3..92869873c1 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -5,6 +5,7 @@ use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{ Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, Widget, @@ -175,14 +176,14 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { self.content.draw( renderer, - defaults, + style, layout.children().next().unwrap(), cursor_position, viewport, diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 1607a5960e..b6b8dc1f6d 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -3,6 +3,7 @@ pub mod viewer; pub use viewer::Viewer; use crate::layout; +use crate::renderer; use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; use std::{ @@ -92,7 +93,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + _style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs index d483ed617a..63087f5f99 100644 --- a/native/src/widget/image/viewer.rs +++ b/native/src/widget/image/viewer.rs @@ -3,6 +3,7 @@ use crate::event::{self, Event}; use crate::image; use crate::layout; use crate::mouse; +use crate::renderer; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, Widget, @@ -283,7 +284,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index b4a8cdb139..1e63fdeb2a 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -32,6 +32,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::overlay; +use crate::renderer; use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, @@ -475,7 +476,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index e94955f7d9..22b3a31053 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -3,6 +3,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; use crate::pane_grid::{self, TitleBar}; +use crate::renderer; use crate::{Clipboard, Element, Hasher, Layout, Point, Rectangle, Size}; /// The content of a [`Pane`]. @@ -57,7 +58,7 @@ where pub fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 9b0b7c7836..ce9249d260 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -3,6 +3,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; use crate::pane_grid; +use crate::renderer; use crate::{ Clipboard, Element, Hasher, Layout, Padding, Point, Rectangle, Size, }; @@ -85,7 +86,7 @@ where pub fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 73b01b0115..ed688a614e 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -5,6 +5,7 @@ use crate::layout; use crate::mouse; use crate::overlay; use crate::overlay::menu::{self, Menu}; +use crate::renderer; use crate::text; use crate::touch; use crate::{ @@ -322,7 +323,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index d8cf53769b..98dd9989cb 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -1,7 +1,7 @@ //! Provide progress feedback to your users. -use crate::{ - layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, -}; +use crate::layout; +use crate::renderer; +use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; use std::{hash::Hash, ops::RangeInclusive}; @@ -93,7 +93,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index c6955079e8..ba9bd9aa9f 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -1,15 +1,15 @@ //! Create choices using radio buttons. use std::hash::Hash; -use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; use crate::mouse; +use crate::renderer; use crate::text; use crate::touch; use crate::{ - Clipboard, Color, Element, Hasher, Layout, Length, Point, Rectangle, Row, - Text, Widget, + Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, + Rectangle, Row, Text, Widget, }; /// A circular button representing a choice. @@ -194,7 +194,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 7eb5ab5556..af63c7da34 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -2,6 +2,7 @@ use crate::event::{self, Event}; use crate::layout; use crate::overlay; +use crate::renderer; use crate::{ Alignment, Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, Widget, @@ -164,13 +165,13 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { for (child, layout) in self.children.iter().zip(layout.children()) { - child.draw(renderer, defaults, layout, cursor_position, viewport); + child.draw(renderer, style, layout, cursor_position, viewport); } } diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs index 1fab77bcf1..24c4a51ad2 100644 --- a/native/src/widget/rule.rs +++ b/native/src/widget/rule.rs @@ -1,11 +1,10 @@ //! Display a horizontal or vertical rule for dividing content. +use crate::layout; +use crate::renderer; +use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; use std::hash::Hash; -use crate::{ - layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, -}; - /// Display a horizontal or vertical rule for dividing content. #[derive(Debug, Copy, Clone)] pub struct Rule { @@ -68,7 +67,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 9835792843..63da539f52 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -425,7 +425,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, @@ -452,7 +452,7 @@ where renderer.with_layer(bounds, Vector::new(0, offset), |renderer| { self.content.draw( renderer, - defaults, + style, content_layout, cursor_position, &Rectangle { @@ -502,7 +502,7 @@ where } else { self.content.draw( renderer, - defaults, + style, content_layout, cursor_position, &Rectangle { diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index bcf811fee3..7bdb72af5f 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -4,12 +4,14 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; +use crate::renderer; use crate::touch; use crate::{ Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, }; -use std::{hash::Hash, ops::RangeInclusive}; +use std::hash::Hash; +use std::ops::RangeInclusive; /// An horizontal bar and a handle that selects a single value from a range of /// values. @@ -246,7 +248,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/space.rs b/native/src/widget/space.rs index 9a70dab14f..1bf8cd68a8 100644 --- a/native/src/widget/space.rs +++ b/native/src/widget/space.rs @@ -1,9 +1,9 @@ //! Distribute content vertically. -use std::hash::Hash; +use crate::layout; +use crate::renderer; +use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; -use crate::{ - layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, -}; +use std::hash::Hash; /// An amount of empty space. /// @@ -62,7 +62,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/svg.rs b/native/src/widget/svg.rs index 737dd8d40c..cce560cbd0 100644 --- a/native/src/widget/svg.rs +++ b/native/src/widget/svg.rs @@ -1,12 +1,11 @@ //! Display vector graphics in your application. use crate::layout; +use crate::renderer; use crate::{Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget}; -use std::{ - hash::{Hash, Hasher as _}, - path::PathBuf, - sync::Arc, -}; +use std::hash::{Hash, Hasher as _}; +use std::path::PathBuf; +use std::sync::Arc; /// A vector graphics image. /// @@ -90,7 +89,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index ea1ba7acdf..a2438d9c99 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -135,7 +135,7 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, @@ -156,9 +156,9 @@ where renderer.fill_text(renderer::text::Section { content: &self.content, - size: self.size.map(f32::from), + size: f32::from(self.size.unwrap_or(renderer.default_size())), bounds: Rectangle { x, y, ..bounds }, - color: self.color, + color: self.color.unwrap_or(style.text_color), font: self.font, horizontal_alignment: self.horizontal_alignment, vertical_alignment: self.vertical_alignment, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index fe95eb955b..93af04d160 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -15,6 +15,7 @@ use crate::event::{self, Event}; use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; +use crate::renderer; use crate::text; use crate::touch; use crate::{ @@ -158,54 +159,6 @@ where } } -impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> -where - Renderer: self::Renderer, -{ - /// Draws the [`TextInput`] with the given [`Renderer`], overriding its - /// [`Value`] if provided. - pub fn draw( - &self, - renderer: &mut Renderer, - layout: Layout<'_>, - cursor_position: Point, - value: Option<&Value>, - ) { - // TODO - // let value = value.unwrap_or(&self.value); - // let bounds = layout.bounds(); - // let text_bounds = layout.children().next().unwrap().bounds(); - - // if self.is_secure { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // &value.secure(), - // &self.state, - // &self.style, - // ) - // } else { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // value, - // &self.state, - // &self.style, - // ) - // } - } -} - impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where @@ -627,12 +580,44 @@ where fn draw( &self, renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { - self.draw(renderer, layout, cursor_position, None) + + // TODO + // let value = value.unwrap_or(&self.value); + // let bounds = layout.bounds(); + // let text_bounds = layout.children().next().unwrap().bounds(); + + // if self.is_secure { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // &value.secure(), + // &self.state, + // &self.style, + // ) + // } else { + // self::Renderer::draw( + // renderer, + // bounds, + // text_bounds, + // cursor_position, + // self.font, + // self.size.unwrap_or(renderer.default_size()), + // &self.placeholder, + // value, + // &self.state, + // &self.style, + // ) + // } } fn hash_layout(&self, state: &mut Hasher) { diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 4a1f1f5d7b..6c7fa97bb6 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -5,6 +5,7 @@ use crate::alignment; use crate::event; use crate::layout; use crate::mouse; +use crate::renderer; use crate::text; use crate::{ Alignment, Clipboard, Element, Event, Hasher, Layout, Length, Point, @@ -185,7 +186,7 @@ where fn draw( &self, renderer: &mut Renderer, - defaults: &Renderer::Defaults, + style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 83d04675b0..496cd41ef5 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -3,12 +3,12 @@ use std::hash::Hash; use iced_core::Rectangle; +use crate::event; +use crate::layout; +use crate::renderer; use crate::widget::container; use crate::widget::text::{self, Text}; -use crate::{ - event, layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, - Widget, -}; +use crate::{Clipboard, Element, Event, Hasher, Layout, Length, Point, Widget}; /// An element to display a widget over another. #[allow(missing_debug_implementations)] @@ -137,7 +137,7 @@ where fn draw( &self, _renderer: &mut Renderer, - _defaults: &Renderer::Defaults, + style: &renderer::Style, _layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index e868a65515..ed99b12693 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -36,9 +36,7 @@ mod backend; mod quad; mod text; -pub use iced_graphics::{ - Antialiasing, Color, Defaults, Error, Primitive, Viewport, -}; +pub use iced_graphics::{Antialiasing, Color, Error, Primitive, Viewport}; pub use wgpu; pub use backend::Backend; From d61cb58d92b6fcd520f665deb093f3747ffd5e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 15:36:32 +0700 Subject: [PATCH 10/92] Wire up `container` styling to `iced_native` --- examples/game_of_life/src/main.rs | 2 +- examples/pane_grid/src/main.rs | 34 +++++++++++++++--------- examples/scrollable/src/main.rs | 4 +-- examples/scrollable/src/style.rs | 4 +-- examples/styling/src/main.rs | 6 ++--- examples/tooltip/src/main.rs | 2 +- graphics/src/widget/container.rs | 10 +------ native/src/overlay/menu.rs | 3 +-- native/src/renderer/null.rs | 5 ---- native/src/widget/container.rs | 29 +++++++------------- native/src/widget/pane_grid.rs | 5 ++-- native/src/widget/pane_grid/content.rs | 11 +++----- native/src/widget/pane_grid/title_bar.rs | 9 +++---- native/src/widget/scrollable.rs | 5 +--- native/src/widget/tooltip.rs | 16 +++++------ style/src/container.rs | 13 ++------- web/src/widget/container.rs | 6 ++--- 17 files changed, 65 insertions(+), 99 deletions(-) diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 5011261801..ee425f4491 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -150,7 +150,7 @@ impl Application for GameOfLife { Container::new(content) .width(Length::Fill) .height(Length::Fill) - .style(style::Container) + .style(&style::Container) .into() } } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 69872bad7d..844b604dcf 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -177,7 +177,11 @@ impl Application for Example { let title_bar = pane_grid::TitleBar::new(title) .controls(pane.controls.view(id, total_panes, pane.is_pinned)) .padding(10) - .style(style::TitleBar { is_focused }); + .style(if is_focused { + &style::TitleBar::Focused + } else { + &style::TitleBar::Active + }); pane_grid::Content::new(pane.content.view( id, @@ -185,7 +189,11 @@ impl Application for Example { pane.is_pinned, )) .title_bar(title_bar) - .style(style::Pane { is_focused }) + .style(if is_focused { + &style::Pane::Focused + } else { + &style::Pane::Active + }) }) .width(Length::Fill) .height(Length::Fill) @@ -387,14 +395,16 @@ mod style { 0xC4 as f32 / 255.0, ); - pub struct TitleBar { - pub is_focused: bool, + pub enum TitleBar { + Active, + Focused, } impl container::StyleSheet for TitleBar { fn style(&self) -> container::Style { - let pane = Pane { - is_focused: self.is_focused, + let pane = match self { + Self::Active => Pane::Active, + Self::Focused => Pane::Focused, } .style(); @@ -406,8 +416,9 @@ mod style { } } - pub struct Pane { - pub is_focused: bool, + pub enum Pane { + Active, + Focused, } impl container::StyleSheet for Pane { @@ -415,10 +426,9 @@ mod style { container::Style { background: Some(Background::Color(SURFACE)), border_width: 2.0, - border_color: if self.is_focused { - Color::BLACK - } else { - Color::from_rgb(0.7, 0.7, 0.7) + border_color: match self { + Self::Active => Color::from_rgb(0.7, 0.7, 0.7), + Self::Focused => Color::BLACK, }, ..Default::default() } diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 272f0ce290..bc20d428c4 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -164,7 +164,7 @@ impl Sandbox for ScrollableDemo { Container::new(scrollable) .width(Length::Fill) .height(Length::Fill) - .style(*theme), + .style(theme.clone().into()), ) .push(ProgressBar::new( 0.0..=1.0, @@ -190,7 +190,7 @@ impl Sandbox for ScrollableDemo { .height(Length::Fill) .center_x() .center_y() - .style(self.theme) + .style(self.theme.into()) .into() } } diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs index d955f52d41..66f3b9d3e3 100644 --- a/examples/scrollable/src/style.rs +++ b/examples/scrollable/src/style.rs @@ -16,11 +16,11 @@ impl Default for Theme { } } -impl From for Box { +impl From for &'static dyn container::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Container.into(), + Theme::Dark => &dark::Container, } } } diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 1746d7b4eb..d8254dd9a1 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -149,7 +149,7 @@ impl Sandbox for Styling { .height(Length::Fill) .center_x() .center_y() - .style(self.theme) + .style(self.theme.into()) .into() } } @@ -176,11 +176,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn container::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Container.into(), + Theme::Dark => &dark::Container, } } } diff --git a/examples/tooltip/src/main.rs b/examples/tooltip/src/main.rs index cfeaf6a697..cb2f81df41 100644 --- a/examples/tooltip/src/main.rs +++ b/examples/tooltip/src/main.rs @@ -115,7 +115,7 @@ fn tooltip<'a>( ) .gap(5) .padding(10) - .style(style::Tooltip) + .style(&style::Tooltip) .into() } diff --git a/graphics/src/widget/container.rs b/graphics/src/widget/container.rs index 811a0c7f5b..c4c4e5ba40 100644 --- a/graphics/src/widget/container.rs +++ b/graphics/src/widget/container.rs @@ -1,6 +1,5 @@ //! Decorate content and apply alignment. -use crate::container; -use crate::{Backend, Renderer}; +use crate::Renderer; pub use iced_style::container::{Style, StyleSheet}; @@ -10,10 +9,3 @@ pub use iced_style::container::{Style, StyleSheet}; /// `Renderer`. pub type Container<'a, Message, Backend> = iced_native::Container<'a, Message, Renderer>; - -impl iced_native::container::Renderer for Renderer -where - B: Backend, -{ - type Style = Box; -} diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index f2367ae7d4..a6527a8dcf 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -1,5 +1,4 @@ //! Build and show dropdown menus. -use crate::container; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -396,7 +395,7 @@ where /// able to use a [`Menu`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: container::Renderer + text::Renderer { +pub trait Renderer: text::Renderer { /// The [`Menu`] style supported by this renderer. type Style: Default + Clone; } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 268248fde4..fdb6ea92ae 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,6 +1,5 @@ use crate::button; use crate::checkbox; -use crate::container; use crate::pane_grid; use crate::progress_bar; use crate::radio; @@ -123,10 +122,6 @@ impl progress_bar::Renderer for Null { const DEFAULT_HEIGHT: u16 = 30; } -impl container::Renderer for Null { - type Style = (); -} - impl pane_grid::Renderer for Null { type Style = (); } diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 92869873c1..99bc3d4908 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -13,11 +13,13 @@ use crate::{ use std::u32; +pub use iced_style::container::{Style, StyleSheet}; + /// An element decorating some content. /// /// It is normally used for alignment purposes. #[allow(missing_debug_implementations)] -pub struct Container<'a, Message, Renderer: self::Renderer> { +pub struct Container<'a, Message, Renderer> { padding: Padding, width: Length, height: Length, @@ -25,13 +27,13 @@ pub struct Container<'a, Message, Renderer: self::Renderer> { max_height: u32, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - style: Renderer::Style, + style: &'a dyn StyleSheet, content: Element<'a, Message, Renderer>, } impl<'a, Message, Renderer> Container<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { /// Creates an empty [`Container`]. pub fn new(content: T) -> Self @@ -46,7 +48,7 @@ where max_height: u32::MAX, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - style: Renderer::Style::default(), + style: Default::default(), content: content.into(), } } @@ -106,8 +108,8 @@ where } /// Sets the style of the [`Container`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { + self.style = style; self } } @@ -115,7 +117,7 @@ where impl<'a, Message, Renderer> Widget for Container<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -211,21 +213,10 @@ where } } -/// The renderer of a [`Container`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Container`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer { - /// The style supported by this renderer. - type Style: Default; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 1e63fdeb2a..afecd68195 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -27,7 +27,6 @@ pub use split::Split; pub use state::State; pub use title_bar::TitleBar; -use crate::container; use crate::event::{self, Event}; use crate::layout; use crate::mouse; @@ -318,7 +317,7 @@ pub struct ResizeEvent { impl<'a, Message, Renderer> Widget for PaneGrid<'a, Message, Renderer> where - Renderer: self::Renderer + container::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -565,7 +564,7 @@ where /// able to use a [`PaneGrid`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + container::Renderer + Sized { +pub trait Renderer: crate::Renderer + Sized { /// The style supported by this renderer. type Style: Default; } diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 22b3a31053..7858c15dd0 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -13,7 +13,7 @@ use crate::{Clipboard, Element, Hasher, Layout, Point, Rectangle, Size}; pub struct Content<'a, Message, Renderer: pane_grid::Renderer> { title_bar: Option>, body: Element<'a, Message, Renderer>, - style: ::Style, + style: &'a dyn container::StyleSheet, } impl<'a, Message, Renderer> Content<'a, Message, Renderer> @@ -39,11 +39,8 @@ where } /// Sets the style of the [`Content`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn container::StyleSheet) -> Self { + self.style = style; self } } @@ -217,7 +214,7 @@ where impl<'a, T, Message, Renderer> From for Content<'a, Message, Renderer> where T: Into>, - Renderer: pane_grid::Renderer + container::Renderer, + Renderer: pane_grid::Renderer, { fn from(element: T) -> Self { Self::new(element) diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index ce9249d260..22d3a9eb66 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -17,7 +17,7 @@ pub struct TitleBar<'a, Message, Renderer: pane_grid::Renderer> { controls: Option>, padding: Padding, always_show_controls: bool, - style: ::Style, + style: &'a dyn container::StyleSheet, } impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> @@ -54,11 +54,8 @@ where } /// Sets the style of the [`TitleBar`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn container::StyleSheet) -> Self { + self.style = style; self } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 63da539f52..ac5b3e4fb2 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -122,10 +122,7 @@ impl<'a, Message, Renderer: crate::Renderer> Scrollable<'a, Message, Renderer> { } /// Sets the style of the [`Scrollable`] . - pub fn style<'b>(mut self, style_sheet: &'b dyn StyleSheet) -> Self - where - 'b: 'a, - { + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { self.style_sheet = style_sheet; self } diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 496cd41ef5..4e8483ad3a 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -16,7 +16,7 @@ pub struct Tooltip<'a, Message, Renderer: self::Renderer> { content: Element<'a, Message, Renderer>, tooltip: Text, position: Position, - style: ::Style, + style: &'a dyn container::StyleSheet, gap: u16, padding: u16, } @@ -70,11 +70,11 @@ where } /// Sets the style of the [`Tooltip`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); + pub fn style<'b>(mut self, style: &'b dyn container::StyleSheet) -> Self + where + 'b: 'a, + { + self.style = style; self } } @@ -160,9 +160,7 @@ where /// /// [`Tooltip`]: struct.Tooltip.html /// [renderer]: ../../renderer/index.html -pub trait Renderer: - crate::Renderer + text::Renderer + container::Renderer -{ +pub trait Renderer: crate::Renderer + text::Renderer { /// The default padding of a [`Tooltip`] drawn by this renderer. const DEFAULT_PADDING: u16; } diff --git a/style/src/container.rs b/style/src/container.rs index 1ce6a7cafc..6b0a129bbd 100644 --- a/style/src/container.rs +++ b/style/src/container.rs @@ -43,17 +43,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/container.rs b/web/src/widget/container.rs index 24aa7cefe3..e61600c4fe 100644 --- a/web/src/widget/container.rs +++ b/web/src/widget/container.rs @@ -19,7 +19,7 @@ pub struct Container<'a, Message> { max_height: u32, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - style_sheet: Box, + style_sheet: &'a dyn StyleSheet, content: Element<'a, Message>, } @@ -89,8 +89,8 @@ impl<'a, Message> Container<'a, Message> { } /// Sets the style of the [`Container`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style_sheet = style.into(); + pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { + self.style_sheet = style; self } } From 945f69e567d1766c82f77d3299d2d619d56d3add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 15:44:03 +0700 Subject: [PATCH 11/92] Draw styling in `Widget::draw` for `Container` --- native/src/widget/container.rs | 44 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 99bc3d4908..84c745e2be 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -7,8 +7,8 @@ use crate::layout; use crate::overlay; use crate::renderer; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, - Widget, + Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, + Point, Rectangle, Widget, }; use std::u32; @@ -27,7 +27,7 @@ pub struct Container<'a, Message, Renderer> { max_height: u32, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - style: &'a dyn StyleSheet, + style_sheet: &'a dyn StyleSheet, content: Element<'a, Message, Renderer>, } @@ -48,7 +48,7 @@ where max_height: u32::MAX, horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, - style: Default::default(), + style_sheet: Default::default(), content: content.into(), } } @@ -108,8 +108,8 @@ where } /// Sets the style of the [`Container`]. - pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { - self.style = style; + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } @@ -178,14 +178,22 @@ where fn draw( &self, renderer: &mut Renderer, - style: &renderer::Style, + renderer_style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, ) { + let style = self.style_sheet.style(); + + draw_background(renderer, &style, layout.bounds()); + self.content.draw( renderer, - style, + &renderer::Style { + text_color: style + .text_color + .unwrap_or(renderer_style.text_color), + }, layout.children().next().unwrap(), cursor_position, viewport, @@ -213,6 +221,26 @@ where } } +pub fn draw_background( + renderer: &mut Renderer, + style: &Style, + bounds: Rectangle, +) where + Renderer: crate::Renderer, +{ + if style.background.is_some() || style.border_width > 0.0 { + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + } +} + impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where From 95acc1deb89c4e75b513edb0f4d53b83c7f75b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 15:49:28 +0700 Subject: [PATCH 12/92] Implement `Widget::draw` for `custom_widget` example --- examples/custom_widget/Cargo.toml | 1 - examples/custom_widget/src/main.rs | 40 ++++++++++++++---------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/examples/custom_widget/Cargo.toml b/examples/custom_widget/Cargo.toml index 3942538d7d..86b0d2a900 100644 --- a/examples/custom_widget/Cargo.toml +++ b/examples/custom_widget/Cargo.toml @@ -8,4 +8,3 @@ publish = false [dependencies] iced = { path = "../.." } iced_native = { path = "../../native" } -iced_graphics = { path = "../../graphics" } diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index c8188f105e..78a4339e8f 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -9,10 +9,11 @@ mod circle { // Of course, you can choose to make the implementation renderer-agnostic, // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. - use iced_graphics::renderer::{self, Renderer}; - use iced_graphics::Backend; + use iced_native::layout::{self, Layout}; + use iced_native::renderer; use iced_native::{ - layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, + Background, Color, Element, Hasher, Length, Point, Rectangle, Size, + Widget, }; pub struct Circle { @@ -25,9 +26,9 @@ mod circle { } } - impl Widget> for Circle + impl Widget for Circle where - B: Backend, + Renderer: renderer::Renderer, { fn width(&self) -> Length { Length::Shrink @@ -39,7 +40,7 @@ mod circle { fn layout( &self, - _renderer: &Renderer, + _renderer: &Renderer, _limits: &layout::Limits, ) -> layout::Node { layout::Node::new(Size::new(self.radius * 2.0, self.radius * 2.0)) @@ -53,30 +54,27 @@ mod circle { fn draw( &self, - _renderer: &mut Renderer, + renderer: &mut Renderer, _style: &renderer::Style, - _layout: Layout<'_>, + layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { - // ( - // Primitive::Quad { - // bounds: layout.bounds(), - // background: Background::Color(Color::BLACK), - // border_radius: self.radius, - // border_width: 0.0, - // border_color: Color::TRANSPARENT, - // }, - // mouse::Interaction::default(), - // ) + renderer.fill_rectangle(renderer::Quad { + bounds: layout.bounds(), + background: Background::Color(Color::BLACK), + border_radius: self.radius, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); } } - impl<'a, Message, B> Into>> for Circle + impl<'a, Message, Renderer> Into> for Circle where - B: Backend, + Renderer: renderer::Renderer, { - fn into(self) -> Element<'a, Message, Renderer> { + fn into(self) -> Element<'a, Message, Renderer> { Element::new(self) } } From 3140cdc4babcefc444f1c1d30eb0f5f4ed1df054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:02:30 +0700 Subject: [PATCH 13/92] Wire up styling to `Button` in `iced_native` --- examples/game_of_life/src/main.rs | 6 ++--- examples/pane_grid/src/main.rs | 10 ++++---- examples/pokedex/src/main.rs | 2 +- examples/stopwatch/src/main.rs | 6 ++--- examples/styling/src/main.rs | 8 +++---- examples/todos/src/main.rs | 40 ++++++++++++++----------------- examples/tour/src/main.rs | 4 ++-- graphics/src/widget/button.rs | 15 ++---------- native/src/renderer/null.rs | 6 ----- native/src/widget/button.rs | 35 ++++++++++----------------- style/src/button.rs | 13 ++-------- web/src/widget/button.rs | 6 ++--- 12 files changed, 55 insertions(+), 96 deletions(-) diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index ee425f4491..3e94bd44be 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -834,12 +834,12 @@ impl Controls { Text::new(if is_playing { "Pause" } else { "Play" }), ) .on_press(Message::TogglePlayback) - .style(style::Button), + .style(&style::Button), ) .push( Button::new(&mut self.next_button, Text::new("Next")) .on_press(Message::Next) - .style(style::Button), + .style(&style::Button), ); let speed_controls = Row::new() @@ -883,7 +883,7 @@ impl Controls { .push( Button::new(&mut self.clear_button, Text::new("Clear")) .on_press(Message::Clear) - .style(style::Clear), + .style(&style::Clear), ) .into() } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 844b604dcf..4126485d16 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -158,7 +158,7 @@ impl Application for Example { let pin_button = Button::new(&mut pane.pin_button, Text::new(text).size(14)) .on_press(Message::TogglePin(id)) - .style(style::Button::Pin) + .style(&style::Button::Pin) .padding(3); let title = Row::with_children(vec![ @@ -316,13 +316,13 @@ impl Content { split_horizontally, "Split horizontally", Message::Split(pane_grid::Axis::Horizontal, pane), - style::Button::Primary, + &style::Button::Primary, )) .push(button( split_vertically, "Split vertically", Message::Split(pane_grid::Axis::Vertical, pane), - style::Button::Primary, + &style::Button::Primary, )); if total_panes > 1 && !is_pinned { @@ -330,7 +330,7 @@ impl Content { close, "Close", Message::Close(pane), - style::Button::Destructive, + &style::Button::Destructive, )); } @@ -364,7 +364,7 @@ impl Controls { ) -> Element { let mut button = Button::new(&mut self.close, Text::new("Close").size(14)) - .style(style::Button::Control) + .style(&style::Button::Control) .padding(3); if total_panes > 1 && !is_pinned { button = button.on_press(Message::Close(pane)); diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index c99240a143..c99301580a 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -243,7 +243,7 @@ impl From for Error { fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { Button::new(state, Text::new(text)) .padding(10) - .style(style::Button::Primary) + .style(&style::Button::Primary) } mod style { diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index dc8a4de74d..e67436200a 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -112,15 +112,15 @@ impl Application for Stopwatch { let toggle_button = { let (label, color) = match self.state { - State::Idle => ("Start", style::Button::Primary), - State::Ticking { .. } => ("Stop", style::Button::Destructive), + State::Idle => ("Start", &style::Button::Primary), + State::Ticking { .. } => ("Stop", &style::Button::Destructive), }; button(&mut self.toggle, label, color).on_press(Message::Toggle) }; let reset_button = - button(&mut self.reset, "Reset", style::Button::Secondary) + button(&mut self.reset, "Reset", &style::Button::Secondary) .on_press(Message::Reset); let controls = Row::new() diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index d8254dd9a1..38ab041107 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -82,7 +82,7 @@ impl Sandbox for Styling { let button = Button::new(&mut self.button, Text::new("Submit")) .padding(10) .on_press(Message::ButtonPressed) - .style(self.theme); + .style(self.theme.into()); let slider = Slider::new( &mut self.slider, @@ -203,11 +203,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn button::StyleSheet { fn from(theme: Theme) -> Self { match theme { - Theme::Light => light::Button.into(), - Theme::Dark => dark::Button.into(), + Theme::Light => &light::Button, + Theme::Dark => &dark::Button, } } } diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 11f23fd456..1734772d2c 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -304,7 +304,7 @@ impl Task { Button::new(edit_button, edit_icon()) .on_press(TaskMessage::Edit) .padding(10) - .style(style::Button::Icon), + .style(&style::Button::Icon), ) .into() } @@ -335,7 +335,7 @@ impl Task { ) .on_press(TaskMessage::Delete) .padding(10) - .style(style::Button::Destructive), + .style(&style::Button::Destructive), ) .into() } @@ -363,8 +363,10 @@ impl Controls { let filter_button = |state, label, filter, current_filter| { let label = Text::new(label).size(16); let button = - Button::new(state, label).style(style::Button::Filter { - selected: filter == current_filter, + Button::new(state, label).style(if filter == current_filter { + &style::Button::FilterSelected + } else { + &style::Button::FilterActive }); button.on_press(Message::FilterChanged(filter)).padding(8) @@ -602,7 +604,8 @@ mod style { use iced::{button, Background, Color, Vector}; pub enum Button { - Filter { selected: bool }, + FilterActive, + FilterSelected, Icon, Destructive, } @@ -610,20 +613,15 @@ mod style { impl button::StyleSheet for Button { fn active(&self) -> button::Style { match self { - Button::Filter { selected } => { - if *selected { - button::Style { - background: Some(Background::Color( - Color::from_rgb(0.2, 0.2, 0.7), - )), - border_radius: 10.0, - text_color: Color::WHITE, - ..button::Style::default() - } - } else { - button::Style::default() - } - } + Button::FilterActive => button::Style::default(), + Button::FilterSelected => button::Style { + background: Some(Background::Color(Color::from_rgb( + 0.2, 0.2, 0.7, + ))), + border_radius: 10.0, + text_color: Color::WHITE, + ..button::Style::default() + }, Button::Icon => button::Style { text_color: Color::from_rgb(0.5, 0.5, 0.5), ..button::Style::default() @@ -646,9 +644,7 @@ mod style { button::Style { text_color: match self { Button::Icon => Color::from_rgb(0.2, 0.2, 0.7), - Button::Filter { selected } if !selected => { - Color::from_rgb(0.2, 0.2, 0.7) - } + Button::FilterActive => Color::from_rgb(0.2, 0.2, 0.7), _ => active.text_color, }, shadow_offset: active.shadow_offset + Vector::new(0.0, 1.0), diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index b5af48c73c..176d275c71 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -64,7 +64,7 @@ impl Sandbox for Tour { controls = controls.push( button(back_button, "Back") .on_press(Message::BackPressed) - .style(style::Button::Secondary), + .style(&style::Button::Secondary), ); } @@ -74,7 +74,7 @@ impl Sandbox for Tour { controls = controls.push( button(next_button, "Next") .on_press(Message::NextPressed) - .style(style::Button::Primary), + .style(&style::Button::Primary), ); } diff --git a/graphics/src/widget/button.rs b/graphics/src/widget/button.rs index 990aac9f92..3d3e667573 100644 --- a/graphics/src/widget/button.rs +++ b/graphics/src/widget/button.rs @@ -1,23 +1,12 @@ //! Allow your users to perform actions by pressing a button. //! //! A [`Button`] has some local [`State`]. -use crate::{Backend, Renderer}; -use iced_native::Padding; +use crate::Renderer; -pub use iced_native::button::State; -pub use iced_style::button::{Style, StyleSheet}; +pub use iced_native::button::{State, Style, StyleSheet}; /// A widget that produces a message when clicked. /// /// This is an alias of an `iced_native` button with an `iced_wgpu::Renderer`. pub type Button<'a, Message, Backend> = iced_native::Button<'a, Message, Renderer>; - -impl iced_native::button::Renderer for Renderer -where - B: Backend, -{ - const DEFAULT_PADDING: Padding = Padding::new(5); - - type Style = Box; -} diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index fdb6ea92ae..07023a67a9 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -90,12 +90,6 @@ impl text_input::Renderer for Null { } } -impl button::Renderer for Null { - const DEFAULT_PADDING: Padding = Padding::ZERO; - - type Style = (); -} - impl radio::Renderer for Null { type Style = (); diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 2aeffd034d..77148673bb 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -11,8 +11,11 @@ use crate::{ Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, Widget, }; + use std::hash::Hash; +pub use iced_style::button::{Style, StyleSheet}; + /// A generic widget that produces a message when pressed. /// /// ``` @@ -54,7 +57,7 @@ use std::hash::Hash; /// } /// ``` #[allow(missing_debug_implementations)] -pub struct Button<'a, Message, Renderer: self::Renderer> { +pub struct Button<'a, Message, Renderer> { state: &'a mut State, content: Element<'a, Message, Renderer>, on_press: Option, @@ -63,13 +66,13 @@ pub struct Button<'a, Message, Renderer: self::Renderer> { min_width: u32, min_height: u32, padding: Padding, - style: Renderer::Style, + style: &'a dyn StyleSheet, } impl<'a, Message, Renderer> Button<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: crate::Renderer, { /// Creates a new [`Button`] with some local [`State`] and the given /// content. @@ -85,8 +88,8 @@ where height: Length::Shrink, min_width: 0, min_height: 0, - padding: Renderer::DEFAULT_PADDING, - style: Renderer::Style::default(), + padding: Padding::new(5), + style: Default::default(), } } @@ -128,8 +131,8 @@ where } /// Sets the style of the [`Button`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { + self.style = style; self } } @@ -151,7 +154,7 @@ impl<'a, Message, Renderer> Widget for Button<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -268,25 +271,11 @@ where } } -/// The renderer of a [`Button`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Button`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// The default padding of a [`Button`]. - const DEFAULT_PADDING: Padding; - - /// The style supported by this renderer. - type Style: Default; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, { fn from( button: Button<'a, Message, Renderer>, diff --git a/style/src/button.rs b/style/src/button.rs index 2281e32fe3..608f344bdb 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -80,17 +80,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/button.rs b/web/src/widget/button.rs index cd450b5545..1ae782011f 100644 --- a/web/src/widget/button.rs +++ b/web/src/widget/button.rs @@ -51,7 +51,7 @@ pub struct Button<'a, Message> { #[allow(dead_code)] min_height: u32, padding: Padding, - style: Box, + style: &'a dyn StyleSheet, } impl<'a, Message> Button<'a, Message> { @@ -104,8 +104,8 @@ impl<'a, Message> Button<'a, Message> { } /// Sets the style of the [`Button`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { + self.style = style; self } From 7a876c8b2918ae90cedfd82d1881cf6406811eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:10:16 +0700 Subject: [PATCH 14/92] Implement `Widget::draw` for `Button` --- native/src/widget/button.rs | 67 +++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 77148673bb..a05210fd45 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -8,8 +8,8 @@ use crate::overlay; use crate::renderer; use crate::touch; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, - Widget, + Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, + Point, Rectangle, Vector, Widget, }; use std::hash::Hash; @@ -66,7 +66,7 @@ pub struct Button<'a, Message, Renderer> { min_width: u32, min_height: u32, padding: Padding, - style: &'a dyn StyleSheet, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message, Renderer> Button<'a, Message, Renderer> @@ -89,7 +89,7 @@ where min_width: 0, min_height: 0, padding: Padding::new(5), - style: Default::default(), + style_sheet: Default::default(), } } @@ -131,8 +131,8 @@ where } /// Sets the style of the [`Button`]. - pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { - self.style = style; + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } @@ -248,11 +248,64 @@ where fn draw( &self, renderer: &mut Renderer, - style: &renderer::Style, + _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { + let bounds = layout.bounds(); + let content_layout = layout.children().next().unwrap(); + + let is_mouse_over = bounds.contains(cursor_position); + + let styling = if self.on_press.is_none() { + self.style_sheet.disabled() + } else if is_mouse_over { + if self.state.is_pressed { + self.style_sheet.pressed() + } else { + self.style_sheet.hovered() + } + } else { + self.style_sheet.active() + }; + + if styling.background.is_some() || styling.border_width > 0.0 { + if styling.shadow_offset != Vector::default() { + // TODO: Implement proper shadow support + renderer.fill_rectangle(renderer::Quad { + bounds: Rectangle { + x: bounds.x + styling.shadow_offset.x, + y: bounds.y + styling.shadow_offset.y, + ..bounds + }, + background: Background::Color([0.0, 0.0, 0.0, 0.5].into()), + border_radius: styling.border_radius, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + } + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: styling + .background + .unwrap_or(Background::Color(Color::TRANSPARENT)), + border_radius: styling.border_radius, + border_width: styling.border_width, + border_color: styling.border_color, + }); + } + + self.content.draw( + renderer, + &renderer::Style { + text_color: styling.text_color, + }, + content_layout, + cursor_position, + &bounds, + ); } fn hash_layout(&self, state: &mut Hasher) { From be97a5d502df8c8b23704f5a8a0d425f4eff2215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:43:18 +0700 Subject: [PATCH 15/92] Introduce `mouse_interaction` method to `Widget` trait --- examples/integration_opengl/src/main.rs | 1 - examples/integration_wgpu/src/main.rs | 1 - glutin/src/application.rs | 29 ++++++++++++++++++---- native/src/element.rs | 32 +++++++++++++++++++++++++ native/src/overlay.rs | 15 +++++++++++- native/src/overlay/element.rs | 13 +++++++++- native/src/program/state.rs | 17 +++++++++---- native/src/user_interface.rs | 25 ++++++++++++++++--- native/src/widget.rs | 13 ++++++++++ winit/src/application.rs | 27 +++++++++++++++++---- 10 files changed, 152 insertions(+), 21 deletions(-) diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs index bab6331e62..ef79b88922 100644 --- a/examples/integration_opengl/src/main.rs +++ b/examples/integration_opengl/src/main.rs @@ -68,7 +68,6 @@ pub fn main() { let mut state = program::State::new( controls, viewport.logical_size(), - conversion::cursor_position(cursor_position, viewport.scale_factor()), &mut renderer, &mut debug, ); diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 9980acc209..c2c857d2ad 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -93,7 +93,6 @@ pub fn main() { let mut state = program::State::new( controls, viewport.logical_size(), - conversion::cursor_position(cursor_position, viewport.scale_factor()), &mut renderer, &mut debug, ); diff --git a/glutin/src/application.rs b/glutin/src/application.rs index 508dfe040d..5d10e96a62 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -1,4 +1,5 @@ //! Create interactive, native cross-platform applications. +use crate::mouse; use crate::{Error, Executor, Runtime}; pub use iced_winit::Application; @@ -179,9 +180,7 @@ async fn run_instance( &mut debug, )); - // TODO - // let mut mouse_interaction = mouse::Interaction::default(); - + let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); @@ -245,9 +244,18 @@ async fn run_instance( } debug.draw_started(); - user_interface.draw(&mut renderer, state.cursor_position()); + let new_mouse_interaction = + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); + if new_mouse_interaction != mouse_interaction { + context.window().set_cursor_icon( + conversion::mouse_interaction(new_mouse_interaction), + ); + + mouse_interaction = new_mouse_interaction; + } + context.window().request_redraw(); } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( @@ -289,9 +297,20 @@ async fn run_instance( debug.layout_finished(); debug.draw_started(); - user_interface.draw(&mut renderer, state.cursor_position()); + let new_mouse_interaction = user_interface + .draw(&mut renderer, state.cursor_position()); debug.draw_finished(); + if new_mouse_interaction != mouse_interaction { + context.window().set_cursor_icon( + conversion::mouse_interaction( + new_mouse_interaction, + ), + ); + + mouse_interaction = new_mouse_interaction; + } + context.resize(glutin::dpi::PhysicalSize::new( physical_size.width, physical_size.height, diff --git a/native/src/element.rs b/native/src/element.rs index 3ecd8e2648..44b6d1ec83 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -1,5 +1,6 @@ use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ @@ -251,6 +252,16 @@ where .draw(renderer, style, layout, cursor_position, viewport) } + pub fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.widget + .mouse_interaction(layout, viewport, cursor_position) + } + /// Computes the _layout_ hash of the [`Element`]. pub fn hash_layout(&self, state: &mut Hasher) { self.widget.hash_layout(state); @@ -346,6 +357,16 @@ where .draw(renderer, style, layout, cursor_position, viewport) } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.widget + .mouse_interaction(layout, viewport, cursor_position) + } + fn hash_layout(&self, state: &mut Hasher) { self.widget.hash_layout(state); } @@ -426,6 +447,17 @@ where ) { } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.element + .widget + .mouse_interaction(layout, viewport, cursor_position) + } + fn hash_layout(&self, state: &mut Hasher) { self.element.widget.hash_layout(state); } diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 25b49adf3f..70e3305b94 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -8,8 +8,9 @@ pub use menu::Menu; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::renderer; -use crate::{Clipboard, Hasher, Layout, Point, Size}; +use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Size}; /// An interactive component that can be displayed on top of other widgets. pub trait Overlay @@ -74,4 +75,16 @@ where ) -> event::Status { event::Status::Ignored } + + /// Returns the current [`mouse::Interaction`] of the [`Widget`]. + /// + /// By default, it returns [`mouse::Interaction::Idle`]. + fn mouse_interaction( + &self, + _layout: Layout<'_>, + _viewport: &Rectangle, + _cursor_position: Point, + ) -> mouse::Interaction { + mouse::Interaction::Idle + } } diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index bf3fb3f0bd..988cca0994 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -2,8 +2,9 @@ pub use crate::Overlay; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::renderer; -use crate::{Clipboard, Hasher, Layout, Point, Size, Vector}; +use crate::{Clipboard, Hasher, Layout, Point, Rectangle, Size, Vector}; /// A generic [`Overlay`]. #[allow(missing_debug_implementations)] @@ -68,6 +69,16 @@ where ) } + pub fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.overlay + .mouse_interaction(layout, viewport, cursor_position) + } + /// Draws the [`Element`] and its children using the given [`Layout`]. pub fn draw( &self, diff --git a/native/src/program/state.rs b/native/src/program/state.rs index 55308a927b..26c0eb21ac 100644 --- a/native/src/program/state.rs +++ b/native/src/program/state.rs @@ -1,3 +1,4 @@ +use crate::mouse; use crate::{ Cache, Clipboard, Command, Debug, Event, Point, Program, Size, UserInterface, @@ -14,6 +15,7 @@ where cache: Option, queued_events: Vec, queued_messages: Vec, + mouse_interaction: mouse::Interaction, } impl

State

@@ -25,11 +27,10 @@ where pub fn new( mut program: P, bounds: Size, - cursor_position: Point, renderer: &mut P::Renderer, debug: &mut Debug, ) -> Self { - let mut user_interface = build_user_interface( + let user_interface = build_user_interface( &mut program, Cache::default(), renderer, @@ -44,6 +45,7 @@ where cache, queued_events: Vec::new(), queued_messages: Vec::new(), + mouse_interaction: mouse::Interaction::Idle, } } @@ -71,6 +73,11 @@ where self.queued_events.is_empty() && self.queued_messages.is_empty() } + /// Returns the current [`mouse::Interaction`] of the [`State`]. + pub fn mouse_interaction(&self) -> mouse::Interaction { + self.mouse_interaction + } + /// Processes all the queued events and messages, rebuilding and redrawing /// the widgets of the linked [`Program`] if necessary. /// @@ -109,7 +116,8 @@ where if messages.is_empty() { debug.draw_started(); - user_interface.draw(renderer, cursor_position); + self.mouse_interaction = + user_interface.draw(renderer, cursor_position); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); @@ -140,7 +148,8 @@ where ); debug.draw_started(); - user_interface.draw(renderer, cursor_position); + self.mouse_interaction = + user_interface.draw(renderer, cursor_position); debug.draw_finished(); self.cache = Some(user_interface.into_cache()); diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 2f76c08499..d93385247b 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -1,5 +1,6 @@ use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{Clipboard, Element, Layout, Point, Rectangle, Size, Vector}; @@ -330,7 +331,11 @@ where /// // Flush rendering operations... /// } /// ``` - pub fn draw(&mut self, renderer: &mut Renderer, cursor_position: Point) { + pub fn draw( + &mut self, + renderer: &mut Renderer, + cursor_position: Point, + ) -> mouse::Interaction { // TODO: Move to shell level (?) renderer.clear(); @@ -346,6 +351,12 @@ where renderer, ); + let mouse_interaction = overlay.mouse_interaction( + Layout::new(&layer.layout), + &viewport, + cursor_position, + ); + let overlay_bounds = layer.layout.bounds(); renderer.with_layer( @@ -363,12 +374,12 @@ where self.overlay = Some(layer); - Some(overlay_bounds) + Some((overlay_bounds, mouse_interaction)) } else { None }; - if let Some(overlay_bounds) = overlay { + if let Some((overlay_bounds, overlay_interaction)) = overlay { let base_cursor = if overlay_bounds.contains(cursor_position) { Point::new(-1.0, -1.0) } else { @@ -382,6 +393,8 @@ where base_cursor, &viewport, ); + + overlay_interaction } else { self.root.widget.draw( renderer, @@ -390,6 +403,12 @@ where cursor_position, &viewport, ); + + self.root.widget.mouse_interaction( + Layout::new(&self.base.layout), + &viewport, + cursor_position, + ) } } diff --git a/native/src/widget.rs b/native/src/widget.rs index 9a494771cb..9999701e18 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -80,6 +80,7 @@ pub use tooltip::Tooltip; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{Clipboard, Hasher, Layout, Length, Point, Rectangle}; @@ -175,6 +176,18 @@ where event::Status::Ignored } + /// Returns the current [`mouse::Interaction`] of the [`Widget`]. + /// + /// By default, it returns [`mouse::Interaction::Idle`]. + fn mouse_interaction( + &self, + _layout: Layout<'_>, + _viewport: &Rectangle, + _cursor_position: Point, + ) -> mouse::Interaction { + mouse::Interaction::Idle + } + /// Returns the overlay of the [`Widget`], if there is any. fn overlay( &mut self, diff --git a/winit/src/application.rs b/winit/src/application.rs index 70562627df..9d787933c9 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -5,6 +5,7 @@ pub use state::State; use crate::clipboard::{self, Clipboard}; use crate::conversion; +use crate::mouse; use crate::{ Color, Command, Debug, Error, Executor, Mode, Proxy, Runtime, Settings, Size, Subscription, @@ -252,9 +253,7 @@ async fn run_instance( &mut debug, )); - // TODO - // let mut mouse_interaction = mouse::Interaction::default(); - + let mut mouse_interaction = mouse::Interaction::default(); let mut events = Vec::new(); let mut messages = Vec::new(); @@ -317,9 +316,18 @@ async fn run_instance( } debug.draw_started(); - user_interface.draw(&mut renderer, state.cursor_position()); + let new_mouse_interaction = + user_interface.draw(&mut renderer, state.cursor_position()); debug.draw_finished(); + if new_mouse_interaction != mouse_interaction { + window.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); + + mouse_interaction = new_mouse_interaction; + } + window.request_redraw(); } event::Event::PlatformSpecific(event::PlatformSpecific::MacOS( @@ -356,7 +364,16 @@ async fn run_instance( debug.layout_finished(); debug.draw_started(); - user_interface.draw(&mut renderer, state.cursor_position()); + let new_mouse_interaction = user_interface + .draw(&mut renderer, state.cursor_position()); + + if new_mouse_interaction != mouse_interaction { + window.set_cursor_icon(conversion::mouse_interaction( + new_mouse_interaction, + )); + + mouse_interaction = new_mouse_interaction; + } debug.draw_finished(); compositor.configure_surface( From 5130e98dc873f57279f7fa7acb0b7e6d12fb7edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:45:53 +0700 Subject: [PATCH 16/92] Implement `Widget::mouse_interaction` for `Container` --- native/src/widget/container.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 84c745e2be..006e07c655 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -4,6 +4,7 @@ use std::hash::Hash; use crate::alignment::{self, Alignment}; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ @@ -175,6 +176,19 @@ where ) } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.content.widget.mouse_interaction( + layout.children().next().unwrap(), + viewport, + cursor_position, + ) + } + fn draw( &self, renderer: &mut Renderer, From 519a0cc233ac915c3a5df2bd81d1375edb05a4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:47:37 +0700 Subject: [PATCH 17/92] Implement `Widget::mouse_interaction` for `Column` --- native/src/widget/column.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index e7ce004172..1d2c848511 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -3,6 +3,7 @@ use std::hash::Hash; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ @@ -163,6 +164,26 @@ where .fold(event::Status::Ignored, event::Status::merge) } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.children + .iter() + .zip(layout.children()) + .map(|(child, layout)| { + child.widget.mouse_interaction( + layout, + viewport, + cursor_position, + ) + }) + .max() + .unwrap_or_default() + } + fn draw( &self, renderer: &mut Renderer, From 14dc750149b6cfaa49ba8fe3533343e0594fc2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:48:39 +0700 Subject: [PATCH 18/92] Implement `Widget::mouse_interaction` for `Row` --- native/src/widget/row.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index af63c7da34..af83f106b6 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -1,6 +1,7 @@ //! Distribute content horizontally. use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ @@ -162,6 +163,26 @@ where .fold(event::Status::Ignored, event::Status::merge) } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + self.children + .iter() + .zip(layout.children()) + .map(|(child, layout)| { + child.widget.mouse_interaction( + layout, + viewport, + cursor_position, + ) + }) + .max() + .unwrap_or_default() + } + fn draw( &self, renderer: &mut Renderer, From 9a4fb82b5818f10e96d2f185a7e5a1f1fce3305c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:53:50 +0700 Subject: [PATCH 19/92] Implement `Widget::mouse_interaction` for `Scrollable` --- native/src/widget/scrollable.rs | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index ac5b3e4fb2..76badbde25 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -419,6 +419,45 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + let bounds = layout.bounds(); + let content_layout = layout.children().next().unwrap(); + let content_bounds = content_layout.bounds(); + let scrollbar = self.scrollbar(bounds, content_bounds); + + let is_mouse_over = bounds.contains(cursor_position); + let is_mouse_over_scrollbar = scrollbar + .as_ref() + .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) + .unwrap_or(false); + + if is_mouse_over_scrollbar || self.state.is_scroller_grabbed() { + mouse::Interaction::Idle + } else { + let offset = self.state.offset(bounds, content_bounds); + + let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { + Point::new(cursor_position.x, cursor_position.y + offset as f32) + } else { + Point::new(cursor_position.x, -1.0) + }; + + self.content.mouse_interaction( + content_layout, + &Rectangle { + y: bounds.y + offset as f32, + ..bounds + }, + cursor_position, + ) + } + } + fn draw( &self, renderer: &mut Renderer, From 4f5f444d7c507105d025471fbe930e50653c05bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:59:42 +0700 Subject: [PATCH 20/92] Implement `Widget::mouse_interaction` for `Button` --- native/src/widget/button.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index a05210fd45..a654cf1281 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -245,6 +245,22 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + let is_mouse_over = layout.bounds().contains(cursor_position); + let is_disabled = self.on_press.is_none(); + + if is_mouse_over && !is_disabled { + mouse::Interaction::Pointer + } else { + mouse::Interaction::default() + } + } + fn draw( &self, renderer: &mut Renderer, @@ -257,8 +273,9 @@ where let content_layout = layout.children().next().unwrap(); let is_mouse_over = bounds.contains(cursor_position); + let is_disabled = self.on_press.is_none(); - let styling = if self.on_press.is_none() { + let styling = if is_disabled { self.style_sheet.disabled() } else if is_mouse_over { if self.state.is_pressed { From e00a2e9b2d97147be5912a97362d1bf1705b7c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 18 Oct 2021 16:59:59 +0700 Subject: [PATCH 21/92] Remove `dbg!` leftover in `Renderer::fill_text` --- graphics/src/renderer.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 8d623868c7..0dca685fcd 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -92,8 +92,6 @@ where type Font = Font; fn fill_text(&mut self, text: renderer::text::Section<'_, Self::Font>) { - dbg!(text); - self.primitives.push(Primitive::Text { content: text.content.to_string(), bounds: text.bounds, From 11bcb1342796a6fabc7c5b89a15c22c754b014ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 15:50:42 +0700 Subject: [PATCH 22/92] Wire up styling to `Slider` in `iced_native` --- examples/game_of_life/src/main.rs | 2 +- examples/styling/src/main.rs | 6 ++-- glow/src/widget/slider.rs | 10 +------ graphics/src/widget/slider.rs | 21 +------------- native/src/renderer/null.rs | 10 +------ native/src/widget/slider.rs | 48 +++++++++++-------------------- style/src/slider.rs | 13 ++------- web/src/widget/slider.rs | 8 +++--- wgpu/src/widget/slider.rs | 10 +------ 9 files changed, 31 insertions(+), 97 deletions(-) diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 3e94bd44be..2c02742132 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -853,7 +853,7 @@ impl Controls { speed as f32, Message::SpeedChanged, ) - .style(style::Slider), + .style(&style::Slider), ) .push(Text::new(format!("x{}", speed)).size(16)); diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 38ab041107..34d18fc941 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -90,7 +90,7 @@ impl Sandbox for Styling { self.slider_value, Message::SliderChanged, ) - .style(self.theme); + .style(self.theme.into()); let progress_bar = ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); @@ -221,11 +221,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn slider::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Slider.into(), + Theme::Dark => &dark::Slider, } } } diff --git a/glow/src/widget/slider.rs b/glow/src/widget/slider.rs index 9a2698589e..2ece156c8e 100644 --- a/glow/src/widget/slider.rs +++ b/glow/src/widget/slider.rs @@ -1,13 +1,5 @@ //! Display an interactive selector of a single value from a range of values. //! //! A [`Slider`] has some local [`State`]. -use crate::Renderer; - pub use iced_graphics::slider::{Handle, HandleShape, Style, StyleSheet}; -pub use iced_native::slider::State; - -/// An horizontal bar and a handle that selects a single value from a range of -/// values. -/// -/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. -pub type Slider<'a, T, Message> = iced_native::Slider<'a, T, Message, Renderer>; +pub use iced_native::slider::{Slider, State}; diff --git a/graphics/src/widget/slider.rs b/graphics/src/widget/slider.rs index 5125d66cf8..766ecbb631 100644 --- a/graphics/src/widget/slider.rs +++ b/graphics/src/widget/slider.rs @@ -1,24 +1,5 @@ //! Display an interactive selector of a single value from a range of values. //! //! A [`Slider`] has some local [`State`]. -use crate::{Backend, Renderer}; -use iced_native::slider; - -pub use iced_native::slider::State; +pub use iced_native::slider::{Slider, State}; pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; - -/// An horizontal bar and a handle that selects a single value from a range of -/// values. -/// -/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. -pub type Slider<'a, T, Message, Backend> = - iced_native::Slider<'a, T, Message, Renderer>; - -impl slider::Renderer for Renderer -where - B: Backend, -{ - type Style = Box; - - const DEFAULT_HEIGHT: u16 = 22; -} diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 07023a67a9..2d6979e3c3 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,14 +1,12 @@ -use crate::button; use crate::checkbox; use crate::pane_grid; use crate::progress_bar; use crate::radio; use crate::renderer::{self, Renderer}; -use crate::slider; use crate::text; use crate::text_input; use crate::toggler; -use crate::{Font, Padding, Point, Rectangle, Size, Vector}; +use crate::{Font, Point, Rectangle, Size, Vector}; /// A renderer that does nothing. /// @@ -104,12 +102,6 @@ impl checkbox::Renderer for Null { const DEFAULT_SPACING: u16 = 15; } -impl slider::Renderer for Null { - type Style = (); - - const DEFAULT_HEIGHT: u16 = 30; -} - impl progress_bar::Renderer for Null { type Style = (); diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 7bdb72af5f..24b6bf6da2 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -13,6 +13,8 @@ use crate::{ use std::hash::Hash; use std::ops::RangeInclusive; +pub use iced_style::slider::{Handle, HandleShape, Style, StyleSheet}; + /// An horizontal bar and a handle that selects a single value from a range of /// values. /// @@ -23,9 +25,8 @@ use std::ops::RangeInclusive; /// /// # Example /// ``` -/// # use iced_native::{slider, renderer::Null}; +/// # use iced_native::slider::{self, Slider}; /// # -/// # pub type Slider<'a, T, Message> = iced_native::Slider<'a, T, Message, Null>; /// #[derive(Clone)] /// pub enum Message { /// SliderChanged(f32), @@ -39,7 +40,7 @@ use std::ops::RangeInclusive; /// /// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Slider<'a, T, Message, Renderer: self::Renderer> { +pub struct Slider<'a, T, Message> { state: &'a mut State, range: RangeInclusive, step: T, @@ -48,15 +49,16 @@ pub struct Slider<'a, T, Message, Renderer: self::Renderer> { on_release: Option, width: Length, height: u16, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } -impl<'a, T, Message, Renderer> Slider<'a, T, Message, Renderer> +impl<'a, T, Message> Slider<'a, T, Message> where T: Copy + From + std::cmp::PartialOrd, Message: Clone, - Renderer: self::Renderer, { + pub const DEFAULT_HEIGHT: u16 = 22; + /// Creates a new [`Slider`]. /// /// It expects: @@ -95,8 +97,8 @@ where on_change: Box::new(on_change), on_release: None, width: Length::Fill, - height: Renderer::DEFAULT_HEIGHT, - style: Renderer::Style::default(), + height: Self::DEFAULT_HEIGHT, + style_sheet: Default::default(), } } @@ -124,8 +126,8 @@ where } /// Sets the style of the [`Slider`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } @@ -150,11 +152,11 @@ impl State { } impl<'a, T, Message, Renderer> Widget - for Slider<'a, T, Message, Renderer> + for Slider<'a, T, Message> where T: Copy + Into + num_traits::FromPrimitive, Message: Clone, - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -275,30 +277,14 @@ where } } -/// The renderer of a [`Slider`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Slider`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer { - /// The style supported by this renderer. - type Style: Default; - - /// The default height of a [`Slider`]. - const DEFAULT_HEIGHT: u16; -} - -impl<'a, T, Message, Renderer> From> +impl<'a, T, Message, Renderer> From> for Element<'a, Message, Renderer> where T: 'a + Copy + Into + num_traits::FromPrimitive, Message: 'a + Clone, - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, { - fn from( - slider: Slider<'a, T, Message, Renderer>, - ) -> Element<'a, Message, Renderer> { + fn from(slider: Slider<'a, T, Message>) -> Element<'a, Message, Renderer> { Element::new(slider) } } diff --git a/style/src/slider.rs b/style/src/slider.rs index 9148fcbea9..c6791c3d5d 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -79,17 +79,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/slider.rs b/web/src/widget/slider.rs index f457aa4c00..7ec84475f5 100644 --- a/web/src/widget/slider.rs +++ b/web/src/widget/slider.rs @@ -41,7 +41,7 @@ pub struct Slider<'a, T, Message> { #[allow(dead_code)] width: Length, #[allow(dead_code)] - style: Box, + style_sheet: &'a dyn StyleSheet, } impl<'a, T, Message> Slider<'a, T, Message> @@ -85,7 +85,7 @@ where step: T::from(1), on_change: Rc::new(Box::new(on_change)), width: Length::Fill, - style: Default::default(), + style_sheet: Default::default(), } } @@ -96,8 +96,8 @@ where } /// Sets the style of the [`Slider`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet.into(); self } diff --git a/wgpu/src/widget/slider.rs b/wgpu/src/widget/slider.rs index 9a2698589e..2ece156c8e 100644 --- a/wgpu/src/widget/slider.rs +++ b/wgpu/src/widget/slider.rs @@ -1,13 +1,5 @@ //! Display an interactive selector of a single value from a range of values. //! //! A [`Slider`] has some local [`State`]. -use crate::Renderer; - pub use iced_graphics::slider::{Handle, HandleShape, Style, StyleSheet}; -pub use iced_native::slider::State; - -/// An horizontal bar and a handle that selects a single value from a range of -/// values. -/// -/// This is an alias of an `iced_native` slider with an `iced_wgpu::Renderer`. -pub type Slider<'a, T, Message> = iced_native::Slider<'a, T, Message, Renderer>; +pub use iced_native::slider::{Slider, State}; From c800fd07c3460d90a120fe30fc0cd7503d829f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 16:28:12 +0700 Subject: [PATCH 23/92] Implement `Widget::draw` for `Slider` --- native/src/widget/slider.rs | 95 +++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 24b6bf6da2..31a5965b2e 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -7,7 +7,8 @@ use crate::mouse; use crate::renderer; use crate::touch; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, + Background, Clipboard, Color, Element, Hasher, Layout, Length, Point, + Rectangle, Size, Widget, }; use std::hash::Hash; @@ -250,23 +251,89 @@ where fn draw( &self, renderer: &mut Renderer, - style: &renderer::Style, + _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { - // TODO - // let start = *self.range.start(); - // let end = *self.range.end(); - - // renderer.draw( - // layout.bounds(), - // cursor_position, - // start.into() as f32..=end.into() as f32, - // self.value.into() as f32, - // self.state.is_dragging, - // &self.style, - // ) + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + let style = if self.state.is_dragging { + self.style_sheet.dragging() + } else if is_mouse_over { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + let rail_y = bounds.y + (bounds.height / 2.0).round(); + + renderer.fill_rectangle(renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: rail_y, + width: bounds.width, + height: 2.0, + }, + background: Background::Color(style.rail_colors.0), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + + renderer.fill_rectangle(renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: rail_y + 2.0, + width: bounds.width, + height: 2.0, + }, + background: Background::Color(style.rail_colors.1), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + + let (handle_width, handle_height, handle_border_radius) = match style + .handle + .shape + { + HandleShape::Circle { radius } => { + (radius * 2.0, radius * 2.0, radius) + } + HandleShape::Rectangle { + width, + border_radius, + } => (f32::from(width), f32::from(bounds.height), border_radius), + }; + + let value = self.value.into() as f32; + let (range_start, range_end) = { + let (start, end) = self.range.clone().into_inner(); + + (start.into() as f32, end.into() as f32) + }; + + let handle_offset = if range_start >= range_end { + 0.0 + } else { + (bounds.width - handle_width) * (value - range_start) + / (range_end - range_start) + }; + + renderer.fill_rectangle(renderer::Quad { + bounds: Rectangle { + x: bounds.x + handle_offset.round(), + y: rail_y - handle_height / 2.0, + width: handle_width, + height: handle_height, + }, + background: Background::Color(style.handle.color), + border_radius: handle_border_radius, + border_width: style.handle.border_width, + border_color: style.handle.border_color, + }); } fn hash_layout(&self, state: &mut Hasher) { From 954d6349a8cd5505d99403285ce9617c65a63a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 16:28:27 +0700 Subject: [PATCH 24/92] Implement `Widget::mouse_interaction` for `Slider` --- native/src/widget/slider.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 31a5965b2e..601241600f 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -336,6 +336,24 @@ where }); } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + if self.state.is_dragging { + mouse::Interaction::Grabbing + } else if is_mouse_over { + mouse::Interaction::Grab + } else { + mouse::Interaction::default() + } + } + fn hash_layout(&self, state: &mut Hasher) { struct Marker; std::any::TypeId::of::().hash(state); From e914888f57394e4b67b40e42f1ad9df4ae8147e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 18:40:39 +0700 Subject: [PATCH 25/92] Implement `Widget::draw` for `TextInput` --- examples/styling/src/main.rs | 6 +- graphics/src/renderer.rs | 36 ++- graphics/src/widget/text.rs | 44 ---- graphics/src/widget/text_input.rs | 75 +----- native/src/overlay/menu.rs | 2 +- native/src/renderer/null.rs | 24 +- native/src/renderer/text.rs | 40 ++- native/src/widget/checkbox.rs | 9 +- native/src/widget/pick_list.rs | 2 +- native/src/widget/radio.rs | 9 +- native/src/widget/text.rs | 48 +--- native/src/widget/text_input.rs | 395 +++++++++++++++++++++--------- native/src/widget/toggler.rs | 9 +- native/src/widget/tooltip.rs | 4 +- style/src/text_input.rs | 13 +- web/src/widget/text_input.rs | 6 +- 16 files changed, 382 insertions(+), 340 deletions(-) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 34d18fc941..cca94c8f14 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -77,7 +77,7 @@ impl Sandbox for Styling { ) .padding(10) .size(20) - .style(self.theme); + .style(self.theme.into()); let button = Button::new(&mut self.button, Text::new("Submit")) .padding(10) @@ -194,11 +194,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn text_input::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::TextInput.into(), + Theme::Dark => &dark::TextInput, } } } diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 0dca685fcd..f8c6704738 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -2,7 +2,7 @@ use crate::backend::{self, Backend}; use crate::{Primitive, Vector}; use iced_native::layout; use iced_native::renderer; -use iced_native::{Element, Font, Rectangle}; +use iced_native::{Element, Font, Point, Rectangle, Size}; pub use iced_native::renderer::Style; @@ -91,6 +91,40 @@ where { type Font = Font; + fn default_size(&self) -> u16 { + self.backend().default_size() + } + + fn measure( + &self, + content: &str, + size: u16, + font: Font, + bounds: Size, + ) -> (f32, f32) { + self.backend() + .measure(content, f32::from(size), font, bounds) + } + + fn hit_test( + &self, + content: &str, + size: f32, + font: Font, + bounds: Size, + point: Point, + nearest_only: bool, + ) -> Option { + self.backend().hit_test( + content, + size, + font, + bounds, + point, + nearest_only, + ) + } + fn fill_text(&mut self, text: renderer::text::Section<'_, Self::Font>) { self.primitives.push(Primitive::Text { content: text.content.to_string(), diff --git a/graphics/src/widget/text.rs b/graphics/src/widget/text.rs index 4ee2c61664..ec0349f9e2 100644 --- a/graphics/src/widget/text.rs +++ b/graphics/src/widget/text.rs @@ -1,51 +1,7 @@ //! Write some text for your users to read. -use crate::backend::{self, Backend}; use crate::Renderer; -use iced_native::text; -use iced_native::{Font, Point, Size}; /// A paragraph of text. /// /// This is an alias of an `iced_native` text with an `iced_wgpu::Renderer`. pub type Text = iced_native::Text>; - -use std::f32; - -impl text::Renderer for Renderer -where - B: Backend + backend::Text, -{ - fn default_size(&self) -> u16 { - self.backend().default_size() - } - - fn measure( - &self, - content: &str, - size: u16, - font: Font, - bounds: Size, - ) -> (f32, f32) { - self.backend() - .measure(content, f32::from(size), font, bounds) - } - - fn hit_test( - &self, - content: &str, - size: f32, - font: Font, - bounds: Size, - point: Point, - nearest_only: bool, - ) -> Option { - self.backend().hit_test( - content, - size, - font, - bounds, - point, - nearest_only, - ) - } -} diff --git a/graphics/src/widget/text_input.rs b/graphics/src/widget/text_input.rs index e9dbf056a7..ebb9138f03 100644 --- a/graphics/src/widget/text_input.rs +++ b/graphics/src/widget/text_input.rs @@ -1,11 +1,7 @@ //! Display fields that can be filled with text. //! //! A [`TextInput`] has some local [`State`]. -use crate::backend::{self, Backend}; -use crate::{Font, Rectangle, Renderer, Size}; - -use iced_native::text_input::{self, cursor}; -use std::f32; +use crate::Renderer; pub use iced_native::text_input::State; pub use iced_style::text_input::{Style, StyleSheet}; @@ -15,72 +11,3 @@ pub use iced_style::text_input::{Style, StyleSheet}; /// This is an alias of an `iced_native` text input with an `iced_wgpu::Renderer`. pub type TextInput<'a, Message, Backend> = iced_native::TextInput<'a, Message, Renderer>; - -impl text_input::Renderer for Renderer -where - B: Backend + backend::Text, -{ - type Style = Box; - - fn measure_value(&self, value: &str, size: u16, font: Font) -> f32 { - let backend = self.backend(); - - let (width, _) = - backend.measure(value, f32::from(size), font, Size::INFINITY); - - width - } - - fn offset( - &self, - text_bounds: Rectangle, - font: Font, - size: u16, - value: &text_input::Value, - state: &text_input::State, - ) -> f32 { - if state.is_focused() { - let cursor = state.cursor(); - - let focus_position = match cursor.state(value) { - cursor::State::Index(i) => i, - cursor::State::Selection { end, .. } => end, - }; - - let (_, offset) = measure_cursor_and_scroll_offset( - self, - text_bounds, - value, - size, - focus_position, - font, - ); - - offset - } else { - 0.0 - } - } -} - -fn measure_cursor_and_scroll_offset( - renderer: &Renderer, - text_bounds: Rectangle, - value: &text_input::Value, - size: u16, - cursor_index: usize, - font: Font, -) -> (f32, f32) -where - B: Backend + backend::Text, -{ - use iced_native::text_input::Renderer; - - let text_before_cursor = value.until(cursor_index).to_string(); - - let text_value_width = - renderer.measure_value(&text_before_cursor, size, font); - let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0); - - (text_value_width, offset) -} diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index a6527a8dcf..0c335bb6bd 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -395,7 +395,7 @@ where /// able to use a [`Menu`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: text::Renderer { +pub trait Renderer: renderer::Text { /// The [`Menu`] style supported by this renderer. type Style: Default + Clone; } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 2d6979e3c3..084b8d6f0f 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -4,7 +4,6 @@ use crate::progress_bar; use crate::radio; use crate::renderer::{self, Renderer}; use crate::text; -use crate::text_input; use crate::toggler; use crate::{Font, Point, Rectangle, Size, Vector}; @@ -38,10 +37,6 @@ impl Renderer for Null { impl renderer::Text for Null { type Font = Font; - fn fill_text(&mut self, _text: renderer::text::Section<'_, Self::Font>) {} -} - -impl text::Renderer for Null { fn default_size(&self) -> u16 { 20 } @@ -67,25 +62,8 @@ impl text::Renderer for Null { ) -> Option { None } -} -impl text_input::Renderer for Null { - type Style = (); - - fn measure_value(&self, _value: &str, _size: u16, _font: Font) -> f32 { - 0.0 - } - - fn offset( - &self, - _text_bounds: Rectangle, - _font: Font, - _size: u16, - _value: &text_input::Value, - _state: &text_input::State, - ) -> f32 { - 0.0 - } + fn fill_text(&mut self, _text: renderer::text::Section<'_, Self::Font>) {} } impl radio::Renderer for Null { diff --git a/native/src/renderer/text.rs b/native/src/renderer/text.rs index 9234c5874e..80769b625d 100644 --- a/native/src/renderer/text.rs +++ b/native/src/renderer/text.rs @@ -1,10 +1,48 @@ use crate::alignment; -use crate::{Color, Rectangle, Renderer}; +use crate::{Color, Point, Rectangle, Renderer, Size}; + +pub use crate::text::Hit; pub trait Text: Renderer { /// The font type used. type Font: Default + Copy; + /// Returns the default size of [`Text`]. + fn default_size(&self) -> u16; + + /// Measures the text in the given bounds and returns the minimum boundaries + /// that can fit the contents. + fn measure( + &self, + content: &str, + size: u16, + font: Self::Font, + bounds: Size, + ) -> (f32, f32); + + fn measure_width(&self, content: &str, size: u16, font: Self::Font) -> f32 { + let (width, _) = self.measure(content, size, font, Size::INFINITY); + + width + } + + /// Tests whether the provided point is within the boundaries of [`Text`] + /// laid out with the given parameters, returning information about + /// the nearest character. + /// + /// If `nearest_only` is true, the hit test does not consider whether the + /// the point is interior to any glyph bounds, returning only the character + /// with the nearest centeroid. + fn hit_test( + &self, + contents: &str, + size: f32, + font: Self::Font, + bounds: Size, + point: Point, + nearest_only: bool, + ) -> Option; + fn fill_text(&mut self, section: Section<'_, Self::Font>); } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 52113322fb..c307d77611 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -5,7 +5,6 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; -use crate::text; use crate::touch; use crate::{ Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, @@ -30,7 +29,7 @@ use crate::{ /// /// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Checkbox { +pub struct Checkbox { is_checked: bool, on_toggle: Box Message>, label: String, @@ -43,7 +42,7 @@ pub struct Checkbox { style: Renderer::Style, } -impl +impl Checkbox { /// Creates a new [`Checkbox`]. @@ -120,7 +119,7 @@ impl impl Widget for Checkbox where - Renderer: self::Renderer + text::Renderer, + Renderer: self::Renderer + renderer::Text, { fn width(&self) -> Length { self.width @@ -239,7 +238,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + text::Renderer, + Renderer: 'a + self::Renderer + renderer::Text, Message: 'a, { fn from( diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index ed688a614e..761cfcc86b 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -366,7 +366,7 @@ where /// able to use a [`PickList`] in your user interface. /// /// [renderer]: crate::renderer -pub trait Renderer: text::Renderer + menu::Renderer { +pub trait Renderer: renderer::Text + menu::Renderer { /// The default padding of a [`PickList`]. const DEFAULT_PADDING: Padding; diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index ba9bd9aa9f..3d6d22cc00 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -5,7 +5,6 @@ use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; -use crate::text; use crate::touch; use crate::{ Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, @@ -39,7 +38,7 @@ use crate::{ /// /// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Radio { +pub struct Radio { is_selected: bool, on_click: Message, label: String, @@ -52,7 +51,7 @@ pub struct Radio { style: Renderer::Style, } -impl +impl Radio where Message: Clone, @@ -135,7 +134,7 @@ where impl Widget for Radio where Message: Clone, - Renderer: self::Renderer + text::Renderer, + Renderer: self::Renderer + renderer::Text, { fn width(&self) -> Length { self.width @@ -260,7 +259,7 @@ impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer + text::Renderer, + Renderer: 'a + self::Renderer + renderer::Text, { fn from(radio: Radio) -> Element<'a, Message, Renderer> { Element::new(radio) diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index a2438d9c99..78bfa4e2e2 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -24,7 +24,7 @@ use std::hash::Hash; /// /// ![Text drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true) #[derive(Debug)] -pub struct Text { +pub struct Text { content: String, size: Option, color: Option, @@ -35,7 +35,7 @@ pub struct Text { vertical_alignment: alignment::Vertical, } -impl Text { +impl Text { /// Create a new fragment of [`Text`] with the given contents. pub fn new>(label: T) -> Self { Text { @@ -103,7 +103,7 @@ impl Text { impl Widget for Text where - Renderer: self::Renderer, + Renderer: renderer::Text, { fn width(&self) -> Length { self.width @@ -176,55 +176,17 @@ where } } -/// The renderer of a [`Text`] fragment. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use [`Text`] in your user interface. -/// -/// [renderer]: crate::Renderer -pub trait Renderer: renderer::Text { - /// Returns the default size of [`Text`]. - fn default_size(&self) -> u16; - - /// Measures the [`Text`] in the given bounds and returns the minimum - /// boundaries that can fit the contents. - fn measure( - &self, - content: &str, - size: u16, - font: Self::Font, - bounds: Size, - ) -> (f32, f32); - - /// Tests whether the provided point is within the boundaries of [`Text`] - /// laid out with the given parameters, returning information about - /// the nearest character. - /// - /// If `nearest_only` is true, the hit test does not consider whether the - /// the point is interior to any glyph bounds, returning only the character - /// with the nearest centeroid. - fn hit_test( - &self, - contents: &str, - size: f32, - font: Self::Font, - bounds: Size, - point: Point, - nearest_only: bool, - ) -> Option; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: self::Renderer + 'a, + Renderer: renderer::Text + 'a, { fn from(text: Text) -> Element<'a, Message, Renderer> { Element::new(text) } } -impl Clone for Text { +impl Clone for Text { fn clone(&self) -> Self { Self { content: self.content.clone(), diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 93af04d160..22cb9092a4 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -11,20 +11,22 @@ pub use value::Value; use editor::Editor; +use crate::alignment; use crate::event::{self, Event}; use crate::keyboard; use crate::layout; use crate::mouse::{self, click}; use crate::renderer; -use crate::text; use crate::touch; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Padding, Point, Rectangle, - Size, Widget, + Background, Clipboard, Color, Element, Hasher, Layout, Length, Padding, + Point, Rectangle, Size, Vector, Widget, }; use std::u32; +pub use iced_style::text_input::{Style, StyleSheet}; + /// A field that can be filled with text. /// /// # Example @@ -50,7 +52,7 @@ use std::u32; /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) #[allow(missing_debug_implementations)] -pub struct TextInput<'a, Message, Renderer: self::Renderer> { +pub struct TextInput<'a, Message, Renderer: renderer::Text> { state: &'a mut State, placeholder: String, value: Value, @@ -62,13 +64,13 @@ pub struct TextInput<'a, Message, Renderer: self::Renderer> { size: Option, on_change: Box Message>, on_submit: Option, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message, Renderer> TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: renderer::Text, { /// Creates a new [`TextInput`]. /// @@ -98,7 +100,7 @@ where size: None, on_change: Box::new(on_change), on_submit: None, - style: Renderer::Style::default(), + style_sheet: Default::default(), } } @@ -148,8 +150,8 @@ where } /// Sets the style of the [`TextInput`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet.into(); self } @@ -163,7 +165,7 @@ impl<'a, Message, Renderer> Widget for TextInput<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer, + Renderer: renderer::Text, { fn width(&self) -> Length { self.width @@ -229,7 +231,8 @@ where self.value.clone() }; - renderer.find_cursor_position( + find_cursor_position( + renderer, text_layout.bounds(), self.font, self.size, @@ -248,16 +251,16 @@ where if self.is_secure { self.state.cursor.select_all(&self.value); } else { - let position = renderer - .find_cursor_position( - text_layout.bounds(), - self.font, - self.size, - &self.value, - &self.state, - target, - ) - .unwrap_or(0); + let position = find_cursor_position( + renderer, + text_layout.bounds(), + self.font, + self.size, + &self.value, + &self.state, + target, + ) + .unwrap_or(0); self.state.cursor.select_range( self.value.previous_start_of_word(position), @@ -296,16 +299,16 @@ where self.value.clone() }; - let position = renderer - .find_cursor_position( - text_layout.bounds(), - self.font, - self.size, - &value, - &self.state, - target, - ) - .unwrap_or(0); + let position = find_cursor_position( + renderer, + text_layout.bounds(), + self.font, + self.size, + &value, + &self.state, + target, + ) + .unwrap_or(0); self.state.cursor.select_range( self.state.cursor.start(&value), @@ -585,39 +588,164 @@ where cursor_position: Point, _viewport: &Rectangle, ) { + let secure_value = self.is_secure.then(|| self.value.secure()); + let value = secure_value.as_ref().unwrap_or(&self.value); + + let bounds = layout.bounds(); + let text_bounds = layout.children().next().unwrap().bounds(); - // TODO - // let value = value.unwrap_or(&self.value); - // let bounds = layout.bounds(); - // let text_bounds = layout.children().next().unwrap().bounds(); - - // if self.is_secure { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // &value.secure(), - // &self.state, - // &self.style, - // ) - // } else { - // self::Renderer::draw( - // renderer, - // bounds, - // text_bounds, - // cursor_position, - // self.font, - // self.size.unwrap_or(renderer.default_size()), - // &self.placeholder, - // value, - // &self.state, - // &self.style, - // ) - // } + let is_mouse_over = bounds.contains(cursor_position); + + let style = if self.state.is_focused() { + self.style_sheet.focused() + } else if is_mouse_over { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + + let text = value.to_string(); + let size = self.size.unwrap_or(renderer.default_size()); + + let (cursor, offset) = if self.state.is_focused() { + match self.state.cursor.state(&value) { + cursor::State::Index(position) => { + let (text_value_width, offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + position, + self.font, + ); + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + text_value_width, + y: text_bounds.y, + width: 1.0, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.value_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + offset, + ) + } + cursor::State::Selection { start, end } => { + let left = start.min(end); + let right = end.max(start); + + let (left_position, left_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + left, + self.font, + ); + + let (right_position, right_offset) = + measure_cursor_and_scroll_offset( + renderer, + text_bounds, + &value, + size, + right, + self.font, + ); + + let width = right_position - left_position; + + ( + Some(renderer::Quad { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + background: Background::Color( + self.style_sheet.selection_color(), + ), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }), + if end == right { + right_offset + } else { + left_offset + }, + ) + } + } + } else { + (None, 0.0) + }; + + let text_width = renderer.measure_width( + if text.is_empty() { + &self.placeholder + } else { + &text + }, + size, + self.font, + ); + + let render = |renderer: &mut Renderer| { + if let Some(cursor) = cursor { + renderer.fill_rectangle(cursor); + } + + renderer.fill_text(renderer::text::Section { + content: if text.is_empty() { + &self.placeholder + } else { + &text + }, + color: if text.is_empty() { + self.style_sheet.placeholder_color() + } else { + self.style_sheet.value_color() + }, + font: self.font, + bounds: Rectangle { + y: text_bounds.center_y(), + width: f32::INFINITY, + ..text_bounds + }, + size: f32::from(size), + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Center, + }); + }; + + if text_width > text_bounds.width { + renderer.with_layer( + text_bounds, + Vector::new(offset as u32, 0), + render, + ); + } else { + render(renderer); + } } fn hash_layout(&self, state: &mut Hasher) { @@ -632,65 +760,11 @@ where } } -/// The renderer of a [`TextInput`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`TextInput`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: text::Renderer + Sized { - /// The style supported by this renderer. - type Style: Default; - - /// Returns the width of the value of the [`TextInput`]. - fn measure_value(&self, value: &str, size: u16, font: Self::Font) -> f32; - - /// Returns the current horizontal offset of the value of the - /// [`TextInput`]. - /// - /// This is the amount of horizontal scrolling applied when the [`Value`] - /// does not fit the [`TextInput`]. - fn offset( - &self, - text_bounds: Rectangle, - font: Self::Font, - size: u16, - value: &Value, - state: &State, - ) -> f32; - - /// Computes the position of the text cursor at the given X coordinate of - /// a [`TextInput`]. - fn find_cursor_position( - &self, - text_bounds: Rectangle, - font: Self::Font, - size: Option, - value: &Value, - state: &State, - x: f32, - ) -> Option { - let size = size.unwrap_or(self.default_size()); - - let offset = self.offset(text_bounds, font, size, &value, &state); - - self.hit_test( - &value.to_string(), - size.into(), - font, - Size::INFINITY, - Point::new(x + offset, text_bounds.height / 2.0), - true, - ) - .map(text::Hit::cursor) - } -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer, + Renderer: 'a + renderer::Text, { fn from( text_input: TextInput<'a, Message, Renderer>, @@ -781,3 +855,88 @@ mod platform { } } } + +fn offset( + renderer: &Renderer, + text_bounds: Rectangle, + font: Renderer::Font, + size: u16, + value: &Value, + state: &State, +) -> f32 +where + Renderer: renderer::Text, +{ + if state.is_focused() { + let cursor = state.cursor(); + + let focus_position = match cursor.state(value) { + cursor::State::Index(i) => i, + cursor::State::Selection { end, .. } => end, + }; + + let (_, offset) = measure_cursor_and_scroll_offset( + renderer, + text_bounds, + value, + size, + focus_position, + font, + ); + + offset + } else { + 0.0 + } +} + +fn measure_cursor_and_scroll_offset( + renderer: &Renderer, + text_bounds: Rectangle, + value: &Value, + size: u16, + cursor_index: usize, + font: Renderer::Font, +) -> (f32, f32) +where + Renderer: renderer::Text, +{ + let text_before_cursor = value.until(cursor_index).to_string(); + + let text_value_width = + renderer.measure_width(&text_before_cursor, size, font); + + let offset = ((text_value_width + 5.0) - text_bounds.width).max(0.0); + + (text_value_width, offset) +} + +/// Computes the position of the text cursor at the given X coordinate of +/// a [`TextInput`]. +fn find_cursor_position( + renderer: &Renderer, + text_bounds: Rectangle, + font: Renderer::Font, + size: Option, + value: &Value, + state: &State, + x: f32, +) -> Option +where + Renderer: renderer::Text, +{ + let size = size.unwrap_or(renderer.default_size()); + + let offset = offset(renderer, text_bounds, font, size, &value, &state); + + renderer + .hit_test( + &value.to_string(), + size.into(), + font, + Size::INFINITY, + Point::new(x + offset, text_bounds.height / 2.0), + true, + ) + .map(renderer::text::Hit::cursor) +} diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 6c7fa97bb6..46a9850bbe 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -6,7 +6,6 @@ use crate::event; use crate::layout; use crate::mouse; use crate::renderer; -use crate::text; use crate::{ Alignment, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Row, Text, Widget, @@ -28,7 +27,7 @@ use crate::{ /// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b)); /// ``` #[allow(missing_debug_implementations)] -pub struct Toggler { +pub struct Toggler { is_active: bool, on_toggle: Box Message>, label: Option, @@ -41,7 +40,7 @@ pub struct Toggler { style: Renderer::Style, } -impl +impl Toggler { /// Creates a new [`Toggler`]. @@ -119,7 +118,7 @@ impl impl Widget for Toggler where - Renderer: self::Renderer + text::Renderer, + Renderer: self::Renderer + renderer::Text, { fn width(&self) -> Length { self.width @@ -246,7 +245,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + text::Renderer, + Renderer: 'a + self::Renderer + renderer::Text, Message: 'a, { fn from( diff --git a/native/src/widget/tooltip.rs b/native/src/widget/tooltip.rs index 4e8483ad3a..c2bc8af0e0 100644 --- a/native/src/widget/tooltip.rs +++ b/native/src/widget/tooltip.rs @@ -7,7 +7,7 @@ use crate::event; use crate::layout; use crate::renderer; use crate::widget::container; -use crate::widget::text::{self, Text}; +use crate::widget::text::Text; use crate::{Clipboard, Element, Event, Hasher, Layout, Length, Point, Widget}; /// An element to display a widget over another. @@ -160,7 +160,7 @@ where /// /// [`Tooltip`]: struct.Tooltip.html /// [renderer]: ../../renderer/index.html -pub trait Renderer: crate::Renderer + text::Renderer { +pub trait Renderer: renderer::Text { /// The default padding of a [`Tooltip`] drawn by this renderer. const DEFAULT_PADDING: u16; } diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 19acea65dd..13b64cd5ae 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -73,17 +73,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/text_input.rs b/web/src/widget/text_input.rs index e4877f2afd..99f765bb7a 100644 --- a/web/src/widget/text_input.rs +++ b/web/src/widget/text_input.rs @@ -39,7 +39,7 @@ pub struct TextInput<'a, Message> { size: Option, on_change: Rc Message>>, on_submit: Option, - style_sheet: Box, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message> TextInput<'a, Message> { @@ -112,8 +112,8 @@ impl<'a, Message> TextInput<'a, Message> { } /// Sets the style of the [`TextInput`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style_sheet = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } From cc560aca181202e827bfdc9afb6fc9fbe42f54d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 18:45:45 +0700 Subject: [PATCH 26/92] Implement `Widget::mouse_interaction` for `TextInput` --- native/src/widget/text_input.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 22cb9092a4..0a2b19a3a7 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -580,6 +580,19 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + if layout.bounds().contains(cursor_position) { + mouse::Interaction::Text + } else { + mouse::Interaction::default() + } + } + fn draw( &self, renderer: &mut Renderer, From d39ad717ed0ab85acbe935d7ab883166b36e7bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 19:06:53 +0700 Subject: [PATCH 27/92] Wire up styling to `Radio` in `iced_native` --- examples/scrollable/src/main.rs | 2 +- examples/scrollable/src/style.rs | 4 +- examples/styling/src/main.rs | 6 +-- glow/src/widget/radio.rs | 2 +- graphics/src/widget/radio.rs | 17 ++---- native/src/renderer/null.rs | 7 --- native/src/widget/radio.rs | 89 ++++++++++---------------------- native/src/widget/text.rs | 72 +++++++++++++++++--------- style/src/radio.rs | 13 +---- web/src/widget/radio.rs | 18 +++---- wgpu/src/widget/radio.rs | 2 +- 11 files changed, 97 insertions(+), 135 deletions(-) diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index bc20d428c4..2f1c5676cf 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -76,7 +76,7 @@ impl Sandbox for ScrollableDemo { Some(*theme), Message::ThemeChanged, ) - .style(*theme), + .style(theme.clone().into()), ) }, ); diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs index 66f3b9d3e3..d7d6437490 100644 --- a/examples/scrollable/src/style.rs +++ b/examples/scrollable/src/style.rs @@ -25,11 +25,11 @@ impl From for &'static dyn container::StyleSheet { } } -impl From for Box { +impl From for &'static dyn radio::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Radio.into(), + Theme::Dark => &dark::Radio, } } } diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index cca94c8f14..7e9b01ab1f 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -64,7 +64,7 @@ impl Sandbox for Styling { Some(self.theme), Message::ThemeChanged, ) - .style(self.theme), + .style(self.theme.into()), ) }, ); @@ -185,11 +185,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn radio::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Radio.into(), + Theme::Dark => &dark::Radio, } } } diff --git a/glow/src/widget/radio.rs b/glow/src/widget/radio.rs index 0b843d1f7d..c200f1a860 100644 --- a/glow/src/widget/radio.rs +++ b/glow/src/widget/radio.rs @@ -7,4 +7,4 @@ pub use iced_graphics::radio::{Style, StyleSheet}; /// /// This is an alias of an `iced_native` radio button with an /// `iced_wgpu::Renderer`. -pub type Radio = iced_native::Radio; +pub type Radio<'a, Message> = iced_native::Radio<'a, Message, Renderer>; diff --git a/graphics/src/widget/radio.rs b/graphics/src/widget/radio.rs index cd83f2ff29..4185bd614f 100644 --- a/graphics/src/widget/radio.rs +++ b/graphics/src/widget/radio.rs @@ -1,6 +1,5 @@ //! Create choices using radio buttons. -use crate::{Backend, Renderer}; -use iced_native::radio; +use crate::Renderer; pub use iced_style::radio::{Style, StyleSheet}; @@ -8,15 +7,5 @@ pub use iced_style::radio::{Style, StyleSheet}; /// /// This is an alias of an `iced_native` radio button with an /// `iced_wgpu::Renderer`. -pub type Radio = - iced_native::Radio>; - -impl radio::Renderer for Renderer -where - B: Backend, -{ - type Style = Box; - - const DEFAULT_SIZE: u16 = 28; - const DEFAULT_SPACING: u16 = 15; -} +pub type Radio<'a, Message, Backend> = + iced_native::Radio<'a, Message, Renderer>; diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 084b8d6f0f..40fd5995a4 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -66,13 +66,6 @@ impl renderer::Text for Null { fn fill_text(&mut self, _text: renderer::text::Section<'_, Self::Font>) {} } -impl radio::Renderer for Null { - type Style = (); - - const DEFAULT_SIZE: u16 = 20; - const DEFAULT_SPACING: u16 = 15; -} - impl checkbox::Renderer for Null { type Style = (); diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 3d6d22cc00..0e1023b508 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -11,12 +11,14 @@ use crate::{ Rectangle, Row, Text, Widget, }; +pub use iced_style::radio::{Style, StyleSheet}; + /// A circular button representing a choice. /// /// # Example /// ``` -/// # type Radio = -/// # iced_native::Radio; +/// # type Radio<'a, Message> = +/// # iced_native::Radio<'a, Message, iced_native::renderer::Null>; /// # /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Choice { @@ -38,7 +40,7 @@ use crate::{ /// /// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Radio { +pub struct Radio<'a, Message, Renderer: renderer::Text> { is_selected: bool, on_click: Message, label: String, @@ -48,14 +50,19 @@ pub struct Radio { text_size: Option, text_color: Option, font: Renderer::Font, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } -impl - Radio +impl<'a, Message, Renderer: renderer::Text> Radio<'a, Message, Renderer> where Message: Clone, { + /// The default size of a [`Radio`] button. + pub const DEFAULT_SIZE: u16 = 28; + + /// The default spacing of a [`Radio`] button. + pub const DEFAULT_SPACING: u16 = 15; + /// Creates a new [`Radio`] button. /// /// It expects: @@ -79,12 +86,12 @@ where on_click: f(value), label: label.into(), width: Length::Shrink, - size: ::DEFAULT_SIZE, - spacing: Renderer::DEFAULT_SPACING, //15 + size: Self::DEFAULT_SIZE, + spacing: Self::DEFAULT_SPACING, //15 text_size: None, text_color: None, font: Default::default(), - style: Renderer::Style::default(), + style_sheet: Default::default(), } } @@ -125,16 +132,17 @@ where } /// Sets the style of the [`Radio`] button. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } -impl Widget for Radio +impl<'a, Message, Renderer> Widget + for Radio<'a, Message, Renderer> where Message: Clone, - Renderer: self::Renderer + renderer::Text, + Renderer: renderer::Text, { fn width(&self) -> Length { self.width @@ -198,36 +206,6 @@ where cursor_position: Point, _viewport: &Rectangle, ) { - // TODO - // let bounds = layout.bounds(); - // let mut children = layout.children(); - - // let radio_layout = children.next().unwrap(); - // let label_layout = children.next().unwrap(); - // let radio_bounds = radio_layout.bounds(); - - // let label = text::Renderer::draw( - // renderer, - // defaults, - // label_layout.bounds(), - // &self.label, - // self.text_size.unwrap_or(renderer.default_size()), - // self.font, - // self.text_color, - // alignment::Horizontal::Left, - // alignment::Vertical::Center, - // ); - - // let is_mouse_over = bounds.contains(cursor_position); - - // self::Renderer::draw( - // renderer, - // radio_bounds, - // self.is_selected, - // is_mouse_over, - // label, - // &self.style, - // ) } fn hash_layout(&self, state: &mut Hasher) { @@ -238,30 +216,15 @@ where } } -/// The renderer of a [`Radio`] button. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Radio`] button in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer { - /// The style supported by this renderer. - type Style: Default; - - /// The default size of a [`Radio`] button. - const DEFAULT_SIZE: u16; - - /// The default spacing of a [`Radio`] button. - const DEFAULT_SPACING: u16; -} - -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, - Renderer: 'a + self::Renderer + renderer::Text, + Renderer: 'a + renderer::Text, { - fn from(radio: Radio) -> Element<'a, Message, Renderer> { + fn from( + radio: Radio<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { Element::new(radio) } } diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index 78bfa4e2e2..2432dbc855 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -140,29 +140,17 @@ where _cursor_position: Point, _viewport: &Rectangle, ) { - let bounds = layout.bounds(); - - let x = match self.horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.center_x(), - alignment::Horizontal::Right => bounds.x + bounds.width, - }; - - let y = match self.vertical_alignment { - alignment::Vertical::Top => bounds.y, - alignment::Vertical::Center => bounds.center_y(), - alignment::Vertical::Bottom => bounds.y + bounds.height, - }; - - renderer.fill_text(renderer::text::Section { - content: &self.content, - size: f32::from(self.size.unwrap_or(renderer.default_size())), - bounds: Rectangle { x, y, ..bounds }, - color: self.color.unwrap_or(style.text_color), - font: self.font, - horizontal_alignment: self.horizontal_alignment, - vertical_alignment: self.vertical_alignment, - }); + draw( + renderer, + style, + layout, + &self.content, + self.font, + self.size, + self.color, + self.horizontal_alignment, + self.vertical_alignment, + ); } fn hash_layout(&self, state: &mut Hasher) { @@ -176,6 +164,44 @@ where } } +pub fn draw( + renderer: &mut Renderer, + style: &renderer::Style, + layout: Layout<'_>, + content: &str, + font: Renderer::Font, + size: Option, + color: Option, + horizontal_alignment: alignment::Horizontal, + vertical_alignment: alignment::Vertical, +) where + Renderer: renderer::Text, +{ + let bounds = layout.bounds(); + + let x = match horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => bounds.center_x(), + alignment::Horizontal::Right => bounds.x + bounds.width, + }; + + let y = match vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => bounds.center_y(), + alignment::Vertical::Bottom => bounds.y + bounds.height, + }; + + renderer.fill_text(renderer::text::Section { + content, + size: f32::from(size.unwrap_or(renderer.default_size())), + bounds: Rectangle { x, y, ..bounds }, + color: color.unwrap_or(style.text_color), + font, + horizontal_alignment, + vertical_alignment, + }); +} + impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where diff --git a/style/src/radio.rs b/style/src/radio.rs index c41b70c031..add1275476 100644 --- a/style/src/radio.rs +++ b/style/src/radio.rs @@ -37,17 +37,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index fbc88d29cb..5e9eaa3f52 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -31,17 +31,17 @@ use dodrio::bumpalo; /// /// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Radio { +pub struct Radio<'a, Message> { is_selected: bool, on_click: Message, label: String, id: Option, name: Option, #[allow(dead_code)] - style: Box, + style_sheet: &'a dyn StyleSheet, } -impl Radio { +impl<'a, Message> Radio<'a, Message> { /// Creates a new [`Radio`] button. /// /// It expects: @@ -66,13 +66,13 @@ impl Radio { label: label.into(), id: None, name: None, - style: Default::default(), + style_sheet: Default::default(), } } /// Sets the style of the [`Radio`] button. - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } @@ -89,7 +89,7 @@ impl Radio { } } -impl Widget for Radio +impl<'a, Message> Widget for Radio<'a, Message> where Message: 'static + Clone, { @@ -142,11 +142,11 @@ where } } -impl<'a, Message> From> for Element<'a, Message> +impl<'a, Message> From> for Element<'a, Message> where Message: 'static + Clone, { - fn from(radio: Radio) -> Element<'a, Message> { + fn from(radio: Radio<'a, Message>) -> Element<'a, Message> { Element::new(radio) } } diff --git a/wgpu/src/widget/radio.rs b/wgpu/src/widget/radio.rs index 0b843d1f7d..c200f1a860 100644 --- a/wgpu/src/widget/radio.rs +++ b/wgpu/src/widget/radio.rs @@ -7,4 +7,4 @@ pub use iced_graphics::radio::{Style, StyleSheet}; /// /// This is an alias of an `iced_native` radio button with an /// `iced_wgpu::Renderer`. -pub type Radio = iced_native::Radio; +pub type Radio<'a, Message> = iced_native::Radio<'a, Message, Renderer>; From 47c8f6ceee801bd2896cd075097dcb075ff070d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 19:07:09 +0700 Subject: [PATCH 28/92] Implement `Widget::draw` for `Radio` --- native/src/widget/radio.rs | 63 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 0e1023b508..ebd145173a 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -1,14 +1,16 @@ //! Create choices using radio buttons. use std::hash::Hash; +use crate::alignment; use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; +use crate::text; use crate::touch; use crate::{ - Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, - Rectangle, Row, Text, Widget, + Alignment, Background, Clipboard, Color, Element, Hasher, Layout, Length, + Point, Rectangle, Row, Text, Widget, }; pub use iced_style::radio::{Style, StyleSheet}; @@ -206,6 +208,63 @@ where cursor_position: Point, _viewport: &Rectangle, ) { + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + let mut children = layout.children(); + + { + let layout = children.next().unwrap(); + let bounds = layout.bounds(); + + let size = bounds.width; + let dot_size = size / 2.0; + + let style = if is_mouse_over { + self.style_sheet.hovered() + } else { + self.style_sheet.active() + }; + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style.background, + border_radius: size / 2.0, + border_width: style.border_width, + border_color: style.border_color, + }); + + if self.is_selected { + renderer.fill_rectangle(renderer::Quad { + bounds: Rectangle { + x: bounds.x + dot_size / 2.0, + y: bounds.y + dot_size / 2.0, + width: bounds.width - dot_size, + height: bounds.height - dot_size, + }, + background: Background::Color(style.dot_color), + border_radius: dot_size / 2.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + } + } + + { + let label_layout = children.next().unwrap(); + + text::draw( + renderer, + style, + label_layout, + &self.label, + self.font, + self.text_size, + self.text_color, + alignment::Horizontal::Left, + alignment::Vertical::Center, + ); + } } fn hash_layout(&self, state: &mut Hasher) { From b6ae87ae55301d6c15f56e6c7df077aabe6383de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 20 Oct 2021 19:08:13 +0700 Subject: [PATCH 29/92] Implement `Widget::mouse_interaction` for `Radio` --- native/src/widget/radio.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index ebd145173a..5601a597d0 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -200,6 +200,19 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + if layout.bounds().contains(cursor_position) { + mouse::Interaction::Pointer + } else { + mouse::Interaction::default() + } + } + fn draw( &self, renderer: &mut Renderer, From 7c08c6bd138207b862933ee479752a4f1d18c4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Oct 2021 18:50:27 +0700 Subject: [PATCH 30/92] Remove `Renderer` trait for `Checkbox` --- examples/styling/src/main.rs | 6 ++-- glow/src/widget/checkbox.rs | 2 +- graphics/src/widget/checkbox.rs | 17 ++-------- native/src/renderer/null.rs | 9 ------ native/src/widget/checkbox.rs | 55 +++++++++++++-------------------- style/src/checkbox.rs | 13 ++------ web/src/widget/checkbox.rs | 18 +++++------ wgpu/src/widget/checkbox.rs | 2 +- 8 files changed, 40 insertions(+), 82 deletions(-) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 7e9b01ab1f..5ce54e23dd 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -108,7 +108,7 @@ impl Sandbox for Styling { "Check me!", Message::CheckboxToggled, ) - .style(self.theme); + .style(self.theme.into()); let toggler = Toggler::new( self.toggler_value, @@ -239,11 +239,11 @@ mod style { } } - impl From for Box { + impl From for &'static dyn checkbox::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Checkbox.into(), + Theme::Dark => &dark::Checkbox, } } } diff --git a/glow/src/widget/checkbox.rs b/glow/src/widget/checkbox.rs index d27d77cc52..9fd003d4d6 100644 --- a/glow/src/widget/checkbox.rs +++ b/glow/src/widget/checkbox.rs @@ -6,4 +6,4 @@ pub use iced_graphics::checkbox::{Style, StyleSheet}; /// A box that can be checked. /// /// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`. -pub type Checkbox = iced_native::Checkbox; +pub type Checkbox<'a, Message> = iced_native::Checkbox<'a, Message, Renderer>; diff --git a/graphics/src/widget/checkbox.rs b/graphics/src/widget/checkbox.rs index 3b75652539..a458d85c91 100644 --- a/graphics/src/widget/checkbox.rs +++ b/graphics/src/widget/checkbox.rs @@ -1,23 +1,10 @@ //! Show toggle controls using checkboxes. -use crate::backend::{self, Backend}; use crate::Renderer; -use iced_native::checkbox; - pub use iced_style::checkbox::{Style, StyleSheet}; /// A box that can be checked. /// /// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`. -pub type Checkbox = - iced_native::Checkbox>; - -impl checkbox::Renderer for Renderer -where - B: Backend + backend::Text, -{ - type Style = Box; - - const DEFAULT_SIZE: u16 = 20; - const DEFAULT_SPACING: u16 = 15; -} +pub type Checkbox<'a, Message, Backend> = + iced_native::Checkbox<'a, Message, Renderer>; diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 40fd5995a4..010305a219 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,7 +1,5 @@ -use crate::checkbox; use crate::pane_grid; use crate::progress_bar; -use crate::radio; use crate::renderer::{self, Renderer}; use crate::text; use crate::toggler; @@ -66,13 +64,6 @@ impl renderer::Text for Null { fn fill_text(&mut self, _text: renderer::text::Section<'_, Self::Font>) {} } -impl checkbox::Renderer for Null { - type Style = (); - - const DEFAULT_SIZE: u16 = 20; - const DEFAULT_SPACING: u16 = 15; -} - impl progress_bar::Renderer for Null { type Style = (); diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index c307d77611..c1408d730c 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -11,6 +11,8 @@ use crate::{ Rectangle, Row, Text, Widget, }; +pub use iced_style::checkbox::{Style, StyleSheet}; + /// A box that can be checked. /// /// # Example @@ -29,7 +31,7 @@ use crate::{ /// /// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Checkbox { +pub struct Checkbox<'a, Message, Renderer: renderer::Text> { is_checked: bool, on_toggle: Box Message>, label: String, @@ -39,12 +41,16 @@ pub struct Checkbox { text_size: Option, font: Renderer::Font, text_color: Option, - style: Renderer::Style, + style_sheet: &'a dyn StyleSheet, } -impl - Checkbox -{ +impl<'a, Message, Renderer: renderer::Text> Checkbox<'a, Message, Renderer> { + /// The default size of a [`Checkbox`]. + const DEFAULT_SIZE: u16 = 20; + + /// The default spacing of a [`Checkbox`]. + const DEFAULT_SPACING: u16 = 15; + /// Creates a new [`Checkbox`]. /// /// It expects: @@ -62,12 +68,12 @@ impl on_toggle: Box::new(f), label: label.into(), width: Length::Shrink, - size: ::DEFAULT_SIZE, - spacing: Renderer::DEFAULT_SPACING, + size: Self::DEFAULT_SIZE, + spacing: Self::DEFAULT_SPACING, text_size: None, font: Renderer::Font::default(), text_color: None, - style: Renderer::Style::default(), + style_sheet: Default::default(), } } @@ -110,16 +116,16 @@ impl } /// Sets the style of the [`Checkbox`]. - pub fn style(mut self, style: impl Into) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } -impl Widget - for Checkbox +impl<'a, Message, Renderer> Widget + for Checkbox<'a, Message, Renderer> where - Renderer: self::Renderer + renderer::Text, + Renderer: renderer::Text, { fn width(&self) -> Length { self.width @@ -218,31 +224,14 @@ where } } -/// The renderer of a [`Checkbox`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`Checkbox`] in your user interface. -/// -/// [renderer]: crate::Renderer -pub trait Renderer: crate::Renderer { - /// The style supported by this renderer. - type Style: Default; - - /// The default size of a [`Checkbox`]. - const DEFAULT_SIZE: u16; - - /// The default spacing of a [`Checkbox`]. - const DEFAULT_SPACING: u16; -} - -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer + renderer::Text, + Renderer: 'a + renderer::Text, Message: 'a, { fn from( - checkbox: Checkbox, + checkbox: Checkbox<'a, Message, Renderer>, ) -> Element<'a, Message, Renderer> { Element::new(checkbox) } diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs index 566136bbc9..4c88faaae6 100644 --- a/style/src/checkbox.rs +++ b/style/src/checkbox.rs @@ -39,17 +39,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 43110aa71a..095ef47d0a 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -24,17 +24,17 @@ use std::rc::Rc; /// /// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true) #[allow(missing_debug_implementations)] -pub struct Checkbox { +pub struct Checkbox<'a, Message> { is_checked: bool, on_toggle: Rc Message>, label: String, id: Option, width: Length, #[allow(dead_code)] - style: Box, + style_sheet: &'a dyn StyleSheet, } -impl Checkbox { +impl<'a, Message> Checkbox<'a, Message> { /// Creates a new [`Checkbox`]. /// /// It expects: @@ -53,7 +53,7 @@ impl Checkbox { label: label.into(), id: None, width: Length::Shrink, - style: Default::default(), + style_sheet: Default::default(), } } @@ -64,8 +64,8 @@ impl Checkbox { } /// Sets the style of the [`Checkbox`]. - pub fn style(mut self, style: impl Into>) -> Self { - self.style = style.into(); + pub fn style(mut self, style_sheet: &'a dyn StyleSheet) -> Self { + self.style_sheet = style_sheet; self } @@ -76,7 +76,7 @@ impl Checkbox { } } -impl Widget for Checkbox +impl<'a, Message> Widget for Checkbox<'a, Message> where Message: 'static, { @@ -137,11 +137,11 @@ where } } -impl<'a, Message> From> for Element<'a, Message> +impl<'a, Message> From> for Element<'a, Message> where Message: 'static, { - fn from(checkbox: Checkbox) -> Element<'a, Message> { + fn from(checkbox: Checkbox<'a, Message>) -> Element<'a, Message> { Element::new(checkbox) } } diff --git a/wgpu/src/widget/checkbox.rs b/wgpu/src/widget/checkbox.rs index d27d77cc52..9fd003d4d6 100644 --- a/wgpu/src/widget/checkbox.rs +++ b/wgpu/src/widget/checkbox.rs @@ -6,4 +6,4 @@ pub use iced_graphics::checkbox::{Style, StyleSheet}; /// A box that can be checked. /// /// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`. -pub type Checkbox = iced_native::Checkbox; +pub type Checkbox<'a, Message> = iced_native::Checkbox<'a, Message, Renderer>; From 1397be38ca2caaf5c49ca51e164a28b63f9e461b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Oct 2021 19:06:22 +0700 Subject: [PATCH 31/92] Implement `Widget::draw` for `Checkbox` --- graphics/src/renderer.rs | 4 ++ native/src/renderer/null.rs | 4 ++ native/src/renderer/text.rs | 13 ++++++ native/src/widget/checkbox.rs | 81 +++++++++++++++++++++++++---------- 4 files changed, 79 insertions(+), 23 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index f8c6704738..5e74019fc1 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -91,6 +91,10 @@ where { type Font = Font; + const ICON_FONT: Font = B::ICON_FONT; + const CHECKMARK_ICON: char = B::CHECKMARK_ICON; + const ARROW_DOWN_ICON: char = B::ARROW_DOWN_ICON; + fn default_size(&self) -> u16 { self.backend().default_size() } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 010305a219..351f623391 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -35,6 +35,10 @@ impl Renderer for Null { impl renderer::Text for Null { type Font = Font; + const ICON_FONT: Font = Font::Default; + const CHECKMARK_ICON: char = '0'; + const ARROW_DOWN_ICON: char = '0'; + fn default_size(&self) -> u16 { 20 } diff --git a/native/src/renderer/text.rs b/native/src/renderer/text.rs index 80769b625d..fbf116a140 100644 --- a/native/src/renderer/text.rs +++ b/native/src/renderer/text.rs @@ -7,6 +7,19 @@ pub trait Text: Renderer { /// The font type used. type Font: Default + Copy; + /// The icon font of the backend. + const ICON_FONT: Self::Font; + + /// The `char` representing a ✔ icon in the [`ICON_FONT`]. + /// + /// [`ICON_FONT`]: Self::ICON_FONT + const CHECKMARK_ICON: char; + + /// The `char` representing a ▼ icon in the built-in [`ICON_FONT`]. + /// + /// [`ICON_FONT`]: Self::ICON_FONT + const ARROW_DOWN_ICON: char; + /// Returns the default size of [`Text`]. fn default_size(&self) -> u16; diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index c1408d730c..babd74a58a 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -1,10 +1,12 @@ //! Show toggle controls using checkboxes. use std::hash::Hash; +use crate::alignment; use crate::event::{self, Event}; use crate::layout; use crate::mouse; use crate::renderer; +use crate::text; use crate::touch; use crate::{ Alignment, Clipboard, Color, Element, Hasher, Layout, Length, Point, @@ -18,7 +20,7 @@ pub use iced_style::checkbox::{Style, StyleSheet}; /// # Example /// /// ``` -/// # type Checkbox = iced_native::Checkbox; +/// # type Checkbox<'a, Message> = iced_native::Checkbox<'a, Message, iced_native::renderer::Null>; /// # /// pub enum Message { /// CheckboxToggled(bool), @@ -192,28 +194,61 @@ where cursor_position: Point, _viewport: &Rectangle, ) { - // let bounds = layout.bounds(); - // let mut children = layout.children(); - - // let checkbox_layout = children.next().unwrap(); - // let label_layout = children.next().unwrap(); - // let checkbox_bounds = checkbox_layout.bounds(); - - // let label = text::Renderer::draw( - // renderer, - // defaults, - // label_layout.bounds(), - // &self.label, - // self.text_size.unwrap_or(renderer.default_size()), - // self.font, - // self.text_color, - // alignment::Horizontal::Left, - // alignment::Vertical::Center, - // ); - - // let is_mouse_over = bounds.contains(cursor_position); - - // TODO + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + let mut children = layout.children(); + + { + let layout = children.next().unwrap(); + let bounds = layout.bounds(); + + let style = if is_mouse_over { + self.style_sheet.hovered(self.is_checked) + } else { + self.style_sheet.active(self.is_checked) + }; + + renderer.fill_rectangle(renderer::Quad { + bounds, + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, + }); + + if self.is_checked { + renderer.fill_text(renderer::text::Section { + content: &Renderer::CHECKMARK_ICON.to_string(), + font: Renderer::ICON_FONT, + size: bounds.height * 0.7, + bounds: Rectangle { + x: bounds.center_x(), + y: bounds.center_y(), + ..bounds + }, + color: style.checkmark_color, + horizontal_alignment: alignment::Horizontal::Center, + vertical_alignment: alignment::Vertical::Center, + }); + } + } + + { + let label_layout = children.next().unwrap(); + + text::draw( + renderer, + style, + label_layout, + &self.label, + self.font, + self.text_size, + self.text_color, + alignment::Horizontal::Left, + alignment::Vertical::Center, + ); + } } fn hash_layout(&self, state: &mut Hasher) { From e7ae4422312e045a1fbbaa7655d4bf78ca6954a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 21 Oct 2021 19:07:30 +0700 Subject: [PATCH 32/92] Implement `Widget::mouse_interaction` for `Checkbox` --- native/src/widget/checkbox.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index babd74a58a..83bc2aa3ff 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -186,6 +186,19 @@ where event::Status::Ignored } + fn mouse_interaction( + &self, + layout: Layout<'_>, + _viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + if layout.bounds().contains(cursor_position) { + mouse::Interaction::Pointer + } else { + mouse::Interaction::default() + } + } + fn draw( &self, renderer: &mut Renderer, From 1afbc98544327b5454b862bec938dc76c4d38fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 25 Oct 2021 15:03:57 +0700 Subject: [PATCH 33/92] Implement `Widget::draw` for `Image` --- graphics/src/renderer.rs | 4 ++++ graphics/src/widget/image.rs | 10 +++++++++- native/src/widget/image.rs | 5 ++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 5e74019fc1..d90a3f1eb6 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -26,6 +26,10 @@ impl Renderer { &self.backend } + pub fn draw_primitive(&mut self, primitive: Primitive) { + self.primitives.push(primitive); + } + pub fn present(&mut self, f: impl FnOnce(&mut B, &[Primitive])) { f(&mut self.backend, &self.primitives); } diff --git a/graphics/src/widget/image.rs b/graphics/src/widget/image.rs index b55ba32fe5..dc2d165773 100644 --- a/graphics/src/widget/image.rs +++ b/graphics/src/widget/image.rs @@ -3,8 +3,9 @@ pub mod viewer; use crate::backend::{self, Backend}; -use crate::Renderer; +use crate::{Primitive, Renderer}; use iced_native::image; +use iced_native::Layout; pub use iced_native::image::{Handle, Image, Viewer}; @@ -15,4 +16,11 @@ where fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { self.backend().dimensions(handle) } + + fn draw(&mut self, handle: image::Handle, layout: Layout<'_>) { + self.draw_primitive(Primitive::Image { + handle, + bounds: layout.bounds(), + }) + } } diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index b6b8dc1f6d..b61904f4b5 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -98,7 +98,7 @@ where _cursor_position: Point, _viewport: &Rectangle, ) { - // TODO + renderer.draw(self.handle.clone(), layout); } fn hash_layout(&self, state: &mut Hasher) { @@ -226,6 +226,9 @@ impl std::fmt::Debug for Data { pub trait Renderer: crate::Renderer { /// Returns the dimensions of an [`Image`] located on the given path. fn dimensions(&self, handle: &Handle) -> (u32, u32); + + // Draws an [`Image`]. + fn draw(&mut self, handle: Handle, layout: Layout<'_>); } impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> From 41394b4e90a81a43c796c070e706e6aa4d8652bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 25 Oct 2021 15:37:28 +0700 Subject: [PATCH 34/92] Implement `Widget::draw` for `PaneGrid` --- graphics/src/widget/pane_grid.rs | 10 +- native/src/renderer/null.rs | 4 - native/src/widget/pane_grid.rs | 217 +++++++++++++++-------- native/src/widget/pane_grid/content.rs | 79 +++++---- native/src/widget/pane_grid/title_bar.rs | 77 ++++---- style/src/pane_grid.rs | 13 +- 6 files changed, 232 insertions(+), 168 deletions(-) diff --git a/graphics/src/widget/pane_grid.rs b/graphics/src/widget/pane_grid.rs index 8c6b0f82a3..4dc42e4121 100644 --- a/graphics/src/widget/pane_grid.rs +++ b/graphics/src/widget/pane_grid.rs @@ -7,8 +7,7 @@ //! drag and drop, and hotkey support. //! //! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid -use crate::{Backend, Renderer}; -use iced_native::pane_grid; +use crate::Renderer; pub use iced_native::pane_grid::{ Axis, Configuration, Content, Direction, DragEvent, Node, Pane, @@ -25,10 +24,3 @@ pub use iced_style::pane_grid::{Line, StyleSheet}; /// This is an alias of an `iced_native` pane grid with an `iced_wgpu::Renderer`. pub type PaneGrid<'a, Message, Backend> = iced_native::PaneGrid<'a, Message, Renderer>; - -impl pane_grid::Renderer for Renderer -where - B: Backend, -{ - type Style = Box; -} diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 351f623391..485c5b2c64 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -74,10 +74,6 @@ impl progress_bar::Renderer for Null { const DEFAULT_HEIGHT: u16 = 30; } -impl pane_grid::Renderer for Null { - type Style = (); -} - impl toggler::Renderer for Null { type Style = (); diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index afecd68195..cb386959b7 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -34,10 +34,12 @@ use crate::overlay; use crate::renderer; use crate::touch; use crate::{ - Clipboard, Element, Hasher, Layout, Length, Point, Rectangle, Size, Vector, - Widget, + Clipboard, Color, Element, Hasher, Layout, Length, Point, Rectangle, Size, + Vector, Widget, }; +pub use iced_style::pane_grid::{Line, StyleSheet}; + /// A collection of panes distributed using either vertical or horizontal splits /// to completely fill the space available. /// @@ -88,7 +90,7 @@ use crate::{ /// .on_resize(10, Message::PaneResized); /// ``` #[allow(missing_debug_implementations)] -pub struct PaneGrid<'a, Message, Renderer: self::Renderer> { +pub struct PaneGrid<'a, Message, Renderer> { state: &'a mut state::Internal, elements: Vec<(Pane, Content<'a, Message, Renderer>)>, width: Length, @@ -97,12 +99,12 @@ pub struct PaneGrid<'a, Message, Renderer: self::Renderer> { on_click: Option Message + 'a>>, on_drag: Option Message + 'a>>, on_resize: Option<(u16, Box Message + 'a>)>, - style: ::Style, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { /// Creates a [`PaneGrid`] with the given [`State`] and view function. /// @@ -129,7 +131,7 @@ where on_click: None, on_drag: None, on_resize: None, - style: Default::default(), + style_sheet: Default::default(), } } @@ -189,18 +191,15 @@ where } /// Sets the style of the [`PaneGrid`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { - self.style = style.into(); + pub fn style(mut self, style: &'a dyn StyleSheet) -> Self { + self.style_sheet = style; self } } impl<'a, Message, Renderer> PaneGrid<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn click_pane( &mut self, @@ -317,7 +316,7 @@ pub struct ResizeEvent { impl<'a, Message, Renderer> Widget for PaneGrid<'a, Message, Renderer> where - Renderer: self::Renderer, + Renderer: crate::Renderer, { fn width(&self) -> Length { self.width @@ -480,55 +479,136 @@ where cursor_position: Point, viewport: &Rectangle, ) { - // let picked_split = self - // .state - // .picked_split() - // .and_then(|(split, axis)| { - // let bounds = layout.bounds(); - - // let splits = self - // .state - // .split_regions(f32::from(self.spacing), bounds.size()); - - // let (_axis, region, ratio) = splits.get(&split)?; - - // let region = axis.split_line_bounds( - // *region, - // *ratio, - // f32::from(self.spacing), - // ); - - // Some((axis, region + Vector::new(bounds.x, bounds.y), true)) - // }) - // .or_else(|| match self.on_resize { - // Some((leeway, _)) => { - // let bounds = layout.bounds(); - - // let relative_cursor = Point::new( - // cursor_position.x - bounds.x, - // cursor_position.y - bounds.y, - // ); - - // let splits = self - // .state - // .split_regions(f32::from(self.spacing), bounds.size()); - - // let (_split, axis, region) = hovered_split( - // splits.iter(), - // f32::from(self.spacing + leeway), - // relative_cursor, - // )?; - - // Some(( - // axis, - // region + Vector::new(bounds.x, bounds.y), - // false, - // )) - // } - // None => None, - // }); - - // TODO + let picked_pane = self.state.picked_pane(); + + let picked_split = self + .state + .picked_split() + .and_then(|(split, axis)| { + let bounds = layout.bounds(); + + let splits = self + .state + .split_regions(f32::from(self.spacing), bounds.size()); + + let (_axis, region, ratio) = splits.get(&split)?; + + let region = axis.split_line_bounds( + *region, + *ratio, + f32::from(self.spacing), + ); + + Some((axis, region + Vector::new(bounds.x, bounds.y), true)) + }) + .or_else(|| match self.on_resize { + Some((leeway, _)) => { + let bounds = layout.bounds(); + + let relative_cursor = Point::new( + cursor_position.x - bounds.x, + cursor_position.y - bounds.y, + ); + + let splits = self + .state + .split_regions(f32::from(self.spacing), bounds.size()); + + let (_split, axis, region) = hovered_split( + splits.iter(), + f32::from(self.spacing + leeway), + relative_cursor, + )?; + + Some(( + axis, + region + Vector::new(bounds.x, bounds.y), + false, + )) + } + None => None, + }); + + let pane_cursor_position = if picked_pane.is_some() { + // TODO: Remove once cursor availability is encoded in the type + // system + Point::new(-1.0, -1.0) + } else { + cursor_position + }; + + for ((id, pane), layout) in self.elements.iter().zip(layout.children()) + { + match picked_pane { + Some((dragging, origin)) if *id == dragging => { + let bounds = layout.bounds(); + + renderer.with_layer( + Rectangle { + x: cursor_position.x - origin.x, + y: cursor_position.y - origin.y, + width: bounds.width + 0.5, + height: bounds.height + 0.5, + }, + Vector::new(0, 0), + |renderer| { + pane.draw( + renderer, + style, + layout, + pane_cursor_position, + viewport, + ); + }, + ); + } + _ => { + pane.draw( + renderer, + style, + layout, + pane_cursor_position, + viewport, + ); + } + } + } + + if let Some((axis, split_region, is_picked)) = picked_split { + let highlight = if is_picked { + self.style_sheet.picked_split() + } else { + self.style_sheet.hovered_split() + }; + + if let Some(highlight) = highlight { + renderer.fill_rectangle(renderer::Quad { + bounds: match axis { + Axis::Horizontal => Rectangle { + x: split_region.x, + y: (split_region.y + + (split_region.height - highlight.width) + / 2.0) + .round(), + width: split_region.width, + height: highlight.width, + }, + Axis::Vertical => Rectangle { + x: (split_region.x + + (split_region.width - highlight.width) / 2.0) + .round(), + y: split_region.y, + width: highlight.width, + height: split_region.height, + }, + }, + background: highlight.color.into(), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }); + } + } } fn hash_layout(&self, state: &mut Hasher) { @@ -558,21 +638,10 @@ where } } -/// The renderer of a [`PaneGrid`]. -/// -/// Your [renderer] will need to implement this trait before being -/// able to use a [`PaneGrid`] in your user interface. -/// -/// [renderer]: crate::renderer -pub trait Renderer: crate::Renderer + Sized { - /// The style supported by this renderer. - type Style: Default; -} - impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + self::Renderer, + Renderer: 'a + crate::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index 7858c15dd0..e74e3c847e 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -2,7 +2,7 @@ use crate::container; use crate::event::{self, Event}; use crate::layout; use crate::overlay; -use crate::pane_grid::{self, TitleBar}; +use crate::pane_grid::TitleBar; use crate::renderer; use crate::{Clipboard, Element, Hasher, Layout, Point, Rectangle, Size}; @@ -10,22 +10,22 @@ use crate::{Clipboard, Element, Hasher, Layout, Point, Rectangle, Size}; /// /// [`Pane`]: crate::widget::pane_grid::Pane #[allow(missing_debug_implementations)] -pub struct Content<'a, Message, Renderer: pane_grid::Renderer> { +pub struct Content<'a, Message, Renderer> { title_bar: Option>, body: Element<'a, Message, Renderer>, - style: &'a dyn container::StyleSheet, + style_sheet: &'a dyn container::StyleSheet, } impl<'a, Message, Renderer> Content<'a, Message, Renderer> where - Renderer: pane_grid::Renderer, + Renderer: crate::Renderer, { /// Creates a new [`Content`] with the provided body. pub fn new(body: impl Into>) -> Self { Self { title_bar: None, body: body.into(), - style: Default::default(), + style_sheet: Default::default(), } } @@ -39,15 +39,15 @@ where } /// Sets the style of the [`Content`]. - pub fn style(mut self, style: &'a dyn container::StyleSheet) -> Self { - self.style = style; + pub fn style(mut self, style_sheet: &'a dyn container::StyleSheet) -> Self { + self.style_sheet = style_sheet; self } } impl<'a, Message, Renderer> Content<'a, Message, Renderer> where - Renderer: pane_grid::Renderer, + Renderer: crate::Renderer, { /// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`]. /// @@ -60,32 +60,41 @@ where cursor_position: Point, viewport: &Rectangle, ) { - // TODO - // if let Some(title_bar) = &self.title_bar { - // let mut children = layout.children(); - // let title_bar_layout = children.next().unwrap(); - // let body_layout = children.next().unwrap(); - - // renderer.draw_pane( - // defaults, - // layout.bounds(), - // &self.style, - // Some((title_bar, title_bar_layout)), - // (&self.body, body_layout), - // cursor_position, - // viewport, - // ) - // } else { - // renderer.draw_pane( - // defaults, - // layout.bounds(), - // &self.style, - // None, - // (&self.body, layout), - // cursor_position, - // viewport, - // ) - // } + let bounds = layout.bounds(); + + { + let style = self.style_sheet.style(); + + container::draw_background(renderer, &style, bounds); + } + + if let Some(title_bar) = &self.title_bar { + let mut children = layout.children(); + let title_bar_layout = children.next().unwrap(); + let body_layout = children.next().unwrap(); + + let show_controls = bounds.contains(cursor_position); + + title_bar.draw( + renderer, + style, + title_bar_layout, + cursor_position, + viewport, + show_controls, + ); + + self.body.draw( + renderer, + style, + body_layout, + cursor_position, + viewport, + ); + } else { + self.body + .draw(renderer, style, layout, cursor_position, viewport); + } } /// Returns whether the [`Content`] with the given [`Layout`] can be picked @@ -214,7 +223,7 @@ where impl<'a, T, Message, Renderer> From for Content<'a, Message, Renderer> where T: Into>, - Renderer: pane_grid::Renderer, + Renderer: crate::Renderer, { fn from(element: T) -> Self { Self::new(element) diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 22d3a9eb66..161eb9bc2a 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -2,7 +2,6 @@ use crate::container; use crate::event::{self, Event}; use crate::layout; use crate::overlay; -use crate::pane_grid; use crate::renderer; use crate::{ Clipboard, Element, Hasher, Layout, Padding, Point, Rectangle, Size, @@ -12,17 +11,17 @@ use crate::{ /// /// [`Pane`]: crate::widget::pane_grid::Pane #[allow(missing_debug_implementations)] -pub struct TitleBar<'a, Message, Renderer: pane_grid::Renderer> { +pub struct TitleBar<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, controls: Option>, padding: Padding, always_show_controls: bool, - style: &'a dyn container::StyleSheet, + style_sheet: &'a dyn container::StyleSheet, } impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> where - Renderer: pane_grid::Renderer, + Renderer: crate::Renderer, { /// Creates a new [`TitleBar`] with the given content. pub fn new(content: E) -> Self @@ -34,7 +33,7 @@ where controls: None, padding: Padding::ZERO, always_show_controls: false, - style: Default::default(), + style_sheet: Default::default(), } } @@ -55,7 +54,7 @@ where /// Sets the style of the [`TitleBar`]. pub fn style(mut self, style: &'a dyn container::StyleSheet) -> Self { - self.style = style; + self.style_sheet = style; self } @@ -75,7 +74,7 @@ where impl<'a, Message, Renderer> TitleBar<'a, Message, Renderer> where - Renderer: pane_grid::Renderer, + Renderer: crate::Renderer, { /// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`]. /// @@ -83,39 +82,47 @@ where pub fn draw( &self, renderer: &mut Renderer, - style: &renderer::Style, + inherited_style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, show_controls: bool, ) { - // let mut children = layout.children(); - // let padded = children.next().unwrap(); - - // let mut children = padded.children(); - // let title_layout = children.next().unwrap(); - - // let controls = if let Some(controls) = &self.controls { - // let controls_layout = children.next().unwrap(); - - // if show_controls || self.always_show_controls { - // Some((controls, controls_layout)) - // } else { - // None - // } - // } else { - // None - // }; - - // renderer.draw_title_bar( - // defaults, - // layout.bounds(), - // &self.style, - // (&self.content, title_layout), - // controls, - // cursor_position, - // viewport, - // ) + let bounds = layout.bounds(); + let style = self.style_sheet.style(); + let inherited_style = renderer::Style { + text_color: style.text_color.unwrap_or(inherited_style.text_color), + }; + + container::draw_background(renderer, &style, bounds); + + let mut children = layout.children(); + let padded = children.next().unwrap(); + + let mut children = padded.children(); + let title_layout = children.next().unwrap(); + + self.content.draw( + renderer, + &inherited_style, + title_layout, + cursor_position, + viewport, + ); + + if let Some(controls) = &self.controls { + let controls_layout = children.next().unwrap(); + + if show_controls || self.always_show_controls { + controls.draw( + renderer, + &inherited_style, + controls_layout, + cursor_position, + viewport, + ); + } + } } /// Returns whether the mouse cursor is over the pick area of the diff --git a/style/src/pane_grid.rs b/style/src/pane_grid.rs index e39ee79770..032fcb6d92 100644 --- a/style/src/pane_grid.rs +++ b/style/src/pane_grid.rs @@ -35,17 +35,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl From for Box -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } From 4a11cbd99445338619dfaf1f327dbc25b2983cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 25 Oct 2021 16:16:35 +0700 Subject: [PATCH 35/92] Implement `Widget::mouse_interaction` for `PaneGrid` ... and fix rendering of drag interaction in `PaneGrid` by introducing an explicit `with_translation` method to `Renderer` and simplifying the `with_layer` and `Clip` primitive. --- graphics/src/layer.rs | 9 +--- graphics/src/primitive.rs | 2 - graphics/src/renderer.rs | 27 ++++++++--- native/src/renderer.rs | 7 +-- native/src/renderer/null.rs | 8 ++-- native/src/user_interface.rs | 22 ++++----- native/src/widget/pane_grid.rs | 57 +++++++++++++++++------- native/src/widget/pane_grid/content.rs | 36 +++++++++++++++ native/src/widget/pane_grid/title_bar.rs | 30 +++++++++++++ native/src/widget/scrollable.rs | 25 ++++++----- native/src/widget/text_input.rs | 8 ++-- 11 files changed, 166 insertions(+), 65 deletions(-) diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index e5cb64c308..7a32c8509f 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -175,11 +175,7 @@ impl<'a> Layer<'a> { }); } } - Primitive::Clip { - bounds, - offset, - content, - } => { + Primitive::Clip { bounds, content } => { let layer = &mut layers[current_layer]; let translated_bounds = *bounds + translation; @@ -192,8 +188,7 @@ impl<'a> Layer<'a> { Self::process_primitive( layers, - translation - - Vector::new(offset.x as f32, offset.y as f32), + translation, content, layers.len() - 1, ); diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 32f8383d63..b984feaa87 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -66,8 +66,6 @@ pub enum Primitive { Clip { /// The bounds of the clip bounds: Rectangle, - /// The offset transformation of the clip - offset: Vector, /// The content of the clip content: Box, }, diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index d90a3f1eb6..8b6c2217af 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -51,10 +51,26 @@ where layout } - fn with_layer( + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)) { + let current_primitives = + std::mem::replace(&mut self.primitives, Vec::new()); + + f(self); + + let layer_primitives = + std::mem::replace(&mut self.primitives, current_primitives); + + self.primitives.push(Primitive::Clip { + bounds, + content: Box::new(Primitive::Group { + primitives: layer_primitives, + }), + }); + } + + fn with_translation( &mut self, - bounds: Rectangle, - offset: Vector, + translation: Vector, f: impl FnOnce(&mut Self), ) { let current_primitives = @@ -65,9 +81,8 @@ where let layer_primitives = std::mem::replace(&mut self.primitives, current_primitives); - self.primitives.push(Primitive::Clip { - bounds, - offset, + self.primitives.push(Primitive::Translate { + translation, content: Box::new(Primitive::Group { primitives: layer_primitives, }), diff --git a/native/src/renderer.rs b/native/src/renderer.rs index e48c701d0e..6d12edae2f 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -46,10 +46,11 @@ pub trait Renderer: Sized { element.layout(self, limits) } - fn with_layer( + fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); + + fn with_translation( &mut self, - bounds: Rectangle, - offset: Vector, + translation: Vector, f: impl FnOnce(&mut Self), ); diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 485c5b2c64..cc1d41ba39 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -1,4 +1,3 @@ -use crate::pane_grid; use crate::progress_bar; use crate::renderer::{self, Renderer}; use crate::text; @@ -19,10 +18,11 @@ impl Null { } impl Renderer for Null { - fn with_layer( + fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} + + fn with_translation( &mut self, - _bounds: Rectangle, - _offset: Vector, + _translation: Vector, _f: impl FnOnce(&mut Self), ) { } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index d93385247b..c3a8394cef 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -3,7 +3,7 @@ use crate::layout; use crate::mouse; use crate::overlay; use crate::renderer; -use crate::{Clipboard, Element, Layout, Point, Rectangle, Size, Vector}; +use crate::{Clipboard, Element, Layout, Point, Rectangle, Size}; use std::hash::Hasher; @@ -359,18 +359,14 @@ where let overlay_bounds = layer.layout.bounds(); - renderer.with_layer( - overlay_bounds, - Vector::new(0, 0), - |renderer| { - overlay.draw( - renderer, - &renderer::Style::default(), - Layout::new(&layer.layout), - cursor_position, - ); - }, - ); + renderer.with_layer(overlay_bounds, |renderer| { + overlay.draw( + renderer, + &renderer::Style::default(), + Layout::new(&layer.layout), + cursor_position, + ); + }); self.overlay = Some(layer); diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index cb386959b7..5f0e73e475 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -471,6 +471,33 @@ where .fold(event_status, event::Status::merge) } + fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + if self.state.picked_pane().is_some() { + return mouse::Interaction::Grab; + } + + if let Some((_, axis)) = self.state.picked_split() { + return match axis { + Axis::Horizontal => mouse::Interaction::ResizingHorizontally, + Axis::Vertical => mouse::Interaction::ResizingVertically, + }; + } + + self.elements + .iter() + .zip(layout.children()) + .map(|((_pane, content), layout)| { + content.mouse_interaction(layout, viewport, cursor_position) + }) + .max() + .unwrap_or_default() + } + fn draw( &self, renderer: &mut Renderer, @@ -543,22 +570,22 @@ where Some((dragging, origin)) if *id == dragging => { let bounds = layout.bounds(); - renderer.with_layer( - Rectangle { - x: cursor_position.x - origin.x, - y: cursor_position.y - origin.y, - width: bounds.width + 0.5, - height: bounds.height + 0.5, - }, - Vector::new(0, 0), + renderer.with_translation( + cursor_position + - Point::new( + bounds.x + origin.x, + bounds.y + origin.y, + ), |renderer| { - pane.draw( - renderer, - style, - layout, - pane_cursor_position, - viewport, - ); + renderer.with_layer(bounds, |renderer| { + pane.draw( + renderer, + style, + layout, + pane_cursor_position, + viewport, + ); + }); }, ); } diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs index e74e3c847e..ddc659cc3e 100644 --- a/native/src/widget/pane_grid/content.rs +++ b/native/src/widget/pane_grid/content.rs @@ -1,6 +1,7 @@ use crate::container; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::pane_grid::TitleBar; use crate::renderer; @@ -194,6 +195,41 @@ where event_status.merge(body_status) } + pub(crate) fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + let mut children = layout.children(); + + let (body_layout, title_bar_interaction) = + if let Some(title_bar) = &self.title_bar { + let title_bar_layout = children.next().unwrap(); + + let is_over_pick_area = title_bar + .is_over_pick_area(title_bar_layout, cursor_position); + + if is_over_pick_area { + return mouse::Interaction::Grab; + } + + let mouse_interaction = title_bar.mouse_interaction( + title_bar_layout, + viewport, + cursor_position, + ); + + (children.next().unwrap(), mouse_interaction) + } else { + (children.next().unwrap(), mouse::Interaction::default()) + }; + + self.body + .mouse_interaction(body_layout, viewport, cursor_position) + .max(title_bar_interaction) + } + pub(crate) fn hash_layout(&self, state: &mut Hasher) { if let Some(title_bar) = &self.title_bar { title_bar.hash_layout(state); diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs index 161eb9bc2a..493c74db0a 100644 --- a/native/src/widget/pane_grid/title_bar.rs +++ b/native/src/widget/pane_grid/title_bar.rs @@ -1,6 +1,7 @@ use crate::container; use crate::event::{self, Event}; use crate::layout; +use crate::mouse; use crate::overlay; use crate::renderer; use crate::{ @@ -249,6 +250,35 @@ where control_status.merge(title_status) } + pub(crate) fn mouse_interaction( + &self, + layout: Layout<'_>, + viewport: &Rectangle, + cursor_position: Point, + ) -> mouse::Interaction { + let mut children = layout.children(); + let padded = children.next().unwrap(); + + let mut children = padded.children(); + let title_layout = children.next().unwrap(); + + let title_interaction = self.content.mouse_interaction( + title_layout, + viewport, + cursor_position, + ); + + if let Some(controls) = &self.controls { + let controls_layout = children.next().unwrap(); + + controls + .mouse_interaction(controls_layout, viewport, cursor_position) + .max(title_interaction) + } else { + title_interaction + } + } + pub(crate) fn overlay( &mut self, layout: Layout<'_>, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 76badbde25..563ea3704a 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -485,15 +485,20 @@ where }; if let Some(scrollbar) = scrollbar { - renderer.with_layer(bounds, Vector::new(0, offset), |renderer| { - self.content.draw( - renderer, - style, - content_layout, - cursor_position, - &Rectangle { - y: bounds.y + offset as f32, - ..bounds + renderer.with_layer(bounds, |renderer| { + renderer.with_translation( + Vector::new(0.0, -(offset as f32)), + |renderer| { + self.content.draw( + renderer, + style, + content_layout, + cursor_position, + &Rectangle { + y: bounds.y + offset as f32, + ..bounds + }, + ); }, ); }); @@ -509,7 +514,7 @@ where let is_scrollbar_visible = style.background.is_some() || style.border_width > 0.0; - renderer.with_layer(bounds, Vector::new(0, 0), |renderer| { + renderer.with_layer(bounds, |renderer| { if is_scrollbar_visible { renderer.fill_rectangle(renderer::Quad { bounds: scrollbar.bounds, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 0a2b19a3a7..c2950db906 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -751,11 +751,9 @@ where }; if text_width > text_bounds.width { - renderer.with_layer( - text_bounds, - Vector::new(offset as u32, 0), - render, - ); + renderer.with_layer(text_bounds, |renderer| { + renderer.with_translation(Vector::new(-offset, 0.0), render) + }); } else { render(renderer); } From 687c35e052a1057042fc8ddf4968c3fa35cc6bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 25 Oct 2021 16:24:26 +0700 Subject: [PATCH 36/92] Avoid flushing empty layers in `iced_wgpu` and `iced_glow` --- glow/src/backend.rs | 5 +++++ wgpu/src/backend.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/glow/src/backend.rs b/glow/src/backend.rs index 9212761e3d..5ab7f922c8 100644 --- a/glow/src/backend.rs +++ b/glow/src/backend.rs @@ -79,6 +79,11 @@ impl Backend { target_height: u32, ) { let mut bounds = (layer.bounds * scale_factor).snap(); + + if bounds.width < 1 || bounds.height < 1 { + return; + } + bounds.height = bounds.height.min(target_height); if !layer.quads.is_empty() { diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 2597cef33f..bb84cc8f34 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -119,6 +119,10 @@ impl Backend { ) { let bounds = (layer.bounds * scale_factor).snap(); + if bounds.width < 1 || bounds.height < 1 { + return; + } + if !layer.quads.is_empty() { self.quad_pipeline.draw( device, From fe4dfeafdbc8f427bd351f394d27f606a3843b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 28 Oct 2021 15:41:12 +0700 Subject: [PATCH 37/92] Wire up style to `PickList` and `overlay::Menu` --- graphics/src/overlay/menu.rs | 11 -------- graphics/src/widget/pick_list.rs | 17 ------------ native/src/overlay/menu.rs | 43 +++++++++++------------------ native/src/widget/pick_list.rs | 46 +++++++++++--------------------- 4 files changed, 30 insertions(+), 87 deletions(-) diff --git a/graphics/src/overlay/menu.rs b/graphics/src/overlay/menu.rs index 7dfb48b9e5..c5ff093de2 100644 --- a/graphics/src/overlay/menu.rs +++ b/graphics/src/overlay/menu.rs @@ -1,14 +1,3 @@ //! Build and show dropdown menus. -use crate::backend::{self, Backend}; -use crate::Renderer; - -use iced_native::overlay; pub use iced_style::menu::Style; - -impl overlay::menu::Renderer for Renderer -where - B: Backend + backend::Text, -{ - type Style = Style; -} diff --git a/graphics/src/widget/pick_list.rs b/graphics/src/widget/pick_list.rs index 54f42cde09..b5cb0a440d 100644 --- a/graphics/src/widget/pick_list.rs +++ b/graphics/src/widget/pick_list.rs @@ -1,26 +1,9 @@ //! Display a dropdown list of selectable values. -use crate::backend::{self, Backend}; use crate::Renderer; -use iced_native::Padding; -use iced_style::menu; - pub use iced_native::pick_list::State; pub use iced_style::pick_list::{Style, StyleSheet}; /// A widget allowing the selection of a single value from a list of options. pub type PickList<'a, T, Message, Backend> = iced_native::PickList<'a, T, Message, Renderer>; - -impl iced_native::pick_list::Renderer for Renderer -where - B: Backend + backend::Text, -{ - type Style = Box; - - const DEFAULT_PADDING: Padding = Padding::new(5); - - fn menu_style(style: &Box) -> menu::Style { - style.menu() - } -} diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 0c335bb6bd..ebb2a4e137 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -5,16 +5,17 @@ use crate::mouse; use crate::overlay; use crate::renderer; use crate::scrollable; -use crate::text; use crate::touch; use crate::{ Clipboard, Container, Element, Hasher, Layout, Length, Padding, Point, Rectangle, Scrollable, Size, Vector, Widget, }; +pub use iced_style::menu::Style; + /// A list of selectable options. #[allow(missing_debug_implementations)] -pub struct Menu<'a, T, Renderer: self::Renderer> { +pub struct Menu<'a, T, Renderer: renderer::Text> { state: &'a mut State, options: &'a [T], hovered_option: &'a mut Option, @@ -23,13 +24,13 @@ pub struct Menu<'a, T, Renderer: self::Renderer> { padding: Padding, text_size: Option, font: Renderer::Font, - style: ::Style, + style: Style, } impl<'a, T, Renderer> Menu<'a, T, Renderer> where T: ToString + Clone, - Renderer: self::Renderer + 'a, + Renderer: renderer::Text + 'a, { /// Creates a new [`Menu`] with the given [`State`], a list of options, and /// the message to produced when an option is selected. @@ -77,10 +78,7 @@ where } /// Sets the style of the [`Menu`]. - pub fn style( - mut self, - style: impl Into<::Style>, - ) -> Self { + pub fn style(mut self, style: impl Into