163 lines
7.2 KiB
Rust
163 lines
7.2 KiB
Rust
//! Border menu and dialog behavior through real egui controls.
|
|
|
|
use egui::{Color32, Shape, vec2};
|
|
use egui_kittest::kittest::Queryable;
|
|
use gridcraft_engine::Session;
|
|
use gridcraft_engine::core::{CellRef, Value};
|
|
use gridcraft_engine::model::{BorderLine, BorderStyle, Borders, Color};
|
|
use gridcraft_ui_egui::SheetApp;
|
|
use serde_json::json;
|
|
|
|
fn harness() -> egui_kittest::Harness<'static, SheetApp> {
|
|
let mut session = Session::new();
|
|
session.new_workbook();
|
|
session.run("range.setValues", json!({"range": "A1", "values": [[1, 2], [3, 4]]})).unwrap();
|
|
session.run("selection.set", json!({"range": "A1:B2"})).unwrap();
|
|
let mut h = egui_kittest::Harness::builder().with_size(vec2(1200.0, 800.0)).build_ui_state(
|
|
|ui, app: &mut SheetApp| {
|
|
app.logic(&ui.ctx().clone());
|
|
app.ui(ui);
|
|
},
|
|
SheetApp::new(session, Default::default()),
|
|
);
|
|
h.run_steps(6);
|
|
h
|
|
}
|
|
|
|
fn borders(h: &egui_kittest::Harness<'static, SheetApp>, cell: &str) -> Borders {
|
|
let doc = h.state().session.active().unwrap();
|
|
let sheet = doc.wb.active().unwrap();
|
|
doc.wb.styles.get(sheet.style_id(CellRef::parse(cell).unwrap())).borders
|
|
}
|
|
|
|
#[test]
|
|
fn diagram_menu_applies_left_right_inside_and_outside_then_undoes() {
|
|
for label in ["Left Border", "Right Border", "Inside Borders", "Outside Borders"] {
|
|
let mut h = harness();
|
|
h.get_by_label("Border presets").click();
|
|
h.run_steps(6);
|
|
h.get_by_label(label).click();
|
|
h.run_steps(6);
|
|
let a = borders(&h, "A1");
|
|
let b = borders(&h, "B2");
|
|
match label {
|
|
"Left Border" => {
|
|
assert_eq!(a.left.style, BorderStyle::Thin);
|
|
assert!(a.right.is_none() && b.left.is_none() && b.right.is_none());
|
|
}
|
|
"Right Border" => {
|
|
assert_eq!(b.right.style, BorderStyle::Thin);
|
|
assert!(a.left.is_none() && a.right.is_none() && b.left.is_none());
|
|
}
|
|
"Inside Borders" => {
|
|
assert_eq!(a.right.style, BorderStyle::Thin);
|
|
assert_eq!(a.bottom.style, BorderStyle::Thin);
|
|
assert_eq!(b.left.style, BorderStyle::Thin);
|
|
assert_eq!(b.top.style, BorderStyle::Thin);
|
|
assert!(a.left.is_none() && a.top.is_none() && b.right.is_none() && b.bottom.is_none());
|
|
}
|
|
_ => {
|
|
assert_eq!(a.left.style, BorderStyle::Thin);
|
|
assert_eq!(a.top.style, BorderStyle::Thin);
|
|
assert_eq!(b.right.style, BorderStyle::Thin);
|
|
assert_eq!(b.bottom.style, BorderStyle::Thin);
|
|
assert!(a.right.is_none() && a.bottom.is_none() && b.left.is_none() && b.top.is_none());
|
|
}
|
|
}
|
|
let sheet = h.state().session.active().unwrap().wb.active().unwrap();
|
|
assert_eq!(sheet.value(CellRef::new(0, 0)), Value::Number(1.0));
|
|
assert_eq!(sheet.value(CellRef::new(1, 1)), Value::Number(4.0));
|
|
h.state_mut().run("edit.undo", json!({})).unwrap();
|
|
assert_eq!(borders(&h, "A1"), Borders::default());
|
|
assert_eq!(borders(&h, "B2"), Borders::default());
|
|
}
|
|
}
|
|
|
|
fn colored_borders() -> Borders {
|
|
let line = |color| BorderLine { style: BorderStyle::Thin, color: Color::Rgb(color) };
|
|
Borders {
|
|
top: line(0xab192d),
|
|
bottom: line(0x174faa),
|
|
left: line(0x16864d),
|
|
right: line(0x985213),
|
|
diag_down: line(0x7c2ba2),
|
|
diag_up: line(0x248088),
|
|
}
|
|
}
|
|
|
|
fn preview_lines(h: &egui_kittest::Harness<'static, SheetApp>, color: Color32) -> usize {
|
|
let preview = h.get_by_label("Border preview").rect();
|
|
h.output()
|
|
.shapes
|
|
.iter()
|
|
.filter(|shape| match &shape.shape {
|
|
Shape::LineSegment { points, stroke } => stroke.color == color && points.iter().all(|p| preview.contains(*p)),
|
|
_ => false,
|
|
})
|
|
.count()
|
|
}
|
|
|
|
#[test]
|
|
fn border_preview_uses_the_colored_draft_until_ok_and_cancel_discards_it() {
|
|
let mut h = harness();
|
|
let original = colored_borders();
|
|
h.state_mut().run("home.formatCells", json!({"style": {"borders": original}})).unwrap();
|
|
h.state_mut().open_dialog("formatCells", json!({"tab": "Border"}));
|
|
h.run_steps(6);
|
|
for line in [original.top, original.bottom, original.left, original.right, original.diag_down, original.diag_up] {
|
|
let [r, g, b] = line.color.resolve(&Default::default()).unwrap();
|
|
assert_eq!(preview_lines(&h, Color32::from_rgb(r, g, b)), 1);
|
|
}
|
|
h.get_by_label("Top double").click();
|
|
h.run_steps(6);
|
|
assert_eq!(borders(&h, "A1"), original);
|
|
assert_eq!(preview_lines(&h, Color32::from_rgb(0xab, 0x19, 0x2d)), 2);
|
|
h.get_by_label(" Cancel ").click();
|
|
h.run_steps(6);
|
|
assert!(h.state().dialog.is_none());
|
|
assert_eq!(borders(&h, "A1"), original);
|
|
h.state_mut().open_dialog("formatCells", json!({"tab": "Border"}));
|
|
h.run_steps(6);
|
|
h.get_by_label("Top double").click();
|
|
h.run_steps(6);
|
|
h.get_by_label(" OK ").click();
|
|
h.run_steps(6);
|
|
assert!(h.state().dialog.is_none());
|
|
assert_eq!(borders(&h, "A1").top.style, BorderStyle::Double);
|
|
h.state_mut().run("edit.undo", json!({})).unwrap();
|
|
assert_eq!(borders(&h, "A1"), original);
|
|
}
|
|
|
|
/// #376: a merged range's outline lives on the cells along each edge (the right edge on the last
|
|
/// column's cells, the bottom on the last row's), not on the anchor alone. All four sides are drawn.
|
|
#[test]
|
|
fn merged_range_draws_the_outline_from_its_edge_cells() {
|
|
let mut h = harness();
|
|
h.state_mut().run("home.borders", json!({"range": "B2:E5", "preset": "thickOutside", "color": "#C81E1E"})).unwrap();
|
|
h.state_mut().run("home.mergeCells", json!({"range": "B2:E5"})).unwrap();
|
|
h.state_mut().run("selection.set", json!({"range": "H10"})).unwrap();
|
|
h.run_steps(6);
|
|
assert!(borders(&h, "B2").right.is_none(), "the anchor carries only the top and left edges");
|
|
let red = Color32::from_rgb(0xC8, 0x1E, 0x1E);
|
|
let segs: Vec<[egui::Pos2; 2]> = h
|
|
.output()
|
|
.shapes
|
|
.iter()
|
|
.filter_map(|shape| match &shape.shape {
|
|
Shape::LineSegment { points, stroke } if stroke.color == red => Some(*points),
|
|
_ => None,
|
|
})
|
|
.collect();
|
|
let vertical = |s: &[egui::Pos2; 2]| (s[0].x - s[1].x).abs() < 0.5;
|
|
let xs: Vec<f32> = segs.iter().filter(|s| vertical(s)).map(|s| s[0].x).collect();
|
|
let ys: Vec<f32> = segs.iter().filter(|s| !vertical(s)).map(|s| s[0].y).collect();
|
|
let (min_x, max_x) = xs.iter().fold((f32::MAX, f32::MIN), |(a, b), x| (a.min(*x), b.max(*x)));
|
|
let (min_y, max_y) = ys.iter().fold((f32::MAX, f32::MIN), |(a, b), y| (a.min(*y), b.max(*y)));
|
|
// Left and right edges (four rows each), top and bottom edges (four columns each).
|
|
assert_eq!(xs.iter().filter(|x| (**x - min_x).abs() < 0.5).count(), 4, "left edge");
|
|
assert_eq!(xs.iter().filter(|x| (**x - max_x).abs() < 0.5).count(), 4, "right edge");
|
|
assert_eq!(ys.iter().filter(|y| (**y - min_y).abs() < 0.5).count(), 4, "top edge");
|
|
assert_eq!(ys.iter().filter(|y| (**y - max_y).abs() < 0.5).count(), 4, "bottom edge");
|
|
assert!(max_x - min_x > 100.0 && max_y - min_y > 40.0, "the outline spans the whole merged area");
|
|
}
|