From aa495b8747862197981ebda664a036a603527769 Mon Sep 17 00:00:00 2001 From: Adrien Burgun Date: Sun, 4 Sep 2022 09:30:46 +0200 Subject: [PATCH] :sparkles: Ability to create and edit Panes in the editor --- editor-solidjs/src/Editor.jsx | 35 ++++++++---- editor-solidjs/src/LeftPane.jsx | 69 ++++++++---------------- editor-solidjs/src/LeftPane.module.css | 9 +++- editor-solidjs/src/Panes.jsx | 73 ++++++++++++++++++++++++++ editor-solidjs/src/Presets.jsx | 47 +++++++++++++++++ editor-solidjs/src/Signal.jsx | 41 --------------- editor-solidjs/src/Tabbed.jsx | 2 + editor-solidjs/src/Tabbed.module.css | 2 +- stackline-wasm/src/lib.rs | 33 ++++++++++++ stackline/src/pane.rs | 55 +++++++++++++++++++ 10 files changed, 266 insertions(+), 100 deletions(-) create mode 100644 editor-solidjs/src/Panes.jsx create mode 100644 editor-solidjs/src/Presets.jsx diff --git a/editor-solidjs/src/Editor.jsx b/editor-solidjs/src/Editor.jsx index bf53039..1bcdeb2 100644 --- a/editor-solidjs/src/Editor.jsx +++ b/editor-solidjs/src/Editor.jsx @@ -16,10 +16,27 @@ import Controls from "./Controls.jsx"; import {World, Pane} from "../stackline-wasm/stackline_wasm.js"; -let json = await (await fetch("/prime.json")).json(); +// let json = await (await fetch("/prime.json")).json(); + +let new_world = window.localStorage.getItem("world"); export default function Editor() { - let [world, setWorld] = createSignal(World.deserialize(json), {equals: false}); + let start_world = window.localStorage.getItem("world"); + + try { + if (start_world) { + start_world = JSON.parse(start_world); + start_world = World.deserialize(start_world); + } else { + start_world = new World(); + } + } catch (err) { + console.error(err); + start_world = new World(); + } + + let [world, setWorld] = createSignal(start_world, {equals: false}); + start_world = null; world.init = () => setWorld((world) => { world.init(); @@ -39,13 +56,13 @@ export default function Editor() { }); setWorld((world) => { - let tile = world.get(4, 0); - tile.signal = {direction: "Up", stack: []}; - tile.state = "Active"; - world.set(4, 0, tile); - world.init(); + // let tile = world.get(4, 0); + // tile.signal = {direction: "Up", stack: []}; + // tile.state = "Active"; + // world.set(4, 0, tile); + // world.init(); - console.log(world.toString()); + // console.log(world.toString()); return world; }); @@ -108,7 +125,7 @@ export default function Editor() { } }); - let [left_pane, presets] = LeftPane({settings, setSettings}); + let [left_pane, presets] = LeftPane({settings, setSettings, world, setWorld}); let last_click = 0, last_selected = null; const DOUBLE_CLICK_DURATION = 250; diff --git a/editor-solidjs/src/LeftPane.jsx b/editor-solidjs/src/LeftPane.jsx index 7d8d9f6..b706f53 100644 --- a/editor-solidjs/src/LeftPane.jsx +++ b/editor-solidjs/src/LeftPane.jsx @@ -1,42 +1,16 @@ -import {createSignal} from "solid-js"; -import {createStore} from "solid-js/store"; +import {onMount, onCleanup} from "solid-js"; +import Tabbed from "./Tabbed.jsx"; -import styles from "./LeftPane.module.css"; - -import {available_tiles, Pane, FullTile} from "../stackline-wasm/stackline_wasm.js"; -import TilePreset from "./TilePreset.jsx"; - -export const BASE_TILES = []; - -export class Tile { - constructor(serialized, name) { - this.serialized = serialized; - this.name = name; - } -} - -export function init() { - let pane = new Pane(1, 1); - BASE_TILES.push(new Tile(pane.serialize(), "Empty")); - pane.free(); +import Presets, {init} from "./Presets.jsx"; +import Panes from "./Panes.jsx"; - for (let name of available_tiles().sort()) { - let pane = new Pane(1, 1); - - let tile = new FullTile(name); - pane.set(0, 0, tile); +import styles from "./LeftPane.module.css"; - let serialized = pane.serialize(); - - pane.free(); - BASE_TILES.push(new Tile(serialized, name)); - } -} +export {init}; export default function LeftPane(props) { - let [preset, setPresets] = createStore(BASE_TILES); - let {settings, setSettings} = props; - let left_pane; + let [preset_picker, presets] = Presets(props); + let container; function resize() { let width = document.body.clientWidth / 4; @@ -48,30 +22,29 @@ export default function LeftPane(props) { width = 125; } - left_pane.style.width = `${Math.round(width)}px`; + container.style.width = `calc(${Math.round(width)}px + 2em)`; } - function set_resize() { + onMount(() => { setTimeout(() => { window.addEventListener("resize", resize); resize(); }, 0); - } + }); - function remove_resize() { + onCleanup(() => { window.removeEventListener("resize", resize); - } + }); return [
- - {(item, index) => { - return item.serialized} name={() => item.name} settings={settings} setSettings={setSettings} index={index} />; - }} - -
, preset]; + +
{preset_picker}
+
+ +
+
+ , presets]; } diff --git a/editor-solidjs/src/LeftPane.module.css b/editor-solidjs/src/LeftPane.module.css index 2e50180..fd36735 100644 --- a/editor-solidjs/src/LeftPane.module.css +++ b/editor-solidjs/src/LeftPane.module.css @@ -1,7 +1,7 @@ .LeftPane_container { background: #18181b; color: white; - padding: 1.5em 1em; + /* padding: 1.5em 1em; */ font-family: monospace; font-size: 15px; flex-shrink: 0; @@ -11,6 +11,9 @@ .LeftPane { width: 25vw; +} + +.Presets { display: grid; grid-template-columns: repeat(auto-fill, calc(8em)); grid-gap: 1em; @@ -48,3 +51,7 @@ .TilePreset.picked:hover { border: 4px solid white; } + +.indent { + padding-left: 1em; +} diff --git a/editor-solidjs/src/Panes.jsx b/editor-solidjs/src/Panes.jsx new file mode 100644 index 0000000..f2def71 --- /dev/null +++ b/editor-solidjs/src/Panes.jsx @@ -0,0 +1,73 @@ +import {createMemo} from "solid-js"; +import Number from "./input/Number.jsx"; +import styles from "./LeftPane.module.css"; +import input_styles from "./input/input.module.css"; +import {Pane} from "../stackline-wasm/stackline_wasm.js"; + +export default function Panes(props) { + let {world, setWorld} = props; + + let panes = createMemo(() => { + let world = props.world(); + + let res = []; + + for (let pane_name of world.panes()) { + let pane = world.get_pane(pane_name); + + res.push({ + x: pane.x, + y: pane.y, + width: pane.width, + height: pane.height, + name: pane_name, + }); + + pane.free(); + } + + return res; + }); + + return (
+
    + + {(item, _index) => { + function setProp(name) { + return function(value) { + setWorld((world) => { + let pane = world.get_pane(item.name); + pane[name] = value; + world.set_pane(item.name, pane); + return world; + }); + } + } + + return
  1. + {item.name}: +
    +
    X: item.x} setValue={setProp("x")} />
    +
    Y: item.y} setValue={setProp("y")} />
    +
    Width: item.width} setValue={setProp("width")} />
    +
    Height: item.height} setValue={setProp("height")} />
    +
    +
  2. ; + }} +
    +
+ +
); +} diff --git a/editor-solidjs/src/Presets.jsx b/editor-solidjs/src/Presets.jsx new file mode 100644 index 0000000..e06f181 --- /dev/null +++ b/editor-solidjs/src/Presets.jsx @@ -0,0 +1,47 @@ +import {createSignal} from "solid-js"; +import {createStore} from "solid-js/store"; + +import styles from "./LeftPane.module.css"; + +import {available_tiles, Pane, FullTile} from "../stackline-wasm/stackline_wasm.js"; +import TilePreset from "./TilePreset.jsx"; + +export const BASE_TILES = []; + +export class Tile { + constructor(serialized, name) { + this.serialized = serialized; + this.name = name; + } +} + +export function init() { + let pane = new Pane(1, 1); + BASE_TILES.push(new Tile(pane.serialize(), "Empty")); + pane.free(); + + for (let name of available_tiles().sort()) { + let pane = new Pane(1, 1); + + let tile = new FullTile(name); + pane.set(0, 0, tile); + + let serialized = pane.serialize(); + + pane.free(); + BASE_TILES.push(new Tile(serialized, name)); + } +} + +export default function Presets(props) { + let [presets, setPresets] = createStore(BASE_TILES); + let {settings, setSettings} = props; + + return [
+ + {(item, index) => { + return item.serialized} name={() => item.name} settings={settings} setSettings={setSettings} index={index} />; + }} + +
, presets]; +} diff --git a/editor-solidjs/src/Signal.jsx b/editor-solidjs/src/Signal.jsx index aa8d5df..f74d569 100644 --- a/editor-solidjs/src/Signal.jsx +++ b/editor-solidjs/src/Signal.jsx @@ -43,47 +43,6 @@ export default function Signal(props) { } : null; return
  • item} setValue={setValue} />
  • ; - - // if (setSignal) { - // if ("Number" in item) { - // // Return number input for the `index()`-th element - // return
  • - // { - // console.log("input"); - // setSignal((signal) => { - // signal.stack[index()] = {"Number": +evt.currentTarget.value}; - // return signal; - // }); - // }} - // /> - //
  • ; - // } else if ("String" in item) { - // // Return string input for the `index()`-th element - // return
  • " - // { - // setSignal((signal) => { - // signal.stack[index()] = {"String": evt.currentTarget.value}; - // return signal; - // }); - // }} - // /> - // "
  • ; - // } - // } else { - // if (item?.["Number"]) { - // return
  • {item["Number"]}
  • ; - // } else if (item?.["String"]) { - // return
  • "{item["String"]}"
  • ; - // } - // } }} diff --git a/editor-solidjs/src/Tabbed.jsx b/editor-solidjs/src/Tabbed.jsx index c280aa6..82b520e 100644 --- a/editor-solidjs/src/Tabbed.jsx +++ b/editor-solidjs/src/Tabbed.jsx @@ -6,6 +6,8 @@ export default function Tabbed(props) { let children = props.children; + if (!Array.isArray(children)) children = [children]; + return
      item.title)}> diff --git a/editor-solidjs/src/Tabbed.module.css b/editor-solidjs/src/Tabbed.module.css index 05e2c67..ee4907d 100644 --- a/editor-solidjs/src/Tabbed.module.css +++ b/editor-solidjs/src/Tabbed.module.css @@ -24,7 +24,7 @@ .header > li { box-sizing: border-box; height: 2em; - padding: 0.5em; + padding: 0.25em 0.5em; color: #d0d0d0; cursor: pointer; user-select: none; diff --git a/stackline-wasm/src/lib.rs b/stackline-wasm/src/lib.rs index e108014..cd7807d 100644 --- a/stackline-wasm/src/lib.rs +++ b/stackline-wasm/src/lib.rs @@ -43,6 +43,7 @@ pub fn available_tiles() -> Vec { #[wasm_bindgen] impl World { /// Creates a new World instance + #[wasm_bindgen(constructor)] pub fn new() -> Self { Self(SLWorld::new()) } @@ -159,11 +160,29 @@ impl Pane { self.0.width().get() } + #[wasm_bindgen(setter)] + pub fn set_width(&mut self, width: usize) { + let height = self.0.height().get(); + + self.0.resize(width, height).unwrap_or_else(|| { + err!("Error while resizing Pane"); + }); + } + #[wasm_bindgen(getter)] pub fn height(&self) -> usize { self.0.height().get() } + #[wasm_bindgen(setter)] + pub fn set_height(&mut self, height: usize) { + let width = self.0.width().get(); + + self.0.resize(width, height).unwrap_or_else(|| { + err!("Error while resizing Pane"); + }); + } + #[wasm_bindgen(getter)] pub fn position(&self) -> Vec { let (x, y) = self.0.position(); @@ -180,6 +199,20 @@ impl Pane { self.0.position().1 } + #[wasm_bindgen(setter)] + pub fn set_x(&mut self, value: i32) { + let mut pos = self.0.position(); + pos.0 = value; + self.0.set_position(pos); + } + + #[wasm_bindgen(setter)] + pub fn set_y(&mut self, value: i32) { + let mut pos = self.0.position(); + pos.1 = value; + self.0.set_position(pos); + } + #[wasm_bindgen(setter)] pub fn set_position(&mut self, position: &[i32]) { if let [x, y] = position[..] { diff --git a/stackline/src/pane.rs b/stackline/src/pane.rs index df6eafb..919a6fc 100644 --- a/stackline/src/pane.rs +++ b/stackline/src/pane.rs @@ -522,6 +522,31 @@ impl Pane { .map(move |(i, v)| (i % self.width, i / self.width, v)) } + pub fn resize(&mut self, width: usize, height: usize) -> Option<()> { + let width_nz: NonZeroUsize = width.try_into().ok()?; + let height_nz: NonZeroUsize = height.try_into().ok()?; + let tiles = std::mem::replace(&mut self.tiles, VecCell::from(vec![FullTile::default(); width * height])); + + for (index, tile) in tiles.into_iter().enumerate() { + let x = index % self.width; + let y = index / self.width; + + if x >= width || y >= height { + continue; + } + + let new_index = y * width + x; + if let Some(slot) = self.tiles.get_mut(new_index) { + *slot = tile; + } + } + + self.width = width_nz; + self.height = height_nz; + + Some(()) + } + /// Draws the Pane at `(dx + self.position.0, dy + self.position.1)` on a [`TextSurface`]. /// Empty tiles will leave the `TextSurface` untouched, but tiles are free to modify the characters around them. pub fn draw(&self, dx: i32, dy: i32, surface: &mut TextSurface, blink: Blink) { @@ -684,4 +709,34 @@ mod test { HashSet::from_iter(vec![(0, 0), (1, 0)].into_iter()) ); } + + #[test] + fn test_pane_resize() { + use crate::tile::Wire; + use Orientation::*; + + for w in 3..6 { + for h in 3..6 { + let mut pane = test_tile_setup!( + 2, + 2, + [ + Wire::new(Horizontal), + Wire::new(Vertical), + Wire::new(Any), + () + ] + ); + + pane.resize(w, h); + + assert_eq!(pane.width().get(), w); + assert_eq!(pane.height().get(), h); + + assert_eq!(pane.get_as::((0, 0)).unwrap(), Wire::new(Horizontal)); + assert_eq!(pane.get_as::((1, 0)).unwrap(), Wire::new(Vertical)); + assert_eq!(pane.get_as::((0, 1)).unwrap(), Wire::new(Any)); + } + } + } }