tauri-nspanel

repository·v2.1·Indexed 19 days ago

https://github.com/ahkohd/tauri-nspanel

A Tauri plugin for subclassing NSWindow to NSPanel, enabling the creation of macOS-specific floating UI elements such as toolbars, inspectors, and HUDs. It provides a fluent API via PanelBuilder, the tauri_panel! macro for defining custom panel classes and event handlers, and support for advanced macOS behaviors including fullscreen overlays, space-joining, and mouse tracking areas.

Tokens
23.4K
Snippets
80
Records
92
Agent score
64%

What's inside tauri-nspanel

  1. Overview of tauri-nspanel core components

    v2.1

    The tauri-nspanel library is built around several key abstractions:

    Main Types

    • Panel<R>: The core trait implemented by all panels.
    • PanelBuilder<R, P>: A builder used to create panels via a fluent API.
    • ManagerExt<R>: An extension trait that allows you to access panels directly from your Tauri AppHandle.

    Key Enums

    • PanelLevel: Controls window layering levels.
    • StyleMask: Defines window appearance styles.
    • CollectionBehavior: Manages how panels behave with respect to macOS Spaces and fullscreen mode.
    • TrackingAreaOptions: Configures mouse tracking areas.

    Macros

    • tauri_panel!: Used to define panel classes and their associated event handlers.
    • panel!: Used to define an individual panel class.
    • panel_event!: Used to define event handler delegates.
  2. Manage multiple event handlers for different panels

    v2.1

    You can define multiple distinct event handlers within a single tauri_panel! block. This allows you to assign specialized logic to different panels (e.g., a MainPanel vs a UtilityPanel). Each handler is instantiated separately and attached to its respective panel.

    tauri_panel! {
        panel!(MainPanel { config: { can_become_key_window: true } })
        panel!(UtilityPanel { config: { is_floating_panel: true, can_become_key_window: false } })
        
        panel_event!(MainPanelEventHandler {
            window_did_become_key(notification: &NSNotification) -> ()
        })
        
        panel_event!(UtilityPanelEventHandler {
            window_did_become_key(notification: &NSNotification) -> ()
        })
    }
    
    let main_handler = MainPanelEventHandler::new();
    let utility_handler = UtilityPanelEventHandler::new();
    
    // Attach them to their respective panels
    main_panel.set_event_handler(Some(main_handler.as_ref()));
    utility_panel.set_event_handler(Some(utility_handler.as_ref()));
  3. Understand thread safety and main thread dispatch in tauri-nspanel

    v2.1

    tauri-nspanel is designed to be compatible with Tauri's multi-threaded architecture and macOS's Cocoa framework requirements.

    Key Concepts

    • Thread Safety: All panel types implement the Send and Sync traits. This means you can safely pass panels between threads, store them in global state, or use them within async contexts.
    • Automatic Main Thread Dispatch: While the panel handles are thread-safe, the actual underlying NSPanel operations (like showing or hiding) must occur on the main thread. The library handles this automatically; when you call a panel method from a background thread or an async task, the operation is internally dispatched to the main thread for you.

    Usage Implications

    You can safely perform the following without manual thread synchronization:

    • Pass panels between threads.
    • Store panels in static variables or global state.
    • Use panels from async contexts and background tasks.
    • Call panel methods from any thread.
    use tauri_nspanel::ManagerExt;
    use std::sync::Arc;
    use tokio::task;
    
    #[tauri::command]
    async fn background_panel_operation(app: tauri::AppHandle) {
        // Safe to use from async context
        let panel = app.get_webview_panel("my-panel").unwrap();
        
        // Safe to move into async task
        let panel_clone = Arc::clone(&panel);
        task::spawn(async move {
            // Panel operations automatically dispatch to main thread
            panel_clone.show();
        });
    }
  4. Understand the PanelLevel hierarchy

    v2.1

    The PanelLevel enum follows this hierarchy from lowest to highest:

    1. Normal: Regular application windows
    2. Floating: Floating palettes and inspectors
    3. ModalPanel: Modal dialogs and sheets
    4. Utility: Utility windows and panels
    5. Status: Menu bar and status items
    6. PopUpMenu: Pop-up menus and tooltips
    7. ScreenSaver: Screen saver windows
  5. What are panels in tauri-nspanel?

    v2.1
    Panels are a specialized type of macOS window (NSPanel) that float above other windows. They are designed to provide auxiliary controls, tool palettes, inspectors, floating controls, or HUD displays. tauri-nspanel allows you to convert regular Tauri windows into these panels or configure new ones specifically as panels using the PanelBuilder API.
  6. Important: Thread safety requirements for Panel methods

    v2.1

    All panel methods must be called on the main thread.

    While the library implements Send and Sync to allow panel objects to be passed through Tauri's command system, the actual underlying operations are performed on the main thread. Attempting to call these methods from a background thread will result in undefined behavior or crashes.

  7. How the panel_event! macro generates selectors

    v2.1

    The panel_event! macro automatically converts Rust method signatures into Objective-C selectors.

    Rules:

    • Parameter Count:
      • Single parameter: method_name(param) $\rightarrow$ methodName:
      • Multiple parameters: method_name(first, second) $\rightarrow$ methodName:second:
    • Casing: Parameter names are converted from snake_case to camelCase.
      • Example: method_name(foo: Type1, bar_baz: Type2) $\rightarrow$ methodName:barBaz:
  8. Convert builder types to panel values

    v2.1

    All builder types (PanelLevel, StyleMask, CollectionBehavior) implement Into traits or provide .value() methods for seamless conversion to the types required by panel setter methods.

    // These are all equivalent
    panel.set_level(PanelLevel::Floating.value());
    panel.set_level(3i64);  // Raw NSWindowLevel value
    
    // These are equivalent
    let style = StyleMask::empty().titled();
    panel.set_style_mask(style.into());
    
    panel.set_collection_behavior(CollectionBehavior::new().can_join_all_spaces().value());
  9. Getting started with tauri-nspanel

    v2.1

    To create macOS panels in a Tauri application, follow this general workflow:

    1. Install and setup the plugin.
    2. Define a panel class using the tauri_panel! macro to specify custom behavior.
    3. Create the panel using the PanelBuilder API or by converting an existing window.
    4. Handle events (like window events or mouse tracking) if your panel requires interactivity.
    5. Control the panel using available methods to manage appearance and behavior.
  10. Configure a panel for hover activation

    v2.1

    To implement hover-based activation, you must configure the panel with can_become_key_window: true and set up a tracking_area that supports mouse entry/exit events. The active_always() option is critical to ensure mouse events are captured even when the application is not currently the active window.

    tauri_panel! {
        panel!(HoverActivatePanel {
            config: {
                can_become_main_window: false,
                can_become_key_window: true,
                becomes_key_only_if_needed: true,
                is_floating_panel: true
            }
            with: {
                tracking_area: {
                    options: TrackingAreaOptions::new()
                        .active_always()           // Track even when app is inactive
                        .mouse_entered_and_exited() // Get hover notifications
                        .mouse_moved()             // Track mouse movement
                        .cursor_update(),          // Track cursor updates
                    auto_resize: true              // Resize tracking area with window
                }
            }
        })
        
        panel_event!(MyPanelEventHandler {})
    }
  11. Enable and use mouse tracking events

    v2.1

    To handle mouse interactions (hover, movement, etc.), you must enable mouse tracking in the panel's with configuration using TrackingAreaOptions.

    Once enabled, the following callback methods become available on your event handler:

    • on_mouse_entered
    • on_mouse_exited
    • on_mouse_moved
    • on_cursor_update
    tauri_panel! {
        panel!(MouseTrackingPanel {
            config: { can_become_key_window: true }
            with: {
                tracking_area: {
                    options: TrackingAreaOptions::new()
                        .active_always()
                        .mouse_entered_and_exited()
                        .mouse_moved()
                        .cursor_update(),
                    auto_resize: true
                }
            }
        })
        
        panel_event!(MouseTrackingPanelEventHandler {
            window_did_become_key(notification: &NSNotification) -> ()
        })
    }
    
    let handler = MouseTrackingPanelEventHandler::new();
    handler.on_mouse_entered(|event| {
        println!("Mouse entered the panel");
    });
    
    handler.on_mouse_moved(|event| {
        let location = unsafe { event.locationInWindow() };
        println!("Mouse at: x={}, y={}", location.x, location.y);
    });
    
    panel.set_event_handler(Some(handler.as_ref()));