egui_desktop/utils/
rounded_corners.rs1use eframe::Frame;
2use egui::Context;
3use raw_window_handle::{HasWindowHandle, RawWindowHandle};
4use std::{
5 collections::HashSet,
6 ffi::c_void,
7 sync::{LazyLock, Mutex, Once},
8};
9
10use crate::utils::os::apply_native_rounded_corners;
11
12static APPLIED_VIEWPORTS: LazyLock<Mutex<HashSet<egui::ViewportId>>> =
14 LazyLock::new(|| Mutex::new(HashSet::new()));
15
16pub fn apply_rounded_corners(frame: &Frame) {
21 static INIT: Once = Once::new();
22
23 INIT.call_once(|| {
24 if let Ok(window_handle) = frame.window_handle() {
25 apply_rounded_corners_from_handle(window_handle);
26 }
27 });
28}
29
30pub fn store_window_handle_for_viewport(
42 ctx: &Context,
43 frame: &Frame,
44 viewport_id: egui::ViewportId,
45) {
46 if let Ok(window_handle) = frame.window_handle() {
47 let raw_handle: RawWindowHandle = window_handle.into();
48
49 let ptr: Option<*mut c_void> = match raw_handle {
51 RawWindowHandle::Win32(h) => Some(h.hwnd.get() as *mut _),
52 RawWindowHandle::AppKit(h) => Some(h.ns_view.as_ptr() as *mut _),
53 RawWindowHandle::Xlib(h) => Some(h.window as *mut _),
54 RawWindowHandle::Wayland(h) => Some(h.surface.as_ptr() as *mut _),
55 _ => None,
56 };
57
58 if let Some(native_ptr) = ptr {
59 let id = egui::Id::new(("rounded_corners_ptr", viewport_id));
60 let ptr_as_usize = native_ptr as usize;
62 ctx.data_mut(|data| {
63 data.insert_temp(id, ptr_as_usize);
64 });
65 }
66 }
67}
68
69pub fn apply_rounded_corners_to_viewport(ctx: &Context) {
91 let viewport_id = ctx.viewport_id();
92
93 if ctx.input(|i| i.viewport().close_requested()) {
96 let mut applied = APPLIED_VIEWPORTS.lock().unwrap();
97 applied.remove(&viewport_id);
98 let id = egui::Id::new(("rounded_corners_ptr", viewport_id));
100 ctx.data_mut(|data| {
101 data.remove::<usize>(id);
102 });
103 return;
104 }
105
106 let id = egui::Id::new(("rounded_corners_ptr", viewport_id));
112 let mut handle_found = false;
113
114 if let Some(ptr_as_usize) = ctx.data(|data| data.get_temp::<usize>(id)) {
115 let native_ptr = ptr_as_usize as *mut c_void;
117
118 match apply_native_rounded_corners(native_ptr) {
119 Ok(_) => {
120 println!("đ Native rounded corners applied successfully to viewport!");
121 handle_found = true;
122 }
123 Err(e) => {
124 eprintln!(
125 "â ī¸ Failed to apply native rounded corners to viewport (stored handle): {}",
126 e
127 );
128 }
130 }
131 }
132
133 if !handle_found {
136 if let Some(native_ptr) = get_viewport_window_handle(ctx) {
137 match apply_native_rounded_corners(native_ptr) {
138 Ok(_) => {
139 println!("đ Native rounded corners applied successfully to viewport!");
140 let ptr_as_usize = native_ptr as usize;
142 ctx.data_mut(|data| {
143 data.insert_temp(id, ptr_as_usize);
144 });
145 handle_found = true;
146 }
147 Err(e) => {
148 eprintln!(
149 "â ī¸ Failed to apply native rounded corners to viewport (found handle): {}",
150 e
151 );
152 }
153 }
154 }
155 }
156
157 if !handle_found {
158 eprintln!(
159 "â ī¸ Could not apply rounded corners to viewport {:?}: Window handle not found. \
160 This may happen if the window hasn't been fully created yet.",
161 viewport_id
162 );
163 }
164}
165
166fn get_viewport_window_handle(ctx: &Context) -> Option<*mut c_void> {
169 let viewport_title = ctx.input(|i| i.viewport().title.clone())?;
171
172 #[cfg(target_os = "windows")]
173 {
174 use std::ffi::OsStr;
175 use std::os::windows::ffi::OsStrExt;
176 use windows::Win32::UI::WindowsAndMessaging::{FindWindowW, GetWindowThreadProcessId};
177 use windows::core::PCWSTR;
178
179 let title_wide: Vec<u16> = OsStr::new(&viewport_title)
181 .encode_wide()
182 .chain(std::iter::once(0))
183 .collect();
184
185 unsafe {
186 if let Ok(hwnd) = FindWindowW(None, PCWSTR::from_raw(title_wide.as_ptr())) {
189 if !hwnd.is_invalid() {
190 let mut process_id = 0u32;
192 GetWindowThreadProcessId(hwnd, Some(&mut process_id));
193 if process_id == std::process::id() {
194 return Some(hwnd.0 as *mut c_void);
195 }
196 }
197 }
198 }
199 }
200
201 #[cfg(target_os = "macos")]
202 {
203 use cocoa::appkit::NSWindow;
204 use cocoa::base::{id, nil};
205 use objc::{msg_send, sel, sel_impl};
206 use std::ffi::CString;
207
208 unsafe {
209 let windows: id = msg_send![cocoa::appkit::NSApp(), windows];
211 let count: usize = msg_send![windows, count];
212
213 for i in 0..count {
214 let window: id = msg_send![windows, objectAtIndex: i];
215 let title: id = msg_send![window, title];
216
217 if title != nil {
218 let title_str = cocoa::foundation::NSString::from_utf8_lossy(
219 cocoa::foundation::NSString::UTF8String(title),
220 );
221 if title_str == viewport_title {
222 let content_view: id = msg_send![window, contentView];
224 if content_view != nil {
225 return Some(content_view as *mut c_void);
226 }
227 }
228 }
229 }
230 }
231 }
232
233 #[cfg(target_os = "linux")]
235 {
236 }
238
239 None
240}
241
242fn apply_rounded_corners_from_handle(window_handle: raw_window_handle::WindowHandle) {
244 let handle: RawWindowHandle = window_handle.into();
245
246 let ptr: Option<*mut c_void> = match handle {
247 RawWindowHandle::Win32(h) => {
248 println!("đĒ Windows: Using Win32 window handle");
249 Some(h.hwnd.get() as *mut _)
250 }
251 RawWindowHandle::AppKit(h) => {
252 println!("đ macOS: Using AppKit window handle");
253 Some(h.ns_view.as_ptr() as *mut _)
254 }
255 RawWindowHandle::Xlib(h) => {
256 println!("đ§ Linux X11: Using Xlib window handle");
257 Some(h.window as *mut _)
258 }
259 RawWindowHandle::Wayland(h) => {
260 println!("đ§ Linux Wayland: Using Wayland surface handle");
261 Some(h.surface.as_ptr() as *mut _)
262 }
263 _ => {
264 println!(
265 "âšī¸ Platform: Native rounded corners not supported for this window handle type: {:?}",
266 handle
267 );
268 None
269 }
270 };
271
272 if let Some(native_ptr) = ptr {
273 match apply_native_rounded_corners(native_ptr) {
274 Ok(_) => println!("đ Native rounded corners applied successfully!"),
275 Err(e) => eprintln!("â ī¸ Failed to apply native rounded corners: {}", e),
276 }
277 }
278}