Skip to content

Add NumberInput widget #11

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

Merged
merged 6 commits into from
May 4, 2021
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
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ modal = []
tab_bar = []
tabs = ["tab_bar"]
time_picker = ["chrono", "icon_text", "iced_graphics/canvas"]
number_input = ["num-traits"]

default = [
"badge",
Expand All @@ -42,6 +43,7 @@ default = [

[dependencies]
iced_style = "0.3"
num-traits = { version = "0.2.14", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
iced_native = "0.4"
Expand Down Expand Up @@ -69,5 +71,6 @@ members = [
#"examples/tabs",
#"examples/tabs_min",
"examples/time_picker",
"examples/web"
"examples/web",
"examples/number_input",
]
11 changes: 11 additions & 0 deletions examples/number_input/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "number_input"
version = "0.1.0"
authors = ["leang27 <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
iced = "0.3"
iced_aw = { path = "../..", default-features = false, features = ["number_input"] }
124 changes: 124 additions & 0 deletions examples/number_input/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use iced::{window, Align, Container, Element, Length, Row, Sandbox, Settings, Text};
use iced_aw::number_input::{self, NumberInput};

#[derive(Default)]
pub struct NumberInputDemo {
state: number_input::State,
value: f32,
}

#[derive(Debug, Clone)]
pub enum Message {
NumInpChanged(f32),
}

fn main() -> iced::Result {
NumberInputDemo::run(Settings {
default_text_size: 14,
window: window::Settings {
size: (250, 200),
..Default::default()
},
..Settings::default()
})
}

impl Sandbox for NumberInputDemo {
type Message = Message;

fn new() -> Self {
Self {
value: 27.0,
..Self::default()
}
}

fn title(&self) -> String {
String::from("Number Input Demo")
}

fn update(&mut self, message: Message) {
match message {
Message::NumInpChanged(val) => {
self.value = val;
}
}
}

fn view(&mut self) -> Element<Message> {
let lb_minute = Text::new("Number Input:");
let txt_minute =
NumberInput::new(&mut self.state, self.value, 255.0, Message::NumInpChanged)
.step(0.5)
.min(1.0)
.input_style(style::CustomTextInput)
.style(style::CustomNumInput);

Container::new(
Row::new()
.spacing(10)
.align_items(Align::Center)
.push(lb_minute)
.push(txt_minute),
)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}

mod style {
use iced::{text_input, Color};
use iced_aw::number_input;

const BACKGROUND: Color = Color::from_rgb(238.0 / 255.0, 238.0 / 255.0, 238.0 / 255.0);
const FOREGROUND: Color = Color::from_rgb(224.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0);
const HOVERED: Color = Color::from_rgb(129.0 / 255.0, 129.0 / 255.0, 129.0 / 255.0);
const PRIMARY: Color = Color::from_rgb(12.0 / 255.0, 46.0 / 251.0, 179.0 / 255.0);

pub struct CustomNumInput;
impl number_input::StyleSheet for CustomNumInput {
fn active(&self) -> number_input::Style {
number_input::Style {
icon_color: PRIMARY,
..number_input::Style::default()
}
}
}

pub struct CustomTextInput;
impl text_input::StyleSheet for CustomTextInput {
fn active(&self) -> text_input::Style {
text_input::Style {
background: BACKGROUND.into(),
border_color: PRIMARY,
border_width: 1.0,
border_radius: 5.5,
..text_input::Style::default()
}
}

fn focused(&self) -> text_input::Style {
let active = self.active();

text_input::Style {
background: FOREGROUND.into(),
..active
}
}

fn placeholder_color(&self) -> Color {
HOVERED
}

fn selection_color(&self) -> Color {
HOVERED
}

fn value_color(&self) -> Color {
Color::BLACK
}
}
}
5 changes: 5 additions & 0 deletions src/graphics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,8 @@ pub mod time_picker;
#[doc(no_inline)]
#[cfg(feature = "time_picker")]
pub use time_picker::TimePicker;

#[cfg(feature = "number_input")]
pub mod number_input;
#[cfg(feature = "number_input")]
pub use number_input::NumberInput;
128 changes: 128 additions & 0 deletions src/graphics/number_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//! Display fields that can only be filled with numeric type.
//!
//! A [`NumberInput`] has some local [`State`].
use crate::native::number_input::{self, ModifierState};
use iced_graphics::backend::{self, Backend};
use iced_graphics::{Primitive, Renderer};
use iced_native::mouse;
use iced_native::{Background, Color, HorizontalAlignment, Point, Rectangle, VerticalAlignment};

pub use crate::native::number_input::State;
pub use crate::style::number_input::{Style, StyleSheet};

/// A field that can only be filled with numeric type.
///
/// This is an alias of an `iced_native` number input with an `iced_wgpu::Renderer`.
pub type NumberInput<'a, T, Message, Backend> =
number_input::NumberInput<'a, T, Message, Renderer<Backend>>;

impl<B> number_input::Renderer for Renderer<B>
where
B: Backend + backend::Text,
{
type Style = Box<dyn StyleSheet>;

const DEFAULT_PADDING: u16 = 5;

fn draw(
&mut self,
cursor_position: Point,
state: &ModifierState,
inc_bounds: Rectangle,
dec_bounds: Rectangle,
is_mouse_over: bool,
is_decrease_disabled: bool,
is_increase_disabled: bool,
(content, _): Self::Output,
style: &<Self as number_input::Renderer>::Style,
font: Self::Font,
) -> Self::Output {
let mouse_over_decrease = dec_bounds.contains(cursor_position);
let mouse_over_increase = inc_bounds.contains(cursor_position);

let decrease_btn_style = if is_decrease_disabled {
style.disabled()
} else if state.decrease_pressed {
style.pressed()
} else {
style.active()
};

let increase_btn_style = if is_increase_disabled {
style.disabled()
} else if state.increase_pressed {
style.pressed()
} else {
style.active()
};

// decrease button section
let decrease_button_rect = Primitive::Quad {
bounds: dec_bounds,
background: decrease_btn_style
.button_background
.unwrap_or(Background::Color(Color::TRANSPARENT)),
border_radius: 3.0,
border_width: 0.,
border_color: Color::TRANSPARENT,
};
let decrease_text = Primitive::Text {
content: String::from("\u{25bc}"),
bounds: Rectangle {
x: dec_bounds.center_x(),
y: dec_bounds.center_y(),
..dec_bounds
},
font,
size: dec_bounds.height * 0.9,
color: decrease_btn_style.icon_color,
horizontal_alignment: HorizontalAlignment::Center,
vertical_alignment: VerticalAlignment::Center,
};
let decrease_btn = Primitive::Group {
primitives: vec![decrease_button_rect, decrease_text],
};

// increase button section
let increase_button_rect = Primitive::Quad {
bounds: inc_bounds,
background: increase_btn_style
.button_background
.unwrap_or(Background::Color(Color::TRANSPARENT)),
border_radius: 3.0,
border_width: 0.,
border_color: Color::TRANSPARENT,
};
let increase_text = Primitive::Text {
content: String::from("\u{25b2}"),
bounds: Rectangle {
x: inc_bounds.center_x(),
y: inc_bounds.center_y(),
..inc_bounds
},
font,
size: inc_bounds.height * 0.9,
color: increase_btn_style.icon_color,
horizontal_alignment: HorizontalAlignment::Center,
vertical_alignment: VerticalAlignment::Center,
};
let increase_btn = Primitive::Group {
primitives: vec![increase_button_rect, increase_text],
};

(
Primitive::Group {
primitives: vec![content, decrease_btn, increase_btn],
},
if (mouse_over_decrease && !is_decrease_disabled)
|| (mouse_over_increase && !is_increase_disabled)
{
mouse::Interaction::Pointer
} else if is_mouse_over {
mouse::Interaction::Text
} else {
mouse::Interaction::default()
},
)
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ mod platform {
#[doc(no_inline)]
#[cfg(feature = "time_picker")]
pub use {crate::graphics::time_picker, time_picker::TimePicker};

#[doc(no_inline)]
#[cfg(feature = "number_input")]
pub use {crate::graphics::number_input, number_input::NumberInput};
}
#[cfg(target_arch = "wasm32")]
pub mod web;
Expand Down
5 changes: 5 additions & 0 deletions src/native/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ pub use tabs::Tabs;
pub mod time_picker;
#[cfg(feature = "time_picker")]
pub use time_picker::TimePicker;

#[cfg(feature = "number_input")]
pub mod number_input;
#[cfg(feature = "number_input")]
pub use number_input::NumberInput;
Loading