refactor: icon styling and headerbar icon styling

Headerbar icons are transparent when their window is not focused, but otherwise share the same style as icons with selection. This updates the icon styles to match figma when selected.
This commit is contained in:
Ashley Wulber 2024-03-11 15:21:48 -04:00 committed by Ashley Wulber
parent 6f6eeec0e7
commit e47684ffdb
11 changed files with 121 additions and 115 deletions

View file

@ -16,12 +16,14 @@ pub type Button<'a, Message> = Builder<'a, Message, Icon>;
pub struct Icon {
handle: Handle,
vertical: bool,
selected: bool,
}
pub fn icon<'a, Message>(handle: impl Into<Handle>) -> Button<'a, Message> {
Button::new(Icon {
handle: handle.into(),
vertical: false,
selected: false,
})
}
@ -124,6 +126,11 @@ impl<'a, Message> Button<'a, Message> {
self
}
pub fn selected(mut self, selected: bool) -> Self {
self.variant.selected = selected;
self
}
pub fn vertical(mut self, vertical: bool) -> Self {
self.variant.vertical = vertical;
self.style = Style::IconVertical;
@ -179,6 +186,7 @@ impl<'a, Message: Clone + 'static> From<Button<'a, Message>> for Element<'a, Mes
.padding(0)
.id(builder.id)
.on_press_maybe(builder.on_press)
.selected(builder.variant.selected)
.style(builder.style);
if builder.tooltip.is_empty() {

View file

@ -68,21 +68,21 @@ pub trait StyleSheet {
type Style: Default;
/// Produces the active [`Appearance`] of a button.
fn active(&self, focused: bool, style: &Self::Style) -> Appearance;
fn active(&self, focused: bool, selected: bool, style: &Self::Style) -> Appearance;
/// Produces the disabled [`Appearance`] of a button.
fn disabled(&self, style: &Self::Style) -> Appearance;
/// [`Appearance`] when the button is the target of a DND operation.
fn drop_target(&self, style: &Self::Style) -> Appearance {
self.hovered(false, style)
self.hovered(false, false, style)
}
/// Produces the hovered [`Appearance`] of a button.
fn hovered(&self, focused: bool, style: &Self::Style) -> Appearance;
fn hovered(&self, focused: bool, selected: bool, style: &Self::Style) -> Appearance;
/// Produces the pressed [`Appearance`] of a button.
fn pressed(&self, focused: bool, style: &Self::Style) -> Appearance;
fn pressed(&self, focused: bool, selected: bool, style: &Self::Style) -> Appearance;
/// Background color of the selection indicator
fn selection_background(&self) -> Background;

View file

@ -528,10 +528,10 @@ where
}
if self.on_press.is_none() {
node.set_disabled()
node.set_disabled();
}
if is_hovered {
node.set_hovered()
node.set_hovered();
}
node.set_default_action_verb(DefaultActionVerb::Click);
@ -696,12 +696,12 @@ where
style_sheet.disabled(style)
} else if is_mouse_over {
if state.is_pressed {
style_sheet.pressed(state.is_focused || is_selected, style)
style_sheet.pressed(state.is_focused, is_selected, style)
} else {
style_sheet.hovered(state.is_focused || is_selected, style)
style_sheet.hovered(state.is_focused, is_selected, style)
}
} else {
style_sheet.active(state.is_focused || is_selected, style)
style_sheet.active(state.is_focused, is_selected, style)
};
let doubled_border_width = styling.border_width * 2.0;

View file

@ -799,7 +799,7 @@ pub fn color_button<'a, Message: 'static>(
} else {
(0.0, Color::TRANSPARENT)
};
let standard = theme.active(focused, &Button::Standard);
let standard = theme.active(focused, false, &Button::Standard);
button::Appearance {
shadow_offset: Vector::default(),
background: color.map(Background::from).or(standard.background),
@ -837,7 +837,7 @@ pub fn color_button<'a, Message: 'static>(
(0.0, Color::TRANSPARENT)
};
let standard = theme.hovered(focused, &Button::Standard);
let standard = theme.hovered(focused, false, &Button::Standard);
button::Appearance {
shadow_offset: Vector::default(),
background: color.map(Background::from).or(standard.background),
@ -859,7 +859,7 @@ pub fn color_button<'a, Message: 'static>(
(0.0, Color::TRANSPARENT)
};
let standard = theme.pressed(focused, &Button::Standard);
let standard = theme.pressed(focused, false, &Button::Standard);
button::Appearance {
shadow_offset: Vector::default(),
background: color.map(Background::from).or(standard.background),

View file

@ -5,7 +5,7 @@ use crate::{ext::CollectionWidget, widget, Element};
use apply::Apply;
use derive_setters::Setters;
use iced::{window, Length};
use iced_core::{renderer::Quad, widget::tree, Background, Renderer, Widget};
use iced_core::{widget::tree, Widget};
use std::borrow::Cow;
#[must_use]
@ -20,7 +20,7 @@ pub fn header_bar<'a, Message>() -> HeaderBar<'a, Message> {
start: Vec::new(),
center: Vec::new(),
end: Vec::new(),
window_id: None,
focused: false,
}
}
@ -50,9 +50,8 @@ pub struct HeaderBar<'a, Message> {
#[setters(strip_option)]
on_right_click: Option<Message>,
/// The window id for the headerbar.
#[setters(strip_option)]
window_id: Option<iced::window::Id>,
/// Focused state of the window
focused: bool,
/// Elements packed at the start of the headerbar.
#[setters(skip)]
@ -100,7 +99,6 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> {
#[must_use]
pub fn build(self) -> HeaderBarWidget<'a, Message> {
HeaderBarWidget {
window_id: self.window_id,
header_bar_inner: self.into_element(),
}
}
@ -108,7 +106,6 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> {
pub struct HeaderBarWidget<'a, Message> {
header_bar_inner: Element<'a, Message>,
window_id: Option<iced::window::Id>,
}
impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
@ -122,23 +119,6 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
self.header_bar_inner.as_widget().size()
}
fn tag(&self) -> tree::Tag {
tree::Tag::of::<MyState>()
}
fn diff(&mut self, tree: &mut tree::Tree) {
tree.diff_children(&mut [&mut self.header_bar_inner]);
let prev = tree.state.downcast_mut::<MyState>();
if prev.window_id != self.window_id {
*prev = MyState::new(self.window_id);
}
}
fn state(&self) -> tree::State {
let state = MyState::new(self.window_id);
tree::State::new(state)
}
fn layout(
&self,
tree: &mut tree::Tree,
@ -174,28 +154,6 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
cursor,
viewport,
);
let state = tree.state.downcast_ref::<MyState>();
if !state.window_has_focus {
let header_bar_appearance =
<crate::Theme as crate::iced_style::container::StyleSheet>::appearance(
theme,
&crate::theme::Container::HeaderBar,
);
let cosmic = theme.cosmic();
let mut neutral_0 = cosmic.palette.neutral_0;
neutral_0.alpha = 0.3;
// draw overlay rectangle
renderer.fill_quad(
Quad {
bounds: layout.bounds(),
border: header_bar_appearance.border,
shadow: header_bar_appearance.shadow,
},
Background::Color(neutral_0.into()),
);
}
}
fn on_event(
@ -209,14 +167,6 @@ impl<'a, Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer
shell: &mut iced_core::Shell<'_, Message>,
viewport: &iced_core::Rectangle,
) -> iced_core::event::Status {
if let iced_core::Event::Window(id, iced_core::window::Event::Focused) = event {
let state = state.state.downcast_mut::<MyState>();
state.focus_window(id);
} else if let iced_core::Event::Window(id, iced_core::window::Event::Unfocused) = event {
let state = state.state.downcast_mut::<MyState>();
state.unfocus_window(id);
}
let child_state = &mut state.children[0];
let child_layout = layout.children().next().unwrap();
self.header_bar_inner.as_widget_mut().on_event(
@ -370,6 +320,8 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> {
widget::icon::from_svg_bytes(icon_bytes)
.symbolic(true)
.apply(widget::button::icon)
.style(crate::theme::Button::HeaderBar)
.selected(self.focused)
.icon_size(size)
.on_press(on_press)
};
@ -415,31 +367,3 @@ impl<'a, Message: Clone + 'static> From<HeaderBarWidget<'a, Message>> for Elemen
Element::new(headerbar)
}
}
pub struct MyState {
pub window_id: Option<window::Id>,
pub window_has_focus: bool,
}
impl MyState {
pub fn new(id: Option<window::Id>) -> Self {
Self {
window_id: id,
window_has_focus: id.is_none(),
}
}
pub fn focus_window(&mut self, id: window::Id) {
if self.window_id != Some(id) {
return;
}
self.window_has_focus = true;
}
pub fn unfocus_window(&mut self, id: window::Id) {
if self.window_id != Some(id) {
return;
}
self.window_has_focus = false;
}
}