From d60f3b89a75f5b2ad8e6fb17827f5574a0a44bd1 Mon Sep 17 00:00:00 2001 From: Songtronix Date: Wed, 1 Jan 2020 17:27:14 +0100 Subject: [PATCH] add(widget): primitive progressbar widget --- core/src/background.rs | 2 +- examples/progressbar.rs | 51 +++++++++ native/src/widget.rs | 3 + native/src/widget/progressbar.rs | 135 ++++++++++++++++++++++++ native/src/widget/text_input.rs | 3 +- wgpu/src/renderer/widget.rs | 1 + wgpu/src/renderer/widget/progressbar.rs | 43 ++++++++ winit/src/conversion.rs | 3 +- winit/src/debug/basic.rs | 3 +- 9 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 examples/progressbar.rs create mode 100644 native/src/widget/progressbar.rs create mode 100644 wgpu/src/renderer/widget/progressbar.rs diff --git a/core/src/background.rs b/core/src/background.rs index 2f75e45c..e1a37ddc 100644 --- a/core/src/background.rs +++ b/core/src/background.rs @@ -12,4 +12,4 @@ impl From for Background { fn from(color: Color) -> Self { Background::Color(color) } -} \ No newline at end of file +} diff --git a/examples/progressbar.rs b/examples/progressbar.rs new file mode 100644 index 00000000..4663f8df --- /dev/null +++ b/examples/progressbar.rs @@ -0,0 +1,51 @@ +use iced::{slider, Column, Element, Sandbox, Settings, Slider}; +use iced_winit::Progressbar; + +pub fn main() { + Progress::run(Settings::default()) +} + +#[derive(Default)] +struct Progress { + value: f32, + progressbar_slider: slider::State, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + SliderChanged(f32), +} + +impl Sandbox for Progress { + type Message = Message; + + fn new() -> Self { + Self::default() + } + + fn title(&self) -> String { + String::from("A simple Progressbar") + } + + fn update(&mut self, message: Message) { + match message { + Message::SliderChanged(x) => { + self.value = x; + } + } + } + + fn view(&mut self) -> Element { + Column::new() + .padding(20) + .push(Progressbar::new(0.0..=100.0, self.value)) + .padding(20) + .push(Slider::new( + &mut self.progressbar_slider, + 0.0..=100.0, + self.value, + Message::SliderChanged, + )) + .into() + } +} diff --git a/native/src/widget.rs b/native/src/widget.rs index dcc99645..d899da58 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -25,6 +25,7 @@ pub mod checkbox; pub mod column; pub mod container; pub mod image; +pub mod progressbar; pub mod radio; pub mod row; pub mod scrollable; @@ -45,6 +46,8 @@ pub use container::Container; #[doc(no_inline)] pub use image::Image; #[doc(no_inline)] +pub use progressbar::Progressbar; +#[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] pub use row::Row; diff --git a/native/src/widget/progressbar.rs b/native/src/widget/progressbar.rs new file mode 100644 index 00000000..a6725f2c --- /dev/null +++ b/native/src/widget/progressbar.rs @@ -0,0 +1,135 @@ +//! Display a progressbar +//! +//! +//! [`Progressbar`]: struct.Progressbar.html +use crate::{ + layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget, +}; + +use std::{hash::Hash, ops::RangeInclusive}; + +/// A progressbar +/// +/// # Example +/// +/// ``` +/// # use iced_native::Progressbar; +/// +/// Progressbar::new(0.0..=100.0, value); +/// ``` +/// +/// TODO: Get a progressbar render +/// ![Slider drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/slider.png?raw=true) +#[allow(missing_debug_implementations)] +pub struct Progressbar { + range: RangeInclusive, + value: f32, + width: Length, +} + +impl Progressbar { + /// Creates a new [`Progressbar`]. + /// + /// It expects: + /// * an inclusive range of possible values + /// * the current value of the [`Progressbar`] + /// + /// [`Progressbar`]: struct.Progressbar.html + pub fn new(range: RangeInclusive, value: f32) -> Self { + Progressbar { + value: value.max(*range.start()).min(*range.end()), + range, + width: Length::Fill, + } + } + + /// Sets the width of the [`Progressbar`]. + /// + /// [`Progressbar`]: struct.Progressbar.html + pub fn width(mut self, width: Length) -> Self { + self.width = width; + self + } +} + +impl Widget for Progressbar +where + Renderer: self::Renderer, +{ + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits + .width(self.width) + .height(Length::Units(renderer.height() as u16)); + + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn draw( + &self, + renderer: &mut Renderer, + layout: Layout<'_>, + _cursor_position: Point, + ) -> Renderer::Output { + renderer.draw(layout.bounds(), self.range.clone(), self.value) + } + + fn hash_layout(&self, state: &mut Hasher) { + self.width.hash(state); + } +} + +/// The renderer of a [`Progressbar`]. +/// +/// Your [renderer] will need to implement this trait before being +/// able to use a [`Progressbar`] in your user interface. +/// +/// [`Progressbar`]: struct.Progressbar.html +/// [renderer]: ../../renderer/index.html +pub trait Renderer: crate::Renderer { + /// Returns the height of the [`Progressbar`]. + /// + /// [`Progressbar`]: struct.Progressbar.html + fn height(&self) -> u32; + + /// Draws a [`Progressbar`]. + /// + /// It receives: + /// * the local state of the [`Progressbar`] + /// * the bounds of the [`Progressbar`] + /// * the range of values of the [`Progressbar`] + /// * the current value of the [`Progressbar`] + /// + /// [`Progressbar`]: struct.Progressbar.html + /// [`State`]: struct.State.html + /// [`Class`]: enum.Class.html + fn draw( + &self, + bounds: Rectangle, + range: RangeInclusive, + value: f32, + ) -> Self::Output; +} + +impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> +where + Renderer: self::Renderer, + Message: 'static, +{ + fn from(progressbar: Progressbar) -> Element<'a, Message, Renderer> { + Element::new(progressbar) + } +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 1d1c32a2..c994b7ba 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -633,7 +633,8 @@ impl Value { .unwrap_or(self.len()) } - /// Returns a new [`Value`] containing the graphemes until the given `index`. + /// Returns a new [`Value`] containing the graphemes until the given + /// `index`. /// /// [`Value`]: struct.Value.html pub fn until(&self, index: usize) -> Self { diff --git a/wgpu/src/renderer/widget.rs b/wgpu/src/renderer/widget.rs index f82631d5..94ebd10e 100644 --- a/wgpu/src/renderer/widget.rs +++ b/wgpu/src/renderer/widget.rs @@ -2,6 +2,7 @@ mod button; mod checkbox; mod column; mod image; +mod progressbar; mod radio; mod row; mod scrollable; diff --git a/wgpu/src/renderer/widget/progressbar.rs b/wgpu/src/renderer/widget/progressbar.rs new file mode 100644 index 00000000..3c62e54d --- /dev/null +++ b/wgpu/src/renderer/widget/progressbar.rs @@ -0,0 +1,43 @@ +use crate::{Primitive, Renderer}; +use iced_native::{progressbar, Background, Color, MouseCursor, Rectangle}; + +impl progressbar::Renderer for Renderer { + fn height(&self) -> u32 { + 30 + } + + fn draw( + &self, + bounds: Rectangle, + range: std::ops::RangeInclusive, + value: f32, + ) -> Self::Output { + 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: Color::from_rgb(0.6, 0.6, 0.6).into(), + border_radius: 5, + }], + }; + + let active_progress = Primitive::Quad { + bounds: Rectangle { + width: active_progress_width, + ..bounds + }, + background: Background::Color([0.0, 0.95, 0.0].into()), + border_radius: 4, + }; + + ( + Primitive::Group { + primitives: vec![background, active_progress], + }, + MouseCursor::OutOfBounds, + ) + } +} diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 0537d853..279b975a 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -50,7 +50,8 @@ pub fn button_state(element_state: winit::event::ElementState) -> ButtonState { } } -/// Convert some `ModifiersState` from [`winit`] to an [`iced_native`] modifiers state. +/// Convert some `ModifiersState` from [`winit`] to an [`iced_native`] modifiers +/// state. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native diff --git a/winit/src/debug/basic.rs b/winit/src/debug/basic.rs index c9da392c..d46edba6 100644 --- a/winit/src/debug/basic.rs +++ b/winit/src/debug/basic.rs @@ -1,5 +1,4 @@ -use std::collections::VecDeque; -use std::time; +use std::{collections::VecDeque, time}; #[derive(Debug)] pub struct Debug {