implement Undo functionality (#32)

* implement undo functionality

* several improvements to the undo functionality

* run fmt

* combine imports
This commit is contained in:
nyx
2024-10-15 15:11:23 -04:00
committed by GitHub
parent c636b5f349
commit 879c145fb7
2 changed files with 112 additions and 24 deletions

View File

@@ -76,6 +76,7 @@ pub struct ConfigGUI {
sidebar: StackSidebar,
load_config_button: Button,
save_config_button: Button,
pub gear_menu: Rc<RefCell<Popover>>,
}
impl ConfigGUI {
@@ -94,6 +95,28 @@ impl ConfigGUI {
let gear_button = Button::from_icon_name("emblem-system-symbolic");
header_bar.pack_start(&gear_button);
let gear_menu = Rc::new(RefCell::new(Popover::new()));
gear_menu.borrow().set_parent(&gear_button);
let gear_menu_box = Box::new(Orientation::Vertical, 5);
gear_menu_box.set_margin_top(5);
gear_menu_box.set_margin_bottom(5);
gear_menu_box.set_margin_start(5);
gear_menu_box.set_margin_end(5);
let save_config_button = Button::with_label("Save HyprGUI Config");
let load_config_button = Button::with_label("Load HyprGUI Config");
gear_menu_box.append(&load_config_button);
gear_menu_box.append(&save_config_button);
gear_menu.borrow().set_child(Some(&gear_menu_box));
let gear_menu_clone = gear_menu.clone();
gear_button.connect_clicked(move |_| {
gear_menu_clone.borrow().popup();
});
let tooltip_button = Button::new();
let question_mark_icon = Image::from_icon_name("dialog-question-symbolic");
tooltip_button.set_child(Some(&question_mark_icon));
@@ -136,27 +159,6 @@ impl ConfigGUI {
sidebar.set_stack(&stack);
sidebar.set_width_request(200);
let popover = Popover::new();
popover.set_parent(&gear_button);
let popover_box = Box::new(Orientation::Vertical, 5);
popover_box.set_margin_top(5);
popover_box.set_margin_bottom(5);
popover_box.set_margin_start(5);
popover_box.set_margin_end(5);
let save_config_button = Button::with_label("Save HyprGUI Config");
let load_config_button = Button::with_label("Load HyprGUI Config");
popover_box.append(&load_config_button);
popover_box.append(&save_config_button);
popover.set_child(Some(&popover_box));
gear_button.connect_clicked(move |_| {
popover.popup();
});
ConfigGUI {
window,
config_widgets,
@@ -167,6 +169,7 @@ impl ConfigGUI {
sidebar,
load_config_button,
save_config_button,
gear_menu,
}
}

View File

@@ -1,10 +1,11 @@
use gtk::{prelude::*, Application};
use gtk::{prelude::*, Application, Button};
use hyprparser::parse_config;
use std::{cell::RefCell, env, fs, path::Path, path::PathBuf, rc::Rc};
mod gui;
const CONFIG_PATH: &str = ".config/hypr/hyprland.conf";
const BACKUP_SUFFIX: &str = "-bak";
fn main() {
let app = Application::builder()
@@ -28,7 +29,7 @@ fn build_ui(app: &Application) {
true,
);
} else {
let config_str = match fs::read_to_string(config_path_full) {
let config_str = match fs::read_to_string(&config_path_full) {
Ok(s) => s,
Err(e) => {
gui.borrow_mut().custom_error_popup_critical(
@@ -46,6 +47,18 @@ fn build_ui(app: &Application) {
gui.borrow().save_button.connect_clicked(move |_| {
save_config_file(gui_clone.clone());
});
let undo_button = Button::with_label("Undo Changes");
let gui_clone = gui.clone();
undo_button.connect_clicked(move |_| {
undo_changes(gui_clone.clone());
});
if let Some(gear_menu_box) = gui.borrow().gear_menu.borrow().child() {
if let Some(box_widget) = gear_menu_box.downcast_ref::<gtk::Box>() {
box_widget.append(&undo_button);
}
}
}
gui.borrow().window.present();
@@ -54,6 +67,12 @@ fn build_ui(app: &Application) {
fn save_config_file(gui: Rc<RefCell<gui::ConfigGUI>>) {
let mut gui_ref = gui.borrow_mut();
let path = get_config_path();
let backup_path = path.with_file_name(format!(
"{}{}",
path.file_name().unwrap().to_str().unwrap(),
BACKUP_SUFFIX
));
let config_str = match fs::read_to_string(&path) {
Ok(s) => s,
Err(e) => {
@@ -62,7 +81,7 @@ fn save_config_file(gui: Rc<RefCell<gui::ConfigGUI>>) {
&format!("Failed to read the configuration file: {}", e),
true,
);
String::new()
return;
}
};
@@ -70,6 +89,17 @@ fn save_config_file(gui: Rc<RefCell<gui::ConfigGUI>>) {
let changes = gui_ref.get_changes();
if !changes.borrow().is_empty() {
if !backup_path.exists() {
if let Err(e) = fs::copy(&path, &backup_path) {
gui_ref.custom_error_popup(
"Backup failed",
&format!("Failed to create backup: {}", e),
true,
);
return;
}
}
gui_ref.apply_changes(&mut parsed_config);
let updated_config_str = parsed_config.to_string();
@@ -89,6 +119,61 @@ fn save_config_file(gui: Rc<RefCell<gui::ConfigGUI>>) {
}
}
fn undo_changes(gui: Rc<RefCell<gui::ConfigGUI>>) {
let mut gui_ref = gui.borrow_mut();
let path = get_config_path();
let backup_path = path.with_file_name(format!(
"{}{}",
path.file_name().unwrap().to_str().unwrap(),
BACKUP_SUFFIX
));
if backup_path.exists() {
match fs::copy(&backup_path, &path) {
Ok(_) => {
println!("Configuration restored from backup");
if let Ok(config_str) = fs::read_to_string(&path) {
let parsed_config = parse_config(&config_str);
gui_ref.load_config(&parsed_config);
if let Err(e) = fs::remove_file(&backup_path) {
gui_ref.custom_error_popup(
"Backup Deletion Failed",
&format!("Failed to delete the backup file: {}", e),
true,
);
} else {
gui_ref.custom_info_popup(
"Undo Successful",
"Configuration restored from backup and backup file deleted.",
true,
);
}
} else {
gui_ref.custom_error_popup(
"Reload Failed",
"Failed to reload the configuration after undo.",
true,
);
}
}
Err(e) => {
gui_ref.custom_error_popup(
"Undo Failed",
&format!("Failed to restore from backup: {}", e),
true,
);
}
}
} else {
gui_ref.custom_error_popup(
"Undo Failed",
"No backup file found. Save changes at least once to create a backup.",
true,
);
}
}
fn get_config_path() -> PathBuf {
Path::new(&env::var("HOME").unwrap_or_else(|_| ".".to_string())).join(CONFIG_PATH)
}