//! Allow your users to perform actions by pressing a button. use crate::overlay; use crate::widget::tree::{self, Tree}; use crate::{Element, Widget}; use iced_native::event::{self, Event}; use iced_native::layout; use iced_native::mouse; use iced_native::renderer; use iced_native::widget::button; use iced_native::{ Clipboard, Layout, Length, Padding, Point, Rectangle, Shell, }; pub use iced_style::button::{Appearance, StyleSheet}; use button::State; /// A generic widget that produces a message when pressed. /// /// ``` /// # type Button<'a, Message> = /// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; /// # /// #[derive(Clone)] /// enum Message { /// ButtonPressed, /// } /// /// let button = Button::new("Press me!").on_press(Message::ButtonPressed); /// ``` /// /// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will /// be disabled: /// /// ``` /// # type Button<'a, Message> = /// # iced_pure::widget::Button<'a, Message, iced_native::renderer::Null>; /// # /// #[derive(Clone)] /// enum Message { /// ButtonPressed, /// } /// /// fn disabled_button<'a>() -> Button<'a, Message> { /// Button::new("I'm disabled!") /// } /// /// fn enabled_button<'a>() -> Button<'a, Message> { /// disabled_button().on_press(Message::ButtonPressed) /// } /// ``` pub struct Button<'a, Message, Renderer> where Renderer: iced_native::Renderer, Renderer::Theme: StyleSheet, { content: Element<'a, Message, Renderer>, on_press: Option, width: Length, height: Length, padding: Padding, style: ::Style, } impl<'a, Message, Renderer> Button<'a, Message, Renderer> where Renderer: iced_native::Renderer, Renderer::Theme: StyleSheet, { /// Creates a new [`Button`] with the given content. pub fn new(content: impl Into>) -> Self { Button { content: content.into(), on_press: None, width: Length::Shrink, height: Length::Shrink, padding: Padding::new(5), style: ::Style::default(), } } /// Sets the width of the [`Button`]. pub fn width(mut self, width: Length) -> Self { self.width = width; self } /// Sets the height of the [`Button`]. pub fn height(mut self, height: Length) -> Self { self.height = height; self } /// Sets the [`Padding`] of the [`Button`]. pub fn padding>(mut self, padding: P) -> Self { self.padding = padding.into(); self } /// Sets the message that will be produced when the [`Button`] is pressed. /// /// Unless `on_press` is called, the [`Button`] will be disabled. pub fn on_press(mut self, msg: Message) -> Self { self.on_press = Some(msg); self } /// Sets the style variant of this [`Button`]. pub fn style( mut self, style: ::Style, ) -> Self { self.style = style; self } } impl<'a, Message, Renderer> Widget for Button<'a, Message, Renderer> where Message: 'a + Clone, Renderer: 'a + iced_native::Renderer, Renderer::Theme: StyleSheet, { fn tag(&self) -> tree::Tag { tree::Tag::of::() } fn state(&self) -> tree::State { tree::State::new(State::new()) } fn children(&self) -> Vec { vec![Tree::new(&self.content)] } fn diff(&self, tree: &mut Tree) { tree.diff_children(std::slice::from_ref(&self.content)) } fn width(&self) -> Length { self.width } fn height(&self) -> Length { self.height } fn layout( &self, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { button::layout( renderer, limits, self.width, self.height, self.padding, |renderer, limits| { self.content.as_widget().layout(renderer, limits) }, ) } fn on_event( &mut self, tree: &mut Tree, event: Event, layout: Layout<'_>, cursor_position: Point, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { if let event::Status::Captured = self.content.as_widget_mut().on_event( &mut tree.children[0], event.clone(), layout.children().next().unwrap(), cursor_position, renderer, clipboard, shell, ) { return event::Status::Captured; } button::update( event, layout, cursor_position, shell, &self.on_press, || tree.state.downcast_mut::(), ) } fn draw( &self, tree: &Tree, renderer: &mut Renderer, theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); let styling = button::draw( renderer, bounds, cursor_position, self.on_press.is_some(), theme, self.style, || tree.state.downcast_ref::(), ); self.content.as_widget().draw( &tree.children[0], renderer, theme, &renderer::Style { text_color: styling.text_color, }, content_layout, cursor_position, &bounds, ); } fn mouse_interaction( &self, _tree: &Tree, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { button::mouse_interaction( layout, cursor_position, self.on_press.is_some(), ) } fn overlay<'b>( &'b self, tree: &'b mut Tree, layout: Layout<'_>, renderer: &Renderer, ) -> Option> { self.content.as_widget().overlay( &mut tree.children[0], layout.children().next().unwrap(), renderer, ) } } impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: Clone + 'a, Renderer: iced_native::Renderer + 'a, Renderer::Theme: StyleSheet, { fn from(button: Button<'a, Message, Renderer>) -> Self { Self::new(button) } }