mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-13 01:14:21 -06:00
Merge pull request #3650 from insideTheFlux/fix/3381-title-bar
LR-BEHAV-TITLEBAR: the Windows title bar takes LightCraft's theme at launch (#3381)
This commit is contained in:
@@ -265,6 +265,7 @@ impl LightcraftApp {
|
||||
self.import_dropped_files(ctx);
|
||||
}
|
||||
self.check_external_editor(ctx);
|
||||
theme::refresh_window_on_first_focus(ctx);
|
||||
// Alt (Option on macOS) while a slider is dragged shows its temporary preview
|
||||
self.ui.alt_held = ctx.input(|i| i.modifiers.alt);
|
||||
// Recent-library checks and launch-list keys read focus/time/keys: they belong to the UI
|
||||
|
||||
@@ -412,6 +412,25 @@ pub fn current(ctx: &egui::Context) -> Option<ThemeKind> {
|
||||
ctx.data(|d| d.get_temp::<ThemeKind>(theme_id()))
|
||||
}
|
||||
|
||||
/// Windows only: tell the window its theme again, the first time it has focus. A first theme that
|
||||
/// reaches a focused window as it comes up changes the title's colour but not the caption's, so
|
||||
/// with LightCraft dark on a light system the title is white on a light caption (it looks blank),
|
||||
/// and light on dark is dark on dark, until focus next moves (#3381). The same command repaints
|
||||
/// the caption once the window is up. A window that opens without focus shows the right colours
|
||||
/// from the start.
|
||||
pub fn refresh_window_on_first_focus(ctx: &egui::Context) {
|
||||
let done = egui::Id::unique("lightcraft-window-theme-refreshed");
|
||||
if !cfg!(target_os = "windows") || !ctx.input(|i| i.focused) || ctx.data(|d| d.get_temp::<bool>(done)).is_some() {
|
||||
return;
|
||||
}
|
||||
ctx.data_mut(|d| d.insert_temp(done, true));
|
||||
let theme = match ctx.theme() {
|
||||
egui::Theme::Dark => egui::SystemTheme::Dark,
|
||||
egui::Theme::Light => egui::SystemTheme::Light,
|
||||
};
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::SetTheme(theme));
|
||||
}
|
||||
|
||||
/// Style `ctx` with the default theme.
|
||||
pub fn apply(ctx: &egui::Context) {
|
||||
apply_kind(ctx, ThemeKind::default());
|
||||
@@ -506,6 +525,51 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `SetTheme(Light)` commands one frame of `ctx` sends the window, with or without
|
||||
/// [`refresh_window_on_first_focus`] in it.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn light_theme_commands(ctx: &egui::Context, focused: bool, refresh: bool) -> usize {
|
||||
let raw = egui::RawInput { focused, ..Default::default() };
|
||||
let mut out = ctx.run_ui(raw, |ui| {
|
||||
if refresh {
|
||||
refresh_window_on_first_focus(ui.ctx());
|
||||
}
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
let commands = out.viewport_output.remove(&egui::ViewportId::ROOT).map(|v| v.commands).unwrap_or_default();
|
||||
commands.iter().filter(|c| matches!(c, egui::ViewportCommand::SetTheme(egui::SystemTheme::Light))).count()
|
||||
}
|
||||
|
||||
/// #3381: egui sends the theme with the first frame, and the window hears it once more the
|
||||
/// first time it has focus, not on every later focus.
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn the_window_hears_its_theme_again_the_first_time_it_has_focus() {
|
||||
let ctx = egui::Context::default();
|
||||
apply_kind(&ctx, ThemeKind::Silver);
|
||||
let sent = |focused: bool| light_theme_commands(&ctx, focused, true);
|
||||
assert_eq!(sent(false), 1, "egui's own, with the first frame");
|
||||
assert_eq!(sent(false), 0, "no focus yet");
|
||||
assert_eq!(sent(true), 1, "the first frame with focus");
|
||||
assert_eq!(sent(true), 0, "still focused");
|
||||
assert_eq!(sent(false), 0, "focus lost");
|
||||
assert_eq!(sent(true), 0, "focus back: Windows repaints this by itself");
|
||||
}
|
||||
|
||||
/// #3381: a window that has focus from its very first frame hears the theme once more in that
|
||||
/// frame, on top of egui's own command. (In the app the first frame never gets here: `ui`
|
||||
/// returns before the fonts are ready, so egui's command always goes a frame earlier.)
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn a_window_focused_from_its_first_frame_hears_its_theme_again_in_that_frame() {
|
||||
let first_frame = |refresh: bool| {
|
||||
let ctx = egui::Context::default();
|
||||
apply_kind(&ctx, ThemeKind::Silver);
|
||||
light_theme_commands(&ctx, true, refresh)
|
||||
};
|
||||
assert_eq!(first_frame(true), first_frame(false) + 1);
|
||||
}
|
||||
|
||||
/// Every theme scrolls 100 points a wheel notch, and a zoom notch stays egui's stock ~1.22x
|
||||
/// (`exp(40 / 200)`) instead of growing with the longer scroll.
|
||||
#[test]
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
hot pixels on a sensor. Stars and other points of light that are kept don't count, so a dense star field still
|
||||
loses its hot pixels. Settings ▸ Performance ▸ Hot pixel limit sets the share (0.05 % to 1 %).
|
||||
|
||||
## 2026-11-02
|
||||
|
||||
### The Windows title bar is readable from the moment the window opens
|
||||
- When LightCraft's appearance differs from the one set in Windows (the dark default on a light Windows, for one), the
|
||||
title bar kept the system's colour with the title in LightCraft's, so the title looked blank until focus moved to
|
||||
another window. It now takes LightCraft's colours as soon as the window has focus.
|
||||
|
||||
## 2026-11-01
|
||||
|
||||
### Korean and Vietnamese text in exports draws whole
|
||||
|
||||
Reference in New Issue
Block a user