Skip to content

Commit

Permalink
refactor Dream trait
Browse files Browse the repository at this point in the history
to avoid building Dreams needlessly.
  • Loading branch information
Barafu committed Aug 22, 2024
1 parent 5d6ae6b commit 6888fbb
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 57 deletions.
12 changes: 5 additions & 7 deletions src/app_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
use directories::ProjectDirs;
use log;
use std::{
collections::{HashMap, HashSet},
collections::{BTreeMap, BTreeSet},
fmt::Display,
fs::File,
path::{Path, PathBuf},
Expand All @@ -13,33 +13,31 @@ pub static SETTINGS: LazyLock<RwLock<SettingsRaw>> = LazyLock::new(|| {
RwLock::new(SettingsRaw::read_from_file_default().unwrap())
});

use crate::dreams::DreamId;

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(default)]
/// Contains all persistant settings of the application
pub struct SettingsRaw {
/// Contains unique settings of particular dreams
pub dream_settings: HashMap<String, String>,
pub dream_settings: BTreeMap<String, String>,

/// Try to detect and cover additional monitors.
pub attempt_multiscreen: bool,

/// Show FPS statistics on primary screen.
pub show_fps: bool,

pub selected_dreams: HashSet<DreamId>,
pub selected_dreams: BTreeSet<String>,

pub viewport_mode: ViewportMode,
}

impl Default for SettingsRaw {
fn default() -> Self {
Self {
dream_settings: HashMap::new(),
dream_settings: BTreeMap::new(),
attempt_multiscreen: false,
show_fps: false,
selected_dreams: HashSet::from(["fractal_clock".to_string()]),
selected_dreams: BTreeSet::from(["fractal_clock".to_string()]),
viewport_mode: ViewportMode::Immediate,
}
}
Expand Down
30 changes: 16 additions & 14 deletions src/dreamconfig.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;

use crate::app_settings::{ViewportMode, SETTINGS};
use crate::dreams::*;
use anyhow::Result;
Expand All @@ -13,7 +15,7 @@ enum ActivePanel {

pub struct DreamConfigApp {
active_panel: ActivePanel,
zoo: Zoo,
zoo: BTreeMap<String, ADream>,
}

impl eframe::App for DreamConfigApp {
Expand Down Expand Up @@ -53,7 +55,7 @@ impl eframe::App for DreamConfigApp {
);
//ui.separator();
// TODO: Can't enable separator: breaks UI for some reason
for dream in self.zoo.iter() {
for dream in self.zoo.values() {
ui.selectable_value(
&mut self.active_panel,
ActivePanel::Dream(dream.read().unwrap().id()),
Expand All @@ -67,8 +69,7 @@ impl eframe::App for DreamConfigApp {
ActivePanel::Select => self.draw_dream_select(ui),
ActivePanel::About => self.draw_about(ui),
ActivePanel::Dream(id) => {
let dream = select_dream_by_id(&self.zoo, id).unwrap();
dream.write().unwrap().config_egui(ui);
self.zoo[*id].write().unwrap().config_egui(ui);
}
});
});
Expand All @@ -80,15 +81,16 @@ impl DreamConfigApp {
pub fn new(_cc: &eframe::CreationContext<'_>) -> Result<Self> {
// Load settings from file

let zoo = build_zoo();
for dream in zoo.iter() {
dream.write().unwrap().prepare();
}
let dream_ids = build_dreams_id_list();
let zoo: BTreeMap<String, ADream> = dream_ids
.keys()
.map(|id| (id.to_string(), build_dream_by_id(id)))
.collect();
Ok(Self { active_panel: ActivePanel::Generic, zoo })
}

fn save(&mut self) {
for dream in self.zoo.iter() {
for dream in self.zoo.values() {
dream.read().unwrap().store();
}
SETTINGS.write().unwrap().write_to_file_default().unwrap();
Expand Down Expand Up @@ -137,7 +139,7 @@ impl DreamConfigApp {
let mut ids = Vec::with_capacity(self.zoo.len());
let mut names = Vec::with_capacity(self.zoo.len());

for arcd in self.zoo.iter() {
for arcd in self.zoo.values() {
let d = arcd.read().unwrap();
ids.push(d.id());
names.push(d.name());
Expand All @@ -149,13 +151,13 @@ impl DreamConfigApp {
ui.label("Select dream:");
let st = &mut settings.selected_dreams;
for n in 0..ids.len() {
let mut active = st.contains(&ids[n]);
ui.checkbox(&mut active, &names[n]);
let mut active = st.contains(ids[n]);
ui.checkbox(&mut active, names[n]);
if active {
st.insert(ids[n].clone());
st.insert(ids[n].to_string());
} else {
if st.len() > 1 {
st.remove(&ids[n]);
st.remove(ids[n]);
}
}
}
Expand Down
41 changes: 23 additions & 18 deletions src/dreams.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::app_settings::SETTINGS;
use std::sync::{Arc, RwLock};
use std::{
collections::BTreeMap,
sync::{Arc, RwLock},
};

mod dendraclock;
mod monet;
mod solid_color;

/// For giggles, I call the collection of all dream types "zoo"
pub type Zoo = Vec<Arc<RwLock<dyn Dream>>>;
pub type DreamId = String;
pub type DreamId = &'static str;
pub type ADream = Arc<RwLock<dyn Dream>>;

#[derive(PartialEq, Debug)]
pub enum DreamType {
Expand All @@ -26,7 +28,7 @@ pub trait Dream: Sync + Send {

/// Gives the name to display in UI. The name also serves as ID, including
/// in settings, so it must be unique
fn name(&self) -> String;
fn name(&self) -> &'static str;

/// Prepare dream for rendering (load resources, initialize RNG etc.)
fn prepare(&mut self) {}
Expand All @@ -53,21 +55,24 @@ pub trait Dream: Sync + Send {
fn store(&self) {}
}

pub fn build_zoo() -> Zoo {
let mut zoo: Zoo = Zoo::new();
let d = RwLock::new(solid_color::SolidColorDream::new());
zoo.push(Arc::new(d));
let d = RwLock::new(dendraclock::DendraClockDream::new());
zoo.push(Arc::new(d));
let d = RwLock::new(monet::MonetDream::new());
zoo.push(Arc::new(d));
pub fn build_dreams_id_list() -> BTreeMap<&'static str, &'static str> {
let mut zoo = BTreeMap::new();
zoo.insert(dendraclock::DREAM_ID, dendraclock::DREAM_NAME);
zoo.insert(monet::DREAM_ID, monet::DREAM_NAME);
zoo.insert(solid_color::DREAM_ID, solid_color::DREAM_NAME);
zoo
}

// Pick a dream from zoo by its id.
pub fn select_dream_by_id(
zoo: &Zoo,
id: &DreamId,
) -> Option<Arc<RwLock<dyn Dream>>> {
zoo.iter().find(|d| d.read().unwrap().id() == *id).map(|d| d.clone())
pub fn build_dream_by_id(id: &str) -> ADream {
match id {
dendraclock::DREAM_ID => {
Arc::new(RwLock::new(dendraclock::DendraClockDream::new()))
}
monet::DREAM_ID => Arc::new(RwLock::new(monet::MonetDream::new())),
solid_color::DREAM_ID => {
Arc::new(RwLock::new(solid_color::SolidColorDream::new()))
}
_ => panic!("Unknown dream id: {}", id),
}
}
17 changes: 12 additions & 5 deletions src/dreams/dendraclock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use chrono::{Local, Timelike};
use egui::{widgets::*, *};
use std::f32::consts::TAU;

pub const DREAM_ID: DreamId = "fractal_clock";
pub const DREAM_NAME: &'static str = "Fractal Clock";

pub struct DendraClockDream {
dream_settings: DendraClockSettings,
}
Expand Down Expand Up @@ -41,19 +44,19 @@ impl Dream for DendraClockDream {
.read()
.unwrap()
.dream_settings
.get(&d.id())
.get(DREAM_ID)
.cloned()
.unwrap_or_default();
d.dream_settings = toml::from_str(&txt).unwrap_or_default();
d
}

fn id(&self) -> DreamId {
"fractal_clock".to_string()
DREAM_ID
}

fn name(&self) -> String {
"Fractal Clock".to_string()
fn name(&self) -> &'static str {
DREAM_NAME
}

fn get_type(&self) -> DreamType {
Expand All @@ -76,7 +79,11 @@ impl Dream for DendraClockDream {

fn store(&self) {
let txt = toml::to_string(&self.dream_settings).unwrap();
SETTINGS.write().unwrap().dream_settings.insert(self.id(), txt);
SETTINGS
.write()
.unwrap()
.dream_settings
.insert(DREAM_ID.to_string(), txt);
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/dreams/monet.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::dreams::*;

pub const DREAM_ID: DreamId = "monet";
pub const DREAM_NAME: &'static str = "Monet";

pub struct MonetDream {
dream_settings: MonetSettings,
}
Expand All @@ -18,19 +21,19 @@ impl Dream for MonetDream {
.read()
.unwrap()
.dream_settings
.get(&d.id())
.get(DREAM_ID)
.cloned()
.unwrap_or_default();
d.dream_settings = toml::from_str(&txt).unwrap_or_default();
d
}

fn id(&self) -> super::DreamId {
"monet".to_string()
DREAM_ID
}

fn name(&self) -> String {
"Monet".to_string()
fn name(&self) -> &'static str {
DREAM_NAME
}

fn get_type(&self) -> super::DreamType {
Expand Down
17 changes: 12 additions & 5 deletions src/dreams/solid_color.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::dreams::*;

pub const DREAM_ID: DreamId = "solid_color";
pub const DREAM_NAME: &'static str = "Solid Color";

/// This dream is intended to be as primitive as possible to serve as example
/// of how to implement Dream trait.
///
Expand Down Expand Up @@ -27,18 +30,18 @@ impl Dream for SolidColorDream {
.read()
.unwrap()
.dream_settings
.get(&d.id())
.get(DREAM_ID)
.cloned()
.unwrap_or_default();
d.dream_settings = toml::from_str(&txt).unwrap_or_default();
d
}
fn id(&self) -> DreamId {
"solid_color".to_string()
DREAM_ID
}

fn name(&self) -> String {
"Solid Color".to_string()
fn name(&self) -> &'static str {
DREAM_NAME
}

fn get_type(&self) -> DreamType {
Expand Down Expand Up @@ -66,6 +69,10 @@ impl Dream for SolidColorDream {

fn store(&self) {
let txt = toml::to_string(&self.dream_settings).unwrap();
SETTINGS.write().unwrap().dream_settings.insert(self.id(), txt);
SETTINGS
.write()
.unwrap()
.dream_settings
.insert(DREAM_ID.to_string(), txt);
}
}
8 changes: 4 additions & 4 deletions src/dreamspinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ impl DreamSpinner {
///
/// Chooses randomly one of the dreams in selected list. Runs prepare on it.
fn select_active_dream() -> Arc<RwLock<dyn Dream>> {
let zoo = build_zoo();
let selected_dreams = &SETTINGS.read().unwrap().selected_dreams;
let selected_dreams = SETTINGS.read().unwrap().selected_dreams.clone();
let mut rng = rand::thread_rng();
let random_index = rng.gen_range(0..selected_dreams.len());
let random_id = selected_dreams.iter().nth(random_index).unwrap();
let dream = select_dream_by_id(&zoo, random_id).unwrap();
let random_id =
selected_dreams.iter().nth(random_index).unwrap().to_string();
let dream = build_dream_by_id(&random_id);
dream.write().unwrap().prepare();
dream
}
Expand Down

0 comments on commit 6888fbb

Please sign in to comment.