summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/stopwatch/src/main.rs2
-rw-r--r--examples/tour/src/main.rs2
-rw-r--r--native/src/widget/button.rs295
-rw-r--r--pure/src/widget/button.rs162
-rw-r--r--pure/src/widget/tree.rs14
5 files changed, 228 insertions, 247 deletions
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs
index dc8a4de7..377d7a2d 100644
--- a/examples/stopwatch/src/main.rs
+++ b/examples/stopwatch/src/main.rs
@@ -105,8 +105,8 @@ impl Application for Stopwatch {
Text::new(label)
.horizontal_alignment(alignment::Horizontal::Center),
)
- .min_width(80)
.padding(10)
+ .width(Length::Units(80))
.style(style)
};
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index d4b41310..b01a1ce2 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -726,7 +726,7 @@ fn button<'a, Message: Clone>(
Text::new(label).horizontal_alignment(alignment::Horizontal::Center),
)
.padding(12)
- .min_width(100)
+ .width(Length::Units(100))
}
fn color_slider(
diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs
index b4a3adc3..049b6544 100644
--- a/native/src/widget/button.rs
+++ b/native/src/widget/button.rs
@@ -63,8 +63,6 @@ pub struct Button<'a, Message, Renderer> {
on_press: Option<Message>,
width: Length,
height: Length,
- min_width: u32,
- min_height: u32,
padding: Padding,
style_sheet: Box<dyn StyleSheet + 'a>,
}
@@ -86,8 +84,6 @@ where
on_press: None,
width: Length::Shrink,
height: Length::Shrink,
- min_width: 0,
- min_height: 0,
padding: Padding::new(5),
style_sheet: Default::default(),
}
@@ -105,18 +101,6 @@ where
self
}
- /// Sets the minimum width of the [`Button`].
- pub fn min_width(mut self, min_width: u32) -> Self {
- self.min_width = min_width;
- self
- }
-
- /// Sets the minimum height of the [`Button`].
- pub fn min_height(mut self, min_height: u32) -> Self {
- self.min_height = min_height;
- self
- }
-
/// Sets the [`Padding`] of the [`Button`].
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
@@ -153,6 +137,153 @@ impl State {
}
}
+/// Processes the given [`Event`] and updates the [`State`] of a [`Button`]
+/// accordingly.
+pub fn update<'a, Message: Clone>(
+ event: Event,
+ layout: Layout<'_>,
+ cursor_position: Point,
+ shell: &mut Shell<'_, Message>,
+ on_press: &Option<Message>,
+ state: impl FnOnce() -> &'a mut State,
+) -> event::Status {
+ match event {
+ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerPressed { .. }) => {
+ if on_press.is_some() {
+ let bounds = layout.bounds();
+
+ if bounds.contains(cursor_position) {
+ let state = state();
+
+ state.is_pressed = true;
+
+ return event::Status::Captured;
+ }
+ }
+ }
+ Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
+ | Event::Touch(touch::Event::FingerLifted { .. }) => {
+ if let Some(on_press) = on_press.clone() {
+ let state = state();
+
+ if state.is_pressed {
+ state.is_pressed = false;
+
+ let bounds = layout.bounds();
+
+ if bounds.contains(cursor_position) {
+ shell.publish(on_press);
+ }
+
+ return event::Status::Captured;
+ }
+ }
+ }
+ Event::Touch(touch::Event::FingerLost { .. }) => {
+ let state = state();
+
+ state.is_pressed = false;
+ }
+ _ => {}
+ }
+
+ event::Status::Ignored
+}
+
+/// Draws a [`Button`].
+pub fn draw<'a, Renderer: crate::Renderer>(
+ renderer: &mut Renderer,
+ bounds: Rectangle,
+ cursor_position: Point,
+ is_enabled: bool,
+ style_sheet: &dyn StyleSheet,
+ state: impl FnOnce() -> &'a State,
+) -> Style {
+ let is_mouse_over = bounds.contains(cursor_position);
+
+ let styling = if !is_enabled {
+ style_sheet.disabled()
+ } else if is_mouse_over {
+ let state = state();
+
+ if state.is_pressed {
+ style_sheet.pressed()
+ } else {
+ style_sheet.hovered()
+ }
+ } else {
+ style_sheet.active()
+ };
+
+ if styling.background.is_some() || styling.border_width > 0.0 {
+ if styling.shadow_offset != Vector::default() {
+ // TODO: Implement proper shadow support
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: Rectangle {
+ x: bounds.x + styling.shadow_offset.x,
+ y: bounds.y + styling.shadow_offset.y,
+ ..bounds
+ },
+ border_radius: styling.border_radius,
+ border_width: 0.0,
+ border_color: Color::TRANSPARENT,
+ },
+ Background::Color([0.0, 0.0, 0.0, 0.5].into()),
+ );
+ }
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds,
+ border_radius: styling.border_radius,
+ border_width: styling.border_width,
+ border_color: styling.border_color,
+ },
+ styling
+ .background
+ .unwrap_or(Background::Color(Color::TRANSPARENT)),
+ );
+ }
+
+ styling
+}
+
+/// Computes the layout of a [`Button`].
+pub fn layout<Renderer>(
+ renderer: &Renderer,
+ limits: &layout::Limits,
+ width: Length,
+ height: Length,
+ padding: Padding,
+ layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
+) -> layout::Node {
+ let limits = limits.width(width).height(height).pad(padding);
+
+ let mut content = layout_content(renderer, &limits);
+ content.move_to(Point::new(padding.left.into(), padding.top.into()));
+
+ let size = limits.resolve(content.size()).pad(padding);
+
+ layout::Node::with_children(size, vec![content])
+}
+
+/// Returns the [`mouse::Interaction`] of a [`Button`].
+pub fn mouse_interaction(
+ layout: Layout<'_>,
+ cursor_position: Point,
+ is_enabled: bool,
+) -> mouse::Interaction {
+ let is_mouse_over = layout.bounds().contains(cursor_position);
+
+ if is_mouse_over && is_enabled {
+ mouse::Interaction::Pointer
+ } else {
+ mouse::Interaction::default()
+ }
+}
+
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Button<'a, Message, Renderer>
where
@@ -172,22 +303,14 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits
- .min_width(self.min_width)
- .min_height(self.min_height)
- .width(self.width)
- .height(self.height)
- .pad(self.padding);
-
- let mut content = self.content.layout(renderer, &limits);
- content.move_to(Point::new(
- self.padding.left.into(),
- self.padding.top.into(),
- ));
-
- let size = limits.resolve(content.size()).pad(self.padding);
-
- layout::Node::with_children(size, vec![content])
+ layout(
+ renderer,
+ limits,
+ self.width,
+ self.height,
+ self.padding,
+ |renderer, limits| self.content.layout(renderer, limits),
+ )
}
fn on_event(
@@ -210,42 +333,14 @@ where
return event::Status::Captured;
}
- match event {
- Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerPressed { .. }) => {
- if self.on_press.is_some() {
- let bounds = layout.bounds();
-
- if bounds.contains(cursor_position) {
- self.state.is_pressed = true;
-
- return event::Status::Captured;
- }
- }
- }
- Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerLifted { .. }) => {
- if let Some(on_press) = self.on_press.clone() {
- let bounds = layout.bounds();
-
- if self.state.is_pressed {
- self.state.is_pressed = false;
-
- if bounds.contains(cursor_position) {
- shell.publish(on_press);
- }
-
- return event::Status::Captured;
- }
- }
- }
- Event::Touch(touch::Event::FingerLost { .. }) => {
- self.state.is_pressed = false;
- }
- _ => {}
- }
-
- event::Status::Ignored
+ update(
+ event,
+ layout,
+ cursor_position,
+ shell,
+ &self.on_press,
+ || &mut self.state,
+ )
}
fn mouse_interaction(
@@ -255,14 +350,7 @@ where
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
- let is_mouse_over = layout.bounds().contains(cursor_position);
- let is_disabled = self.on_press.is_none();
-
- if is_mouse_over && !is_disabled {
- mouse::Interaction::Pointer
- } else {
- mouse::Interaction::default()
- }
+ mouse_interaction(layout, cursor_position, self.on_press.is_some())
}
fn draw(
@@ -276,51 +364,14 @@ where
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
- let is_mouse_over = bounds.contains(cursor_position);
- let is_disabled = self.on_press.is_none();
-
- let styling = if is_disabled {
- self.style_sheet.disabled()
- } else if is_mouse_over {
- if self.state.is_pressed {
- self.style_sheet.pressed()
- } else {
- self.style_sheet.hovered()
- }
- } else {
- self.style_sheet.active()
- };
-
- if styling.background.is_some() || styling.border_width > 0.0 {
- if styling.shadow_offset != Vector::default() {
- // TODO: Implement proper shadow support
- renderer.fill_quad(
- renderer::Quad {
- bounds: Rectangle {
- x: bounds.x + styling.shadow_offset.x,
- y: bounds.y + styling.shadow_offset.y,
- ..bounds
- },
- border_radius: styling.border_radius,
- border_width: 0.0,
- border_color: Color::TRANSPARENT,
- },
- Background::Color([0.0, 0.0, 0.0, 0.5].into()),
- );
- }
-
- renderer.fill_quad(
- renderer::Quad {
- bounds,
- border_radius: styling.border_radius,
- border_width: styling.border_width,
- border_color: styling.border_color,
- },
- styling
- .background
- .unwrap_or(Background::Color(Color::TRANSPARENT)),
- );
- }
+ let styling = draw(
+ renderer,
+ bounds,
+ cursor_position,
+ self.on_press.is_some(),
+ self.style_sheet.as_ref(),
+ || &self.state,
+ );
self.content.draw(
renderer,
@@ -338,6 +389,8 @@ where
std::any::TypeId::of::<Marker>().hash(state);
self.width.hash(state);
+ self.height.hash(state);
+ self.padding.hash(state);
self.content.hash_layout(state);
}
diff --git a/pure/src/widget/button.rs b/pure/src/widget/button.rs
index 198a3af9..89acb7f5 100644
--- a/pure/src/widget/button.rs
+++ b/pure/src/widget/button.rs
@@ -4,15 +4,16 @@ use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::renderer;
-use iced_native::touch;
+use iced_native::widget::button;
use iced_native::{
- Background, Clipboard, Color, Hasher, Layout, Length, Padding, Point,
- Rectangle, Shell, Vector,
+ Clipboard, Hasher, Layout, Length, Padding, Point, Rectangle, Shell,
};
use iced_style::button::StyleSheet;
use std::any::Any;
+pub use button::State;
+
pub struct Button<'a, Message, Renderer> {
content: Element<'a, Message, Renderer>,
on_press: Option<Message>,
@@ -51,7 +52,7 @@ where
}
fn state(&self) -> Box<dyn Any> {
- Box::new(State { is_pressed: false })
+ Box::new(State::new())
}
fn children(&self) -> &[Element<Message, Renderer>] {
@@ -71,6 +72,8 @@ where
self.tag().hash(state);
self.width.hash(state);
+ self.height.hash(state);
+ self.padding.hash(state);
self.content.as_widget().hash_layout(state);
}
@@ -79,20 +82,16 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits
- .width(self.width)
- .height(self.height)
- .pad(self.padding);
-
- let mut content = self.content.as_widget().layout(renderer, &limits);
- content.move_to(Point::new(
- self.padding.left.into(),
- self.padding.top.into(),
- ));
-
- let size = limits.resolve(content.size()).pad(self.padding);
-
- layout::Node::with_children(size, vec![content])
+ button::layout(
+ renderer,
+ limits,
+ self.width,
+ self.height,
+ self.padding,
+ |renderer, limits| {
+ self.content.as_widget().layout(renderer, &limits)
+ },
+ )
}
fn on_event(
@@ -105,12 +104,6 @@ where
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
- let state = if let Some(state) = tree.state.downcast_mut::<State>() {
- state
- } else {
- return event::Status::Ignored;
- };
-
if let event::Status::Captured = self.content.as_widget_mut().on_event(
&mut tree.children[0],
event.clone(),
@@ -123,42 +116,14 @@ where
return event::Status::Captured;
}
- match event {
- Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerPressed { .. }) => {
- if self.on_press.is_some() {
- let bounds = layout.bounds();
-
- if bounds.contains(cursor_position) {
- state.is_pressed = true;
-
- return event::Status::Captured;
- }
- }
- }
- Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
- | Event::Touch(touch::Event::FingerLifted { .. }) => {
- if let Some(on_press) = self.on_press.clone() {
- let bounds = layout.bounds();
-
- if state.is_pressed {
- state.is_pressed = false;
-
- if bounds.contains(cursor_position) {
- shell.publish(on_press);
- }
-
- return event::Status::Captured;
- }
- }
- }
- Event::Touch(touch::Event::FingerLost { .. }) => {
- state.is_pressed = false;
- }
- _ => {}
- }
-
- event::Status::Ignored
+ button::update(
+ event,
+ layout,
+ cursor_position,
+ shell,
+ &self.on_press,
+ || tree.state_mut::<State>(),
+ )
}
fn draw(
@@ -170,60 +135,17 @@ where
cursor_position: Point,
_viewport: &Rectangle,
) {
- let state = if let Some(state) = tree.state.downcast_ref::<State>() {
- state
- } else {
- return;
- };
-
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
- let is_mouse_over = bounds.contains(cursor_position);
- let is_disabled = self.on_press.is_none();
-
- let styling = if is_disabled {
- self.style_sheet.disabled()
- } else if is_mouse_over {
- if state.is_pressed {
- self.style_sheet.pressed()
- } else {
- self.style_sheet.hovered()
- }
- } else {
- self.style_sheet.active()
- };
-
- if styling.background.is_some() || styling.border_width > 0.0 {
- if styling.shadow_offset != Vector::default() {
- // TODO: Implement proper shadow support
- renderer.fill_quad(
- renderer::Quad {
- bounds: Rectangle {
- x: bounds.x + styling.shadow_offset.x,
- y: bounds.y + styling.shadow_offset.y,
- ..bounds
- },
- border_radius: styling.border_radius,
- border_width: 0.0,
- border_color: Color::TRANSPARENT,
- },
- Background::Color([0.0, 0.0, 0.0, 0.5].into()),
- );
- }
-
- renderer.fill_quad(
- renderer::Quad {
- bounds,
- border_radius: styling.border_radius,
- border_width: styling.border_width,
- border_color: styling.border_color,
- },
- styling
- .background
- .unwrap_or(Background::Color(Color::TRANSPARENT)),
- );
- }
+ let styling = button::draw(
+ renderer,
+ bounds,
+ cursor_position,
+ self.on_press.is_some(),
+ self.style_sheet.as_ref(),
+ || tree.state::<State>(),
+ );
self.content.as_widget().draw(
&tree.children[0],
@@ -245,22 +167,14 @@ where
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
- let is_mouse_over = layout.bounds().contains(cursor_position);
- let is_disabled = self.on_press.is_none();
-
- if is_mouse_over && !is_disabled {
- mouse::Interaction::Pointer
- } else {
- mouse::Interaction::default()
- }
+ button::mouse_interaction(
+ layout,
+ cursor_position,
+ self.on_press.is_some(),
+ )
}
}
-#[derive(Debug, Clone)]
-struct State {
- is_pressed: bool,
-}
-
impl<'a, Message, Renderer> Into<Element<'a, Message, Renderer>>
for Button<'a, Message, Renderer>
where
diff --git a/pure/src/widget/tree.rs b/pure/src/widget/tree.rs
index 2353edc5..1ab6d80b 100644
--- a/pure/src/widget/tree.rs
+++ b/pure/src/widget/tree.rs
@@ -58,4 +58,18 @@ impl Tree {
*self = Self::new(new);
}
}
+
+ pub fn state<T>(&self) -> &T
+ where
+ T: 'static,
+ {
+ self.state.downcast_ref().expect("Downcast widget state")
+ }
+
+ pub fn state_mut<T>(&mut self) -> &mut T
+ where
+ T: 'static,
+ {
+ self.state.downcast_mut().expect("Downcast widget state")
+ }
}