Skip to content

Commit

Permalink
Simplified config file, remove "cache.toml"
Browse files Browse the repository at this point in the history
  • Loading branch information
DougLau committed Oct 30, 2023
1 parent e736d7a commit 3c0305a
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 424 deletions.
266 changes: 94 additions & 172 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,212 +1,87 @@
//! There are two files that store properties for Alloy, the *cache* and the *config*.
//!
//! The most important distinction between these is that Alloy never writes to the *config*
//! but it does write to the *cache* to save portions of the state of the program (e.g. window size
//! and position).
//!
//! Furthermore it's generally true that the user will only edit the *config* to specify their
//! preferences.
//!
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::BTreeMap, fs, path::Path, path::PathBuf};
use std::{collections::BTreeMap, fs, path::PathBuf};

/// Application name for project directories
const APPLICATION: &str = "Alloy";

#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum WindowMode {
#[default]
Normal,
Maximized,
Fullscreen,
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum Theme {
#[default]
Light,
Dark,
}

impl Theme {
pub fn switch_theme(self) -> Self {
pub fn toggle(self) -> Self {
match self {
Theme::Dark => Theme::Light,
Theme::Light => Theme::Dark,
}
}
}

#[derive(
Copy, Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize,
)]
#[serde(rename_all = "snake_case")]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum ScalingMode {
#[default]
Fixed,
FitStretch,
FitMin,
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub enum Antialias {
#[default]
Auto,
Always,
Never,
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CacheImageSection {
pub fit_stretches: bool,
pub antialiasing: Antialias,
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize)]
pub struct ConfigImageSection {
pub antialiasing: Option<String>,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct CacheWindowSection {
pub dark: bool,
pub win_w: u32,
pub win_h: u32,
pub win_x: i32,
pub win_y: i32,
}

impl Default for CacheWindowSection {
fn default() -> Self {
Self {
dark: false,
win_w: 580,
win_h: 558,
win_x: 64,
win_y: 64,
}
}
}

#[derive(Debug, Clone, Deserialize)]
pub struct ConfigWindowSection {
pub start_fullscreen: Option<bool>,
pub start_maximized: Option<bool>,
pub show_bottom_bar: Option<bool>,
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ConfigWindow {
pub title_folders: Option<u32>,
pub mode: Option<WindowMode>,
pub theme: Option<Theme>,
pub use_last_window_area: Option<bool>,
pub win_w: Option<u32>,
pub win_h: Option<u32>,
pub win_x: Option<i32>,
pub win_y: Option<i32>,
}

#[derive(Deserialize)]
struct IncompleteCache {
pub window: Option<CacheWindowSection>,
pub image: Option<CacheImageSection>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize)]
pub struct Cache {
pub window: CacheWindowSection,
pub image: CacheImageSection,
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ConfigImage {
pub scaling: Option<ScalingMode>,
pub antialiasing: Option<Antialias>,
}

impl From<IncompleteCache> for Cache {
fn from(cache: IncompleteCache) -> Self {
Self {
window: cache.window.unwrap_or_default(),
image: cache.image.unwrap_or_default(),
}
}
}

impl Cache {
pub fn theme(&self) -> Theme {
if self.window.dark {
Theme::Dark
} else {
Theme::Light
}
}

pub fn set_theme(&mut self, theme: Theme) {
self.window.dark = theme == Theme::Dark;
}

pub fn load() -> Result<Cache, String> {
let file_path = cache_file();
let cfg_str = fs::read_to_string(&file_path).map_err(|_| {
format!("Could not read cache from {file_path:?}")
})?;
let result: IncompleteCache =
toml::from_str(&cfg_str).map_err(|e| format!("{e}"))?;
//println!("Read cache from file:\n{:#?}", result);
Ok(result.into())
}

pub fn save(&self) -> Result<(), String> {
let file_path = cache_file();
let string = toml::to_string(self).map_err(|e| format!("{e}"))?;
fs::write(&file_path, string).map_err(|_| {
format!("Could not write to cache file {:?}", file_path)
})?;
Ok(())
}
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct EnvVar {
pub name: String,
pub value: String,
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Command {
pub input: Vec<String>,
pub program: String,
pub args: Option<Vec<String>>,
pub envs: Option<Vec<EnvVar>>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize)]
pub struct TitleSection {
pub displayed_folders: Option<u32>,
pub show_program_name: Option<bool>,
}

impl TitleSection {
pub fn format_file_path<'a>(&self, file_path: &'a Path) -> Cow<'a, str> {
match self.displayed_folders {
Some(0) | None => file_path.file_name().unwrap().to_string_lossy(),
Some(n) => {
let mut component_count = 0;
// On Windows the root can be the second component, when a
// `Prefix` is the first.
let mut root_index = 0;
for (idx, c) in file_path.components().enumerate() {
component_count += 1;
if c == std::path::Component::RootDir {
root_index = idx as u32;
}
}
let path = if (component_count - root_index) <= (1 + n) {
file_path
.to_string_lossy()
.trim_start_matches("\\\\?\\")
.to_owned()
.into()
} else {
let ancestor = file_path
.ancestors()
.take(2 + n as usize)
.last()
.unwrap();
file_path.strip_prefix(ancestor).unwrap().to_string_lossy()
};
path
}
}
}
}

#[derive(Debug, Default, Clone, Deserialize)]
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct Configuration {
pub window: Option<ConfigWindow>,
pub image: Option<ConfigImage>,
pub bindings: Option<BTreeMap<String, Vec<String>>>,
pub commands: Option<Vec<Command>>,
pub title: Option<TitleSection>,
pub image: Option<ConfigImageSection>,
pub window: Option<ConfigWindowSection>,
}

impl Configuration {
pub fn load() -> Result<Configuration, String> {
pub fn load() -> Result<Self, String> {
let file_path = config_file();
let cfg_str = fs::read_to_string(&file_path)
.map_err(|_| format!("Could not read config from {file_path:?}"))?;
Expand All @@ -215,6 +90,64 @@ impl Configuration {
//println!("Read config from file:\n{:#?}", result);
Ok(result)
}

pub fn save(&self) -> Result<(), String> {
let file_path = config_file();
let cfg_str = toml::to_string(self).map_err(|e| format!("{e}"))?;
fs::write(&file_path, cfg_str).map_err(|_| {
format!("Could not write to config file {file_path:?}")
})?;
Ok(())
}

pub fn scaling(&self) -> ScalingMode {
self.image.as_ref().and_then(|i| i.scaling).unwrap_or_default()
}

pub fn set_scaling(&mut self, scaling: ScalingMode) {
if self.image.is_none() {
self.image = Some(ConfigImage::default());
}
if let Some(image) = &mut self.image {
image.scaling = Some(scaling);
}
}

pub fn antialiasing(&self) -> Antialias {
self.image.as_ref().and_then(|i| i.antialiasing).unwrap_or_default()
}

pub fn set_antialiasing(&mut self, antialias: Antialias) {
if self.image.is_none() {
self.image = Some(ConfigImage::default());
}
if let Some(image) = &mut self.image {
image.antialiasing = Some(antialias);
}
}

pub fn title_folders(&self) -> u32 {
self.window.as_ref().and_then(|w| w.title_folders).unwrap_or_default()
}

pub fn window_mode(&self) -> WindowMode {
self.window.as_ref().and_then(|w| w.mode).unwrap_or_default()
}

pub fn theme(&self) -> Theme {
self.window.as_ref().and_then(|w| w.theme).unwrap_or_default()
}

pub fn set_theme(&mut self, theme: Theme) {
self.window_config().theme = Some(theme);
}

fn window_config(&mut self) -> &mut ConfigWindow {
if self.window.is_none() {
self.window = Some(ConfigWindow::default());
}
self.window.as_mut().unwrap()
}
}

fn project_dir_fallback() -> PathBuf {
Expand All @@ -231,18 +164,7 @@ fn config_file() -> PathBuf {
if !config_dir.exists() {
std::fs::create_dir_all(&config_dir).unwrap();
}
config_dir.join("cfg.toml")
}

fn cache_file() -> PathBuf {
let cache_dir = match ProjectDirs::from("", "", APPLICATION) {
Some(proj) => proj.cache_dir().to_owned(),
None => project_dir_fallback(),
};
if !cache_dir.exists() {
std::fs::create_dir_all(&cache_dir).unwrap();
}
cache_dir.join("cache.toml")
config_dir.join("config.toml")
}

pub fn data_dir() -> PathBuf {
Expand Down
10 changes: 0 additions & 10 deletions src/gelatin/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,6 @@ impl Application {
self.windows.insert(window.get_id(), window);
}

#[allow(dead_code)]
pub fn add_global_event_handler<
F: FnMut(&Event<()>) -> NextUpdate + 'static,
>(
&mut self,
fun: F,
) {
self.global_handlers.push(Box::new(fun));
}

pub fn start_event_loop(self) -> ! {
let mut windows = self.windows;
let mut at_exit = self.at_exit;
Expand Down
8 changes: 0 additions & 8 deletions src/gelatin/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,6 @@ impl Window {
resulting_window
}

pub fn add_global_event_handler<F: FnMut(&WindowEvent) + 'static>(
&self,
fun: F,
) {
let mut borrowed = self.data.borrow_mut();
borrowed.global_event_handlers.push(Box::new(fun));
}

pub fn set_root<T: Widget>(&self, widget: Rc<T>) {
let mut borrowed = self.data.borrow_mut();
widget.set_valid_ref(borrowed.render_validity.clone());
Expand Down
Loading

0 comments on commit 3c0305a

Please sign in to comment.