Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers. - Introduce `preview.rs` for geometry handling of dock drop previews. - Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels. - Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
//! - undo_after_rect_golden: validates history snapshot and restoration
|
||||
|
||||
use hcie_engine_api::Engine;
|
||||
use hcie_engine_api::{BrushTip, BrushStyle, VectorShape};
|
||||
use hcie_engine_api::{BrushStyle, BrushTip, VectorShape};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
/// Compute SHA-256 hash of pixel buffer for golden comparison.
|
||||
@@ -46,7 +46,10 @@ fn white_canvas_empty_document() {
|
||||
for i in (0..pixels.len()).step_by(4) {
|
||||
assert_eq!(pixels[i + 3], 0, "Alpha channel should be 0 (transparent)");
|
||||
}
|
||||
assert_eq!(hash_pixels(&pixels), "5341e6b2646979a70e57653007a1f310169421ec9bdd9f1a5648f75ade005af1");
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"5341e6b2646979a70e57653007a1f310169421ec9bdd9f1a5648f75ade005af1"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Green rectangle draw and composite.
|
||||
@@ -64,7 +67,10 @@ fn green_rect_draw_and_composite() {
|
||||
assert_eq!(pixels[center_idx + 1], 255);
|
||||
assert_eq!(pixels[center_idx + 2], 0);
|
||||
assert_eq!(pixels[center_idx + 3], 255);
|
||||
assert_eq!(hash_pixels(&pixels), "01b9681b08e6d9bafd568f110f0656613c7447c667fda4af9350d705dadc758a");
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"01b9681b08e6d9bafd568f110f0656613c7447c667fda4af9350d705dadc758a"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Red rectangle over green rectangle.
|
||||
@@ -91,7 +97,10 @@ fn red_rect_over_green_rect() {
|
||||
assert_eq!(pixels[center_idx], 255);
|
||||
assert_eq!(pixels[center_idx + 1], 0);
|
||||
assert_eq!(pixels[center_idx + 2], 0);
|
||||
assert_eq!(hash_pixels(&pixels), "c893ae44fb82ff080e286a6625389fef843ac60e82cdb4d7dde8c7975bbae750");
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"c893ae44fb82ff080e286a6625389fef843ac60e82cdb4d7dde8c7975bbae750"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Vector shape rendering.
|
||||
@@ -100,7 +109,10 @@ fn red_rect_over_green_rect() {
|
||||
fn vector_rect_golden() {
|
||||
let mut engine = Engine::new(16, 16);
|
||||
let shape = VectorShape::Rect {
|
||||
x1: 0.0, y1: 0.0, x2: 16.0, y2: 16.0,
|
||||
x1: 0.0,
|
||||
y1: 0.0,
|
||||
x2: 16.0,
|
||||
y2: 16.0,
|
||||
stroke: 1.0,
|
||||
color: [255, 0, 0, 255],
|
||||
fill_color: [255, 0, 0, 255],
|
||||
@@ -116,9 +128,14 @@ fn vector_rect_golden() {
|
||||
assert_eq!(pixels.len(), 16 * 16 * 4);
|
||||
|
||||
// Verify at least one red pixel (vector rect was drawn)
|
||||
let has_red = pixels.chunks(4).any(|p| p[0] == 255 && p[1] == 0 && p[2] == 0);
|
||||
let has_red = pixels
|
||||
.chunks(4)
|
||||
.any(|p| p[0] == 255 && p[1] == 0 && p[2] == 0);
|
||||
assert!(has_red, "Vector rect should contain red pixels");
|
||||
assert_eq!(hash_pixels(&pixels), "71205eb7a329a3ead670c77eee185c0fbeb612f7a2b3d6aadbe2af4f9276b60d");
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"71205eb7a329a3ead670c77eee185c0fbeb612f7a2b3d6aadbe2af4f9276b60d"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Brush stroke rendering.
|
||||
@@ -144,8 +161,14 @@ fn brush_stroke_golden() {
|
||||
|
||||
// Brush stroke should produce non-white pixels
|
||||
let has_non_white = pixels.iter().any(|&p| p < 255);
|
||||
assert!(has_non_white, "Brush stroke should produce non-white pixels");
|
||||
assert_eq!(hash_pixels(&pixels), "e97f0dd89644a40cd4d16b7440576265761849c45180fd4dece66b4f709fa087");
|
||||
assert!(
|
||||
has_non_white,
|
||||
"Brush stroke should produce non-white pixels"
|
||||
);
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"e97f0dd89644a40cd4d16b7440576265761849c45180fd4dece66b4f709fa087"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Invert filter.
|
||||
@@ -164,7 +187,10 @@ fn invert_filter_golden() {
|
||||
assert_eq!(pixels[idx], 155); // 255 - 100
|
||||
assert_eq!(pixels[idx + 1], 105); // 255 - 150
|
||||
assert_eq!(pixels[idx + 2], 55); // 255 - 200
|
||||
assert_eq!(hash_pixels(&pixels), "35490247d911e119aeae86afa584459b47b853262afde75f832521a738bb069f");
|
||||
assert_eq!(
|
||||
hash_pixels(&pixels),
|
||||
"35490247d911e119aeae86afa584459b47b853262afde75f832521a738bb069f"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test: Undo after rectangle.
|
||||
@@ -196,7 +222,10 @@ fn vector_fill_toggle_and_delete() {
|
||||
|
||||
// Create a rect with fill=false
|
||||
let shape = VectorShape::Rect {
|
||||
x1: 2.0, y1: 2.0, x2: 14.0, y2: 14.0,
|
||||
x1: 2.0,
|
||||
y1: 2.0,
|
||||
x2: 14.0,
|
||||
y2: 14.0,
|
||||
stroke: 1.0,
|
||||
color: [255, 0, 0, 255],
|
||||
fill_color: [0, 0, 255, 255],
|
||||
@@ -211,25 +240,39 @@ fn vector_fill_toggle_and_delete() {
|
||||
|
||||
// Verify fill is initially false
|
||||
let shapes = engine.active_vector_shapes().unwrap();
|
||||
assert_eq!(shapes[0].fill(), Some(false), "fill should be false initially");
|
||||
assert_eq!(
|
||||
shapes[0].fill(),
|
||||
Some(false),
|
||||
"fill should be false initially"
|
||||
);
|
||||
|
||||
// Toggle fill ON
|
||||
engine.set_vector_shape_fill(layer_id, 0, true);
|
||||
let shapes = engine.active_vector_shapes().unwrap();
|
||||
assert_eq!(shapes[0].fill(), Some(true), "fill should be true after toggle");
|
||||
assert_eq!(
|
||||
shapes[0].fill(),
|
||||
Some(true),
|
||||
"fill should be true after toggle"
|
||||
);
|
||||
|
||||
// Verify composite pixels contain blue fill
|
||||
let pixels = engine.get_composite_pixels();
|
||||
let has_blue = pixels.chunks(4).any(|p| p[2] == 255 && p[0] == 0 && p[1] == 0 && p[3] > 0);
|
||||
let has_blue = pixels
|
||||
.chunks(4)
|
||||
.any(|p| p[2] == 255 && p[0] == 0 && p[1] == 0 && p[3] > 0);
|
||||
assert!(has_blue, "should have blue fill pixels after enabling fill");
|
||||
|
||||
// Toggle fill OFF
|
||||
engine.set_vector_shape_fill(layer_id, 0, false);
|
||||
let shapes = engine.active_vector_shapes().unwrap();
|
||||
assert_eq!(shapes[0].fill(), Some(false), "fill should be false after toggling off");
|
||||
assert_eq!(
|
||||
shapes[0].fill(),
|
||||
Some(false),
|
||||
"fill should be false after toggling off"
|
||||
);
|
||||
|
||||
// Delete the shape
|
||||
engine.delete_vector_shape(layer_id, 0);
|
||||
let shapes = engine.active_vector_shapes().unwrap();
|
||||
assert!(shapes.is_empty(), "shapes should be empty after delete");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user