From 05af8d00d4c0f7b8e0ece85224fd90a92da86da8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 19 May 2020 17:15:44 +0200 Subject: Draft new `iced_graphics` crate :tada: --- glow/Cargo.toml | 4 +- glow/src/backend.rs | 423 ++++++++++++++++++++++++++++ glow/src/defaults.rs | 32 --- glow/src/lib.rs | 10 +- glow/src/primitive.rs | 107 -------- glow/src/renderer.rs | 455 ------------------------------- glow/src/renderer/widget.rs | 19 -- glow/src/renderer/widget/button.rs | 93 ------- glow/src/renderer/widget/checkbox.rs | 63 ----- glow/src/renderer/widget/column.rs | 34 --- glow/src/renderer/widget/container.rs | 48 ---- glow/src/renderer/widget/image.rs | 22 -- glow/src/renderer/widget/pane_grid.rs | 93 ------- glow/src/renderer/widget/progress_bar.rs | 54 ---- glow/src/renderer/widget/radio.rs | 63 ----- glow/src/renderer/widget/row.rs | 34 --- glow/src/renderer/widget/scrollable.rs | 125 --------- glow/src/renderer/widget/slider.rs | 106 ------- glow/src/renderer/widget/space.rs | 8 - glow/src/renderer/widget/svg.rs | 22 -- glow/src/renderer/widget/text.rs | 61 ----- glow/src/renderer/widget/text_input.rs | 261 ------------------ glow/src/text.rs | 2 +- glow/src/triangle.rs | 25 +- glow/src/widget/button.rs | 2 +- glow/src/widget/checkbox.rs | 2 +- glow/src/widget/container.rs | 2 +- glow/src/widget/progress_bar.rs | 2 +- glow/src/widget/radio.rs | 2 +- glow/src/widget/scrollable.rs | 2 +- glow/src/widget/slider.rs | 2 +- glow/src/widget/text_input.rs | 2 +- glow/src/window.rs | 2 - glow/src/window/backend.rs | 11 +- glow/src/window/swap_chain.rs | 5 - 35 files changed, 448 insertions(+), 1750 deletions(-) create mode 100644 glow/src/backend.rs delete mode 100644 glow/src/defaults.rs delete mode 100644 glow/src/primitive.rs delete mode 100644 glow/src/renderer.rs delete mode 100644 glow/src/renderer/widget.rs delete mode 100644 glow/src/renderer/widget/button.rs delete mode 100644 glow/src/renderer/widget/checkbox.rs delete mode 100644 glow/src/renderer/widget/column.rs delete mode 100644 glow/src/renderer/widget/container.rs delete mode 100644 glow/src/renderer/widget/image.rs delete mode 100644 glow/src/renderer/widget/pane_grid.rs delete mode 100644 glow/src/renderer/widget/progress_bar.rs delete mode 100644 glow/src/renderer/widget/radio.rs delete mode 100644 glow/src/renderer/widget/row.rs delete mode 100644 glow/src/renderer/widget/scrollable.rs delete mode 100644 glow/src/renderer/widget/slider.rs delete mode 100644 glow/src/renderer/widget/space.rs delete mode 100644 glow/src/renderer/widget/svg.rs delete mode 100644 glow/src/renderer/widget/text.rs delete mode 100644 glow/src/renderer/widget/text_input.rs delete mode 100644 glow/src/window/swap_chain.rs (limited to 'glow') diff --git a/glow/Cargo.toml b/glow/Cargo.toml index e130d563..212fbb30 100644 --- a/glow/Cargo.toml +++ b/glow/Cargo.toml @@ -21,9 +21,9 @@ glyph_brush = "0.6" version = "0.2" path = "../native" -[dependencies.iced_style] +[dependencies.iced_graphics] version = "0.1" -path = "../style" +path = "../graphics" [dependencies.surfman] path = "../../surfman/surfman" diff --git a/glow/src/backend.rs b/glow/src/backend.rs new file mode 100644 index 00000000..7293eba1 --- /dev/null +++ b/glow/src/backend.rs @@ -0,0 +1,423 @@ +use crate::quad; +use crate::text; +use crate::triangle; +use crate::{Quad, Settings, Transformation, Viewport}; +use iced_graphics::backend; +use iced_graphics::Primitive; +use iced_native::mouse; +use iced_native::{Background, Font, Point, Rectangle, Size, Vector}; + +/// A [`glow`] renderer. +/// +/// [`glow`]: https://github.com/grovesNL/glow +#[derive(Debug)] +pub struct Backend { + quad_pipeline: quad::Pipeline, + text_pipeline: text::Pipeline, + triangle_pipeline: triangle::Pipeline, +} + +struct Layer<'a> { + bounds: Rectangle, + quads: Vec, + text: Vec>, + meshes: Vec<(Vector, Rectangle, &'a triangle::Mesh2D)>, +} + +impl<'a> Layer<'a> { + pub fn new(bounds: Rectangle) -> Self { + Self { + bounds, + quads: Vec::new(), + text: Vec::new(), + meshes: Vec::new(), + } + } + + pub fn intersection(&self, rectangle: Rectangle) -> Option> { + let layer_bounds: Rectangle = self.bounds.into(); + + layer_bounds.intersection(&rectangle).map(Into::into) + } +} + +impl Backend { + /// Creates a new [`Renderer`]. + /// + /// [`Renderer`]: struct.Renderer.html + pub fn new(gl: &glow::Context, settings: Settings) -> Self { + let text_pipeline = text::Pipeline::new(gl, settings.default_font); + let quad_pipeline = quad::Pipeline::new(gl); + let triangle_pipeline = + triangle::Pipeline::new(gl, settings.antialiasing); + + Self { + quad_pipeline, + text_pipeline, + triangle_pipeline, + } + } + + /// Draws the provided primitives in the given [`Target`]. + /// + /// The text provided as overlay will be renderer on top of the primitives. + /// This is useful for rendering debug information. + /// + /// [`Target`]: struct.Target.html + pub fn draw>( + &mut self, + gl: &glow::Context, + viewport: &Viewport, + (primitive, mouse_interaction): &(Primitive, mouse::Interaction), + scale_factor: f64, + overlay: &[T], + ) -> mouse::Interaction { + let (width, height) = viewport.dimensions(); + let scale_factor = scale_factor as f32; + let transformation = viewport.transformation(); + + let mut layers = Vec::new(); + + layers.push(Layer::new(Rectangle { + x: 0, + y: 0, + width: (width as f32 / scale_factor).round() as u32, + height: (height as f32 / scale_factor).round() as u32, + })); + + self.draw_primitive(Vector::new(0.0, 0.0), primitive, &mut layers); + self.draw_overlay(overlay, &mut layers); + + for layer in layers { + self.flush( + gl, + viewport, + scale_factor, + transformation, + &layer, + width, + height, + ); + } + + *mouse_interaction + } + + fn draw_primitive<'a>( + &mut self, + translation: Vector, + primitive: &'a Primitive, + layers: &mut Vec>, + ) { + match primitive { + Primitive::None => {} + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + self.draw_primitive(translation, primitive, layers) + } + } + Primitive::Text { + content, + bounds, + size, + color, + font, + horizontal_alignment, + vertical_alignment, + } => { + let layer = layers.last_mut().unwrap(); + + layer.text.push(glow_glyph::Section { + text: &content, + screen_position: ( + bounds.x + translation.x, + bounds.y + translation.y, + ), + bounds: (bounds.width, bounds.height), + scale: glow_glyph::Scale { x: *size, y: *size }, + color: color.into_linear(), + font_id: self.text_pipeline.find_font(*font), + layout: glow_glyph::Layout::default() + .h_align(match horizontal_alignment { + iced_native::HorizontalAlignment::Left => { + glow_glyph::HorizontalAlign::Left + } + iced_native::HorizontalAlignment::Center => { + glow_glyph::HorizontalAlign::Center + } + iced_native::HorizontalAlignment::Right => { + glow_glyph::HorizontalAlign::Right + } + }) + .v_align(match vertical_alignment { + iced_native::VerticalAlignment::Top => { + glow_glyph::VerticalAlign::Top + } + iced_native::VerticalAlignment::Center => { + glow_glyph::VerticalAlign::Center + } + iced_native::VerticalAlignment::Bottom => { + glow_glyph::VerticalAlign::Bottom + } + }), + ..Default::default() + }) + } + Primitive::Quad { + bounds, + background, + border_radius, + border_width, + border_color, + } => { + let layer = layers.last_mut().unwrap(); + + // TODO: Move some of these computations to the GPU (?) + layer.quads.push(Quad { + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + scale: [bounds.width, bounds.height], + color: match background { + Background::Color(color) => color.into_linear(), + }, + border_radius: *border_radius as f32, + border_width: *border_width as f32, + border_color: border_color.into_linear(), + }); + } + Primitive::Mesh2D { size, buffers } => { + let layer = layers.last_mut().unwrap(); + + // Only draw visible content + if let Some(clip_bounds) = layer.intersection(Rectangle::new( + Point::new(translation.x, translation.y), + *size, + )) { + layer.meshes.push(( + translation, + clip_bounds.into(), + buffers, + )); + } + } + Primitive::Clip { + bounds, + offset, + content, + } => { + let layer = layers.last_mut().unwrap(); + + // Only draw visible content + if let Some(clip_bounds) = + layer.intersection(*bounds + translation) + { + let clip_layer = Layer::new(clip_bounds.into()); + let new_layer = Layer::new(layer.bounds); + + layers.push(clip_layer); + self.draw_primitive( + translation + - Vector::new(offset.x as f32, offset.y as f32), + content, + layers, + ); + layers.push(new_layer); + } + } + Primitive::Translate { + translation: new_translation, + content, + } => { + self.draw_primitive( + translation + *new_translation, + &content, + layers, + ); + } + + Primitive::Cached { cache } => { + self.draw_primitive(translation, &cache, layers); + } + + #[cfg(feature = "image")] + Primitive::Image { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image { + handle: image::Handle::Raster(handle.clone()), + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + size: [bounds.width, bounds.height], + }); + } + #[cfg(not(feature = "image"))] + Primitive::Image { .. } => {} + + #[cfg(feature = "svg")] + Primitive::Svg { handle, bounds } => { + let layer = layers.last_mut().unwrap(); + + layer.images.push(Image { + handle: image::Handle::Vector(handle.clone()), + position: [ + bounds.x + translation.x, + bounds.y + translation.y, + ], + size: [bounds.width, bounds.height], + }); + } + #[cfg(not(feature = "svg"))] + Primitive::Svg { .. } => {} + } + } + + fn draw_overlay<'a, T: AsRef>( + &mut self, + lines: &'a [T], + layers: &mut Vec>, + ) { + let first = layers.first().unwrap(); + let mut overlay = Layer::new(first.bounds); + + let font_id = self.text_pipeline.overlay_font(); + let scale = glow_glyph::Scale { x: 20.0, y: 20.0 }; + + for (i, line) in lines.iter().enumerate() { + overlay.text.push(glow_glyph::Section { + text: line.as_ref(), + screen_position: (11.0, 11.0 + 25.0 * i as f32), + color: [0.9, 0.9, 0.9, 1.0], + scale, + font_id, + ..glow_glyph::Section::default() + }); + + overlay.text.push(glow_glyph::Section { + text: line.as_ref(), + screen_position: (10.0, 10.0 + 25.0 * i as f32), + color: [0.0, 0.0, 0.0, 1.0], + scale, + font_id, + ..glow_glyph::Section::default() + }); + } + + layers.push(overlay); + } + + fn flush( + &mut self, + gl: &glow::Context, + viewport: &Viewport, + scale_factor: f32, + transformation: Transformation, + layer: &Layer<'_>, + target_width: u32, + target_height: u32, + ) { + let bounds = layer.bounds * scale_factor; + + if !layer.quads.is_empty() { + self.quad_pipeline.draw( + gl, + viewport, + &layer.quads, + transformation, + scale_factor, + bounds, + ); + } + + if !layer.meshes.is_empty() { + let scaled = transformation + * Transformation::scale(scale_factor, scale_factor); + + self.triangle_pipeline.draw( + gl, + target_width, + target_height, + scaled, + scale_factor, + &layer.meshes, + ); + } + + if !layer.text.is_empty() { + for text in layer.text.iter() { + // Target physical coordinates directly to avoid blurry text + let text = glow_glyph::Section { + // TODO: We `round` here to avoid rerasterizing text when + // its position changes slightly. This can make text feel a + // bit "jumpy". We may be able to do better once we improve + // our text rendering/caching pipeline. + screen_position: ( + (text.screen_position.0 * scale_factor).round(), + (text.screen_position.1 * scale_factor).round(), + ), + // TODO: Fix precision issues with some scale factors. + // + // The `ceil` here can cause some words to render on the + // same line when they should not. + // + // Ideally, `wgpu_glyph` should be able to compute layout + // using logical positions, and then apply the proper + // scaling when rendering. This would ensure that both + // measuring and rendering follow the same layout rules. + bounds: ( + (text.bounds.0 * scale_factor).ceil(), + (text.bounds.1 * scale_factor).ceil(), + ), + scale: glow_glyph::Scale { + x: text.scale.x * scale_factor, + y: text.scale.y * scale_factor, + }, + ..*text + }; + + self.text_pipeline.queue(text); + } + + self.text_pipeline.draw_queued( + gl, + transformation, + glow_glyph::Region { + x: bounds.x, + y: viewport.height() + - (bounds.y + bounds.height).min(viewport.height()), + width: bounds.width, + height: bounds.height, + }, + ); + } + } +} + +impl iced_graphics::Backend for Backend { + fn trim_measurements(&mut self) { + self.text_pipeline.trim_measurement_cache() + } +} + +impl backend::Text for Backend { + const ICON_FONT: Font = text::BUILTIN_ICONS; + const CHECKMARK_ICON: char = text::CHECKMARK_ICON; + + fn measure( + &self, + contents: &str, + size: f32, + font: Font, + bounds: Size, + ) -> (f32, f32) { + self.text_pipeline.measure(contents, size, font, bounds) + } + + fn space_width(&self, size: f32) -> f32 { + self.text_pipeline.space_width(size) + } +} diff --git a/glow/src/defaults.rs b/glow/src/defaults.rs deleted file mode 100644 index 11718a87..00000000 --- a/glow/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/glow/src/lib.rs b/glow/src/lib.rs index ce447192..27c39b99 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -4,10 +4,8 @@ //#![forbid(unsafe_code)] #![forbid(rust_2018_idioms)] -mod defaults; -mod primitive; +mod backend; mod quad; -mod renderer; mod text; mod transformation; mod triangle; @@ -17,15 +15,15 @@ pub mod settings; pub mod widget; pub mod window; -pub use defaults::Defaults; -pub use primitive::Primitive; -pub use renderer::Renderer; pub use settings::Settings; pub use viewport::Viewport; +pub(crate) use backend::Backend; pub(crate) use quad::Quad; pub(crate) use transformation::Transformation; +pub type Renderer = iced_graphics::Renderer; + #[doc(no_inline)] pub use widget::*; diff --git a/glow/src/primitive.rs b/glow/src/primitive.rs deleted file mode 100644 index e73227ef..00000000 --- a/glow/src/primitive.rs +++ /dev/null @@ -1,107 +0,0 @@ -use iced_native::{ - image, svg, Background, Color, Font, HorizontalAlignment, Rectangle, Size, - Vector, VerticalAlignment, -}; - -use crate::triangle; -use std::sync::Arc; - -/// A rendering primitive. -#[derive(Debug, Clone)] -pub enum Primitive { - /// An empty primitive - None, - /// A group of primitives - Group { - /// The primitives of the group - primitives: Vec, - }, - /// A text primitive - Text { - /// The contents of the text - content: String, - /// The bounds of the text - bounds: Rectangle, - /// The color of the text - color: Color, - /// The size of the text - size: f32, - /// The font of the text - font: Font, - /// The horizontal alignment of the text - horizontal_alignment: HorizontalAlignment, - /// The vertical alignment of the text - vertical_alignment: VerticalAlignment, - }, - /// A quad primitive - Quad { - /// The bounds of the quad - bounds: Rectangle, - /// The background of the quad - background: Background, - /// The border radius of the quad - border_radius: u16, - /// The border width of the quad - border_width: u16, - /// The border color of the quad - border_color: Color, - }, - /// An image primitive - Image { - /// The handle of the image - handle: image::Handle, - /// The bounds of the image - bounds: Rectangle, - }, - /// An SVG primitive - Svg { - /// The path of the SVG file - handle: svg::Handle, - - /// The bounds of the viewport - bounds: Rectangle, - }, - /// A clip primitive - Clip { - /// The bounds of the clip - bounds: Rectangle, - /// The offset transformation of the clip - offset: Vector, - /// The content of the clip - content: Box, - }, - /// A primitive that applies a translation - Translate { - /// The translation vector - translation: Vector, - - /// The primitive to translate - content: Box, - }, - /// A low-level primitive to render a mesh of triangles. - /// - /// It can be used to render many kinds of geometry freely. - Mesh2D { - /// The size of the drawable region of the mesh. - /// - /// Any geometry that falls out of this region will be clipped. - size: Size, - - /// The vertex and index buffers of the mesh - buffers: triangle::Mesh2D, - }, - /// A cached primitive. - /// - /// This can be useful if you are implementing a widget where primitive - /// generation is expensive. - Cached { - /// The cached primitive - cache: Arc, - }, -} - -impl Default for Primitive { - fn default() -> Primitive { - Primitive::None - } -} diff --git a/glow/src/renderer.rs b/glow/src/renderer.rs deleted file mode 100644 index 40228a6b..00000000 --- a/glow/src/renderer.rs +++ /dev/null @@ -1,455 +0,0 @@ -use crate::{ - quad, text, triangle, Defaults, Primitive, Quad, Settings, Transformation, - Viewport, -}; - -use iced_native::{ - layout, mouse, Background, Color, Layout, Point, Rectangle, Vector, Widget, -}; - -mod widget; - -/// A [`glow`] renderer. -/// -/// [`glow`]: https://github.com/grovesNL/glow -#[derive(Debug)] -pub struct Renderer { - quad_pipeline: quad::Pipeline, - text_pipeline: text::Pipeline, - triangle_pipeline: triangle::Pipeline, -} - -struct Layer<'a> { - bounds: Rectangle, - quads: Vec, - text: Vec>, - meshes: Vec<(Vector, Rectangle, &'a triangle::Mesh2D)>, -} - -impl<'a> Layer<'a> { - pub fn new(bounds: Rectangle) -> Self { - Self { - bounds, - quads: Vec::new(), - text: Vec::new(), - meshes: Vec::new(), - } - } - - pub fn intersection(&self, rectangle: Rectangle) -> Option> { - let layer_bounds: Rectangle = self.bounds.into(); - - layer_bounds.intersection(&rectangle).map(Into::into) - } -} - -impl Renderer { - /// Creates a new [`Renderer`]. - /// - /// [`Renderer`]: struct.Renderer.html - pub fn new(gl: &glow::Context, settings: Settings) -> Self { - let text_pipeline = text::Pipeline::new(gl, settings.default_font); - let quad_pipeline = quad::Pipeline::new(gl); - let triangle_pipeline = - triangle::Pipeline::new(gl, settings.antialiasing); - - Self { - quad_pipeline, - text_pipeline, - triangle_pipeline, - } - } - - /// Draws the provided primitives in the given [`Target`]. - /// - /// The text provided as overlay will be renderer on top of the primitives. - /// This is useful for rendering debug information. - /// - /// [`Target`]: struct.Target.html - pub fn draw>( - &mut self, - gl: &glow::Context, - viewport: &Viewport, - (primitive, mouse_interaction): &(Primitive, mouse::Interaction), - scale_factor: f64, - overlay: &[T], - ) -> mouse::Interaction { - let (width, height) = viewport.dimensions(); - let scale_factor = scale_factor as f32; - let transformation = viewport.transformation(); - - let mut layers = Vec::new(); - - layers.push(Layer::new(Rectangle { - x: 0, - y: 0, - width: (width as f32 / scale_factor).round() as u32, - height: (height as f32 / scale_factor).round() as u32, - })); - - self.draw_primitive(Vector::new(0.0, 0.0), primitive, &mut layers); - self.draw_overlay(overlay, &mut layers); - - for layer in layers { - self.flush( - gl, - viewport, - scale_factor, - transformation, - &layer, - width, - height, - ); - } - - *mouse_interaction - } - - fn draw_primitive<'a>( - &mut self, - translation: Vector, - primitive: &'a Primitive, - layers: &mut Vec>, - ) { - match primitive { - Primitive::None => {} - Primitive::Group { primitives } => { - // TODO: Inspect a bit and regroup (?) - for primitive in primitives { - self.draw_primitive(translation, primitive, layers) - } - } - Primitive::Text { - content, - bounds, - size, - color, - font, - horizontal_alignment, - vertical_alignment, - } => { - let layer = layers.last_mut().unwrap(); - - layer.text.push(glow_glyph::Section { - text: &content, - screen_position: ( - bounds.x + translation.x, - bounds.y + translation.y, - ), - bounds: (bounds.width, bounds.height), - scale: glow_glyph::Scale { x: *size, y: *size }, - color: color.into_linear(), - font_id: self.text_pipeline.find_font(*font), - layout: glow_glyph::Layout::default() - .h_align(match horizontal_alignment { - iced_native::HorizontalAlignment::Left => { - glow_glyph::HorizontalAlign::Left - } - iced_native::HorizontalAlignment::Center => { - glow_glyph::HorizontalAlign::Center - } - iced_native::HorizontalAlignment::Right => { - glow_glyph::HorizontalAlign::Right - } - }) - .v_align(match vertical_alignment { - iced_native::VerticalAlignment::Top => { - glow_glyph::VerticalAlign::Top - } - iced_native::VerticalAlignment::Center => { - glow_glyph::VerticalAlign::Center - } - iced_native::VerticalAlignment::Bottom => { - glow_glyph::VerticalAlign::Bottom - } - }), - ..Default::default() - }) - } - Primitive::Quad { - bounds, - background, - border_radius, - border_width, - border_color, - } => { - let layer = layers.last_mut().unwrap(); - - // TODO: Move some of these computations to the GPU (?) - layer.quads.push(Quad { - position: [ - bounds.x + translation.x, - bounds.y + translation.y, - ], - scale: [bounds.width, bounds.height], - color: match background { - Background::Color(color) => color.into_linear(), - }, - border_radius: *border_radius as f32, - border_width: *border_width as f32, - border_color: border_color.into_linear(), - }); - } - Primitive::Mesh2D { size, buffers } => { - let layer = layers.last_mut().unwrap(); - - // Only draw visible content - if let Some(clip_bounds) = layer.intersection(Rectangle::new( - Point::new(translation.x, translation.y), - *size, - )) { - layer.meshes.push(( - translation, - clip_bounds.into(), - buffers, - )); - } - } - Primitive::Clip { - bounds, - offset, - content, - } => { - let layer = layers.last_mut().unwrap(); - - // Only draw visible content - if let Some(clip_bounds) = - layer.intersection(*bounds + translation) - { - let clip_layer = Layer::new(clip_bounds.into()); - let new_layer = Layer::new(layer.bounds); - - layers.push(clip_layer); - self.draw_primitive( - translation - - Vector::new(offset.x as f32, offset.y as f32), - content, - layers, - ); - layers.push(new_layer); - } - } - Primitive::Translate { - translation: new_translation, - content, - } => { - self.draw_primitive( - translation + *new_translation, - &content, - layers, - ); - } - - Primitive::Cached { cache } => { - self.draw_primitive(translation, &cache, layers); - } - - #[cfg(feature = "image")] - Primitive::Image { handle, bounds } => { - let layer = layers.last_mut().unwrap(); - - layer.images.push(Image { - handle: image::Handle::Raster(handle.clone()), - position: [ - bounds.x + translation.x, - bounds.y + translation.y, - ], - size: [bounds.width, bounds.height], - }); - } - #[cfg(not(feature = "image"))] - Primitive::Image { .. } => {} - - #[cfg(feature = "svg")] - Primitive::Svg { handle, bounds } => { - let layer = layers.last_mut().unwrap(); - - layer.images.push(Image { - handle: image::Handle::Vector(handle.clone()), - position: [ - bounds.x + translation.x, - bounds.y + translation.y, - ], - size: [bounds.width, bounds.height], - }); - } - #[cfg(not(feature = "svg"))] - Primitive::Svg { .. } => {} - } - } - - fn draw_overlay<'a, T: AsRef>( - &mut self, - lines: &'a [T], - layers: &mut Vec>, - ) { - let first = layers.first().unwrap(); - let mut overlay = Layer::new(first.bounds); - - let font_id = self.text_pipeline.overlay_font(); - let scale = glow_glyph::Scale { x: 20.0, y: 20.0 }; - - for (i, line) in lines.iter().enumerate() { - overlay.text.push(glow_glyph::Section { - text: line.as_ref(), - screen_position: (11.0, 11.0 + 25.0 * i as f32), - color: [0.9, 0.9, 0.9, 1.0], - scale, - font_id, - ..glow_glyph::Section::default() - }); - - overlay.text.push(glow_glyph::Section { - text: line.as_ref(), - screen_position: (10.0, 10.0 + 25.0 * i as f32), - color: [0.0, 0.0, 0.0, 1.0], - scale, - font_id, - ..glow_glyph::Section::default() - }); - } - - layers.push(overlay); - } - - fn flush( - &mut self, - gl: &glow::Context, - viewport: &Viewport, - scale_factor: f32, - transformation: Transformation, - layer: &Layer<'_>, - target_width: u32, - target_height: u32, - ) { - let bounds = layer.bounds * scale_factor; - - if !layer.quads.is_empty() { - self.quad_pipeline.draw( - gl, - viewport, - &layer.quads, - transformation, - scale_factor, - bounds, - ); - } - - if !layer.meshes.is_empty() { - let scaled = transformation - * Transformation::scale(scale_factor, scale_factor); - - self.triangle_pipeline.draw( - gl, - target_width, - target_height, - scaled, - scale_factor, - &layer.meshes, - ); - } - - if !layer.text.is_empty() { - for text in layer.text.iter() { - // Target physical coordinates directly to avoid blurry text - let text = glow_glyph::Section { - // TODO: We `round` here to avoid rerasterizing text when - // its position changes slightly. This can make text feel a - // bit "jumpy". We may be able to do better once we improve - // our text rendering/caching pipeline. - screen_position: ( - (text.screen_position.0 * scale_factor).round(), - (text.screen_position.1 * scale_factor).round(), - ), - // TODO: Fix precision issues with some scale factors. - // - // The `ceil` here can cause some words to render on the - // same line when they should not. - // - // Ideally, `wgpu_glyph` should be able to compute layout - // using logical positions, and then apply the proper - // scaling when rendering. This would ensure that both - // measuring and rendering follow the same layout rules. - bounds: ( - (text.bounds.0 * scale_factor).ceil(), - (text.bounds.1 * scale_factor).ceil(), - ), - scale: glow_glyph::Scale { - x: text.scale.x * scale_factor, - y: text.scale.y * scale_factor, - }, - ..*text - }; - - self.text_pipeline.queue(text); - } - - self.text_pipeline.draw_queued( - gl, - transformation, - glow_glyph::Region { - x: bounds.x, - y: viewport.height() - - (bounds.y + bounds.height).min(viewport.height()), - width: bounds.width, - height: bounds.height, - }, - ); - } - } -} - -impl iced_native::Renderer for Renderer { - type Output = (Primitive, mouse::Interaction); - type Defaults = Defaults; - - fn layout<'a, Message>( - &mut self, - element: &iced_native::Element<'a, Message, Self>, - limits: &iced_native::layout::Limits, - ) -> iced_native::layout::Node { - let node = element.layout(self, limits); - - self.text_pipeline.clear_measurement_cache(); - - node - } -} - -impl layout::Debugger for Renderer { - fn explain( - &mut self, - defaults: &Defaults, - widget: &dyn Widget, - layout: Layout<'_>, - cursor_position: Point, - color: Color, - ) -> Self::Output { - let mut primitives = Vec::new(); - let (primitive, cursor) = - widget.draw(self, defaults, layout, cursor_position); - - 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, - border_width: 1, - border_color: [0.6, 0.6, 0.6, 0.5].into(), - }); - - for child in layout.children() { - explain_layout(child, color, primitives); - } -} diff --git a/glow/src/renderer/widget.rs b/glow/src/renderer/widget.rs deleted file mode 100644 index 37421fbe..00000000 --- a/glow/src/renderer/widget.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod button; -mod checkbox; -mod column; -mod container; -mod pane_grid; -mod progress_bar; -mod radio; -mod row; -mod scrollable; -mod slider; -mod space; -mod text; -mod text_input; - -#[cfg(feature = "svg")] -mod svg; - -#[cfg(feature = "image")] -mod image; diff --git a/glow/src/renderer/widget/button.rs b/glow/src/renderer/widget/button.rs deleted file mode 100644 index eb225038..00000000 --- a/glow/src/renderer/widget/button.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; -use iced_native::{ - mouse, Background, Color, Element, Layout, Point, Rectangle, Vector, -}; - -impl iced_native::button::Renderer for Renderer { - const DEFAULT_PADDING: u16 = 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, - ); - - ( - if styling.background.is_some() || styling.border_width > 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, - border_color: Color::TRANSPARENT, - }; - - Primitive::Group { - primitives: vec![shadow, background, content], - } - } - } else { - content - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } -} diff --git a/glow/src/renderer/widget/checkbox.rs b/glow/src/renderer/widget/checkbox.rs deleted file mode 100644 index 0340bf62..00000000 --- a/glow/src/renderer/widget/checkbox.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{checkbox::StyleSheet, Primitive, Renderer}; -use iced_native::{ - checkbox, mouse, HorizontalAlignment, Rectangle, VerticalAlignment, -}; - -impl checkbox::Renderer for Renderer { - type Style = Box; - - 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: crate::text::CHECKMARK_ICON.to_string(), - font: crate::text::BUILTIN_ICONS, - size: bounds.height * 0.7, - bounds: Rectangle { - x: bounds.center_x(), - y: bounds.center_y(), - ..bounds - }, - color: style.checkmark_color, - horizontal_alignment: HorizontalAlignment::Center, - vertical_alignment: VerticalAlignment::Center, - }; - - vec![checkbox, check, label] - } else { - vec![checkbox, label] - }, - }, - if is_mouse_over { - mouse::Interaction::Pointer - } else { - mouse::Interaction::default() - }, - ) - } -} diff --git a/glow/src/renderer/widget/column.rs b/glow/src/renderer/widget/column.rs deleted file mode 100644 index b853276d..00000000 --- a/glow/src/renderer/widget/column.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{column, mouse, Element, Layout, Point}; - -impl column::Renderer for Renderer { - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - ) -> 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); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - primitive - }) - .collect(), - }, - mouse_interaction, - ) - } -} diff --git a/glow/src/renderer/widget/container.rs b/glow/src/renderer/widget/container.rs deleted file mode 100644 index 30cc3f07..00000000 --- a/glow/src/renderer/widget/container.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{container, defaults, Defaults, Primitive, Renderer}; -use iced_native::{Background, Color, Element, Layout, Point, Rectangle}; - -impl iced_native::container::Renderer for Renderer { - type Style = Box; - - fn draw( - &mut self, - defaults: &Defaults, - bounds: Rectangle, - cursor_position: Point, - 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); - - if style.background.is_some() || style.border_width > 0 { - let quad = 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, - }; - - ( - Primitive::Group { - primitives: vec![quad, content], - }, - mouse_interaction, - ) - } else { - (content, mouse_interaction) - } - } -} diff --git a/glow/src/renderer/widget/image.rs b/glow/src/renderer/widget/image.rs deleted file mode 100644 index c4c04984..00000000 --- a/glow/src/renderer/widget/image.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{image, mouse, Layout}; - -impl image::Renderer for Renderer { - fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { - self.image_pipeline.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/glow/src/renderer/widget/pane_grid.rs b/glow/src/renderer/widget/pane_grid.rs deleted file mode 100644 index 2253e4af..00000000 --- a/glow/src/renderer/widget/pane_grid.rs +++ /dev/null @@ -1,93 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{ - mouse, - pane_grid::{self, Axis, Pane}, - Element, Layout, Point, Rectangle, Vector, -}; - -impl pane_grid::Renderer for Renderer { - fn draw( - &mut self, - defaults: &Self::Defaults, - content: &[(Pane, Element<'_, Message, Self>)], - dragging: Option, - resizing: Option, - layout: Layout<'_>, - cursor_position: Point, - ) -> 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); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - if Some(*id) == dragging { - dragged_pane = Some((i, layout)); - } - - primitive - }) - .collect(); - - let primitives = if let Some((index, layout)) = 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 - bounds.width / 2.0, - y: cursor_position.y - bounds.height / 2.0, - 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 - bounds.width / 2.0, - cursor_position.y - bounds.y - bounds.height / 2.0, - ), - content: Box::new(pane), - }), - }; - - panes.push(clip); - - panes - } else { - panes - }; - - ( - Primitive::Group { primitives }, - if dragging.is_some() { - mouse::Interaction::Grabbing - } else if let Some(axis) = resizing { - match axis { - Axis::Horizontal => mouse::Interaction::ResizingVertically, - Axis::Vertical => mouse::Interaction::ResizingHorizontally, - } - } else { - mouse_interaction - }, - ) - } -} diff --git a/glow/src/renderer/widget/progress_bar.rs b/glow/src/renderer/widget/progress_bar.rs deleted file mode 100644 index 2baeeb14..00000000 --- a/glow/src/renderer/widget/progress_bar.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{progress_bar::StyleSheet, Primitive, Renderer}; -use iced_native::{mouse, progress_bar, Color, Rectangle}; - -impl progress_bar::Renderer for Renderer { - 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 = bounds.width - * ((value - range_start) / (range_end - range_start).max(1.0)); - - let background = Primitive::Group { - primitives: vec![Primitive::Quad { - bounds: Rectangle { ..bounds }, - background: style.background, - border_radius: style.border_radius, - border_width: 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, - border_color: Color::TRANSPARENT, - }; - - Primitive::Group { - primitives: vec![background, bar], - } - } else { - background - }, - mouse::Interaction::default(), - ) - } -} diff --git a/glow/src/renderer/widget/radio.rs b/glow/src/renderer/widget/radio.rs deleted file mode 100644 index cee0deb6..00000000 --- a/glow/src/renderer/widget/radio.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{radio::StyleSheet, Primitive, Renderer}; -use iced_native::{mouse, radio, Background, Color, Rectangle}; - -const SIZE: f32 = 28.0; -const DOT_SIZE: f32 = SIZE / 2.0; - -impl radio::Renderer for Renderer { - type Style = Box; - - const DEFAULT_SIZE: u16 = SIZE as u16; - 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 radio = Primitive::Quad { - bounds, - background: style.background, - border_radius: (SIZE / 2.0) as u16, - 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) as u16, - border_width: 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/glow/src/renderer/widget/row.rs b/glow/src/renderer/widget/row.rs deleted file mode 100644 index d0b7ef09..00000000 --- a/glow/src/renderer/widget/row.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, row, Element, Layout, Point}; - -impl row::Renderer for Renderer { - fn draw( - &mut self, - defaults: &Self::Defaults, - children: &[Element<'_, Message, Self>], - layout: Layout<'_>, - cursor_position: Point, - ) -> Self::Output { - let mut mouse_interaction = mouse::Interaction::default(); - - ( - Primitive::Group { - primitives: children - .iter() - .zip(layout.children()) - .map(|(child, layout)| { - let (primitive, new_mouse_interaction) = - child.draw(self, defaults, layout, cursor_position); - - if new_mouse_interaction > mouse_interaction { - mouse_interaction = new_mouse_interaction; - } - - primitive - }) - .collect(), - }, - mouse_interaction, - ) - } -} diff --git a/glow/src/renderer/widget/scrollable.rs b/glow/src/renderer/widget/scrollable.rs deleted file mode 100644 index 8a400b82..00000000 --- a/glow/src/renderer/widget/scrollable.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, scrollable, Background, Color, Rectangle, Vector}; - -const SCROLLBAR_WIDTH: u16 = 10; -const SCROLLBAR_MARGIN: u16 = 2; - -impl scrollable::Renderer for Renderer { - type Style = Box; - - fn scrollbar( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - ) -> Option { - if content_bounds.height > bounds.height { - let scrollbar_bounds = Rectangle { - x: bounds.x + bounds.width - - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - y: bounds.y, - width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - height: bounds.height, - }; - - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; - - let scroller_bounds = Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - y: scrollbar_bounds.y + y_offset, - width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - height: scrollbar_height, - }; - - Some(scrollable::Scrollbar { - bounds: scrollbar_bounds, - scroller: scrollable::Scroller { - bounds: scroller_bounds, - }, - }) - } else { - 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; - - 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: Rectangle { - x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), - width: scrollbar.bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), - ..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 - }; - - Primitive::Group { - primitives: vec![clip, scrollbar, scroller], - } - } else { - content - }, - if is_mouse_over_scrollbar || state.is_scroller_grabbed() { - mouse::Interaction::Idle - } else { - mouse_interaction - }, - ) - } -} diff --git a/glow/src/renderer/widget/slider.rs b/glow/src/renderer/widget/slider.rs deleted file mode 100644 index 220feace..00000000 --- a/glow/src/renderer/widget/slider.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::{ - slider::{HandleShape, StyleSheet}, - Primitive, Renderer, -}; -use iced_native::{mouse, slider, Background, Color, Point, Rectangle}; - -const HANDLE_HEIGHT: f32 = 22.0; - -impl slider::Renderer for Renderer { - type Style = Box; - - fn height(&self) -> u32 { - 30 - } - - 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, - border_width: 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, - border_width: 0, - border_color: Color::TRANSPARENT, - }, - ); - - let (range_start, range_end) = range.into_inner(); - - let (handle_width, handle_height, handle_border_radius) = - match style.handle.shape { - HandleShape::Circle { radius } => { - (f32::from(radius * 2), f32::from(radius * 2), radius) - } - HandleShape::Rectangle { - width, - border_radius, - } => (f32::from(width), HANDLE_HEIGHT, border_radius), - }; - - let handle_offset = (bounds.width - handle_width) - * ((value - range_start) / (range_end - range_start).max(1.0)); - - 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/glow/src/renderer/widget/space.rs b/glow/src/renderer/widget/space.rs deleted file mode 100644 index 225f7e6c..00000000 --- a/glow/src/renderer/widget/space.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, space, Rectangle}; - -impl space::Renderer for Renderer { - fn draw(&mut self, _bounds: Rectangle) -> Self::Output { - (Primitive::None, mouse::Interaction::default()) - } -} diff --git a/glow/src/renderer/widget/svg.rs b/glow/src/renderer/widget/svg.rs deleted file mode 100644 index f6d6d0ba..00000000 --- a/glow/src/renderer/widget/svg.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{mouse, svg, Layout}; - -impl svg::Renderer for Renderer { - fn dimensions(&self, handle: &svg::Handle) -> (u32, u32) { - self.image_pipeline.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/glow/src/renderer/widget/text.rs b/glow/src/renderer/widget/text.rs deleted file mode 100644 index 4605ed06..00000000 --- a/glow/src/renderer/widget/text.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{Primitive, Renderer}; -use iced_native::{ - mouse, text, Color, Font, HorizontalAlignment, Rectangle, Size, - VerticalAlignment, -}; - -use std::f32; - -impl text::Renderer for Renderer { - type Font = Font; - - const DEFAULT_SIZE: u16 = 20; - - fn measure( - &self, - content: &str, - size: u16, - font: Font, - bounds: Size, - ) -> (f32, f32) { - self.text_pipeline - .measure(content, f32::from(size), font, bounds) - } - - fn draw( - &mut self, - defaults: &Self::Defaults, - bounds: Rectangle, - content: &str, - size: u16, - font: Font, - color: Option, - horizontal_alignment: HorizontalAlignment, - vertical_alignment: VerticalAlignment, - ) -> Self::Output { - let x = match horizontal_alignment { - iced_native::HorizontalAlignment::Left => bounds.x, - iced_native::HorizontalAlignment::Center => bounds.center_x(), - iced_native::HorizontalAlignment::Right => bounds.x + bounds.width, - }; - - let y = match vertical_alignment { - iced_native::VerticalAlignment::Top => bounds.y, - iced_native::VerticalAlignment::Center => bounds.center_y(), - iced_native::VerticalAlignment::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/glow/src/renderer/widget/text_input.rs b/glow/src/renderer/widget/text_input.rs deleted file mode 100644 index 57be6692..00000000 --- a/glow/src/renderer/widget/text_input.rs +++ /dev/null @@ -1,261 +0,0 @@ -use crate::{text_input::StyleSheet, Primitive, Renderer}; - -use iced_native::{ - mouse, - text_input::{self, cursor}, - Background, Color, Font, HorizontalAlignment, Point, Rectangle, Size, - Vector, VerticalAlignment, -}; -use std::f32; - -impl text_input::Renderer for Renderer { - type Style = Box; - - fn default_size(&self) -> u16 { - // TODO: Make this configurable - 20 - } - - fn measure_value(&self, value: &str, size: u16, font: Font) -> f32 { - let (mut width, _) = self.text_pipeline.measure( - value, - f32::from(size), - font, - Size::INFINITY, - ); - - let spaces_around = value.len() - value.trim().len(); - - if spaces_around > 0 { - let space_width = self.text_pipeline.space_width(size as f32); - width += spaces_around as f32 * space_width; - } - - 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 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: HorizontalAlignment::Left, - vertical_alignment: VerticalAlignment::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, - border_width: 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, - border_width: 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( - renderer: &Renderer, - text_bounds: Rectangle, - value: &text_input::Value, - size: u16, - cursor_index: usize, - font: Font, -) -> (f32, f32) { - 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/glow/src/text.rs b/glow/src/text.rs index bda619fc..159c80a6 100644 --- a/glow/src/text.rs +++ b/glow/src/text.rs @@ -130,7 +130,7 @@ impl Pipeline { .advance_width } - pub fn clear_measurement_cache(&mut self) { + pub fn trim_measurement_cache(&mut self) { // TODO: We should probably use a `GlyphCalculator` for this. However, // it uses a lifetimed `GlyphCalculatorGuard` with side-effects on drop. // This makes stuff quite inconvenient. A manual method for trimming the diff --git a/glow/src/triangle.rs b/glow/src/triangle.rs index f71022d0..8b21c0a8 100644 --- a/glow/src/triangle.rs +++ b/glow/src/triangle.rs @@ -3,6 +3,8 @@ use crate::{settings, Transformation}; use iced_native::{Rectangle, Vector}; use std::mem; +pub use iced_graphics::triangle::Mesh2D; + const UNIFORM_BUFFER_SIZE: usize = 100; const VERTEX_BUFFER_SIZE: usize = 10_000; const INDEX_BUFFER_SIZE: usize = 10_000; @@ -59,26 +61,3 @@ impl From for Uniforms { } } } - -/// A two-dimensional vertex with some color in __linear__ RGBA. -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct Vertex2D { - /// The vertex position - pub position: [f32; 2], - /// The vertex color in __linear__ RGBA. - pub color: [f32; 4], -} - -/// A set of [`Vertex2D`] and indices representing a list of triangles. -/// -/// [`Vertex2D`]: struct.Vertex2D.html -#[derive(Clone, Debug)] -pub struct Mesh2D { - /// The vertices of the mesh - pub vertices: Vec, - /// The list of vertex indices that defines the triangles of the mesh. - /// - /// Therefore, this list should always have a length that is a multiple of 3. - pub indices: Vec, -} diff --git a/glow/src/widget/button.rs b/glow/src/widget/button.rs index b738c55e..fee7a7f8 100644 --- a/glow/src/widget/button.rs +++ b/glow/src/widget/button.rs @@ -6,8 +6,8 @@ //! [`State`]: struct.State.html use crate::Renderer; +pub use iced_graphics::button::{Style, StyleSheet}; pub use iced_native::button::State; -pub use iced_style::button::{Style, StyleSheet}; /// A widget that produces a message when clicked. /// diff --git a/glow/src/widget/checkbox.rs b/glow/src/widget/checkbox.rs index da0d7a84..d27d77cc 100644 --- a/glow/src/widget/checkbox.rs +++ b/glow/src/widget/checkbox.rs @@ -1,7 +1,7 @@ //! Show toggle controls using checkboxes. use crate::Renderer; -pub use iced_style::checkbox::{Style, StyleSheet}; +pub use iced_graphics::checkbox::{Style, StyleSheet}; /// A box that can be checked. /// diff --git a/glow/src/widget/container.rs b/glow/src/widget/container.rs index 9a93a246..bc26cef2 100644 --- a/glow/src/widget/container.rs +++ b/glow/src/widget/container.rs @@ -1,7 +1,7 @@ //! Decorate content and apply alignment. use crate::Renderer; -pub use iced_style::container::{Style, StyleSheet}; +pub use iced_graphics::container::{Style, StyleSheet}; /// An element decorating some content. /// diff --git a/glow/src/widget/progress_bar.rs b/glow/src/widget/progress_bar.rs index 34450b5e..5782103c 100644 --- a/glow/src/widget/progress_bar.rs +++ b/glow/src/widget/progress_bar.rs @@ -6,7 +6,7 @@ //! [`State`]: struct.State.html use crate::Renderer; -pub use iced_style::progress_bar::{Style, StyleSheet}; +pub use iced_graphics::progress_bar::{Style, StyleSheet}; /// A bar that displays progress. /// diff --git a/glow/src/widget/radio.rs b/glow/src/widget/radio.rs index 6e5cf042..0b843d1f 100644 --- a/glow/src/widget/radio.rs +++ b/glow/src/widget/radio.rs @@ -1,7 +1,7 @@ //! Create choices using radio buttons. use crate::Renderer; -pub use iced_style::radio::{Style, StyleSheet}; +pub use iced_graphics::radio::{Style, StyleSheet}; /// A circular button representing a choice. /// diff --git a/glow/src/widget/scrollable.rs b/glow/src/widget/scrollable.rs index 1d236105..fabb4318 100644 --- a/glow/src/widget/scrollable.rs +++ b/glow/src/widget/scrollable.rs @@ -1,8 +1,8 @@ //! Navigate an endless amount of content with a scrollbar. use crate::Renderer; +pub use iced_graphics::scrollable::{Scrollbar, Scroller, StyleSheet}; pub use iced_native::scrollable::State; -pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; /// A widget that can vertically display an infinite amount of content /// with a scrollbar. diff --git a/glow/src/widget/slider.rs b/glow/src/widget/slider.rs index 4e47978f..cf036829 100644 --- a/glow/src/widget/slider.rs +++ b/glow/src/widget/slider.rs @@ -6,8 +6,8 @@ //! [`State`]: struct.State.html use crate::Renderer; +pub use iced_graphics::slider::{Handle, HandleShape, Style, StyleSheet}; pub use iced_native::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. diff --git a/glow/src/widget/text_input.rs b/glow/src/widget/text_input.rs index 260fe3a6..1da3fbe6 100644 --- a/glow/src/widget/text_input.rs +++ b/glow/src/widget/text_input.rs @@ -6,8 +6,8 @@ //! [`State`]: struct.State.html use crate::Renderer; +pub use iced_graphics::text_input::{Style, StyleSheet}; pub use iced_native::text_input::State; -pub use iced_style::text_input::{Style, StyleSheet}; /// A field that can be filled with text. /// diff --git a/glow/src/window.rs b/glow/src/window.rs index b7adad82..a8edb016 100644 --- a/glow/src/window.rs +++ b/glow/src/window.rs @@ -1,6 +1,4 @@ //! Display rendering results on windows. mod backend; -mod swap_chain; pub use backend::Backend; -pub use swap_chain::SwapChain; diff --git a/glow/src/window/backend.rs b/glow/src/window/backend.rs index 05f988f7..34245f35 100644 --- a/glow/src/window/backend.rs +++ b/glow/src/window/backend.rs @@ -53,7 +53,7 @@ impl iced_native::window::Backend for Backend { .make_context_current(&self.gl_context) .expect("Make context current"); - Renderer::new(self.gl.as_ref().unwrap(), settings) + Renderer::new(crate::Backend::new(self.gl.as_ref().unwrap(), settings)) } fn create_surface( @@ -151,8 +151,13 @@ impl iced_native::window::Backend for Backend { gl.clear(glow::COLOR_BUFFER_BIT); } - let mouse = - renderer.draw(gl, swap_chain, output, scale_factor, overlay); + let mouse = renderer.backend_mut().draw( + gl, + swap_chain, + output, + scale_factor, + overlay, + ); { let mut surface = self diff --git a/glow/src/window/swap_chain.rs b/glow/src/window/swap_chain.rs deleted file mode 100644 index 41d19968..00000000 --- a/glow/src/window/swap_chain.rs +++ /dev/null @@ -1,5 +0,0 @@ -/// The rendering target of a window. -/// -/// It represents a series of virtual framebuffers with a scale factor. -#[derive(Debug)] -pub struct SwapChain; -- cgit