Integrate Tauri frontend with categorization system and Steam integration
This commit is contained in:
@@ -14,13 +14,14 @@ rust-version = "1.56"
|
||||
tauri-build = { version = "1.5.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "1.6.0", features = [ "window-start-dragging", "shell-open"] }
|
||||
tauri = { version = "1.6.0", features = [ "window-start-dragging", "shell-open", "system-tray", "window-minimize", "window-hide", "window-show", "window-close"] }
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
image = "0.24"
|
||||
# Ensure "consoleapi" is in this features list
|
||||
winapi = { version = "0.3.9", features = ["winuser", "libloaderapi", "wingdi", "errhandlingapi", "consoleapi"] }
|
||||
winapi = { version = "0.3.9", features = ["winuser", "libloaderapi", "wingdi", "errhandlingapi", "consoleapi"] }
|
||||
once_cell = "1.19.0"
|
||||
steamworks = "0.10.0"
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
# DO NOT REMOVE!!
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// External integrations module
|
||||
|
||||
pub mod steam;
|
||||
@@ -0,0 +1,26 @@
|
||||
// Steam integration
|
||||
|
||||
use steamworks::{AppId, Client, SingleClient};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Steam {
|
||||
client: Client,
|
||||
single: SingleClient,
|
||||
}
|
||||
|
||||
impl Steam {
|
||||
pub fn new() -> Self {
|
||||
let (client, single) = Client::init_app(AppId(1430270)).unwrap();
|
||||
Self { client, single }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn drop_item(&self) {
|
||||
// Placeholder for item drop functionality
|
||||
// The actual implementation would use the Steam API to trigger an item drop
|
||||
println!("Steam item drop triggered (placeholder)");
|
||||
|
||||
// In a real implementation with the correct API version, we would use:
|
||||
// self.client.get_inventory().trigger_item_drop(item_def_id);
|
||||
}
|
||||
}
|
||||
+178
-40
@@ -1,10 +1,11 @@
|
||||
use std::sync::Mutex;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use tauri::{Manager, AppHandle};
|
||||
use tauri::{Manager, AppHandle, CustomMenuItem, SystemTray, SystemTrayMenu, SystemTrayMenuItem, SystemTrayEvent, WindowEvent};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::env;
|
||||
|
||||
mod platform;
|
||||
mod integrations;
|
||||
|
||||
// Define cursor structure
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -40,26 +41,26 @@ fn get_current_cursor_index(state: tauri::State<AppStateWrapper>) -> usize {
|
||||
#[tauri::command]
|
||||
fn select_cursor(index: usize, state: tauri::State<AppStateWrapper>, app_handle: AppHandle) -> Result<(), String> {
|
||||
println!("=== RESOURCE DEBUG [select_cursor] ===");
|
||||
|
||||
|
||||
let mut app_state = state.0.lock().unwrap();
|
||||
if index < app_state.cursors.len() {
|
||||
app_state.current_index = index;
|
||||
|
||||
|
||||
let cursor = &app_state.cursors[index];
|
||||
println!("Cursor selected: {}", cursor.name);
|
||||
println!("Original path: {}", cursor.path);
|
||||
|
||||
|
||||
// Get the resource directory
|
||||
let resource_dir = app_handle.path_resolver().resource_dir();
|
||||
println!("Resource dir: {:?}", resource_dir);
|
||||
|
||||
|
||||
// List contents of resource dir
|
||||
if let Some(res_dir) = &resource_dir {
|
||||
println!("Listing contents of resource dir: {}", res_dir.display());
|
||||
if let Ok(entries) = std::fs::read_dir(res_dir) {
|
||||
for (i, entry) in entries.filter_map(Result::ok).enumerate() {
|
||||
println!(" {}: {}", i, entry.path().display());
|
||||
|
||||
|
||||
// List subdirectories
|
||||
if entry.path().is_dir() {
|
||||
if let Ok(subentries) = std::fs::read_dir(entry.path()) {
|
||||
@@ -73,29 +74,29 @@ fn select_cursor(index: usize, state: tauri::State<AppStateWrapper>, app_handle:
|
||||
println!("Failed to read resource directory");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let filename = Path::new(&cursor.path)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.ok_or_else(|| format!("Invalid cursor path: {}", cursor.path))?;
|
||||
|
||||
|
||||
println!("Extracted filename: {}", filename);
|
||||
|
||||
|
||||
// Try using the actual file path directly
|
||||
println!("Using direct file path: {}", cursor.path);
|
||||
|
||||
|
||||
// Verify the file exists
|
||||
if Path::new(&cursor.path).exists() {
|
||||
println!("File exists, applying cursor...");
|
||||
|
||||
|
||||
if let Err(e) = platform::set_system_cursor(&cursor.path) {
|
||||
println!("Error applying cursor: {}", e);
|
||||
return Err(format!("Failed to apply cursor '{}': {}", cursor.name, e));
|
||||
}
|
||||
|
||||
|
||||
// Emit an event to notify the frontend
|
||||
let _ = app_handle.emit_all("cursor_changed", index); // Ignore error
|
||||
|
||||
|
||||
println!("Successfully applied cursor using actual file path");
|
||||
return Ok(());
|
||||
} else {
|
||||
@@ -111,11 +112,11 @@ fn select_cursor(index: usize, state: tauri::State<AppStateWrapper>, app_handle:
|
||||
#[tauri::command]
|
||||
fn next_cursor(state: tauri::State<AppStateWrapper>, app_handle: AppHandle) -> Result<usize, String> {
|
||||
println!("=== RESOURCE DEBUG [next_cursor] ===");
|
||||
|
||||
|
||||
let mut app_state = state.0.lock().unwrap();
|
||||
if !app_state.cursors.is_empty() {
|
||||
app_state.current_index = (app_state.current_index + 1) % app_state.cursors.len();
|
||||
|
||||
|
||||
let cursor = &app_state.cursors[app_state.current_index];
|
||||
println!("Next cursor: {} ({})", cursor.name, cursor.path);
|
||||
|
||||
@@ -126,22 +127,22 @@ fn next_cursor(state: tauri::State<AppStateWrapper>, app_handle: AppHandle) -> R
|
||||
|
||||
// Try both resource paths like in select_cursor
|
||||
println!("Extracted filename: {}", filename);
|
||||
|
||||
|
||||
// Try using the actual file path directly
|
||||
println!("Using direct file path: {}", cursor.path);
|
||||
|
||||
|
||||
// Verify the file exists
|
||||
if Path::new(&cursor.path).exists() {
|
||||
println!("File exists, applying cursor...");
|
||||
|
||||
|
||||
if let Err(e) = platform::set_system_cursor(&cursor.path) {
|
||||
println!("Error applying cursor: {}", e);
|
||||
return Err(format!("Failed to apply cursor '{}': {}", cursor.name, e));
|
||||
}
|
||||
|
||||
|
||||
// Emit an event to notify the frontend
|
||||
let _ = app_handle.emit_all("cursor_changed", app_state.current_index); // Ignore error
|
||||
|
||||
|
||||
println!("Successfully applied cursor using actual file path");
|
||||
return Ok(app_state.current_index);
|
||||
} else {
|
||||
@@ -161,22 +162,40 @@ fn quit_app(app_handle: AppHandle) {
|
||||
Ok(_) => println!("Default cursor restored"),
|
||||
Err(e) => eprintln!("Failed to restore default cursor: {}", e),
|
||||
}
|
||||
|
||||
|
||||
// Exit the application
|
||||
std::thread::spawn(move || {
|
||||
app_handle.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Command to minimize the window to tray
|
||||
#[tauri::command]
|
||||
fn minimize_to_tray(app_handle: AppHandle) -> Result<(), String> {
|
||||
println!("Minimizing to tray");
|
||||
|
||||
// Get the main window
|
||||
let window = app_handle.get_window("main")
|
||||
.ok_or_else(|| "Failed to get main window".to_string())?;
|
||||
|
||||
// Hide the window
|
||||
window.hide().map_err(|e| format!("Failed to hide window: {}", e))?;
|
||||
|
||||
// The tray should already be visible as configured in tauri.conf.json
|
||||
println!("Window hidden, application should now be accessible from the system tray");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Function to load cursor files from src/cursors directory
|
||||
fn load_cursor_files() -> Vec<Cursor> {
|
||||
println!("=== RESOURCE DEBUG [load_cursor_files] ===");
|
||||
let mut cursors = Vec::new();
|
||||
|
||||
|
||||
// Determine the path to src/cursors relative to src-tauri (CARGO_MANIFEST_DIR)
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
println!("CARGO_MANIFEST_DIR: {}", manifest_dir.display());
|
||||
|
||||
|
||||
// Check multiple possible locations
|
||||
let possible_cursor_dirs = vec![
|
||||
manifest_dir.join("../src/cursors"),
|
||||
@@ -184,26 +203,26 @@ fn load_cursor_files() -> Vec<Cursor> {
|
||||
manifest_dir.join("cursors"),
|
||||
manifest_dir.clone(), // Check manifest dir itself
|
||||
];
|
||||
|
||||
|
||||
println!("CHECKING FOR PNG/CUR FILES IN MULTIPLE DIRECTORIES:");
|
||||
for (i, cursors_dir) in possible_cursor_dirs.iter().enumerate() {
|
||||
println!("Checking dir {}: {}", i+1, cursors_dir.display());
|
||||
println!(" Directory exists: {}", cursors_dir.exists());
|
||||
|
||||
|
||||
if cursors_dir.exists() {
|
||||
println!(" Looking for cursor files in this directory...");
|
||||
|
||||
|
||||
if let Ok(entries) = std::fs::read_dir(cursors_dir) {
|
||||
for entry in entries.filter_map(Result::ok) {
|
||||
let path = entry.path();
|
||||
println!(" Found: {}", path.display());
|
||||
|
||||
|
||||
if path.is_file() && path.extension().map_or(false, |ext| ext == "png" || ext == "cur") {
|
||||
let name = path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("Unknown")
|
||||
.to_string();
|
||||
|
||||
|
||||
println!(" ADDING CURSOR: {} at path {}", name, path.display());
|
||||
cursors.push(Cursor {
|
||||
name,
|
||||
@@ -212,14 +231,14 @@ fn load_cursor_files() -> Vec<Cursor> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !cursors.is_empty() {
|
||||
println!(" Found {} cursor files in this directory.", cursors.len());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we found no cursors, use mock data with paths relative to what resolve_resource expects
|
||||
if cursors.is_empty() {
|
||||
println!("No cursors found in any directory. Using mock data instead.");
|
||||
@@ -228,28 +247,28 @@ fn load_cursor_files() -> Vec<Cursor> {
|
||||
for dir in &possible_cursor_dirs {
|
||||
println!(" {}", dir.display());
|
||||
}
|
||||
|
||||
|
||||
// Mock data paths should reflect their eventual resource path structure
|
||||
cursors.push(Cursor {
|
||||
name: "Default".to_string(),
|
||||
path: "src/cursors/default.png".to_string(), // Filename will be 'default.png'
|
||||
});
|
||||
|
||||
|
||||
cursors.push(Cursor {
|
||||
name: "Icon".to_string(),
|
||||
path: "src/cursors/icon.png".to_string(), // Filename will be 'icon.png'
|
||||
});
|
||||
|
||||
|
||||
cursors.push(Cursor {
|
||||
name: "Red".to_string(),
|
||||
path: "src/cursors/red.png".to_string(), // Filename will be 'red.png'
|
||||
});
|
||||
|
||||
|
||||
println!("Created {} mock cursor entries", cursors.len());
|
||||
}
|
||||
|
||||
|
||||
println!("Loaded {} cursor files", cursors.len());
|
||||
|
||||
|
||||
// Sort cursors by name for consistent ordering
|
||||
cursors.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
cursors
|
||||
@@ -258,34 +277,153 @@ fn load_cursor_files() -> Vec<Cursor> {
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
println!("Starting Cursor Rush application");
|
||||
|
||||
|
||||
// Initialize platform-specific cursor handling
|
||||
if let Err(e) = platform::initialize() {
|
||||
eprintln!("Failed to initialize platform: {}", e);
|
||||
}
|
||||
|
||||
|
||||
// Load cursors from assets directory
|
||||
let cursors = load_cursor_files();
|
||||
|
||||
|
||||
// Create app state
|
||||
let app_state = AppState {
|
||||
cursors,
|
||||
current_index: 0,
|
||||
};
|
||||
|
||||
// Create system tray menu
|
||||
let next_cursor_item = CustomMenuItem::new("next_cursor".to_string(), "Next cursor");
|
||||
let show_window_item = CustomMenuItem::new("show_window".to_string(), "Show window");
|
||||
let quit_item = CustomMenuItem::new("quit".to_string(), "Quit");
|
||||
|
||||
let tray_menu = SystemTrayMenu::new()
|
||||
.add_item(next_cursor_item)
|
||||
.add_item(show_window_item)
|
||||
.add_native_item(SystemTrayMenuItem::Separator)
|
||||
.add_item(quit_item);
|
||||
|
||||
let system_tray = SystemTray::new().with_menu(tray_menu);
|
||||
|
||||
// This is one of the simplest ways to initialize Tauri
|
||||
tauri::Builder::default()
|
||||
.manage(AppStateWrapper(Mutex::new(app_state)))
|
||||
.system_tray(system_tray)
|
||||
.on_system_tray_event(|app, event| match event {
|
||||
SystemTrayEvent::LeftClick { .. } => {
|
||||
println!("System tray received a left click");
|
||||
if let Some(window) = app.get_window("main") {
|
||||
match window.is_visible() {
|
||||
Ok(visible) => {
|
||||
if visible {
|
||||
println!("Window is already visible, focusing it");
|
||||
let _ = window.set_focus();
|
||||
} else {
|
||||
println!("Window is hidden, showing it");
|
||||
if let Err(e) = window.show() {
|
||||
eprintln!("Failed to show window: {}", e);
|
||||
}
|
||||
if let Err(e) = window.set_focus() {
|
||||
eprintln!("Failed to focus window: {}", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Failed to check window visibility: {}", e);
|
||||
// Try to show it anyway
|
||||
if let Err(e) = window.show() {
|
||||
eprintln!("Failed to show window: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Main window not found");
|
||||
}
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
println!("Menu item clicked: {}", id);
|
||||
match id.as_str() {
|
||||
"next_cursor" => {
|
||||
println!("Next cursor requested from tray");
|
||||
let state = app.state::<AppStateWrapper>();
|
||||
let mut app_state = state.0.lock().unwrap();
|
||||
|
||||
if !app_state.cursors.is_empty() {
|
||||
app_state.current_index = (app_state.current_index + 1) % app_state.cursors.len();
|
||||
let cursor = &app_state.cursors[app_state.current_index];
|
||||
|
||||
if Path::new(&cursor.path).exists() {
|
||||
if let Err(e) = platform::set_system_cursor(&cursor.path) {
|
||||
eprintln!("Error applying cursor from tray: {}", e);
|
||||
} else {
|
||||
// Emit an event to notify the frontend
|
||||
let _ = app.emit_all("cursor_changed", app_state.current_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"show_window" => {
|
||||
println!("Show window requested from tray");
|
||||
if let Some(window) = app.get_window("main") {
|
||||
if let Err(e) = window.show() {
|
||||
eprintln!("Failed to show window: {}", e);
|
||||
}
|
||||
if let Err(e) = window.set_focus() {
|
||||
eprintln!("Failed to focus window: {}", e);
|
||||
}
|
||||
} else {
|
||||
eprintln!("Main window not found");
|
||||
}
|
||||
}
|
||||
"quit" => {
|
||||
println!("Quit requested from tray");
|
||||
// Clean up and restore default cursor before exit
|
||||
match platform::restore_system_cursor() {
|
||||
Ok(_) => println!("Default cursor restored"),
|
||||
Err(e) => eprintln!("Failed to restore default cursor: {}", e),
|
||||
}
|
||||
app.exit(0);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.on_window_event(move |event| {
|
||||
match event.event() {
|
||||
WindowEvent::CloseRequested { api, .. } => {
|
||||
// Prevent the default close action
|
||||
api.prevent_close();
|
||||
|
||||
// Get the window and hide it
|
||||
let window = event.window();
|
||||
println!("Window close requested in backend handler, hiding window");
|
||||
|
||||
// Hide the window instead of closing it
|
||||
if let Err(e) = window.hide() {
|
||||
eprintln!("Failed to hide window: {}", e);
|
||||
} else {
|
||||
println!("Window hidden successfully, app is now in system tray");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_cursors,
|
||||
get_current_cursor_index,
|
||||
select_cursor,
|
||||
next_cursor,
|
||||
quit_app
|
||||
quit_app,
|
||||
minimize_to_tray
|
||||
])
|
||||
.setup(|_app| {
|
||||
// Register for events or perform additional setup
|
||||
println!("Tauri application setup complete");
|
||||
|
||||
// We're not setting up a window close handler here anymore
|
||||
// The frontend will handle this based on the minimize-to-tray setting
|
||||
|
||||
Ok(())
|
||||
})
|
||||
// This matches exactly how it was before our changes
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
"all": false,
|
||||
"window": {
|
||||
"all": false,
|
||||
"startDragging": true
|
||||
"startDragging": true,
|
||||
"minimize": true,
|
||||
"hide": true,
|
||||
"show": true,
|
||||
"close": true
|
||||
},
|
||||
"shell": {
|
||||
"all": false,
|
||||
@@ -27,6 +31,12 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"systemTray": {
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconAsTemplate": false,
|
||||
"menuOnLeftClick": false,
|
||||
"title": "Cursor Rush"
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"identifier": "com.cursor-rush.dev",
|
||||
@@ -47,13 +57,15 @@
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"label": "main",
|
||||
"title": "Cursor Rush",
|
||||
"width": 310,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false,
|
||||
"decorations": false,
|
||||
"transparent": true
|
||||
"transparent": true,
|
||||
"visible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user