From ecb3df8e018930c407e469ce2b8f4208a9d15426 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 11 Feb 2022 23:17:07 +0700 Subject: Expose reusable `Button` logic ... and reuse it in `iced_pure`! --- pure/src/widget/button.rs | 162 +++++++++++----------------------------------- 1 file changed, 38 insertions(+), 124 deletions(-) (limited to 'pure/src/widget/button.rs') 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, @@ -51,7 +52,7 @@ where } fn state(&self) -> Box { - Box::new(State { is_pressed: false }) + Box::new(State::new()) } fn children(&self) -> &[Element] { @@ -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 - } 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::(), + ) } fn draw( @@ -170,60 +135,17 @@ where cursor_position: Point, _viewport: &Rectangle, ) { - let state = if let Some(state) = tree.state.downcast_ref::() { - 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::(), + ); 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> for Button<'a, Message, Renderer> where -- cgit