Move layouts definitions to backend and fix Firefox overlay scrollbars (#647)

* Fix two-axis scrollbars in scrollable regions on Firefox

* Move Document Mode dropdown to the backend; and related code cleanup

* Port the Layer Tree options bar layout to the backend

* Port the tool shelf to the backend

* Clean up initialization and wasm wrapper

* Fix crash

* Fix missing document bar

* Remove unused functions in api.rs

* Code review

* Tool initalisation

* Remove some frontend functions

* Initalise -> Init so en-US/GB doesn't have to matter :)

* Remove blend_mode and opacity from LayerPanelEntry

Co-authored-by: 0hypercube <0hypercube@gmail.com>
This commit is contained in:
Keavon Chambers
2022-05-17 13:12:52 -07:00
co-authored by 0hypercube
parent e7d63276ad
commit 29e00e488b
50 changed files with 1034 additions and 978 deletions
+39 -112
View File
@@ -1,58 +1,18 @@
<template>
<LayoutCol class="document">
<LayoutRow class="options-bar" :scrollableX="true">
<LayoutRow class="left side">
<DropdownInput :menuEntries="documentModeEntries" v-model:selectedIndex="documentModeSelectionIndex" :drawIcon="true" />
<Separator :type="'Section'" />
<WidgetLayout :layout="toolOptionsLayout" class="tool-options" />
</LayoutRow>
<WidgetLayout :layout="documentModeLayout" />
<Separator :type="'Section'" />
<WidgetLayout :layout="toolOptionsLayout" />
<LayoutRow class="spacer"></LayoutRow>
<WidgetLayout :layout="documentBarLayout" class="right side document-bar" />
<WidgetLayout :layout="documentBarLayout" />
</LayoutRow>
<LayoutRow class="shelf-and-viewport">
<LayoutCol class="shelf">
<LayoutCol class="tools" :scrollableY="true">
<ShelfItemInput icon="GeneralSelectTool" title="Select Tool (V)" :active="activeTool === 'Select'" :action="() => selectTool('Select')" />
<ShelfItemInput icon="GeneralArtboardTool" title="Artboard Tool" :active="activeTool === 'Artboard'" :action="() => selectTool('Artboard')" />
<ShelfItemInput icon="GeneralNavigateTool" title="Navigate Tool (Z)" :active="activeTool === 'Navigate'" :action="() => selectTool('Navigate')" />
<ShelfItemInput icon="GeneralEyedropperTool" title="Eyedropper Tool (I)" :active="activeTool === 'Eyedropper'" :action="() => selectTool('Eyedropper')" />
<ShelfItemInput icon="GeneralFillTool" title="Fill Tool (F)" :active="activeTool === 'Fill'" :action="() => selectTool('Fill')" />
<ShelfItemInput icon="GeneralGradientTool" title="Gradient Tool (H)" :active="activeTool === 'Gradient'" :action="() => selectTool('Gradient')" />
<Separator :type="'Section'" :direction="'Vertical'" />
<ShelfItemInput icon="VectorPathTool" title="Path Tool (A)" :active="activeTool === 'Path'" :action="() => selectTool('Path')" />
<ShelfItemInput icon="VectorPenTool" title="Pen Tool (P)" :active="activeTool === 'Pen'" :action="() => selectTool('Pen')" />
<ShelfItemInput icon="VectorFreehandTool" title="Freehand Tool (N)" :active="activeTool === 'Freehand'" :action="() => selectTool('Freehand')" />
<ShelfItemInput icon="VectorSplineTool" title="Spline Tool" :active="activeTool === 'Spline'" :action="() => selectTool('Spline')" />
<ShelfItemInput icon="VectorLineTool" title="Line Tool (L)" :active="activeTool === 'Line'" :action="() => selectTool('Line')" />
<ShelfItemInput icon="VectorRectangleTool" title="Rectangle Tool (M)" :active="activeTool === 'Rectangle'" :action="() => selectTool('Rectangle')" />
<ShelfItemInput icon="VectorEllipseTool" title="Ellipse Tool (E)" :active="activeTool === 'Ellipse'" :action="() => selectTool('Ellipse')" />
<ShelfItemInput icon="VectorShapeTool" title="Shape Tool (Y)" :active="activeTool === 'Shape'" :action="() => selectTool('Shape')" />
<ShelfItemInput icon="VectorTextTool" title="Text Tool (T)" :active="activeTool === 'Text'" :action="() => selectTool('Text')" />
<Separator :type="'Section'" :direction="'Vertical'" />
<ShelfItemInput icon="RasterBrushTool" title="Coming Soon: Brush Tool (B)" :active="activeTool === 'Brush'" :action="() => (dialog.comingSoon(), false) && selectTool('Brush')" />
<ShelfItemInput icon="RasterHealTool" title="Coming Soon: Heal Tool (J)" :active="activeTool === 'Heal'" :action="() => (dialog.comingSoon(), false) && selectTool('Heal')" />
<ShelfItemInput icon="RasterCloneTool" title="Coming Soon: Clone Tool (C)" :active="activeTool === 'Clone'" :action="() => (dialog.comingSoon(), false) && selectTool('Clone')" />
<ShelfItemInput icon="RasterPatchTool" title="Coming Soon: Patch Tool" :active="activeTool === 'Patch'" :action="() => (dialog.comingSoon(), false) && selectTool('Patch')" />
<ShelfItemInput
icon="RasterDetailTool"
title="Coming Soon: Detail Tool (D)"
:active="activeTool === 'Detail'"
:action="() => (dialog.comingSoon(), false) && selectTool('Detail')"
/>
<ShelfItemInput
icon="RasterRelightTool"
title="Coming Soon: Relight Tool (O)"
:active="activeTool === 'Relight'"
:action="() => (dialog.comingSoon(), false) && selectTool('Relight')"
/>
<WidgetLayout :layout="toolShelfLayout" />
</LayoutCol>
<LayoutCol class="spacer"></LayoutCol>
@@ -128,13 +88,7 @@
.options-bar {
height: 32px;
flex: 0 0 auto;
.side {
height: 100%;
flex: 0 0 auto;
align-items: center;
margin: 0 4px;
}
margin: 0 4px;
.spacer {
min-width: 40px;
@@ -148,7 +102,7 @@
.tools {
flex: 0 1 auto;
.shelf-item-input[title^="Coming Soon"] {
.icon-button[title^="Coming Soon"] {
opacity: 0.25;
transition: opacity 0.25s;
@@ -272,13 +226,11 @@ import {
UpdateDocumentOverlays,
UpdateDocumentScrollbars,
UpdateDocumentRulers,
UpdateActiveTool,
UpdateCanvasZoom,
UpdateCanvasRotation,
ToolName,
UpdateDocumentArtboards,
UpdateMouseCursor,
UpdateDocumentModeLayout,
UpdateToolOptionsLayout,
UpdateToolShelfLayout,
defaultWidgetLayout,
UpdateDocumentBarLayout,
UpdateImageData,
@@ -299,10 +251,6 @@ import { loadDefaultFont, setLoadDefaultFontCallback } from "@/utilities/fonts";
import LayoutCol from "@/components/layout/LayoutCol.vue";
import LayoutRow from "@/components/layout/LayoutRow.vue";
import IconButton from "@/components/widgets/buttons/IconButton.vue";
import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/MenuList.vue";
import DropdownInput from "@/components/widgets/inputs/DropdownInput.vue";
import { RadioEntries } from "@/components/widgets/inputs/RadioInput.vue";
import ShelfItemInput from "@/components/widgets/inputs/ShelfItemInput.vue";
import SwatchPairInput from "@/components/widgets/inputs/SwatchPairInput.vue";
import CanvasRuler from "@/components/widgets/rulers/CanvasRuler.vue";
import PersistentScrollbar from "@/components/widgets/scrollbars/PersistentScrollbar.vue";
@@ -366,9 +314,6 @@ export default defineComponent({
const move = delta < 0 ? 1 : -1;
this.editor.instance.translate_canvas_by_fraction(0, move);
},
selectTool(toolName: string) {
this.editor.instance.select_tool(toolName);
},
swapWorkingColors() {
this.editor.instance.swap_colors();
},
@@ -440,19 +385,6 @@ export default defineComponent({
this.rulerInterval = updateDocumentRulers.interval;
});
this.editor.dispatcher.subscribeJsMessage(UpdateActiveTool, (updateActiveTool) => {
this.activeTool = updateActiveTool.tool_name;
});
this.editor.dispatcher.subscribeJsMessage(UpdateCanvasZoom, (updateCanvasZoom) => {
this.documentZoom = updateCanvasZoom.factor * 100;
});
this.editor.dispatcher.subscribeJsMessage(UpdateCanvasRotation, (updateCanvasRotation) => {
const newRotation = updateCanvasRotation.angle_radians * (180 / Math.PI);
this.documentRotation = (360 + (newRotation % 360)) % 360;
});
this.editor.dispatcher.subscribeJsMessage(UpdateMouseCursor, (updateMouseCursor) => {
this.canvasCursor = updateMouseCursor.cursor;
});
@@ -502,6 +434,10 @@ export default defineComponent({
);
});
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentModeLayout, (updateDocumentModeLayout) => {
this.documentModeLayout = updateDocumentModeLayout;
});
this.editor.dispatcher.subscribeJsMessage(UpdateToolOptionsLayout, (updateToolOptionsLayout) => {
this.toolOptionsLayout = updateToolOptionsLayout;
});
@@ -509,6 +445,11 @@ export default defineComponent({
this.editor.dispatcher.subscribeJsMessage(UpdateDocumentBarLayout, (updateDocumentBarLayout) => {
this.documentBarLayout = updateDocumentBarLayout;
});
this.editor.dispatcher.subscribeJsMessage(UpdateToolShelfLayout, (updateToolShelfLayout) => {
this.toolShelfLayout = updateToolShelfLayout;
});
this.editor.dispatcher.subscribeJsMessage(TriggerViewportResize, this.viewportResize);
this.editor.dispatcher.subscribeJsMessage(UpdateImageData, (updateImageData) => {
@@ -524,7 +465,7 @@ export default defineComponent({
});
});
// Gets metadat populated in `frontend/vue.config.js`. We could potentially move this functionality in a build.rs file.
// Gets metadata populated in `frontend/vue.config.js`. We could potentially move this functionality in a build.rs file.
const loadBuildMetadata = (): void => {
const release = process.env.VUE_APP_RELEASE_SERIES;
let timestamp = "";
@@ -544,64 +485,50 @@ export default defineComponent({
this.editor.instance.populate_build_metadata(release || "", timestamp, hash, branch || "");
};
// TODO(mfish33): Replace with initialization system Issue:#524
// Get initial Document Bar
this.editor.instance.init_document_bar();
setLoadDefaultFontCallback((font: string, data: Uint8Array) => this.editor.instance.on_font_load(font, data, true));
loadBuildMetadata();
},
data() {
const documentModeEntries: SectionsOfMenuListEntries = [
[
{ label: "Design Mode", icon: "ViewportDesignMode" },
{ label: "Select Mode", icon: "ViewportSelectMode", action: (): void => this.dialog.comingSoon(330) },
{ label: "Guide Mode", icon: "ViewportGuideMode", action: (): void => this.dialog.comingSoon(331) },
],
];
const viewModeEntries: RadioEntries = [
{ value: "normal", icon: "ViewModeNormal", tooltip: "View Mode: Normal", action: (): void => this.setViewMode("Normal") },
{ value: "outline", icon: "ViewModeOutline", tooltip: "View Mode: Outline", action: (): void => this.setViewMode("Outline") },
{ value: "pixels", icon: "ViewModePixels", tooltip: "View Mode: Pixels", action: (): void => this.dialog.comingSoon(320) },
];
return {
artworkSvg: "",
artboardSvg: "",
overlaysSvg: "",
// Interactive text editing
textInput: undefined as undefined | HTMLDivElement,
// CSS properties
canvasSvgWidth: "100%",
canvasSvgHeight: "100%",
canvasCursor: "default",
activeTool: "Select" as ToolName,
toolOptionsLayout: defaultWidgetLayout(),
documentBarLayout: defaultWidgetLayout(),
documentModeEntries,
viewModeEntries,
documentModeSelectionIndex: 0,
viewModeIndex: 0,
snappingEnabled: true,
gridEnabled: true,
overlaysEnabled: true,
documentRotation: 0,
documentZoom: 100,
// Scrollbars
scrollbarPos: { x: 0.5, y: 0.5 },
scrollbarSize: { x: 0.5, y: 0.5 },
scrollbarMultiplier: { x: 0, y: 0 },
// Rulers
rulerOrigin: { x: 0, y: 0 },
rulerSpacing: 100,
rulerInterval: 100,
textInput: undefined as undefined | HTMLDivElement,
// Rendered SVG viewport data
artworkSvg: "",
artboardSvg: "",
overlaysSvg: "",
// Layouts
documentModeLayout: defaultWidgetLayout(),
toolOptionsLayout: defaultWidgetLayout(),
documentBarLayout: defaultWidgetLayout(),
toolShelfLayout: defaultWidgetLayout(),
};
},
components: {
LayoutRow,
LayoutCol,
SwatchPairInput,
ShelfItemInput,
Separator,
PersistentScrollbar,
CanvasRuler,
IconButton,
DropdownInput,
WidgetLayout,
},
});