add basic settings

This commit is contained in:
2025-04-10 13:56:00 +01:00
parent f9f326904e
commit ba49ed0e67
3 changed files with 133 additions and 21 deletions
+67 -16
View File
@@ -1,3 +1,5 @@
mod settings;
use crate::{
features::{
self,
@@ -6,6 +8,7 @@ use crate::{
force_close::ForceClose,
launch::{Launch, Platform},
},
gui::settings::Settings,
util::{
self,
consts::{ENHANCED, GTA_WINDOW_TITLE, LEGACY},
@@ -21,7 +24,6 @@ use std::{
time::{Duration, Instant},
};
const THEME: catppuccin_egui::Theme = catppuccin_egui::MOCHA;
const WINDOW_SIZE: [f32; 2] = [240.0, 240.0];
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
dirs::config_local_dir()
@@ -33,12 +35,14 @@ static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
#[derive(Serialize, Deserialize)]
struct PersistentState {
launcher: Platform,
settings: Settings,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub enum Stage {
#[default]
Main,
Settings,
About,
}
@@ -54,6 +58,7 @@ pub struct Flags {
#[derive(Debug)]
pub struct App {
settings: Settings,
stage: Stage,
flags: Flags,
pub sysinfo: sysinfo::System,
@@ -67,6 +72,7 @@ pub struct App {
impl Default for App {
fn default() -> Self {
Self {
settings: Settings::default(),
stage: Stage::default(),
flags: Flags::default(),
sysinfo: sysinfo::System::new_all(),
@@ -82,13 +88,7 @@ impl Default for App {
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if !self.flags.initialized {
catppuccin_egui::set_theme(ctx, THEME);
egui_extras::install_image_loaders(ctx);
if let Ok(config) = fs::read_to_string(CONFIG_PATH.as_path()) {
if let Ok(persistent_state) = serde_json::from_str::<PersistentState>(&config) {
self.launch.selected = persistent_state.launcher;
}
}
ctx.style_mut(|style| {
style.spacing.item_spacing = egui::vec2(4.0, 4.0);
style.interaction.selectable_labels = false;
@@ -96,12 +96,14 @@ impl eframe::App for App {
self.flags.elevated = util::win::is_elevated();
self.flags.initialized = true;
}
catppuccin_egui::set_theme(ctx, self.settings.theme.into());
self.run_timers();
egui::TopBottomPanel::bottom("bottom_panel")
.exact_height(25.0)
.show(ctx, |ui| {
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
ui.selectable_value(&mut self.stage, Stage::Main, "Main");
ui.selectable_value(&mut self.stage, Stage::Settings, "Settings");
ui.selectable_value(&mut self.stage, Stage::About, "About");
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let button = ui
@@ -109,7 +111,9 @@ impl eframe::App for App {
.on_hover_text("Relaunch ourselves as administrator.")
.on_disabled_hover_text("We are already running elevated.");
if button.clicked() {
util::win::elevate(&mut self.flags.closing);
util::win::elevate(util::win::ElevationExitMethod::Gentle(
&mut self.flags.closing,
));
}
});
});
@@ -123,6 +127,7 @@ impl eframe::App for App {
self.header(ui, "Network");
self.show_network(ctx, ui);
}
Stage::Settings => self.show_settings(ctx, ui),
Stage::About => self.show_about(ctx, ui),
});
if check_debug_keycombo_pressed(ctx) {
@@ -183,7 +188,7 @@ impl App {
if ui.button("Launch").clicked() {
features::launch::launch(&self.launch.selected);
};
egui::ComboBox::from_label("")
egui::ComboBox::from_id_salt("")
.selected_text(self.launch.selected.to_string())
.width(120.0)
.show_ui(ui, |ui| {
@@ -261,7 +266,10 @@ impl App {
fn show_network(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
egui::Frame::new()
.inner_margin(egui::vec2(4.0, 4.0))
.stroke(egui::Stroke::new(1.0, THEME.overlay1))
.stroke(egui::Stroke::new(
1.0,
self.settings.theme.to_catppuccin().overlay1,
))
.show(ui, |ui| {
let response = ui.add_enabled_ui(self.flags.elevated, |ui| {
let label = ui.label("Game's network access");
@@ -312,6 +320,37 @@ impl App {
});
}
fn show_settings(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
ui.horizontal(|ui| {
ui.label("Theme");
egui::ComboBox::from_id_salt("")
.selected_text(self.settings.theme.to_string())
.show_ui(ui, |ui| {
ui.selectable_value(
&mut self.settings.theme,
settings::Theme::CatppuccinLatte,
settings::Theme::CatppuccinLatte.to_string(),
);
ui.selectable_value(
&mut self.settings.theme,
settings::Theme::CatppuccinFrappe,
settings::Theme::CatppuccinFrappe.to_string(),
);
ui.selectable_value(
&mut self.settings.theme,
settings::Theme::CatppuccinMacchiato,
settings::Theme::CatppuccinMacchiato.to_string(),
);
ui.selectable_value(
&mut self.settings.theme,
settings::Theme::CatppuccinMocha,
settings::Theme::CatppuccinMocha.to_string(),
);
});
});
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
}
fn show_debug(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
ui.collapsing("times", |ui| {
ui.label(format!(
@@ -355,6 +394,7 @@ impl Drop for App {
// save any persistent state to config file
let persistent_state = PersistentState {
launcher: self.launch.selected,
settings: self.settings.clone(),
};
let config_path = CONFIG_PATH.as_path();
let config_path_parent = config_path.parent().unwrap();
@@ -394,6 +434,22 @@ fn load_icon() -> egui::IconData {
}
}
fn app_creator(
_cc: &eframe::CreationContext<'_>,
) -> Result<Box<dyn eframe::App>, Box<dyn std::error::Error + Send + Sync>> {
let mut app = Box::<App>::default();
if let Ok(config) = fs::read_to_string(CONFIG_PATH.as_path()) {
if let Ok(persistent_state) = serde_json::from_str::<PersistentState>(&config) {
app.launch.selected = persistent_state.launcher;
app.settings = persistent_state.settings;
}
}
if app.settings.start_elevated && !util::win::is_elevated() {
util::win::elevate(util::win::ElevationExitMethod::Forced);
}
Ok(app)
}
pub fn run() {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
@@ -404,10 +460,5 @@ pub fn run() {
centered: true,
..Default::default()
};
eframe::run_native(
"GTA Tools",
options,
Box::new(|_cc| Ok(Box::<App>::default())),
)
.unwrap();
eframe::run_native("GTA Tools", options, Box::new(app_creator)).unwrap();
}
+54
View File
@@ -0,0 +1,54 @@
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Theme {
CatppuccinLatte,
CatppuccinFrappe,
CatppuccinMacchiato,
CatppuccinMocha,
}
impl Into<catppuccin_egui::Theme> for Theme {
fn into(self) -> catppuccin_egui::Theme {
match self {
Theme::CatppuccinLatte => catppuccin_egui::LATTE,
Theme::CatppuccinFrappe => catppuccin_egui::FRAPPE,
Theme::CatppuccinMacchiato => catppuccin_egui::MACCHIATO,
Theme::CatppuccinMocha => catppuccin_egui::MOCHA,
}
}
}
impl Display for Theme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let x = match self {
Theme::CatppuccinLatte => "Catppuccin Latte",
Theme::CatppuccinFrappe => "Catppuccin Frappe",
Theme::CatppuccinMacchiato => "Catppuccin Macchiato",
Theme::CatppuccinMocha => "Catppuccin Mocha",
};
write!(f, "{x}")
}
}
impl Theme {
pub fn to_catppuccin(&self) -> catppuccin_egui::Theme {
(*self).into()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Settings {
pub theme: Theme,
pub start_elevated: bool,
}
impl Default for Settings {
fn default() -> Self {
Self {
theme: Theme::CatppuccinMocha,
start_elevated: false,
}
}
}