improv(segmented_button): nav bar, tab, and segmented control theme improvements
This commit is contained in:
parent
562e885872
commit
8badf73383
8 changed files with 279 additions and 256 deletions
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use crate::widget::segmented_button::{Appearance, ItemAppearance, StyleSheet};
|
||||
use crate::{theme::Theme, widget::segmented_button::ItemStatusAppearance};
|
||||
use cosmic_theme::{Component, Container};
|
||||
use iced::Border;
|
||||
use iced_core::{Background, border::Radius};
|
||||
use palette::WithAlpha;
|
||||
|
||||
|
|
@ -16,6 +16,8 @@ pub enum SegmentedButton {
|
|||
TabBar,
|
||||
/// A widget for multiple choice selection.
|
||||
Control,
|
||||
/// Navigation bar style
|
||||
NavBar,
|
||||
/// Or implement any custom theme of your liking.
|
||||
Custom(Box<dyn Fn(&Theme) -> Appearance>),
|
||||
}
|
||||
|
|
@ -25,85 +27,54 @@ impl StyleSheet for Theme {
|
|||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn horizontal(&self, style: &Self::Style) -> Appearance {
|
||||
let container = &self.current_container();
|
||||
|
||||
let cosmic = self.cosmic();
|
||||
let container = self.current_container();
|
||||
match style {
|
||||
SegmentedButton::TabBar => {
|
||||
let cosmic = self.cosmic();
|
||||
let active = horizontal::tab_bar_active(cosmic);
|
||||
let hc = cosmic.is_high_contrast;
|
||||
let (border_end, border_start, border_top) = if hc {
|
||||
(
|
||||
Some((1., container.component.border.into())),
|
||||
Some((1., container.component.border.into())),
|
||||
Some((1., container.component.border.into())),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
Appearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
first: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
border_bottom: Some((1.0, cosmic.accent.base.into())),
|
||||
border_end,
|
||||
border_start,
|
||||
border_top,
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
border_bottom: Some((1.0, cosmic.accent.base.into())),
|
||||
border_end,
|
||||
border_start,
|
||||
border_top,
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
border_bottom: Some((1.0, cosmic.accent.base.into())),
|
||||
border_end,
|
||||
border_start,
|
||||
border_top,
|
||||
},
|
||||
text_color: container.component.on.into(),
|
||||
},
|
||||
hover: hover(cosmic, &container.component, &active),
|
||||
focus: focus(cosmic, container, &active),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
SegmentedButton::Control => {
|
||||
let cosmic = self.cosmic();
|
||||
let active = horizontal::selection_active(cosmic, &container.component);
|
||||
let rad_m = cosmic.corner_radii.radius_m;
|
||||
let rad_xl = cosmic.corner_radii.radius_xl;
|
||||
let rad_0 = cosmic.corner_radii.radius_0;
|
||||
let active = horizontal::selection_active(cosmic, &container.component);
|
||||
Appearance {
|
||||
background: Some(Background::Color(container.small_widget.into())),
|
||||
border_radius: rad_m.into(),
|
||||
background: Some(Background::Color(container.component.base.into())),
|
||||
border: Border {
|
||||
radius: rad_xl.into(),
|
||||
..Default::default()
|
||||
},
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
first: ItemAppearance {
|
||||
border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_xl[0], rad_0[1], rad_0[2], rad_xl[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_0[0], rad_xl[1], rad_xl[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
text_color: container.component.on.into(),
|
||||
},
|
||||
hover: hover(cosmic, &container.component, &active),
|
||||
focus: focus(cosmic, container, &active),
|
||||
hover: hover(cosmic, &active, 0.2),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
SegmentedButton::NavBar => Appearance {
|
||||
active_width: 0.0,
|
||||
..horizontal::tab_bar(cosmic, container)
|
||||
},
|
||||
|
||||
SegmentedButton::TabBar => horizontal::tab_bar(cosmic, container),
|
||||
|
||||
SegmentedButton::Custom(func) => func(self),
|
||||
}
|
||||
}
|
||||
|
|
@ -111,84 +82,126 @@ impl StyleSheet for Theme {
|
|||
#[allow(clippy::too_many_lines)]
|
||||
fn vertical(&self, style: &Self::Style) -> Appearance {
|
||||
let cosmic = self.cosmic();
|
||||
let rad_m = cosmic.corner_radii.radius_m;
|
||||
let rad_0 = cosmic.corner_radii.radius_0;
|
||||
let container = self.current_container();
|
||||
match style {
|
||||
SegmentedButton::TabBar => {
|
||||
let container = &self.cosmic().primary;
|
||||
let active = vertical::tab_bar_active(cosmic);
|
||||
Appearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
text_color: container.component.on.into(),
|
||||
..active
|
||||
},
|
||||
hover: hover(cosmic, &container.component, &active),
|
||||
focus: focus(cosmic, container, &active),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
SegmentedButton::Control => {
|
||||
let container = self.current_container();
|
||||
let rad_xl = cosmic.corner_radii.radius_xl;
|
||||
let rad_0 = cosmic.corner_radii.radius_0;
|
||||
let active = vertical::selection_active(cosmic, &container.component);
|
||||
Appearance {
|
||||
background: Some(Background::Color(container.small_widget.into())),
|
||||
border_radius: rad_m.into(),
|
||||
background: Some(Background::Color(container.component.base.into())),
|
||||
border: Border {
|
||||
radius: rad_xl.into(),
|
||||
..Default::default()
|
||||
},
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
first: ItemAppearance {
|
||||
border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[0], rad_0[0]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_xl[0], rad_xl[1], rad_0[0], rad_0[0]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_0[0], rad_0[1], rad_xl[2], rad_xl[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
text_color: container.component.on.into(),
|
||||
},
|
||||
hover: hover(cosmic, &container.component, &active),
|
||||
focus: focus(cosmic, container, &active),
|
||||
hover: hover(cosmic, &active, 0.2),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
SegmentedButton::NavBar => Appearance {
|
||||
active_width: 0.0,
|
||||
..vertical::tab_bar(cosmic, container)
|
||||
},
|
||||
|
||||
SegmentedButton::TabBar => vertical::tab_bar(cosmic, container),
|
||||
|
||||
SegmentedButton::Custom(func) => func(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod horizontal {
|
||||
use super::Appearance;
|
||||
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
|
||||
use cosmic_theme::Component;
|
||||
use cosmic_theme::{Component, Container};
|
||||
use iced::Border;
|
||||
use iced_core::{Background, border::Radius};
|
||||
use palette::WithAlpha;
|
||||
|
||||
pub fn tab_bar(cosmic: &cosmic_theme::Theme, container: &Container) -> Appearance {
|
||||
let active = tab_bar_active(cosmic);
|
||||
let hc = cosmic.is_high_contrast;
|
||||
let border = if hc {
|
||||
Border {
|
||||
color: container.component.border.into(),
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
width: 1.0,
|
||||
}
|
||||
} else {
|
||||
Border::default()
|
||||
};
|
||||
|
||||
Appearance {
|
||||
active_width: 4.0,
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
first: ItemAppearance { border },
|
||||
middle: ItemAppearance { border },
|
||||
last: ItemAppearance { border },
|
||||
text_color: container.component.on.into(),
|
||||
},
|
||||
hover: super::hover(cosmic, &active, 0.3),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selection_active(
|
||||
cosmic: &cosmic_theme::Theme,
|
||||
component: &Component,
|
||||
) -> ItemStatusAppearance {
|
||||
let rad_m = cosmic.corner_radii.radius_m;
|
||||
let rad_xl = cosmic.corner_radii.radius_xl;
|
||||
let rad_0 = cosmic.corner_radii.radius_0;
|
||||
|
||||
ItemStatusAppearance {
|
||||
background: Some(Background::Color(component.selected_state_color().into())),
|
||||
background: Some(Background::Color(
|
||||
cosmic.palette.neutral_5.with_alpha(0.1).into(),
|
||||
)),
|
||||
first: ItemAppearance {
|
||||
border_radius: Radius::from([rad_m[0], rad_0[1], rad_0[2], rad_m[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_xl[0], rad_0[1], rad_0[2], rad_xl[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: Radius::from([rad_0[0], rad_m[1], rad_m[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_0[0], rad_xl[1], rad_xl[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
}
|
||||
|
|
@ -202,78 +215,86 @@ mod horizontal {
|
|||
cosmic.palette.neutral_5.with_alpha(0.2).into(),
|
||||
)),
|
||||
first: ItemAppearance {
|
||||
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
border_bottom: Some((4.0, cosmic.accent.base.into())),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
color: cosmic.accent.base.into(),
|
||||
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
width: 0.0,
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
border_bottom: Some((4.0, cosmic.accent.base.into())),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
color: cosmic.accent.base.into(),
|
||||
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
width: 0.0,
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
border_bottom: Some((4.0, cosmic.accent.base.into())),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
color: cosmic.accent.base.into(),
|
||||
radius: Radius::from([rad_s[0], rad_s[1], rad_0[2], rad_0[3]]),
|
||||
width: 0.0,
|
||||
},
|
||||
},
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus(
|
||||
cosmic: &cosmic_theme::Theme,
|
||||
container: &Container,
|
||||
default: &ItemStatusAppearance,
|
||||
) -> ItemStatusAppearance {
|
||||
let color = container.small_widget;
|
||||
ItemStatusAppearance {
|
||||
background: Some(Background::Color(color.into())),
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
..*default
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hover(
|
||||
cosmic: &cosmic_theme::Theme,
|
||||
component: &Component,
|
||||
default: &ItemStatusAppearance,
|
||||
) -> ItemStatusAppearance {
|
||||
ItemStatusAppearance {
|
||||
background: Some(Background::Color(component.hover.with_alpha(0.2).into())),
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
..*default
|
||||
}
|
||||
}
|
||||
|
||||
mod vertical {
|
||||
use super::Appearance;
|
||||
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
|
||||
use cosmic_theme::Component;
|
||||
use cosmic_theme::{Component, Container};
|
||||
use iced::Border;
|
||||
use iced_core::{Background, border::Radius};
|
||||
use palette::WithAlpha;
|
||||
|
||||
pub fn tab_bar(cosmic: &cosmic_theme::Theme, container: &Container) -> Appearance {
|
||||
let active = tab_bar_active(cosmic);
|
||||
Appearance {
|
||||
active_width: 4.0,
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
inactive: ItemStatusAppearance {
|
||||
background: None,
|
||||
text_color: container.component.on.into(),
|
||||
..active
|
||||
},
|
||||
hover: super::hover(cosmic, &active, 0.3),
|
||||
active,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selection_active(
|
||||
cosmic: &cosmic_theme::Theme,
|
||||
component: &Component,
|
||||
) -> ItemStatusAppearance {
|
||||
let rad_0 = cosmic.corner_radii.radius_0;
|
||||
let rad_m = cosmic.corner_radii.radius_m;
|
||||
let rad_xl = cosmic.corner_radii.radius_xl;
|
||||
|
||||
ItemStatusAppearance {
|
||||
background: Some(Background::Color(
|
||||
component.selected_state_color().with_alpha(0.3).into(),
|
||||
cosmic.palette.neutral_5.with_alpha(0.1).into(),
|
||||
)),
|
||||
first: ItemAppearance {
|
||||
border_radius: Radius::from([rad_m[0], rad_m[1], rad_0[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_xl[0], rad_xl[1], rad_0[2], rad_0[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: Radius::from([rad_0[0], rad_0[1], rad_m[2], rad_m[3]]),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: Radius::from([rad_0[0], rad_0[1], rad_xl[2], rad_xl[3]]),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
}
|
||||
|
|
@ -285,18 +306,41 @@ mod vertical {
|
|||
cosmic.palette.neutral_5.with_alpha(0.2).into(),
|
||||
)),
|
||||
first: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_m.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_m.into(),
|
||||
width: 0.0,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
middle: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_m.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_m.into(),
|
||||
width: 0.0,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
last: ItemAppearance {
|
||||
border_radius: cosmic.corner_radii.radius_m.into(),
|
||||
..Default::default()
|
||||
border: Border {
|
||||
radius: cosmic.corner_radii.radius_m.into(),
|
||||
width: 0.0,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hover(
|
||||
cosmic: &cosmic_theme::Theme,
|
||||
default: &ItemStatusAppearance,
|
||||
alpha: f32,
|
||||
) -> ItemStatusAppearance {
|
||||
ItemStatusAppearance {
|
||||
background: Some(Background::Color(
|
||||
cosmic.palette.neutral_5.with_alpha(alpha).into(),
|
||||
)),
|
||||
text_color: cosmic.accent_text_color().into(),
|
||||
..*default
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl<'a, Message: Clone + 'static> From<NavBar<'a, Message>>
|
|||
.button_padding([space_s, space_xxs, space_s, space_xxs])
|
||||
.button_spacing(space_xxs)
|
||||
.spacing(space_xxs)
|
||||
.style(crate::theme::SegmentedButton::TabBar)
|
||||
.style(crate::theme::SegmentedButton::NavBar)
|
||||
.apply(container)
|
||||
.padding(space_xxs)
|
||||
.apply(scrollable)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ where
|
|||
Model<SelectionMode>: Selectable,
|
||||
SelectionMode: Default,
|
||||
{
|
||||
const VERTICAL: bool = false;
|
||||
|
||||
fn variant_appearance(
|
||||
theme: &crate::Theme,
|
||||
style: &crate::theme::SegmentedButton,
|
||||
|
|
|
|||
|
|
@ -1,31 +1,24 @@
|
|||
// Copyright 2022 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use iced_core::{Background, Color, border::Radius};
|
||||
use iced::Border;
|
||||
use iced_core::{Background, Color};
|
||||
|
||||
/// Appearance of the segmented button.
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct Appearance {
|
||||
pub background: Option<Background>,
|
||||
pub border_radius: Radius,
|
||||
pub border_bottom: Option<(f32, Color)>,
|
||||
pub border_end: Option<(f32, Color)>,
|
||||
pub border_start: Option<(f32, Color)>,
|
||||
pub border_top: Option<(f32, Color)>,
|
||||
pub border: Border,
|
||||
pub active_width: f32,
|
||||
pub active: ItemStatusAppearance,
|
||||
pub inactive: ItemStatusAppearance,
|
||||
pub hover: ItemStatusAppearance,
|
||||
pub focus: ItemStatusAppearance,
|
||||
}
|
||||
|
||||
/// Appearance of an item in the segmented button.
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct ItemAppearance {
|
||||
pub border_radius: Radius,
|
||||
pub border_bottom: Option<(f32, Color)>,
|
||||
pub border_end: Option<(f32, Color)>,
|
||||
pub border_start: Option<(f32, Color)>,
|
||||
pub border_top: Option<(f32, Color)>,
|
||||
pub border: Border,
|
||||
}
|
||||
|
||||
/// Appearance of an item based on its status.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ where
|
|||
Model<SelectionMode>: Selectable,
|
||||
SelectionMode: Default,
|
||||
{
|
||||
const VERTICAL: bool = true;
|
||||
|
||||
fn variant_appearance(
|
||||
theme: &crate::Theme,
|
||||
style: &crate::theme::SegmentedButton,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,12 @@ use iced::{
|
|||
use iced_core::mouse::ScrollDelta;
|
||||
use iced_core::text::{LineHeight, Renderer as TextRenderer, Shaping, Wrapping};
|
||||
use iced_core::widget::{self, operation, tree};
|
||||
use iced_core::{Border, Gradient, Point, Renderer as IcedRenderer, Shadow, Text};
|
||||
use iced_core::{Border, Point, Renderer as IcedRenderer, Shadow, Text};
|
||||
use iced_core::{Clipboard, Layout, Shell, Widget, layout, renderer, widget::Tree};
|
||||
use iced_runtime::{Action, task};
|
||||
use slotmap::{Key, SecondaryMap};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::LazyCell;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
|
@ -46,6 +47,8 @@ pub enum ItemBounds {
|
|||
|
||||
/// Isolates variant-specific behaviors from [`SegmentedButton`].
|
||||
pub trait SegmentedVariant {
|
||||
const VERTICAL: bool;
|
||||
|
||||
/// Get the appearance for this variant of the widget.
|
||||
fn variant_appearance(
|
||||
theme: &crate::Theme,
|
||||
|
|
@ -107,11 +110,11 @@ where
|
|||
/// Spacing for each indent.
|
||||
pub(super) indent_spacing: u16,
|
||||
/// Desired font for active tabs.
|
||||
pub(super) font_active: Option<crate::font::Font>,
|
||||
pub(super) font_active: crate::font::Font,
|
||||
/// Desired font for hovered tabs.
|
||||
pub(super) font_hovered: Option<crate::font::Font>,
|
||||
pub(super) font_hovered: crate::font::Font,
|
||||
/// Desired font for inactive tabs.
|
||||
pub(super) font_inactive: Option<crate::font::Font>,
|
||||
pub(super) font_inactive: crate::font::Font,
|
||||
/// Size of the font.
|
||||
pub(super) font_size: f32,
|
||||
/// Desired width of the widget.
|
||||
|
|
@ -175,9 +178,9 @@ where
|
|||
minimum_button_width: u16::MIN,
|
||||
maximum_button_width: u16::MAX,
|
||||
indent_spacing: 16,
|
||||
font_active: None,
|
||||
font_hovered: None,
|
||||
font_inactive: None,
|
||||
font_active: crate::font::semibold(),
|
||||
font_hovered: crate::font::semibold(),
|
||||
font_inactive: crate::font::default(),
|
||||
font_size: 14.0,
|
||||
height: Length::Shrink,
|
||||
width: Length::Fill,
|
||||
|
|
@ -603,16 +606,16 @@ where
|
|||
|
||||
for key in self.model.order.iter().copied() {
|
||||
if let Some(text) = self.model.text.get(key) {
|
||||
let (font, button_state) =
|
||||
if self.model.is_active(key) || self.button_is_focused(state, key) {
|
||||
(self.font_active, 0)
|
||||
} else if self.button_is_hovered(state, key) {
|
||||
(self.font_hovered, 1)
|
||||
} else {
|
||||
(self.font_inactive, 2)
|
||||
};
|
||||
let (font, button_state) = if self.button_is_focused(state, key) {
|
||||
(self.font_active, 0)
|
||||
} else if state.show_context.is_some() || self.button_is_hovered(state, key) {
|
||||
(self.font_hovered, 1)
|
||||
} else if self.model.is_active(key) {
|
||||
(self.font_active, 2)
|
||||
} else {
|
||||
(self.font_inactive, 3)
|
||||
};
|
||||
|
||||
let font = font.unwrap_or_else(crate::font::default);
|
||||
let mut hasher = DefaultHasher::new();
|
||||
text.hash(&mut hasher);
|
||||
font.hash(&mut hasher);
|
||||
|
|
@ -1171,47 +1174,15 @@ where
|
|||
let bounds: Rectangle = layout.bounds();
|
||||
let button_amount = self.model.items.len();
|
||||
|
||||
// Modifies alpha color when `on_activate` is unset.
|
||||
let apply_alpha = |mut c: Color| {
|
||||
if self.on_activate.is_none() {
|
||||
c.a /= 2.0;
|
||||
}
|
||||
|
||||
c
|
||||
};
|
||||
|
||||
// Maps `apply_alpha` to background color.
|
||||
let bg_with_alpha = |mut b| {
|
||||
match &mut b {
|
||||
Background::Color(c) => {
|
||||
*c = apply_alpha(*c);
|
||||
}
|
||||
|
||||
Background::Gradient(g) => {
|
||||
let Gradient::Linear(l) = g;
|
||||
for c in &mut l.stops {
|
||||
let Some(stop) = c else {
|
||||
continue;
|
||||
};
|
||||
stop.color = apply_alpha(stop.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
b
|
||||
};
|
||||
|
||||
// Draw the background, if a background was defined.
|
||||
if let Some(background) = appearance.background {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds,
|
||||
border: Border {
|
||||
radius: appearance.border_radius,
|
||||
..Border::default()
|
||||
},
|
||||
border: appearance.border,
|
||||
shadow: Shadow::default(),
|
||||
},
|
||||
bg_with_alpha(background),
|
||||
background,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1222,7 +1193,7 @@ where
|
|||
// Previous tab button
|
||||
let mut background_appearance =
|
||||
if self.on_activate.is_some() && Item::PrevButton == state.focused_item {
|
||||
Some(appearance.focus)
|
||||
Some(appearance.active)
|
||||
} else if self.on_activate.is_some() && Item::PrevButton == state.hovered {
|
||||
Some(appearance.hover)
|
||||
} else {
|
||||
|
|
@ -1241,7 +1212,7 @@ where
|
|||
},
|
||||
background_appearance
|
||||
.background
|
||||
.map_or(Background::Color(Color::TRANSPARENT), bg_with_alpha),
|
||||
.unwrap_or(Background::Color(Color::TRANSPARENT)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1251,13 +1222,11 @@ where
|
|||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
apply_alpha(if state.buttons_offset == 0 {
|
||||
if state.buttons_offset == 0 {
|
||||
appearance.inactive.text_color
|
||||
} else if let Item::PrevButton = state.focused_item {
|
||||
appearance.focus.text_color
|
||||
} else {
|
||||
appearance.active.text_color
|
||||
}),
|
||||
},
|
||||
Rectangle {
|
||||
x: tab_bounds.x + 8.0,
|
||||
y: tab_bounds.y + f32::from(self.button_height) / 4.0,
|
||||
|
|
@ -1272,7 +1241,7 @@ where
|
|||
// Next tab button
|
||||
background_appearance =
|
||||
if self.on_activate.is_some() && Item::NextButton == state.focused_item {
|
||||
Some(appearance.focus)
|
||||
Some(appearance.active)
|
||||
} else if self.on_activate.is_some() && Item::NextButton == state.hovered {
|
||||
Some(appearance.hover)
|
||||
} else {
|
||||
|
|
@ -1301,13 +1270,13 @@ where
|
|||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
apply_alpha(if self.next_tab_sensitive(state) {
|
||||
if self.next_tab_sensitive(state) {
|
||||
appearance.active.text_color
|
||||
} else if let Item::NextButton = state.focused_item {
|
||||
appearance.focus.text_color
|
||||
appearance.active.text_color
|
||||
} else {
|
||||
appearance.inactive.text_color
|
||||
}),
|
||||
},
|
||||
Rectangle {
|
||||
x: tab_bounds.x + 8.0,
|
||||
y: tab_bounds.y + f32::from(self.button_height) / 4.0,
|
||||
|
|
@ -1349,22 +1318,23 @@ where
|
|||
|
||||
let center_y = bounds.center_y();
|
||||
|
||||
let menu_open = !tree.children.is_empty()
|
||||
&& tree.children[0]
|
||||
.state
|
||||
.downcast_ref::<MenuBarState>()
|
||||
.inner
|
||||
.with_data(|data| data.open);
|
||||
let menu_open = || {
|
||||
state.show_context == Some(key)
|
||||
&& !tree.children.is_empty()
|
||||
&& tree.children[0]
|
||||
.state
|
||||
.downcast_ref::<MenuBarState>()
|
||||
.inner
|
||||
.with_data(|data| data.open)
|
||||
};
|
||||
|
||||
let key_is_active = self.model.is_active(key);
|
||||
let key_is_hovered = self.button_is_hovered(state, key);
|
||||
let key_has_context_menu_open = menu_open && state.show_context == Some(key);
|
||||
let status_appearance = if self.button_is_focused(state, key) {
|
||||
appearance.focus
|
||||
let key_is_focused = self.button_is_focused(state, key);
|
||||
let key_is_hovered = LazyCell::new(|| self.button_is_hovered(state, key));
|
||||
let status_appearance = if *key_is_hovered || menu_open() {
|
||||
appearance.hover
|
||||
} else if key_is_active {
|
||||
appearance.active
|
||||
} else if key_is_hovered || key_has_context_menu_open {
|
||||
appearance.hover
|
||||
} else {
|
||||
appearance.inactive
|
||||
};
|
||||
|
|
@ -1378,29 +1348,45 @@ where
|
|||
};
|
||||
|
||||
// Render the background of the button.
|
||||
if status_appearance.background.is_some() {
|
||||
if key_is_focused || status_appearance.background.is_some() {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds,
|
||||
border: Border {
|
||||
radius: button_appearance.border_radius,
|
||||
..Default::default()
|
||||
border: if key_is_focused {
|
||||
Border {
|
||||
width: 1.0,
|
||||
color: appearance.active.text_color,
|
||||
radius: button_appearance.border.radius,
|
||||
}
|
||||
} else {
|
||||
button_appearance.border
|
||||
},
|
||||
shadow: Shadow::default(),
|
||||
},
|
||||
status_appearance
|
||||
.background
|
||||
.map_or(Background::Color(Color::TRANSPARENT), bg_with_alpha),
|
||||
.unwrap_or(Background::Color(Color::TRANSPARENT)),
|
||||
);
|
||||
}
|
||||
|
||||
// Draw the bottom border defined for this button.
|
||||
if let Some((width, background)) = button_appearance.border_bottom {
|
||||
let mut bounds = bounds;
|
||||
bounds.y = bounds.y + bounds.height - width;
|
||||
bounds.height = width;
|
||||
|
||||
// Draw the active hint on tabs
|
||||
if appearance.active_width > 0.0 {
|
||||
let rad_0 = THEME.lock().unwrap().cosmic().corner_radii.radius_0;
|
||||
let active_width = if key_is_active {
|
||||
appearance.active_width
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let mut bounds = bounds;
|
||||
|
||||
if Self::VERTICAL {
|
||||
bounds.x += bounds.height - active_width;
|
||||
bounds.width = active_width;
|
||||
} else {
|
||||
bounds.y += bounds.height - active_width;
|
||||
bounds.height = active_width;
|
||||
}
|
||||
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds,
|
||||
|
|
@ -1410,7 +1396,7 @@ where
|
|||
},
|
||||
shadow: Shadow::default(),
|
||||
},
|
||||
bg_with_alpha(background.into()),
|
||||
appearance.active.text_color,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1455,7 +1441,7 @@ where
|
|||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
apply_alpha(status_appearance.text_color),
|
||||
status_appearance.text_color,
|
||||
Rectangle {
|
||||
width,
|
||||
height: width,
|
||||
|
|
@ -1470,7 +1456,7 @@ where
|
|||
if key_is_active {
|
||||
if let crate::theme::SegmentedButton::Control = self.style {
|
||||
let mut image_bounds = bounds;
|
||||
image_bounds.y = center_y - 16.0 / 2.0;
|
||||
image_bounds.y = center_y - 8.0;
|
||||
|
||||
draw_icon::<Message>(
|
||||
renderer,
|
||||
|
|
@ -1478,7 +1464,7 @@ where
|
|||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
apply_alpha(status_appearance.text_color),
|
||||
status_appearance.text_color,
|
||||
Rectangle {
|
||||
width: 16.0,
|
||||
height: 16.0,
|
||||
|
|
@ -1505,7 +1491,7 @@ where
|
|||
|
||||
// Whether to show the close button on this tab.
|
||||
let show_close_button =
|
||||
(key_is_active || !self.show_close_icon_on_hover || key_is_hovered)
|
||||
(key_is_active || !self.show_close_icon_on_hover || *key_is_hovered)
|
||||
&& self.model.is_closable(key);
|
||||
|
||||
// Width of the icon used by the close button, which we will subtract from the text bounds.
|
||||
|
|
@ -1527,7 +1513,7 @@ where
|
|||
renderer.fill_paragraph(
|
||||
state.paragraphs[key].raw(),
|
||||
bounds.position(),
|
||||
apply_alpha(status_appearance.text_color),
|
||||
status_appearance.text_color,
|
||||
Rectangle {
|
||||
x: bounds.x,
|
||||
width: bounds.width,
|
||||
|
|
@ -1546,7 +1532,7 @@ where
|
|||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
apply_alpha(status_appearance.text_color),
|
||||
status_appearance.text_color,
|
||||
close_button_bounds,
|
||||
self.close_icon.clone(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ where
|
|||
.button_padding([space_s, 0, space_s, 0])
|
||||
.button_spacing(space_xxs)
|
||||
.style(crate::theme::SegmentedButton::Control)
|
||||
.font_active(Some(crate::font::semibold()))
|
||||
}
|
||||
|
||||
/// A selection of multiple choices appearing as a conjoined button.
|
||||
|
|
@ -55,5 +54,4 @@ where
|
|||
.button_padding([space_s, 0, space_s, 0])
|
||||
.button_spacing(space_xxs)
|
||||
.style(crate::theme::SegmentedButton::Control)
|
||||
.font_active(Some(crate::font::semibold()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ where
|
|||
.button_height(44)
|
||||
.button_padding([space_s, space_xs, space_s, space_xs])
|
||||
.style(crate::theme::SegmentedButton::TabBar)
|
||||
.font_active(Some(crate::font::semibold()))
|
||||
}
|
||||
|
||||
/// A collection of tabs for developing a tabbed interface.
|
||||
|
|
@ -52,5 +51,4 @@ where
|
|||
.button_height(44)
|
||||
.button_padding([space_s, space_xs, space_s, space_xs])
|
||||
.style(crate::theme::SegmentedButton::TabBar)
|
||||
.font_active(Some(crate::font::semibold()))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue