mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
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:
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user