BIG REFACTOR GPT SOL
feat: Refactor Iced panel adapter and introduce build metadata management - Updated Cargo.toml files across multiple crates to use workspace versioning. - Enhanced the `iced-panel-adapter` to include a new `plain_slider` module and updated widget rendering to support theme colors. - Added new theme color utilities for recessed and elevated surfaces in `iced-panel-adapter`. - Introduced a new `hcie-build-info` crate to manage build metadata, including a build ID system. - Created a build script to synchronize build IDs across the workspace. - Added a Makefile for simplified build commands for the Iced application. - Implemented regression tests for vector shape creation history in the engine API. - Added a new script for managing Cargo commands with synchronized build ID increments. - Updated line count report to reflect recent changes in codebase. - Created a visual plan document for future improvements in the Iced history and panel systems.
This commit is contained in:
+13
-27
@@ -29,6 +29,8 @@ use hcie_tile::TiledLayer;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub use crate::shape_catalog::{ShapeCatalog, ShapeEntry};
|
||||
pub use crate::svg_editor::{SvgEditable, SvgNode};
|
||||
pub use hcie_blend::Adjustment;
|
||||
pub use hcie_brush_engine::presets::BrushPreset;
|
||||
pub use hcie_protocol::thumbnail_nearest;
|
||||
@@ -41,8 +43,6 @@ pub use hcie_protocol::{
|
||||
FilterType, LayerData, LayerInfo, LayerStyle, LayerType, LineCap, ModulePanel, ToolConfigs,
|
||||
VectorEditHandle, VectorShape, WidgetDescription, ZOOM_MAX, ZOOM_MIN,
|
||||
};
|
||||
pub use crate::shape_catalog::{ShapeCatalog, ShapeEntry};
|
||||
pub use crate::svg_editor::{SvgEditable, SvgNode};
|
||||
pub mod brush {
|
||||
pub use hcie_brush_engine::{BrushStyle, BrushTip};
|
||||
}
|
||||
@@ -556,8 +556,7 @@ impl Engine {
|
||||
cached_selection_mask: None,
|
||||
pending_history: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
|
||||
shape_catalog: {
|
||||
let base = dirs::data_local_dir()
|
||||
.unwrap_or_else(|| std::path::PathBuf::from("."));
|
||||
let base = dirs::data_local_dir().unwrap_or_else(|| std::path::PathBuf::from("."));
|
||||
let shapes_dir = base.join("hcie").join("shapes");
|
||||
crate::shape_catalog::ShapeCatalog::new(shapes_dir)
|
||||
},
|
||||
@@ -850,9 +849,7 @@ impl Engine {
|
||||
let idx = y * w + x;
|
||||
if mask[idx] == 0 {
|
||||
let pidx = idx * 4;
|
||||
if pidx + 3 < layer.pixels.len()
|
||||
&& pidx + 3 < before_pixels.len()
|
||||
{
|
||||
if pidx + 3 < layer.pixels.len() && pidx + 3 < before_pixels.len() {
|
||||
layer.pixels[pidx] = before_pixels[pidx];
|
||||
layer.pixels[pidx + 1] = before_pixels[pidx + 1];
|
||||
layer.pixels[pidx + 2] = before_pixels[pidx + 2];
|
||||
@@ -899,9 +896,7 @@ impl Engine {
|
||||
let idx = y * w + x;
|
||||
if mask[idx] == 0 {
|
||||
let pidx = idx * 4;
|
||||
if pidx + 3 < layer.pixels.len()
|
||||
&& pidx + 3 < saved_before.len()
|
||||
{
|
||||
if pidx + 3 < layer.pixels.len() && pidx + 3 < saved_before.len() {
|
||||
layer.pixels[pidx] = saved_before[pidx];
|
||||
layer.pixels[pidx + 1] = saved_before[pidx + 1];
|
||||
layer.pixels[pidx + 2] = saved_before[pidx + 2];
|
||||
@@ -1346,23 +1341,14 @@ impl Engine {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Active layer is NOT vector — create a NEW vector layer (matching V2 behavior)
|
||||
let new_id = self
|
||||
.document
|
||||
.add_layer(&format!("Vector {}", self.document.layers.len()));
|
||||
if let Some(idx) = self.document.layer_index_by_id(new_id) {
|
||||
// Set as vector layer
|
||||
if let Some(layer) = self.document.get_layer_by_id_mut(new_id) {
|
||||
layer.data = LayerData::Vector {
|
||||
shapes: vec![shape],
|
||||
};
|
||||
layer.layer_type = LayerType::Vector;
|
||||
layer.dirty = true;
|
||||
self.document.composite_dirty = true;
|
||||
self.document.modified = true;
|
||||
}
|
||||
self.document.set_active_layer(idx);
|
||||
}
|
||||
// Create the final vector layer before recording history. This keeps the first shape and
|
||||
// its auto-created layer in one atomic Undo/Redo entry instead of capturing an empty
|
||||
// intermediate raster layer.
|
||||
self.document.add_vector_layer_with_shape(
|
||||
format!("Vector {}", self.document.layers.len()),
|
||||
shape,
|
||||
"Add Vector Shape",
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_svg_source(&self, layer_id: u64) -> Option<&[u8]> {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
//! Regression coverage for atomic vector-shape creation history.
|
||||
//!
|
||||
//! **Purpose:** Ensures the first vector shape and its auto-created vector layer form one exact
|
||||
//! Undo/Redo transaction, while later shapes on the same layer continue to use vector snapshots.
|
||||
//! **Logic & Workflow:** Creates shapes through the public `Engine` API, inspects history metadata,
|
||||
//! and exercises Undo/Redo against layer and shape counts.
|
||||
//! **Side Effects / Dependencies:** Uses only in-memory engine documents.
|
||||
|
||||
use hcie_engine_api::{Engine, LayerType, VectorShape};
|
||||
|
||||
/// Builds a deterministic rectangle for history tests.
|
||||
///
|
||||
/// **Arguments:** `offset` shifts the rectangle so repeated shapes remain distinct.
|
||||
/// **Returns:** A filled vector rectangle with stable styling.
|
||||
/// **Side Effects / Dependencies:** None.
|
||||
fn rectangle(offset: f32) -> VectorShape {
|
||||
VectorShape::Rect {
|
||||
name: String::new(),
|
||||
x1: 10.0 + offset,
|
||||
y1: 12.0 + offset,
|
||||
x2: 60.0 + offset,
|
||||
y2: 48.0 + offset,
|
||||
stroke: 2.0,
|
||||
color: [10, 20, 30, 255],
|
||||
fill: true,
|
||||
fill_color: [40, 50, 60, 255],
|
||||
radius: 0.0,
|
||||
angle: 0.0,
|
||||
opacity: 1.0,
|
||||
hardness: 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_vector_shape_is_one_atomic_layer_transaction() {
|
||||
let mut engine = Engine::new(128, 96);
|
||||
let layers_before = engine.get_layer_count();
|
||||
let history_before = engine.history_len();
|
||||
|
||||
engine.add_vector_shape(rectangle(0.0));
|
||||
|
||||
assert_eq!(engine.get_layer_count(), layers_before + 1);
|
||||
assert_eq!(engine.history_len(), history_before + 1);
|
||||
assert_eq!(
|
||||
engine.history_description(history_before).as_deref(),
|
||||
Some("Add Vector Shape")
|
||||
);
|
||||
assert_eq!(
|
||||
engine
|
||||
.get_layer_info(engine.active_layer_id())
|
||||
.map(|info| info.layer_type),
|
||||
Some(LayerType::Vector)
|
||||
);
|
||||
assert_eq!(
|
||||
engine.active_vector_shapes().map(|shapes| shapes.len()),
|
||||
Some(1)
|
||||
);
|
||||
|
||||
assert!(engine.undo());
|
||||
assert_eq!(engine.get_layer_count(), layers_before);
|
||||
|
||||
assert!(engine.redo());
|
||||
assert_eq!(engine.get_layer_count(), layers_before + 1);
|
||||
assert!(engine
|
||||
.layer_infos()
|
||||
.iter()
|
||||
.any(|info| info.layer_type == LayerType::Vector && info.shape_count == 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn later_vector_shapes_each_add_one_snapshot() {
|
||||
let mut engine = Engine::new(128, 96);
|
||||
engine.add_vector_shape(rectangle(0.0));
|
||||
let history_before = engine.history_len();
|
||||
|
||||
engine.add_vector_shape(rectangle(8.0));
|
||||
|
||||
assert_eq!(engine.history_len(), history_before + 1);
|
||||
assert_eq!(
|
||||
engine.active_vector_shapes().map(|shapes| shapes.len()),
|
||||
Some(2)
|
||||
);
|
||||
assert!(engine.undo());
|
||||
assert_eq!(
|
||||
engine.active_vector_shapes().map(|shapes| shapes.len()),
|
||||
Some(1)
|
||||
);
|
||||
assert!(engine.redo());
|
||||
assert_eq!(
|
||||
engine.active_vector_shapes().map(|shapes| shapes.len()),
|
||||
Some(2)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user