mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Theme: the field test only counts a fill or outline that is actually painted (#1204)
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
use deckcraft_engine::Session;
|
||||
use egui::accesskit::Role;
|
||||
use egui::epaint::Shape;
|
||||
use egui::{Color32, Rect};
|
||||
use egui::{Color32, Rect, Stroke};
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
use serde_json::json;
|
||||
@@ -31,7 +31,7 @@ fn distance(a: Color32, b: Color32) -> u8 {
|
||||
}
|
||||
|
||||
/// The frame egui painted for the text field at `field` (its fill and outline).
|
||||
fn frame_at(h: &Harness<'_, SlideApp>, field: Rect) -> (Color32, egui::Stroke) {
|
||||
fn frame_at(h: &Harness<'_, SlideApp>, field: Rect) -> (Color32, Stroke) {
|
||||
h.output()
|
||||
.shapes
|
||||
.iter()
|
||||
@@ -42,20 +42,49 @@ fn frame_at(h: &Harness<'_, SlideApp>, field: Rect) -> (Color32, egui::Stroke) {
|
||||
.unwrap_or_else(|| panic!("no frame painted for the field at {field:?}"))
|
||||
}
|
||||
|
||||
/// What `c` shows when painted over the opaque `surface`. `Color32` is premultiplied, so each
|
||||
/// channel is `c + surface * (255 - alpha) / 255`.
|
||||
fn over(c: Color32, surface: Color32) -> Color32 {
|
||||
let behind = |s: u8| (u16::from(s) * u16::from(255 - c.a()) / 255) as u8;
|
||||
Color32::from_rgb(c.r().saturating_add(behind(surface.r())), c.g().saturating_add(behind(surface.g())), c.b().saturating_add(behind(surface.b())))
|
||||
}
|
||||
|
||||
/// A frame with this `fill` and `stroke` stands apart from the opaque `surface`: it paints a fill
|
||||
/// or an outline (at least 1 px wide) that, as actually shown over the surface, differs from it.
|
||||
fn stands_apart(fill: Color32, stroke: Stroke, surface: Color32) -> bool {
|
||||
let outlined = stroke.width >= 1.0 && distance(over(stroke.color, surface), surface) >= 24;
|
||||
outlined || distance(over(fill, surface), surface) >= 16
|
||||
}
|
||||
|
||||
/// The field stands apart from `surface` by its fill or by its outline.
|
||||
fn assert_visible(h: &Harness<'_, SlideApp>, what: &str, surface: Color32) {
|
||||
let fields: Vec<Rect> = h.query_all_by_role(Role::TextInput).map(|n| n.rect()).collect();
|
||||
assert!(!fields.is_empty(), "{what}: no text field");
|
||||
for field in fields {
|
||||
let (fill, stroke) = frame_at(h, field);
|
||||
let outlined = stroke.width >= 1.0 && distance(stroke.color, surface) >= 24;
|
||||
assert!(
|
||||
outlined || distance(fill, surface) >= 16,
|
||||
stands_apart(fill, stroke, surface),
|
||||
"{what}: the field at {field:?} (fill {fill:?}, outline {stroke:?}) can't be told from the {surface:?} behind it"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_painted_fill_or_outline_stands_apart() {
|
||||
let surface = Color32::WHITE;
|
||||
// Stroke::NONE is transparent black: nothing is painted, whatever its RGB.
|
||||
assert!(!stands_apart(surface, Stroke::NONE, surface));
|
||||
assert!(!stands_apart(Color32::WHITE, Stroke::NONE, Color32::from_rgb(250, 250, 250)));
|
||||
// A fully transparent fill shows the surface.
|
||||
assert!(!stands_apart(Color32::TRANSPARENT, Stroke::NONE, surface));
|
||||
assert!(!stands_apart(Color32::TRANSPARENT, Stroke::new(0.5, Color32::BLACK), surface));
|
||||
// A transparent outline paints nothing either.
|
||||
assert!(!stands_apart(surface, Stroke::new(1.0, Color32::TRANSPARENT), surface));
|
||||
// A real 1 px dark outline, or a clearly different opaque fill, does.
|
||||
assert!(stands_apart(surface, Stroke::new(1.0, Color32::from_gray(120)), surface));
|
||||
assert!(stands_apart(Color32::from_gray(200), Stroke::NONE, surface));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_text_fields_show_in_dialogs_panes_and_the_ribbon() {
|
||||
let light = theme::Tokens::light();
|
||||
@@ -71,7 +100,9 @@ fn light_text_fields_show_in_dialogs_panes_and_the_ribbon() {
|
||||
h.run_steps(2);
|
||||
assert!(h.ctx.memory(|m| m.focused().is_some()), "the click puts the keyboard in the field");
|
||||
let (fill, stroke) = frame_at(&h, field);
|
||||
assert!(distance(fill, light.card) >= 16 || distance(stroke.color, light.card) >= 24, "focused field: fill {fill:?}, outline {stroke:?}");
|
||||
// Same surface as the dialog check above: the field sits on the dialog's window fill.
|
||||
let surface = h.ctx.global_style().visuals.window_fill;
|
||||
assert!(stands_apart(fill, stroke, surface), "focused field: fill {fill:?}, outline {stroke:?}");
|
||||
|
||||
// Review › Thesaurus: the search box on the task pane.
|
||||
let mut h = harness(SlideApp::new(Session::with_new(), Services::default()));
|
||||
|
||||
Reference in New Issue
Block a user