summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/src/widget/button.rs8
-rw-r--r--core/src/widget/text_input.rs13
-rw-r--r--examples/resources/icons.ttfbin0 -> 5596 bytes
-rw-r--r--examples/todos.rs178
-rw-r--r--examples/tour.rs1
-rw-r--r--native/src/widget/checkbox.rs6
-rw-r--r--native/src/widget/container.rs2
-rw-r--r--native/src/widget/text_input.rs8
-rw-r--r--wgpu/src/renderer/widget/button.rs45
9 files changed, 221 insertions, 40 deletions
diff --git a/core/src/widget/button.rs b/core/src/widget/button.rs
index 9cf20071..e7961284 100644
--- a/core/src/widget/button.rs
+++ b/core/src/widget/button.rs
@@ -19,6 +19,8 @@ pub struct Button<'a, Message, Element> {
pub width: Length,
+ pub min_width: u32,
+
pub padding: u16,
pub background: Option<Background>,
@@ -52,6 +54,7 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
content: content.into(),
on_press: None,
width: Length::Shrink,
+ min_width: 0,
padding: 0,
background: None,
border_radius: 0,
@@ -66,6 +69,11 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
self
}
+ pub fn min_width(mut self, min_width: u32) -> Self {
+ self.min_width = min_width;
+ self
+ }
+
pub fn padding(mut self, padding: u16) -> Self {
self.padding = padding;
self
diff --git a/core/src/widget/text_input.rs b/core/src/widget/text_input.rs
index 9c7f4bc8..450a7cae 100644
--- a/core/src/widget/text_input.rs
+++ b/core/src/widget/text_input.rs
@@ -91,6 +91,15 @@ impl State {
Self::default()
}
+ pub fn focused() -> Self {
+ use std::usize;
+
+ Self {
+ is_focused: true,
+ cursor_position: usize::MAX,
+ }
+ }
+
pub fn move_cursor_right(&mut self, value: &Value) {
let current = self.cursor_position(value);
@@ -107,6 +116,10 @@ impl State {
}
}
+ pub fn move_cursor_to_end(&mut self, value: &Value) {
+ self.cursor_position = value.len();
+ }
+
pub fn cursor_position(&self, value: &Value) -> usize {
self.cursor_position.min(value.len())
}
diff --git a/examples/resources/icons.ttf b/examples/resources/icons.ttf
new file mode 100644
index 00000000..4498299d
--- /dev/null
+++ b/examples/resources/icons.ttf
Binary files differ
diff --git a/examples/todos.rs b/examples/todos.rs
index 7f37300b..028b2d65 100644
--- a/examples/todos.rs
+++ b/examples/todos.rs
@@ -1,6 +1,7 @@
use iced::{
- scrollable, text::HorizontalAlignment, text_input, Application, Checkbox,
- Color, Column, Container, Element, Length, Scrollable, Text, TextInput,
+ button, scrollable, text::HorizontalAlignment, text_input, Align,
+ Application, Background, Button, Checkbox, Color, Column, Container,
+ Element, Font, Length, Row, Scrollable, Text, TextInput,
};
pub fn main() {
@@ -19,7 +20,7 @@ struct Todos {
pub enum Message {
InputChanged(String),
CreateTask,
- TaskChanged(usize, bool),
+ TaskMessage(usize, TaskMessage),
}
impl Application for Todos {
@@ -40,9 +41,12 @@ impl Application for Todos {
self.input_value.clear();
}
}
- Message::TaskChanged(i, completed) => {
+ Message::TaskMessage(i, TaskMessage::Delete) => {
+ self.tasks.remove(i);
+ }
+ Message::TaskMessage(i, task_message) => {
if let Some(task) = self.tasks.get_mut(i) {
- task.completed = completed;
+ task.update(task_message);
}
}
}
@@ -66,15 +70,29 @@ impl Application for Todos {
.size(30)
.on_submit(Message::CreateTask);
- let tasks = self.tasks.iter_mut().enumerate().fold(
- Column::new().spacing(20),
- |column, (i, task)| {
- column.push(
- task.view()
- .map(move |state| Message::TaskChanged(i, state)),
+ let tasks: Element<_> =
+ if self.tasks.len() > 0 {
+ self.tasks
+ .iter_mut()
+ .enumerate()
+ .fold(Column::new().spacing(20), |column, (i, task)| {
+ column.push(task.view().map(move |message| {
+ Message::TaskMessage(i, message)
+ }))
+ })
+ .into()
+ } else {
+ Container::new(
+ Text::new("You do not have any tasks! :D")
+ .size(25)
+ .horizontal_alignment(HorizontalAlignment::Center)
+ .color([0.7, 0.7, 0.7]),
)
- },
- );
+ .width(Length::Fill)
+ .height(Length::Units(200))
+ .center_y()
+ .into()
+ };
let content = Column::new()
.max_width(800)
@@ -94,6 +112,27 @@ impl Application for Todos {
struct Task {
description: String,
completed: bool,
+ state: TaskState,
+}
+
+#[derive(Debug)]
+pub enum TaskState {
+ Idle {
+ edit_button: button::State,
+ },
+ Editing {
+ text_input: text_input::State,
+ delete_button: button::State,
+ },
+}
+
+#[derive(Debug, Clone)]
+pub enum TaskMessage {
+ Completed(bool),
+ Edit,
+ DescriptionEdited(String),
+ FinishEdition,
+ Delete,
}
impl Task {
@@ -101,12 +140,97 @@ impl Task {
Task {
description,
completed: false,
+ state: TaskState::Idle {
+ edit_button: button::State::new(),
+ },
}
}
- fn view(&mut self) -> Element<bool> {
- Checkbox::new(self.completed, &self.description, |checked| checked)
- .into()
+ fn update(&mut self, message: TaskMessage) {
+ match message {
+ TaskMessage::Completed(completed) => {
+ self.completed = completed;
+ }
+ TaskMessage::Edit => {
+ self.state = TaskState::Editing {
+ text_input: text_input::State::focused(),
+ delete_button: button::State::new(),
+ };
+ }
+ TaskMessage::DescriptionEdited(new_description) => {
+ self.description = new_description;
+ }
+ TaskMessage::FinishEdition => {
+ if !self.description.is_empty() {
+ self.state = TaskState::Idle {
+ edit_button: button::State::new(),
+ }
+ }
+ }
+ TaskMessage::Delete => {}
+ }
+ }
+
+ fn view(&mut self) -> Element<TaskMessage> {
+ match &mut self.state {
+ TaskState::Idle { edit_button } => {
+ let checkbox = Checkbox::new(
+ self.completed,
+ &self.description,
+ TaskMessage::Completed,
+ );
+
+ Row::new()
+ .spacing(20)
+ .align_items(Align::Center)
+ .push(checkbox)
+ .push(
+ Button::new(
+ edit_button,
+ edit_icon().color([0.5, 0.5, 0.5]),
+ )
+ .on_press(TaskMessage::Edit)
+ .padding(10),
+ )
+ .into()
+ }
+ TaskState::Editing {
+ text_input,
+ delete_button,
+ } => {
+ let text_input = TextInput::new(
+ text_input,
+ "Describe your task...",
+ &self.description,
+ TaskMessage::DescriptionEdited,
+ )
+ .on_submit(TaskMessage::FinishEdition)
+ .padding(10);
+
+ Row::new()
+ .spacing(20)
+ .align_items(Align::Center)
+ .push(text_input)
+ .push(
+ Button::new(
+ delete_button,
+ Row::new()
+ .spacing(10)
+ .push(delete_icon().color(Color::WHITE))
+ .push(
+ Text::new("Delete")
+ .width(Length::Shrink)
+ .color(Color::WHITE),
+ ),
+ )
+ .on_press(TaskMessage::Delete)
+ .padding(10)
+ .border_radius(5)
+ .background(Background::Color([0.8, 0.2, 0.2].into())),
+ )
+ .into()
+ }
+ }
}
}
@@ -117,3 +241,25 @@ const GRAY: Color = Color {
b: 0.5,
a: 1.0,
};
+
+// Fonts
+const ICONS: Font = Font::External {
+ name: "Icons",
+ bytes: include_bytes!("./resources/icons.ttf"),
+};
+
+fn icon(unicode: char) -> Text {
+ Text::new(&unicode.to_string())
+ .font(ICONS)
+ .width(Length::Units(20))
+ .horizontal_alignment(HorizontalAlignment::Center)
+ .size(20)
+}
+
+fn edit_icon() -> Text {
+ icon('\u{F303}')
+}
+
+fn delete_icon() -> Text {
+ icon('\u{F1F8}')
+}
diff --git a/examples/tour.rs b/examples/tour.rs
index 3fd031b8..34ad0a34 100644
--- a/examples/tour.rs
+++ b/examples/tour.rs
@@ -671,6 +671,7 @@ fn button<'a, Message>(
)
.padding(12)
.border_radius(12)
+ .min_width(100)
}
fn primary_button<'a, Message>(
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index a7040e02..3f0f8dda 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -2,7 +2,7 @@
use std::hash::Hash;
use crate::input::{mouse, ButtonState};
-use crate::{layout, Element, Event, Hasher, Layout, Point, Widget};
+use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
pub use iced_core::Checkbox;
@@ -10,6 +10,10 @@ impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message>
where
Renderer: self::Renderer,
{
+ fn width(&self) -> Length {
+ Length::Fill
+ }
+
fn layout(
&self,
renderer: &Renderer,
diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs
index c616db2a..fd7a5ca5 100644
--- a/native/src/widget/container.rs
+++ b/native/src/widget/container.rs
@@ -27,7 +27,7 @@ where
.width(self.width)
.height(self.height);
- let mut content = self.content.layout(renderer, &limits);
+ let mut content = self.content.layout(renderer, &limits.loose());
let size = limits.resolve(content.size());
content.align(self.horizontal_alignment, self.vertical_alignment, size);
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 7e81e257..35e10000 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -11,6 +11,10 @@ where
Renderer: self::Renderer,
Message: Clone + std::fmt::Debug,
{
+ fn width(&self) -> Length {
+ self.width
+ }
+
fn layout(
&self,
renderer: &Renderer,
@@ -46,6 +50,10 @@ where
}) => {
self.state.is_focused =
layout.bounds().contains(cursor_position);
+
+ if self.state.cursor_position(&self.value) == 0 {
+ self.state.move_cursor_to_end(&self.value);
+ }
}
Event::Keyboard(keyboard::Event::CharacterReceived(c))
if self.state.is_focused && !c.is_control() =>
diff --git a/wgpu/src/renderer/widget/button.rs b/wgpu/src/renderer/widget/button.rs
index 3d5e42ba..a19c7d86 100644
--- a/wgpu/src/renderer/widget/button.rs
+++ b/wgpu/src/renderer/widget/button.rs
@@ -12,7 +12,7 @@ impl button::Renderer for Renderer {
) -> layout::Node {
let padding = f32::from(button.padding);
let limits = limits
- .min_width(100)
+ .min_width(button.min_width)
.width(button.width)
.height(Length::Shrink)
.pad(padding);
@@ -56,28 +56,29 @@ impl button::Renderer for Renderer {
};
(
- Primitive::Group {
- primitives: vec![
- Primitive::Quad {
- bounds: Rectangle {
- x: bounds.x + 1.0,
- y: bounds.y + shadow_offset,
- ..bounds
+ match button.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: button.border_radius,
},
- background: Background::Color(
- [0.0, 0.0, 0.0, 0.5].into(),
- ),
- border_radius: button.border_radius,
- },
- Primitive::Quad {
- bounds,
- background: button.background.unwrap_or(
- Background::Color([0.8, 0.8, 0.8].into()),
- ),
- border_radius: button.border_radius,
- },
- content,
- ],
+ Primitive::Quad {
+ bounds,
+ background,
+ border_radius: button.border_radius,
+ },
+ content,
+ ],
+ },
},
if is_mouse_over {
MouseCursor::Pointer