feat(editor): open with a single empty starter frame
A fresh launch seeded the demo `sample()` document — a Frame with a 'Hello OpenPencil' title and a 'Click me' button group. New `EditorState::starter()` returns just one empty Frame (selected), and both hosts open with it; `sample()` stays as the widget-test fixture. input_tests retarget their `n11` selections to the starter frame's `n10`.
This commit is contained in:
parent
12747384c6
commit
d1093b7dc4
|
|
@ -21,9 +21,8 @@ impl EditorState {
|
|||
/// containing a Text `n11` and a Group `n12` (`n13` blue rect +
|
||||
/// `n14` text). Selection anchors on `n11`.
|
||||
///
|
||||
/// Mirrors the historical shell-core `Document::sample()` fixture
|
||||
/// so the native host opens with identical content after the
|
||||
/// migration onto `EditorState`.
|
||||
/// This is the widget-test fixture. The native host opens with
|
||||
/// [`EditorState::starter`] (a single empty Frame) instead.
|
||||
pub fn sample() -> Self {
|
||||
let src = r##"{
|
||||
"version": "0.8.0",
|
||||
|
|
@ -55,6 +54,29 @@ impl EditorState {
|
|||
state
|
||||
}
|
||||
|
||||
/// Build the document a fresh launch opens with — a single empty
|
||||
/// starter Frame `n10` (white fill, 1-px black stroke), selected
|
||||
/// so the user can immediately resize / move it or drop nodes
|
||||
/// inside. No demo decoration.
|
||||
pub fn starter() -> Self {
|
||||
let src = r##"{
|
||||
"version": "0.8.0",
|
||||
"children": [
|
||||
{"type":"frame","id":"n10","name":"Frame",
|
||||
"x":40,"y":40,"width":360,"height":240,
|
||||
"fill":[{"type":"solid","color":"#FFFFFF"}],
|
||||
"stroke":{"thickness":1,"fill":[{"type":"solid","color":"#000000"}]},
|
||||
"children":[]}
|
||||
]
|
||||
}"##;
|
||||
let doc = jian_ops_schema::load_str(src)
|
||||
.expect("EditorState::starter() fixture parses")
|
||||
.value;
|
||||
let mut state = Self::from_document(doc);
|
||||
state.set_single_selection(NodeId::new("n10"));
|
||||
state
|
||||
}
|
||||
|
||||
/// Spawn a fresh leaf node for the active shape / frame / text
|
||||
/// tool at `(doc_x, doc_y)`, sized `init_w × init_h`. Returns the
|
||||
/// new node's id on success; `None` for `Select` / `Hand` (no
|
||||
|
|
@ -200,6 +222,17 @@ mod tests {
|
|||
assert_eq!(s.max_node_id(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn starter_is_a_single_empty_frame_with_it_selected() {
|
||||
let s = EditorState::starter();
|
||||
// Exactly one top-level node — the starter Frame. The
|
||||
// `max_node_id() == 10` check proves no demo children:
|
||||
// the n11..n14 sample tree would lift it to 14.
|
||||
assert_eq!(s.doc.children.len(), 1);
|
||||
assert_eq!(s.max_node_id(), 10);
|
||||
assert_eq!(s.selection.anchor, NodeId::new("n10"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_node_for_tool_spawns_a_rect_and_returns_its_id() {
|
||||
let mut s = EditorState::new();
|
||||
|
|
|
|||
|
|
@ -373,7 +373,9 @@ pub(in crate::widget_host) struct ComponentBrowserDragState {
|
|||
|
||||
impl WidgetHostNative {
|
||||
pub fn new() -> Self {
|
||||
let editor_state = op_editor_core::EditorState::sample();
|
||||
// A fresh launch opens with a single empty starter Frame —
|
||||
// see `EditorState::starter`.
|
||||
let editor_state = op_editor_core::EditorState::starter();
|
||||
// Seed the render scene once up front; subsequent frames
|
||||
// re-derive only when `editor_state_dirty` is set.
|
||||
let layout_scene = op_pen_loader::editor_state_to_layout_scene(&editor_state);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ fn escape_closes_one_overlay_per_press_in_priority_order() {
|
|||
host.editor_state_mut().editor_ui.fill_type_picker_open = true;
|
||||
host.editor_state_mut().chat.focused = true;
|
||||
host.editor_state_mut()
|
||||
.set_single_selection(NodeId::new("n11"));
|
||||
.set_single_selection(NodeId::new("n10"));
|
||||
|
||||
// 1. Property focus clears first.
|
||||
assert!(host.apply_escape());
|
||||
|
|
@ -92,21 +92,21 @@ fn backspace_with_property_draft_does_not_delete_selected() {
|
|||
// char from the draft, not delete the selected node.
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut()
|
||||
.set_single_selection(NodeId::new("n11"));
|
||||
.set_single_selection(NodeId::new("n10"));
|
||||
host.editor_state_mut().ui.property_focus = Some(PropertyFocus::PositionX);
|
||||
host.editor_state_mut().ui.property_input_draft = "123".to_string();
|
||||
|
||||
assert!(host.apply_backspace());
|
||||
assert_eq!(host.editor_state().ui.property_input_draft, "12");
|
||||
// Selection must be untouched.
|
||||
assert_eq!(host.editor_state().selection.anchor, NodeId::new("n11"));
|
||||
assert_eq!(host.editor_state().selection.anchor, NodeId::new("n10"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backspace_without_focus_deletes_selected() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut()
|
||||
.set_single_selection(NodeId::new("n11"));
|
||||
.set_single_selection(NodeId::new("n10"));
|
||||
host.editor_state_mut().ui.property_focus = None;
|
||||
host.editor_state_mut().chat.focused = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,9 @@ pub(in crate::widget_host) struct LayerDragState {
|
|||
|
||||
impl WidgetHost {
|
||||
pub fn new() -> Self {
|
||||
let editor_state = op_editor_core::EditorState::sample();
|
||||
// A fresh launch opens with a single empty starter Frame —
|
||||
// see `EditorState::starter`.
|
||||
let editor_state = op_editor_core::EditorState::starter();
|
||||
// Seed the render scene once up front; subsequent frames
|
||||
// re-derive only when `editor_state_dirty` is set.
|
||||
let layout_scene = op_pen_loader::editor_state_to_layout_scene(&editor_state);
|
||||
|
|
|
|||
Loading…
Reference in a new issue