Merged system tray functionality with latest Steam integration

This commit is contained in:
2025-05-04 21:26:36 -05:00
parent 1305481a35
commit 80d091eb71
3 changed files with 496 additions and 137 deletions
+469 -137
View File
@@ -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<CursorInfo>,
current_cursor_image: Option<RgbaImage>,
running: bool,
window_visible: bool, // Track if the window is visible
}
// Our application state
#[derive(Default)]
struct CursorRush {
cursors: Vec<CursorInfo>,
current_index: usize,
shared_state: Option<Arc<Mutex<SharedState>>>,
cursor_position: (f32, f32), // Track the mouse position
main_window: Option<iced::window::Id>, // Store the main window ID
tray_icon: Option<TrayIcon>, // Store the TrayIcon object to keep it alive
tray_tx: Option<mpsc::UnboundedSender<Message>>, // 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<std::sync::Mutex<Option<mpsc::UnboundedSender<Message>>>> =
Lazy::new(|| std::sync::Mutex::new(None));
// Function to get or create the global tray sender
fn get_tray_sender() -> mpsc::UnboundedSender<Message> {
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<Message> {
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::<Message>();
// 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<Message>) {
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<Message>) {
}
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<Mutex<SharedState>>,
tx: mpsc::Sender<Message>,
) {
) -> Option<TrayIcon> { // Return Option<TrayIcon>
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<Message> {
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<Message> {
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<Message> {
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<Message> {
}
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<Message> {
.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)
+26
View File
@@ -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};