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
@@ -14,14 +14,6 @@ use std::{collections::HashMap, path::PathBuf};
|
||||
use store::{Edit, Snapshot, set};
|
||||
|
||||
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 path = match args.next() {
|
||||
Some(arg) if arg == "--config" => {
|
||||
@@ -92,6 +84,7 @@ struct App {
|
||||
ranges: HashMap<String, f64>,
|
||||
new_command: String,
|
||||
font: cosmic::font::Font,
|
||||
native_palette: visuals::Palette,
|
||||
thumbnail: Option<widget::image::Handle>,
|
||||
thumbnail_path: String,
|
||||
thumbnail_loading: bool,
|
||||
@@ -130,6 +123,7 @@ impl cosmic::Application for App {
|
||||
let error = initial.as_ref().err().cloned();
|
||||
let current = initial.unwrap_or_else(|_| Snapshot::parse(String::new()).unwrap());
|
||||
let font = visuals::configured_font(¤t);
|
||||
let native_palette = visuals::Palette::from(¤t);
|
||||
let mut app = Self {
|
||||
note_editors: HashMap::new(),
|
||||
core,
|
||||
@@ -148,6 +142,7 @@ impl cosmic::Application for App {
|
||||
ranges: HashMap::new(),
|
||||
new_command: String::new(),
|
||||
font,
|
||||
native_palette,
|
||||
thumbnail: None,
|
||||
thumbnail_path: String::new(),
|
||||
thumbnail_loading: false,
|
||||
@@ -241,16 +236,12 @@ impl cosmic::Application for App {
|
||||
self.error = None;
|
||||
self.status = "Updated from your config".into();
|
||||
|
||||
return Task::batch([
|
||||
cosmic::command::set_theme(visuals::native_theme(Some(&self.draft))),
|
||||
self.load_thumbnail(),
|
||||
]);
|
||||
return Task::batch([self.update_theme(), self.load_thumbnail()]);
|
||||
}
|
||||
}
|
||||
Err(error) => self.error = Some(format!("Config reload failed: {error}")),
|
||||
},
|
||||
Message::Page(page) => {
|
||||
self.sync_notes();
|
||||
self.page = page;
|
||||
self.search.clear();
|
||||
if page == Page::Wallpaper {
|
||||
@@ -321,10 +312,8 @@ impl cosmic::Application for App {
|
||||
}
|
||||
.into();
|
||||
|
||||
let theme = visuals::native_theme(Some(&self.draft));
|
||||
|
||||
return Task::batch([
|
||||
cosmic::command::set_theme(theme),
|
||||
self.update_theme(),
|
||||
self.flush(),
|
||||
self.load_thumbnail(),
|
||||
]);
|
||||
@@ -351,10 +340,7 @@ impl cosmic::Application for App {
|
||||
self.undo = None;
|
||||
self.status = "Reloaded from your config".into();
|
||||
|
||||
return Task::batch([
|
||||
cosmic::command::set_theme(visuals::native_theme(Some(&self.draft))),
|
||||
self.load_thumbnail(),
|
||||
]);
|
||||
return Task::batch([self.update_theme(), self.load_thumbnail()]);
|
||||
}
|
||||
Err(error) => self.error = Some(error),
|
||||
},
|
||||
@@ -591,28 +577,46 @@ impl cosmic::Application for App {
|
||||
);
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
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);
|
||||
for (row_index, choices) in visuals::PRESETS.chunks(3).enumerate() {
|
||||
let mut tiles = row([]).spacing(10);
|
||||
for (column_index, preset) in choices.iter().enumerate() {
|
||||
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(
|
||||
column([])
|
||||
.spacing(6)
|
||||
.push(visuals::swatches(index))
|
||||
.push(self.label(preset.name, 12.))
|
||||
.push(self.label(preset.description, 10.)),
|
||||
.spacing(8)
|
||||
.push(visuals::preset_preview(index))
|
||||
.push(
|
||||
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)
|
||||
.padding(10)
|
||||
.class(visuals::button_style(
|
||||
palette,
|
||||
visuals::preset_selected(&self.draft, index),
|
||||
));
|
||||
.class(visuals::button_style(palette, selected));
|
||||
tiles = tiles.push(if self.saving {
|
||||
tile
|
||||
} else {
|
||||
@@ -866,7 +870,22 @@ impl cosmic::Application for App {
|
||||
}
|
||||
|
||||
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([])
|
||||
.spacing(4)
|
||||
@@ -945,6 +964,15 @@ impl cosmic::Application for 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> {
|
||||
(0..self.draft.records("desktop_widgets.notes")).find(|index| {
|
||||
self.draft
|
||||
|
||||
@@ -7,10 +7,10 @@ use cosmic::{
|
||||
Element,
|
||||
iced::{Background, Border, Color, Length, Vector},
|
||||
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 background: 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.
|
||||
}
|
||||
|
||||
pub fn swatches(index: usize) -> Element<'static, Message> {
|
||||
let preset = &PRESETS[index];
|
||||
let mut row = widget::row([]).spacing(3);
|
||||
for value in [preset.base, preset.gradient_end, preset.accent] {
|
||||
row = row.push(
|
||||
container(widget::Space::new().width(12).height(22))
|
||||
.class(surface(color(value, Color::WHITE), 4.)),
|
||||
);
|
||||
}
|
||||
row.into()
|
||||
fn preset_preview_handle(preset: &Preset) -> svg_icon::Handle {
|
||||
let base = preset.base;
|
||||
let accent = preset.accent;
|
||||
let end = preset.gradient_end;
|
||||
let text = preset.text;
|
||||
let muted = preset.muted;
|
||||
let surface = color(base, Color::BLACK);
|
||||
let card = hex(mix(surface, surface_shade(surface), 0.055));
|
||||
let svg = format!(
|
||||
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> {
|
||||
|
||||
Reference in new issue
Block a user