Implemented window header color change when focused (on top). (#3515)
This PR replaces an old one with many problems (no collapse icon, the header background was not funny colored, ugly...). It fixes those problems. It implements the highlight of the header to the focused window. And allows the rest of the application to know the selected window. It allows, for instance, to display multiple windows with images and some additional meta information about those images on the side panel of the main window. The side panel updates itself according to the selected image window. * Added a theme color for the selected window header. * Added a function to retrieve the LayerId of the focused window. * Implemented a simple demo of this function in the demo app where a message states if the Option window is focused (at the bottom of the Options window).  A technical point: The header color is applied with a transparency of 125 so the collapsible button becomes visible. The reason is that the collapsible button is rendered before the rest of the header and before the header size is known. We cannot draw the background before knowing this value, so rendering with transparency is a solution to see the collapsible button through the header background. This PR has been sponsored by my company, which left me to do it during my work time. This is part of an evil plan to convince them to switch to rust for new projects :) --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
4a6999bbad
commit
1777bb7789
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<LayerId> {
|
||||
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());
|
||||
|
|
|
|||
|
|
@ -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<LayerId> {
|
||||
self.order
|
||||
.iter()
|
||||
.filter(|layer| layer.order == order)
|
||||
.last()
|
||||
.copied()
|
||||
}
|
||||
|
||||
pub(crate) fn end_frame(&mut self) {
|
||||
let Self {
|
||||
visible_last_frame,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue