diff options
Diffstat (limited to 'wgpu/src/renderer')
-rw-r--r-- | wgpu/src/renderer/target.rs | 90 | ||||
-rw-r--r-- | wgpu/src/renderer/widget.rs | 10 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/button.rs | 104 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/checkbox.rs | 58 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/column.rs | 3 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/container.rs | 49 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/image.rs | 12 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/progress_bar.rs | 54 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/radio.rs | 52 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/row.rs | 3 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/scrollable.rs | 132 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/slider.rs | 81 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/space.rs | 8 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/svg.rs | 22 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/text.rs | 10 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/text_input.rs | 139 |
16 files changed, 488 insertions, 339 deletions
diff --git a/wgpu/src/renderer/target.rs b/wgpu/src/renderer/target.rs deleted file mode 100644 index 569f3c62..00000000 --- a/wgpu/src/renderer/target.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{Renderer, Transformation}; - -use raw_window_handle::HasRawWindowHandle; - -/// A rendering target. -#[derive(Debug)] -pub struct Target { - surface: wgpu::Surface, - width: u16, - height: u16, - dpi: f32, - transformation: Transformation, - swap_chain: wgpu::SwapChain, -} - -impl Target { - pub(crate) fn dimensions(&self) -> (u16, u16) { - (self.width, self.height) - } - - pub(crate) fn dpi(&self) -> f32 { - self.dpi - } - - pub(crate) fn transformation(&self) -> Transformation { - self.transformation - } - - pub(crate) fn next_frame(&mut self) -> wgpu::SwapChainOutput<'_> { - self.swap_chain.get_next_texture() - } -} - -impl iced_native::renderer::Target for Target { - type Renderer = Renderer; - - fn new<W: HasRawWindowHandle>( - window: &W, - width: u16, - height: u16, - dpi: f32, - renderer: &Renderer, - ) -> Target { - let surface = wgpu::Surface::create(window); - let swap_chain = - new_swap_chain(&surface, width, height, &renderer.device); - - Target { - surface, - width, - height, - dpi, - transformation: Transformation::orthographic(width, height), - swap_chain, - } - } - - fn resize( - &mut self, - width: u16, - height: u16, - dpi: f32, - renderer: &Renderer, - ) { - self.width = width; - self.height = height; - self.dpi = dpi; - self.transformation = Transformation::orthographic(width, height); - self.swap_chain = - new_swap_chain(&self.surface, width, height, &renderer.device); - } -} - -fn new_swap_chain( - surface: &wgpu::Surface, - width: u16, - height: u16, - device: &wgpu::Device, -) -> wgpu::SwapChain { - device.create_swap_chain( - &surface, - &wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: u32::from(width), - height: u32::from(height), - present_mode: wgpu::PresentMode::Vsync, - }, - ) -} diff --git a/wgpu/src/renderer/widget.rs b/wgpu/src/renderer/widget.rs index 52410bee..84f908e7 100644 --- a/wgpu/src/renderer/widget.rs +++ b/wgpu/src/renderer/widget.rs @@ -1,10 +1,18 @@ mod button; mod checkbox; mod column; -mod image; +mod container; +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/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs index 86963053..0de5bf5c 100644 --- a/wgpu/src/renderer/widget/button.rs +++ b/wgpu/src/renderer/widget/button.rs @@ -1,54 +1,88 @@ -use crate::{Primitive, Renderer}; -use iced_native::{button, Background, MouseCursor, Point, Rectangle}; +use crate::{button::StyleSheet, defaults, Defaults, Primitive, Renderer}; +use iced_native::{ + Background, Color, Element, Layout, MouseCursor, Point, Rectangle, Vector, +}; -impl button::Renderer for Renderer { - fn draw( +impl iced_native::button::Renderer for Renderer { + const DEFAULT_PADDING: u16 = 5; + + type Style = Box<dyn StyleSheet>; + + fn draw<Message>( &mut self, + defaults: &Defaults, bounds: Rectangle, cursor_position: Point, + is_disabled: bool, is_pressed: bool, - background: Option<Background>, - border_radius: u16, - (content, _): Self::Output, + style: &Box<dyn StyleSheet>, + content: &Element<'_, Message, Self>, + content_layout: Layout<'_>, ) -> Self::Output { let is_mouse_over = bounds.contains(cursor_position); - // TODO: Render proper shadows - // TODO: Make hovering and pressed styles configurable - let shadow_offset = if is_mouse_over { + let styling = if is_disabled { + style.disabled() + } else if is_mouse_over { if is_pressed { - 0.0 + style.pressed() } else { - 2.0 + style.hovered() } } else { - 1.0 + style.active() }; + let (content, _) = content.draw( + self, + &Defaults { + text: defaults::Text { + color: styling.text_color, + }, + ..*defaults + }, + content_layout, + cursor_position, + ); + ( - match background { - None => content, - Some(background) => Primitive::Group { - primitives: vec![ - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + shadow_offset, - ..bounds - }, - background: Background::Color( - [0.0, 0.0, 0.0, 0.5].into(), - ), - border_radius, - }, - Primitive::Quad { - bounds, - background, - border_radius, + 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 }, - content, - ], - }, + 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 { MouseCursor::Pointer diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index 54b4b1cc..1a0585d3 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -1,15 +1,13 @@ -use crate::{Primitive, Renderer}; +use crate::{checkbox::StyleSheet, Primitive, Renderer}; use iced_native::{ - checkbox, Background, HorizontalAlignment, MouseCursor, Rectangle, - VerticalAlignment, + checkbox, HorizontalAlignment, MouseCursor, Rectangle, VerticalAlignment, }; -const SIZE: f32 = 28.0; - impl checkbox::Renderer for Renderer { - fn default_size(&self) -> u32 { - SIZE as u32 - } + type Style = Box<dyn StyleSheet>; + + const DEFAULT_SIZE: u16 = 20; + const DEFAULT_SPACING: u16 = 15; fn draw( &mut self, @@ -17,31 +15,21 @@ impl checkbox::Renderer for Renderer { is_checked: bool, is_mouse_over: bool, (label, _): Self::Output, + style_sheet: &Self::Style, ) -> Self::Output { - let (checkbox_border, checkbox_box) = ( - Primitive::Quad { - bounds, - background: Background::Color([0.6, 0.6, 0.6].into()), - border_radius: 6, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 5, - }, - ); + 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 { @@ -51,14 +39,14 @@ impl checkbox::Renderer for Renderer { font: crate::text::BUILTIN_ICONS, size: bounds.height * 0.7, bounds: bounds, - color: [0.3, 0.3, 0.3].into(), + color: style.checkmark_color, horizontal_alignment: HorizontalAlignment::Center, vertical_alignment: VerticalAlignment::Center, }; - vec![checkbox_border, checkbox_box, check, label] + vec![checkbox, check, label] } else { - vec![checkbox_border, checkbox_box, label] + vec![checkbox, label] }, }, if is_mouse_over { diff --git a/wgpu/src/renderer/widget/column.rs b/wgpu/src/renderer/widget/column.rs index 6c31af90..95a7463a 100644 --- a/wgpu/src/renderer/widget/column.rs +++ b/wgpu/src/renderer/widget/column.rs @@ -4,6 +4,7 @@ use iced_native::{column, Element, Layout, MouseCursor, Point}; impl column::Renderer for Renderer { fn draw<Message>( &mut self, + defaults: &Self::Defaults, content: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, @@ -17,7 +18,7 @@ impl column::Renderer for Renderer { .zip(layout.children()) .map(|(child, layout)| { let (primitive, new_mouse_cursor) = - child.draw(self, layout, cursor_position); + child.draw(self, defaults, layout, cursor_position); if new_mouse_cursor > mouse_cursor { mouse_cursor = new_mouse_cursor; diff --git a/wgpu/src/renderer/widget/container.rs b/wgpu/src/renderer/widget/container.rs new file mode 100644 index 00000000..2d4d1db8 --- /dev/null +++ b/wgpu/src/renderer/widget/container.rs @@ -0,0 +1,49 @@ +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<dyn container::StyleSheet>; + + fn draw<Message>( + &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), + }, + ..*defaults + }; + + let (content, mouse_cursor) = + 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_cursor, + ) + } else { + (content, mouse_cursor) + } + } +} diff --git a/wgpu/src/renderer/widget/image.rs b/wgpu/src/renderer/widget/image.rs index 0006dde1..70dc5d97 100644 --- a/wgpu/src/renderer/widget/image.rs +++ b/wgpu/src/renderer/widget/image.rs @@ -2,14 +2,18 @@ use crate::{Primitive, Renderer}; use iced_native::{image, Layout, MouseCursor}; impl image::Renderer for Renderer { - fn dimensions(&self, path: &str) -> (u32, u32) { - self.image_pipeline.dimensions(path) + fn dimensions(&self, handle: &image::Handle) -> (u32, u32) { + self.image_pipeline.dimensions(handle) } - fn draw(&mut self, path: &str, layout: Layout<'_>) -> Self::Output { + fn draw( + &mut self, + handle: image::Handle, + layout: Layout<'_>, + ) -> Self::Output { ( Primitive::Image { - path: String::from(path), + handle, bounds: layout.bounds(), }, MouseCursor::OutOfBounds, diff --git a/wgpu/src/renderer/widget/progress_bar.rs b/wgpu/src/renderer/widget/progress_bar.rs new file mode 100644 index 00000000..34e33276 --- /dev/null +++ b/wgpu/src/renderer/widget/progress_bar.rs @@ -0,0 +1,54 @@ +use crate::{progress_bar::StyleSheet, Primitive, Renderer}; +use iced_native::{progress_bar, Color, MouseCursor, Rectangle}; + +impl progress_bar::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; + + const DEFAULT_HEIGHT: u16 = 30; + + fn draw( + &self, + bounds: Rectangle, + range: std::ops::RangeInclusive<f32>, + 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 + }, + MouseCursor::OutOfBounds, + ) + } +} diff --git a/wgpu/src/renderer/widget/radio.rs b/wgpu/src/renderer/widget/radio.rs index 3c00a4c2..564f066b 100644 --- a/wgpu/src/renderer/widget/radio.rs +++ b/wgpu/src/renderer/widget/radio.rs @@ -1,10 +1,12 @@ -use crate::{Primitive, Renderer}; -use iced_native::{radio, Background, MouseCursor, Rectangle}; +use crate::{radio::StyleSheet, Primitive, Renderer}; +use iced_native::{radio, Background, Color, MouseCursor, Rectangle}; const SIZE: f32 = 28.0; const DOT_SIZE: f32 = SIZE / 2.0; impl radio::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; + fn default_size(&self) -> u32 { SIZE as u32 } @@ -15,31 +17,21 @@ impl radio::Renderer for Renderer { is_selected: bool, is_mouse_over: bool, (label, _): Self::Output, + style_sheet: &Self::Style, ) -> Self::Output { - let (radio_border, radio_box) = ( - Primitive::Quad { - bounds, - background: Background::Color([0.6, 0.6, 0.6].into()), - border_radius: (SIZE / 2.0) as u16, - }, - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, - background: Background::Color( - if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: (SIZE / 2.0 - 1.0) as u16, - }, - ); + 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 { @@ -51,13 +43,15 @@ impl radio::Renderer for Renderer { width: bounds.width - DOT_SIZE, height: bounds.height - DOT_SIZE, }, - background: Background::Color([0.3, 0.3, 0.3].into()), + background: Background::Color(style.dot_color), border_radius: (DOT_SIZE / 2.0) as u16, + border_width: 0, + border_color: Color::TRANSPARENT, }; - vec![radio_border, radio_box, radio_circle, label] + vec![radio, radio_circle, label] } else { - vec![radio_border, radio_box, label] + vec![radio, label] }, }, if is_mouse_over { diff --git a/wgpu/src/renderer/widget/row.rs b/wgpu/src/renderer/widget/row.rs index f082dc61..bd9f1a04 100644 --- a/wgpu/src/renderer/widget/row.rs +++ b/wgpu/src/renderer/widget/row.rs @@ -4,6 +4,7 @@ use iced_native::{row, Element, Layout, MouseCursor, Point}; impl row::Renderer for Renderer { fn draw<Message>( &mut self, + defaults: &Self::Defaults, children: &[Element<'_, Message, Self>], layout: Layout<'_>, cursor_position: Point, @@ -17,7 +18,7 @@ impl row::Renderer for Renderer { .zip(layout.children()) .map(|(child, layout)| { let (primitive, new_mouse_cursor) = - child.draw(self, layout, cursor_position); + child.draw(self, defaults, layout, cursor_position); if new_mouse_cursor > mouse_cursor { mouse_cursor = new_mouse_cursor; diff --git a/wgpu/src/renderer/widget/scrollable.rs b/wgpu/src/renderer/widget/scrollable.rs index 58dc3df9..bfee7411 100644 --- a/wgpu/src/renderer/widget/scrollable.rs +++ b/wgpu/src/renderer/widget/scrollable.rs @@ -1,45 +1,63 @@ use crate::{Primitive, Renderer}; use iced_native::{ - scrollable, Background, MouseCursor, Point, Rectangle, Vector, + scrollable, Background, Color, MouseCursor, Rectangle, Vector, }; const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_MARGIN: u16 = 2; -fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { - 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, - } -} - impl scrollable::Renderer for Renderer { - fn is_mouse_over_scrollbar( + type Style = Box<dyn iced_style::scrollable::StyleSheet>; + + fn scrollbar( &self, bounds: Rectangle, content_bounds: Rectangle, - cursor_position: Point, - ) -> bool { - content_bounds.height > bounds.height - && scrollbar_bounds(bounds).contains(cursor_position) + offset: u32, + ) -> Option<scrollable::Scrollbar> { + 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, + _content_bounds: Rectangle, is_mouse_over: bool, is_mouse_over_scrollbar: bool, + scrollbar: Option<scrollable::Scrollbar>, offset: u32, + style_sheet: &Self::Style, (content, mouse_cursor): Self::Output, ) -> Self::Output { - let is_content_overflowing = content_bounds.height > bounds.height; - let scrollbar_bounds = scrollbar_bounds(bounds); - let clip = Primitive::Clip { bounds, offset: Vector::new(0, offset), @@ -47,51 +65,59 @@ impl scrollable::Renderer for Renderer { }; ( - if is_content_overflowing - && (is_mouse_over || state.is_scrollbar_grabbed()) - { - let ratio = bounds.height / content_bounds.height; - let scrollbar_height = bounds.height * ratio; - let y_offset = offset as f32 * ratio; + if let Some(scrollbar) = scrollbar { + 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 scrollbar = Primitive::Quad { - 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, - }, - background: Background::Color([0.0, 0.0, 0.0, 0.7].into()), - border_radius: 5, + 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 }; - if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { - let scrollbar_background = Primitive::Quad { + let scrollbar = if is_scrollbar_visible { + Primitive::Quad { bounds: Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), - width: scrollbar_bounds.width + x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), + width: scrollbar.bounds.width - f32::from(2 * SCROLLBAR_MARGIN), - ..scrollbar_bounds + ..scrollbar.bounds }, - background: Background::Color( - [0.0, 0.0, 0.0, 0.3].into(), - ), - border_radius: 5, - }; - - Primitive::Group { - primitives: vec![clip, scrollbar_background, scrollbar], + 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::Group { - primitives: vec![clip, scrollbar], - } + Primitive::None + }; + + Primitive::Group { + primitives: vec![clip, scrollbar, scroller], } } else { clip }, - if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { + if is_mouse_over_scrollbar || state.is_scroller_grabbed() { MouseCursor::Idle } else { mouse_cursor diff --git a/wgpu/src/renderer/widget/slider.rs b/wgpu/src/renderer/widget/slider.rs index f561be0a..c8ebd0da 100644 --- a/wgpu/src/renderer/widget/slider.rs +++ b/wgpu/src/renderer/widget/slider.rs @@ -1,10 +1,14 @@ -use crate::{Primitive, Renderer}; +use crate::{ + slider::{HandleShape, StyleSheet}, + Primitive, Renderer, +}; use iced_native::{slider, Background, Color, MouseCursor, Point, Rectangle}; -const HANDLE_WIDTH: f32 = 8.0; const HANDLE_HEIGHT: f32 = 22.0; impl slider::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; + fn height(&self) -> u32 { 30 } @@ -16,9 +20,18 @@ impl slider::Renderer for Renderer { range: std::ops::RangeInclusive<f32>, 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) = ( @@ -29,8 +42,10 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color([0.6, 0.6, 0.6].into()), + background: Background::Color(style.rail_colors.0), border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, }, Primitive::Quad { bounds: Rectangle { @@ -39,51 +54,45 @@ impl slider::Renderer for Renderer { width: bounds.width, height: 2.0, }, - background: Background::Color(Color::WHITE), + 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_offset = (bounds.width - HANDLE_WIDTH) + 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_border, handle) = ( - Primitive::Quad { - bounds: Rectangle { - x: bounds.x + handle_offset.round() - 1.0, - y: rail_y - HANDLE_HEIGHT / 2.0 - 1.0, - width: HANDLE_WIDTH + 2.0, - height: HANDLE_HEIGHT + 2.0, - }, - background: Background::Color([0.6, 0.6, 0.6].into()), - border_radius: 5, + 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, }, - 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( - if is_dragging { - [0.85, 0.85, 0.85] - } else if is_mouse_over { - [0.90, 0.90, 0.90] - } else { - [0.95, 0.95, 0.95] - } - .into(), - ), - border_radius: 4, - }, - ); + 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_border, handle], + primitives: vec![rail_top, rail_bottom, handle], }, if is_dragging { MouseCursor::Grabbing diff --git a/wgpu/src/renderer/widget/space.rs b/wgpu/src/renderer/widget/space.rs new file mode 100644 index 00000000..28e05437 --- /dev/null +++ b/wgpu/src/renderer/widget/space.rs @@ -0,0 +1,8 @@ +use crate::{Primitive, Renderer}; +use iced_native::{space, MouseCursor, Rectangle}; + +impl space::Renderer for Renderer { + fn draw(&mut self, _bounds: Rectangle) -> Self::Output { + (Primitive::None, MouseCursor::OutOfBounds) + } +} diff --git a/wgpu/src/renderer/widget/svg.rs b/wgpu/src/renderer/widget/svg.rs new file mode 100644 index 00000000..67bc3fe1 --- /dev/null +++ b/wgpu/src/renderer/widget/svg.rs @@ -0,0 +1,22 @@ +use crate::{Primitive, Renderer}; +use iced_native::{svg, Layout, MouseCursor}; + +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(), + }, + MouseCursor::OutOfBounds, + ) + } +} diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs index 08a162ba..33e549cd 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/wgpu/src/renderer/widget/text.rs @@ -6,13 +6,8 @@ use iced_native::{ use std::f32; -// TODO: Obtain from renderer configuration -const DEFAULT_TEXT_SIZE: f32 = 20.0; - impl text::Renderer for Renderer { - fn default_size(&self) -> u16 { - DEFAULT_TEXT_SIZE as u16 - } + const DEFAULT_SIZE: u16 = 20; fn measure( &self, @@ -27,6 +22,7 @@ impl text::Renderer for Renderer { fn draw( &mut self, + defaults: &Self::Defaults, bounds: Rectangle, content: &str, size: u16, @@ -40,7 +36,7 @@ impl text::Renderer for Renderer { content: content.to_string(), size: f32::from(size), bounds, - color: color.unwrap_or(Color::BLACK), + color: color.unwrap_or(defaults.text.color), font, horizontal_alignment, vertical_alignment, diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index 9ed3b415..e2a1b3a9 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -1,4 +1,4 @@ -use crate::{Primitive, Renderer}; +use crate::{text_input::StyleSheet, Primitive, Renderer}; use iced_native::{ text_input, Background, Color, Font, HorizontalAlignment, MouseCursor, @@ -7,48 +7,85 @@ use iced_native::{ use std::f32; impl text_input::Renderer for Renderer { + type Style = Box<dyn StyleSheet>; + 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_at_the_end = value.len() - value.trim_end().len(); + + if spaces_at_the_end > 0 { + let space_width = self.text_pipeline.space_width(size as f32); + width += spaces_at_the_end as f32 * space_width; + } + + width + } + + fn offset( + &self, + text_bounds: Rectangle, + size: u16, + value: &text_input::Value, + state: &text_input::State, + font: Font, + ) -> f32 { + if state.is_focused() { + let (_, offset) = measure_cursor_and_scroll_offset( + self, + text_bounds, + value, + size, + state.cursor_position(value), + font, + ); + + offset + } else { + 0.0 + } + } + fn draw( &mut self, bounds: Rectangle, text_bounds: Rectangle, cursor_position: Point, size: u16, + font: Font, 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 border = Primitive::Quad { - bounds, - background: Background::Color( - if is_mouse_over || state.is_focused() { - [0.5, 0.5, 0.5] - } else { - [0.7, 0.7, 0.7] - } - .into(), - ), - border_radius: 5, + 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: Rectangle { - x: bounds.x + 1.0, - y: bounds.y + 1.0, - width: bounds.width - 2.0, - height: bounds.height - 2.0, - }, - background: Background::Color(Color::WHITE), - border_radius: 5, + bounds, + background: style.background, + border_radius: style.border_radius, + border_width: style.border_width, + border_color: style.border_color, }; - let size = f32::from(size); let text = value.to_string(); let text_value = Primitive::Text { @@ -58,40 +95,31 @@ impl text_input::Renderer for Renderer { text.clone() }, color: if text.is_empty() { - [0.7, 0.7, 0.7] + style_sheet.placeholder_color() } else { - [0.3, 0.3, 0.3] + style_sheet.value_color() } .into(), - font: Font::Default, + font, bounds: Rectangle { width: f32::INFINITY, ..text_bounds }, - size, + size: f32::from(size), horizontal_alignment: HorizontalAlignment::Left, vertical_alignment: VerticalAlignment::Center, }; let (contents_primitive, offset) = if state.is_focused() { - let text_before_cursor = - value.until(state.cursor_position(value)).to_string(); - - let (mut text_value_width, _) = self.text_pipeline.measure( - &text_before_cursor, + let (text_value_width, offset) = measure_cursor_and_scroll_offset( + self, + text_bounds, + value, size, - Font::Default, - Size::new(f32::INFINITY, text_bounds.height), + state.cursor_position(value), + font, ); - let spaces_at_the_end = - text_before_cursor.len() - text_before_cursor.trim_end().len(); - - if spaces_at_the_end > 0 { - let space_width = self.text_pipeline.space_width(size); - text_value_width += spaces_at_the_end as f32 * space_width; - } - let cursor = Primitive::Quad { bounds: Rectangle { x: text_bounds.x + text_value_width, @@ -99,19 +127,17 @@ impl text_input::Renderer for Renderer { width: 1.0, height: text_bounds.height, }, - background: Background::Color(Color::BLACK), + background: Background::Color(style_sheet.value_color()), border_radius: 0, + border_width: 0, + border_color: Color::TRANSPARENT, }; ( Primitive::Group { primitives: vec![text_value, cursor], }, - Vector::new( - ((text_value_width + 5.0) - text_bounds.width).max(0.0) - as u32, - 0, - ), + Vector::new(offset as u32, 0), ) } else { (text_value, Vector::new(0, 0)) @@ -125,7 +151,7 @@ impl text_input::Renderer for Renderer { ( Primitive::Group { - primitives: vec![border, input, contents], + primitives: vec![input, contents], }, if is_mouse_over { MouseCursor::Text @@ -135,3 +161,22 @@ impl text_input::Renderer for Renderer { ) } } + +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) +} |