Rust Dioxus -- 实现MacOS圆角窗口

在Rust Dioxus桌面应用中实现圆角窗口,需要导入objc2的相关库,并开启对应的features。Toml配置如下:

[package]
name = "demo"
version = "0.1.0"
authors = ["Hwang <yugecoseceant@foxmail.com>"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
dioxus = { version = "0.6.0", features = [] }
# 导入objc2相关库,开启objc2-core-foundation的feature
objc2 = "0.6.1"
objc2-app-kit = { version = "0.3.1", features = ["objc2-core-foundation"] }
objc2-quartz-core = { version = "0.3.1", features = ["objc2-core-foundation"] }

[features]
default = ["desktop"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]

[profile]

[profile.wasm-dev]
inherits = "dev"
opt-level = 1

[profile.server-dev]
inherits = "dev"

[profile.android-dev]
inherits = "dev"

代码中使用窗口钩子,拿到窗口指针,通过content_view、layer来设置窗口圆角,如下:

use dioxus::{
    desktop::{tao::platform::macos::WindowExtMacOS, use_window},
    prelude::*,
};

const FAVICON: Asset = asset!("/assets/favicon.ico");
const MAIN_CSS: Asset = asset!("/assets/main.css");
const HEADER_SVG: Asset = asset!("/assets/header.svg");

fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    rsx! {
        document::Link { rel: "icon", href: FAVICON }
        document::Link { rel: "stylesheet", href: MAIN_CSS }
        Hero {}
    }
}

#[component]
pub fn Hero() -> Element {
    #[cfg(target_os = "macos")]
    set_window_corner_radius(use_window(), 12.0);
    rsx! {
        div {
            id: "hero",
            img { src: HEADER_SVG, id: "header" }
            div { id: "links",
                a { href: "https://dioxuslabs.com/learn/0.6/", "📚 Learn Dioxus" }
                a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
                a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
                a { href: "https://github.com/DioxusLabs/sdk", "⚙️ Dioxus Development Kit" }
                a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus", "💫 VSCode Extension" }
                a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
            }
        }
    }
}

#[cfg(target_os = "macos")]
pub fn set_window_corner_radius(window: dioxus::desktop::DesktopContext, radius: f64) {
    unsafe {
        window.set_always_on_top(false);
        let ns_window = window.ns_window();
        {
            use objc2::rc::Retained;
            // use objc2_core_graphics::CGColor;
            // use objc2_core_foundation::CGFloat;dioxus://index.html/assets/header-73ca13e70f7867c1.svg
            use objc2_app_kit::{NSColor, NSWindow, NSWindowStyleMask};
            let window_ptr = ns_window as *mut NSWindow;
            let win = Retained::retain(window_ptr).unwrap();
            win.setStyleMask(
                NSWindowStyleMask::Borderless | NSWindowStyleMask::FullSizeContentView,
            );
            win.setBackgroundColor(Some(&NSColor::clearColor()));
            let content_view = win.contentView().unwrap();
            content_view.setWantsLayer(true);
            content_view.layer().map(|layer| {
                // // 创建透明背景色 - 使用Option类型
                // let clear_color: Option<CGColor> = Some(CGColor::new_srgb(0.0 as CGFloat, 0.0, 0.0, 0.0));
                // layer.setBackgroundColor(clear_color);
                layer.setCornerRadius(radius);
                layer.setMasksToBounds(true);
            });
        }
    }
}


重点方法注意:

  1. 设置窗口无边框样式
    win.setStyleMask(
                NSWindowStyleMask::Borderless | NSWindowStyleMask::FullSizeContentView,
            );
    content_view.setWantsLayer(true) //设置视图需要Layer
    
  2. 设置layer圆角效果
    layer.setCornerRadius(radius);
                layer.setMasksToBounds(true);
    

注意:Tauri应用大概也是这个原理。

使用macos自己的窗口按钮,并设置窗口可以移动,代码如下:

#[cfg(target_os = "macos")]
pub fn set_window_corner_radius(window: dioxus::desktop::DesktopContext, radius: f64) {
    unsafe {
        window.set_always_on_top(false);
        let ns_window = window.ns_window();
        {
            use objc2::rc::Retained;
            // use objc2_core_graphics::CGColor;
            // use objc2_core_foundation::CGFloat;dioxus://index.html/assets/header-73ca13e70f7867c1.svg
            use objc2_app_kit::{NSColor, NSWindow, NSWindowStyleMask, NSWindowTitleVisibility};
            let window_ptr = ns_window as *mut NSWindow;
            let win = Retained::retain(window_ptr).unwrap();
            let win_style = win.styleMask();
            win.setTitlebarAppearsTransparent(true);
            win.setTitleVisibility(NSWindowTitleVisibility::Hidden);
            win.setStyleMask(win_style | NSWindowStyleMask::FullSizeContentView);
            win.setMovableByWindowBackground(true);
            win.setBackgroundColor(Some(&NSColor::clearColor()));
            let content_view = win.contentView().unwrap();
            content_view.setWantsLayer(true);
            content_view.layer().map(|layer| {
                // // 创建透明背景色 - 使用Option类型
                // let clear_color: Option<CGColor> = Some(CGColor::new_srgb(0.0 as CGFloat, 0.0, 0.0, 0.0));
                // layer.setBackgroundColor(clear_color);
                layer.setCornerRadius(radius);
                layer.setMasksToBounds(true);
            });
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容