Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Tray Icons with Objc / Swift #205

Open
ColeTownsend opened this issue Nov 7, 2024 · 1 comment
Open

Custom Tray Icons with Objc / Swift #205

ColeTownsend opened this issue Nov 7, 2024 · 1 comment

Comments

@ColeTownsend
Copy link

ColeTownsend commented Nov 7, 2024

It would be cool to support custom icon implementations. While currently possible on macOS using custom NSView within an NSStatusItem, it's yet to make it into any Tauri implementations.

In Swift you can do it roughly like below, which I based on this blog

let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let customView = NSView(frame: NSRect(x: 0, y: 0, width: 30, height: 30))
let arcPath = NSBezierPath()
arcPath.appendArc(withCenter: NSPoint(x: 15, y: 15), radius: 10, startAngle: 90, endAngle: 90 - 270, clockwise: true)
NSColor.systemBlue.setStroke()
arcPath.lineWidth = 3
arcPath.stroke()
statusItem.button?.addSubview(customView)

A hacky(?) way to do it could be modify the set_icon_for_ns_status_item_button. The example below is not very flexible, but is just to prove the concept. For flexibility, it could accept an NSView, which the user can define.

use objc2::foundation::{NSPoint, NSRect, NSString};
use objc2::rc::Retained;
use objc2::{msg_send, sel};
use objc2_app_kit::{NSColor, NSStatusItem, NSView};
use objc2_foundation::MainThreadMarker;

pub fn set_icon_for_ns_status_item_button(
    ns_status_item: &NSStatusItem,
    progress: Option<f64>, // Use progress to set circle color dynamically
    mtm: MainThreadMarker,
) -> crate::Result<()> {
    let button = unsafe { ns_status_item.button(mtm).unwrap() };

    // Remove any previous image or view from the button
    unsafe { button.setImage(None) };

    // Create a new custom view and configure it
    let view = create_custom_view(progress);

    // Set the frame of the button and add the custom view as a subview
    let frame = NSRect::new(0.0, 0.0, 120.0, 22.0);
    view.setFrame(frame);
    button.setFrame(frame);
    button.addSubview(&view);

    Ok(())
}

// Function to create the custom view with dynamic color based on progress
fn create_custom_view(progress: Option<f64>) -> Retained<NSView> {
    unsafe {
        // Create a base NSView with specified frame dimensions
        let frame = NSRect::new(0.0, 0.0, 120.0, 22.0);
        let view = NSView::alloc(nil).initWithFrame(frame);

        // Enable the layer for custom drawing
        view.setWantsLayer(true);

        // Add a label displaying "Custom View" to the view
        let label: *mut Object = msg_send![class!(NSTextField), alloc];
        let label: *mut Object = msg_send![label, initWithFrame: NSRect::new(10.0, 2.0, 80.0, 20.0)];
        let text = NSString::from_str("Custom View");
        let _: () = msg_send![label, setStringValue: text];
        let _: () = msg_send![label, setBezeled: false];
        let _: () = msg_send![label, setDrawsBackground: false];
        let _: () = msg_send![label, setEditable: false];
        let _: () = msg_send![label, setSelectable: false];
        view.addSubview(label);

        // Draw a partial circle to represent a progress indicator
        let circle_path: *mut Object = msg_send![class!(NSBezierPath), bezierPath];
        let _: () = msg_send![circle_path, appendBezierPathWithArcWithCenter: NSPoint::new(100.0, 11.0)
            radius: 8.0
            startAngle: 0.0
            endAngle: 270.0
            clockwise: true];

        // Determine the color based on the progress value
        let stroke_color: *mut Object = match progress {
            Some(p) if p < 40.0 => msg_send![class!(NSColor), systemRedColor],
            Some(p) if p < 65.0 => msg_send![class!(NSColor), systemYellowColor],
            _ => msg_send![class!(NSColor), systemGreenColor],
        };

        // Set the color and line width, then draw the circle
        let _: () = msg_send![stroke_color, setStroke];
        let _: () = msg_send![circle_path, setLineWidth: 3.0];
        let _: () = msg_send![circle_path, stroke];

        view
    }
}
@amrbashir
Copy link
Member

We can add a macOS-specific trait that could expose a set_icon_from_ns_view, this way you can create the view using Objc as you like.

Please open a PR if you'd like. I can't really work on this since I don't have a macOS but I will help in any way I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants