From 0c7c1f1f5dcee10ab5f6b5789e37cf6310e7f878 Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Thu, 3 Oct 2024 14:20:08 +0200 Subject: [PATCH] improv: move available themes to context drawer --- Cargo.lock | 83 +++++ Cargo.toml | 4 + build.rs | 17 + i18n/en/cosmic_ext_tweaks.ftl | 20 ++ res/icons/bundled/list-add-symbolic.svg | 3 + res/icons/bundled/search-global-symbolic.svg | 2 + src/app.rs | 336 ++++++++++++++++++- src/app/key_bind.rs | 28 ++ src/core/config.rs | 10 +- src/core/icons.rs | 1 + src/main.rs | 5 +- src/pages/color_schemes.rs | 122 +------ src/pages/color_schemes/preview.rs | 19 +- src/settings.rs | 58 ++++ 14 files changed, 575 insertions(+), 133 deletions(-) create mode 100644 build.rs create mode 100644 res/icons/bundled/list-add-symbolic.svg create mode 100644 res/icons/bundled/search-global-symbolic.svg create mode 100644 src/app/key_bind.rs create mode 100644 src/settings.rs diff --git a/Cargo.lock b/Cargo.lock index 00403d6..d80db11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1126,12 +1126,14 @@ dependencies = [ "log", "once_cell", "open", + "paste", "pretty_env_logger", "reqwest", "ron", "rust-embed", "serde", "tokio", + "vergen", ] [[package]] @@ -1349,6 +1351,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -3482,6 +3493,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -3554,6 +3571,15 @@ dependencies = [ "syn 2.0.63", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -4013,6 +4039,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4534,6 +4566,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "rustybuzz" version = "0.12.1" @@ -5157,6 +5195,39 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -5663,6 +5734,18 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vergen" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +dependencies = [ + "anyhow", + "cfg-if", + "rustversion", + "time", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 0ccdfaa..d6e300e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ pretty_env_logger = "0.5.0" reqwest = { version = "0.12", features = ["json"] } ron = "0.8.1" rust-embed = "8.3.0" +paste = "1.0" [dependencies.libcosmic] git = "https://github.com/pop-os/libcosmic.git" @@ -36,3 +37,6 @@ features = ["derive"] [dependencies.tokio] version = "1.35.1" features = ["macros", "fs", "rt"] + +[build-dependencies] +vergen = { version = "8", features = ["git", "gitcl"] } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..be10241 --- /dev/null +++ b/build.rs @@ -0,0 +1,17 @@ +fn main() -> Result<(), Box> { + // Rebuild if i18n files change + println!("cargo:rerun-if-changed=i18n"); + + // Emit version information (if not cached by just vendor) + let mut vergen = vergen::EmitBuilder::builder(); + println!("cargo:rerun-if-env-changed=VERGEN_GIT_COMMIT_DATE"); + if std::env::var_os("VERGEN_GIT_COMMIT_DATE").is_none() { + vergen.git_commit_date(); + } + println!("cargo:rerun-if-env-changed=VERGEN_GIT_SHA"); + if std::env::var_os("VERGEN_GIT_SHA").is_none() { + vergen.git_sha(false); + } + vergen.fail_on_error().emit()?; + Ok(()) +} diff --git a/i18n/en/cosmic_ext_tweaks.ftl b/i18n/en/cosmic_ext_tweaks.ftl index 4098643..066762c 100644 --- a/i18n/en/cosmic_ext_tweaks.ftl +++ b/i18n/en/cosmic_ext_tweaks.ftl @@ -9,12 +9,16 @@ color-schemes-error = Error loading color schemes import-color-scheme = Import color scheme delete-color-scheme = Delete color scheme install-color-scheme = Install color scheme +find-color-schemes = Find color schemes open-containing-folder = Open containing folder open-link = Open link installed = Installed available = Available loading = Loading... show-more = Show more +settings = Settings +about = About +find-themes = Find themes # Panel only show-panel = Show panel @@ -30,3 +34,19 @@ save = Save cancel = Cancel save-current-color-scheme = Save current color scheme color-scheme-name = Color scheme name + +## About +git-description = Git commit {$hash} on {$date} + +## Settings +settings = Settings + +### Appearance +appearance = Appearance +theme = Theme +match-desktop = Match desktop +dark = Dark +light = Light + +# Menu +view = View diff --git a/res/icons/bundled/list-add-symbolic.svg b/res/icons/bundled/list-add-symbolic.svg new file mode 100644 index 0000000..59b2fb0 --- /dev/null +++ b/res/icons/bundled/list-add-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/icons/bundled/search-global-symbolic.svg b/res/icons/bundled/search-global-symbolic.svg new file mode 100644 index 0000000..ba27c18 --- /dev/null +++ b/res/icons/bundled/search-global-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/app.rs b/src/app.rs index 397796c..c3a3fc8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,27 +1,52 @@ -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; use cosmic::{ app::{self, Core}, + cosmic_config, iced::{Alignment, Command, Length}, - widget::{self, segmented_button}, - Application, Element, + widget::{ + self, + menu::{self, KeyBind}, + segmented_button, + }, + Application, ApplicationExt, Apply, Element, }; +use key_bind::key_binds; +use pages::color_schemes::providers::cosmic_themes::CosmicTheme; use crate::{ core::nav::NavPage, fl, pages::{ self, - color_schemes::{ColorSchemeProvider, ColorSchemes}, + color_schemes::{config::ColorScheme, preview, ColorSchemeProvider, ColorSchemes}, }, + settings::{AppTheme, TweaksSettings}, }; +mod key_bind; + pub struct TweakTool { core: Core, nav_model: segmented_button::SingleSelectModel, dialog_pages: VecDeque, dialog_text_input: widget::Id, + key_binds: HashMap, color_schemes: ColorSchemes, + context_page: ContextPage, + app_themes: Vec, + config_handler: Option, + config: TweaksSettings, + available: Vec, + status: Status, + limit: usize, + offset: usize, +} + +pub enum Status { + Idle, + Loading, + LoadingMore, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -39,12 +64,56 @@ pub enum Message { DialogComplete, DialogCancel, SaveNewColorScheme(String), + ToggleContextPage(ContextPage), + LaunchUrl(String), + AppTheme(usize), + FetchAvailableColorSchemes(ColorSchemeProvider, usize), + SetAvailableColorSchemes(Vec), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum ContextPage { + AvailableThemes, + Settings, + About, +} + +impl ContextPage { + fn title(&self) -> String { + match self { + Self::About => fl!("about"), + Self::Settings => fl!("settings"), + Self::AvailableThemes => fl!("available"), + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Action { + About, + Settings, +} + +impl cosmic::widget::menu::Action for Action { + type Message = Message; + fn message(&self) -> Self::Message { + match self { + Action::About => Message::ToggleContextPage(ContextPage::About), + Action::Settings => Message::ToggleContextPage(ContextPage::Settings), + } + } +} + +#[derive(Clone, Debug)] +pub struct Flags { + pub config_handler: Option, + pub config: TweaksSettings, } impl Application for TweakTool { type Executor = cosmic::executor::Default; - type Flags = (); + type Flags = Flags; type Message = Message; @@ -58,6 +127,21 @@ impl Application for TweakTool { &mut self.core } + fn header_start(&self) -> Vec> { + let menu_bar = menu::bar(vec![menu::Tree::with_children( + menu::root(fl!("view")), + menu::items( + &self.key_binds, + vec![ + menu::Item::Button(fl!("settings"), Action::Settings), + menu::Item::Button(fl!("about"), Action::About), + ], + ), + )]); + + vec![menu_bar.into()] + } + fn header_center(&self) -> Vec> { vec![widget::text::text(fl!("app-title")).into()] } @@ -74,6 +158,18 @@ impl Application for TweakTool { Command::none() } + fn context_drawer(&self) -> Option> { + if !self.core.window.show_context { + return None; + } + + Some(match self.context_page { + ContextPage::About => self.about(), + ContextPage::Settings => self.settings(), + ContextPage::AvailableThemes => self.available_themes(), + }) + } + fn dialog(&self) -> Option> { let dialog_page = match self.dialog_pages.front() { Some(some) => some, @@ -106,7 +202,7 @@ impl Application for TweakTool { Some(dialog.into()) } - fn init(core: Core, _flags: Self::Flags) -> (Self, Command>) { + fn init(core: Core, flags: Self::Flags) -> (Self, Command>) { log::info!("Starting Cosmic Tweak Tool..."); let mut nav_model = segmented_button::SingleSelectModel::default(); @@ -128,15 +224,22 @@ impl Application for TweakTool { core, dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), + key_binds: key_binds(), color_schemes: ColorSchemes::default(), + context_page: ContextPage::About, + app_themes: vec![fl!("match-desktop"), fl!("dark"), fl!("light")], + config_handler: flags.config_handler, + config: flags.config, + available: vec![], + status: Status::Idle, + limit: 15, + offset: 0, }; - let commands = vec![app.update(Message::ColorSchemes(Box::new( - pages::color_schemes::Message::FetchAvailableColorSchemes( - ColorSchemeProvider::CosmicThemes, - app.color_schemes.limit, - ), - )))]; + let commands = vec![app.update(Message::FetchAvailableColorSchemes( + ColorSchemeProvider::CosmicThemes, + app.limit, + ))]; (app, Command::batch(commands)) } @@ -164,12 +267,99 @@ impl Application for TweakTool { .into() } - fn update( - &mut self, - message: Self::Message, - ) -> cosmic::iced::Command> { + fn update(&mut self, message: Self::Message) -> cosmic::Command> { + // Helper for updating config values efficiently + macro_rules! config_set { + ($name: ident, $value: expr) => { + match &self.config_handler { + Some(config_handler) => { + match paste::paste! { self.config.[](config_handler, $value) } { + Ok(_) => {} + Err(err) => { + log::warn!( + "failed to save config {:?}: {}", + stringify!($name), + err + ); + } + } + } + None => { + self.config.$name = $value; + log::warn!( + "failed to save config {:?}: no config handler", + stringify!($name) + ); + } + } + }; + } + let mut commands = vec![]; match message { + Message::FetchAvailableColorSchemes(provider, limit) => { + if self.offset == 0 { + self.status = Status::Loading; + } else { + self.status = Status::LoadingMore; + } + self.limit = limit; + self.offset = self.offset + self.limit; + let limit = self.limit.clone(); + let offset = self.offset.clone(); + commands.push(Command::perform( + async move { + let url = match provider { + ColorSchemeProvider::CosmicThemes => { + format!("https://cosmic-themes.org/api/themes/?order=name&limit={}&offset={}", limit, offset) + } + }; + + let response = reqwest::get(url).await?; + let themes: Vec = response.json().await?; + let available = themes + .into_iter() + .map(ColorScheme::from) + .collect(); + Ok(available) + }, + |res: Result, reqwest::Error>| match res { + Ok(themes) => cosmic::app::Message::App(Message::SetAvailableColorSchemes(themes)), + Err(e) => { + log::error!("{e}"); + cosmic::app::Message::App(Message::SetAvailableColorSchemes(vec![])) + } + }, + )); + } + Message::SetAvailableColorSchemes(mut available) => { + self.status = Status::Idle; + self.available.append(&mut available); + } + Message::AppTheme(index) => { + let app_theme = match index { + 1 => AppTheme::Dark, + 2 => AppTheme::Light, + _ => AppTheme::System, + }; + config_set!(app_theme, app_theme); + return self.update_config(); + } + Message::LaunchUrl(url) => match open::that_detached(&url) { + Ok(()) => {} + Err(err) => { + log::warn!("failed to open {:?}: {}", url, err); + } + }, + Message::ToggleContextPage(page) => { + if self.context_page == page { + self.core.window.show_context = !self.core.window.show_context; + } else { + self.context_page = page.clone(); + self.core.window.show_context = true; + } + self.set_context_title(page.clone().title()); + } Message::Dock(message) => commands.push( pages::dock::Dock::default() .update(message) @@ -184,6 +374,8 @@ impl Application for TweakTool { pages::color_schemes::Message::SaveCurrentColorScheme(None) => { commands.push(self.update(Message::OpenSaveDialog)) } + pages::color_schemes::Message::OpenAvailableThemes => commands + .push(self.update(Message::ToggleContextPage(ContextPage::AvailableThemes))), _ => commands.push( self.color_schemes .update(*message) @@ -220,3 +412,115 @@ impl Application for TweakTool { Command::batch(commands) } } + +impl TweakTool { + fn update_config(&mut self) -> Command> { + app::command::set_theme(self.config.app_theme.theme()) + } + + fn about(&self) -> Element { + let spacing = cosmic::theme::active().cosmic().spacing; + let repository = "https://github.com/cosmic-utils/tweaks"; + let hash = env!("VERGEN_GIT_SHA"); + let short_hash: String = hash.chars().take(7).collect(); + let date = env!("VERGEN_GIT_COMMIT_DATE"); + widget::column::with_children(vec![ + widget::svg(widget::svg::Handle::from_memory( + &include_bytes!("../res/icons/hicolor/scalable/apps/icon.svg")[..], + )) + .into(), + widget::text::title3(fl!("app-title")).into(), + widget::button::link(repository) + .on_press(Message::LaunchUrl(repository.to_string())) + .padding(spacing.space_none) + .into(), + widget::button::link(fl!( + "git-description", + hash = short_hash.as_str(), + date = date + )) + .on_press(Message::LaunchUrl(format!("{repository}/commits/{hash}"))) + .padding(spacing.space_none) + .into(), + ]) + .align_items(Alignment::Center) + .spacing(spacing.space_xxs) + .width(Length::Fill) + .into() + } + + fn settings(&self) -> Element { + let app_theme_selected = match self.config.app_theme { + AppTheme::Dark => 1, + AppTheme::Light => 2, + AppTheme::System => 0, + }; + widget::settings::view_column(vec![widget::settings::section() + .title(fl!("appearance")) + .add( + widget::settings::item::builder(fl!("theme")).control(widget::dropdown( + &self.app_themes, + Some(app_theme_selected), + Message::AppTheme, + )), + ) + .into()]) + .into() + } + + fn available_themes<'a>(&self) -> Element<'a, Message> { + let spacing = cosmic::theme::active().cosmic().spacing; + + let loading: Option> = if let Status::Loading = self.status { + Some(widget::text(fl!("loading")).into()) + } else { + None + }; + + let available: Option> = match self.status { + Status::Idle | Status::LoadingMore => Some( + widget::settings::section() + .title(fl!("available")) + .add({ + let themes: Vec> = + self.available.iter().map(preview::available).collect(); + + widget::flex_row(themes) + .row_spacing(spacing.space_xs) + .column_spacing(spacing.space_xs) + .apply(widget::container) + .padding([0, spacing.space_xxs]) + }) + .into(), + ), + Status::Loading => None, + }; + + let show_more_button: Option> = match self.status { + Status::Idle => Some( + widget::button::text(fl!("show-more")) + .on_press(Message::FetchAvailableColorSchemes( + ColorSchemeProvider::CosmicThemes, + self.limit, + )) + .style(cosmic::theme::Button::Standard) + .into(), + ), + Status::LoadingMore => Some( + widget::button::text(fl!("loading")) + .style(cosmic::theme::Button::Standard) + .into(), + ), + Status::Loading => None, + }; + + widget::settings::view_column( + loading + .into_iter() + .chain(available.into_iter()) + .chain(show_more_button.into_iter()) + .collect(), + ) + .into() + } +} diff --git a/src/app/key_bind.rs b/src/app/key_bind.rs new file mode 100644 index 0000000..5ca76d5 --- /dev/null +++ b/src/app/key_bind.rs @@ -0,0 +1,28 @@ +use std::collections::HashMap; + +use cosmic::iced::keyboard::Key; +use cosmic::widget::menu::key_bind::KeyBind; +use cosmic::widget::menu::key_bind::Modifier; + +use crate::app::Action; + +pub fn key_binds() -> HashMap { + let mut key_binds = HashMap::new(); + + macro_rules! bind { + ([$($modifier:ident),* $(,)?], $key:expr, $action:ident) => {{ + key_binds.insert( + KeyBind { + modifiers: vec![$(Modifier::$modifier),*], + key: $key, + }, + Action::$action, + ); + }}; + } + + bind!([Ctrl], Key::Character(",".into()), Settings); + bind!([Ctrl], Key::Character("i".into()), About); + + key_binds +} diff --git a/src/core/config.rs b/src/core/config.rs index 48d5cc7..a8d13ed 100644 --- a/src/core/config.rs +++ b/src/core/config.rs @@ -1,10 +1,16 @@ use super::icons::{IconCache, ICON_CACHE}; use std::sync::Mutex; -pub fn get() -> cosmic::app::Settings { +pub fn get() -> (cosmic::app::Settings, crate::app::Flags) { ICON_CACHE.get_or_init(|| Mutex::new(IconCache::new())); init_logger(); - cosmic::app::Settings::default() + ( + cosmic::app::Settings::default(), + crate::app::Flags { + config_handler: crate::settings::TweaksSettings::config_handler(), + config: crate::settings::TweaksSettings::config(), + }, + ) } fn init_logger() { diff --git a/src/core/icons.rs b/src/core/icons.rs index 3cc7fde..dff8946 100644 --- a/src/core/icons.rs +++ b/src/core/icons.rs @@ -41,6 +41,7 @@ impl IconCache { bundle!("resize-mode-symbolic", 18); bundle!("arrow-into-box-symbolic", 16); bundle!("document-save-symbolic", 16); + bundle!("search-global-symbolic", 16); bundle!("symbolic-link-symbolic", 14); bundle!("user-trash-symbolic", 14); bundle!("folder-download-symbolic", 14); diff --git a/src/main.rs b/src/main.rs index 3040aa2..a109283 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ mod app; mod core; mod pages; +mod settings; fn main() -> cosmic::iced::Result { - let settings = core::config::get(); - cosmic::app::run::(settings, ()) + let (settings, flags) = core::config::get(); + cosmic::app::run::(settings, flags) } diff --git a/src/pages/color_schemes.rs b/src/pages/color_schemes.rs index 1bd9d67..585d352 100644 --- a/src/pages/color_schemes.rs +++ b/src/pages/color_schemes.rs @@ -7,35 +7,22 @@ use cosmic::{ cosmic_config::{Config, CosmicConfigEntry}, cosmic_theme::{Theme, ThemeBuilder, ThemeMode}, iced::Length, - prelude::CollectionWidget, widget::{self, tooltip}, Apply, Command, Element, }; -mod config; -mod preview; -mod providers; - -use providers::cosmic_themes::CosmicTheme; +pub mod config; +pub mod preview; +pub mod providers; pub struct ColorSchemes { selected: ColorScheme, installed: Vec, - available: Vec, config_helper: Option, config: Option, theme_mode: ThemeMode, theme_builder_config: Option, theme_builder: ThemeBuilder, - status: Status, - pub limit: usize, - offset: usize, -} - -pub enum Status { - Idle, - Loading, - LoadingMore, } impl Default for ColorSchemes { @@ -88,15 +75,11 @@ impl Default for ColorSchemes { Self { selected, installed, - available: vec![], config_helper, config, theme_mode, theme_builder_config, theme_builder, - status: Status::Idle, - limit: 15, - offset: 0, } } } @@ -114,8 +97,7 @@ pub enum Message { OpenContainingFolder(ColorScheme), OpenLink(Option), ReloadColorSchemes, - FetchAvailableColorSchemes(ColorSchemeProvider, usize), - SetAvailableColorSchemes(Vec), + OpenAvailableThemes, } #[derive(Debug, Clone)] @@ -127,49 +109,6 @@ impl ColorSchemes { pub fn view<'a>(&self) -> Element<'a, Message> { let spacing = cosmic::theme::active().cosmic().spacing; - let loading: Option> = if let Status::Loading = self.status { - Some(widget::text(fl!("loading")).into()) - } else { - None - }; - - let available: Option> = match self.status { - Status::Idle | Status::LoadingMore => Some( - widget::settings::section() - .title(fl!("available")) - .add({ - let themes: Vec> = - self.available.iter().map(preview::available).collect(); - - widget::flex_row(themes) - .row_spacing(spacing.space_xs) - .column_spacing(spacing.space_xs) - .apply(widget::container) - .padding([0, spacing.space_xxs]) - }) - .into(), - ), - Status::Loading => None, - }; - - let show_more_button: Option> = match self.status { - Status::Idle => Some( - widget::button::text(fl!("show-more")) - .on_press(Message::FetchAvailableColorSchemes( - ColorSchemeProvider::CosmicThemes, - self.limit, - )) - .style(cosmic::theme::Button::Standard) - .into(), - ), - Status::LoadingMore => Some( - widget::button::text(fl!("loading")) - .style(cosmic::theme::Button::Standard) - .into(), - ), - Status::Loading => None, - }; - widget::column::with_children(vec![ widget::row::with_children(vec![ widget::text::title3(fl!("color-schemes")).into(), @@ -194,6 +133,16 @@ impl ColorSchemes { tooltip::Position::Bottom, ) .into(), + widget::tooltip::tooltip( + icons::get_handle("search-global-symbolic", 16) + .apply(widget::button::icon) + .padding(spacing.space_xxs) + .on_press(Message::OpenAvailableThemes) + .style(cosmic::theme::Button::Standard), + fl!("find-color-schemes"), + tooltip::Position::Bottom, + ) + .into(), ]) .spacing(spacing.space_xxs) .into(), @@ -214,9 +163,6 @@ impl ColorSchemes { }) .into(), ]) - .push_maybe(loading) - .push_maybe(available) - .push_maybe(show_more_button) .spacing(spacing.space_xxs) .apply(widget::scrollable) .into() @@ -225,44 +171,8 @@ impl ColorSchemes { pub fn update(&mut self, message: Message) -> Command { let mut commands = vec![]; match message { - Message::FetchAvailableColorSchemes(provider, limit) => { - if self.offset == 0 { - self.status = Status::Loading; - } else { - self.status = Status::LoadingMore; - } - self.limit = limit; - self.offset = self.offset + self.limit; - let limit = self.limit.clone(); - let offset = self.offset.clone(); - commands.push(Command::perform( - async move { - let url = match provider { - ColorSchemeProvider::CosmicThemes => { - format!("https://cosmic-themes.org/api/themes/?order=name&limit={}&offset={}", limit, offset) - } - }; - - let response = reqwest::get(url).await?; - let themes: Vec = response.json().await?; - let available = themes - .into_iter() - .map(ColorScheme::from) - .collect(); - Ok(available) - }, - |res: Result, reqwest::Error>| match res { - Ok(themes) => Message::SetAvailableColorSchemes(themes), - Err(e) => { - log::error!("{e}"); - Message::SetAvailableColorSchemes(vec![]) - } - }, - )); - } - Message::SetAvailableColorSchemes(mut available) => { - self.status = Status::Idle; - self.available.append(&mut available); + Message::OpenAvailableThemes => { + commands.push(self.update(Message::OpenAvailableThemes)) } Message::StartImport => commands.push(Command::perform( async { diff --git a/src/pages/color_schemes/preview.rs b/src/pages/color_schemes/preview.rs index 1da287a..5e12b7d 100644 --- a/src/pages/color_schemes/preview.rs +++ b/src/pages/color_schemes/preview.rs @@ -72,7 +72,7 @@ pub fn installed<'a>( .into() } -pub fn available<'a>(color_scheme: &ColorScheme) -> Element<'a, super::Message> { +pub fn available<'a>(color_scheme: &ColorScheme) -> Element<'a, crate::app::Message> { let theme = color_scheme.theme.clone().build(); let spacing = cosmic::theme::active().cosmic().spacing; let color_scheme_name = color_scheme.name.clone(); @@ -102,9 +102,11 @@ pub fn available<'a>(color_scheme: &ColorScheme) -> Element<'a, super::Message> .apply(widget::button::icon) .style(cosmic::theme::Button::Link) .padding(spacing.space_xxs) - .on_press(super::Message::OpenLink(color_scheme.link.clone())), + .on_press(crate::app::Message::ColorSchemes(Box::new( + super::Message::OpenLink(color_scheme.link.clone()), + ))), fl!("open-link"), - tooltip::Position::Bottom, + cosmic::widget::tooltip::Position::Bottom, ) .into(), widget::tooltip::tooltip( @@ -112,9 +114,11 @@ pub fn available<'a>(color_scheme: &ColorScheme) -> Element<'a, super::Message> .apply(widget::button::icon) .style(cosmic::theme::Button::Suggested) .padding(spacing.space_xxs) - .on_press(super::Message::InstallColorScheme(color_scheme.clone())), + .on_press(crate::app::Message::ColorSchemes(Box::new( + super::Message::InstallColorScheme(color_scheme.clone()), + ))), fl!("install-color-scheme"), - tooltip::Position::Bottom, + cosmic::widget::tooltip::Position::Bottom, ) .into(), ]) @@ -123,13 +127,14 @@ pub fn available<'a>(color_scheme: &ColorScheme) -> Element<'a, super::Message> .padding([0, spacing.space_xxs, spacing.space_xxs, spacing.space_xxs]) .into(), ]) - .width(Length::Fixed(200.0)) .height(Length::Fixed(160.0)) .apply(widget::container) .style(background(&theme)), ) .style(cosmic::theme::Button::Image) - .on_press(super::Message::SetColorScheme(color_scheme.clone())) + .on_press(crate::app::Message::ColorSchemes(Box::new( + super::Message::SetColorScheme(color_scheme.clone()), + ))) .into() } diff --git a/src/settings.rs b/src/settings.rs new file mode 100644 index 0000000..28c1ca6 --- /dev/null +++ b/src/settings.rs @@ -0,0 +1,58 @@ +use cosmic::{ + cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, Config, CosmicConfigEntry}, + theme, Application, +}; +use serde::{Deserialize, Serialize}; + +use crate::app::TweakTool; + +pub const CONFIG_VERSION: u64 = 1; + +#[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize, Serialize, CosmicConfigEntry)] +pub struct TweaksSettings { + pub app_theme: AppTheme, +} + +impl TweaksSettings { + pub fn config_handler() -> Option { + Config::new(TweakTool::APP_ID, CONFIG_VERSION).ok() + } + + pub fn config() -> TweaksSettings { + match Self::config_handler() { + Some(config_handler) => { + TweaksSettings::get_entry(&config_handler).unwrap_or_else(|(errs, config)| { + log::info!("errors loading config: {:?}", errs); + config + }) + } + None => TweaksSettings::default(), + } + } +} + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub enum AppTheme { + Dark, + Light, + #[default] + System, +} + +impl AppTheme { + pub fn theme(&self) -> theme::Theme { + match self { + Self::Dark => { + let mut t = theme::system_dark(); + t.theme_type.prefer_dark(Some(true)); + t + } + Self::Light => { + let mut t = theme::system_light(); + t.theme_type.prefer_dark(Some(false)); + t + } + Self::System => theme::system_preference(), + } + } +}