Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add theme+settings #3

Merged
merged 5 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ once_cell = "1.19.0"
rust-embed = "8.3.0"
log = "0.4"
open = "5.0.2"
serde = { version = "1.0.202", features = ["serde_derive"] }
paste = "1.0"

[dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic.git"
Expand Down
12 changes: 12 additions & 0 deletions i18n/en/cosmic_backups.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,19 @@ welcome = Backup and backup often! ✨
# Context Pages

## About
about = 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

## File
Expand All @@ -20,4 +31,5 @@ cut = Cut

## View
view = View
menu-settings = Settings...
menu-about = About COSMIC Backups...
162 changes: 118 additions & 44 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
// SPDX-License-Identifier: GPL-3.0-only

use std::{
collections::{HashMap},
env,
process,
};
use crate::app::config::AppTheme;
use crate::fl;
use cosmic::app::{Command, Core};
use cosmic::{
cosmic_theme, ApplicationExt,
iced::{Alignment, Length},
};
use cosmic::iced::alignment::{Horizontal, Vertical};
use cosmic::iced::window;
use cosmic::iced_core::keyboard::Key;
use cosmic::widget::menu::{
action::{MenuAction},
action::MenuAction,
key_bind::{KeyBind, Modifier},
};
use cosmic::widget::segmented_button::Entity;
use cosmic::{
cosmic_config, cosmic_theme,
iced::{Alignment, Length},
ApplicationExt,
};
use cosmic::{widget, Application, Element};
use std::{collections::HashMap, env, process};

pub mod config;
pub mod menu;

/// This is the struct that represents your application.
Expand All @@ -30,6 +29,9 @@ pub struct App {
/// This is the core of your application, it is used to communicate with the Cosmic runtime.
/// It is used to send messages to your application, and to access the resources of the Cosmic runtime.
core: Core,
config_handler: Option<cosmic_config::Config>,
config: config::CosmicBackupsConfig,
app_themes: Vec<String>,
context_page: ContextPage,
key_binds: HashMap<KeyBind, Action>,
}
Expand All @@ -42,27 +44,38 @@ pub enum Message {
// Cut(Option<Entity>),
ToggleContextPage(ContextPage),
LaunchUrl(String),
AppTheme(usize),
SystemThemeModeChange(cosmic_theme::ThemeMode),
WindowClose,
WindowNew,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ContextPage {
About,
Settings,
}

impl ContextPage {
fn title(&self) -> String {
match self {
Self::About => String::new(),
Self::About => fl!("about"),
Self::Settings => fl!("settings"),
}
}
}

#[derive(Clone, Debug)]
pub struct Flags {
pub config_handler: Option<cosmic_config::Config>,
pub config: config::CosmicBackupsConfig,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Action {
About,
// Cut,
Settings,
WindowClose,
WindowNew,
}
Expand All @@ -73,16 +86,17 @@ impl MenuAction for Action {
match self {
Action::About => Message::ToggleContextPage(ContextPage::About),
// Action::Cut => Message::Cut(entity_opt),
Action::Settings => Message::ToggleContextPage(ContextPage::Settings),
Action::WindowClose => Message::WindowClose,
Action::WindowNew => Message::WindowNew,
}
}
}

impl App {
// fn update_config(&mut self) -> Command<CosmicMessage<Message>> {
// cosmic::app::command::set_theme(self.config.app_theme.theme())
// }
fn update_config(&mut self) -> Command<Message> {
cosmic::app::command::set_theme(self.config.app_theme.theme())
}

fn about(&self) -> Element<Message> {
let cosmic_theme::Spacing { space_xxs, .. } = cosmic::theme::active().cosmic().spacing;
Expand All @@ -91,38 +105,57 @@ impl App {
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/128x128/apps/com.example.CosmicAppTemplate.svg"
)[..],
))
widget::svg(widget::svg::Handle::from_memory(
&include_bytes!(
"../res/icons/hicolor/128x128/apps/com.example.CosmicAppTemplate.svg"
)[..],
))
.into(),
widget::text::title3(fl!("cosmic-backups")).into(),
widget::button::link(repository)
.on_press(Message::LaunchUrl(repository.to_string()))
.padding(0)
.into(),
widget::text::title3(fl!("cosmic-backups")).into(),
widget::button::link(repository)
.on_press(Message::LaunchUrl(repository.to_string()))
.padding(0)
.into(),
widget::button::link(fl!(
"git-description",
hash = short_hash.as_str(),
date = date
))
.on_press(Message::LaunchUrl(format!("{}/commits/{}", repository, hash)))
.padding(0)
.into(),
])
widget::button::link(fl!(
"git-description",
hash = short_hash.as_str(),
date = date
))
.on_press(Message::LaunchUrl(format!(
"{}/commits/{}",
repository, hash
)))
.padding(0)
.into(),
])
.align_items(Alignment::Center)
.spacing(space_xxs)
.into()
}

}

fn settings(&self) -> Element<Message> {
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::view_section(fl!("appearance"))
.add(
widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
&self.app_themes,
Some(app_theme_selected),
Message::AppTheme,
)),
)
.into()])
.into()
}
}

impl Application for App {
type Executor = cosmic::executor::Default;

type Flags = ();
type Flags = Flags;

type Message = Message;

Expand All @@ -141,10 +174,13 @@ impl Application for App {
vec![menu::menu_bar(&self.key_binds)]
}

fn init(core: Core, _input: Self::Flags) -> (Self, Command<Self::Message>) {
fn init(core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
let app = App {
core,
context_page: ContextPage::About,
context_page: ContextPage::Settings,
config_handler: flags.config_handler,
config: flags.config,
app_themes: vec![fl!("match-desktop"), fl!("dark"), fl!("light")],
key_binds: key_binds(),
};

Expand All @@ -158,6 +194,7 @@ impl Application for App {

Some(match self.context_page {
ContextPage::About => self.about(),
ContextPage::Settings => self.settings(),
})
}

Expand All @@ -169,11 +206,37 @@ impl Application for App {
.align_y(Vertical::Center)
.into()
}

/// Handle application events here.
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {

match message {
// 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.[<set_ $name>](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)
);
}
}
};
}

match message {
Message::ToggleContextPage(context_page) => {
//TODO: ensure context menus are closed
if self.context_page == context_page {
Expand Down Expand Up @@ -204,11 +267,22 @@ impl Application for App {
log::warn!("failed to open {:?}: {}", url, err);
}
},
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::SystemThemeModeChange(_) => {
return self.update_config();
}
}

Command::none()

}
Command::none()
}
}

pub fn key_binds() -> HashMap<KeyBind, Action> {
Expand Down
52 changes: 52 additions & 0 deletions src/app/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::app::App;
use cosmic::{
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, Config, CosmicConfigEntry},
theme, Application,
};
use serde::{Deserialize, Serialize};

pub const CONFIG_VERSION: u64 = 1;

#[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize, Serialize, CosmicConfigEntry)]
pub struct CosmicBackupsConfig {
pub app_theme: AppTheme,
}

impl CosmicBackupsConfig {
pub fn config_handler() -> Option<Config> {
Config::new(App::APP_ID, CONFIG_VERSION).ok()
}

pub fn config() -> CosmicBackupsConfig {
match Self::config_handler() {
Some(config_handler) => {
let config = CosmicBackupsConfig::get_entry(&config_handler).unwrap_or_else(
|(errs, config)| {
log::info!("errors loading config: {:?}", errs);
config
},
);
config
}
None => CosmicBackupsConfig::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 => theme::Theme::dark(),
Self::Light => theme::Theme::light(),
Self::System => theme::system_preference(),
}
}
}
2 changes: 2 additions & 0 deletions src/app/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub fn menu_bar<'a>(key_binds: &HashMap<KeyBind, Action>) -> Element<'a, Message
items(
key_binds,
vec![
Item::Button(fl!("menu-settings"), Action::Settings),
Item::Divider,
Item::Button(fl!("menu-about"), Action::About),
],
),
Expand Down
Loading