fix(windows): align custom shell rounded frame

This commit is contained in:
Cooper-X-Oak authored and baiqing committed 2026-05-02 12:12:02 +08:00
1 parent f83a5339c7
commit 4ea8389ceb
5 files changed
+109 -4

No files matched your search

+3
View File
@@ -53,8 +53,11 @@ objc2-foundation = "0.2"
objc2-app-kit = "0.2"
[target.'cfg(target_os = "windows")'.dependencies]
raw-window-handle = "0.6"
windows = { version = "0.58", features = [
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_UI_Shell",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_WindowsAndMessaging",
+99 -1
View File
@@ -113,6 +113,7 @@ pub fn run() {
if let Err(e) = apply_mica(&main, None) {
log::warn!("[main] mica failed: {e}");
}
apply_windows_rounded_frame(&main);
}
if let Err(e) = main.show() {
log::warn!("[main] initial show failed: {e}");
@@ -219,10 +220,16 @@ pub fn run() {
RunEvent::Reopen { .. } => show_main_window(app),
RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
if let tauri::WindowEvent::CloseRequested { ref api, .. } = event {
api.prevent_close();
hide_main_window(app);
}
#[cfg(target_os = "windows")]
if matches!(event, tauri::WindowEvent::Resized(_) | tauri::WindowEvent::ScaleFactorChanged { .. }) {
if let Some(main) = app.get_webview_window("main") {
apply_windows_rounded_frame(&main);
}
}
}
}
RunEvent::Exit => {
@@ -234,6 +241,97 @@ pub fn run() {
});
}
#[cfg(target_os = "windows")]
fn apply_windows_rounded_frame<R: Runtime>(window: &tauri::WebviewWindow<R>) {
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use windows::Win32::Foundation::{BOOL, HWND, RECT};
use windows::Win32::Graphics::Dwm::{
DwmSetWindowAttribute, DWMWA_BORDER_COLOR, DWMWA_WINDOW_CORNER_PREFERENCE, DWMWCP_ROUND,
};
use windows::Win32::Graphics::Gdi::{CreateRoundRectRgn, SetWindowRgn, HRGN};
use windows::Win32::UI::WindowsAndMessaging::{
GetWindowLongW, GetWindowRect, SetWindowLongW, SetWindowPos, GWL_STYLE, SWP_FRAMECHANGED,
SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WS_CAPTION, WS_THICKFRAME,
};
let handle = match window.window_handle().map(|h| h.as_raw()) {
Ok(RawWindowHandle::Win32(handle)) => handle,
Ok(other) => {
log::warn!("[main] unexpected raw window handle for DWM frame: {other:?}");
return;
}
Err(e) => {
log::warn!("[main] read raw window handle failed: {e}");
return;
}
};
let hwnd = HWND(handle.hwnd.get() as *mut core::ffi::c_void);
unsafe {
let style = GetWindowLongW(hwnd, GWL_STYLE);
let native_frame_bits = (WS_CAPTION.0 | WS_THICKFRAME.0) as i32;
if style & native_frame_bits != 0 {
SetWindowLongW(hwnd, GWL_STYLE, style & !native_frame_bits);
if let Err(e) = SetWindowPos(
hwnd,
HWND::default(),
0,
0,
0,
0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED,
) {
log::warn!("[main] refresh native frame after style update failed: {e}");
}
}
if window.is_maximized().unwrap_or(false) {
let _ = SetWindowRgn(hwnd, HRGN::default(), BOOL(1));
return;
}
let corner_preference = DWMWCP_ROUND;
if let Err(e) = DwmSetWindowAttribute(
hwnd,
DWMWA_WINDOW_CORNER_PREFERENCE,
&corner_preference as *const _ as *const core::ffi::c_void,
std::mem::size_of_val(&corner_preference) as u32,
) {
log::warn!("[main] set DWM rounded corners failed: {e}");
}
// Remove DWM's fallback 1px light border; the React shell draws the visual stroke.
let border_color_none: u32 = 0xFFFFFFFE;
if let Err(e) = DwmSetWindowAttribute(
hwnd,
DWMWA_BORDER_COLOR,
&border_color_none as *const _ as *const core::ffi::c_void,
std::mem::size_of_val(&border_color_none) as u32,
) {
log::warn!("[main] remove DWM border color failed: {e}");
}
let mut rect = RECT::default();
if let Err(e) = GetWindowRect(hwnd, &mut rect) {
log::warn!("[main] read window rect for rounded region failed: {e}");
return;
}
let width = rect.right - rect.left;
let height = rect.bottom - rect.top;
if width <= 0 || height <= 0 {
return;
}
let region = CreateRoundRectRgn(0, 0, width + 1, height + 1, 18, 18);
if region.is_invalid() {
log::warn!("[main] create rounded window region failed");
return;
}
if SetWindowRgn(hwnd, region, BOOL(1)) == 0 {
log::warn!("[main] apply rounded window region failed");
}
}
}
#[tauri::command]
fn restart_app(app: AppHandle) {
// macOS:自动更新会让新装的 .app 带 com.apple.quarantine(无论 Tauri updater
@@ -255,7 +255,7 @@ function FloatingShellBody({ os, initialTab, initialSettings }: { os: OS; initia
flex: 1, minWidth: 0,
overflow: 'hidden',
background: 'var(--ol-surface)',
borderRadius: os === 'mac' ? 20 : 14,
borderRadius: os === 'mac' ? 20 : 10,
border: '0.5px solid rgba(0,0,0,0.06)',
boxShadow: '0 1px 0 rgba(255,255,255,0.8) inset, 0 8px 24px -12px rgba(15,17,22,0.10), 0 2px 6px -2px rgba(15,17,22,0.06)',
display: 'flex',
@@ -30,6 +30,8 @@ export function detectOS(): OS {
const MAC_TITLEBAR_HEIGHT = 36;
const MAC_SYSTEM_CONTROLS_RESERVED_WIDTH = 80;
const MAC_WINDOW_RADIUS = 20;
const WIN_WINDOW_RADIUS = 0;
interface WindowChromeProps {
os?: OS;
@@ -45,7 +47,7 @@ export function WindowChrome({ os = 'mac', title = 'OpenLess', children, height
width: '100%',
height,
position: 'relative',
borderRadius: os === 'mac' ? 20 : 14,
borderRadius: os === 'mac' ? MAC_WINDOW_RADIUS : WIN_WINDOW_RADIUS,
boxShadow: 'var(--ol-shadow-xl)',
overflow: 'hidden',
display: 'flex',
+3 -1
View File
@@ -3,17 +3,19 @@ html, body, #root {
width: 100%;
margin: 0;
padding: 0;
background: transparent;
overflow: hidden;
}
body {
background: transparent;
user-select: none;
-webkit-user-select: none;
overflow: hidden;
}
#root {
display: flex;
isolation: isolate;
}
button {