From 597bc80b14cc3ccb0873c353af6798fe11f9f6d5 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz <6529475+prokopyl@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:21:42 +0200 Subject: [PATCH] Implement show() and hide() functions --- examples/plugin_clack/src/gui.rs | 15 +++++++++++++-- examples/render_wgpu/src/main.rs | 17 +++++++++-------- src/platform/macos/view.rs | 20 ++++++++++++++++++++ src/platform/macos/window.rs | 19 ++++++++++++++++++- src/platform/win/window.rs | 18 ++++++++++++++++-- src/platform/x11/event_loop.rs | 8 ++++++++ src/platform/x11/window_shared.rs | 1 - src/platform/x11/window_thread.rs | 12 ++++++++++++ src/platform/x11/xcb_window.rs | 4 ++++ src/window.rs | 23 +++++++++++++++++++++++ src/wrappers/win32/window/handle.rs | 6 +++++- 11 files changed, 128 insertions(+), 15 deletions(-) diff --git a/examples/plugin_clack/src/gui.rs b/examples/plugin_clack/src/gui.rs index d485c9ce..bace4d76 100644 --- a/examples/plugin_clack/src/gui.rs +++ b/examples/plugin_clack/src/gui.rs @@ -114,6 +114,7 @@ impl PluginGuiImpl for ExamplePluginMainThread<'_> { let parent = unsafe { raw_window_handle::WindowHandle::borrow_raw(parent) }; gui.handle.set_parent(&parent)?; + gui.handle.show()?; Ok(()) } @@ -127,11 +128,21 @@ impl PluginGuiImpl for ExamplePluginMainThread<'_> { } fn show(&mut self) -> Result<(), PluginError> { - Ok(()) // Not supported yet + let Some(gui) = &self.gui else { + return Err(PluginError::Message("show called without a GUI active")); + }; + gui.handle.show()?; + + Ok(()) } fn hide(&mut self) -> Result<(), PluginError> { - Ok(()) // Not supported yet + let Some(gui) = &self.gui else { + return Err(PluginError::Message("hide called without a GUI active")); + }; + gui.handle.show()?; + + Ok(()) } } diff --git a/examples/render_wgpu/src/main.rs b/examples/render_wgpu/src/main.rs index b10fad7b..5203a2ee 100644 --- a/examples/render_wgpu/src/main.rs +++ b/examples/render_wgpu/src/main.rs @@ -134,27 +134,28 @@ impl WindowHandler for WgpuExample { let mut surface = self.surface.borrow_mut(); let surface_texture = match surface.get_current_texture() { - wgpu::CurrentSurfaceTexture::Success(texture) => texture, + wgpu::CurrentSurfaceTexture::Success(texture) => Some(texture), wgpu::CurrentSurfaceTexture::Occluded | wgpu::CurrentSurfaceTexture::Timeout => { return Ok(()) } wgpu::CurrentSurfaceTexture::Suboptimal(_) | wgpu::CurrentSurfaceTexture::Outdated => { - surface.configure(&self.device, &self.surface_config.borrow()); - // We'll retry next frame - return Ok(()); + None } wgpu::CurrentSurfaceTexture::Lost => { *surface = self.instance.create_surface(self.window_context.platform_handle())?; - surface.configure(&self.device, &self.surface_config.borrow()); - - // We'll retry next frame - return Ok(()); + None } wgpu::CurrentSurfaceTexture::Validation => { unreachable!("No error scope registered, so validation errors will panic") } }; + let Some(surface_texture) = surface_texture else { + surface.configure(&self.device, &self.surface_config.borrow()); + // We'll retry next frame + return Ok(()); + }; + let view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index 3e297975..c8f2491a 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -154,6 +154,26 @@ impl BaseviewView { Ok((view, state)) } + pub fn show(this: ViewRef) { + let Ok(parent) = this.parenting.try_borrow() else { return }; + + if let ViewParentingType::Windowed { owned_window } = &*parent { + if let Some(window) = owned_window.load() { + window.makeKeyAndOrderFront(None) + } + } + } + + pub fn hide(this: ViewRef) { + let Ok(parent) = this.parenting.try_borrow() else { return }; + + if let ViewParentingType::Windowed { owned_window } = &*parent { + if let Some(window) = owned_window.load() { + window.orderOut(None) + } + } + } + pub fn close(this: ViewRef, from_host: bool) { this.state.closed.set(true); this.view.removeFromSuperview(); diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 055e4063..85e9eb9b 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -93,6 +93,8 @@ impl WindowHandle { let Some(view) = self.view.load() else { return Ok(()) }; let Some(view) = view.inner_ref() else { return Ok(()) }; + BaseviewView::show(view); + let app = NSApplication::sharedApplication(self.mtm); view.lifetime_tied_to_app.set(Some(Weak::from_retained(&app))); @@ -137,6 +139,22 @@ impl WindowHandle { Ok(()) } + + pub fn show(&self) -> Result<()> { + let Some(view) = self.view.load() else { return Ok(()) }; + let Some(view) = view.inner_ref() else { return Ok(()) }; + + BaseviewView::show(view); + Ok(()) + } + + pub fn hide(&self) -> Result<()> { + let Some(view) = self.view.load() else { return Ok(()) }; + let Some(view) = view.inner_ref() else { return Ok(()) }; + + BaseviewView::hide(view); + Ok(()) + } } fn create_window_with_options( @@ -154,7 +172,6 @@ fn create_window_with_options( let title = NSString::from_str(&options.title); window.setTitle(&title); - window.makeKeyAndOrderFront(None); window } diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index a44272f9..f5030e10 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -58,6 +58,8 @@ pub struct WindowHandle { impl WindowHandle { pub fn run_until_closed(self) -> Result<()> { + self.show()?; + run_thread_message_loop_until(|| !self.is_open())?; Ok(()) } @@ -135,6 +137,20 @@ impl WindowHandle { pub fn handle_main_thread_callback(&self) { // No-op } + + pub fn show(&self) -> Result<()> { + let Some(hwnd) = self.hwnd.get() else { return Ok(()) }; + hwnd.show_and_activate(); + + Ok(()) + } + + pub fn hide(&self) -> Result<()> { + let Some(hwnd) = self.hwnd.get() else { return Ok(()) }; + hwnd.hide(); + + Ok(()) + } } impl Drop for WindowHandle { @@ -598,8 +614,6 @@ impl WindowHandle { // TODO: create a new timer instead of hard-coding a specific ID window.set_timer(WIN_FRAME_TIMER, 15)?; - window.show_and_activate(); - Ok(WindowHandle { hwnd: Some(window).into(), state: shared_state }) } } diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index df0f6ea9..14f777b5 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -206,6 +206,14 @@ impl EventLoop { Ok(()) } + WindowThreadRequest::Show => { + self.window.xcb_window.map_window()?.check()?; + Ok(()) + } + WindowThreadRequest::Hide => { + self.window.xcb_window.unmap_window()?.check()?; + Ok(()) + } } } diff --git a/src/platform/x11/window_shared.rs b/src/platform/x11/window_shared.rs index 44cb0fb8..8a2487dc 100644 --- a/src/platform/x11/window_shared.rs +++ b/src/platform/x11/window_shared.rs @@ -96,7 +96,6 @@ impl WindowInner { )?; let cookies = [ - xcb_window.map_window()?, xcb_window.set_title(&options.title)?, xcb_window.enable_wm_protocols()?, xcb_window.enable_dnd_protocols()?, diff --git a/src/platform/x11/window_thread.rs b/src/platform/x11/window_thread.rs index f6e4b785..1f4c7ae5 100644 --- a/src/platform/x11/window_thread.rs +++ b/src/platform/x11/window_thread.rs @@ -70,6 +70,8 @@ pub enum WindowThreadRequest { SuggestScaleFactor(f64), Resize(Size), SetParent(ParentWindowHandle), + Show, + Hide, } pub type WindowThreadResponseMessage = core::result::Result<(), String>; @@ -158,6 +160,8 @@ impl WindowThreadHandle { } pub fn run_until_closed(&self) -> Result<()> { + self.request(WindowThreadRequest::Show)?; + let Some(thread) = self.event_loop_handle.take() else { return Ok(()) }; if let Err(panic) = thread.join() { @@ -171,6 +175,14 @@ impl WindowThreadHandle { Ok(()) } + pub fn show(&self) -> Result<()> { + self.request(WindowThreadRequest::Show) + } + + pub fn hide(&self) -> Result<()> { + self.request(WindowThreadRequest::Hide) + } + pub fn is_open(&self) -> bool { !self.shared.stopped.load(Ordering::Relaxed) } diff --git a/src/platform/x11/xcb_window.rs b/src/platform/x11/xcb_window.rs index 2b40592d..3c23f1f9 100644 --- a/src/platform/x11/xcb_window.rs +++ b/src/platform/x11/xcb_window.rs @@ -65,6 +65,10 @@ impl XcbWindow { Ok(self.connection.conn.map_window(self.window_id.get())?) } + pub fn unmap_window(&self) -> Result, ReplyOrIdError> { + Ok(self.connection.conn.unmap_window(self.window_id.get())?) + } + pub fn resize( &self, size: PhysicalSize, ) -> Result, ConnectionError> { diff --git a/src/window.rs b/src/window.rs index 97d58c5e..6311d590 100644 --- a/src/window.rs +++ b/src/window.rs @@ -17,6 +17,9 @@ impl WindowHandle { Self { window_handle, phantom: PhantomData } } + /// Blocks the thread and runs an event loop until the window is closed. + /// + /// The window is shown automatically if it wasn't already. #[inline] pub fn run_until_closed(self) -> Result<(), Error> { self.window_handle.run_until_closed()?; @@ -92,11 +95,31 @@ impl WindowHandle { self.window_handle.handle_main_thread_callback() } + /// Reparents this window using the given `parent`. + /// + /// If the window was a floating window, it will become parented. #[inline] pub fn set_parent(&self, parent: impl Into) -> Result<(), Error> { self.window_handle.set_parent(parent.into().inner)?; Ok(()) } + + /// Shows the window to the screen. + #[inline] + pub fn show(&self) -> Result<(), Error> { + self.window_handle.show()?; + Ok(()) + } + + /// Hides the window from the screen. + /// + /// The window will still exist, and it might still receive some events, but rendering will be + /// paused and the user will not be able to see or interact with it. + #[inline] + pub fn hide(&self) -> Result<(), Error> { + self.window_handle.hide()?; + Ok(()) + } } #[inline] diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index ce94ae91..dbae8918 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -16,7 +16,7 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ use windows_sys::Win32::UI::WindowsAndMessaging::{ DestroyWindow, GetWindowLongPtrW, GetWindowLongW, SetParent, SetTimer, SetWindowLongPtrW, SetWindowLongW, SetWindowPos, ShowWindow, GWLP_USERDATA, GWL_EXSTYLE, GWL_STYLE, - SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, SW_SHOW, WINDOW_LONG_PTR_INDEX, + SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, SW_HIDE, SW_SHOW, WINDOW_LONG_PTR_INDEX, }; /// A simple wrapper around a HWND. @@ -193,6 +193,10 @@ impl HWnd { result != 0 } + pub fn hide(&self) { + unsafe { ShowWindow(self.as_raw(), SW_HIDE) }; + } + pub fn set_nc_rect(&self, nc_rect: Rect) -> Result<()> { let size = nc_rect.size();