diff --git a/client/web/src/components/widgets/inputs/MenuBarInput.vue b/client/web/src/components/widgets/inputs/MenuBarInput.vue
index 6fe09727..1def90c4 100644
--- a/client/web/src/components/widgets/inputs/MenuBarInput.vue
+++ b/client/web/src/components/widgets/inputs/MenuBarInput.vue
@@ -73,7 +73,7 @@ const menuEntries: MenuListEntries = [
ref: undefined,
children: [
[
- { label: "New", icon: "File", shortcut: ["Ctrl", "N"] },
+ { label: "New", icon: "File", shortcut: ["Ctrl", "N"], action: async () => (await wasm).new_document() },
{ label: "Open…", shortcut: ["Ctrl", "O"] },
{
label: "Open Recent",
diff --git a/client/web/src/components/workspace/Panel.vue b/client/web/src/components/workspace/Panel.vue
index acf42993..dc167abf 100644
--- a/client/web/src/components/workspace/Panel.vue
+++ b/client/web/src/components/workspace/Panel.vue
@@ -2,7 +2,7 @@
-
+
{{ tabLabel }}
@@ -144,6 +144,8 @@ import IconButton from "../widgets/buttons/IconButton.vue";
import PopoverButton, { PopoverButtonIcon } from "../widgets/buttons/PopoverButton.vue";
import { MenuDirection } from "../widgets/floating-menus/FloatingMenu.vue";
+const wasm = import("../../../wasm/pkg");
+
export default defineComponent({
components: {
Document,
@@ -153,6 +155,12 @@ export default defineComponent({
IconButton,
PopoverButton,
},
+ methods: {
+ async handleTabClick(tabIndex: number) {
+ const { select_document } = await wasm;
+ select_document(tabIndex);
+ },
+ },
props: {
tabMinWidths: { type: Boolean, default: false },
tabCloseButtons: { type: Boolean, default: false },
diff --git a/client/web/src/components/workspace/Workspace.vue b/client/web/src/components/workspace/Workspace.vue
index 07c62a6b..2c966d55 100644
--- a/client/web/src/components/workspace/Workspace.vue
+++ b/client/web/src/components/workspace/Workspace.vue
@@ -1,13 +1,7 @@
-
+
@@ -52,6 +46,7 @@
diff --git a/client/web/src/response-handler.ts b/client/web/src/response-handler.ts
index 135e3b72..38284a6f 100644
--- a/client/web/src/response-handler.ts
+++ b/client/web/src/response-handler.ts
@@ -16,6 +16,8 @@ export enum ResponseType {
ExpandFolder = "ExpandFolder",
CollapseFolder = "CollapseFolder",
SetActiveTool = "SetActiveTool",
+ SetActiveDocument = "SetActiveDocument",
+ NewDocument = "NewDocument",
UpdateWorkingColors = "UpdateWorkingColors",
}
@@ -52,6 +54,10 @@ function parseResponse(responseType: string, data: any): Response {
return newExpandFolder(data.ExpandFolder);
case "SetActiveTool":
return newSetActiveTool(data.SetActiveTool);
+ case "SetActiveDocument":
+ return newSetActiveDocument(data.SetActiveDocument);
+ case "NewDocument":
+ return newNewDocument(data.NewDocument);
case "UpdateCanvas":
return newUpdateCanvas(data.UpdateCanvas);
case "ExportDocument":
@@ -95,6 +101,24 @@ function newSetActiveTool(input: any): SetActiveTool {
};
}
+export interface SetActiveDocument {
+ document_index: number;
+}
+function newSetActiveDocument(input: any): SetActiveDocument {
+ return {
+ document_index: input.document_index,
+ };
+}
+
+export interface NewDocument {
+ document_name: string;
+}
+function newNewDocument(input: any): NewDocument {
+ return {
+ document_name: input.document_name,
+ };
+}
+
export interface UpdateCanvas {
document: string;
}
diff --git a/client/web/wasm/src/document.rs b/client/web/wasm/src/document.rs
index 0974f607..bf12008e 100644
--- a/client/web/wasm/src/document.rs
+++ b/client/web/wasm/src/document.rs
@@ -21,6 +21,16 @@ pub fn select_tool(tool: String) -> Result<(), JsValue> {
})
}
+#[wasm_bindgen]
+pub fn select_document(document: usize) -> Result<(), JsValue> {
+ EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SelectDocument(document)).map_err(convert_error))
+}
+
+#[wasm_bindgen]
+pub fn new_document() -> Result<(), JsValue> {
+ EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::NewDocument).map_err(convert_error))
+}
+
// TODO: When a mouse button is down that started in the viewport, this should trigger even when the mouse is outside the viewport (or even the browser window if the browser supports it)
/// Mouse movement within the screenspace bounds of the viewport
#[wasm_bindgen]
diff --git a/client/web/wasm/src/wrappers.rs b/client/web/wasm/src/wrappers.rs
index 20e1807d..e4a16be0 100644
--- a/client/web/wasm/src/wrappers.rs
+++ b/client/web/wasm/src/wrappers.rs
@@ -123,6 +123,7 @@ pub fn translate_key(name: &str) -> Key {
"backspace" => KeyBackspace,
"alt" => KeyAlt,
"escape" => KeyEscape,
+ "tab" => KeyTab,
_ => UnknownKey,
}
}
diff --git a/core/editor/src/document/document_file.rs b/core/editor/src/document/document_file.rs
index 008c0cda..d724a913 100644
--- a/core/editor/src/document/document_file.rs
+++ b/core/editor/src/document/document_file.rs
@@ -14,7 +14,17 @@ impl Default for Document {
fn default() -> Self {
Self {
document: InteralDocument::default(),
- name: String::from("Unnamed Document"),
+ name: String::from("Untitled Document"),
+ layer_data: vec![(vec![], LayerData { selected: false, expanded: true })].into_iter().collect(),
+ }
+ }
+}
+
+impl Document {
+ pub fn with_name(name: String) -> Self {
+ Self {
+ document: InteralDocument::default(),
+ name,
layer_data: vec![(vec![], LayerData { selected: false, expanded: true })].into_iter().collect(),
}
}
diff --git a/core/editor/src/document/document_message_handler.rs b/core/editor/src/document/document_message_handler.rs
index 70ccc600..0dcc23bb 100644
--- a/core/editor/src/document/document_message_handler.rs
+++ b/core/editor/src/document/document_message_handler.rs
@@ -16,6 +16,9 @@ pub enum DocumentMessage {
ToggleLayerVisibility(Vec),
ToggleLayerExpansion(Vec),
SelectDocument(usize),
+ NewDocument,
+ NextDocument,
+ PrevDocument,
ExportDocument,
RenderDocument,
Undo,
@@ -85,6 +88,51 @@ impl MessageHandler for DocumentMessageHandler {
SelectDocument(id) => {
assert!(id < self.documents.len(), "Tried to select a document that was not initialized");
self.active_document = id;
+ responses.push_back(FrontendMessage::SetActiveDocument { document_index: self.active_document }.into());
+ responses.push_back(
+ FrontendMessage::UpdateCanvas {
+ document: self.active_document_mut().document.render_root(),
+ }
+ .into(),
+ );
+ }
+ NewDocument => {
+ self.active_document = self.documents.len();
+ let new_document = Document::with_name(format!("Untitled Document {}", self.active_document + 1));
+ self.documents.push(new_document);
+ responses.push_back(
+ FrontendMessage::NewDocument {
+ document_name: self.active_document().name.clone(),
+ }
+ .into(),
+ );
+ responses.push_back(FrontendMessage::SetActiveDocument { document_index: self.active_document }.into());
+ responses.push_back(
+ FrontendMessage::UpdateCanvas {
+ document: self.active_document_mut().document.render_root(),
+ }
+ .into(),
+ );
+ }
+ NextDocument => {
+ self.active_document = (self.active_document + 1) % self.documents.len();
+ responses.push_back(FrontendMessage::SetActiveDocument { document_index: self.active_document }.into());
+ responses.push_back(
+ FrontendMessage::UpdateCanvas {
+ document: self.active_document_mut().document.render_root(),
+ }
+ .into(),
+ );
+ }
+ PrevDocument => {
+ self.active_document = (self.active_document + self.documents.len() - 1) % self.documents.len();
+ responses.push_back(FrontendMessage::SetActiveDocument { document_index: self.active_document }.into());
+ responses.push_back(
+ FrontendMessage::UpdateCanvas {
+ document: self.active_document_mut().document.render_root(),
+ }
+ .into(),
+ );
}
ExportDocument => responses.push_back(
FrontendMessage::ExportDocument {
@@ -160,9 +208,9 @@ impl MessageHandler for DocumentMessageHandler {
}
fn actions(&self) -> ActionList {
if self.active_document().layer_data.values().any(|data| data.selected) {
- actions!(DocumentMessageDiscriminant; Undo, DeleteSelectedLayers, RenderDocument, ExportDocument)
+ actions!(DocumentMessageDiscriminant; Undo, DeleteSelectedLayers, RenderDocument, ExportDocument, NewDocument, NextDocument, PrevDocument)
} else {
- actions!(DocumentMessageDiscriminant; Undo, RenderDocument, ExportDocument)
+ actions!(DocumentMessageDiscriminant; Undo, RenderDocument, ExportDocument, NewDocument, NextDocument, PrevDocument)
}
}
}
diff --git a/core/editor/src/frontend/frontend_message_handler.rs b/core/editor/src/frontend/frontend_message_handler.rs
index 45e5d9cd..7d574a20 100644
--- a/core/editor/src/frontend/frontend_message_handler.rs
+++ b/core/editor/src/frontend/frontend_message_handler.rs
@@ -11,6 +11,8 @@ pub enum FrontendMessage {
CollapseFolder { path: Vec },
ExpandFolder { path: Vec, children: Vec },
SetActiveTool { tool_name: String },
+ SetActiveDocument { document_index: usize },
+ NewDocument { document_name: String },
UpdateCanvas { document: String },
ExportDocument { document: String },
EnableTextInput,
@@ -38,6 +40,7 @@ impl MessageHandler for FrontendMessageHandler {
CollapseFolder,
ExpandFolder,
SetActiveTool,
+ NewDocument,
UpdateCanvas,
EnableTextInput,
DisableTextInput,
diff --git a/core/editor/src/input/input_mapper.rs b/core/editor/src/input/input_mapper.rs
index 565453ff..5806110f 100644
--- a/core/editor/src/input/input_mapper.rs
+++ b/core/editor/src/input/input_mapper.rs
@@ -161,6 +161,8 @@ impl Default for Mapping {
entry! {action=DocumentMessage::DeleteSelectedLayers, key_down=KeyBackspace},
entry! {action=DocumentMessage::ExportDocument, key_down=KeyS, modifiers=[KeyControl, KeyShift]},
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
+ entry! {action=DocumentMessage::NewDocument, key_down=KeyN, modifiers=[KeyShift]},
+ entry! {action=DocumentMessage::NextDocument, key_down=KeyTab, modifiers=[KeyShift]},
// Global Actions
entry! {action=GlobalMessage::LogInfo, key_down=Key1},
entry! {action=GlobalMessage::LogDebug, key_down=Key2},
diff --git a/core/editor/src/input/keyboard.rs b/core/editor/src/input/keyboard.rs
index 87fc83a3..ca9e2fd5 100644
--- a/core/editor/src/input/keyboard.rs
+++ b/core/editor/src/input/keyboard.rs
@@ -59,6 +59,7 @@ pub enum Key {
KeyBackspace,
KeyAlt,
KeyEscape,
+ KeyTab,
// This has to be the last element in the enum.
NumKeys,