Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ image = { version = "0.25", default-features = false, features = ["png"] }
windows-sys = { version = "0.59", features = [
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_Media",
"Win32_UI_WindowsAndMessaging",
] }
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"allow-chat-clear-cache",
"allow-animate-window-bounds",
"allow-set-window-bounds",
"allow-set-window-region",
"allow-monitor-work-area",
"allow-panel-open",
"allow-panel-close",
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src-tauri/permissions/animation.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ commands.allow = ["animate_window_bounds"]
identifier = "allow-set-window-bounds"
description = "Allows applying window position and size in a single step, without animating"
commands.allow = ["set_window_bounds"]

[[permission]]
identifier = "allow-set-window-region"
description = "Allows clipping the window's paint and hit-test area to a rounded rectangle"
commands.allow = ["set_window_region"]
90 changes: 90 additions & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,87 @@ fn set_window_bounds(window: WebviewWindow, to: Bounds) -> Result<(), String> {
requeue_bounds(&window, to)
}

#[cfg(windows)]
#[tauri::command]
fn set_window_region(
window: WebviewWindow,
region: Option<Bounds>,
radius: Option<u32>,
) -> Result<(), String> {
// `SetWindowRgn` mora em user32, mas o windows-sys o expõe em Graphics::Gdi
// junto do resto da API de regiões — não em UI::WindowsAndMessaging.
use windows_sys::Win32::Graphics::Gdi::{CreateRoundRectRgn, SetWindowRgn};

let hwnd = window.hwnd().map_err(|e| e.to_string())?;

let hrgn = match region {
Some(rect) => {
// Limites exclusivos, sem `+1`: é melhor a região ficar um pixel
// dentro do desenho do que um pixel fora. Fora dela a janela expõe
// o fundo padrão do WebView2 (branco), que vira uma linha clara em
// volta da pílula; dentro, no máximo se perde uma fatia da borda.
let diameter = i32::try_from(radius.unwrap_or(0)).unwrap_or(0) * 2;
unsafe {
CreateRoundRectRgn(
rect.x,
rect.y,
rect.x + rect.width as i32,
rect.y + rect.height as i32,
diameter,
diameter,
)
}
}
None => std::ptr::null_mut(),
};

// A janela assume a posse da região; deletá-la aqui invalidaria o handle.
let ok = unsafe { SetWindowRgn(hwnd.0 as _, hrgn, 1) };
if ok == 0 {
return Err("SetWindowRgn failed".into());
}

hide_dwm_border(hwnd.0 as _);
Ok(())
}

/// Desliga o realce que o Windows 11 desenha na borda da janela ativa.
///
/// Só o `DWMWA_BORDER_COLOR`: é o atributo que descreve exatamente esse realce.
/// `DWMWA_NCRENDERING_POLICY` e `DWMWA_WINDOW_CORNER_PREFERENCE` já foram
/// tentados aqui e não resolveram o sintoma — desligar a renderização
/// não-cliente inteira é largo demais para o problema e mexe no comportamento
/// geral da janela, então não vale manter no escuro.
#[cfg(windows)]
fn hide_dwm_border(hwnd: windows_sys::Win32::Foundation::HWND) {
use windows_sys::Win32::Graphics::Dwm::{
DwmSetWindowAttribute, DWMWA_BORDER_COLOR, DWMWA_COLOR_NONE,
};

let color = DWMWA_COLOR_NONE;
// Ignorado em silêncio em versões que não conhecem o atributo.
unsafe {
DwmSetWindowAttribute(
hwnd,
DWMWA_BORDER_COLOR as u32,
&color as *const _ as *const core::ffi::c_void,
core::mem::size_of_val(&color) as u32,
);
}
}

#[cfg(not(windows))]
#[tauri::command]
fn set_window_region(
_window: WebviewWindow,
_region: Option<Bounds>,
_radius: Option<u32>,
) -> Result<(), String> {
// Só o Windows precisa do recorte: é lá que a faixa transparente capta
// cliques. Nas outras plataformas o comando existe para o front não ramificar.
Ok(())
}

#[tauri::command]
fn monitor_work_area(window: WebviewWindow) -> Result<Bounds, String> {
let monitor = window
Expand Down Expand Up @@ -309,6 +390,14 @@ pub fn run() {
if let Some(panel) = app.get_webview_window("panel") {
panel::init_native_blur(&panel);
}
// A ilha já nasce recortada pelo front; tirar o realce da borda aqui
// evita ele aparecer no primeiro foco, antes do primeiro recorte.
#[cfg(windows)]
if let Some(main) = app.get_webview_window("main") {
if let Ok(handle) = main.hwnd() {
hide_dwm_border(handle.0 as _);
}
}
// Desliga o fade de show/hide das janelas e deixa o overlay fora de
// qualquer captura. Precisa rodar aqui: as quatro janelas são
// criadas pelo `tauri.conf.json`, antes deste ponto.
Expand All @@ -318,6 +407,7 @@ pub fn run() {
.invoke_handler(tauri::generate_handler![
animate_window_bounds,
set_window_bounds,
set_window_region,
monitor_work_area,
app::app_quit,
auth::auth_set_tokens,
Expand Down
Loading
Loading