Files
galaxy/app/src/ui_components/window_focus_dimming.rs
T

86 lines
3.3 KiB
Rust

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<dyn Element>,
mouse_state: MouseStateHandle,
height: f32,
background_color: galaxyui::color::ColorU,
window_id: WindowId,
ctx: &AppContext,
) -> Box<dyn Element> {
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
}
}
}