From 80d091eb71342ff7d3de97a1897985dc6bf50a17 Mon Sep 17 00:00:00 2001 From: JobyGitGud Date: Sun, 4 May 2025 21:26:36 -0500 Subject: [PATCH] Merged system tray functionality with latest Steam integration --- Cargo.toml | 1 + src/main.rs | 606 +++++++++++++++++++++++++++++++++++++----------- src/platform.rs | 26 +++ 3 files changed, 496 insertions(+), 137 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d185baa..adab236 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ global-hotkey = "0.4.2" # Global hotkey re lazy_static = "1.4.0" # Thread-safe static initialization steamworks = { version = "0.11.0", features = ["raw-bindings"] } anyhow = "1.0.98" +once_cell = "1.18.0" # Thread-safe lazy initialized global variables # Platform-specific dependencies [target.'cfg(windows)'.dependencies] diff --git a/src/main.rs b/src/main.rs index 4def902..b338878 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use iced::{ widget::{button, text, Column, Row, container, image as iced_image}, Element, Length, Subscription, Task, Theme, + futures::{channel::mpsc, StreamExt}, }; use std::{ path::{Path, PathBuf}, - sync::{mpsc, Arc, Mutex}, + sync::{Arc, Mutex}, thread, time::Duration, }; @@ -12,6 +13,9 @@ use ::image::{GenericImageView, RgbaImage, open as image_open}; use walkdir::WalkDir; use tray_icon::{ TrayIconBuilder, + TrayIcon, // Import the TrayIcon type itself + TrayIconEvent, // Import TrayIconEvent for handling left/right clicks + MouseButton, // Import MouseButton to distinguish between left and right clicks menu::{Menu, MenuEvent}, }; use global_hotkey::{ @@ -37,6 +41,7 @@ struct CursorInfo { // Define menu item IDs const NEXT_CURSOR_ID: &str = "next_cursor"; +const SHOW_WINDOW_ID: &str = "show_window"; const QUIT_ID: &str = "quit"; // Shared state between the UI and the tray icon @@ -45,15 +50,31 @@ struct SharedState { cursors: Vec, current_cursor_image: Option, running: bool, + window_visible: bool, // Track if the window is visible } // Our application state -#[derive(Default)] struct CursorRush { cursors: Vec, current_index: usize, shared_state: Option>>, - cursor_position: (f32, f32), // Track the mouse position + main_window: Option, // Store the main window ID + tray_icon: Option, // Store the TrayIcon object to keep it alive + tray_tx: Option>, // Sender for tray events +} + +// Make sure the default impl initializes tray_icon to None +impl Default for CursorRush { + fn default() -> Self { + Self { + cursors: Vec::new(), + current_index: 0, + shared_state: None, + main_window: None, + tray_icon: None, // Initialize as None + tray_tx: None, // Initialize as None + } + } } // Messages that can be sent in our application @@ -62,8 +83,11 @@ enum Message { NextCursor, SelectCursor(usize), Quit, - TrayEvent, - CursorMoved((f32, f32)), // New message for cursor movement + TrayEvent, // Used as a no-op message + CloseRequested(iced::window::Id), // Message for window close requests with the real window ID + ShowWindow, // Message to show the window from tray + WindowHidden, // Message indicating window was successfully hidden + WindowShown, // Message indicating window was successfully shown } // Function to load all PNG files from the assets directory @@ -139,17 +163,74 @@ fn apply_cursor(path: &Path) -> bool { false } +// Global channel for tray events +use std::sync::Once; +use std::sync::atomic::{AtomicBool, Ordering}; +use once_cell::sync::Lazy; + +static INIT: Once = Once::new(); +static INITIALIZED: AtomicBool = AtomicBool::new(false); +static TRAY_SENDER: Lazy>>> = + Lazy::new(|| std::sync::Mutex::new(None)); + +// Function to get or create the global tray sender +fn get_tray_sender() -> mpsc::UnboundedSender { + if !INITIALIZED.load(Ordering::SeqCst) { + INIT.call_once(|| { + let (tx, _) = mpsc::unbounded(); + *TRAY_SENDER.lock().unwrap() = Some(tx); + INITIALIZED.store(true, Ordering::SeqCst); + }); + } + + TRAY_SENDER.lock().unwrap().clone().unwrap() +} + // Top-level subscription function to listen for events fn subscription(_state: &CursorRush) -> Subscription { - iced::event::listen().map(|event| { - if let iced::Event::Mouse(iced::mouse::Event::CursorMoved { position }) = event { - // Track cursor position for UI display purposes - Message::CursorMoved((position.x, position.y)) - } else { - // We need to return some message, so we'll use TrayEvent as a no-op - Message::TrayEvent + // Create a vector to hold all subscriptions + let mut subs = vec![ + // Listen for events but filter out mouse movement events to avoid spamming + iced::event::listen().map(|event| { + match event { + // Skip cursor movement events to avoid spamming + iced::Event::Mouse(iced::mouse::Event::CursorMoved { .. }) => { + // Return TrayEvent which is essentially a no-op + Message::TrayEvent + }, + // Handle other events + _ => Message::TrayEvent, // Use TrayEvent as a no-op message + } + }), + + // Specifically subscribe to window close requests + // This is the key to handling the X button properly + iced::window::close_requests().map(|window_id| { + println!("Window close requested via X button for window ID: {:?}", window_id); + Message::CloseRequested(window_id) + }), + ]; + + // Add a subscription for tray events + // We'll use a simple channel that the tray icon can send messages to + let tray_subscription = Subscription::run( + // Stream generator function + || { + // Create a channel that the tray icon can send messages to + let (tx, rx) = mpsc::unbounded::(); + + // Store the sender in the global state + *TRAY_SENDER.lock().unwrap() = Some(tx); + + // Return the receiver as a stream + rx } - }) + ); + + subs.push(tray_subscription); + + // Combine all subscriptions + Subscription::batch(subs) } // Boot function to initialize the application @@ -185,72 +266,119 @@ fn boot() -> (CursorRush, Task) { cursors: cursors.clone(), current_cursor_image, running: true, + window_visible: true, // Window is initially visible })); - // Create a channel to communicate with the UI - let (tx, rx) = mpsc::channel(); - - // Set up the tray icon - setup_tray_icon(&cursors, shared_state.clone(), tx.clone()); - // Initialize the application state with loaded cursors let mut state = CursorRush::default(); - state.cursors = cursors; + state.cursors = cursors.clone(); // Clone here state.shared_state = Some(shared_state.clone()); + // Get the global tray sender (will be initialized by the subscription) + // We'll set it up in the state after the subscription is running + + // Set up the tray icon AND store the result in the state + // The tray sender will be available from the global state + state.tray_icon = setup_tray_icon(&state.cursors, shared_state.clone()); // Pass the cloned cursors + if state.tray_icon.is_none() { + eprintln!("Failed to initialize tray icon, application might not function correctly in background."); + // Optionally, decide how to handle this failure (e.g., exit or run without tray) + } + + // We'll get the real window ID from the CloseRequested event + // No need to create a window ID here + // Set up global hotkey manager - let hotkey_manager = GlobalHotKeyManager::new().expect("Failed to create hotkey manager"); + let hotkey_manager = match GlobalHotKeyManager::new() { + Ok(manager) => manager, + Err(e) => { + eprintln!("Failed to create hotkey manager: {}", e); + // Continue without hotkey support + GlobalHotKeyManager::new().expect("Critical failure: could not create hotkey manager") + } + }; // Register Ctrl+Shift+F12 as the exit hotkey (uncommon but easy to remember) use global_hotkey::hotkey::{Code, Modifiers}; + + // We don't need a unique ID for the hotkey in this simple case + let hotkey = GlobalHotKey::new( Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::F12, ); - if let Err(e) = hotkey_manager.register(hotkey) { - eprintln!("Failed to register hotkey: {}", e); - } else { - println!("Press Ctrl+Shift+F12 to exit the application"); + // Register the hotkey with better error handling + match hotkey_manager.register(hotkey) { + Ok(_) => { + println!("Successfully registered Ctrl+Shift+F12 as exit hotkey"); + }, + Err(e) => { + eprintln!("Failed to register hotkey: {}", e); + // Try registering with a different hotkey as fallback + let fallback_hotkey = GlobalHotKey::new( + Some(Modifiers::CONTROL | Modifiers::ALT), + Code::F12, + ); + + match hotkey_manager.register(fallback_hotkey) { + Ok(_) => println!("Registered fallback hotkey Ctrl+Alt+F12 instead"), + Err(e) => eprintln!("Failed to register fallback hotkey: {}", e), + } + } } + println!("Press Ctrl+Shift+F12 to exit the application"); + // We're using the event-driven approach with the subscription function for mouse tracking - // Spawn a thread to listen for tray events - let tx_clone = tx.clone(); - thread::spawn(move || { - loop { - if let Ok(_) = rx.recv() { - // Send a message to the UI - let _ = tx_clone.send(Message::TrayEvent); - } - } - }); + // We no longer need the thread to forward messages from rx to tx_clone_ui + // since we're using the iced subscription system directly // Spawn a thread to listen for hotkeys let state_clone = shared_state.clone(); thread::spawn(move || { + println!("Hotkey listener thread started"); let event_receiver = global_hotkey::GlobalHotKeyEvent::receiver(); + loop { - if let Ok(event) = event_receiver.try_recv() { - if event.state == HotKeyState::Pressed { - println!("Exit hotkey pressed, shutting down..."); - - // Set running to false - if let Ok(mut state) = state_clone.lock() { - state.running = false; + match event_receiver.try_recv() { + Ok(event) => { + println!("Received hotkey event: {:?}", event); + if event.state == HotKeyState::Pressed { + println!("Exit hotkey pressed, sending Quit message..."); + // Send Quit message instead of direct exit + // Get the global sender + if let Some(tx) = TRAY_SENDER.lock().unwrap().as_ref() { + if let Err(e) = tx.unbounded_send(Message::Quit) { + eprintln!("Failed to send Quit message from hotkey thread: {}", e); + // Fallback to direct exit if channel fails + platform::restore_cursor(); + std::process::exit(0); + } + } else { + eprintln!("Hotkey: Global sender not initialized yet"); + // Fallback to direct exit if sender not available + platform::restore_cursor(); + std::process::exit(0); + } + // Don't exit directly here, let the main thread handle it. } + }, + Err(_) => { + // No events or channel disconnected, sleep briefly + std::thread::sleep(Duration::from_millis(50)); - // Restore the system cursor - platform::restore_cursor(); - - // Exit the application - std::process::exit(0); + // Check if we should exit (will be checked in the loop condition below) + } + } + // Check shared state running flag (optional, main thread handles exit) + if let Ok(state) = state_clone.lock() { + if !state.running { + println!("Hotkey listener detected running=false, exiting thread."); + break; } } - - // Small sleep to prevent high CPU usage - std::thread::sleep(Duration::from_millis(10)); } }); @@ -260,120 +388,223 @@ fn boot() -> (CursorRush, Task) { } fn main() -> iced::Result { + // Initialize Steam let _steam = steam::Steam::new(); + + // Note: We can't use a consistent window ID across boot() and main() + // because iced creates its own window ID internally + // Instead, we'll capture the real window ID in the CloseRequested event iced::application("Cursor Rush", update, view) .theme(|_| Theme::Dark) .subscription(subscription) .centered() + // This is the key - we need to tell iced not to exit when the window close button is clicked + .exit_on_close_request(false) + // run_with expects a function that returns (State, Task), not the tuple directly .run_with(boot) } // Set up the system tray icon +// Simplified signature without passing the channel fn setup_tray_icon( cursors: &[CursorInfo], shared_state: Arc>, - tx: mpsc::Sender, -) { +) -> Option { // Return Option if cursors.is_empty() { - return; + return None; // Return None if no cursors } // Create a menu - let mut menu = Menu::new(); + let menu = Menu::with_items(&[ + &tray_icon::menu::MenuItem::with_id(NEXT_CURSOR_ID, "Next cursor", true, None), + &tray_icon::menu::MenuItem::with_id(SHOW_WINDOW_ID, "Show window", true, None), + &tray_icon::menu::MenuItem::with_id(QUIT_ID, "Quit", true, None), + ]).ok()?; // Use ok()? to convert Result to Option - // Add menu items with IDs - let next_item = tray_icon::menu::MenuItem::with_id( - NEXT_CURSOR_ID, - "Next cursor", - true, - None, // No accelerator - ); - - let quit_item = tray_icon::menu::MenuItem::with_id( - QUIT_ID, - "Quit", - true, - None, // No accelerator - ); - - // Add items to menu - if let Ok(menu_with_items) = Menu::with_items(&[&next_item, &quit_item]) { - menu = menu_with_items; - } // Use the first PNG as the tray icon let icon_path = &cursors[0].path; - - // Load the image - let img = match image_open(icon_path) { - Ok(img) => img, - Err(e) => { - eprintln!("Failed to open image {}: {}", icon_path.display(), e); - return; - } - }; - - // Convert to RGBA + let img = image_open(icon_path).ok()?; // Use ok()? let rgba_img = img.to_rgba8(); let (width, height) = rgba_img.dimensions(); - - // Create icon from RGBA data - let icon = match tray_icon::Icon::from_rgba(rgba_img.into_raw(), width, height) { - Ok(icon) => icon, - Err(e) => { - eprintln!("Failed to create icon: {}", e); - return; - } - }; + let icon = tray_icon::Icon::from_rgba(rgba_img.into_raw(), width, height).ok()?; // Use ok()? // Build the tray icon - let _tray = TrayIconBuilder::new() + println!("Building tray icon..."); + let tray_result = TrayIconBuilder::new() .with_tooltip("Cursor Rush") .with_icon(icon) .with_menu(Box::new(menu)) - .build() - .unwrap(); + .with_menu_on_left_click(false) // Disable menu on left click - only show on right click + .build(); + + println!("Tray icon configuration: menu_on_left_click = false"); + + // Store the tray icon itself + let tray = match tray_result { + Ok(tray_instance) => { + println!("Tray icon built successfully"); + tray_instance // Assign the TrayIcon object + }, + Err(e) => { + eprintln!("Failed to build tray icon: {}", e); + return None; // Return None on error + } + }; // Listen for tray menu events in a separate thread let state_clone = shared_state.clone(); + println!("Spawning tray menu event listener thread..."); thread::spawn(move || { let menu_channel = MenuEvent::receiver(); + println!("Tray menu event listener waiting for events..."); + while let Ok(event) = menu_channel.recv() { - match event.id.as_ref() { + println!("Tray menu event received: ID = {:?}", event.id); + let msg_to_send = match event.id.as_ref() { NEXT_CURSOR_ID => { - // Handle next cursor event - let mut state = state_clone.lock().unwrap(); - if !state.cursors.is_empty() { - state.current_index = (state.current_index + 1) % state.cursors.len(); - if let Some(cursor) = state.cursors.get(state.current_index) { - apply_cursor(&cursor.path); - // Ensure the render thread is running with the new cursor - platform::render_cursor(); - - // Load the new cursor image - let current_cursor_image = match image_open(&cursor.path) { - Ok(img) => Some(img.to_rgba8()), - Err(_) => None, - }; - - // Update the cursor image in shared state - state.current_cursor_image = current_cursor_image; - } + println!("Tray: Next Cursor requested"); + match state_clone.lock() { + Ok(mut state) => { + if !state.cursors.is_empty() { + state.current_index = (state.current_index + 1) % state.cursors.len(); + if let Some(cursor) = state.cursors.get(state.current_index) { + println!("Tray: Applying next cursor: {}", cursor.name); + apply_cursor(&cursor.path); + platform::render_cursor(); // Ensure render starts/updates + state.current_cursor_image = image_open(&cursor.path).ok().map(|img| img.to_rgba8()); + } + } + }, + Err(e) => eprintln!("Tray: Failed to lock shared state for next cursor: {}", e), } - // Notify the UI - let _ = tx.send(Message::TrayEvent); + // Send TrayEvent just to trigger UI update if needed + Some(Message::TrayEvent) + }, + SHOW_WINDOW_ID => { + println!("Tray: Show Window requested"); + // Send ShowWindow message to main thread + Some(Message::ShowWindow) }, QUIT_ID => { - // Restore the system cursor before exiting - platform::restore_cursor(); - - // Exit the application - std::process::exit(0); + println!("Tray: Quit requested"); + // Send Quit message to main thread + Some(Message::Quit) }, - _ => {} // any other menu items you add later + _ => { + println!("Tray: Unknown menu event ID: {:?}", event.id); + None // Don't send a message for unknown IDs + } + }; + + // Send the determined message to the main UI thread using the global sender + if let Some(msg) = msg_to_send { + // Get the global sender + if let Some(tx) = TRAY_SENDER.lock().unwrap().as_ref() { + if let Err(e) = tx.unbounded_send(msg.clone()) { // Use clone if msg is needed after send + eprintln!("Tray: Failed to send {:?} message: {}", msg, e); + break; // Stop thread if channel broken + } + // If Quit message was sent, break the loop to exit the listener thread + if matches!(msg, Message::Quit) { + println!("Tray: Quit message sent, exiting listener thread"); + break; + } + } else { + eprintln!("Tray: Global sender not initialized yet"); + } } } + + println!("Tray menu event listener thread finished"); }); + + // Set up a direct event handler for tray icon events + // This is the preferred way to handle tray icon clicks + println!("Setting up tray icon click event handler..."); + + // Use set_event_handler to directly handle tray icon events + TrayIconEvent::set_event_handler(Some(move |event| { + // Only process important events, filter out mouse movements + match event { + // Handle click events + TrayIconEvent::Click { button, .. } => { + println!("Tray: Click detected with button: {:?}", button); + // Left click should show the window + if button == MouseButton::Left { + println!("Tray: Left click detected, sending ShowWindow message"); + // Get the global sender + if let Some(tx) = TRAY_SENDER.lock().unwrap().as_ref() { + if let Err(e) = tx.unbounded_send(Message::ShowWindow) { + eprintln!("Tray: Failed to send ShowWindow message via handler: {}", e); + } + } else { + eprintln!("Tray: Global sender not initialized yet for click handler"); + } + } + }, + // Handle double-click events + TrayIconEvent::DoubleClick { button, .. } => { + println!("Tray: Double click detected with button: {:?}", button); + // Double left click should also show the window + if button == MouseButton::Left { + println!("Tray: Double left click detected, sending ShowWindow message"); + // Get the global sender + if let Some(tx) = TRAY_SENDER.lock().unwrap().as_ref() { + if let Err(e) = tx.unbounded_send(Message::ShowWindow) { + eprintln!("Tray: Failed to send ShowWindow message via handler on double click: {}", e); + } + } else { + eprintln!("Tray: Global sender not initialized yet for double click handler"); + } + } + }, + // Silently ignore mouse movement events + TrayIconEvent::Move { .. } => { + // Don't log anything for mouse movements + }, + // Silently ignore mouse enter/leave events + TrayIconEvent::Enter { .. } | TrayIconEvent::Leave { .. } => { + // Don't log anything for mouse enter/leave + }, + // Log other events for debugging (but not mouse-related ones) + _ => { + println!("Tray: Event received: {:?}", event); + } + } + })); + + println!("Tray setup complete using set_event_handler for clicks."); // Updated log + Some(tray) // Return the TrayIcon object +} + +// Helper function to hide a window to the tray +fn hide_window_to_tray(window_id: iced::window::Id) -> Task { + println!("Attempting to hide window with ID: {:?}", window_id); + + // Use ONLY the iced method for hiding. Platform specific might interfere. + let hide_task: Task<()> = iced::window::change_mode(window_id, iced::window::Mode::Hidden); + + hide_task.map(|_: ()| { + println!("Window hide task completed (via iced)"); + // Return a specific message to confirm the window was hidden + Message::WindowHidden + }) +} + +// Helper function to show a window from the tray +fn show_window_from_tray(window_id: iced::window::Id) -> Task { + println!("Attempting to show window with ID: {:?}", window_id); + + // Use ONLY the iced methods for showing and focusing. + let show_task: Task<()> = iced::window::change_mode(window_id, iced::window::Mode::Windowed); + let focus_task: Task<()> = iced::window::gain_focus(window_id); + + Task::batch(vec![show_task, focus_task]).map(|_: ()| { + println!("Window show/focus tasks completed (via iced)"); + // Return a specific message to confirm the window was shown + Message::WindowShown + }) } // Update function to handle messages @@ -434,11 +665,28 @@ fn update(state: &mut CursorRush, message: Message) -> Task { Task::none() }, Message::Quit => { + println!("Update: Handling Quit message"); + + // Set running flag to false to signal other threads (like hotkey listener) + if let Some(shared) = &state.shared_state { + if let Ok(mut shared_state) = shared.lock() { + shared_state.running = false; + } + } + // Restore the system cursor before exiting platform::restore_cursor(); - // Exit the application - std::process::exit(0); + // Cleanly drop the tray icon *before* exiting iced + // This removes the icon from the system tray immediately + if let Some(tray) = state.tray_icon.take() { + drop(tray); + println!("Tray icon explicitly dropped."); + } + + // Use iced's exit mechanism for a cleaner shutdown + println!("Requesting application exit via iced::exit..."); + iced::exit() }, Message::TrayEvent => { // Sync with shared state @@ -449,11 +697,95 @@ fn update(state: &mut CursorRush, message: Message) -> Task { } Task::none() }, - Message::CursorMoved(position) => { - // Update the cursor position - state.cursor_position = position; + Message::CloseRequested(window_id) => { + println!("Handling CloseRequested: Hiding window with ID: {:?}", window_id); + + // Store the real window ID for future use + // This is critical for showing the window later + state.main_window = Some(window_id); + println!("Stored window ID: {:?} for future use", window_id); + + // Update shared state first + if let Some(shared) = &state.shared_state { + match shared.lock() { + Ok(mut shared_state) => { + if shared_state.window_visible { // Only hide if visible + shared_state.window_visible = false; + println!("Set window_visible to false in shared state"); + println!("Hiding window with ID: {:?}, app continues running in tray", window_id); + return hide_window_to_tray(window_id); + } else { + println!("Window already hidden, ignoring CloseRequested."); + return Task::none(); // Don't try to hide again + } + } + Err(e) => { + eprintln!("Error locking shared state for hiding: {}", e); + return Task::none(); + } + } + } else { + eprintln!("Error: Shared state not available for hiding."); + return Task::none(); + } + }, + Message::WindowHidden => { + println!("Window successfully hidden to tray"); Task::none() }, + Message::WindowShown => { + println!("Update: Received WindowShown message. Window should now be visible."); + Task::none() + }, + Message::ShowWindow => { + println!("Handling ShowWindow: Attempting to show window"); // More specific starting log + + if let Some(window_id) = state.main_window { + println!("Using stored window ID: {:?}", window_id); + + // It's generally safer to attempt the show operation even if the state *thinks* it's visible. + // Let's update the shared state *after* we know the task is being created. + + println!("Creating show/focus tasks for window ID: {:?}", window_id); + let show_task = iced::window::change_mode(window_id, iced::window::Mode::Windowed); + let focus_task = iced::window::gain_focus(window_id); + + // Update shared state *before* returning the task, but after creating it. + if let Some(shared) = &state.shared_state { + match shared.lock() { + Ok(mut shared_state) => { + if !shared_state.window_visible { // Only log if changing state + println!("Updating shared state: window_visible = true"); + shared_state.window_visible = true; + } else { + println!("Shared state already indicates window is visible."); + } + } + Err(e) => { + eprintln!("Error locking shared state during show: {}", e); + // Proceed with showing the window anyway, state might be out of sync + } + } + } else { + eprintln!("Shared state unavailable during show."); + // Proceed with showing the window anyway + } + + println!("Returning Task::batch to show and focus window."); + // Return the batch task + return Task::batch(vec![show_task, focus_task]) + .map(|_: ()| { + // This message confirms the Iced runtime *completed* the tasks. + println!("Iced runtime reports show/focus tasks completed successfully."); + Message::WindowShown // Send confirmation message + }); + + } else { + eprintln!("Error: Main window ID not found when trying to show. Cannot create tasks."); + Task::none() // Cannot proceed without window_id + } + }, + } } @@ -502,21 +834,21 @@ fn view(state: &CursorRush) -> Element { .push(button("Next cursor").on_press(Message::NextCursor)) .push(button("Quit").on_press(Message::Quit)); - // Add instructions for the hotkey + // Add instructions for the hotkey and close button behavior content = content.push( - text("Press Ctrl+Shift+F12 to exit the application").size(14) + container( + Column::new() + .spacing(5) + .push(text("Press Ctrl+Shift+F12 to exit the application").size(16)) + .push(text("Close button (X) minimizes to system tray").size(16)) + ) + .padding(10) ); } else { content = content.push(text("No cursors found in assets directory")); } - // Add cursor position information for debugging - if !state.cursors.is_empty() { - let (x, y) = state.cursor_position; - content = content.push( - text(format!("Cursor position: ({:.1}, {:.1})", x, y)).size(12) - ); - } + // Cursor position display removed to avoid spammy updates // Create the main container container(content) diff --git a/src/platform.rs b/src/platform.rs index 80d1c82..700f6cc 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -731,6 +731,32 @@ mod linux { } } +// Windows-specific function to hide a window - DEPRECATED +// We now use iced's window::change_mode instead for cross-platform compatibility +#[cfg(windows)] +pub fn hide_window() { + println!("WARNING: platform::hide_window is deprecated. Use iced::window::change_mode instead."); +} + +// Windows-specific function to show a window - DEPRECATED +// We now use iced's window::change_mode instead for cross-platform compatibility +#[cfg(windows)] +pub fn show_window() { + println!("WARNING: platform::show_window is deprecated. Use iced::window::change_mode instead."); +} + +// Placeholder for non-Windows platforms - DEPRECATED +#[cfg(not(windows))] +pub fn hide_window() { + println!("WARNING: platform::hide_window is deprecated. Use iced::window::change_mode instead."); +} + +// Placeholder for non-Windows platforms - DEPRECATED +#[cfg(not(windows))] +pub fn show_window() { + println!("WARNING: platform::show_window is deprecated. Use iced::window::change_mode instead."); +} + // Export the appropriate implementation based on platform #[cfg(windows)] pub use windows::{set_cursor, restore_cursor, render_cursor};