Skip to content

Commit

Permalink
add persistent_storage
Browse files Browse the repository at this point in the history
  • Loading branch information
khanghugo committed Sep 20, 2024
1 parent 2ea3291 commit c046ad0
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 17 deletions.
24 changes: 19 additions & 5 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::path::Path;
use std::{
path::Path,
sync::{Arc, Mutex},
};

use eframe::{egui, Theme};
use egui_tiles::Tree;
Expand All @@ -7,6 +10,7 @@ use utils::preview_file_being_dropped;
use crate::{
config::{parse_config, parse_config_from_file, Config},
err,
persistent_storage::PersistentStorage,
};

use self::{
Expand Down Expand Up @@ -58,12 +62,15 @@ pub fn gui() -> eyre::Result<()> {
}
}

let persistent_storage = PersistentStorage::start()?;
let persistent_storage = Arc::new(Mutex::new(persistent_storage));

let gui_res = eframe::run_native(
"gchimp",
options,
Box::new(|cc| {
egui_extras::install_image_loaders(&cc.egui_ctx);
Ok(Box::new(MyApp::new(config_res)))
Ok(Box::new(MyApp::new(config_res, persistent_storage)))
}),
);

Expand All @@ -76,6 +83,8 @@ pub fn gui() -> eyre::Result<()> {
struct MyApp {
tree: Option<Tree<Pane>>,
_no_config_status: String,
// duplicated because create_tree should have been a struct method
persistent_storage: Arc<Mutex<PersistentStorage>>,
}

impl eframe::App for MyApp {
Expand Down Expand Up @@ -113,17 +122,22 @@ impl eframe::App for MyApp {
}

impl MyApp {
pub fn new(config_res: eyre::Result<Config>) -> Self {
pub fn new(
config_res: eyre::Result<Config>,
persistent_storage: Arc<Mutex<PersistentStorage>>,
) -> Self {
if let Err(err) = config_res {
return Self {
tree: None,
_no_config_status: format!("Error with parsing config.toml: {}", err),
persistent_storage,
};
}

Self {
tree: Some(create_tree(config_res.unwrap())),
tree: Some(create_tree(config_res.unwrap(), persistent_storage.clone())),
_no_config_status: "".to_string(),
persistent_storage,
}
}

Expand All @@ -132,7 +146,7 @@ impl MyApp {

match config {
Err(err) => self._no_config_status = err.to_string(),
Ok(config) => self.tree = Some(create_tree(config)),
Ok(config) => self.tree = Some(create_tree(config, self.persistent_storage.clone())),
}
}
}
13 changes: 10 additions & 3 deletions src/gui/pane.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::sync::{Arc, Mutex};

use eframe::egui::{self, vec2, Sense, Stroke, TextStyle, Vec2};

use crate::config::Config;
use crate::{config::Config, persistent_storage::PersistentStorage};

use super::{
programs::{
Expand Down Expand Up @@ -46,14 +48,19 @@ impl Pane {
}
}

pub fn create_tree(app_config: Config) -> egui_tiles::Tree<Pane> {
pub fn create_tree(
app_config: Config,
persistent_storage: Arc<Mutex<PersistentStorage>>,
) -> egui_tiles::Tree<Pane> {
let mut tiles = egui_tiles::Tiles::default();

let tabs = vec![
// TODO maybe no need to clone config?
// config is readonly so there should be a way with it
tiles.insert_pane(Pane::S2G(S2GGui::new(app_config.clone()))),
tiles.insert_pane(Pane::SkyMod(SkyModGui::new(app_config.clone()))),
tiles.insert_pane(Pane::TexTile(TexTileGui::default())),
tiles.insert_pane(Pane::Waddy(WaddyGui::default())),
tiles.insert_pane(Pane::Waddy(WaddyGui::new(persistent_storage))),
tiles.insert_pane(Pane::Map2Prop(Map2MdlGui::new(app_config.clone()))),
tiles.insert_pane(Pane::DemDoc(DemDoc::default())),
tiles.insert_pane(Pane::Blbh(BLBHGui::new(app_config.clone()))),
Expand Down
93 changes: 84 additions & 9 deletions src/gui/programs/waddy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::path::{Path, PathBuf};
use std::{
path::{Path, PathBuf},
sync::{Arc, Mutex},
};

use arboard::Clipboard;
use eframe::egui::{self, Context, Modifiers, RichText, ScrollArea, Sense, Ui};
Expand All @@ -14,13 +17,15 @@ use crate::{
TabProgram,
},
modules::waddy::Waddy,
persistent_storage::PersistentStorage,
};

pub struct WaddyGui {
instances: Vec<WaddyInstance>,
extra_image_viewports: Vec<WadImage>,
/// 32x32 texture on 512x512 grid is VERY TINY
fit_texture: bool,
persistent_storage: Arc<Mutex<PersistentStorage>>,
}

struct WaddyInstance {
Expand Down Expand Up @@ -85,21 +90,22 @@ impl TextureTile {
}
}

#[allow(clippy::derivable_impls)]
impl Default for WaddyGui {
fn default() -> Self {
const BASE_IMAGE_TILE_SIZE: f32 = 96.0;
const SUPPORTED_TEXTURE_FORMATS: &[&str] = &["png", "jpeg", "jpg", "bmp"];

const PERSISTENT_STORAGE_RECENTLY_USED_UPDATE_ERROR: &str =
"cannot update recently used wad for Waddy";

impl WaddyGui {
pub fn new(persistent_storage: Arc<Mutex<PersistentStorage>>) -> Self {
Self {
instances: vec![],
extra_image_viewports: vec![],
fit_texture: true,
persistent_storage,
}
}
}

static BASE_IMAGE_TILE_SIZE: f32 = 96.0;
static SUPPORTED_TEXTURE_FORMATS: &[&str] = &["png", "jpeg", "jpg", "bmp"];

impl WaddyGui {
/// Returns index of texture to delete
fn texture_tile(
&mut self,
Expand Down Expand Up @@ -815,6 +821,15 @@ impl WaddyGui {
(Waddy::new(), true)
};

if !is_changed {
// this only happens when we open a wad on disk rather than a new wad or bsp
self.persistent_storage
.lock()
.unwrap()
.push_waddy_recent_wads(path.unwrap().to_str().unwrap())
.expect(PERSISTENT_STORAGE_RECENTLY_USED_UPDATE_ERROR);
}

let texture_tiles = waddy
.wad()
.entries
Expand Down Expand Up @@ -891,6 +906,8 @@ impl WaddyGui {
ui.close_menu();
}

self.open_recent_menu_button(ui);

ui.separator();

if ui.button("Save (Ctrl+S)").clicked() {
Expand Down Expand Up @@ -972,6 +989,12 @@ impl WaddyGui {

fn menu_save(&mut self, instance_index: usize) {
if let Some(path) = &self.instances[instance_index].path {
self.persistent_storage
.lock()
.unwrap()
.push_waddy_recent_wads(path.to_str().unwrap())
.expect(PERSISTENT_STORAGE_RECENTLY_USED_UPDATE_ERROR);

// TODO TOAST TOAST
if let Err(err) = self.instances[instance_index]
.waddy
Expand All @@ -997,6 +1020,12 @@ impl WaddyGui {
})
.save_file()
{
self.persistent_storage
.lock()
.unwrap()
.push_waddy_recent_wads(path.to_str().unwrap())
.expect(PERSISTENT_STORAGE_RECENTLY_USED_UPDATE_ERROR);

// TODO TOAST TOAST
if let Err(err) = self.instances[instance_index]
.waddy
Expand Down Expand Up @@ -1026,6 +1055,8 @@ impl WaddyGui {

ui.close_menu();
}

self.open_recent_menu_button(ui);
});

ui.separator();
Expand Down Expand Up @@ -1055,6 +1086,50 @@ impl WaddyGui {
}
}
}

fn open_recent_menu_button(&mut self, ui: &mut egui::Ui) {
ui.menu_button("Open Recent", |ui| {
let mutex = self.persistent_storage.clone();
let persistent_storage = mutex.lock().unwrap();
let recent_wads = persistent_storage.get_waddy_recent_wads();

let to_remove = if recent_wads.is_none() || recent_wads.unwrap().is_empty() {
ui.add_enabled(false, egui::Button::new("No recently opened"));

None
} else {
let recent_wads = recent_wads.unwrap().to_owned();

// start_waddy_instance will block until it has persistent_storage guard
drop(persistent_storage);

recent_wads.into_iter().find(|recent_wad| {
if ui.button(recent_wad.as_str()).clicked() {
let path = Path::new(recent_wad.as_str());

if path.exists() {
self.start_waddy_instance(ui, Some(Path::new(recent_wad.as_str())))
.expect("cannot start a Waddy instance");

ui.close_menu();
} else {
return true;
}
}

false
})
};

if let Some(to_remove) = to_remove {
mutex
.lock()
.unwrap()
.remove_waddy_recent_wads(&to_remove)
.expect(PERSISTENT_STORAGE_RECENTLY_USED_UPDATE_ERROR);
}
});
}
}

impl TabProgram for WaddyGui {
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ mod cli;
mod config;
mod entity;
mod gui;
mod persistent_storage;

pub mod modules;
pub mod utils;

Expand Down
106 changes: 106 additions & 0 deletions src/persistent_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::{
env,
fs::{File, OpenOptions},
io::{Read, Seek, Write},
path::PathBuf,
};

use serde::{Deserialize, Serialize};

pub const PERSISTENT_STORAGE_FILE_NAME: &str = "data.toml";

#[derive(Debug)]
pub struct PersistentStorage {
file: File,
data: PersistentStorageData,
}

pub const PERSISTENT_STORAGE_WADDY_RECENT_WADS_COUNT: usize = 10;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PersistentStorageData {
waddy_recent_wads: Option<Vec<String>>,
}

impl PersistentStorage {
pub fn start() -> eyre::Result<Self> {
let path = match env::current_exe() {
Ok(path) => path.parent().unwrap().join(PERSISTENT_STORAGE_FILE_NAME),
Err(_) => PathBuf::from(PERSISTENT_STORAGE_FILE_NAME),
};

let mut file = OpenOptions::new()
.create(true)
.write(true)
.read(true)
// .truncate(true) // dont truncate otherwise we cannot read the file
.open(path.clone())?;

let mut buf = String::new();

match file.read_to_string(&mut buf) {
Ok(_) => (),
Err(_) => {
file.seek(std::io::SeekFrom::Start(0)).unwrap();
file.set_len(0).unwrap();
}
};

let data: PersistentStorageData = toml::from_str(&buf).unwrap();

Ok(PersistentStorage { file, data })
}

pub fn get_waddy_recent_wads(&self) -> Option<&Vec<String>> {
self.data.waddy_recent_wads.as_ref()
}

pub fn push_waddy_recent_wads(&mut self, s: &str) -> eyre::Result<()> {
if let Some(waddy_recent_wads) = &mut self.data.waddy_recent_wads {
// remove duplicate
waddy_recent_wads.retain(|e| e != s);

// insert at the beginning
// might be a bit anal because this could be better by pushing
// but it is not that ergonomic to truncate later
waddy_recent_wads.insert(0, s.to_string());

// truncate
waddy_recent_wads.truncate(PERSISTENT_STORAGE_WADDY_RECENT_WADS_COUNT);
} else {
self.data.waddy_recent_wads = Some(vec![s.to_string()])
}

self.update()
}

// Removes an element that is sure in here
pub fn remove_waddy_recent_wads(&mut self, s: &str) -> eyre::Result<()> {
if let Some(waddy_recent_wads) = &mut self.data.waddy_recent_wads {
waddy_recent_wads.retain(|e| e != s);
}

self.update()
}

/// Writes into file
pub fn update(&mut self) -> eyre::Result<()> {
let res = toml::to_string(&self.data)?;

// hacky shit to clear file
// let's hope there's no race condition from this from the ui part
self.clear_data()?;

self.file.write_all(res.as_bytes())?;
self.file.flush()?;

Ok(())
}

pub fn clear_data(&mut self) -> eyre::Result<()> {
self.file.seek(std::io::SeekFrom::Start(0))?;
self.file.set_len(0)?;

Ok(())
}
}

0 comments on commit c046ad0

Please sign in to comment.