feat(ui): refine Graphite visuals and center navigation content
This commit is contained in:
1 parent
cc3b6f903f
commit
1e34ec47ad
14 files changed
+646
-121
No files matched your search
@@ -501,43 +501,83 @@ impl App {
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
pub fn settings_hub(&self) -> Element<'_, Message> {
|
||||
let mut out = column([]).spacing(18);
|
||||
pub fn settings_hub(&self, width: f32) -> Element<'_, Message> {
|
||||
self.hub_layout(if width >= 680. { 2 } else { 1 })
|
||||
}
|
||||
fn hub_layout(&self, columns: usize) -> Element<'_, Message> {
|
||||
let palette = visuals::Palette::from_resolved(&self.resolved.presented);
|
||||
let mut out = column([]).spacing(24);
|
||||
for (name, pages) in Page::NAVIGATION {
|
||||
if name == "Appearance" {
|
||||
if name == "Advanced" {
|
||||
continue;
|
||||
}
|
||||
out = out.push(self.panel_heading(tr(name), 15.));
|
||||
for &p in pages {
|
||||
out = out.push(self.hub_card(p));
|
||||
let pages = if name == "Appearance" {
|
||||
vec![Page::General, Page::Configuration]
|
||||
} else {
|
||||
pages
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|p| *p != Page::Appearance)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
let mut section = column([]).spacing(8).push(
|
||||
self.panel_heading(
|
||||
tr(if name == "Appearance" {
|
||||
"Quick access"
|
||||
} else {
|
||||
name
|
||||
}),
|
||||
12.,
|
||||
)
|
||||
.class(cosmic::theme::Text::Color(palette.muted)),
|
||||
);
|
||||
for pair in pages.chunks(columns) {
|
||||
let mut cards = row([]).spacing(12);
|
||||
for &page in pair {
|
||||
cards = cards.push(self.hub_card(page));
|
||||
}
|
||||
if pair.len() < columns {
|
||||
cards = cards.push(cosmic::widget::Space::new().width(Length::Fill));
|
||||
}
|
||||
section = section.push(cards);
|
||||
}
|
||||
out = out.push(section);
|
||||
}
|
||||
column([])
|
||||
.spacing(12)
|
||||
.push(self.hub_card(Page::General))
|
||||
.push(out)
|
||||
.into()
|
||||
out.into()
|
||||
}
|
||||
fn hub_card(&self, p: Page) -> Element<'static, Message> {
|
||||
let palette = visuals::Palette::from_resolved(&self.resolved.presented);
|
||||
button::custom(
|
||||
row([])
|
||||
.spacing(12)
|
||||
.align_y(Alignment::Center)
|
||||
.push(visuals::icon(p, palette.accent))
|
||||
.push(
|
||||
column([])
|
||||
.spacing(3)
|
||||
.push(self.label(p.title(), 15.))
|
||||
.push(self.label(p.subtitle(), 12.)),
|
||||
)
|
||||
.push(self.label(ferese_config::tr!("›"), 20.)),
|
||||
let content = row([])
|
||||
.spacing(12)
|
||||
.align_y(Alignment::Center)
|
||||
.push(visuals::icon(p, palette.muted))
|
||||
.push(
|
||||
column([])
|
||||
.spacing(4)
|
||||
.push(self.label(p.title(), 14.).font(cosmic::iced::Font {
|
||||
weight: cosmic::iced::font::Weight::Medium,
|
||||
..self.font
|
||||
}))
|
||||
.push(
|
||||
self.label(p.subtitle(), 12.)
|
||||
.class(cosmic::theme::Text::Color(palette.muted)),
|
||||
)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(visuals::action_icon("M9 5l7 7-7 7", palette.muted));
|
||||
container(
|
||||
button::custom(content)
|
||||
.name(p.title())
|
||||
.padding(16)
|
||||
.width(Length::Fill)
|
||||
.class(visuals::button_style(palette, false))
|
||||
.on_press(Message::Page(p)),
|
||||
)
|
||||
.name(p.title())
|
||||
.padding(14)
|
||||
.id(cosmic::widget::Id::new(format!(
|
||||
"settings-hub-{}",
|
||||
p as usize
|
||||
)))
|
||||
.width(Length::Fill)
|
||||
.class(visuals::button_style(palette, false))
|
||||
.on_press(Message::Page(p))
|
||||
.into()
|
||||
}
|
||||
pub fn all_options_view(&self) -> Element<'_, Message> {
|
||||
|
||||
@@ -248,9 +248,9 @@ impl App {
|
||||
.push(labels)
|
||||
.push(control),
|
||||
)
|
||||
.padding([7, 10])
|
||||
.padding([10, 12])
|
||||
.width(Length::Fill)
|
||||
.class(visuals::surface(palette.card, 0.))
|
||||
.class(visuals::surface(cosmic::iced::Color::TRANSPARENT, 0.))
|
||||
.into()
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,17 @@ pub(super) const SIDEBAR_BREAKPOINT: f32 = 960.;
|
||||
impl App {
|
||||
pub(super) fn page_view(&self) -> Element<'_, Message> {
|
||||
cosmic::iced::widget::responsive(move |size| {
|
||||
self.page_layout(size.width < SIDEBAR_BREAKPOINT, size.height)
|
||||
self.page_layout(size.width < SIDEBAR_BREAKPOINT, size.height, size.width)
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn page_layout(&self, compact: bool, height: f32) -> Element<'_, Message> {
|
||||
fn page_layout(&self, compact: bool, height: f32, width: f32) -> Element<'_, Message> {
|
||||
let palette = visuals::Palette::from_resolved(&self.resolved.presented);
|
||||
let mut sidebar = column([])
|
||||
.spacing(3)
|
||||
.spacing(4)
|
||||
.push(
|
||||
widget::mouse_area(
|
||||
row([])
|
||||
@@ -26,7 +26,7 @@ impl App {
|
||||
.align_y(Alignment::Center)
|
||||
.push(visuals::brand_icon())
|
||||
.push(
|
||||
self.label(ferese_config::tr!("Ferese"), 16.)
|
||||
self.label(ferese_config::tr!("Ferese"), 14.)
|
||||
.class(cosmic::theme::Text::Color(palette.text)),
|
||||
)
|
||||
.width(Length::Fill),
|
||||
@@ -52,37 +52,83 @@ impl App {
|
||||
false,
|
||||
));
|
||||
}
|
||||
let mut navigation = column([]).spacing(16);
|
||||
let mut navigation = column([]).spacing(24);
|
||||
for (title, pages) in Page::NAVIGATION {
|
||||
let mut section = column([]).spacing(3).push(
|
||||
container(
|
||||
self.label(ferese_config::locale::text(title), 11.)
|
||||
.class(cosmic::theme::Text::Color(palette.muted)),
|
||||
self.label(
|
||||
ferese_config::locale::text(if title == "Appearance" {
|
||||
"Settings"
|
||||
} else {
|
||||
title
|
||||
}),
|
||||
11.,
|
||||
)
|
||||
.class(cosmic::theme::Text::Color(palette.muted)),
|
||||
)
|
||||
.padding([0, 8]),
|
||||
);
|
||||
for &page in pages {
|
||||
let selected = self.page.navigation_page() == page;
|
||||
let indicator = container(widget::Space::new().width(2).height(14))
|
||||
.id(widget::Id::new(format!(
|
||||
"settings-nav-marker-{}",
|
||||
page as usize
|
||||
)))
|
||||
.class(visuals::surface(
|
||||
if selected {
|
||||
palette.accent
|
||||
} else {
|
||||
cosmic::iced::Color::TRANSPARENT
|
||||
},
|
||||
1.,
|
||||
));
|
||||
let icon = container(visuals::icon(
|
||||
page,
|
||||
if selected {
|
||||
palette.accent
|
||||
} else {
|
||||
palette.muted
|
||||
},
|
||||
))
|
||||
.id(widget::Id::new(format!(
|
||||
"settings-nav-icon-{}",
|
||||
page as usize
|
||||
)));
|
||||
let label = container(self.label(page.title(), 13.)).id(widget::Id::new(format!(
|
||||
"settings-nav-label-{}",
|
||||
page as usize
|
||||
)));
|
||||
let content = container(
|
||||
row([])
|
||||
.spacing(8)
|
||||
.align_y(Alignment::Center)
|
||||
.push(indicator)
|
||||
.push(icon)
|
||||
.push(label),
|
||||
)
|
||||
.id(widget::Id::new(format!(
|
||||
"settings-nav-content-{}",
|
||||
page as usize
|
||||
)));
|
||||
// The button lays its child out at its top padding. Center the
|
||||
// entire row inside its 36 px hit area, as well as row children.
|
||||
section =
|
||||
section.push(
|
||||
button::custom(
|
||||
row([])
|
||||
.spacing(8)
|
||||
.align_y(Alignment::Center)
|
||||
.push(visuals::icon(
|
||||
page,
|
||||
if selected {
|
||||
palette.accent
|
||||
} else {
|
||||
palette.muted
|
||||
},
|
||||
))
|
||||
.push(self.label(page.title(), 13.)),
|
||||
container(content)
|
||||
.center_y(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.padding([8, 8])
|
||||
.class(visuals::navigation_style(palette, selected))
|
||||
.id(widget::Id::new(format!(
|
||||
"settings-nav-button-{}",
|
||||
page as usize
|
||||
)))
|
||||
.name(page.title())
|
||||
.width(Length::Fill)
|
||||
.height(36)
|
||||
.padding([0, 8])
|
||||
.class(visuals::navigation_style(palette, selected))
|
||||
.on_press(Message::Page(if selected { self.page } else { page })),
|
||||
);
|
||||
}
|
||||
@@ -90,21 +136,27 @@ impl App {
|
||||
}
|
||||
let sidebar = container(sidebar.push(scrollable(navigation).height(Length::Fill)))
|
||||
.id("settings-sidebar")
|
||||
.width(if compact { 244 } else { 204 })
|
||||
.width(if compact { 244 } else { 208 })
|
||||
.height(Length::Fill)
|
||||
.padding([20, 10])
|
||||
.class(visuals::surface(palette.sidebar, 0.));
|
||||
.padding([24, 12])
|
||||
.class(visuals::sidebar(palette));
|
||||
let mut heading = row([]).align_y(Alignment::Center).spacing(12).push(
|
||||
column([])
|
||||
.spacing(3)
|
||||
.push(self.label(
|
||||
if self.search.is_empty() {
|
||||
self.page.navigation_page().title()
|
||||
} else {
|
||||
"Search"
|
||||
},
|
||||
20.,
|
||||
))
|
||||
.push(
|
||||
self.label(
|
||||
if self.search.is_empty() {
|
||||
self.page.navigation_page().title()
|
||||
} else {
|
||||
"Search"
|
||||
},
|
||||
24.,
|
||||
)
|
||||
.font(cosmic::iced::Font {
|
||||
weight: cosmic::iced::font::Weight::Medium,
|
||||
..self.font
|
||||
}),
|
||||
)
|
||||
.push(
|
||||
self.label(
|
||||
if self.search.is_empty() {
|
||||
@@ -207,7 +259,9 @@ impl App {
|
||||
}
|
||||
} else {
|
||||
if self.page == Page::Appearance {
|
||||
body = body.push(self.settings_hub());
|
||||
body = body.push(
|
||||
self.settings_hub((width - if compact { 0. } else { 208. }).min(1040.) - 62.),
|
||||
);
|
||||
}
|
||||
if self.page == Page::Configuration {
|
||||
body = body.push(self.all_options_view());
|
||||
@@ -306,11 +360,7 @@ impl App {
|
||||
group = group.push(self.theme_file_picker());
|
||||
}
|
||||
}
|
||||
body = body.push(
|
||||
container(group)
|
||||
.padding(4)
|
||||
.class(visuals::surface(palette.card, 14.)),
|
||||
);
|
||||
body = body.push(container(group).padding(4).class(visuals::section(palette)));
|
||||
}
|
||||
|
||||
match self.page {
|
||||
@@ -364,11 +414,8 @@ impl App {
|
||||
)
|
||||
.padding([7, 10]),
|
||||
);
|
||||
body = body.push(
|
||||
container(group)
|
||||
.padding(4)
|
||||
.class(visuals::surface(palette.card, 14.)),
|
||||
);
|
||||
body =
|
||||
body.push(container(group).padding(4).class(visuals::section(palette)));
|
||||
}
|
||||
body = body.push(
|
||||
row([])
|
||||
@@ -453,7 +500,7 @@ impl App {
|
||||
container(group)
|
||||
.padding(4)
|
||||
.height(164)
|
||||
.class(visuals::surface(palette.card, 14.))
|
||||
.class(visuals::section(palette))
|
||||
.into()
|
||||
} else {
|
||||
widget::Space::new().height(164).width(Length::Fill).into()
|
||||
@@ -492,9 +539,7 @@ impl App {
|
||||
.on_press(Message::DragWindow)
|
||||
.interaction(cosmic::iced::mouse::Interaction::Grab),
|
||||
);
|
||||
let mut content = column([])
|
||||
.spacing(if self.page == Page::Bar { 16 } else { 8 })
|
||||
.push(page_heading);
|
||||
let mut content = column([]).spacing(16).push(page_heading);
|
||||
if self.search.is_empty() && !self.page.subpages().is_empty() {
|
||||
content = content.push(
|
||||
container(
|
||||
@@ -579,7 +624,18 @@ impl App {
|
||||
),
|
||||
widget::tooltip::Position::Top,
|
||||
))
|
||||
.push(widget::Space::new().width(Length::Fill))
|
||||
.push(
|
||||
self.label(
|
||||
if self.saving {
|
||||
ferese_config::tr!("Saving…").to_owned()
|
||||
} else {
|
||||
ferese_config::locale::translate(&self.status)
|
||||
},
|
||||
11.,
|
||||
)
|
||||
.class(cosmic::theme::Text::Color(palette.muted))
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(widget::tooltip(
|
||||
button::custom(visuals::action_icon(
|
||||
"M3 10h8 M3 10V3 M3 10c3-7 17-6 17 3a7 7 0 0 1-7 7",
|
||||
@@ -653,12 +709,12 @@ impl App {
|
||||
.spacing(6),
|
||||
)
|
||||
.padding(cosmic::iced::Padding {
|
||||
top: 20.,
|
||||
right: 20.,
|
||||
top: 24.,
|
||||
right: 24.,
|
||||
bottom: 8.,
|
||||
left: 20.,
|
||||
left: 24.,
|
||||
})
|
||||
.max_width(if self.page == Page::Bar { 1120 } else { 840 })
|
||||
.max_width(if self.page == Page::Bar { 1120 } else { 1040 })
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
|
||||
@@ -1021,10 +1021,10 @@ fn sidebar_collapses_at_the_breakpoint_and_compact_navigation_dismisses() {
|
||||
let compact = width < 960.;
|
||||
assert_eq!(bounds.sidebar.is_none(), compact);
|
||||
assert_eq!(bounds.toggle.is_some(), compact);
|
||||
assert_eq!(bounds.page.unwrap().x, if compact { 0. } else { 204. });
|
||||
assert_eq!(bounds.page.unwrap().x, if compact { 0. } else { 208. });
|
||||
assert_eq!(
|
||||
bounds.page.unwrap().width,
|
||||
if compact { width } else { width - 204. }
|
||||
if compact { width } else { width - 208. }
|
||||
);
|
||||
}
|
||||
let _ = app.update(Message::Sidebar(true));
|
||||
@@ -1084,3 +1084,173 @@ fn sidebar_collapses_at_the_breakpoint_and_compact_navigation_dismisses() {
|
||||
);
|
||||
assert!(app.pending.is_empty() && !app.saving);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn appearance_cards_reflow_without_overlap_and_keep_every_destination() {
|
||||
#[derive(Default)]
|
||||
struct Cards {
|
||||
bounds: std::collections::HashMap<usize, Rectangle>,
|
||||
}
|
||||
impl Operation for Cards {
|
||||
fn traverse(&mut self, children: &mut dyn FnMut(&mut dyn Operation)) {
|
||||
children(self);
|
||||
}
|
||||
fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
|
||||
for page in Page::ALL {
|
||||
if id == Some(&widget::Id::new(format!("settings-hub-{}", page as usize))) {
|
||||
self.bounds.insert(page as usize, bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let renderer = runtime
|
||||
.block_on(<cosmic::Renderer as Headless>::new(
|
||||
Font::default(),
|
||||
Pixels(14.),
|
||||
Some("tiny-skia"),
|
||||
))
|
||||
.unwrap();
|
||||
let app = App::init(
|
||||
Core::default(),
|
||||
(
|
||||
"/unused/overview.kdl".into(),
|
||||
Snapshot::parse(String::new()),
|
||||
None,
|
||||
),
|
||||
)
|
||||
.0;
|
||||
for width in [480., 720., 960., 1280., 1800.] {
|
||||
let mut view = app.page_view();
|
||||
let mut tree = Tree::new(&view);
|
||||
let node = view.as_widget_mut().layout(
|
||||
&mut tree,
|
||||
&renderer,
|
||||
&layout::Limits::new(Size::ZERO, Size::new(width, 760.)),
|
||||
);
|
||||
let mut cards = Cards::default();
|
||||
view.as_widget_mut()
|
||||
.operate(&mut tree, Layout::new(&node), &renderer, &mut cards);
|
||||
assert_eq!(
|
||||
cards.bounds.len(),
|
||||
14,
|
||||
"{width}: all settings destinations must be reachable"
|
||||
);
|
||||
for bounds in cards.bounds.values() {
|
||||
assert!(
|
||||
bounds.x >= 0. && bounds.x + bounds.width <= width + 0.5,
|
||||
"{width}: {bounds:?}"
|
||||
);
|
||||
assert!(bounds.width > 200. && bounds.height > 40.);
|
||||
}
|
||||
let general = cards.bounds[&(Page::General as usize)];
|
||||
let complete = cards.bounds[&(Page::Configuration as usize)];
|
||||
if width <= 720. {
|
||||
assert!((general.x - complete.x).abs() < 0.5);
|
||||
assert!(complete.y >= general.y + general.height);
|
||||
} else {
|
||||
assert!((general.y - complete.y).abs() < 0.5);
|
||||
assert!(complete.x >= general.x + general.width + 8.);
|
||||
}
|
||||
let rectangles = cards.bounds.values().collect::<Vec<_>>();
|
||||
for (i, a) in rectangles.iter().enumerate() {
|
||||
for b in &rectangles[i + 1..] {
|
||||
assert!(
|
||||
a.intersection(*b).is_none(),
|
||||
"{width}: cards overlap: {a:?} {b:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sidebar_rows_center_inside_the_full_button_hit_area() {
|
||||
#[derive(Default)]
|
||||
struct Centers {
|
||||
buttons: std::collections::HashMap<usize, Rectangle>,
|
||||
parts: Vec<(usize, Rectangle)>,
|
||||
}
|
||||
impl Operation for Centers {
|
||||
fn traverse(&mut self, children: &mut dyn FnMut(&mut dyn Operation)) {
|
||||
children(self);
|
||||
}
|
||||
fn focusable(
|
||||
&mut self,
|
||||
id: Option<&widget::Id>,
|
||||
bounds: Rectangle,
|
||||
_: &mut dyn cosmic::iced::advanced::widget::operation::Focusable,
|
||||
) {
|
||||
for page in Page::ALL {
|
||||
if id
|
||||
== Some(&widget::Id::new(format!(
|
||||
"settings-nav-button-{}",
|
||||
page as usize
|
||||
)))
|
||||
{
|
||||
self.buttons.insert(page as usize, bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
|
||||
for page in Page::ALL {
|
||||
for part in ["content", "icon", "label", "marker"] {
|
||||
if id
|
||||
== Some(&widget::Id::new(format!(
|
||||
"settings-nav-{part}-{}",
|
||||
page as usize
|
||||
)))
|
||||
{
|
||||
self.parts.push((page as usize, bounds));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let renderer = runtime
|
||||
.block_on(<cosmic::Renderer as Headless>::new(
|
||||
Font::default(),
|
||||
Pixels(14.),
|
||||
Some("tiny-skia"),
|
||||
))
|
||||
.unwrap();
|
||||
let mut app = App::init(
|
||||
Core::default(),
|
||||
(
|
||||
"/unused/navigation-centers.kdl".into(),
|
||||
Snapshot::parse(String::new()),
|
||||
None,
|
||||
),
|
||||
)
|
||||
.0;
|
||||
for selected in [Page::Windows, Page::Keyboard, Page::Accessibility] {
|
||||
app.page = selected;
|
||||
let mut view = app.page_view();
|
||||
let mut tree = Tree::new(&view);
|
||||
let node = view.as_widget_mut().layout(
|
||||
&mut tree,
|
||||
&renderer,
|
||||
&layout::Limits::new(Size::ZERO, Size::new(1280., 760.)),
|
||||
);
|
||||
let mut centers = Centers::default();
|
||||
view.as_widget_mut()
|
||||
.operate(&mut tree, Layout::new(&node), &renderer, &mut centers);
|
||||
assert_eq!(centers.buttons.len(), 15);
|
||||
assert_eq!(centers.parts.len(), 60);
|
||||
for (page, part) in ¢ers.parts {
|
||||
let button = centers.buttons[page];
|
||||
assert_eq!(button.height, 36.);
|
||||
assert!(
|
||||
((part.y + part.height / 2.) - (button.y + button.height / 2.)).abs() < 0.51,
|
||||
"page {page}: content {part:?} is not centered in {button:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,6 +389,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
let mut families = ferese_config::families::builtins();
|
||||
let builtins = families.len();
|
||||
let mut imported = families[0].clone();
|
||||
imported.id = "sunset".into();
|
||||
families.insert(1, imported.clone());
|
||||
@@ -398,9 +399,16 @@ mod tests {
|
||||
legacy.id = "monokai".into();
|
||||
families.push(legacy);
|
||||
let sections = FamilySections::new(&families, &snapshot);
|
||||
assert_eq!(sections.custom.as_slice(), &[1, 7]);
|
||||
assert_eq!(sections.built_in.as_slice(), &[0, 2, 3, 4, 5, 6, 8]);
|
||||
assert_eq!(sections.position(7), Some((true, 1)));
|
||||
assert_eq!(sections.position(8), Some((false, 6)));
|
||||
assert_eq!(sections.custom.as_slice(), &[1, builtins + 1]);
|
||||
assert_eq!(
|
||||
sections.built_in.as_slice(),
|
||||
std::iter::once(0)
|
||||
.chain(2..=builtins)
|
||||
.chain(std::iter::once(builtins + 2))
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice()
|
||||
);
|
||||
assert_eq!(sections.position(builtins + 1), Some((true, 1)));
|
||||
assert_eq!(sections.position(builtins + 2), Some((false, builtins)));
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,38 @@ pub fn panel_select<'a>(
|
||||
cosmic::widget::container(content).class(surface(ferese_theme::mix(p.card, p.text, 0.035), 7.))
|
||||
}
|
||||
|
||||
/// Low-contrast section planes replace large filled cards. High contrast
|
||||
/// preferences strengthen boundaries without changing the section hierarchy.
|
||||
pub fn section(p: Palette) -> cosmic::theme::Container<'static> {
|
||||
cosmic::theme::Container::custom(move |_| cosmic::widget::container::Style {
|
||||
background: Some(ferese_theme::mix(p.background, p.card, 0.42).into()),
|
||||
border: cosmic::iced::Border {
|
||||
width: 1.,
|
||||
color: ferese_theme::mix(
|
||||
p.background,
|
||||
p.text,
|
||||
if p.high_contrast { 0.45 } else { 0.09 },
|
||||
),
|
||||
radius: p.radius.min(10.).into(),
|
||||
shape: cosmic::iced::border::Shape::Continuous,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
pub fn sidebar(p: Palette) -> cosmic::theme::Container<'static> {
|
||||
cosmic::theme::Container::custom(move |_| cosmic::widget::container::Style {
|
||||
background: Some(p.sidebar.into()),
|
||||
border: cosmic::iced::Border {
|
||||
width: 1.,
|
||||
color: ferese_theme::mix(p.sidebar, p.text, if p.high_contrast { 0.45 } else { 0.07 }),
|
||||
radius: 0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn color(value: &str, fallback: Color) -> Color {
|
||||
ferese_theme::parse_color(value).unwrap_or(fallback)
|
||||
}
|
||||
@@ -86,7 +118,7 @@ pub fn brand_icon() -> svg_icon::Icon {
|
||||
cosmic::widget::icon::from_svg_bytes(ferese_theme::icons::APPLICATION)
|
||||
.symbolic(false)
|
||||
.icon()
|
||||
.size(32)
|
||||
.size(24)
|
||||
.content_fit(cosmic::iced::ContentFit::Contain)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user