mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Rework wasm initialization and reduce global state (#379)
* wasm: do the async initialization only once This allows the rest of the app to access wasm synchronously. This allows removing of a global. * provide the wasm via vue provide/inject. There's still code directly accessing the wasm. That will be changed later. * MenuBarInput: use injected wasm instead of the global instance * Let the App handle event listeners * move stateful modules into state/ * state/fullscreen: create per instance * App: load the initial document list on mount. This got lost a few commits ago. Now it's back. * state/dialog: create per instance * util/input: remove dependency on global dialog instance * state/documents: create per instance * reponse-handler: move into EditorWasm * comingSoon: move into dialog * wasm: allow instantiating multiple editors * input handlers: do not look at canvases outside the mounted App * input: listen on the container instead of the window when possible * - removed proxy from wasm-loader - integrated with js-dispatcher - state functions to classes - integrated some upstream changes * fix errors caused by merge * Getting closer: - added global state to track all instances - fix fullscreen close trigger - wasm-loader is statefull - panic across instanes * - fix outline while using editor - removed circular import rule - added editorInstance to js message constructor * - changed input handler to a class - still need a better way of handeling it in App.vue * - fixed single instance of inputManager to weakmap * - fix no-explicit-any in a few places - removed global state from input.ts * simplified two long lines * removed global state * removed $data from App * add mut self to functions in api.rs * Update Workspace.vue remove outdated import * fixed missing import * Changes throughout code review; note this causes some bugs to be fixed in a later commit * PR review round 1 * - fix coming soon bugs - changed folder structure * moved declaration to .d.ts * - changed from classes to functions - moved decs back to app.vue * removed need to export js function to rust * changed folder structure * fixed indentation breaking multiline strings * Fix eslint rule to whitelist @/../ * Simplify strip-indents implementation * replace type assertions with better annotations or proper runtime checks * Small tweaks and code rearranging improvements after second code review pass * maybe fix mouse events * Add back preventDefault for mouse scroll * code review round 2 * Comment improvements * -removed runtime checks - fixed layers not showing * - extened proxy to cover classes - stopped multiple panics from logging - Stop wasm-bindgen from mut ref counting our struct * cleaned up messageConstructors exports * Fix input and fullscreen regressions Co-authored-by: Max Fisher <maxmfishernj@gmail.com> Co-authored-by: mfish33 <32677537+mfish33@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
parent
3c1ed1a235
commit
0aefa1bb07
@@ -3,14 +3,14 @@
|
||||
<FloatingMenu :type="MenuType.Dialog" :direction="MenuDirection.Center">
|
||||
<LayoutRow>
|
||||
<LayoutCol :class="'icon-column'">
|
||||
<!-- `dialog.icon` class exists to provide special sizing in CSS to specific icons -->
|
||||
<IconLabel :icon="dialog.icon" :class="dialog.icon.toLowerCase()" />
|
||||
<!-- `dialog.state.icon` class exists to provide special sizing in CSS to specific icons -->
|
||||
<IconLabel :icon="dialog.state.icon" :class="dialog.state.icon.toLowerCase()" />
|
||||
</LayoutCol>
|
||||
<LayoutCol :class="'main-column'">
|
||||
<TextLabel :bold="true" :class="'heading'">{{ dialog.heading }}</TextLabel>
|
||||
<TextLabel :class="'details'">{{ dialog.details }}</TextLabel>
|
||||
<LayoutRow :class="'buttons-row'" v-if="dialog.buttons.length > 0">
|
||||
<TextButton v-for="(button, index) in dialog.buttons" :key="index" :title="button.tooltip" :action="button.callback" v-bind="button.props" />
|
||||
<TextLabel :bold="true" :class="'heading'">{{ dialog.state.heading }}</TextLabel>
|
||||
<TextLabel :class="'details'">{{ dialog.state.details }}</TextLabel>
|
||||
<LayoutRow :class="'buttons-row'" v-if="dialog.state.buttons.length > 0">
|
||||
<TextButton v-for="(button, index) in dialog.state.buttons" :key="index" :title="button.tooltip" :action="button.callback" v-bind="button.props" />
|
||||
</LayoutRow>
|
||||
</LayoutCol>
|
||||
</LayoutRow>
|
||||
@@ -79,8 +79,6 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { dismissDialog } from "@/utilities/dialog";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
import FloatingMenu, { MenuDirection, MenuType } from "@/components/widgets/floating-menus/FloatingMenu.vue";
|
||||
@@ -100,7 +98,7 @@ export default defineComponent({
|
||||
},
|
||||
methods: {
|
||||
dismiss() {
|
||||
dismissDialog();
|
||||
this.dialog.dismissDialog();
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<span class="entry-label">{{ entry.label }}</span>
|
||||
|
||||
<IconLabel v-if="entry.shortcutRequiresLock && !fullscreen.keyboardLocked" :icon="'Info'" :title="keyboardLockInfoMessage" />
|
||||
<IconLabel v-if="entry.shortcutRequiresLock && !fullscreen.state.keyboardLocked" :icon="'Info'" :title="keyboardLockInfoMessage" />
|
||||
<UserInputLabel v-else-if="entry.shortcut && entry.shortcut.length" :inputKeys="[entry.shortcut]" />
|
||||
|
||||
<div class="submenu-arrow" v-if="entry.children && entry.children.length"></div>
|
||||
@@ -132,7 +132,6 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { keyboardLockApiSupported } from "@/utilities/fullscreen";
|
||||
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
|
||||
|
||||
import FloatingMenu, { MenuDirection, MenuType } from "@/components/widgets/floating-menus/FloatingMenu.vue";
|
||||
@@ -265,7 +264,7 @@ const MenuList = defineComponent({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
keyboardLockInfoMessage: keyboardLockApiSupported() ? KEYBOARD_LOCK_USE_FULLSCREEN : KEYBOARD_LOCK_SWITCH_BROWSER,
|
||||
keyboardLockInfoMessage: this.fullscreen.keyboardLockApiSupported ? KEYBOARD_LOCK_USE_FULLSCREEN : KEYBOARD_LOCK_SWITCH_BROWSER,
|
||||
SeparatorDirection,
|
||||
SeparatorType,
|
||||
MenuDirection,
|
||||
|
||||
@@ -53,117 +53,116 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { comingSoon } from "@/utilities/errors";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
import { ApplicationPlatform } from "@/components/window/MainWindow.vue";
|
||||
import MenuList, { MenuListEntry, MenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
|
||||
import { MenuDirection } from "@/components/widgets/floating-menus/FloatingMenu.vue";
|
||||
import { EditorState } from "@/state/wasm-loader";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
const menuEntries: MenuListEntries = [
|
||||
{
|
||||
label: "File",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "New", icon: "File", shortcut: ["Ctrl", "N"], shortcutRequiresLock: true, action: async () => (await wasm).new_document() },
|
||||
{ label: "Open…", shortcut: ["Ctrl", "O"], action: async () => (await wasm).open_document() },
|
||||
{
|
||||
label: "Open Recent",
|
||||
shortcut: ["Ctrl", "⇧", "O"],
|
||||
children: [
|
||||
[{ label: "Reopen Last Closed", shortcut: ["Ctrl", "⇧", "T"], 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" },
|
||||
function makeMenuEntries(editor: EditorState): MenuListEntries {
|
||||
return [
|
||||
{
|
||||
label: "File",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "New", icon: "File", shortcut: ["Ctrl", "N"], shortcutRequiresLock: true, action: async () => editor.instance.new_document() },
|
||||
{ label: "Open…", shortcut: ["Ctrl", "O"], action: async () => editor.instance.open_document() },
|
||||
{
|
||||
label: "Open Recent",
|
||||
shortcut: ["Ctrl", "⇧", "O"],
|
||||
children: [
|
||||
[{ label: "Reopen Last Closed", shortcut: ["Ctrl", "⇧", "T"], 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: ["Ctrl", "W"], shortcutRequiresLock: true, action: async () => editor.instance.close_active_document_with_confirmation() },
|
||||
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"], action: async () => editor.instance.close_all_documents_with_confirmation() },
|
||||
],
|
||||
[
|
||||
{ label: "Save", shortcut: ["Ctrl", "S"], action: async () => editor.instance.save_document() },
|
||||
{ label: "Save As…", shortcut: ["Ctrl", "⇧", "S"], action: async () => editor.instance.save_document() },
|
||||
{ label: "Save All", shortcut: ["Ctrl", "Alt", "S"] },
|
||||
{ label: "Auto-Save", checkbox: true, checked: true },
|
||||
],
|
||||
[
|
||||
{ label: "Import…", shortcut: ["Ctrl", "I"] },
|
||||
{ label: "Export…", shortcut: ["Ctrl", "E"], action: async () => editor.instance.export_document() },
|
||||
],
|
||||
[{ label: "Quit", shortcut: ["Ctrl", "Q"] }],
|
||||
],
|
||||
[
|
||||
{ label: "Close", shortcut: ["Ctrl", "W"], shortcutRequiresLock: true, action: async () => (await wasm).close_active_document_with_confirmation() },
|
||||
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"], action: async () => (await wasm).close_all_documents_with_confirmation() },
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "Undo", shortcut: ["Ctrl", "Z"], action: async () => editor.instance.undo() },
|
||||
{ label: "Redo", shortcut: ["Ctrl", "⇧", "Z"], action: async () => editor.instance.redo() },
|
||||
],
|
||||
[
|
||||
{ label: "Cut", shortcut: ["Ctrl", "X"] },
|
||||
{ label: "Copy", icon: "Copy", shortcut: ["Ctrl", "C"] },
|
||||
{ label: "Paste", icon: "Paste", shortcut: ["Ctrl", "V"] },
|
||||
],
|
||||
],
|
||||
[
|
||||
{ label: "Save", shortcut: ["Ctrl", "S"], action: async () => (await wasm).save_document() },
|
||||
{ label: "Save As…", shortcut: ["Ctrl", "⇧", "S"], action: async () => (await wasm).save_document() },
|
||||
{ label: "Save All", shortcut: ["Ctrl", "Alt", "S"] },
|
||||
{ label: "Auto-Save", checkbox: true, checked: true },
|
||||
],
|
||||
[
|
||||
{ label: "Import…", shortcut: ["Ctrl", "I"] },
|
||||
{ label: "Export…", shortcut: ["Ctrl", "E"], action: async () => (await wasm).export_document() },
|
||||
],
|
||||
[{ label: "Quit", shortcut: ["Ctrl", "Q"] }],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "Undo", shortcut: ["Ctrl", "Z"], action: async () => (await wasm).undo() },
|
||||
{ label: "Redo", shortcut: ["Ctrl", "⇧", "Z"], action: async () => (await wasm).redo() },
|
||||
],
|
||||
[
|
||||
{ label: "Cut", shortcut: ["Ctrl", "X"] },
|
||||
{ label: "Copy", icon: "Copy", shortcut: ["Ctrl", "C"] },
|
||||
{ label: "Paste", icon: "Paste", shortcut: ["Ctrl", "V"] },
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Layer",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "Select All", shortcut: ["Ctrl", "A"], action: async () => (await wasm).select_all_layers() },
|
||||
{ label: "Deselect All", shortcut: ["Ctrl", "Alt", "A"], action: async () => (await wasm).deselect_all_layers() },
|
||||
{
|
||||
label: "Order",
|
||||
children: [
|
||||
[
|
||||
{ label: "Raise To Front", shortcut: ["Ctrl", "Shift", "]"], action: async () => (await wasm).reorder_selected_layers((await wasm).i32_max()) },
|
||||
{ label: "Raise", shortcut: ["Ctrl", "]"], action: async () => (await wasm).reorder_selected_layers(1) },
|
||||
{ label: "Lower", shortcut: ["Ctrl", "["], action: async () => (await wasm).reorder_selected_layers(-1) },
|
||||
{ label: "Lower to Back", shortcut: ["Ctrl", "Shift", "["], action: async () => (await wasm).reorder_selected_layers((await wasm).i32_min()) },
|
||||
},
|
||||
{
|
||||
label: "Layer",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[
|
||||
{ label: "Select All", shortcut: ["Ctrl", "A"], action: async () => editor.instance.select_all_layers() },
|
||||
{ label: "Deselect All", shortcut: ["Ctrl", "Alt", "A"], action: async () => editor.instance.deselect_all_layers() },
|
||||
{
|
||||
label: "Order",
|
||||
children: [
|
||||
[
|
||||
{ label: "Raise To Front", shortcut: ["Ctrl", "Shift", "]"], action: async () => editor.instance.reorder_selected_layers(editor.rawWasm.i32_max()) },
|
||||
{ label: "Raise", shortcut: ["Ctrl", "]"], action: async () => editor.instance.reorder_selected_layers(1) },
|
||||
{ label: "Lower", shortcut: ["Ctrl", "["], action: async () => editor.instance.reorder_selected_layers(-1) },
|
||||
{ label: "Lower to Back", shortcut: ["Ctrl", "Shift", "["], action: async () => editor.instance.reorder_selected_layers(editor.rawWasm.i32_min()) },
|
||||
],
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Document",
|
||||
ref: undefined,
|
||||
children: [[{ label: "Menu entries coming soon" }]],
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
ref: undefined,
|
||||
children: [[{ label: "Menu entries coming soon" }]],
|
||||
},
|
||||
{
|
||||
label: "Help",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[{ label: "About Graphite", action: async () => (await wasm).request_about_graphite_dialog() }],
|
||||
[
|
||||
{ label: "Report a Bug", action: () => window.open("https://github.com/GraphiteEditor/Graphite/issues/new", "_blank") },
|
||||
{ label: "Visit on GitHub", action: () => window.open("https://github.com/GraphiteEditor/Graphite", "_blank") },
|
||||
},
|
||||
{
|
||||
label: "Document",
|
||||
ref: undefined,
|
||||
children: [[{ label: "Menu entries coming soon" }]],
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
ref: undefined,
|
||||
children: [[{ label: "Menu entries coming soon" }]],
|
||||
},
|
||||
{
|
||||
label: "Help",
|
||||
ref: undefined,
|
||||
children: [
|
||||
[{ label: "About Graphite", action: async () => editor.instance.request_about_graphite_dialog() }],
|
||||
[
|
||||
{ label: "Report a Bug", action: () => window.open("https://github.com/GraphiteEditor/Graphite/issues/new", "_blank") },
|
||||
{ label: "Visit on GitHub", action: () => window.open("https://github.com/GraphiteEditor/Graphite", "_blank") },
|
||||
],
|
||||
[{ label: "Debug: Panic (DANGER)", action: async () => editor.rawWasm.intentional_panic() }],
|
||||
],
|
||||
[{ label: "Debug: Panic (DANGER)", action: async () => (await wasm).intentional_panic() }],
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["editor", "dialog"],
|
||||
methods: {
|
||||
setEntryRefs(menuEntry: MenuListEntry, ref: typeof MenuList) {
|
||||
if (ref) menuEntry.ref = ref;
|
||||
@@ -180,9 +179,9 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
ApplicationPlatform,
|
||||
menuEntries,
|
||||
menuEntries: makeMenuEntries(this.editor),
|
||||
MenuDirection,
|
||||
comingSoon,
|
||||
comingSoon: () => this.dialog.comingSoon(),
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
||||
@@ -69,16 +69,13 @@
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { rgbToDecimalRgb, RGB } from "@/utilities/color";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import { subscribeJsMessage } from "@/utilities/js-message-dispatcher";
|
||||
import { UpdateWorkingColors } from "@/utilities/js-messages";
|
||||
|
||||
import ColorPicker from "@/components/widgets/floating-menus/ColorPicker.vue";
|
||||
import FloatingMenu, { MenuDirection, MenuType } from "@/components/widgets/floating-menus/FloatingMenu.vue";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
import { UpdateWorkingColors } from "@/dispatcher/js-messages";
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["editor"],
|
||||
components: {
|
||||
FloatingMenu,
|
||||
ColorPicker,
|
||||
@@ -115,7 +112,7 @@ export default defineComponent({
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
|
||||
color = rgbToDecimalRgb(this.primaryColor);
|
||||
(await wasm).update_primary_color(color.r, color.g, color.b, color.a);
|
||||
this.editor.instance.update_primary_color(color.r, color.g, color.b, color.a);
|
||||
},
|
||||
|
||||
async updateSecondaryColor() {
|
||||
@@ -124,7 +121,7 @@ export default defineComponent({
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
|
||||
color = rgbToDecimalRgb(this.secondaryColor);
|
||||
(await wasm).update_secondary_color(color.r, color.g, color.b, color.a);
|
||||
this.editor.instance.update_secondary_color(color.r, color.g, color.b, color.a);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@@ -136,7 +133,7 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
subscribeJsMessage(UpdateWorkingColors, (updateWorkingColors) => {
|
||||
this.editor.dispatcher.subscribeJsMessage(UpdateWorkingColors, (updateWorkingColors) => {
|
||||
const { primary, secondary } = updateWorkingColors;
|
||||
|
||||
this.primaryColor = primary.toRgba();
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { comingSoon } from "@/utilities/errors";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import { WidgetRow, SeparatorType, IconButtonWidget } from "@/components/widgets/widgets";
|
||||
|
||||
import Separator from "@/components/widgets/separators/Separator.vue";
|
||||
@@ -40,9 +38,8 @@ import IconButton from "@/components/widgets/buttons/IconButton.vue";
|
||||
import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue";
|
||||
import NumberInput from "@/components/widgets/inputs/NumberInput.vue";
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
export default defineComponent({
|
||||
inject: ["editor", "dialog"],
|
||||
props: {
|
||||
activeTool: { type: String },
|
||||
activeToolOptions: { type: Object as PropType<Record<string, object>> },
|
||||
@@ -50,10 +47,10 @@ export default defineComponent({
|
||||
methods: {
|
||||
async updateToolOptions(path: string[], newValue: number) {
|
||||
this.setToolOption(path, newValue);
|
||||
(await wasm).set_tool_options(this.activeTool || "", this.activeToolOptions);
|
||||
this.editor.instance.set_tool_options(this.activeTool || "", this.activeToolOptions);
|
||||
},
|
||||
async sendToolMessage(message: string | object) {
|
||||
(await wasm).send_tool_message(this.activeTool || "", message);
|
||||
this.editor.instance.send_tool_message(this.activeTool || "", message);
|
||||
},
|
||||
// Traverses the given path and returns the direct parent of the option
|
||||
getRecordContainingOption(optionPath: string[]): Record<string, number> {
|
||||
@@ -87,7 +84,7 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
comingSoon();
|
||||
this.dialog.comingSoon();
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@@ -132,11 +129,11 @@ export default defineComponent({
|
||||
|
||||
{ kind: "Separator", props: { type: SeparatorType.Section } },
|
||||
|
||||
{ kind: "IconButton", tooltip: "Boolean Union", callback: () => comingSoon(197), props: { icon: "BooleanUnion", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Subtract Front", callback: () => comingSoon(197), props: { icon: "BooleanSubtractFront", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Subtract Back", callback: () => comingSoon(197), props: { icon: "BooleanSubtractBack", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Intersect", callback: () => comingSoon(197), props: { icon: "BooleanIntersect", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Difference", callback: () => comingSoon(197), props: { icon: "BooleanDifference", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Union", callback: () => this.dialog.comingSoon(197), props: { icon: "BooleanUnion", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Subtract Front", callback: () => this.dialog.comingSoon(197), props: { icon: "BooleanSubtractFront", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Subtract Back", callback: () => this.dialog.comingSoon(197), props: { icon: "BooleanSubtractBack", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Intersect", callback: () => this.dialog.comingSoon(197), props: { icon: "BooleanIntersect", size: 24 } },
|
||||
{ kind: "IconButton", tooltip: "Boolean Difference", callback: () => this.dialog.comingSoon(197), props: { icon: "BooleanDifference", size: 24 } },
|
||||
|
||||
{ kind: "Separator", props: { type: SeparatorType.Related } },
|
||||
|
||||
@@ -157,7 +154,6 @@ export default defineComponent({
|
||||
return {
|
||||
toolOptionsWidgets,
|
||||
SeparatorType,
|
||||
comingSoon,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
||||
Reference in New Issue
Block a user