Menu bar definition in backend (#681)

* initial menu layout working

* removed api.rs functions

* no action shortcut for no-op

* code review

* nitpicks
This commit is contained in:
mfish33
2022-06-18 18:46:23 -06:00
committed by Keavon Chambers
parent 9bd27ec3f8
commit 5d6d2b22bc
35 changed files with 781 additions and 475 deletions

View File

@@ -2,7 +2,7 @@
<template>
<div class="widget-layout">
<component :is="layoutRowType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layout.layout_target" v-for="(layoutRow, index) in layout.layout" :key="index" />
<component :is="LayoutGroupType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layout.layout_target" v-for="(layoutRow, index) in layout.layout" :key="index" />
</div>
</template>
@@ -18,7 +18,7 @@
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { isWidgetColumn, isWidgetRow, isWidgetSection, LayoutRow, WidgetLayout } from "@/wasm-communication/messages";
import { isWidgetColumn, isWidgetRow, isWidgetSection, LayoutGroup, WidgetLayout } from "@/wasm-communication/messages";
import WidgetSection from "@/components/widgets/groups/WidgetSection.vue";
import WidgetRow from "@/components/widgets/WidgetRow.vue";
@@ -28,7 +28,7 @@ export default defineComponent({
layout: { type: Object as PropType<WidgetLayout>, required: true },
},
methods: {
layoutRowType(layoutRow: LayoutRow): unknown {
LayoutGroupType(layoutRow: LayoutGroup): unknown {
if (isWidgetColumn(layoutRow)) return WidgetRow;
if (isWidgetRow(layoutRow)) return WidgetRow;
if (isWidgetSection(layoutRow)) return WidgetSection;

View File

@@ -7,7 +7,7 @@
<TextLabel :bold="true">{{ widgetData.name }}</TextLabel>
</button>
<LayoutCol class="body" v-if="expanded">
<component :is="layoutRowType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layoutTarget" v-for="(layoutRow, index) in widgetData.layout" :key="index"></component>
<component :is="layoutGroupType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layoutTarget" v-for="(layoutRow, index) in widgetData.layout" :key="index"></component>
</LayoutCol>
</LayoutCol>
</template>
@@ -72,7 +72,7 @@
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { isWidgetRow, isWidgetSection, LayoutRow as LayoutSystemRow, WidgetSection as WidgetSectionFromJsMessages } from "@/wasm-communication/messages";
import { isWidgetRow, isWidgetSection, LayoutGroup, WidgetSection as WidgetSectionFromJsMessages } from "@/wasm-communication/messages";
import LayoutCol from "@/components/layout/LayoutCol.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
@@ -96,9 +96,9 @@ const WidgetSection = defineComponent({
updateLayout(widgetId: bigint, value: unknown) {
this.editor.instance.update_layout(this.layoutTarget, widgetId, value);
},
layoutRowType(layoutRow: LayoutSystemRow): unknown {
if (isWidgetRow(layoutRow)) return WidgetRow;
if (isWidgetSection(layoutRow)) return WidgetSection;
layoutGroupType(layoutGroup: LayoutGroup): unknown {
if (isWidgetRow(layoutGroup)) return WidgetRow;
if (isWidgetSection(layoutGroup)) return WidgetSection;
throw new Error("Layout row type does not exist");
},

View File

@@ -72,151 +72,42 @@
<script lang="ts">
import { defineComponent } from "vue";
import { Editor } from "@/wasm-communication/editor";
import { MenuEntry, UpdateMenuBarLayout } from "@/wasm-communication/messages";
import MenuList, { MenuListEntry, MenuListEntries } from "@/components/floating-menus/MenuList.vue";
import MenuList, { MenuListEntry } from "@/components/floating-menus/MenuList.vue";
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
function makeEntries(editor: Editor): MenuListEntries {
return [
{
label: "File",
ref: undefined,
children: [
[
{ label: "New…", icon: "File", shortcut: ["KeyControl", "KeyN"], shortcutRequiresLock: true, action: (): void => editor.instance.request_new_document_dialog() },
{ label: "Open…", shortcut: ["KeyControl", "KeyO"], action: (): void => editor.instance.open_document() },
{
label: "Open Recent",
shortcut: ["KeyControl", "KeyShift", "KeyO"],
action: (): void => undefined,
children: [
[{ label: "Reopen Last Closed", shortcut: ["KeyControl", "KeyShift", "KeyT"], shortcutRequiresLock: true }, { label: "Clear Recently Opened" }],
[
{ label: "Some Recent File.gdd" },
{ label: "Another Recent File.gdd" },
{ label: "An Older File.gdd" },
{ label: "Some Other Older File.gdd" },
{ label: "Yet Another Older File.gdd" },
],
],
},
],
[
{ label: "Close", shortcut: ["KeyControl", "KeyW"], shortcutRequiresLock: true, action: async (): Promise<void> => editor.instance.close_active_document_with_confirmation() },
{ label: "Close All", shortcut: ["KeyControl", "KeyAlt", "KeyW"], action: async (): Promise<void> => editor.instance.close_all_documents_with_confirmation() },
],
[
{ label: "Save", shortcut: ["KeyControl", "KeyS"], action: async (): Promise<void> => editor.instance.save_document() },
{ label: "Save As…", shortcut: ["KeyControl", "KeyShift", "KeyS"], action: async (): Promise<void> => editor.instance.save_document() },
{ label: "Save All", shortcut: ["KeyControl", "KeyAlt", "KeyS"] },
{ label: "Auto-Save", icon: "CheckboxChecked" },
],
[
{ label: "Import…", shortcut: ["KeyControl", "KeyI"] },
{ label: "Export…", shortcut: ["KeyControl", "KeyE"], action: async (): Promise<void> => editor.instance.export_document() },
],
[{ label: "Quit", shortcut: ["KeyControl", "KeyQ"] }],
],
},
{
label: "Edit",
ref: undefined,
children: [
[
{ label: "Undo", shortcut: ["KeyControl", "KeyZ"], action: async (): Promise<void> => editor.instance.undo() },
{ label: "Redo", shortcut: ["KeyControl", "KeyShift", "KeyZ"], action: async (): Promise<void> => editor.instance.redo() },
],
[
{ label: "Cut", shortcut: ["KeyControl", "KeyX"], action: async (): Promise<void> => editor.instance.cut() },
{ label: "Copy", icon: "Copy", shortcut: ["KeyControl", "KeyC"], action: async (): Promise<void> => editor.instance.copy() },
// TODO: Fix this
// { label: "Paste", icon: "Paste", shortcut: ["KeyControl", "KeyV"], action: async (): Promise<void> => editor.instance.paste() },
],
],
},
{
label: "Layer",
ref: undefined,
children: [
[
{ label: "Select All", shortcut: ["KeyControl", "KeyA"], action: async (): Promise<void> => editor.instance.select_all_layers() },
{ label: "Deselect All", shortcut: ["KeyControl", "KeyAlt", "KeyA"], action: async (): Promise<void> => editor.instance.deselect_all_layers() },
{
label: "Order",
action: (): void => undefined,
children: [
[
{
label: "Raise To Front",
shortcut: ["KeyControl", "KeyShift", "KeyLeftBracket"],
action: async (): Promise<void> => editor.instance.reorder_selected_layers(editor.instance.i32_max()),
},
{ label: "Raise", shortcut: ["KeyControl", "KeyRightBracket"], action: async (): Promise<void> => editor.instance.reorder_selected_layers(1) },
{ label: "Lower", shortcut: ["KeyControl", "KeyLeftBracket"], action: async (): Promise<void> => editor.instance.reorder_selected_layers(-1) },
{
label: "Lower to Back",
shortcut: ["KeyControl", "KeyShift", "KeyRightBracket"],
action: async (): Promise<void> => editor.instance.reorder_selected_layers(editor.instance.i32_min()),
},
],
],
},
],
],
},
{
label: "Document",
ref: undefined,
children: [[{ label: "Menu entries coming soon" }]],
},
{
label: "View",
ref: undefined,
children: [
[
{
label: "Show/Hide Node Graph (In Development)",
action: async (): Promise<void> => editor.instance.toggle_node_graph_visibility(),
},
],
],
},
{
label: "Help",
ref: undefined,
children: [
[
{
label: "About Graphite",
action: async (): Promise<void> => editor.instance.request_about_graphite_dialog(),
},
],
[
{ label: "Report a Bug", action: (): unknown => window.open("https://github.com/GraphiteEditor/Graphite/issues/new", "_blank") },
{ label: "Visit on GitHub", action: (): unknown => window.open("https://github.com/GraphiteEditor/Graphite", "_blank") },
],
[
{
label: "Debug: Set Log Level",
action: (): void => undefined,
children: [
[
{ label: "Log Level Info", action: async (): Promise<void> => editor.instance.log_level_info(), shortcut: ["Key1"] },
{ label: "Log Level Debug", action: async (): Promise<void> => editor.instance.log_level_debug(), shortcut: ["Key2"] },
{ label: "Log Level Trace", action: async (): Promise<void> => editor.instance.log_level_trace(), shortcut: ["Key3"] },
],
],
},
{ label: "Debug: Panic (DANGER)", action: async (): Promise<void> => editor.instance.intentional_panic() },
],
],
},
];
}
const LOCK_REQUIRING_SHORTCUTS = [
["KeyControl", "KeyN"],
["KeyControl", "KeyShift", "KeyT"],
["KeyControl", "KeyW"],
];
type FrontendMenuColumn = {
label: string;
children: FrontendMenuEntry[][];
};
type FrontendMenuEntry = Omit<MenuEntry, "action" | "children"> & { shortcutRequiresLock: boolean | undefined; action: () => void; children: FrontendMenuEntry[][] | undefined };
export default defineComponent({
inject: ["editor"],
mounted() {
this.editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => {
const shortcutRequiresLock = (shortcut: string[]): boolean => LOCK_REQUIRING_SHORTCUTS.some((lockKeyCombo) => shortcut.every((shortcutKey, index) => shortcutKey === lockKeyCombo[index]));
const menuEntryToFrontendMenuEntry = (subLayout: MenuEntry[][]): FrontendMenuEntry[][] =>
subLayout.map((group) =>
group.map((entry) => ({
...entry,
children: entry.children ? menuEntryToFrontendMenuEntry(entry.children) : undefined,
action: (): void => this.editor.instance.update_layout(updateMenuBarLayout.layout_target, entry.action.widget_id, undefined),
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut) : undefined,
}))
);
this.entries = updateMenuBarLayout.layout.map((column) => ({ ...column, children: menuEntryToFrontendMenuEntry(column.children) }));
});
},
methods: {
onClick(menuEntry: MenuListEntry, target: EventTarget | null) {
// Focus the target so that keyboard inputs are sent to the dropdown
@@ -236,7 +127,7 @@ export default defineComponent({
},
data() {
return {
entries: makeEntries(this.editor),
entries: [] as FrontendMenuColumn[],
open: false,
};
},