diff options
author | 2022-02-11 23:17:07 +0700 | |
---|---|---|
committer | 2022-02-11 23:17:07 +0700 | |
commit | ecb3df8e018930c407e469ce2b8f4208a9d15426 (patch) | |
tree | 9370945e6c5092e2730ec6835fa6c0556622ca9d /native/src/widget/button.rs | |
parent | 01c5004959c9b11f2580840f4553ad7d706f4564 (diff) | |
download | iced-ecb3df8e018930c407e469ce2b8f4208a9d15426.tar.gz iced-ecb3df8e018930c407e469ce2b8f4208a9d15426.tar.bz2 iced-ecb3df8e018930c407e469ce2b8f4208a9d15426.zip |
Expose reusable `Button` logic
... and reuse it in `iced_pure`!
Diffstat (limited to 'native/src/widget/button.rs')
-rw-r--r-- | native/src/widget/button.rs | 295 |
1 files changed, 174 insertions, 121 deletions
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); } |