diff --git a/src/applet/mod.rs b/src/applet/mod.rs index fcd04d8c..5b7e72d8 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -152,7 +152,7 @@ impl Context { pub fn icon_button_from_handle<'a, Message: 'static>( &self, icon: widget::icon::Handle, - ) -> crate::widget::Button<'a, Message, crate::Theme, Renderer> { + ) -> crate::widget::Button<'a, Message> { let symbolic = icon.symbolic; let suggested = self.suggested_size(symbolic); let applet_padding = self.suggested_padding(symbolic); @@ -176,7 +176,7 @@ impl Context { pub fn icon_button<'a, Message: 'static>( &self, icon_name: &'a str, - ) -> crate::widget::Button<'a, Message, crate::Theme, Renderer> { + ) -> crate::widget::Button<'a, Message> { self.icon_button_from_handle( widget::icon::from_name(icon_name) .symbolic(true) @@ -371,7 +371,7 @@ pub fn style() -> ::Style { pub fn menu_button<'a, Message>( content: impl Into>, -) -> crate::widget::Button<'a, Message, crate::Theme, crate::Renderer> { +) -> crate::widget::Button<'a, Message> { crate::widget::Button::new(content) .style(Button::AppletMenu) .padding(menu_control_padding()) diff --git a/src/widget/button/link.rs b/src/widget/button/link.rs index 6843269c..d1d43e90 100644 --- a/src/widget/button/link.rs +++ b/src/widget/button/link.rs @@ -58,33 +58,32 @@ pub fn icon() -> Handle { impl<'a, Message: Clone + 'static> From> for Element<'a, Message> { fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> { - let button: super::Button<'a, Message, crate::Theme, crate::Renderer> = - row::with_capacity(2) - .push({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; + let button: super::Button<'a, Message> = row::with_capacity(2) + .push({ + let mut font = crate::font::DEFAULT; + font.weight = builder.font_weight; - // TODO: Avoid allocation - crate::widget::text(builder.label.to_string()) - .size(builder.font_size) - .line_height(LineHeight::Absolute(builder.line_height.into())) - .font(font) - }) - .push_maybe(if builder.variant.trailing_icon { - Some(icon().icon().size(builder.icon_size)) - } else { - None - }) - .padding(builder.padding) - .width(builder.width) - .height(builder.height) - .spacing(builder.spacing) - .align_items(Alignment::Center) - .apply(button) - .padding(0) - .id(builder.id) - .on_press_maybe(builder.on_press.take()) - .style(builder.style); + // TODO: Avoid allocation + crate::widget::text(builder.label.to_string()) + .size(builder.font_size) + .line_height(LineHeight::Absolute(builder.line_height.into())) + .font(font) + }) + .push_maybe(if builder.variant.trailing_icon { + Some(icon().icon().size(builder.icon_size)) + } else { + None + }) + .padding(builder.padding) + .width(builder.width) + .height(builder.height) + .spacing(builder.spacing) + .align_items(Alignment::Center) + .apply(button) + .padding(0) + .id(builder.id) + .on_press_maybe(builder.on_press.take()) + .style(builder.style); if builder.tooltip.is_empty() { button.into() diff --git a/src/widget/button/mod.rs b/src/widget/button/mod.rs index b0d1afad..9ba5dc2f 100644 --- a/src/widget/button/mod.rs +++ b/src/widget/button/mod.rs @@ -26,28 +26,19 @@ pub use text::{destructive, standard, suggested, text}; mod widget; pub use widget::{draw, focus, layout, mouse_interaction, Button}; -use crate::iced::Element; use iced_core::font::Weight; use iced_core::widget::Id; use iced_core::{Length, Padding}; use std::borrow::Cow; -pub fn button<'a, Message, Theme>( - content: impl Into>, -) -> Button<'a, Message, Theme, crate::Renderer> -where - Theme: style::StyleSheet, -{ +pub fn button<'a, Message>(content: impl Into>) -> Button<'a, Message> { Button::new(content) } -pub fn custom_image_button<'a, Message, Theme>( - content: impl Into>, +pub fn custom_image_button<'a, Message>( + content: impl Into>, on_remove: Option, -) -> Button<'a, Message, Theme, crate::Renderer> -where - Theme: style::StyleSheet, -{ +) -> Button<'a, Message> { Button::new_image(content, on_remove) } diff --git a/src/widget/button/text.rs b/src/widget/button/text.rs index d8f339ff..134e0623 100644 --- a/src/widget/button/text.rs +++ b/src/widget/button/text.rs @@ -109,24 +109,23 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes .into() }); - let button: super::Button<'a, Message, crate::Theme, crate::Renderer> = - row::with_capacity(3) - // Optional icon to place before label. - .push_maybe(leading_icon) - // Optional label between icons. - .push_maybe(label) - // Optional icon to place behind the label. - .push_maybe(trailing_icon) - .padding(builder.padding) - .width(builder.width) - .height(builder.height) - .spacing(builder.spacing) - .align_items(Alignment::Center) - .apply(button) - .padding(0) - .id(builder.id) - .on_press_maybe(builder.on_press.take()) - .style(builder.style); + let button: super::Button<'a, Message> = row::with_capacity(3) + // Optional icon to place before label. + .push_maybe(leading_icon) + // Optional label between icons. + .push_maybe(label) + // Optional icon to place behind the label. + .push_maybe(trailing_icon) + .padding(builder.padding) + .width(builder.width) + .height(builder.height) + .spacing(builder.spacing) + .align_items(Alignment::Center) + .apply(button) + .padding(0) + .id(builder.id) + .on_press_maybe(builder.on_press.take()) + .style(builder.style); if builder.tooltip.is_empty() { button.into() diff --git a/src/widget/button/widget.rs b/src/widget/button/widget.rs index d17dd623..2bea46d1 100644 --- a/src/widget/button/widget.rs +++ b/src/widget/button/widget.rs @@ -10,7 +10,7 @@ use iced_runtime::core::widget::Id; use iced_runtime::{keyboard, Command}; use iced_core::event::{self, Event}; -use iced_core::renderer::{self, Quad}; +use iced_core::renderer::{self, Quad, Renderer}; use iced_core::touch; use iced_core::widget::tree::{self, Tree}; use iced_core::widget::Operation; @@ -18,8 +18,7 @@ use iced_core::{layout, svg}; use iced_core::{mouse, Border}; use iced_core::{overlay, Shadow}; use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle, Shell, - Vector, Widget, + Background, Clipboard, Color, Layout, Length, Padding, Point, Rectangle, Shell, Vector, Widget, }; use iced_renderer::core::widget::{operation, OperationOutputWrapper}; @@ -39,11 +38,7 @@ enum Variant { /// A generic button which emits a message when pressed. #[allow(missing_debug_implementations)] #[must_use] -pub struct Button<'a, Message, Theme, Renderer> -where - Renderer: iced_core::Renderer, - Theme: super::style::StyleSheet, -{ +pub struct Button<'a, Message> { id: Id, #[cfg(feature = "a11y")] name: Option>, @@ -51,23 +46,19 @@ where description: Option>, #[cfg(feature = "a11y")] label: Option>, - content: Element<'a, Message, Theme, Renderer>, + content: crate::Element<'a, Message>, on_press: Option, width: Length, height: Length, padding: Padding, selected: bool, - style: ::Style, + style: crate::theme::Button, variant: Variant, } -impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> -where - Renderer: iced_core::Renderer, - Theme: super::style::StyleSheet, -{ +impl<'a, Message> Button<'a, Message> { /// Creates a new [`Button`] with the given content. - pub fn new(content: impl Into>) -> Self { + pub fn new(content: impl Into>) -> Self { Self { id: Id::unique(), #[cfg(feature = "a11y")] @@ -82,13 +73,13 @@ where height: Length::Shrink, padding: Padding::new(5.0), selected: false, - style: ::Style::default(), + style: crate::theme::Button::default(), variant: Variant::Normal, } } pub fn new_image( - content: impl Into>, + content: impl Into>, on_remove: Option, ) -> Self { Self { @@ -105,7 +96,7 @@ where height: Length::Shrink, padding: Padding::new(5.0), selected: false, - style: ::Style::default(), + style: crate::theme::Button::default(), variant: Variant::Image { on_remove, close_icon: crate::widget::icon::from_name("window-close-symbolic") @@ -171,7 +162,7 @@ where } /// Sets the style variant of this [`Button`]. - pub fn style(mut self, style: ::Style) -> Self { + pub fn style(mut self, style: crate::theme::Button) -> Self { self.style = style; self } @@ -207,12 +198,8 @@ where } } -impl<'a, Message, Theme, Renderer> Widget - for Button<'a, Message, Theme, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_core::Renderer + svg::Renderer, - Theme: super::style::StyleSheet, +impl<'a, Message: 'a + Clone> Widget + for Button<'a, Message> { fn tag(&self) -> tree::Tag { tree::Tag::of::() @@ -237,7 +224,7 @@ where fn layout( &self, tree: &mut Tree, - renderer: &Renderer, + renderer: &crate::Renderer, limits: &layout::Limits, ) -> layout::Node { layout( @@ -258,7 +245,7 @@ where &self, tree: &mut Tree, layout: Layout<'_>, - renderer: &Renderer, + renderer: &crate::Renderer, operation: &mut dyn Operation>, ) { operation.container(None, layout.bounds(), &mut |operation| { @@ -279,7 +266,7 @@ where event: Event, layout: Layout<'_>, cursor: mouse::Cursor, - renderer: &Renderer, + renderer: &crate::Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, @@ -330,11 +317,12 @@ where ) } + #[allow(clippy::too_many_lines)] fn draw( &self, tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, + renderer: &mut crate::Renderer, + theme: &crate::Theme, renderer_style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, @@ -343,31 +331,60 @@ where let bounds = layout.bounds(); let content_layout = layout.children().next().unwrap(); - let styling = draw::<_, Theme>( - renderer, - bounds, - cursor, - self.on_press.is_some(), - self.selected, - theme, - &self.style, - || tree.state.downcast_ref::(), - |renderer, styling| { - self.content.as_widget().draw( - &tree.children[0], - renderer, - theme, - &renderer::Style { - icon_color: styling.icon_color.unwrap_or(renderer_style.icon_color), - text_color: styling.text_color.unwrap_or(renderer_style.icon_color), - scale_factor: renderer_style.scale_factor, - }, - content_layout, - cursor, - &bounds, - ); - }, - ); + let mut headerbar_alpha = None; + + let is_enabled = self.on_press.is_some(); + let is_mouse_over = cursor.position().is_some_and(|p| bounds.contains(p)); + + let state = tree.state.downcast_ref::(); + + let styling = if !is_enabled { + theme.disabled(&self.style) + } else if is_mouse_over { + if state.is_pressed { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.8); + } + + theme.pressed(state.is_focused, self.selected, &self.style) + } else { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.8); + } + + theme.hovered(state.is_focused, self.selected, &self.style) + } + } else { + if !self.selected && matches!(self.style, crate::theme::Button::HeaderBar) { + headerbar_alpha = Some(0.75); + } + + theme.active(state.is_focused, self.selected, &self.style) + }; + + let mut icon_color = styling.icon_color.unwrap_or(renderer_style.icon_color); + let mut text_color = styling.text_color.unwrap_or(renderer_style.text_color); + + if let Some(alpha) = headerbar_alpha { + icon_color.a = alpha; + text_color.a = alpha; + } + + draw::<_, crate::Theme>(renderer, bounds, &styling, |renderer, _styling| { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + &renderer::Style { + icon_color, + text_color, + scale_factor: renderer_style.scale_factor, + }, + content_layout, + cursor, + &bounds, + ); + }); if let Variant::Image { close_icon, @@ -405,7 +422,7 @@ where iced_core::svg::Renderer::draw( renderer, crate::widget::common::object_select().clone(), - styling.icon_color, + Some(icon_color), Rectangle { width: 16.0, height: 16.0, @@ -434,7 +451,7 @@ where iced_core::svg::Renderer::draw( renderer, close_icon.clone(), - styling.icon_color, + Some(icon_color), Rectangle { width: 16.0, height: 16.0, @@ -454,7 +471,7 @@ where layout: Layout<'_>, cursor: mouse::Cursor, _viewport: &Rectangle, - _renderer: &Renderer, + _renderer: &crate::Renderer, ) -> mouse::Interaction { mouse_interaction(layout, cursor, self.on_press.is_some()) } @@ -463,8 +480,8 @@ where &'b mut self, tree: &'b mut Tree, layout: Layout<'_>, - renderer: &Renderer, - ) -> Option> { + renderer: &crate::Renderer, + ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], layout.children().next().unwrap(), @@ -547,14 +564,8 @@ where } } -impl<'a, Message, Theme, Renderer> From> - for Element<'a, Message, Theme, Renderer> -where - Message: Clone + 'a, - Renderer: iced_core::Renderer + svg::Renderer + 'a, - Theme: super::style::StyleSheet + 'a, -{ - fn from(button: Button<'a, Message, Theme, Renderer>) -> Self { +impl<'a, Message: Clone + 'a> From> for crate::Element<'a, Message> { + fn from(button: Button<'a, Message>) -> Self { Self::new(button) } } @@ -674,36 +685,14 @@ pub fn update<'a, Message: Clone>( } #[allow(clippy::too_many_arguments)] -pub fn draw<'a, Renderer: iced_core::Renderer, Theme>( +pub fn draw( renderer: &mut Renderer, bounds: Rectangle, - cursor: mouse::Cursor, - is_enabled: bool, - is_selected: bool, - style_sheet: &dyn StyleSheet