Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Progress bar widget #141

Merged
merged 7 commits into from
Jan 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/src/background.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ impl From<Color> for Background {
fn from(color: Color) -> Self {
Background::Color(color)
}
}
}
63 changes: 63 additions & 0 deletions examples/progress_bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use iced::{
settings::Window, slider, Background, Color, Column, Element, Length,
ProgressBar, Sandbox, Settings, Slider,
};

pub fn main() {
Progress::run(Settings {
window: Window {
size: (700, 300),
resizable: true,
decorations: true,
},
})
}

#[derive(Default)]
struct Progress {
value: f32,
progress_bar_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<Message> {
Column::new()
.padding(20)
.push(
ProgressBar::new(0.0..=100.0, self.value)
.background(Background::Color(Color::from_rgb(
0.6, 0.6, 0.6,
)))
.active_color(Color::from_rgb(0.0, 0.95, 0.0))
.height(Length::Units(30)),
)
.push(Slider::new(
&mut self.progress_bar_slider,
0.0..=100.0,
self.value,
Message::SliderChanged,
))
.into()
}
}
3 changes: 3 additions & 0 deletions native/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod checkbox;
pub mod column;
pub mod container;
pub mod image;
pub mod progress_bar;
pub mod radio;
pub mod row;
pub mod scrollable;
Expand All @@ -45,6 +46,8 @@ pub use container::Container;
#[doc(no_inline)]
pub use image::Image;
#[doc(no_inline)]
pub use progress_bar::ProgressBar;
#[doc(no_inline)]
pub use radio::Radio;
#[doc(no_inline)]
pub use row::Row;
Expand Down
173 changes: 173 additions & 0 deletions native/src/widget/progress_bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//! Provide progress feedback to your users.
use crate::{
layout, Background, Color, Element, Hasher, Layout, Length, Point,
Rectangle, Size, Widget,
};

use std::{hash::Hash, ops::RangeInclusive};

/// A bar that displays progress.
///
/// # Example
/// ```
/// # use iced_native::ProgressBar;
/// #
/// let value = 50.0;
///
/// ProgressBar::new(0.0..=100.0, value);
/// ```
///
/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)]
pub struct ProgressBar {
range: RangeInclusive<f32>,
value: f32,
width: Length,
height: Option<Length>,
background: Option<Background>,
active_color: Option<Color>,
}

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<f32>, value: f32) -> Self {
ProgressBar {
value: value.max(*range.start()).min(*range.end()),
range,
width: Length::Fill,
height: None,
background: None,
active_color: None,
}
}

/// Sets the width of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}

/// Sets the height of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn height(mut self, height: Length) -> Self {
self.height = Some(height);
self
}

/// Sets the background of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn background(mut self, background: Background) -> Self {
self.background = Some(background);
self
}

/// Sets the active color of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn active_color(mut self, active_color: Color) -> Self {
self.active_color = Some(active_color);
self
}
}

impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar
where
Renderer: self::Renderer,
{
fn width(&self) -> Length {
self.width
}

fn height(&self) -> Length {
self.height
.unwrap_or(Length::Units(Renderer::DEFAULT_HEIGHT))
}

fn layout(
&self,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(
self.height
.unwrap_or(Length::Units(Renderer::DEFAULT_HEIGHT)),
);

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,
self.background,
self.active_color,
)
}

fn hash_layout(&self, state: &mut Hasher) {
self.width.hash(state);
self.height.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 {
/// The default height of a [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
const DEFAULT_HEIGHT: u16;

/// Draws a [`ProgressBar`].
///
/// It receives:
/// * the bounds of the [`ProgressBar`]
/// * the range of values of the [`ProgressBar`]
/// * the current value of the [`ProgressBar`]
/// * maybe a specific background of the [`ProgressBar`]
/// * maybe a specific active color of the [`ProgressBar`]
///
/// [`ProgressBar`]: struct.ProgressBar.html
fn draw(
&self,
bounds: Rectangle,
range: RangeInclusive<f32>,
value: f32,
background: Option<Background>,
active_color: Option<Color>,
) -> Self::Output;
}

impl<'a, Message, Renderer> From<ProgressBar> for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
Message: 'static,
{
fn from(progress_bar: ProgressBar) -> Element<'a, Message, Renderer> {
Element::new(progress_bar)
}
}
3 changes: 2 additions & 1 deletion native/src/widget/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub mod widget {
pub use iced_winit::svg::{Handle, Svg};
}

pub use iced_winit::{Checkbox, Radio, Text};
pub use iced_winit::{Checkbox, ProgressBar, Radio, Text};

#[doc(no_inline)]
pub use {
Expand Down
14 changes: 14 additions & 0 deletions web/src/widget/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct TextInput<'a, Message> {
_state: &'a mut State,
placeholder: String,
value: String,
is_secure: bool,
width: Length,
max_width: Length,
padding: u16,
Expand Down Expand Up @@ -64,6 +65,7 @@ impl<'a, Message> TextInput<'a, Message> {
_state: state,
placeholder: String::from(placeholder),
value: String::from(value),
is_secure: false,
width: Length::Fill,
max_width: Length::Shrink,
padding: 0,
Expand All @@ -73,6 +75,14 @@ impl<'a, Message> TextInput<'a, Message> {
}
}

/// Converts the [`TextInput`] into a secure password input.
///
/// [`TextInput`]: struct.TextInput.html
pub fn password(mut self) -> Self {
self.is_secure = true;
self
}

/// Sets the width of the [`TextInput`].
///
/// [`TextInput`]: struct.TextInput.html
Expand Down Expand Up @@ -161,6 +171,10 @@ where
"value",
bumpalo::format!(in bump, "{}", self.value).into_bump_str(),
)
.attr(
"type",
bumpalo::format!(in bump, "{}", if self.is_secure { "password" } else { "text" }).into_bump_str(),
)
.on("input", move |root, vdom, event| {
let text_input = match event.target().and_then(|t| {
t.dyn_into::<web_sys::HtmlInputElement>().ok()
Expand Down
1 change: 1 addition & 0 deletions wgpu/src/renderer/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod button;
mod checkbox;
mod column;
mod image;
mod progress_bar;
mod radio;
mod row;
mod scrollable;
Expand Down
47 changes: 47 additions & 0 deletions wgpu/src/renderer/widget/progress_bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::{Primitive, Renderer};
use iced_native::{progress_bar, Background, Color, MouseCursor, Rectangle};

impl progress_bar::Renderer for Renderer {
const DEFAULT_HEIGHT: u16 = 30;

fn draw(
&self,
bounds: Rectangle,
range: std::ops::RangeInclusive<f32>,
value: f32,
background: Option<Background>,
active_color: Option<Color>,
) -> 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: background
.unwrap_or(Background::Color([0.6, 0.6, 0.6].into()))
.into(),
border_radius: 5,
}],
};

let active_progress = Primitive::Quad {
bounds: Rectangle {
width: active_progress_width,
..bounds
},
background: Background::Color(
active_color.unwrap_or([0.0, 0.95, 0.0].into()),
),
border_radius: 5,
};

(
Primitive::Group {
primitives: vec![background, active_progress],
},
MouseCursor::OutOfBounds,
)
}
}
3 changes: 2 additions & 1 deletion winit/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions winit/src/debug/basic.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::collections::VecDeque;
use std::time;
use std::{collections::VecDeque, time};

#[derive(Debug)]
pub struct Debug {
Expand Down