From 256376d0279346cd5fc095c4eac8aa937aa38f48 Mon Sep 17 00:00:00 2001
From: Cyrix126 <gupaxx@baermail.fr>
Date: Sun, 26 Jan 2025 19:01:31 +0100
Subject: [PATCH] feat: add settings to hide all elements related to a process

---
 src/app/keys.rs                           |  38 ++++----
 src/app/mod.rs                            |  24 ++++-
 src/app/panels/bottom.rs                  |  22 +++--
 src/app/panels/middle/gupax.rs            |  62 +++++++++++++
 src/app/panels/middle/mod.rs              |   1 +
 src/app/panels/middle/status/mod.rs       |   2 +
 src/app/panels/middle/status/processes.rs | 102 ++++++++++++----------
 src/app/panels/top.rs                     |  15 ++--
 src/disk/state.rs                         |  26 +++---
 src/disk/tests.rs                         |   7 +-
 src/helper/mod.rs                         |  14 ++-
 11 files changed, 217 insertions(+), 96 deletions(-)

diff --git a/src/app/keys.rs b/src/app/keys.rs
index 69bf229..cf0adc0 100644
--- a/src/app/keys.rs
+++ b/src/app/keys.rs
@@ -139,27 +139,29 @@ impl App {
             }
         // Change Tabs LEFT
         } else if key.is_z() && !wants_input {
-            match self.tab {
-                Tab::About => self.tab = Tab::Xvb,
-                Tab::Status => self.tab = Tab::About,
-                Tab::Gupax => self.tab = Tab::Status,
-                Tab::Node => self.tab = Tab::Gupax,
-                Tab::P2pool => self.tab = Tab::Node,
-                Tab::Xmrig => self.tab = Tab::P2pool,
-                Tab::XmrigProxy => self.tab = Tab::Xmrig,
-                Tab::Xvb => self.tab = Tab::XmrigProxy,
+            let tabs = Tab::from_show_processes(&self.state.gupax.show_processes);
+            let index = tabs
+                .iter()
+                .position(|t| *t == self.tab)
+                .expect("can't be on a hidden tab");
+            self.tab = if (index as i32 - 1) < 0 {
+                tabs.last()
+                    .expect("there is always 3 tabs that can not be hidden")
+                    .to_owned()
+            } else {
+                tabs[index - 1]
             };
         // Change Tabs RIGHT
         } else if key.is_x() && !wants_input {
-            match self.tab {
-                Tab::About => self.tab = Tab::Status,
-                Tab::Status => self.tab = Tab::Gupax,
-                Tab::Gupax => self.tab = Tab::Node,
-                Tab::Node => self.tab = Tab::P2pool,
-                Tab::P2pool => self.tab = Tab::Xmrig,
-                Tab::Xmrig => self.tab = Tab::XmrigProxy,
-                Tab::XmrigProxy => self.tab = Tab::Xvb,
-                Tab::Xvb => self.tab = Tab::About,
+            let tabs = Tab::from_show_processes(&self.state.gupax.show_processes);
+            let index = tabs
+                .iter()
+                .position(|t| *t == self.tab)
+                .expect("can't be on a hidden tab");
+            self.tab = if (index + 1) == tabs.len() {
+                tabs[0]
+            } else {
+                tabs[index + 1]
             };
         // Change Submenu LEFT
         } else if key.is_c() && !wants_input {
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 03198e5..722e0cd 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -619,9 +619,12 @@ impl App {
         og.version.lock().unwrap().gupax = GUPAX_VERSION.to_string();
         app.state.version.lock().unwrap().gupax = GUPAX_VERSION.to_string();
 
-        // Set saved [Tab]
+        // Set saved [Tab], only if it is not hidden
         info!("App Init | Setting saved [Tab]...");
-        app.tab = app.state.gupax.tab;
+        if Tab::from_show_processes(&app.state.gupax.show_processes).contains(&app.state.gupax.tab)
+        {
+            app.tab = app.state.gupax.tab
+        }
 
         // Set saved prefer local node to runtime
         app.p2pool_api.lock().unwrap().prefer_local_node = app.state.p2pool.prefer_local_node;
@@ -792,6 +795,23 @@ impl Tab {
             Tab::Xvb => GUPAX_TAB_XVB,
         }
     }
+    pub fn from_process_name(process: &ProcessName) -> Self {
+        match process {
+            ProcessName::Node => Tab::Node,
+            ProcessName::P2pool => Tab::P2pool,
+            ProcessName::Xmrig => Tab::Xmrig,
+            ProcessName::XmrigProxy => Tab::XmrigProxy,
+            ProcessName::Xvb => Tab::Xvb,
+        }
+    }
+    pub fn from_show_processes(processes: &[ProcessName]) -> Vec<Self> {
+        // tabs that can not be hidden
+        let mut tabs = vec![Self::About, Self::Status, Self::Gupax];
+        processes
+            .iter()
+            .for_each(|p| tabs.push(Tab::from_process_name(p)));
+        tabs
+    }
 }
 //---------------------------------------------------------------------------------------------------- [Restart] Enum
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
diff --git a/src/app/panels/bottom.rs b/src/app/panels/bottom.rs
index d222652..17e4e2f 100644
--- a/src/app/panels/bottom.rs
+++ b/src/app/panels/bottom.rs
@@ -62,17 +62,27 @@ impl crate::app::App {
                             self.os_show(ui);
                             // width of each status
                             let width_status = if !tiny_width {
-                                ((ui.available_width() / 3.0 / states.iter().count() as f32)
+                                ((ui.available_width()
+                                    / 3.0
+                                    / states
+                                        .iter()
+                                        .filter(|s| {
+                                            self.state.gupax.show_processes.contains(&s.name)
+                                        })
+                                        .count() as f32)
                                     - spacing(ui))
                                 .max(0.0)
                             } else {
                                 0.0
                             };
-                            states.iter().for_each(|p| {
-                                ui.add(Separator::default().grow(extra_separator));
-                                // width must be minimum if less than 16px is available.
-                                Self::status_process(p, ui, width_status);
-                            });
+                            states
+                                .iter()
+                                .filter(|s| self.state.gupax.show_processes.contains(&s.name))
+                                .for_each(|p| {
+                                    ui.add(Separator::default().grow(extra_separator));
+                                    // width must be minimum if less than 16px is available.
+                                    Self::status_process(p, ui, width_status);
+                                });
                         });
 
                         if let Some(name) = self.tab.linked_process() {
diff --git a/src/app/panels/middle/gupax.rs b/src/app/panels/middle/gupax.rs
index 59a6947..827c247 100644
--- a/src/app/panels/middle/gupax.rs
+++ b/src/app/panels/middle/gupax.rs
@@ -89,6 +89,22 @@ impl Gupax {
                 });
             });
 
+            // debug!("Gupaxx Tab | Rendering bool buttons");
+            ui.group(|ui| {
+                ui.vertical_centered(|ui| {
+                    ui.add(Label::new(
+                        RichText::new("Visible Processes")
+                            .underline()
+                            .color(LIGHT_GRAY),
+                    ))
+                });
+                ui.separator();
+                self.horizontal_flex_show_processes(ui, ProcessName::having_tab());
+            })
+            .response
+            .on_hover_text(
+                "Show(checked) elements (Tab/Status column/bottom status) related to a process",
+            );
             // debug!("Gupaxx Tab | Rendering bool buttons");
             ui.group(|ui| {
                 ui.vertical_centered(|ui| {
@@ -359,6 +375,52 @@ impl Gupax {
             });
         });
     }
+    /// widget: AutoStart variant and selectable label (true) or checkbox (false)
+    pub fn horizontal_flex_show_processes(&mut self, ui: &mut Ui, processes: Vec<ProcessName>) {
+        let text_style = TextStyle::Button;
+        ui.style_mut().override_text_style = Some(text_style);
+        let spacing = 2.0;
+        ScrollArea::horizontal()
+            .id_salt("show_processes")
+            .show(ui, |ui| {
+                ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
+                    let width = (((ui.available_width()) / processes.len() as f32)
+                        - ((ui.style().spacing.item_spacing.x * 2.0) + spacing))
+                        .max(0.0);
+                    // TODO: calculate minimum width needed, if ui.available width is less, show items on two lines, then on 3 etc..
+                    // checkbox padding + item spacing + text + separator
+
+                    let size = [width, 0.0];
+                    let len = processes.iter().len();
+                    for (count, process) in processes.iter().enumerate() {
+                        ui.horizontal(|ui| {
+                            ui.vertical(|ui| {
+                                ui.horizontal(|ui| {
+                                    let mut is_checked = self.show_processes.contains(process);
+                                    let widget =
+                                        Checkbox::new(&mut is_checked, process.to_string());
+
+                                    if ui.add_sized(size, widget).clicked() {
+                                        if is_checked {
+                                            self.show_processes.push(*process);
+                                            // reorganize in case the order was changed
+                                            self.show_processes.sort_unstable();
+                                        } else {
+                                            self.show_processes.retain(|p| p != process);
+                                        }
+                                    }
+                                });
+                                // add a space to prevent selectable button to be at the same line as the end of the top bar. Make it the same spacing as separators.
+                                ui.add_space(spacing * 4.0);
+                            });
+                            if count + 1 != len {
+                                ui.add(Separator::default().spacing(spacing).vertical());
+                            }
+                        });
+                    }
+                });
+            });
+    }
 }
 fn path_binary(
     path: &mut String,
diff --git a/src/app/panels/middle/mod.rs b/src/app/panels/middle/mod.rs
index 9652f53..ada028f 100644
--- a/src/app/panels/middle/mod.rs
+++ b/src/app/panels/middle/mod.rs
@@ -56,6 +56,7 @@ impl crate::app::App {
                     debug!("App | Entering [Status] Tab");
                     crate::disk::state::Status::show(
                         &mut self.state.status,
+                        &self.state.gupax.show_processes,
                         &self.pub_sys,
                         &self.node_api,
                         &self.p2pool_api,
diff --git a/src/app/panels/middle/status/mod.rs b/src/app/panels/middle/status/mod.rs
index 7357d72..a66ebed 100644
--- a/src/app/panels/middle/status/mod.rs
+++ b/src/app/panels/middle/status/mod.rs
@@ -40,6 +40,7 @@ impl Status {
     #[allow(clippy::too_many_arguments)]
     pub fn show(
         &mut self,
+        show_process: &[ProcessName],
         sys: &Arc<Mutex<Sys>>,
         node_api: &Arc<Mutex<PubNodeApi>>,
         p2pool_api: &Arc<Mutex<PubP2poolApi>>,
@@ -58,6 +59,7 @@ impl Status {
         //---------------------------------------------------------------------------------------------------- [Processes]
         if self.submenu == Submenu::Processes {
             self.processes(
+                show_process,
                 sys,
                 ui,
                 node_api,
diff --git a/src/app/panels/middle/status/processes.rs b/src/app/panels/middle/status/processes.rs
index ef6877f..3668e90 100644
--- a/src/app/panels/middle/status/processes.rs
+++ b/src/app/panels/middle/status/processes.rs
@@ -17,6 +17,7 @@ impl Status {
     #[allow(clippy::too_many_arguments)]
     pub(super) fn processes(
         &mut self,
+        show_processes: &[ProcessName],
         sys: &Arc<Mutex<Sys>>,
         ui: &mut egui::Ui,
         node_api: &Arc<Mutex<PubNodeApi>>,
@@ -38,56 +39,67 @@ impl Status {
             .show(ui, |ui| {
                 ui.horizontal(|ui| {
                     ScrollArea::horizontal().show(ui, |ui| {
-                        column_process(ui, size_column, self.show_system, |ui| {
+                        column_process(ui, size_column, true, |ui| {
                             gupax(ui, sys);
                         });
-                        column_process(ui, size_column, self.show_node, |ui| {
-                            node(ui, states.is_alive(ProcessName::Node), node_api);
-                        });
-                        column_process(ui, size_column, self.show_p2pool, |ui| {
-                            p2pool(
-                                ui,
-                                states.is_alive(ProcessName::P2pool),
-                                p2pool_api,
-                                p2pool_img,
-                            );
-                        });
-                        column_process(ui, size_column, self.show_xmrig, |ui| {
-                            xmrig(
-                                ui,
-                                states.is_alive(ProcessName::Xmrig),
-                                xmrig_api,
-                                xmrig_img,
-                                max_threads,
-                            );
-                        });
-                        column_process(ui, size_column, self.show_proxy, |ui| {
-                            xmrig_proxy(
-                                ui,
-                                states.is_alive(ProcessName::XmrigProxy),
-                                xmrig_proxy_api,
-                            );
-                        });
-                        column_process(ui, size_column, self.show_xvb, |ui| {
-                            xvb(ui, states.is_alive(ProcessName::Xvb), xvb_api);
-                        });
+                        column_process(
+                            ui,
+                            size_column,
+                            show_processes.contains(&ProcessName::Node),
+                            |ui| {
+                                node(ui, states.is_alive(ProcessName::Node), node_api);
+                            },
+                        );
+                        column_process(
+                            ui,
+                            size_column,
+                            show_processes.contains(&ProcessName::P2pool),
+                            |ui| {
+                                p2pool(
+                                    ui,
+                                    states.is_alive(ProcessName::P2pool),
+                                    p2pool_api,
+                                    p2pool_img,
+                                );
+                            },
+                        );
+                        column_process(
+                            ui,
+                            size_column,
+                            show_processes.contains(&ProcessName::Xmrig),
+                            |ui| {
+                                xmrig(
+                                    ui,
+                                    states.is_alive(ProcessName::Xmrig),
+                                    xmrig_api,
+                                    xmrig_img,
+                                    max_threads,
+                                );
+                            },
+                        );
+                        column_process(
+                            ui,
+                            size_column,
+                            show_processes.contains(&ProcessName::XmrigProxy),
+                            |ui| {
+                                xmrig_proxy(
+                                    ui,
+                                    states.is_alive(ProcessName::XmrigProxy),
+                                    xmrig_proxy_api,
+                                );
+                            },
+                        );
+                        column_process(
+                            ui,
+                            size_column,
+                            show_processes.contains(&ProcessName::Xvb),
+                            |ui| {
+                                xvb(ui, states.is_alive(ProcessName::Xvb), xvb_api);
+                            },
+                        );
                     });
                 });
             });
-        // buttons to hide
-
-        ScrollArea::horizontal().show(ui, |ui| {
-            ui.label("Visible columns:");
-            ui.add_space(SPACE);
-            ui.horizontal(|ui| {
-                ui.checkbox(&mut self.show_system, "System");
-                ui.checkbox(&mut self.show_node, "Node");
-                ui.checkbox(&mut self.show_p2pool, "P2Pool");
-                ui.checkbox(&mut self.show_xmrig, "XMRig");
-                ui.checkbox(&mut self.show_proxy, "XMRig-Proxy");
-                ui.checkbox(&mut self.show_xvb, "XvB");
-            });
-        });
     }
 }
 
diff --git a/src/app/panels/top.rs b/src/app/panels/top.rs
index 83e76db..8415da5 100644
--- a/src/app/panels/top.rs
+++ b/src/app/panels/top.rs
@@ -19,11 +19,13 @@ use crate::app::Tab;
 use egui::TextStyle;
 use egui::{ScrollArea, SelectableLabel, Separator, TopBottomPanel, Ui};
 use log::debug;
-use strum::{EnumCount, IntoEnumIterator};
 
 impl crate::app::App {
     pub fn top_panel(&mut self, ctx: &egui::Context) {
         debug!("App | Rendering TOP tabs");
+        let tabs = Tab::from_show_processes(&self.state.gupax.show_processes);
+        dbg!(&tabs);
+
         TopBottomPanel::top("top").show(ctx, |ui| {
             // low spacing to shrink and be able to show all tabs on one line on 640x480
             ui.style_mut().spacing.item_spacing.x = 4.0;
@@ -39,20 +41,21 @@ impl crate::app::App {
                     .size
                     * 2.75;
                 // width = (width - / number of tab) - (space between widget * 2.0 + space of separator / 2.0)
-                let width = (((self.size.x) / Tab::COUNT as f32)
+                let width = (((self.size.x) / tabs.len() as f32)
                     - ((ui.style().spacing.item_spacing.x * 2.0) + (spacing_separator / 2.0)))
                     .max(0.0);
                 // height of tab menu relative to size of text. coeff 2.75 is arbitrary but good enough to be easily clickable.
-                self.tabs(ui, [width, height], spacing_separator);
+                self.tabs(ui, [width, height], spacing_separator, tabs);
             });
         });
     }
 
-    fn tabs(&mut self, ui: &mut Ui, size: [f32; 2], spacing_separator: f32) {
+    fn tabs(&mut self, ui: &mut Ui, size: [f32; 2], spacing_separator: f32, tabs: Vec<Tab>) {
         ScrollArea::horizontal()
             .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
             .show(ui, |ui| {
-                for (count, tab) in Tab::iter().enumerate() {
+                let nb_tabs = tabs.len();
+                for (count, tab) in tabs.into_iter().enumerate() {
                     ui.horizontal(|ui| {
                         ui.vertical(|ui| {
                             // we don't want y item spacing to influence the added space
@@ -72,7 +75,7 @@ impl crate::app::App {
                             // add a space to prevent selectable button to be at the same line as the end of the top bar. Make it the same spacing as separators.
                             ui.add_space(spacing_separator);
                         });
-                        if count + 1 != Tab::COUNT {
+                        if count + 1 != nb_tabs {
                             ui.add(Separator::default().spacing(spacing_separator).vertical());
                         }
                     });
diff --git a/src/disk/state.rs b/src/disk/state.rs
index 8da7b37..a737a21 100644
--- a/src/disk/state.rs
+++ b/src/disk/state.rs
@@ -179,12 +179,6 @@ pub struct Status {
     pub manual_hash: bool,
     pub hashrate: f64,
     pub hash_metric: Hash,
-    pub show_system: bool,
-    pub show_node: bool,
-    pub show_p2pool: bool,
-    pub show_xmrig: bool,
-    pub show_proxy: bool,
-    pub show_xvb: bool,
 }
 
 #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
@@ -204,6 +198,7 @@ pub struct Gupax {
     pub selected_scale: f32,
     pub tab: Tab,
     pub ratio: Ratio,
+    pub show_processes: Vec<ProcessName>,
 }
 
 #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
@@ -586,12 +581,6 @@ impl Default for Status {
             manual_hash: false,
             hashrate: 1.0,
             hash_metric: Hash::default(),
-            show_system: true,
-            show_node: true,
-            show_p2pool: true,
-            show_xmrig: true,
-            show_proxy: true,
-            show_xvb: true,
         }
     }
 }
@@ -614,6 +603,7 @@ impl Default for Gupax {
             selected_scale: APP_DEFAULT_SCALE,
             ratio: Ratio::Width,
             tab: Tab::Xvb,
+            show_processes: ProcessName::having_tab(),
         }
     }
 }
@@ -772,3 +762,15 @@ pub enum StartOptionsMode {
     Advanced,
     Custom,
 }
+
+impl ProcessName {
+    pub fn having_tab() -> Vec<ProcessName> {
+        vec![
+            ProcessName::Node,
+            ProcessName::P2pool,
+            ProcessName::Xmrig,
+            ProcessName::XmrigProxy,
+            ProcessName::Xvb,
+        ]
+    }
+}
diff --git a/src/disk/tests.rs b/src/disk/tests.rs
index bd8177d..01fd970 100644
--- a/src/disk/tests.rs
+++ b/src/disk/tests.rs
@@ -50,6 +50,7 @@ mod test {
 			tab = "About"
 			ratio = "Width"
 			bundled = false
+            show_processes = ["Node", "P2pool", "Xmrig", "XmrigProxy", "Xvb"]
 
 			[gupax.auto]
             update = false
@@ -65,12 +66,6 @@ mod test {
 			manual_hash = false
 			hashrate = 1241.23
 			hash_metric = "Hash"
-			show_system = true
-			show_node = true
-			show_p2pool = true
-			show_xmrig = true
-			show_proxy = true
-			show_xvb = true
 			
 
 			[p2pool]
diff --git a/src/helper/mod.rs b/src/helper/mod.rs
index 48b277f..a3d948c 100644
--- a/src/helper/mod.rs
+++ b/src/helper/mod.rs
@@ -253,7 +253,19 @@ impl Default for ProcessSignal {
 }
 
 #[derive(
-    Copy, Clone, Eq, PartialEq, Debug, Display, EnumIter, EnumCount, Serialize, Deserialize, Default,
+    Copy,
+    Clone,
+    Eq,
+    PartialEq,
+    Debug,
+    Display,
+    EnumIter,
+    EnumCount,
+    Serialize,
+    Deserialize,
+    Default,
+    PartialOrd,
+    Ord,
 )]
 pub enum ProcessName {
     Node,