From f208e771603c9bf00435c920f23365214a28c5e8 Mon Sep 17 00:00:00 2001 From: lava1879 Date: Sat, 20 Dec 2025 16:07:11 +0100 Subject: [PATCH 1/3] feat: add auto-borderless apps, startup options and admin elevation - add "Automatically make borderless" checkbox for selected windows - persist auto-borderless app list in config (ihateborders_config.json) - add settings window with option to run ihateborders on windows startup - add option to launch at startup with administrator privileges (using scheduled task with highest privileges) - fix ui refresh issues: button text and auto-checkbox now update instantly - improve overall responsiveness and visual feedback --- .gitignore | 5 + Cargo.lock | 28 +++- Cargo.toml | 4 +- src/app.rs | 336 ++++++++++++++++++++++++++++++++++++------ src/config.rs | 98 ++++++++++++ src/main.rs | 61 +++++++- src/startup.rs | 112 ++++++++++++++ src/ui.rs | 37 ++++- src/window_manager.rs | 11 +- 9 files changed, 638 insertions(+), 54 deletions(-) create mode 100644 src/config.rs create mode 100644 src/startup.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..5225d67 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ /target +/.idea/.gitignore +/.idea/ihateborders.iml +/.idea/modules.xml +/.idea/dictionaries/project.xml +/.idea/vcs.xml diff --git a/Cargo.lock b/Cargo.lock index 8de2607..287a9a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -821,13 +821,15 @@ dependencies = [ [[package]] name = "ihateborders" -version = "1.1.1" +version = "1.2.0" dependencies = [ "anyhow", "eframe", "egui", "embed-resource", "image", + "serde", + "serde_json", "windows", ] @@ -854,6 +856,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jni" version = "0.21.1" @@ -1581,6 +1589,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "same-file" version = "1.0.6" @@ -1628,6 +1642,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_spanned" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index aed14d7..77a4adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ihateborders" -version = "1.1.1" +version = "1.2.0" edition = "2024" [dependencies] @@ -20,6 +20,8 @@ windows = { version = "0.61", features = [ ] } anyhow = { version = "1.0", default-features = false } image = { version = "0.25", default-features = false, features = ["ico", "png"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" [build-dependencies] embed-resource = { version = "3.0", default-features = false } diff --git a/src/app.rs b/src/app.rs index ea37c81..cba59bf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,4 +1,6 @@ use crate::{ + config::Config, + startup::{is_elevated, relaunch_elevated, remove_scheduled_task, task_exists}, ui::{self, IconCacheInterface}, window_manager::{DisplayInfo, WindowManager}, }; @@ -87,34 +89,44 @@ pub struct BorderlessApp { window_manager: WindowManager, selected_window: Option, - last_refresh: std::time::Instant, + last_refresh: Instant, icon_cache: IconCache, resize_to_screen: bool, selected_display: Option, displays: Vec, needs_repaint: bool, refresh_receiver: Option>>, + config: Config, + auto_borderless_enabled: bool, + show_settings: bool, + applied_auto_borderless: std::collections::HashSet, } impl BorderlessApp { - pub fn new(cc: &eframe::CreationContext<'_>) -> Self + pub fn new(cc: &eframe::CreationContext<'_>, open_settings: bool) -> Self { ui::setup_dark_theme(&cc.egui_ctx); let window_manager = WindowManager::new(); let displays = window_manager.get_displays(); + + let config = Config::load().unwrap_or_default(); let mut app = Self { window_manager, selected_window: None, - last_refresh: std::time::Instant::now(), + last_refresh: Instant::now(), icon_cache: IconCache::new(), resize_to_screen: true, selected_display: if !displays.is_empty() { Some(0) } else { None }, displays, needs_repaint: false, refresh_receiver: None, + config, + auto_borderless_enabled: false, + show_settings: open_settings, + applied_auto_borderless: std::collections::HashSet::new(), }; app.start_async_refresh(); @@ -136,7 +148,7 @@ impl BorderlessApp if let Ok(windows) = receiver.try_recv() { if !windows.is_empty() { self.window_manager.set_windows(windows); - self.last_refresh = std::time::Instant::now(); + self.last_refresh = Instant::now(); self.needs_repaint = true; if let Some(selected) = self.selected_window { @@ -144,6 +156,7 @@ impl BorderlessApp self.selected_window = None; } } + self.apply_auto_borderless(); } self.refresh_receiver = None; } @@ -154,6 +167,32 @@ impl BorderlessApp self.start_async_refresh(); } } + + fn apply_auto_borderless(&mut self) + { + let windows = self.window_manager.get_windows().to_vec(); + + for window in windows.iter() { + if self.config.is_auto_borderless(&window.process_name) + && !window.is_borderless + && !self.applied_auto_borderless.contains(&window.hwnd) + { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if let Ok(_) = self.window_manager.toggle_borderless( + window.hwnd, + self.resize_to_screen, + selected_display, + ) { + self.applied_auto_borderless.insert(window.hwnd); + } + } + } + } fn handle_keyboard_input(&mut self, ctx: &egui::Context) { @@ -164,68 +203,271 @@ impl BorderlessApp } if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { - self.selected_window = None; + if self.show_settings { + self.show_settings = false; + } else { + self.selected_window = None; + } self.needs_repaint = true; } } fn handle_window_action(&mut self, window_index: usize) { - let windows = self.window_manager.get_windows(); - if let Some(window) = windows.get(window_index) { - let selected_display = if self.resize_to_screen { - self.selected_display.and_then(|idx| self.displays.get(idx)) - } else { - None - }; - - if let Err(e) = self.window_manager.toggle_borderless( - window.hwnd, - self.resize_to_screen, - selected_display, - ) { - eprintln!("Failed to toggle borderless for window '{}': {}", window.title, e); - } else { - self.refresh_receiver = None; - self.start_async_refresh(); - self.needs_repaint = true; + let hwnd = self.window_manager.get_windows()[window_index].hwnd; + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if let Err(e) = self.window_manager.toggle_borderless( + hwnd, + self.resize_to_screen, + selected_display, + ) { + eprintln!("Failed to toggle borderless for window: {}", e); + } else { + if let Some(window) = self.window_manager.get_window_mut(window_index) { + window.is_borderless = !window.is_borderless; } + self.refresh_receiver = None; + self.start_async_refresh(); + self.needs_repaint = true; + } + } + + fn save_config(&self) + { + if let Err(e) = self.config.save() { + eprintln!("Failed to save config: {}", e); } } + + fn handle_settings_window(&mut self, ctx: &egui::Context) + { + if !self.show_settings { + return; + } + + egui::Window::new("Settings") + .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) + .collapsible(false) + .resizable(false) + .show(ctx, |ui| { + ui.set_min_width(300.0); + let mut run_on_startup = self.config.run_on_startup; + if ui.checkbox(&mut run_on_startup, "Run ihateborders when my computer starts").changed() { + if run_on_startup { + if !is_elevated() { + self.config.run_on_startup = true; + self.save_config(); + if let Err(e) = relaunch_elevated(&["--create-startup-task"]) { + eprintln!("Failed to relaunch elevated: {}", e); + self.config.run_on_startup = false; + self.save_config(); + } + } else { + match crate::startup::create_scheduled_task(false) { + Ok(_) => { + self.config.run_on_startup = true; + self.save_config(); + } + Err(e) => { + eprintln!("Failed to create startup task: {}", e); + } + } + } + } else { + self.config.run_on_startup = false; + self.config.startup_admin = false; + if task_exists() { + let _ = remove_scheduled_task(); + } + self.save_config(); + } + } + ui.add_enabled_ui(self.config.run_on_startup, |ui| { + let mut startup_admin = self.config.startup_admin; + + if ui.checkbox(&mut startup_admin, "Enable startup with administrator privileges").changed() { + if startup_admin { + if !is_elevated() { + self.config.startup_admin = true; + self.save_config(); + if let Err(e) = relaunch_elevated(&["--install-admin-task"]) { + eprintln!("Failed to relaunch elevated: {}", e); + self.config.startup_admin = false; + self.save_config(); + } + } else { + match crate::startup::create_scheduled_task(true) { + Ok(_) => { + self.config.startup_admin = true; + self.save_config(); + } + Err(e) => { + eprintln!("Failed to create scheduled task: {}", e); + } + } + } + } else { + if !is_elevated() { + self.config.startup_admin = false; + self.save_config(); + if let Err(e) = relaunch_elevated(&["--create-startup-task"]) { + eprintln!("Failed to relaunch elevated: {}", e); + } + } else { + if task_exists() { + let _ = remove_scheduled_task(); + } + + match crate::startup::create_scheduled_task(false) { + Ok(_) => { + self.config.startup_admin = false; + self.save_config(); + } + Err(e) => { + eprintln!("Failed to recreate startup task: {}", e); + } + } + } + } + } + }); + + ui.add_space(10.0); + + if ui.button("Close").clicked() { + self.show_settings = false; + } + }); + } } impl eframe::App for BorderlessApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + if let Some(pos) = ctx.input(|i| i.viewport().outer_rect).map(|r| r.min) { + if self.config.window_position.as_ref().map_or(true, |saved_pos| { + (saved_pos.x - pos.x).abs() > 1.0 || (saved_pos.y - pos.y).abs() > 1.0 + }) { + self.config.window_position = Some(crate::config::WindowPosition { + x: pos.x, + y: pos.y, + }); + } + } + self.handle_refresh(); self.handle_keyboard_input(ctx); self.icon_cache.cleanup_expired(); - egui::CentralPanel::default().show(ctx, |ui| { - let windows = self.window_manager.get_windows(); + egui::CentralPanel::default().show(ctx, |ui| { + + ui::render_header(ui, self.window_manager.get_windows().len()); - ui::render_header(ui, windows.len()); + ui::render_window_selector( + ui, + self.window_manager.get_windows(), + &mut self.selected_window, + &mut self.icon_cache, + ); - ui::render_window_selector( - ui, - windows, - &mut self.selected_window, - &mut self.icon_cache, - ); + ui::render_position_checkbox(ui, &mut self.resize_to_screen); - ui::render_position_checkbox(ui, &mut self.resize_to_screen); + if self.resize_to_screen { + ui::render_display_selector(ui, &self.displays, &mut self.selected_display); + } - if self.resize_to_screen { - ui::render_display_selector(ui, &self.displays, &mut self.selected_display); + let mut auto_changed = false; + let checkbox_enabled = self.selected_window.is_some(); + + ui::render_auto_borderless_checkbox( + ui, + &mut self.auto_borderless_enabled, + &mut auto_changed, + checkbox_enabled, + ); + + if auto_changed { + if let Some(index) = self.selected_window { + let current_windows = self.window_manager.get_windows(); + let window = ¤t_windows[index]; + let process_name = window.process_name.clone(); + let hwnd = window.hwnd; + let currently_borderless = window.is_borderless; + + let should_toggle = (self.auto_borderless_enabled && !currently_borderless) + || (!self.auto_borderless_enabled && currently_borderless); + + if should_toggle { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if self.window_manager + .toggle_borderless(hwnd, self.resize_to_screen, selected_display) + .is_ok() + { + if let Some(w) = self.window_manager.get_window_mut(index) { + w.is_borderless = !w.is_borderless; + } + self.needs_repaint = true; + self.refresh_receiver = None; + self.start_async_refresh(); + } + } + + if self.auto_borderless_enabled { + self.config.add_auto_borderless(process_name); + if !currently_borderless && should_toggle { + self.applied_auto_borderless.insert(hwnd); + } + } else { + self.config.remove_auto_borderless(&process_name); + if currently_borderless && should_toggle { + self.applied_auto_borderless.remove(&hwnd); + } + } + self.save_config(); } + } - if let Some(window_index) = ui::render_action_button(ui, windows, self.selected_window) - { - self.handle_window_action(window_index); + if let Some(index) = self.selected_window { + let windows = self.window_manager.get_windows(); + if let Some(window) = windows.get(index) { + self.auto_borderless_enabled = self.config.is_auto_borderless(&window.process_name); + } + } + + let action_button_enabled = self.selected_window.is_some() && !self.auto_borderless_enabled; + + let clicked_index = ui::render_action_button( + ui, + self.window_manager.get_windows(), + self.selected_window, + action_button_enabled, + ); + + if let Some(window_index) = clicked_index { + self.handle_window_action(window_index); + } + + ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { + ui.add_space(5.0); + if ui.button("⚙ Settings").clicked() { + self.show_settings = !self.show_settings; } }); + }); + + self.handle_settings_window(ctx); if self.needs_repaint { self.needs_repaint = false; @@ -234,20 +476,28 @@ impl eframe::App for BorderlessApp ctx.request_repaint_after(Duration::from_secs(5)); } } + + fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) + { + let _ = self.config.save(); + } } pub fn create_app_options() -> eframe::NativeOptions { let icon_data = load_icon(); + let config = Config::load().unwrap_or_default(); + let initial_position = config.window_position.map(|pos| egui::Pos2::new(pos.x, pos.y)); eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_title("ihateborders") - .with_inner_size([350.0, 320.0]) - .with_min_inner_size([350.0, 320.0]) - .with_max_inner_size([350.0, 320.0]) + .with_inner_size([350.0, 360.0]) + .with_min_inner_size([350.0, 360.0]) + .with_max_inner_size([350.0, 360.0]) .with_resizable(false) .with_maximize_button(false) + .with_position(initial_position.unwrap_or(egui::Pos2::new(100.0, 100.0))) .with_icon(icon_data), ..Default::default() } @@ -262,5 +512,5 @@ fn load_icon() -> egui::IconData let (width, height) = image.dimensions(); let rgba = image.into_raw(); - egui::IconData { rgba, width: width as u32, height: height as u32 } -} + egui::IconData { rgba, width, height } +} \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..6bcaf89 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,98 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; +use std::{ + fs, + path::PathBuf, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowPosition +{ + pub x: f32, + pub y: f32, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config +{ + #[serde(default)] + pub auto_borderless_apps: Vec, + + #[serde(default)] + pub run_on_startup: bool, + + #[serde(default)] + pub startup_admin: bool, + + #[serde(default)] + pub window_position: Option, +} + +impl Default for Config +{ + fn default() -> Self + { + Self { + auto_borderless_apps: Vec::new(), + run_on_startup: false, + startup_admin: false, + window_position: None, + } + } +} + +impl Config +{ + pub fn load() -> Result + { + let config_path = Self::get_config_path()?; + + if !config_path.exists() { + let config = Self::default(); + config.save()?; + return Ok(config); + } + + let content = fs::read_to_string(&config_path)?; + let config: Self = serde_json::from_str(&content)?; + Ok(config) + } + + pub fn save(&self) -> Result<()> + { + let config_path = Self::get_config_path()?; + + if let Some(parent) = config_path.parent() { + fs::create_dir_all(parent)?; + } + + let content = serde_json::to_string_pretty(self)?; + fs::write(&config_path, content)?; + Ok(()) + } + + pub fn get_config_path() -> Result + { + let exe_path = std::env::current_exe()?; + let exe_dir = exe_path.parent() + .ok_or_else(|| anyhow::anyhow!("Failed to get executable directory"))?; + Ok(exe_dir.join("ihateborders_config.json")) + } + + pub fn add_auto_borderless(&mut self, process_name: String) + { + if !self.auto_borderless_apps.contains(&process_name) { + self.auto_borderless_apps.push(process_name); + } + } + + pub fn remove_auto_borderless(&mut self, process_name: &str) + { + self.auto_borderless_apps.retain(|app| app != process_name); + } + + pub fn is_auto_borderless(&self, process_name: &str) -> bool + { + self.auto_borderless_apps.contains(&process_name.to_string()) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9565ffa..d3bfcbb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,75 @@ #![windows_subsystem = "windows"] mod app; +mod config; +mod startup; mod ui; mod window_manager; use app::{BorderlessApp, create_app_options}; +use startup::{create_scheduled_task, is_elevated}; + +fn handle_elevated_task_creation(with_admin: bool) -> ! +{ + if !is_elevated() { + eprintln!("Error: Task creation requires administrator privileges"); + std::process::exit(1); + } + + match create_scheduled_task(with_admin) { + Ok(_) => { + let exe_path = std::env::current_exe().expect("Failed to get exe path"); + std::process::Command::new(exe_path) + .arg("--open-settings") + .spawn() + .expect("Failed to relaunch app"); + std::process::exit(0); + } + Err(e) => { + eprintln!("Failed to create scheduled task: {}", e); + std::process::exit(1); + } + } +} fn main() -> Result<(), eframe::Error> { + let args: Vec = std::env::args().collect(); + + let mut open_settings = false; + if args.len() > 1 && args[1] == "--install-admin-task" { + handle_elevated_task_creation(true); + } + if args.len() > 1 && args[1] == "--create-startup-task" { + handle_elevated_task_creation(false); + } + if args.len() > 1 && args[1] == "--create-startup-task" { + if !is_elevated() { + eprintln!("Error: --create-startup-task requires administrator privileges"); + std::process::exit(1); + } + + match create_scheduled_task(false) { + Ok(_) => { + let exe_path = std::env::current_exe().expect("Failed to get exe path"); + std::process::Command::new(exe_path) + .spawn() + .expect("Failed to relaunch app"); + std::process::exit(0); + } + Err(e) => { + eprintln!("Failed to create scheduled task: {}", e); + std::process::exit(1); + } + } + } + if args.len() > 1 && args[1] == "--open-settings" { + open_settings = true; + } + eframe::run_native( "ihateborders", create_app_options(), - Box::new(|cc| Ok(Box::new(BorderlessApp::new(cc)))), + Box::new(move |cc| Ok(Box::new(BorderlessApp::new(cc, open_settings)))), ) } diff --git a/src/startup.rs b/src/startup.rs new file mode 100644 index 0000000..9138f0f --- /dev/null +++ b/src/startup.rs @@ -0,0 +1,112 @@ +use anyhow::Result; +use std::os::windows::process::CommandExt; +use std::process::Command; +use windows::Win32::{ + Foundation::HWND, + UI::Shell::{IsUserAnAdmin, ShellExecuteW}, + UI::WindowsAndMessaging::SW_SHOWNORMAL, +}; + +const CREATE_NO_WINDOW: u32 = 0x08000000; + +pub fn is_elevated() -> bool +{ + unsafe { IsUserAnAdmin().as_bool() } +} + +pub fn relaunch_elevated(args: &[&str]) -> Result<()> +{ + let exe_path = std::env::current_exe()?; + let exe_path_str = exe_path.to_string_lossy(); + + let args_string = args.join(" "); + + unsafe { + let operation = windows::core::w!("runas"); + let file = windows::core::HSTRING::from(exe_path_str.as_ref()); + let parameters = windows::core::HSTRING::from(&args_string); + + let result = ShellExecuteW( + Some(HWND::default()), + operation, + &file, + ¶meters, + None, + SW_SHOWNORMAL, + ); + + if result.0 as i32 <= 32 { + anyhow::bail!("Failed to relaunch with elevation"); + } + } + + std::process::exit(0); +} + +pub fn create_scheduled_task(with_admin: bool) -> Result<()> +{ + let exe_path = std::env::current_exe()?; + let exe_path_str = exe_path.to_string_lossy(); + let tr_arg = format!("\"{}\"", exe_path_str); + + let mut args = vec![ + "/Create", + "/TN", "ihateborders_startup", + "/TR", &tr_arg, + "/SC", "ONLOGON", + "/F", + ]; + let rl_highest = "HIGHEST"; + if with_admin { + args.push("/RL"); + args.push(rl_highest); + } + let output = Command::new("schtasks") + .args(&args) + .creation_flags(CREATE_NO_WINDOW) + .output()?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Failed to create scheduled task: {}", error); + } + + Ok(()) +} + +pub fn remove_scheduled_task() -> Result<()> +{ + let output = Command::new("schtasks") + .args(&[ + "/Delete", + "/TN", "ihateborders_startup", + "/F", + ]) + .creation_flags(CREATE_NO_WINDOW) + .output()?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + if !error.contains("cannot find the file") { + anyhow::bail!("Failed to remove scheduled task: {}", error); + } + } + + Ok(()) +} + +pub fn task_exists() -> bool +{ + let output = Command::new("schtasks") + .args(&[ + "/Query", + "/TN", "ihateborders_startup", + ]) + .creation_flags(CREATE_NO_WINDOW) + .output(); + + match output { + Ok(output) => output.status.success(), + Err(_) => false, + } +} \ No newline at end of file diff --git a/src/ui.rs b/src/ui.rs index 9614914..fac9cd2 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -224,6 +224,33 @@ pub fn render_position_checkbox(ui: &mut egui::Ui, resize_to_screen: &mut bool) }); } +pub fn render_auto_borderless_checkbox( + ui: &mut egui::Ui, + auto_borderless: &mut bool, + changed: &mut bool, + enabled: bool, +) +{ + ui.add_space(5.0); + + ui.horizontal(|ui| { + ui.add_space(5.0); + + ui.add_enabled_ui(enabled, |ui| { + let response = ui.checkbox(auto_borderless, ""); + if response.changed() { + *changed = true; + } + + ui.label( + RichText::new("Automatically make borderless") + .font(FontId::proportional(12.0)) + .color(if enabled { Color32::from_gray(180) } else { Color32::from_gray(120) }), + ); + }); + }); +} + pub fn render_display_selector( ui: &mut egui::Ui, displays: &[DisplayInfo], @@ -276,13 +303,13 @@ pub fn render_action_button( ui: &mut egui::Ui, windows: &[WindowInfo], selected_window: Option, + enabled: bool, ) -> Option { ui.add_space(15.0); let mut clicked_window = None; - let button_enabled = selected_window.is_some(); let button_text = if let Some(index) = selected_window { if let Some(window) = windows.get(index) { if window.is_borderless { "Restore Borders" } else { "Make Borderless" } @@ -294,15 +321,15 @@ pub fn render_action_button( }; ui.with_layout(Layout::top_down(Align::Center), |ui| { - ui.add_enabled_ui(button_enabled, |ui| { + ui.add_enabled_ui(enabled, |ui| { let button = egui::Button::new( RichText::new(button_text).font(FontId::proportional(14.0)).color( - if button_enabled { Color32::from_gray(255) } else { Color32::from_gray(120) }, + if enabled { Color32::from_gray(255) } else { Color32::from_gray(120) }, ), ) - .min_size(egui::vec2(180.0, 35.0)); + .min_size(egui::vec2(180.0, 35.0)); - if ui.add(button).clicked() && button_enabled { + if ui.add(button).clicked() && enabled { clicked_window = selected_window; } }); diff --git a/src/window_manager.rs b/src/window_manager.rs index 14a4c63..629039b 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -14,7 +14,7 @@ use windows::Win32::{ DrawIconEx, EnumWindows, GCLP_HICON, GWL_STYLE, GetClassLongPtrW, GetSystemMetrics, GetWindowLongW, GetWindowTextW, GetWindowThreadProcessId, HWND_TOP, ICON_SMALL, IsWindowVisible, SM_CXSCREEN, SM_CYSCREEN, SWP_FRAMECHANGED, SWP_NOMOVE, SWP_NOSIZE, - SWP_NOZORDER, SendMessageW, SetWindowLongW, SetWindowPos, WM_GETICON, WS_BORDER, + SWP_NOZORDER, SWP_NOACTIVATE, SendMessageW, SetWindowLongW, SetWindowPos, WM_GETICON, WS_BORDER, WS_CAPTION, WS_DLGFRAME, WS_THICKFRAME, }, }; @@ -197,7 +197,7 @@ impl WindowManager y, width, height, - SWP_FRAMECHANGED | SWP_NOZORDER, + SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE, )?; } else { SetWindowPos( @@ -207,7 +207,7 @@ impl WindowManager 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE, )?; } } @@ -215,10 +215,15 @@ impl WindowManager Ok(()) } + pub fn get_window_mut(&mut self, index: usize) -> Option<&mut WindowInfo> { + self.windows.get_mut(index) + } + pub fn is_refresh_in_progress(&self) -> bool { *self.refresh_in_progress.lock().unwrap() } + } unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> windows::core::BOOL From 2f43f14630e5494945f06df8fe769dcdec857bca Mon Sep 17 00:00:00 2001 From: lava1879 Date: Sat, 20 Dec 2025 18:12:43 +0100 Subject: [PATCH 2/3] refactor: simplify auto-borderless logic and optimize window position tracking --- src/app.rs | 82 ++++++++++++++++++++++++++++------------------------- src/main.rs | 25 +++------------- 2 files changed, 47 insertions(+), 60 deletions(-) diff --git a/src/app.rs b/src/app.rs index cba59bf..94eeead 100644 --- a/src/app.rs +++ b/src/app.rs @@ -351,7 +351,9 @@ impl eframe::App for BorderlessApp fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { if let Some(pos) = ctx.input(|i| i.viewport().outer_rect).map(|r| r.min) { - if self.config.window_position.as_ref().map_or(true, |saved_pos| { + let should_update = self.last_refresh.elapsed().as_secs() >= 1; + + if should_update && self.config.window_position.as_ref().map_or(true, |saved_pos| { (saved_pos.x - pos.x).abs() > 1.0 || (saved_pos.y - pos.y).abs() > 1.0 }) { self.config.window_position = Some(crate::config::WindowPosition { @@ -359,7 +361,7 @@ impl eframe::App for BorderlessApp y: pos.y, }); } - } +} self.handle_refresh(); self.handle_keyboard_input(ctx); @@ -395,47 +397,49 @@ impl eframe::App for BorderlessApp if auto_changed { if let Some(index) = self.selected_window { - let current_windows = self.window_manager.get_windows(); - let window = ¤t_windows[index]; - let process_name = window.process_name.clone(); - let hwnd = window.hwnd; - let currently_borderless = window.is_borderless; - - let should_toggle = (self.auto_borderless_enabled && !currently_borderless) - || (!self.auto_borderless_enabled && currently_borderless); - - if should_toggle { - let selected_display = if self.resize_to_screen { - self.selected_display.and_then(|idx| self.displays.get(idx)) + let windows = self.window_manager.get_windows(); + if let Some(window) = windows.get(index) { + let process_name = window.process_name.clone(); + let hwnd = window.hwnd; + let is_borderless = window.is_borderless; + + if self.auto_borderless_enabled { + self.config.add_auto_borderless(process_name); + if !is_borderless { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if self.window_manager.toggle_borderless(hwnd, self.resize_to_screen, selected_display).is_ok() { + if let Some(w) = self.window_manager.get_window_mut(index) { + w.is_borderless = true; + } + self.applied_auto_borderless.insert(hwnd); + self.needs_repaint = true; + } + } } else { - None - }; - - if self.window_manager - .toggle_borderless(hwnd, self.resize_to_screen, selected_display) - .is_ok() - { - if let Some(w) = self.window_manager.get_window_mut(index) { - w.is_borderless = !w.is_borderless; + self.config.remove_auto_borderless(&process_name); + if is_borderless { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if self.window_manager.toggle_borderless(hwnd, self.resize_to_screen, selected_display).is_ok() { + if let Some(w) = self.window_manager.get_window_mut(index) { + w.is_borderless = false; + } + self.applied_auto_borderless.remove(&hwnd); + self.needs_repaint = true; + } } - self.needs_repaint = true; - self.refresh_receiver = None; - self.start_async_refresh(); - } - } - - if self.auto_borderless_enabled { - self.config.add_auto_borderless(process_name); - if !currently_borderless && should_toggle { - self.applied_auto_borderless.insert(hwnd); - } - } else { - self.config.remove_auto_borderless(&process_name); - if currently_borderless && should_toggle { - self.applied_auto_borderless.remove(&hwnd); } + self.save_config(); } - self.save_config(); } } diff --git a/src/main.rs b/src/main.rs index d3bfcbb..4095f14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,32 +37,15 @@ fn main() -> Result<(), eframe::Error> let args: Vec = std::env::args().collect(); let mut open_settings = false; + if args.len() > 1 && args[1] == "--install-admin-task" { handle_elevated_task_creation(true); } + if args.len() > 1 && args[1] == "--create-startup-task" { handle_elevated_task_creation(false); } - if args.len() > 1 && args[1] == "--create-startup-task" { - if !is_elevated() { - eprintln!("Error: --create-startup-task requires administrator privileges"); - std::process::exit(1); - } - - match create_scheduled_task(false) { - Ok(_) => { - let exe_path = std::env::current_exe().expect("Failed to get exe path"); - std::process::Command::new(exe_path) - .spawn() - .expect("Failed to relaunch app"); - std::process::exit(0); - } - Err(e) => { - eprintln!("Failed to create scheduled task: {}", e); - std::process::exit(1); - } - } - } + if args.len() > 1 && args[1] == "--open-settings" { open_settings = true; } @@ -72,4 +55,4 @@ fn main() -> Result<(), eframe::Error> create_app_options(), Box::new(move |cc| Ok(Box::new(BorderlessApp::new(cc, open_settings)))), ) -} +} \ No newline at end of file From e312fddcba922dd2baeba0ca35b42bdf29b3d320 Mon Sep 17 00:00:00 2001 From: lava1879 Date: Sat, 20 Dec 2025 19:21:52 +0100 Subject: [PATCH 3/3] style: apply nightly rustfmt formatting --- src/app.rs | 268 +++++++++++++++++++++++------------------- src/config.rs | 10 +- src/main.rs | 12 +- src/startup.rs | 36 ++---- src/ui.rs | 10 +- src/window_manager.rs | 10 +- 6 files changed, 178 insertions(+), 168 deletions(-) diff --git a/src/app.rs b/src/app.rs index 94eeead..f59dca6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -110,7 +110,7 @@ impl BorderlessApp let window_manager = WindowManager::new(); let displays = window_manager.get_displays(); - + let config = Config::load().unwrap_or_default(); let mut app = Self { @@ -167,14 +167,14 @@ impl BorderlessApp self.start_async_refresh(); } } - + fn apply_auto_borderless(&mut self) { let windows = self.window_manager.get_windows().to_vec(); - + for window in windows.iter() { - if self.config.is_auto_borderless(&window.process_name) - && !window.is_borderless + if self.config.is_auto_borderless(&window.process_name) + && !window.is_borderless && !self.applied_auto_borderless.contains(&window.hwnd) { let selected_display = if self.resize_to_screen { @@ -182,7 +182,7 @@ impl BorderlessApp } else { None }; - + if let Ok(_) = self.window_manager.toggle_borderless( window.hwnd, self.resize_to_screen, @@ -221,11 +221,9 @@ impl BorderlessApp None }; - if let Err(e) = self.window_manager.toggle_borderless( - hwnd, - self.resize_to_screen, - selected_display, - ) { + if let Err(e) = + self.window_manager.toggle_borderless(hwnd, self.resize_to_screen, selected_display) + { eprintln!("Failed to toggle borderless for window: {}", e); } else { if let Some(window) = self.window_manager.get_window_mut(window_index) { @@ -236,20 +234,20 @@ impl BorderlessApp self.needs_repaint = true; } } - + fn save_config(&self) { if let Err(e) = self.config.save() { eprintln!("Failed to save config: {}", e); } } - + fn handle_settings_window(&mut self, ctx: &egui::Context) { if !self.show_settings { return; } - + egui::Window::new("Settings") .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) .collapsible(false) @@ -257,7 +255,10 @@ impl BorderlessApp .show(ctx, |ui| { ui.set_min_width(300.0); let mut run_on_startup = self.config.run_on_startup; - if ui.checkbox(&mut run_on_startup, "Run ihateborders when my computer starts").changed() { + if ui + .checkbox(&mut run_on_startup, "Run ihateborders when my computer starts") + .changed() + { if run_on_startup { if !is_elevated() { self.config.run_on_startup = true; @@ -272,10 +273,10 @@ impl BorderlessApp Ok(_) => { self.config.run_on_startup = true; self.save_config(); - } + }, Err(e) => { eprintln!("Failed to create startup task: {}", e); - } + }, } } } else { @@ -289,8 +290,14 @@ impl BorderlessApp } ui.add_enabled_ui(self.config.run_on_startup, |ui| { let mut startup_admin = self.config.startup_admin; - - if ui.checkbox(&mut startup_admin, "Enable startup with administrator privileges").changed() { + + if ui + .checkbox( + &mut startup_admin, + "Enable startup with administrator privileges", + ) + .changed() + { if startup_admin { if !is_elevated() { self.config.startup_admin = true; @@ -305,10 +312,10 @@ impl BorderlessApp Ok(_) => { self.config.startup_admin = true; self.save_config(); - } + }, Err(e) => { eprintln!("Failed to create scheduled task: {}", e); - } + }, } } } else { @@ -322,23 +329,23 @@ impl BorderlessApp if task_exists() { let _ = remove_scheduled_task(); } - + match crate::startup::create_scheduled_task(false) { Ok(_) => { self.config.startup_admin = false; self.save_config(); - } + }, Err(e) => { eprintln!("Failed to recreate startup task: {}", e); - } + }, } } } } }); - + ui.add_space(10.0); - + if ui.button("Close").clicked() { self.show_settings = false; } @@ -352,124 +359,141 @@ impl eframe::App for BorderlessApp { if let Some(pos) = ctx.input(|i| i.viewport().outer_rect).map(|r| r.min) { let should_update = self.last_refresh.elapsed().as_secs() >= 1; - - if should_update && self.config.window_position.as_ref().map_or(true, |saved_pos| { - (saved_pos.x - pos.x).abs() > 1.0 || (saved_pos.y - pos.y).abs() > 1.0 - }) { - self.config.window_position = Some(crate::config::WindowPosition { - x: pos.x, - y: pos.y, - }); + + if should_update + && self.config.window_position.as_ref().map_or(true, |saved_pos| { + (saved_pos.x - pos.x).abs() > 1.0 || (saved_pos.y - pos.y).abs() > 1.0 + }) + { + self.config.window_position = + Some(crate::config::WindowPosition { x: pos.x, y: pos.y }); } -} - + } + self.handle_refresh(); self.handle_keyboard_input(ctx); self.icon_cache.cleanup_expired(); - egui::CentralPanel::default().show(ctx, |ui| { - - ui::render_header(ui, self.window_manager.get_windows().len()); + egui::CentralPanel::default().show(ctx, |ui| { + ui::render_header(ui, self.window_manager.get_windows().len()); - ui::render_window_selector( - ui, - self.window_manager.get_windows(), - &mut self.selected_window, - &mut self.icon_cache, - ); + ui::render_window_selector( + ui, + self.window_manager.get_windows(), + &mut self.selected_window, + &mut self.icon_cache, + ); - ui::render_position_checkbox(ui, &mut self.resize_to_screen); + ui::render_position_checkbox(ui, &mut self.resize_to_screen); - if self.resize_to_screen { - ui::render_display_selector(ui, &self.displays, &mut self.selected_display); - } + if self.resize_to_screen { + ui::render_display_selector(ui, &self.displays, &mut self.selected_display); + } - let mut auto_changed = false; - let checkbox_enabled = self.selected_window.is_some(); - - ui::render_auto_borderless_checkbox( - ui, - &mut self.auto_borderless_enabled, - &mut auto_changed, - checkbox_enabled, - ); - - if auto_changed { - if let Some(index) = self.selected_window { - let windows = self.window_manager.get_windows(); - if let Some(window) = windows.get(index) { - let process_name = window.process_name.clone(); - let hwnd = window.hwnd; - let is_borderless = window.is_borderless; - - if self.auto_borderless_enabled { - self.config.add_auto_borderless(process_name); - if !is_borderless { - let selected_display = if self.resize_to_screen { - self.selected_display.and_then(|idx| self.displays.get(idx)) - } else { - None - }; - - if self.window_manager.toggle_borderless(hwnd, self.resize_to_screen, selected_display).is_ok() { - if let Some(w) = self.window_manager.get_window_mut(index) { - w.is_borderless = true; + let mut auto_changed = false; + let checkbox_enabled = self.selected_window.is_some(); + + ui::render_auto_borderless_checkbox( + ui, + &mut self.auto_borderless_enabled, + &mut auto_changed, + checkbox_enabled, + ); + + if auto_changed { + if let Some(index) = self.selected_window { + let windows = self.window_manager.get_windows(); + if let Some(window) = windows.get(index) { + let process_name = window.process_name.clone(); + let hwnd = window.hwnd; + let is_borderless = window.is_borderless; + + if self.auto_borderless_enabled { + self.config.add_auto_borderless(process_name); + if !is_borderless { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if self + .window_manager + .toggle_borderless( + hwnd, + self.resize_to_screen, + selected_display, + ) + .is_ok() + { + if let Some(w) = self.window_manager.get_window_mut(index) { + w.is_borderless = true; + } + self.applied_auto_borderless.insert(hwnd); + self.needs_repaint = true; } - self.applied_auto_borderless.insert(hwnd); - self.needs_repaint = true; } - } - } else { - self.config.remove_auto_borderless(&process_name); - if is_borderless { - let selected_display = if self.resize_to_screen { - self.selected_display.and_then(|idx| self.displays.get(idx)) - } else { - None - }; - - if self.window_manager.toggle_borderless(hwnd, self.resize_to_screen, selected_display).is_ok() { - if let Some(w) = self.window_manager.get_window_mut(index) { - w.is_borderless = false; + } else { + self.config.remove_auto_borderless(&process_name); + if is_borderless { + let selected_display = if self.resize_to_screen { + self.selected_display.and_then(|idx| self.displays.get(idx)) + } else { + None + }; + + if self + .window_manager + .toggle_borderless( + hwnd, + self.resize_to_screen, + selected_display, + ) + .is_ok() + { + if let Some(w) = self.window_manager.get_window_mut(index) { + w.is_borderless = false; + } + self.applied_auto_borderless.remove(&hwnd); + self.needs_repaint = true; } - self.applied_auto_borderless.remove(&hwnd); - self.needs_repaint = true; } } + self.save_config(); } - self.save_config(); } } - } - if let Some(index) = self.selected_window { - let windows = self.window_manager.get_windows(); - if let Some(window) = windows.get(index) { - self.auto_borderless_enabled = self.config.is_auto_borderless(&window.process_name); + if let Some(index) = self.selected_window { + let windows = self.window_manager.get_windows(); + if let Some(window) = windows.get(index) { + self.auto_borderless_enabled = + self.config.is_auto_borderless(&window.process_name); + } } - } - let action_button_enabled = self.selected_window.is_some() && !self.auto_borderless_enabled; - - let clicked_index = ui::render_action_button( - ui, - self.window_manager.get_windows(), - self.selected_window, - action_button_enabled, - ); - - if let Some(window_index) = clicked_index { - self.handle_window_action(window_index); - } + let action_button_enabled = + self.selected_window.is_some() && !self.auto_borderless_enabled; - ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { - ui.add_space(5.0); - if ui.button("⚙ Settings").clicked() { - self.show_settings = !self.show_settings; + let clicked_index = ui::render_action_button( + ui, + self.window_manager.get_windows(), + self.selected_window, + action_button_enabled, + ); + + if let Some(window_index) = clicked_index { + self.handle_window_action(window_index); } + + ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { + ui.add_space(5.0); + if ui.button("⚙ Settings").clicked() { + self.show_settings = !self.show_settings; + } + }); }); - }); self.handle_settings_window(ctx); @@ -480,7 +504,7 @@ impl eframe::App for BorderlessApp ctx.request_repaint_after(Duration::from_secs(5)); } } - + fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) { let _ = self.config.save(); @@ -517,4 +541,4 @@ fn load_icon() -> egui::IconData let rgba = image.into_raw(); egui::IconData { rgba, width, height } -} \ No newline at end of file +} diff --git a/src/config.rs b/src/config.rs index 6bcaf89..09d8686 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,6 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::{ - fs, - path::PathBuf, -}; +use std::{fs, path::PathBuf}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WindowPosition @@ -74,7 +71,8 @@ impl Config pub fn get_config_path() -> Result { let exe_path = std::env::current_exe()?; - let exe_dir = exe_path.parent() + let exe_dir = exe_path + .parent() .ok_or_else(|| anyhow::anyhow!("Failed to get executable directory"))?; Ok(exe_dir.join("ihateborders_config.json")) } @@ -95,4 +93,4 @@ impl Config { self.auto_borderless_apps.contains(&process_name.to_string()) } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 4095f14..78b8477 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,11 +24,11 @@ fn handle_elevated_task_creation(with_admin: bool) -> ! .spawn() .expect("Failed to relaunch app"); std::process::exit(0); - } + }, Err(e) => { eprintln!("Failed to create scheduled task: {}", e); std::process::exit(1); - } + }, } } @@ -37,15 +37,15 @@ fn main() -> Result<(), eframe::Error> let args: Vec = std::env::args().collect(); let mut open_settings = false; - + if args.len() > 1 && args[1] == "--install-admin-task" { handle_elevated_task_creation(true); } - + if args.len() > 1 && args[1] == "--create-startup-task" { handle_elevated_task_creation(false); } - + if args.len() > 1 && args[1] == "--open-settings" { open_settings = true; } @@ -55,4 +55,4 @@ fn main() -> Result<(), eframe::Error> create_app_options(), Box::new(move |cc| Ok(Box::new(BorderlessApp::new(cc, open_settings)))), ) -} \ No newline at end of file +} diff --git a/src/startup.rs b/src/startup.rs index 9138f0f..0a57945 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -1,10 +1,11 @@ use anyhow::Result; -use std::os::windows::process::CommandExt; -use std::process::Command; +use std::{os::windows::process::CommandExt, process::Command}; use windows::Win32::{ Foundation::HWND, - UI::Shell::{IsUserAnAdmin, ShellExecuteW}, - UI::WindowsAndMessaging::SW_SHOWNORMAL, + UI::{ + Shell::{IsUserAnAdmin, ShellExecuteW}, + WindowsAndMessaging::SW_SHOWNORMAL, + }, }; const CREATE_NO_WINDOW: u32 = 0x08000000; @@ -49,22 +50,14 @@ pub fn create_scheduled_task(with_admin: bool) -> Result<()> let exe_path_str = exe_path.to_string_lossy(); let tr_arg = format!("\"{}\"", exe_path_str); - let mut args = vec![ - "/Create", - "/TN", "ihateborders_startup", - "/TR", &tr_arg, - "/SC", "ONLOGON", - "/F", - ]; + let mut args = + vec!["/Create", "/TN", "ihateborders_startup", "/TR", &tr_arg, "/SC", "ONLOGON", "/F"]; let rl_highest = "HIGHEST"; if with_admin { args.push("/RL"); args.push(rl_highest); } - let output = Command::new("schtasks") - .args(&args) - .creation_flags(CREATE_NO_WINDOW) - .output()?; + let output = Command::new("schtasks").args(&args).creation_flags(CREATE_NO_WINDOW).output()?; if !output.status.success() { let error = String::from_utf8_lossy(&output.stderr); @@ -77,11 +70,7 @@ pub fn create_scheduled_task(with_admin: bool) -> Result<()> pub fn remove_scheduled_task() -> Result<()> { let output = Command::new("schtasks") - .args(&[ - "/Delete", - "/TN", "ihateborders_startup", - "/F", - ]) + .args(&["/Delete", "/TN", "ihateborders_startup", "/F"]) .creation_flags(CREATE_NO_WINDOW) .output()?; @@ -98,10 +87,7 @@ pub fn remove_scheduled_task() -> Result<()> pub fn task_exists() -> bool { let output = Command::new("schtasks") - .args(&[ - "/Query", - "/TN", "ihateborders_startup", - ]) + .args(&["/Query", "/TN", "ihateborders_startup"]) .creation_flags(CREATE_NO_WINDOW) .output(); @@ -109,4 +95,4 @@ pub fn task_exists() -> bool Ok(output) => output.status.success(), Err(_) => false, } -} \ No newline at end of file +} diff --git a/src/ui.rs b/src/ui.rs index fac9cd2..9086744 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -323,11 +323,13 @@ pub fn render_action_button( ui.with_layout(Layout::top_down(Align::Center), |ui| { ui.add_enabled_ui(enabled, |ui| { let button = egui::Button::new( - RichText::new(button_text).font(FontId::proportional(14.0)).color( - if enabled { Color32::from_gray(255) } else { Color32::from_gray(120) }, - ), + RichText::new(button_text).font(FontId::proportional(14.0)).color(if enabled { + Color32::from_gray(255) + } else { + Color32::from_gray(120) + }), ) - .min_size(egui::vec2(180.0, 35.0)); + .min_size(egui::vec2(180.0, 35.0)); if ui.add(button).clicked() && enabled { clicked_window = selected_window; diff --git a/src/window_manager.rs b/src/window_manager.rs index 629039b..68eefe4 100644 --- a/src/window_manager.rs +++ b/src/window_manager.rs @@ -13,9 +13,9 @@ use windows::Win32::{ UI::WindowsAndMessaging::{ DrawIconEx, EnumWindows, GCLP_HICON, GWL_STYLE, GetClassLongPtrW, GetSystemMetrics, GetWindowLongW, GetWindowTextW, GetWindowThreadProcessId, HWND_TOP, ICON_SMALL, - IsWindowVisible, SM_CXSCREEN, SM_CYSCREEN, SWP_FRAMECHANGED, SWP_NOMOVE, SWP_NOSIZE, - SWP_NOZORDER, SWP_NOACTIVATE, SendMessageW, SetWindowLongW, SetWindowPos, WM_GETICON, WS_BORDER, - WS_CAPTION, WS_DLGFRAME, WS_THICKFRAME, + IsWindowVisible, SM_CXSCREEN, SM_CYSCREEN, SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, + SWP_NOSIZE, SWP_NOZORDER, SendMessageW, SetWindowLongW, SetWindowPos, WM_GETICON, + WS_BORDER, WS_CAPTION, WS_DLGFRAME, WS_THICKFRAME, }, }; @@ -215,7 +215,8 @@ impl WindowManager Ok(()) } - pub fn get_window_mut(&mut self, index: usize) -> Option<&mut WindowInfo> { + pub fn get_window_mut(&mut self, index: usize) -> Option<&mut WindowInfo> + { self.windows.get_mut(index) } @@ -223,7 +224,6 @@ impl WindowManager { *self.refresh_in_progress.lock().unwrap() } - } unsafe extern "system" fn enum_windows_proc(hwnd: HWND, lparam: LPARAM) -> windows::core::BOOL