forked from github-starred/hyprgui
implement Undo functionality (#32)
* implement undo functionality * several improvements to the undo functionality * run fmt * combine imports
This commit is contained in:
45
src/gui.rs
45
src/gui.rs
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
91
src/main.rs
91
src/main.rs
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user