在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);
});
}
}
}
重点方法注意:
- 设置窗口无边框样式
win.setStyleMask( NSWindowStyleMask::Borderless | NSWindowStyleMask::FullSizeContentView, ); content_view.setWantsLayer(true) //设置视图需要Layer
- 设置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);
});
}
}
}