diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 4182af4e8c3..6383125bb14 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -405,10 +405,19 @@ impl<'open> Window<'open> { let resize = resize.resizable(false); // We move it manually let mut resize = resize.id(resize_id); + let on_top = Some(area_layer_id) == ctx.top_layer_id(); let mut area = area.begin(ctx); let title_content_spacing = 2.0 * ctx.style().spacing.item_spacing.y; + // Calculate roughly how much larger the window size is compared to the inner rect + let title_bar_height = if with_title_bar { + let style = ctx.style(); + ctx.fonts(|f| title.font_height(f, &style)) + title_content_spacing * 2.0 + } else { + 0.0 + }; + // First interact (move etc) to avoid frame delay: let last_frame_outer_rect = area.state().rect(); let interaction = if possible.movable || possible.resizable() { @@ -420,13 +429,6 @@ impl<'open> Window<'open> { last_frame_outer_rect, ) .and_then(|window_interaction| { - // Calculate roughly how much larger the window size is compared to the inner rect - let title_bar_height = if with_title_bar { - let style = ctx.style(); - ctx.fonts(|f| title.font_height(f, &style)) + title_content_spacing - } else { - 0.0 - }; let margins = frame.outer_margin.sum() + frame.inner_margin.sum() + vec2(0.0, title_bar_height); @@ -453,6 +455,9 @@ impl<'open> Window<'open> { let mut frame = frame.begin(&mut area_content_ui); let show_close_button = open.is_some(); + + let where_to_put_header_background = &area_content_ui.painter().add(Shape::Noop); + let title_bar = if with_title_bar { let title_bar = show_title_bar( &mut frame.content_ui, @@ -489,6 +494,27 @@ impl<'open> Window<'open> { // END FRAME -------------------------------- if let Some(title_bar) = title_bar { + if on_top { + let rect = Rect::from_min_size( + outer_rect.min, + Vec2 { + x: outer_rect.size().x, + y: title_bar_height, + }, + ); + let mut round = area_content_ui.visuals().window_rounding; + if !is_collapsed { + round.se = 0.0; + round.sw = 0.0; + } + let header_color = area_content_ui.visuals().widgets.hovered.bg_fill; + + area_content_ui.painter().set( + *where_to_put_header_background, + RectShape::filled(rect, round, header_color), + ); + }; + title_bar.ui( &mut area_content_ui, outer_rect, diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index b1b96ebc250..b242144f130 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1982,6 +1982,11 @@ impl Context { self.memory_mut(|mem| mem.areas_mut().move_to_top(layer_id)); } + /// Retrieve the [`LayerId`] of the top level windows. + pub fn top_layer_id(&self) -> Option { + self.memory(|mem| mem.areas().top_layer_id(Order::Middle)) + } + pub(crate) fn rect_contains_pointer(&self, layer_id: LayerId, rect: Rect) -> bool { rect.is_positive() && { let pointer_pos = self.input(|i| i.pointer.interact_pos()); diff --git a/crates/egui/src/memory.rs b/crates/egui/src/memory.rs index 8aca0d2d7df..86e2164d5b7 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -1,11 +1,10 @@ #![warn(missing_docs)] // Let's keep this file well-documented.` to memory.rs -use epaint::{emath::Rangef, vec2, Vec2}; - use crate::{ - area, + area, vec2, window::{self, WindowInteraction}, - EventFilter, Id, IdMap, LayerId, Pos2, Rect, Style, ViewportId, ViewportIdMap, ViewportIdSet, + EventFilter, Id, IdMap, LayerId, Order, Pos2, Rangef, Rect, Style, Vec2, ViewportId, + ViewportIdMap, ViewportIdSet, }; // ---------------------------------------------------------------------------- @@ -908,6 +907,14 @@ impl Areas { } } + pub fn top_layer_id(&self, order: Order) -> Option { + self.order + .iter() + .filter(|layer| layer.order == order) + .last() + .copied() + } + pub(crate) fn end_frame(&mut self) { let Self { visible_last_frame, diff --git a/crates/egui_demo_lib/src/demo/window_options.rs b/crates/egui_demo_lib/src/demo/window_options.rs index 228047263e1..baa6eac652e 100644 --- a/crates/egui_demo_lib/src/demo/window_options.rs +++ b/crates/egui_demo_lib/src/demo/window_options.rs @@ -138,7 +138,10 @@ impl super::View for WindowOptions { }); ui.separator(); + let on_top = Some(ui.layer_id()) == ui.ctx().top_layer_id(); + ui.label(format!("This window is on top: {on_top}.")); + ui.separator(); ui.horizontal(|ui| { if ui.button("Disable for 2 seconds").clicked() { self.disabled_time = ui.input(|i| i.time);