Refine Settings performance, theme gallery, and shell popups
This commit is contained in:
1 parent
7e96a62e43
commit
d71fab7102
7 files changed
+313
-120
No files matched your search
@@ -73,6 +73,8 @@ Built-in defaults (your configuration can override them):
|
|||||||
The bar shows workspaces 1–9 in a compact numbered strip, including empty ones.
|
The bar shows workspaces 1–9 in a compact numbered strip, including empty ones.
|
||||||
Click a number to switch using the same behavior as Super+1–9. A filled button marks this monitor's
|
Click a number to switch using the same behavior as Super+1–9. A filled button marks this monitor's
|
||||||
active workspace; an outlined button marks one active on another monitor.
|
active workspace; an outlined button marks one active on another monitor.
|
||||||
|
Status controls and the clock use matching compact groups. The control center
|
||||||
|
opens as one rounded panel with grouped controls and a Settings shortcut.
|
||||||
Three-finger swipes up/down navigate workspaces on this monitor; left/right
|
Three-finger swipes up/down navigate workspaces on this monitor; left/right
|
||||||
navigate windows. Assign swipes to any keyboard-binding action (including
|
navigate windows. Assign swipes to any keyboard-binding action (including
|
||||||
overview, move, or a launcher command) in **Settings → Shortcuts**.
|
overview, move, or a launcher command) in **Settings → Shortcuts**.
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ Changing default width preserves manually resized columns.
|
|||||||
|
|
||||||
Settings → Appearance offers six presets: **Ferese Blue** (default),
|
Settings → Appearance offers six presets: **Ferese Blue** (default),
|
||||||
**Monochrome**, **Gruvbox**, **Dracula**, **Ayu Light**, and **Monokai**.
|
**Monochrome**, **Gruvbox**, **Dracula**, **Ayu Light**, and **Monokai**.
|
||||||
|
The two-row gallery previews each palette on a miniature desktop; a checkmark
|
||||||
|
identifies the active preset.
|
||||||
Ferese Blue uses the README logo's `#3D7BE6`. Each preset coordinates surfaces,
|
Ferese Blue uses the README logo's `#3D7BE6`. Each preset coordinates surfaces,
|
||||||
text, menu-bar colors, borders and the focus ring. Ayu Light also switches native
|
text, menu-bar colors, borders and the focus ring. Ayu Light also switches native
|
||||||
Settings controls to light styling. Presets preserve wallpaper, typography,
|
Settings controls to light styling. Presets preserve wallpaper, typography,
|
||||||
|
|||||||
@@ -14,14 +14,6 @@ use std::{collections::HashMap, path::PathBuf};
|
|||||||
use store::{Edit, Snapshot, set};
|
use store::{Edit, Snapshot, set};
|
||||||
|
|
||||||
fn main() -> cosmic::iced::Result {
|
fn main() -> cosmic::iced::Result {
|
||||||
// Set before libcosmic creates worker threads. The software backend is
|
|
||||||
// efficient for this mostly-static interface; WGPU remains a fallback.
|
|
||||||
if std::env::var_os("ICED_BACKEND").is_none() {
|
|
||||||
unsafe {
|
|
||||||
std::env::set_var("ICED_BACKEND", "tiny-skia,wgpu");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut args = std::env::args_os().skip(1);
|
let mut args = std::env::args_os().skip(1);
|
||||||
let path = match args.next() {
|
let path = match args.next() {
|
||||||
Some(arg) if arg == "--config" => {
|
Some(arg) if arg == "--config" => {
|
||||||
@@ -92,6 +84,7 @@ struct App {
|
|||||||
ranges: HashMap<String, f64>,
|
ranges: HashMap<String, f64>,
|
||||||
new_command: String,
|
new_command: String,
|
||||||
font: cosmic::font::Font,
|
font: cosmic::font::Font,
|
||||||
|
native_palette: visuals::Palette,
|
||||||
thumbnail: Option<widget::image::Handle>,
|
thumbnail: Option<widget::image::Handle>,
|
||||||
thumbnail_path: String,
|
thumbnail_path: String,
|
||||||
thumbnail_loading: bool,
|
thumbnail_loading: bool,
|
||||||
@@ -130,6 +123,7 @@ impl cosmic::Application for App {
|
|||||||
let error = initial.as_ref().err().cloned();
|
let error = initial.as_ref().err().cloned();
|
||||||
let current = initial.unwrap_or_else(|_| Snapshot::parse(String::new()).unwrap());
|
let current = initial.unwrap_or_else(|_| Snapshot::parse(String::new()).unwrap());
|
||||||
let font = visuals::configured_font(¤t);
|
let font = visuals::configured_font(¤t);
|
||||||
|
let native_palette = visuals::Palette::from(¤t);
|
||||||
let mut app = Self {
|
let mut app = Self {
|
||||||
note_editors: HashMap::new(),
|
note_editors: HashMap::new(),
|
||||||
core,
|
core,
|
||||||
@@ -148,6 +142,7 @@ impl cosmic::Application for App {
|
|||||||
ranges: HashMap::new(),
|
ranges: HashMap::new(),
|
||||||
new_command: String::new(),
|
new_command: String::new(),
|
||||||
font,
|
font,
|
||||||
|
native_palette,
|
||||||
thumbnail: None,
|
thumbnail: None,
|
||||||
thumbnail_path: String::new(),
|
thumbnail_path: String::new(),
|
||||||
thumbnail_loading: false,
|
thumbnail_loading: false,
|
||||||
@@ -241,16 +236,12 @@ impl cosmic::Application for App {
|
|||||||
self.error = None;
|
self.error = None;
|
||||||
self.status = "Updated from your config".into();
|
self.status = "Updated from your config".into();
|
||||||
|
|
||||||
return Task::batch([
|
return Task::batch([self.update_theme(), self.load_thumbnail()]);
|
||||||
cosmic::command::set_theme(visuals::native_theme(Some(&self.draft))),
|
|
||||||
self.load_thumbnail(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => self.error = Some(format!("Config reload failed: {error}")),
|
Err(error) => self.error = Some(format!("Config reload failed: {error}")),
|
||||||
},
|
},
|
||||||
Message::Page(page) => {
|
Message::Page(page) => {
|
||||||
self.sync_notes();
|
|
||||||
self.page = page;
|
self.page = page;
|
||||||
self.search.clear();
|
self.search.clear();
|
||||||
if page == Page::Wallpaper {
|
if page == Page::Wallpaper {
|
||||||
@@ -321,10 +312,8 @@ impl cosmic::Application for App {
|
|||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let theme = visuals::native_theme(Some(&self.draft));
|
|
||||||
|
|
||||||
return Task::batch([
|
return Task::batch([
|
||||||
cosmic::command::set_theme(theme),
|
self.update_theme(),
|
||||||
self.flush(),
|
self.flush(),
|
||||||
self.load_thumbnail(),
|
self.load_thumbnail(),
|
||||||
]);
|
]);
|
||||||
@@ -351,10 +340,7 @@ impl cosmic::Application for App {
|
|||||||
self.undo = None;
|
self.undo = None;
|
||||||
self.status = "Reloaded from your config".into();
|
self.status = "Reloaded from your config".into();
|
||||||
|
|
||||||
return Task::batch([
|
return Task::batch([self.update_theme(), self.load_thumbnail()]);
|
||||||
cosmic::command::set_theme(visuals::native_theme(Some(&self.draft))),
|
|
||||||
self.load_thumbnail(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
Err(error) => self.error = Some(error),
|
Err(error) => self.error = Some(error),
|
||||||
},
|
},
|
||||||
@@ -591,28 +577,46 @@ impl cosmic::Application for App {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if matches!(self.page, Page::Appearance | Page::Bar | Page::Windows) {
|
if matches!(self.page, Page::Bar | Page::Windows) {
|
||||||
body = body.push(visuals::preview(&self.draft));
|
body = body.push(visuals::preview(&self.draft));
|
||||||
}
|
}
|
||||||
if self.page == Page::Appearance {
|
if self.page == Page::Appearance {
|
||||||
|
body = body.push(
|
||||||
|
column([])
|
||||||
|
.spacing(4)
|
||||||
|
.push(self.label("Theme", 15.))
|
||||||
|
.push(self.note("Choose a look for your desktop.")),
|
||||||
|
);
|
||||||
let mut presets = column([]).spacing(10);
|
let mut presets = column([]).spacing(10);
|
||||||
for (row_index, choices) in visuals::PRESETS.chunks(3).enumerate() {
|
for (row_index, choices) in visuals::PRESETS.chunks(3).enumerate() {
|
||||||
let mut tiles = row([]).spacing(10);
|
let mut tiles = row([]).spacing(10);
|
||||||
for (column_index, preset) in choices.iter().enumerate() {
|
for (column_index, preset) in choices.iter().enumerate() {
|
||||||
let index = row_index * 3 + column_index;
|
let index = row_index * 3 + column_index;
|
||||||
|
let selected = visuals::preset_selected(&self.draft, index);
|
||||||
|
let indicator: Element<'_, Message> = if selected {
|
||||||
|
visuals::action_icon("M5 12l4 4L19 6", palette.accent).into()
|
||||||
|
} else {
|
||||||
|
widget::Space::new().width(16).height(16).into()
|
||||||
|
};
|
||||||
let tile = button::custom(
|
let tile = button::custom(
|
||||||
column([])
|
column([])
|
||||||
.spacing(6)
|
.spacing(8)
|
||||||
.push(visuals::swatches(index))
|
.push(visuals::preset_preview(index))
|
||||||
.push(self.label(preset.name, 12.))
|
.push(
|
||||||
.push(self.label(preset.description, 10.)),
|
row([])
|
||||||
|
.spacing(6)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.push(self.label(preset.name, 12.).width(Length::Fill))
|
||||||
|
.push(indicator),
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
self.label(preset.description, 10.)
|
||||||
|
.class(cosmic::theme::Text::Color(palette.muted)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.class(visuals::button_style(
|
.class(visuals::button_style(palette, selected));
|
||||||
palette,
|
|
||||||
visuals::preset_selected(&self.draft, index),
|
|
||||||
));
|
|
||||||
tiles = tiles.push(if self.saving {
|
tiles = tiles.push(if self.saving {
|
||||||
tile
|
tile
|
||||||
} else {
|
} else {
|
||||||
@@ -866,7 +870,22 @@ impl cosmic::Application for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
content = content.push(
|
content = content.push(
|
||||||
scrollable(container(body).padding([0, 2]).width(Length::Fill)).height(Length::Fill),
|
scrollable(
|
||||||
|
container(body)
|
||||||
|
.padding(cosmic::iced::Padding {
|
||||||
|
top: 0.,
|
||||||
|
right: 12.,
|
||||||
|
bottom: 0.,
|
||||||
|
left: 2.,
|
||||||
|
})
|
||||||
|
.width(Length::Fill),
|
||||||
|
)
|
||||||
|
.direction(cosmic::iced::widget::scrollable::Direction::Vertical(
|
||||||
|
cosmic::iced::widget::scrollable::Scrollbar::new()
|
||||||
|
.width(10)
|
||||||
|
.scroller_width(3),
|
||||||
|
))
|
||||||
|
.height(Length::Fill),
|
||||||
);
|
);
|
||||||
let footer = row([])
|
let footer = row([])
|
||||||
.spacing(4)
|
.spacing(4)
|
||||||
@@ -945,6 +964,15 @@ impl cosmic::Application for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
fn update_theme(&mut self) -> Task<Message> {
|
||||||
|
let palette = visuals::Palette::from(&self.draft);
|
||||||
|
if palette == self.native_palette {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
self.native_palette = palette;
|
||||||
|
cosmic::command::set_theme(visuals::native_theme(Some(&self.draft)))
|
||||||
|
}
|
||||||
|
|
||||||
fn note_index(&self, id: &str) -> Option<usize> {
|
fn note_index(&self, id: &str) -> Option<usize> {
|
||||||
(0..self.draft.records("desktop_widgets.notes")).find(|index| {
|
(0..self.draft.records("desktop_widgets.notes")).find(|index| {
|
||||||
self.draft
|
self.draft
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ use cosmic::{
|
|||||||
Element,
|
Element,
|
||||||
iced::{Background, Border, Color, Length, Vector},
|
iced::{Background, Border, Color, Length, Vector},
|
||||||
theme,
|
theme,
|
||||||
widget::{self, button, container, icon as svg_icon},
|
widget::{button, container, icon as svg_icon},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub struct Palette {
|
pub struct Palette {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub sidebar: Color,
|
pub sidebar: Color,
|
||||||
@@ -376,16 +376,53 @@ pub fn preset_selected(snapshot: &Snapshot, index: usize) -> bool {
|
|||||||
}) && snapshot.number("theme.focus_ring.gradient.angle", 0.) == 0.
|
}) && snapshot.number("theme.focus_ring.gradient.angle", 0.) == 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swatches(index: usize) -> Element<'static, Message> {
|
fn preset_preview_handle(preset: &Preset) -> svg_icon::Handle {
|
||||||
let preset = &PRESETS[index];
|
let base = preset.base;
|
||||||
let mut row = widget::row([]).spacing(3);
|
let accent = preset.accent;
|
||||||
for value in [preset.base, preset.gradient_end, preset.accent] {
|
let end = preset.gradient_end;
|
||||||
row = row.push(
|
let text = preset.text;
|
||||||
container(widget::Space::new().width(12).height(22))
|
let muted = preset.muted;
|
||||||
.class(surface(color(value, Color::WHITE), 4.)),
|
let surface = color(base, Color::BLACK);
|
||||||
);
|
let card = hex(mix(surface, surface_shade(surface), 0.055));
|
||||||
}
|
let svg = format!(
|
||||||
row.into()
|
r##"<svg xmlns="http://www.w3.org/2000/svg" width="240" height="132" viewBox="0 0 240 132">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="wall" x2="1" y2="1">
|
||||||
|
<stop stop-color="{accent}" stop-opacity=".24"/>
|
||||||
|
<stop offset="1" stop-color="{end}" stop-opacity=".06"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="240" height="132" rx="10" fill="{base}"/>
|
||||||
|
<rect width="240" height="132" rx="10" fill="url(#wall)"/>
|
||||||
|
<path d="M0 112Q52 48 120 93T240 67V132H0Z" fill="{accent}" opacity=".09"/>
|
||||||
|
<rect x="8" y="8" width="224" height="12" rx="4" fill="{base}"/>
|
||||||
|
<rect x="14" y="12" width="6" height="4" rx="1" fill="{accent}"/>
|
||||||
|
<path d="M26 14h4m4 0h4m164 0h6m4 0h6" stroke="{muted}" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
<rect x="16" y="34" width="121" height="82" rx="7" fill="{card}"/>
|
||||||
|
<path d="M26 44h102" stroke="{muted}" opacity=".16"/>
|
||||||
|
<rect x="23" y="50" width="25" height="59" rx="3" fill="{base}"/>
|
||||||
|
<rect x="26" y="57" width="19" height="7" rx="2" fill="{accent}" opacity=".6"/>
|
||||||
|
<path d="M29 73h11m-11 9h9m-9 9h12" stroke="{muted}" opacity=".6" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
<path d="M56 59h38m-38 10h63m-63 8h49m-49 8h58" stroke="{text}" opacity=".55" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
<rect x="55" y="94" width="29" height="9" rx="3" fill="{accent}"/>
|
||||||
|
<rect x="145" y="37" width="78" height="70" rx="7" fill="{card}" stroke="{accent}" stroke-width="1.2"/>
|
||||||
|
<circle cx="154" cy="45" r="1.5" fill="{accent}"/>
|
||||||
|
<path d="M161 45h17" stroke="{muted}" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
<path d="M155 58h27m-27 9h56m-56 9h37m-37 9h49m-49 9h30" stroke="{text}" opacity=".55" stroke-width="2" stroke-linecap="round"/>
|
||||||
|
</svg>"##,
|
||||||
|
);
|
||||||
|
svg_icon::from_svg_bytes(svg.into_bytes()).symbolic(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preset_preview(index: usize) -> Element<'static, Message> {
|
||||||
|
static HANDLES: std::sync::OnceLock<Vec<svg_icon::Handle>> = std::sync::OnceLock::new();
|
||||||
|
HANDLES.get_or_init(|| PRESETS.iter().map(preset_preview_handle).collect())[index]
|
||||||
|
.clone()
|
||||||
|
.icon()
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fixed(90.))
|
||||||
|
.content_fit(cosmic::iced::ContentFit::Contain)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preview(snapshot: &Snapshot) -> Element<'static, Message> {
|
pub fn preview(snapshot: &Snapshot) -> Element<'static, Message> {
|
||||||
|
|||||||
@@ -436,7 +436,11 @@ impl cosmic::Application for FereseShell {
|
|||||||
&& menu.id == id
|
&& menu.id == id
|
||||||
&& menu.effects.is_none()
|
&& menu.effects.is_none()
|
||||||
{
|
{
|
||||||
match EffectsBinding::attach_role(&surface, menu.kind.material_role()) {
|
match EffectsBinding::attach_role(
|
||||||
|
&surface,
|
||||||
|
menu.kind.material_role(),
|
||||||
|
menu.progress(),
|
||||||
|
) {
|
||||||
Ok(binding) => menu.effects = Some(binding),
|
Ok(binding) => menu.effects = Some(binding),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("ferese-shell: popover material unavailable: {error}")
|
eprintln!("ferese-shell: popover material unavailable: {error}")
|
||||||
@@ -1546,6 +1550,7 @@ impl FereseShell {
|
|||||||
match EffectsBinding::attach_role(
|
match EffectsBinding::attach_role(
|
||||||
&surface,
|
&surface,
|
||||||
menu.kind.material_role(),
|
menu.kind.material_role(),
|
||||||
|
menu.progress(),
|
||||||
) {
|
) {
|
||||||
Ok(binding) => menu.effects = Some(binding),
|
Ok(binding) => menu.effects = Some(binding),
|
||||||
Err(error) => eprintln!(
|
Err(error) => eprintln!(
|
||||||
@@ -1561,6 +1566,7 @@ impl FereseShell {
|
|||||||
match EffectsBinding::attach_role(
|
match EffectsBinding::attach_role(
|
||||||
&surface,
|
&surface,
|
||||||
menu.kind.material_role(),
|
menu.kind.material_role(),
|
||||||
|
menu.progress(),
|
||||||
) {
|
) {
|
||||||
Ok(binding) => menu.effects = Some(binding),
|
Ok(binding) => menu.effects = Some(binding),
|
||||||
Err(error) => eprintln!(
|
Err(error) => eprintln!(
|
||||||
@@ -1593,7 +1599,11 @@ impl FereseShell {
|
|||||||
))) if self.menu.as_ref().is_some_and(|menu| menu.id == frame_id) => {
|
))) if self.menu.as_ref().is_some_and(|menu| menu.id == frame_id) => {
|
||||||
if let Some(menu) = &mut self.menu {
|
if let Some(menu) = &mut self.menu {
|
||||||
if menu.effects.is_none() {
|
if menu.effects.is_none() {
|
||||||
match EffectsBinding::attach_role(&surface, menu.kind.material_role()) {
|
match EffectsBinding::attach_role(
|
||||||
|
&surface,
|
||||||
|
menu.kind.material_role(),
|
||||||
|
menu.progress(),
|
||||||
|
) {
|
||||||
Ok(binding) => menu.effects = Some(binding),
|
Ok(binding) => menu.effects = Some(binding),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("ferese-shell: popover material unavailable: {error}")
|
eprintln!("ferese-shell: popover material unavailable: {error}")
|
||||||
@@ -1657,7 +1667,7 @@ impl FereseShell {
|
|||||||
.align_y(cosmic::iced::Alignment::Center);
|
.align_y(cosmic::iced::Alignment::Center);
|
||||||
|
|
||||||
workspace_row = workspace_row.push(motion::button(
|
workspace_row = workspace_row.push(motion::button(
|
||||||
button::custom(overview_control(bar, color(shell_theme.text_primary)))
|
button::custom(overview_control(bar, color(shell_theme.accent)))
|
||||||
.height(bar.control_height)
|
.height(bar.control_height)
|
||||||
.padding([0, 7])
|
.padding([0, 7])
|
||||||
.on_press(cosmic::Action::App(Message::ToggleOverview)),
|
.on_press(cosmic::Action::App(Message::ToggleOverview)),
|
||||||
@@ -1715,18 +1725,7 @@ impl FereseShell {
|
|||||||
workspace_buttons = workspace_buttons.push(selector);
|
workspace_buttons = workspace_buttons.push(selector);
|
||||||
}
|
}
|
||||||
workspace_row = workspace_row.push(container(workspace_buttons).padding([0, 3]).class(
|
workspace_row = workspace_row.push(container(workspace_buttons).padding([0, 3]).class(
|
||||||
theme::Container::custom(move |_| container::Style {
|
theme::Container::custom(move |_| bar_group_style(shell_theme)),
|
||||||
background: Some(Background::Color(color_with_opacity(
|
|
||||||
shell_theme.text_primary,
|
|
||||||
0.035,
|
|
||||||
))),
|
|
||||||
border: Border {
|
|
||||||
color: color_with_opacity(shell_theme.text_muted, 0.12),
|
|
||||||
width: 1.0,
|
|
||||||
radius: 7.0.into(),
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let foreground = color(shell_theme.text_primary);
|
let foreground = color(shell_theme.text_primary);
|
||||||
@@ -1738,13 +1737,25 @@ impl FereseShell {
|
|||||||
))
|
))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(bar.control_height);
|
.height(bar.control_height);
|
||||||
|
let (date, time) = self.clock.split_once(", ").unwrap_or(("", &self.clock));
|
||||||
let clock = container(
|
let clock = container(
|
||||||
text(&self.clock)
|
row![
|
||||||
.size(bar.text_size)
|
text(date)
|
||||||
.wrapping(cosmic::iced::core::text::Wrapping::None)
|
.size(12)
|
||||||
.class(theme::Text::Color(foreground)),
|
.class(theme::Text::Color(color(shell_theme.text_muted))),
|
||||||
|
text(time)
|
||||||
|
.size(bar.text_size)
|
||||||
|
.class(theme::Text::Color(foreground)),
|
||||||
|
]
|
||||||
|
.spacing(8)
|
||||||
|
.align_y(cosmic::iced::Alignment::Center),
|
||||||
)
|
)
|
||||||
.padding([0, 6]);
|
.height(bar.control_height)
|
||||||
|
.align_y(alignment::Vertical::Center)
|
||||||
|
.padding([0, 8])
|
||||||
|
.class(theme::Container::custom(move |_| {
|
||||||
|
bar_group_style(shell_theme)
|
||||||
|
}));
|
||||||
let right = row![
|
let right = row![
|
||||||
self.view_status_bar().map(move |action| match action {
|
self.view_status_bar().map(move |action| match action {
|
||||||
cosmic::Action::App(Message::OpenMenu(kind, anchor)) =>
|
cosmic::Action::App(Message::OpenMenu(kind, anchor)) =>
|
||||||
@@ -1753,7 +1764,7 @@ impl FereseShell {
|
|||||||
}),
|
}),
|
||||||
clock
|
clock
|
||||||
]
|
]
|
||||||
.spacing(6)
|
.spacing(8)
|
||||||
.align_y(cosmic::iced::Alignment::Center);
|
.align_y(cosmic::iced::Alignment::Center);
|
||||||
let content = row![
|
let content = row![
|
||||||
container(left).width(Length::Fill),
|
container(left).width(Length::Fill),
|
||||||
@@ -2127,6 +2138,21 @@ fn bar_style(theme: ShellTheme, compositor_material: bool) -> container::Style {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bar_group_style(theme: ShellTheme) -> container::Style {
|
||||||
|
container::Style {
|
||||||
|
background: Some(Background::Color(color_with_opacity(
|
||||||
|
theme.text_primary,
|
||||||
|
0.035,
|
||||||
|
))),
|
||||||
|
border: Border {
|
||||||
|
color: color_with_opacity(theme.text_muted, 0.12),
|
||||||
|
width: 1.0,
|
||||||
|
radius: 7.0.into(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn color([red, green, blue, alpha]: [u8; 4]) -> Color {
|
fn color([red, green, blue, alpha]: [u8; 4]) -> Color {
|
||||||
Color::from_rgba8(red, green, blue, f32::from(alpha) / 255.0)
|
Color::from_rgba8(red, green, blue, f32::from(alpha) / 255.0)
|
||||||
}
|
}
|
||||||
@@ -2195,12 +2221,14 @@ impl EffectsBinding {
|
|||||||
Self::attach_role(
|
Self::attach_role(
|
||||||
surface,
|
surface,
|
||||||
visible.then_some(ferese_surface_effects_v1::Role::Panel),
|
visible.then_some(ferese_surface_effects_v1::Role::Panel),
|
||||||
|
if visible { 1.0 } else { 0.0 },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attach_role(
|
fn attach_role(
|
||||||
surface: &wl_surface::WlSurface,
|
surface: &wl_surface::WlSurface,
|
||||||
role: Option<ferese_surface_effects_v1::Role>,
|
role: Option<ferese_surface_effects_v1::Role>,
|
||||||
|
opacity: f32,
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let backend = surface
|
let backend = surface
|
||||||
.backend()
|
.backend()
|
||||||
@@ -2212,13 +2240,14 @@ impl EffectsBinding {
|
|||||||
let manager = globals.bind::<FereseEffectsManagerV1, _, _>(&qh, 1..=3, ())?;
|
let manager = globals.bind::<FereseEffectsManagerV1, _, _>(&qh, 1..=3, ())?;
|
||||||
let effects = manager.get_surface_effects(surface, &qh, ());
|
let effects = manager.get_surface_effects(surface, &qh, ());
|
||||||
|
|
||||||
|
let opacity = (opacity.clamp(0.0, 1.0) * 1000.0).round() as u32;
|
||||||
|
if effects.version() >= 3 {
|
||||||
|
effects.set_opacity(opacity);
|
||||||
|
}
|
||||||
if let Some(role) = role {
|
if let Some(role) = role {
|
||||||
effects.set_role(role);
|
effects.set_role(role);
|
||||||
} else {
|
} else {
|
||||||
effects.clear_role();
|
effects.clear_role();
|
||||||
if effects.version() >= 3 {
|
|
||||||
effects.set_opacity(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
connection.flush()?;
|
connection.flush()?;
|
||||||
|
|
||||||
@@ -2228,7 +2257,7 @@ impl EffectsBinding {
|
|||||||
surface: effects,
|
surface: effects,
|
||||||
_queue: queue,
|
_queue: queue,
|
||||||
regions: Default::default(),
|
regions: Default::default(),
|
||||||
opacity: std::cell::Cell::new(None),
|
opacity: std::cell::Cell::new(Some(opacity)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -351,11 +351,13 @@ pub fn animated<'a, M: 'a>(
|
|||||||
content: Element<'a, M>,
|
content: Element<'a, M>,
|
||||||
progress: f32,
|
progress: f32,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
|
radius: f32,
|
||||||
) -> Element<'a, M> {
|
) -> Element<'a, M> {
|
||||||
Element::new(Motion {
|
Element::new(Motion {
|
||||||
content,
|
content,
|
||||||
progress,
|
progress,
|
||||||
regions,
|
regions,
|
||||||
|
radius,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +365,7 @@ struct Motion<'a, M> {
|
|||||||
content: Element<'a, M>,
|
content: Element<'a, M>,
|
||||||
progress: f32,
|
progress: f32,
|
||||||
regions: Regions,
|
regions: Regions,
|
||||||
|
radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> Motion<'_, M> {
|
impl<M> Motion<'_, M> {
|
||||||
@@ -442,7 +445,7 @@ impl<M> Widget<M, Theme, cosmic::Renderer> for Motion<'_, M> {
|
|||||||
(r.y - origin.y + translation.y).round() as i32,
|
(r.y - origin.y + translation.y).round() as i32,
|
||||||
r.width.round() as i32,
|
r.width.round() as i32,
|
||||||
r.height.round() as i32,
|
r.height.round() as i32,
|
||||||
11,
|
self.radius.round().max(0.0) as i32,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -23,16 +23,25 @@ pub enum Menu {
|
|||||||
impl Menu {
|
impl Menu {
|
||||||
fn width(self) -> f32 {
|
fn width(self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::System => 320.0,
|
Self::System => 360.0,
|
||||||
Self::Network | Self::Bluetooth => 280.0,
|
Self::Battery => 280.0,
|
||||||
Self::Battery => 250.0,
|
_ => 300.0,
|
||||||
Self::Audio | Self::Notifications => 290.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn material_role(self) -> Option<ferese_surface_effects_v1::Role> {
|
pub(super) fn material_role(self) -> Option<ferese_surface_effects_v1::Role> {
|
||||||
// Popovers contain independent cards, with no shared material backing.
|
Some(ferese_surface_effects_v1::Role::Popover)
|
||||||
None
|
}
|
||||||
|
|
||||||
|
fn title(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::System => "Control Center",
|
||||||
|
Self::Network => "Wi-Fi",
|
||||||
|
Self::Bluetooth => "Bluetooth",
|
||||||
|
Self::Audio => "Sound",
|
||||||
|
Self::Battery => "Battery",
|
||||||
|
Self::Notifications => "Notifications",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn available(self, status: &Snapshot) -> bool {
|
fn available(self, status: &Snapshot) -> bool {
|
||||||
@@ -93,6 +102,28 @@ impl FereseShell {
|
|||||||
super::EFFECT_FRAME_PENDING.store(true, std::sync::atomic::Ordering::Relaxed);
|
super::EFFECT_FRAME_PENDING.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
self.status_error = None;
|
self.status_error = None;
|
||||||
let parent = self.bar_surface_id;
|
let parent = self.bar_surface_id;
|
||||||
|
let anchor = if kind == Menu::System {
|
||||||
|
self.outputs
|
||||||
|
.iter()
|
||||||
|
.find(|output| output.bar == parent)
|
||||||
|
.and_then(|output| output.size)
|
||||||
|
.map(|(width, _)| {
|
||||||
|
let theme = self.config.theme;
|
||||||
|
Rectangle {
|
||||||
|
x: (width
|
||||||
|
- theme.bar_margin_horizontal * 2
|
||||||
|
- theme.panel_padding.round() as i32
|
||||||
|
- 1)
|
||||||
|
.max(0),
|
||||||
|
y: 0,
|
||||||
|
width: 1,
|
||||||
|
height: theme.bar_height.round() as i32,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(anchor)
|
||||||
|
} else {
|
||||||
|
anchor
|
||||||
|
};
|
||||||
let action = cosmic::surface::action::app_popup::<Self>(
|
let action = cosmic::surface::action::app_popup::<Self>(
|
||||||
|_| Default::default(),
|
|_| Default::default(),
|
||||||
move |_| SctkPopupSettings {
|
move |_| SctkPopupSettings {
|
||||||
@@ -107,7 +138,7 @@ impl FereseShell {
|
|||||||
// Numeric protocol values avoid depending on libcosmic's private SCTK reexport.
|
// Numeric protocol values avoid depending on libcosmic's private SCTK reexport.
|
||||||
anchor: 2u32.try_into().unwrap(), // bottom
|
anchor: 2u32.try_into().unwrap(), // bottom
|
||||||
gravity: 6u32.try_into().unwrap(), // bottom-left
|
gravity: 6u32.try_into().unwrap(), // bottom-left
|
||||||
offset: (anchor.width / 2, 4),
|
offset: (anchor.width / 2, 8),
|
||||||
size_limits: Limits::NONE
|
size_limits: Limits::NONE
|
||||||
.min_width(kind.width())
|
.min_width(kind.width())
|
||||||
.max_width(kind.width())
|
.max_width(kind.width())
|
||||||
@@ -209,8 +240,15 @@ impl FereseShell {
|
|||||||
if !kind.available(&self.status) {
|
if !kind.available(&self.status) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (source, _) = status_icon(kind, &self.status);
|
let (source, enabled) = status_icon(kind, &self.status);
|
||||||
let foreground = color(theme.text_primary);
|
let selected = self.menu.as_ref().is_some_and(|m| m.kind == kind);
|
||||||
|
let foreground = color(if selected {
|
||||||
|
theme.accent
|
||||||
|
} else if enabled {
|
||||||
|
theme.text_primary
|
||||||
|
} else {
|
||||||
|
theme.text_muted
|
||||||
|
});
|
||||||
let mut content = row![accented_icon(
|
let mut content = row![accented_icon(
|
||||||
source,
|
source,
|
||||||
metrics.icon_size,
|
metrics.icon_size,
|
||||||
@@ -246,12 +284,11 @@ impl FereseShell {
|
|||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let selected = self.menu.as_ref().is_some_and(|m| m.kind == kind);
|
|
||||||
// A fixed button height does not center its child in libcosmic.
|
// A fixed button height does not center its child in libcosmic.
|
||||||
// Keep the visual content centered inside the entire click target.
|
// Keep the visual content centered inside the entire click target.
|
||||||
let content = container(content)
|
let content = container(content)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.align_x(alignment::Horizontal::Center)
|
||||||
.height(metrics.height)
|
.height(metrics.control_height)
|
||||||
.align_y(alignment::Vertical::Center);
|
.align_y(alignment::Vertical::Center);
|
||||||
let control = button::custom(content)
|
let control = button::custom(content)
|
||||||
.name(status_label(kind, &self.status))
|
.name(status_label(kind, &self.status))
|
||||||
@@ -259,7 +296,7 @@ impl FereseShell {
|
|||||||
0.0,
|
0.0,
|
||||||
((metrics.height - f32::from(metrics.icon_size)) * 0.5).max(4.0),
|
((metrics.height - f32::from(metrics.icon_size)) * 0.5).max(4.0),
|
||||||
])
|
])
|
||||||
.height(metrics.height)
|
.height(metrics.control_height)
|
||||||
.on_press_with_rectangle(move |offset, bounds| {
|
.on_press_with_rectangle(move |offset, bounds| {
|
||||||
cosmic::Action::App(Message::OpenMenu(
|
cosmic::Action::App(Message::OpenMenu(
|
||||||
kind,
|
kind,
|
||||||
@@ -273,7 +310,10 @@ impl FereseShell {
|
|||||||
});
|
});
|
||||||
controls = controls.push(motion::button(control, foreground, selected, 1.0));
|
controls = controls.push(motion::button(control, foreground, selected, 1.0));
|
||||||
}
|
}
|
||||||
controls.into()
|
container(controls)
|
||||||
|
.padding([0, 3])
|
||||||
|
.class(theme::Container::custom(move |_| bar_group_style(theme)))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view_status_menu(&self) -> Element<'_, cosmic::Action<Message>> {
|
fn view_status_menu(&self) -> Element<'_, cosmic::Action<Message>> {
|
||||||
@@ -281,12 +321,42 @@ impl FereseShell {
|
|||||||
return text("").into();
|
return text("").into();
|
||||||
};
|
};
|
||||||
let theme = self.config.theme;
|
let theme = self.config.theme;
|
||||||
// The compositor fades the complete surface, not individual caches.
|
let p = if menu.effects.is_some() {
|
||||||
let p = 1.0;
|
1.0
|
||||||
|
} else {
|
||||||
|
menu.progress()
|
||||||
|
};
|
||||||
let kind = menu.kind;
|
let kind = menu.kind;
|
||||||
let primary = color_with_opacity(theme.text_primary, p);
|
let primary = color_with_opacity(theme.text_primary, p);
|
||||||
let muted = color_with_opacity(theme.text_muted, p);
|
let muted = color_with_opacity(theme.text_muted, p);
|
||||||
let mut rows = column::with_capacity(12).spacing(8).width(Length::Fill);
|
let badge = container(accented_icon(
|
||||||
|
status_icon(kind, &self.status).0,
|
||||||
|
22,
|
||||||
|
primary,
|
||||||
|
color_with_opacity(theme.accent, p),
|
||||||
|
))
|
||||||
|
.width(36)
|
||||||
|
.height(36)
|
||||||
|
.align_x(alignment::Horizontal::Center)
|
||||||
|
.align_y(alignment::Vertical::Center)
|
||||||
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
|
background: Some(Background::Color(color_with_opacity(
|
||||||
|
theme.accent,
|
||||||
|
0.12 * p,
|
||||||
|
))),
|
||||||
|
border: Border {
|
||||||
|
radius: 10.0.into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}));
|
||||||
|
let heading = row![badge, text(kind.title()).size(18)]
|
||||||
|
.spacing(10)
|
||||||
|
.align_y(Alignment::Center);
|
||||||
|
let mut rows = column::with_capacity(12)
|
||||||
|
.push(heading)
|
||||||
|
.spacing(12)
|
||||||
|
.width(Length::Fill);
|
||||||
if let Some(action) = &menu.confirm {
|
if let Some(action) = &menu.confirm {
|
||||||
let title = match action {
|
let title = match action {
|
||||||
Action::Poweroff => "Power off this computer?",
|
Action::Poweroff => "Power off this computer?",
|
||||||
@@ -577,7 +647,8 @@ impl FereseShell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if combined {
|
if combined {
|
||||||
let mut actions = row::with_capacity(4).spacing(4);
|
let mut actions = column::with_capacity(2).spacing(8);
|
||||||
|
let mut power = row::with_capacity(3).spacing(6).width(Length::Fill);
|
||||||
if self
|
if self
|
||||||
.config
|
.config
|
||||||
.status
|
.status
|
||||||
@@ -586,15 +657,15 @@ impl FereseShell {
|
|||||||
.and_then(|a| a.first())
|
.and_then(|a| a.first())
|
||||||
.is_some_and(|p| status::available(p))
|
.is_some_and(|p| status::available(p))
|
||||||
{
|
{
|
||||||
actions = actions.push(menu_button(
|
actions = actions.push(footer_button(
|
||||||
"Settings",
|
"Open Settings",
|
||||||
Message::Control(Action::Settings),
|
Message::Control(Action::Settings),
|
||||||
primary,
|
primary,
|
||||||
p,
|
p,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if self.status.poweroff {
|
if self.status.poweroff {
|
||||||
actions = actions.push(menu_button(
|
power = power.push(footer_button(
|
||||||
"Power off…",
|
"Power off…",
|
||||||
Message::ConfirmPower(Action::Poweroff),
|
Message::ConfirmPower(Action::Poweroff),
|
||||||
primary,
|
primary,
|
||||||
@@ -602,7 +673,7 @@ impl FereseShell {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if self.status.reboot {
|
if self.status.reboot {
|
||||||
actions = actions.push(menu_button(
|
power = power.push(footer_button(
|
||||||
"Restart…",
|
"Restart…",
|
||||||
Message::ConfirmPower(Action::Reboot),
|
Message::ConfirmPower(Action::Reboot),
|
||||||
primary,
|
primary,
|
||||||
@@ -610,14 +681,17 @@ impl FereseShell {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if self.status.suspend {
|
if self.status.suspend {
|
||||||
actions = actions.push(menu_button(
|
power = power.push(footer_button(
|
||||||
"Suspend…",
|
"Suspend…",
|
||||||
Message::ConfirmPower(Action::Suspend),
|
Message::ConfirmPower(Action::Suspend),
|
||||||
primary,
|
primary,
|
||||||
p,
|
p,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
rows = rows.push(control_card(actions.wrap().into(), primary, p));
|
if self.status.poweroff || self.status.reboot || self.status.suspend {
|
||||||
|
actions = actions.push(power);
|
||||||
|
}
|
||||||
|
rows = rows.push(control_card(actions.into(), primary, p));
|
||||||
}
|
}
|
||||||
if !menu.kind.available(&self.status) {
|
if !menu.kind.available(&self.status) {
|
||||||
rows = rows.push(
|
rows = rows.push(
|
||||||
@@ -633,28 +707,32 @@ impl FereseShell {
|
|||||||
..Color::from_rgb8(230, 172, 90)
|
..Color::from_rgb8(230, 172, 90)
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
let content: Element<'_, cosmic::Action<Message>> = if kind == Menu::System {
|
let compositor_material = menu.effects.is_some();
|
||||||
rows.into()
|
let panel = container(rows)
|
||||||
} else {
|
.id("ferese-blur-card")
|
||||||
control_card(rows.into(), primary, p)
|
.width(kind.width())
|
||||||
};
|
.padding(16)
|
||||||
let panel =
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
container(content)
|
background: (!compositor_material).then_some(Background::Color(
|
||||||
.width(kind.width())
|
color_with_opacity(theme.surface_popover, p),
|
||||||
.padding(12)
|
)),
|
||||||
.class(theme::Container::custom(move |_| container::Style {
|
text_color: Some(primary),
|
||||||
background: None,
|
icon_color: Some(primary),
|
||||||
text_color: Some(primary),
|
border: Border {
|
||||||
icon_color: Some(primary),
|
color: color_with_opacity(theme.text_muted, 0.18 * p),
|
||||||
border: Border {
|
width: 1.0,
|
||||||
radius: theme.material_radius.into(),
|
radius: theme.material_radius.into(),
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
},
|
||||||
// Keep the outer surface transparent; only inner cards paint a background.
|
..Default::default()
|
||||||
|
}));
|
||||||
cosmic::widget::autosize::autosize(
|
cosmic::widget::autosize::autosize(
|
||||||
super::motion::animated(panel.into(), menu.progress(), menu.regions.clone()),
|
super::motion::animated(
|
||||||
|
panel.into(),
|
||||||
|
menu.progress(),
|
||||||
|
menu.regions.clone(),
|
||||||
|
theme.material_radius,
|
||||||
|
),
|
||||||
cosmic::iced::advanced::widget::Id::new("ferese-status-menu"),
|
cosmic::iced::advanced::widget::Id::new("ferese-status-menu"),
|
||||||
)
|
)
|
||||||
.limits(
|
.limits(
|
||||||
@@ -768,7 +846,6 @@ fn control_card<'a>(
|
|||||||
opacity: f32,
|
opacity: f32,
|
||||||
) -> Element<'a, cosmic::Action<Message>> {
|
) -> Element<'a, cosmic::Action<Message>> {
|
||||||
container(content)
|
container(content)
|
||||||
.id("ferese-blur-card")
|
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.class(theme::Container::custom(move |_| container::Style {
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
@@ -837,7 +914,6 @@ fn connection_control<'a>(
|
|||||||
// giving it a misleading clickable/disabled-button appearance.
|
// giving it a misleading clickable/disabled-button appearance.
|
||||||
let Some(action) = action else {
|
let Some(action) = action else {
|
||||||
return container(content)
|
return container(content)
|
||||||
.id("ferese-blur-card")
|
|
||||||
.width(Length::FillPortion(1))
|
.width(Length::FillPortion(1))
|
||||||
.padding(4)
|
.padding(4)
|
||||||
.into();
|
.into();
|
||||||
@@ -851,7 +927,6 @@ fn connection_control<'a>(
|
|||||||
false,
|
false,
|
||||||
opacity,
|
opacity,
|
||||||
))
|
))
|
||||||
.id("ferese-blur-card")
|
|
||||||
.width(Length::FillPortion(1))
|
.width(Length::FillPortion(1))
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@@ -872,6 +947,23 @@ fn menu_button<'a>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn footer_button<'a>(
|
||||||
|
label: &'a str,
|
||||||
|
message: Message,
|
||||||
|
foreground: Color,
|
||||||
|
opacity: f32,
|
||||||
|
) -> Element<'a, cosmic::Action<Message>> {
|
||||||
|
motion::button(
|
||||||
|
button::custom(text(label).size(12))
|
||||||
|
.width(Length::Fill)
|
||||||
|
.padding([8, 6])
|
||||||
|
.on_press(cosmic::Action::App(message)),
|
||||||
|
foreground,
|
||||||
|
false,
|
||||||
|
opacity,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn toggle_row<'a>(
|
fn toggle_row<'a>(
|
||||||
label: &'a str,
|
label: &'a str,
|
||||||
on: bool,
|
on: bool,
|
||||||
@@ -1081,12 +1173,12 @@ fn status_icon(kind: Menu, s: &Snapshot) -> (&'static [u8], bool) {
|
|||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn smaller_popups_have_content_specific_widths() {
|
fn smaller_popups_have_content_specific_widths() {
|
||||||
assert_eq!(Menu::System.width(), 320.0);
|
assert_eq!(Menu::System.width(), 360.0);
|
||||||
assert_eq!(Menu::Network.width(), 280.0);
|
assert_eq!(Menu::Network.width(), 300.0);
|
||||||
assert_eq!(Menu::Bluetooth.width(), 280.0);
|
assert_eq!(Menu::Bluetooth.width(), 300.0);
|
||||||
assert_eq!(Menu::Battery.width(), 250.0);
|
assert_eq!(Menu::Battery.width(), 280.0);
|
||||||
assert_eq!(Menu::Audio.width(), 290.0);
|
assert_eq!(Menu::Audio.width(), 300.0);
|
||||||
assert_eq!(Menu::Notifications.width(), 290.0);
|
assert_eq!(Menu::Notifications.width(), 300.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user