use galaxy_core::ui::color::coloru_with_opacity; use galaxyui::elements::{ ConstrainedBox, Element, Fill, Hoverable, MouseStateHandle, ParentElement, Rect, Stack, }; use galaxyui::windowing::WindowManager; use galaxyui::{AppContext, SingletonEntity, WindowId}; use crate::window_settings::WindowSettings; use crate::workspace::panel_header_corner_radius; /// Opacity level for dimming the header of unfocused windows. /// 0 means no dimming, 100 means 100% cover the top bar. const UNFOCUSED_WINDOW_DIMMING_OPACITY: crate::util::color::Opacity = 45; /// Utility functions for applying consistent window focus dimming across all UI components. pub struct WindowFocusDimming; impl WindowFocusDimming { /// Returns true if the specified window is currently focused. /// /// On mobile WASM, this always returns true because mobile browsers don't have /// the traditional concept of "unfocused windows", and focus events can be /// unreliable due to soft keyboard management. pub fn is_window_focused(window_id: WindowId, ctx: &AppContext) -> bool { #[cfg(target_family = "wasm")] if galaxyui::platform::wasm::is_mobile_device() { return true; } let window_manager = WindowManager::as_ref(ctx); if !window_manager.app_is_active() { return false; } window_manager.active_window() == Some(window_id) } /// Applies dimming overlay for headers and top bar areas. /// Takes height and background color parameters for maximum flexibility. pub fn apply_panel_header_dimming( element: Box, mouse_state: MouseStateHandle, height: f32, background_color: galaxyui::color::ColorU, window_id: WindowId, ctx: &AppContext, ) -> Box { if !Self::is_window_focused(window_id, ctx) { let background_opacity = WindowSettings::as_ref(ctx) .background_opacity .effective_opacity(window_id, ctx); let scaled_opacity = (UNFOCUSED_WINDOW_DIMMING_OPACITY as f32 * background_opacity as f32 / 100.) as u8; let mut stack = Stack::new().with_child(element); let dimming_overlay = Rect::new() .with_background(Fill::Solid(coloru_with_opacity( background_color, scaled_opacity, ))) .with_corner_radius(panel_header_corner_radius()) .finish(); stack.add_child( ConstrainedBox::new(dimming_overlay) .with_height(height) .finish(), ); // Wrap the dimmed content in a hoverable that can clear dimming // if the window becomes active during hover (failsafe mechanism) Hoverable::new(mouse_state, |_| stack.finish()) .on_hover(move |is_hovered, ctx, app, _position| { if is_hovered { // Double-check if window became active during hover // If so, trigger a re-render to clear the dimming if Self::is_window_focused(window_id, app) { ctx.notify(); } } }) .finish() } else { element } } }