Plumb layer panel (#107)
* WIP ExpandFolder handling * Implement response parsing in typescript * Update layer panel with list sent by wasm * Add events for layer interaction * Add proper default naming * Fix displaying of the eye icon * Attach path to LayerPanelEntry * Fix lint issues Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
76d3e8cde4
commit
6adb984f2d
|
|
@ -21,7 +21,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
indent: ["error", "tab"],
|
indent: ["error", "tab", { SwitchCase: 1 }],
|
||||||
quotes: ["error", "double"],
|
quotes: ["error", "double"],
|
||||||
"linebreak-style": ["error", "unix"],
|
"linebreak-style": ["error", "unix"],
|
||||||
"eol-last": ["error", "always"],
|
"eol-last": ["error", "always"],
|
||||||
|
|
@ -29,7 +29,7 @@ module.exports = {
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
"max-len": ["error", { code: 200, tabWidth: 4 }],
|
"max-len": ["error", { code: 200, tabWidth: 4 }],
|
||||||
"@typescript-eslint/camelcase": "off",
|
"@typescript-eslint/camelcase": "off",
|
||||||
camelcase: ["error", { ignoreImports: true, ignoreDestructuring: true }],
|
camelcase: ["error", { allow: ["^(?:[a-z]+_)*[a-z]+$"] }],
|
||||||
"prettier-vue/prettier": [
|
"prettier-vue/prettier": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { ResponseType, registerResponseHandler } from "../../response-handler";
|
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, SetActiveTool } from "../../response-handler";
|
||||||
import LayoutRow from "../layout/LayoutRow.vue";
|
import LayoutRow from "../layout/LayoutRow.vue";
|
||||||
import LayoutCol from "../layout/LayoutCol.vue";
|
import LayoutCol from "../layout/LayoutCol.vue";
|
||||||
import ShelfItem from "../widgets/ShelfItem.vue";
|
import ShelfItem from "../widgets/ShelfItem.vue";
|
||||||
|
|
@ -324,13 +324,13 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
registerResponseHandler(ResponseType.UpdateCanvas, (responseData: Response) => {
|
||||||
registerResponseHandler(ResponseType["Tool::UpdateCanvas"], (responseData: any) => {
|
const updateData = responseData as UpdateCanvas;
|
||||||
this.viewportSvg = responseData.Tool.UpdateCanvas.document;
|
if (updateData) this.viewportSvg = updateData.document;
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
registerResponseHandler(ResponseType.SetActiveTool, (responseData: Response) => {
|
||||||
registerResponseHandler(ResponseType["Tool::SetActiveTool"], (responseData: any) => {
|
const toolData = responseData as SetActiveTool;
|
||||||
this.activeTool = responseData.Tool.SetActiveTool.tool_name;
|
if (toolData) this.activeTool = toolData.tool_name;
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("keyup", (e: KeyboardEvent) => this.keyUp(e));
|
window.addEventListener("keyup", (e: KeyboardEvent) => this.keyUp(e));
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,10 @@
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow :class="'layer-tree'">
|
<LayoutRow :class="'layer-tree'">
|
||||||
<LayoutCol :class="'list'">
|
<LayoutCol :class="'list'">
|
||||||
<div
|
<div class="layer-row" v-for="layer in layers" :key="layer.path">
|
||||||
class="layer-row"
|
|
||||||
v-for="layerId in Array(5)
|
|
||||||
.fill()
|
|
||||||
.map((_, i) => i)"
|
|
||||||
:key="layerId"
|
|
||||||
>
|
|
||||||
<div class="layer-visibility">
|
<div class="layer-visibility">
|
||||||
<IconButton v-if="layerId % 2 == 0" @click="hideLayer(layerId)" :size="24" title="Visible"><EyeVisible /></IconButton>
|
<IconButton v-if="layer.visible" @click="hideLayer(layer)" :size="24" title="Visible"><EyeVisible /></IconButton>
|
||||||
<IconButton v-if="layerId % 2 == 1" @click="showLayer(layerId)" :size="24" title="Hidden"><EyeHidden /></IconButton>
|
<IconButton v-if="!layer.visible" @click="showLayer(layer)" :size="24" title="Hidden"><EyeHidden /></IconButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="layer">
|
<div class="layer">
|
||||||
<div class="layer-thumbnail"></div>
|
<div class="layer-thumbnail"></div>
|
||||||
|
|
@ -24,7 +18,7 @@
|
||||||
<IconContainer :size="24" title="Path"><NodeTypePath /></IconContainer>
|
<IconContainer :size="24" title="Path"><NodeTypePath /></IconContainer>
|
||||||
</div>
|
</div>
|
||||||
<div class="layer-name">
|
<div class="layer-name">
|
||||||
<span>Foo bar</span>
|
<span>{{ layer.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -77,7 +71,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { ResponseType, registerResponseHandler } from "../../response-handler";
|
import { ResponseType, registerResponseHandler, Response, ExpandFolder, LayerPanelEntry } from "../../response-handler";
|
||||||
import LayoutRow from "../layout/LayoutRow.vue";
|
import LayoutRow from "../layout/LayoutRow.vue";
|
||||||
import LayoutCol from "../layout/LayoutCol.vue";
|
import LayoutCol from "../layout/LayoutCol.vue";
|
||||||
import NumberInput from "../widgets/NumberInput.vue";
|
import NumberInput from "../widgets/NumberInput.vue";
|
||||||
|
|
@ -102,23 +96,42 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
methods: {
|
methods: {
|
||||||
hideLayer(layerId: number) {
|
hideLayer(layerId: LayerPanelEntry) {
|
||||||
console.log(`Hidden layer ID: ${layerId}`);
|
const layer = layerId as LayerPanelEntry;
|
||||||
|
if (layer) {
|
||||||
|
console.log(`Hidden layer ID: ${layer.path}`);
|
||||||
|
} else {
|
||||||
|
console.error("hideLayer did not receive valid arguments");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
showLayer(layerId: number) {
|
showLayer(layerId: LayerPanelEntry) {
|
||||||
console.log(`Shown layer ID: ${layerId}`);
|
const layer = layerId as LayerPanelEntry;
|
||||||
|
if (layer) {
|
||||||
|
console.log(`Shown layer: ${layer.path}`);
|
||||||
|
} else {
|
||||||
|
console.error("showLayer did not receive valid arguments");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
registerResponseHandler(ResponseType["Document::ExpandFolder"], (responseData) => {
|
registerResponseHandler(ResponseType.ExpandFolder, (responseData: Response) => {
|
||||||
console.log("ExpandFolder: ", responseData);
|
const expandData = responseData as ExpandFolder;
|
||||||
|
if (expandData) {
|
||||||
|
const responsePath = expandData.path;
|
||||||
|
const responseLayers = expandData.children as Array<LayerPanelEntry>;
|
||||||
|
if (responsePath.length > 0) console.error("Non root paths are currently not implemented");
|
||||||
|
|
||||||
|
this.layers = responseLayers;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
registerResponseHandler(ResponseType["Document::CollapseFolder"], (responseData) => {
|
registerResponseHandler(ResponseType.CollapseFolder, (responseData) => {
|
||||||
console.log("CollapseFolder: ", responseData);
|
console.log("CollapseFolder: ", responseData);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
layers: [] as Array<LayerPanelEntry>,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
type ResponseCallback = (responseData: string) => void;
|
type ResponseCallback = (responseData: Response) => void;
|
||||||
type ResponseMap = {
|
type ResponseMap = {
|
||||||
[response: string]: ResponseCallback | undefined;
|
[response: string]: ResponseCallback | undefined;
|
||||||
};
|
};
|
||||||
|
|
@ -9,10 +9,10 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ResponseType {
|
export enum ResponseType {
|
||||||
"Tool::UpdateCanvas" = "Tool::UpdateCanvas",
|
UpdateCanvas = "UpdateCanvas",
|
||||||
"Document::ExpandFolder" = "Document::ExpandFolder",
|
ExpandFolder = "ExpandFolder",
|
||||||
"Document::CollapseFolder" = "Document::CollapseFolder",
|
CollapseFolder = "CollapseFolder",
|
||||||
"Tool::SetActiveTool" = "Tool::SetActiveTool",
|
SetActiveTool = "SetActiveTool",
|
||||||
}
|
}
|
||||||
|
|
||||||
export function attachResponseHandlerToPage() {
|
export function attachResponseHandlerToPage() {
|
||||||
|
|
@ -24,12 +24,82 @@ export function registerResponseHandler(responseType: ResponseType, callback: Re
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function handleResponse(responseType: ResponseType, responseData: any) {
|
function parseResponse(origin: string, responseType: string, data: any): Response {
|
||||||
const callback = window.responseMap[responseType];
|
type OriginNames = "Document" | "Tool";
|
||||||
|
|
||||||
if (callback) {
|
const originHandlers = {
|
||||||
callback(responseData);
|
Document: () => {
|
||||||
|
switch (responseType) {
|
||||||
|
case "DocumentChanged":
|
||||||
|
return (data.Document.DocumentChanged as DocumentChanged) as Response;
|
||||||
|
case "CollapseFolder":
|
||||||
|
return (data.Document.CollapseFolder as CollapseFolder) as Response;
|
||||||
|
case "ExpandFolder":
|
||||||
|
return (data.Document.ExpandFolder as ExpandFolder) as Response;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Tool: () => {
|
||||||
|
switch (responseType) {
|
||||||
|
case "SetActiveTool":
|
||||||
|
return (data.Tool.SetActiveTool as SetActiveTool) as Response;
|
||||||
|
case "UpdateCanvas":
|
||||||
|
return (data.Tool.UpdateCanvas as UpdateCanvas) as Response;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Optional chaining would be nice here when we can upgrade to Webpack 5: https://github.com/webpack/webpack/issues/10227
|
||||||
|
// const response = originHandlers[origin as OriginNames]?.();
|
||||||
|
const response = originHandlers[origin as OriginNames] && originHandlers[origin as OriginNames]();
|
||||||
|
if (!response) throw new Error("ResponseType not recognized.");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export function handleResponse(responseIdentifier: string, responseData: any) {
|
||||||
|
const [origin, responesType] = responseIdentifier.split("::", 2);
|
||||||
|
const callback = window.responseMap[responesType];
|
||||||
|
const data = parseResponse(origin, responesType, responseData);
|
||||||
|
|
||||||
|
if (callback && data) {
|
||||||
|
callback(data);
|
||||||
|
} else if (data) {
|
||||||
|
console.error(`Received a Response of type "${responseIdentifier}" but no handler was registered for it from the client.`);
|
||||||
} else {
|
} else {
|
||||||
console.error(`Received a Response of type "${responseType}" but no handler was registered for it from the client.`);
|
console.error(`Received a Response of type "${responseIdentifier}" but but was not able to parse the data.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Response = SetActiveTool | UpdateCanvas | DocumentChanged | CollapseFolder | ExpandFolder;
|
||||||
|
|
||||||
|
export interface SetActiveTool {
|
||||||
|
tool_name: string;
|
||||||
|
}
|
||||||
|
export interface UpdateCanvas {
|
||||||
|
document: string;
|
||||||
|
}
|
||||||
|
export type DocumentChanged = {};
|
||||||
|
export interface CollapseFolder {
|
||||||
|
path: Array<number>;
|
||||||
|
}
|
||||||
|
export interface ExpandFolder {
|
||||||
|
path: Array<number>;
|
||||||
|
children: Array<LayerPanelEntry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LayerPanelEntry {
|
||||||
|
name: string;
|
||||||
|
visible: boolean;
|
||||||
|
layer_type: LayerType;
|
||||||
|
collapsed: boolean;
|
||||||
|
path: Array<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LayerType {
|
||||||
|
Folder,
|
||||||
|
Shape,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::shims::Error;
|
use crate::shims::Error;
|
||||||
use crate::wrappers::{translate_key, translate_tool, Color};
|
use crate::wrappers::{translate_key, translate_tool, Color};
|
||||||
use crate::EDITOR_STATE;
|
use crate::EDITOR_STATE;
|
||||||
use editor_core::events;
|
use editor_core::{events, LayerId};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
fn convert_error(err: editor_core::EditorError) -> JsValue {
|
fn convert_error(err: editor_core::EditorError) -> JsValue {
|
||||||
|
|
@ -32,7 +32,14 @@ mod mouse_state {
|
||||||
(false, 1) => Event::LmbUp(state),
|
(false, 1) => Event::LmbUp(state),
|
||||||
(false, 2) => Event::RmbUp(state),
|
(false, 2) => Event::RmbUp(state),
|
||||||
(false, 4) => Event::MmbUp(state),
|
(false, 4) => Event::MmbUp(state),
|
||||||
_ => panic!("two buttons where modified at the same time. modification: {:#010b}", diff),
|
(down, _) => {
|
||||||
|
log::warn!("two buttons where modified at the same time. Modification: {:#010b}", diff);
|
||||||
|
if down {
|
||||||
|
Event::AmbiguousMouseDown(state)
|
||||||
|
} else {
|
||||||
|
Event::AmbiguousMouseUp(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,3 +125,45 @@ pub fn swap_colors() -> Result<(), JsValue> {
|
||||||
pub fn reset_colors() -> Result<(), JsValue> {
|
pub fn reset_colors() -> Result<(), JsValue> {
|
||||||
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(events::Event::ResetColors)).map_err(convert_error)
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(events::Event::ResetColors)).map_err(convert_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select a layer from the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn select_layer(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(events::Event::SelectLayer(path))).map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle visibility of a layer from the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn toggle_layer_visibility(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE
|
||||||
|
.with(|editor| editor.borrow_mut().handle_event(events::Event::ToggleLayerVisibility(path)))
|
||||||
|
.map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle expansions state of a layer from the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn toggle_layer_expansion(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE
|
||||||
|
.with(|editor| editor.borrow_mut().handle_event(events::Event::ToggleLayerExpansion(path)))
|
||||||
|
.map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renames a layer from the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn rename_layer(path: Vec<LayerId>, new_name: String) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE
|
||||||
|
.with(|editor| editor.borrow_mut().handle_event(events::Event::RenameLayer(path, new_name)))
|
||||||
|
.map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a layer from the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn delete_layer(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(events::Event::DeleteLayer(path))).map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests the backend to add a layer to the layer list
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn add_layer(path: Vec<LayerId>) -> Result<(), JsValue> {
|
||||||
|
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_event(events::Event::AddLayer(path))).map_err(convert_error)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, PolyLine, Rect, Shape},
|
layers::{self, Folder, Layer, LayerData, LayerDataTypes, Line, PolyLine, Rect, Shape},
|
||||||
response::{LayerPanelEntry, LayerType},
|
response::LayerPanelEntry,
|
||||||
DocumentError, DocumentResponse, LayerId, Operation,
|
DocumentError, DocumentResponse, LayerId, Operation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -172,16 +172,12 @@ impl Document {
|
||||||
/// any actual data, but rather metadata such as visibility and names of the layers.
|
/// any actual data, but rather metadata such as visibility and names of the layers.
|
||||||
pub fn layer_panel(&self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, DocumentError> {
|
pub fn layer_panel(&self, path: &[LayerId]) -> Result<Vec<LayerPanelEntry>, DocumentError> {
|
||||||
let folder = self.document_folder(path)?;
|
let folder = self.document_folder(path)?;
|
||||||
let l_type = |layer: &LayerDataTypes| match layer {
|
let entries = folder
|
||||||
LayerDataTypes::Folder(_) => LayerType::Folder,
|
.layers()
|
||||||
_ => LayerType::Shape,
|
.iter()
|
||||||
};
|
.zip(folder.layer_ids.iter())
|
||||||
let translate = |layer: &Layer| LayerPanelEntry {
|
.map(|(layer, id)| LayerPanelEntry::from_layer(layer, [path, &[*id]].concat()))
|
||||||
name: layer.name.clone().unwrap_or_else(|| String::from("UnnamedFolder")),
|
.collect();
|
||||||
visible: layer.visible,
|
|
||||||
layer_type: l_type(&layer.data),
|
|
||||||
};
|
|
||||||
let entries = folder.layers().iter().map(|layer| translate(layer)).collect();
|
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ pub struct Folder {
|
||||||
next_assignment_id: LayerId,
|
next_assignment_id: LayerId,
|
||||||
pub layer_ids: Vec<LayerId>,
|
pub layer_ids: Vec<LayerId>,
|
||||||
layers: Vec<Layer>,
|
layers: Vec<Layer>,
|
||||||
|
pub collapsed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayerData for Folder {
|
impl LayerData for Folder {
|
||||||
|
|
@ -87,6 +88,7 @@ impl Default for Folder {
|
||||||
layer_ids: vec![],
|
layer_ids: vec![],
|
||||||
layers: vec![],
|
layers: vec![],
|
||||||
next_assignment_id: 0,
|
next_assignment_id: 0,
|
||||||
|
collapsed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::LayerId;
|
use crate::{
|
||||||
|
layers::{Layer, LayerDataTypes},
|
||||||
|
LayerId,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -7,12 +10,19 @@ pub struct LayerPanelEntry {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
pub layer_type: LayerType,
|
pub layer_type: LayerType,
|
||||||
|
pub collapsed: bool,
|
||||||
|
pub path: Vec<LayerId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum LayerType {
|
pub enum LayerType {
|
||||||
Folder,
|
Folder,
|
||||||
Shape,
|
Shape,
|
||||||
|
Circle,
|
||||||
|
Rect,
|
||||||
|
Line,
|
||||||
|
PolyLine,
|
||||||
|
Ellipse,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LayerType {
|
impl fmt::Display for LayerType {
|
||||||
|
|
@ -20,12 +30,47 @@ impl fmt::Display for LayerType {
|
||||||
let name = match self {
|
let name = match self {
|
||||||
LayerType::Folder => "folder",
|
LayerType::Folder => "folder",
|
||||||
LayerType::Shape => "shape",
|
LayerType::Shape => "shape",
|
||||||
|
LayerType::Rect => "rect",
|
||||||
|
LayerType::Line => "line",
|
||||||
|
LayerType::Circle => "circle",
|
||||||
|
LayerType::PolyLine => "poly line",
|
||||||
|
LayerType::Ellipse => "ellipse",
|
||||||
};
|
};
|
||||||
|
|
||||||
formatter.write_str(name)
|
formatter.write_str(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&LayerDataTypes> for LayerType {
|
||||||
|
fn from(data: &LayerDataTypes) -> Self {
|
||||||
|
use LayerDataTypes::*;
|
||||||
|
match data {
|
||||||
|
Folder(_) => LayerType::Folder,
|
||||||
|
Shape(_) => LayerType::Shape,
|
||||||
|
Circle(_) => LayerType::Circle,
|
||||||
|
Rect(_) => LayerType::Rect,
|
||||||
|
Line(_) => LayerType::Line,
|
||||||
|
PolyLine(_) => LayerType::PolyLine,
|
||||||
|
Ellipse(_) => LayerType::Ellipse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerPanelEntry {
|
||||||
|
pub fn from_layer(layer: &Layer, path: Vec<LayerId>) -> Self {
|
||||||
|
let layer_type: LayerType = (&layer.data).into();
|
||||||
|
let name = layer.name.clone().unwrap_or_else(|| format!("Unnamed {}", layer_type));
|
||||||
|
let collapsed = if let LayerDataTypes::Folder(f) = &layer.data { f.collapsed } else { true };
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
visible: layer.visible,
|
||||||
|
layer_type,
|
||||||
|
collapsed,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
// TODO - Make Copy when possible
|
// TODO - Make Copy when possible
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{fmt, ops::Add};
|
use std::{fmt, ops::Add};
|
||||||
|
|
||||||
use kurbo::{PathEl, Point, Vec2};
|
use kurbo::{PathEl, Point, Vec2};
|
||||||
use log::info;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct ShapePoints {
|
pub struct ShapePoints {
|
||||||
|
|
@ -47,7 +46,6 @@ impl std::fmt::Display for ShapePoints {
|
||||||
let sine = theta.sin();
|
let sine = theta.sin();
|
||||||
Vec2::new(v.x * cosine - v.y * sine, v.x * sine + v.y * cosine)
|
Vec2::new(v.x * cosine - v.y * sine, v.x * sine + v.y * cosine)
|
||||||
}
|
}
|
||||||
info!("sides{}", self.sides);
|
|
||||||
for i in 0..self.sides {
|
for i in 0..self.sides {
|
||||||
let radians = self.apothem_offset_angle() * ((i * 2 + (self.sides % 2)) as f64);
|
let radians = self.apothem_offset_angle() * ((i * 2 + (self.sides % 2)) as f64);
|
||||||
let offset = rotate(&self.extent, radians);
|
let offset = rotate(&self.extent, radians);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
use super::{Event, EventHandler, Operation, Response};
|
||||||
|
use crate::tools::{DocumentToolData, ToolData};
|
||||||
|
use crate::Document;
|
||||||
|
|
||||||
|
pub struct DocumentEventHandler {}
|
||||||
|
|
||||||
|
impl DocumentEventHandler {
|
||||||
|
fn pre_process_event(&mut self, editor_state: &Document, tool_data: &mut DocumentToolData, events: &mut Vec<Event>, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) {}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ use crate::tools::ToolType;
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use document_core::LayerId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
|
@ -18,8 +19,16 @@ pub enum Event {
|
||||||
SelectTool(ToolType),
|
SelectTool(ToolType),
|
||||||
SelectPrimaryColor(Color),
|
SelectPrimaryColor(Color),
|
||||||
SelectSecondaryColor(Color),
|
SelectSecondaryColor(Color),
|
||||||
|
SelectLayer(Vec<LayerId>),
|
||||||
|
ToggleLayerVisibility(Vec<LayerId>),
|
||||||
|
ToggleLayerExpansion(Vec<LayerId>),
|
||||||
|
DeleteLayer(Vec<LayerId>),
|
||||||
|
AddLayer(Vec<LayerId>),
|
||||||
|
RenameLayer(Vec<LayerId>, String),
|
||||||
SwapColors,
|
SwapColors,
|
||||||
ResetColors,
|
ResetColors,
|
||||||
|
AmbiguousMouseDown(MouseState),
|
||||||
|
AmbiguousMouseUp(MouseState),
|
||||||
LmbDown(MouseState),
|
LmbDown(MouseState),
|
||||||
RmbDown(MouseState),
|
RmbDown(MouseState),
|
||||||
MmbDown(MouseState),
|
MmbDown(MouseState),
|
||||||
|
|
@ -34,6 +43,7 @@ pub enum Event {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum ToolResponse {
|
pub enum ToolResponse {
|
||||||
|
// These may not have the same names as any of the DocumentResponses
|
||||||
SetActiveTool { tool_name: String },
|
SetActiveTool { tool_name: String },
|
||||||
UpdateCanvas { document: String },
|
UpdateCanvas { document: String },
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
use super::{input_manager::InputManager, Event, EventHandler, Operation, Response};
|
||||||
|
use crate::tools::{DocumentToolData, ToolData, ToolSettings};
|
||||||
|
use document_core::document::Document;
|
||||||
|
|
||||||
|
pub struct GlobalEventHandler {}
|
||||||
|
|
||||||
|
impl GlobalEventHandler {
|
||||||
|
fn new(tool_data: ToolData) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_process_event(&mut self, input: &InputManager, events: &mut Vec<Event>, responses: &mut Vec<Response>, operations: &mut Vec<Operation>) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -90,6 +90,7 @@ impl Dispatcher {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => todo!("Implement layer handling"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut tool_responses, operations) = editor_state
|
let (mut tool_responses, operations) = editor_state
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
pub type PanelId = usize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub type PanelId = u32;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
pub hovered_panel: PanelId,
|
pub hovered_panel: PanelId,
|
||||||
pub root: PanelGroup,
|
pub root: PanelGroup,
|
||||||
|
|
@ -15,14 +18,20 @@ impl Workspace {
|
||||||
// add panel / panel group
|
// add panel / panel group
|
||||||
// delete panel / panel group
|
// delete panel / panel group
|
||||||
// move panel / panel group
|
// move panel / panel group
|
||||||
// get_serialized_layout()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct PanelGroup {
|
pub struct PanelGroup {
|
||||||
pub contents: Vec<Contents>,
|
pub contents: Vec<Contents>,
|
||||||
pub layout_direction: LayoutDirection,
|
pub layout_direction: LayoutDirection,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for PanelGroup {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PanelGroup {
|
impl PanelGroup {
|
||||||
fn new() -> PanelGroup {
|
fn new() -> PanelGroup {
|
||||||
PanelGroup {
|
PanelGroup {
|
||||||
|
|
@ -32,16 +41,19 @@ impl PanelGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum Contents {
|
pub enum Contents {
|
||||||
PanelArea(PanelArea),
|
PanelArea(PanelArea),
|
||||||
Group(PanelGroup),
|
Group(PanelGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct PanelArea {
|
pub struct PanelArea {
|
||||||
pub panels: Vec<PanelId>,
|
pub panels: Vec<PanelId>,
|
||||||
pub active: PanelId,
|
pub active: PanelId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum LayoutDirection {
|
pub enum LayoutDirection {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue