mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 10:58:04 +08:00
Change responses to use classes instead of interfaces (#394)
* ability to mark an open document as unsaved * unsaved detection now being triggered based on layer tree height * Changed responses to use classes instead of interfaces * - rust implementation of unsaved markers - upgraded eslint * updated eslint in package.json * - Renamed GetOpenDocumentsList -> UpdateOpenDocumentsList - is not -> was not * changed hash to current identifier to better reflect its meaning * resolve some merge conflicts * removed console.log statement leftover from debuging * - changed Response to jsMessage - split files - Array<> -> [] * -remove path from UpdateLayer * - remove unused if statements * - comment for reflect-metadata - registerJsMessageHandler -> subscribeJsMessage - readonly message properties - fixed binding filename and comment - toRgb -> toRgba * - newOpacity -> transformer - added comments * MessageMaker -> messageMaker
This commit is contained in:
@@ -238,7 +238,8 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/utilities/response-handler";
|
||||
import { subscribeJsMessage } from "@/utilities/js-message-dispatcher";
|
||||
import { UpdateCanvas, UpdateScrollbars, UpdateRulers, SetActiveTool, SetCanvasZoom, SetCanvasRotation } from "@/utilities/js-messages";
|
||||
import { SeparatorDirection, SeparatorType } from "@/components/widgets/widgets";
|
||||
import { comingSoon } from "@/utilities/errors";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
@@ -332,50 +333,34 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
registerResponseHandler(ResponseType.UpdateCanvas, (responseData: Response) => {
|
||||
const updateData = responseData as UpdateCanvas;
|
||||
if (updateData) this.viewportSvg = updateData.document;
|
||||
subscribeJsMessage(UpdateCanvas, (updateCanvas) => {
|
||||
this.viewportSvg = updateCanvas.document;
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.UpdateScrollbars, (responseData: Response) => {
|
||||
const updateData = responseData as UpdateScrollbars;
|
||||
if (updateData) {
|
||||
this.scrollbarPos = updateData.position;
|
||||
this.scrollbarSize = updateData.size;
|
||||
this.scrollbarMultiplier = updateData.multiplier;
|
||||
}
|
||||
subscribeJsMessage(UpdateScrollbars, (updateScrollbars) => {
|
||||
this.scrollbarPos = updateScrollbars.position;
|
||||
this.scrollbarSize = updateScrollbars.size;
|
||||
this.scrollbarMultiplier = updateScrollbars.multiplier;
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.UpdateRulers, (responseData: Response) => {
|
||||
const updateData = responseData as UpdateRulers;
|
||||
if (updateData) {
|
||||
this.rulerOrigin = updateData.origin;
|
||||
this.rulerSpacing = updateData.spacing;
|
||||
this.rulerInterval = updateData.interval;
|
||||
}
|
||||
subscribeJsMessage(UpdateRulers, (updateRulers) => {
|
||||
this.rulerOrigin = updateRulers.origin;
|
||||
this.rulerSpacing = updateRulers.spacing;
|
||||
this.rulerInterval = updateRulers.interval;
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.SetActiveTool, (responseData: Response) => {
|
||||
const toolData = responseData as SetActiveTool;
|
||||
if (toolData) {
|
||||
this.activeTool = toolData.tool_name;
|
||||
this.activeToolOptions = toolData.tool_options;
|
||||
}
|
||||
subscribeJsMessage(SetActiveTool, (setActiveTool) => {
|
||||
this.activeTool = setActiveTool.tool_name;
|
||||
this.activeToolOptions = setActiveTool.tool_options;
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.SetCanvasZoom, (responseData: Response) => {
|
||||
const updateData = responseData as SetCanvasZoom;
|
||||
if (updateData) {
|
||||
this.documentZoom = updateData.new_zoom * 100;
|
||||
}
|
||||
subscribeJsMessage(SetCanvasZoom, (setCanvasZoom) => {
|
||||
this.documentZoom = setCanvasZoom.new_zoom * 100;
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.SetCanvasRotation, (responseData: Response) => {
|
||||
const updateData = responseData as SetCanvasRotation;
|
||||
if (updateData) {
|
||||
const newRotation = updateData.new_radians * (180 / Math.PI);
|
||||
this.documentRotation = (360 + (newRotation % 360)) % 360;
|
||||
}
|
||||
subscribeJsMessage(SetCanvasRotation, (setCanvasRotation) => {
|
||||
const newRotation = setCanvasRotation.new_radians * (180 / Math.PI);
|
||||
this.documentRotation = (360 + (newRotation % 360)) % 360;
|
||||
});
|
||||
|
||||
window.addEventListener("resize", this.viewportResize);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
v-if="layer.layer_type === LayerType.Folder"
|
||||
v-if="layer.layer_type === LayerTypeOptions.Folder"
|
||||
class="node-connector"
|
||||
:class="{ expanded: layer.layer_data.expanded }"
|
||||
@click.stop="handleNodeConnectorClick(layer.path)"
|
||||
@@ -43,7 +43,7 @@
|
||||
>
|
||||
<div class="layer-thumbnail" v-html="layer.thumbnail"></div>
|
||||
<div class="layer-type-icon">
|
||||
<IconLabel v-if="layer.layer_type === LayerType.Folder" :icon="'NodeTypeFolder'" title="Folder" />
|
||||
<IconLabel v-if="layer.layer_type === LayerTypeOptions.Folder" :icon="'NodeTypeFolder'" title="Folder" />
|
||||
<IconLabel v-else :icon="'NodeTypePath'" title="Path" />
|
||||
</div>
|
||||
<div class="layer-name">
|
||||
@@ -197,7 +197,8 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
import { ResponseType, registerResponseHandler, Response, BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry, LayerType } from "@/utilities/response-handler";
|
||||
import { subscribeJsMessage } from "@/utilities/js-message-dispatcher";
|
||||
import { BlendMode, DisplayFolderTreeStructure, UpdateLayer, LayerPanelEntry, LayerTypeOptions } from "@/utilities/js-messages";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import { SeparatorType } from "@/components/widgets/widgets";
|
||||
|
||||
@@ -214,42 +215,42 @@ import { SectionsOfMenuListEntries } from "@/components/widgets/floating-menus/M
|
||||
|
||||
const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
const blendModeEntries: SectionsOfMenuListEntries = [
|
||||
[{ label: "Normal", value: BlendMode.Normal }],
|
||||
const blendModeEntries: SectionsOfMenuListEntries<BlendMode> = [
|
||||
[{ label: "Normal", value: "normal" }],
|
||||
[
|
||||
{ label: "Multiply", value: BlendMode.Multiply },
|
||||
{ label: "Darken", value: BlendMode.Darken },
|
||||
{ label: "Color Burn", value: BlendMode.ColorBurn },
|
||||
{ label: "Multiply", value: "multiply" },
|
||||
{ label: "Darken", value: "darken" },
|
||||
{ label: "Color Burn", value: "color-burn" },
|
||||
// { label: "Linear Burn", value: "" }, // Not supported by SVG
|
||||
// { label: "Darker Color", value: "" }, // Not supported by SVG
|
||||
],
|
||||
[
|
||||
{ label: "Screen", value: BlendMode.Screen },
|
||||
{ label: "Lighten", value: BlendMode.Lighten },
|
||||
{ label: "Color Dodge", value: BlendMode.ColorDodge },
|
||||
{ label: "Screen", value: "screen" },
|
||||
{ label: "Lighten", value: "lighten" },
|
||||
{ label: "Color Dodge", value: "color-dodge" },
|
||||
// { label: "Linear Dodge (Add)", value: "" }, // Not supported by SVG
|
||||
// { label: "Lighter Color", value: "" }, // Not supported by SVG
|
||||
],
|
||||
[
|
||||
{ label: "Overlay", value: BlendMode.Overlay },
|
||||
{ label: "Soft Light", value: BlendMode.SoftLight },
|
||||
{ label: "Hard Light", value: BlendMode.HardLight },
|
||||
{ label: "Overlay", value: "overlay" },
|
||||
{ label: "Soft Light", value: "soft-light" },
|
||||
{ label: "Hard Light", value: "hard-light" },
|
||||
// { label: "Vivid Light", value: "" }, // Not supported by SVG
|
||||
// { label: "Linear Light", value: "" }, // Not supported by SVG
|
||||
// { label: "Pin Light", value: "" }, // Not supported by SVG
|
||||
// { label: "Hard Mix", value: "" }, // Not supported by SVG
|
||||
],
|
||||
[
|
||||
{ label: "Difference", value: BlendMode.Difference },
|
||||
{ label: "Exclusion", value: BlendMode.Exclusion },
|
||||
{ label: "Difference", value: "difference" },
|
||||
{ label: "Exclusion", value: "exclusion" },
|
||||
// { label: "Subtract", value: "" }, // Not supported by SVG
|
||||
// { label: "Divide", value: "" }, // Not supported by SVG
|
||||
],
|
||||
[
|
||||
{ label: "Hue", value: BlendMode.Hue },
|
||||
{ label: "Saturation", value: BlendMode.Saturation },
|
||||
{ label: "Color", value: BlendMode.Color },
|
||||
{ label: "Luminosity", value: BlendMode.Luminosity },
|
||||
{ label: "Hue", value: "hue" },
|
||||
{ label: "Saturation", value: "saturation" },
|
||||
{ label: "Color", value: "color" },
|
||||
{ label: "Luminosity", value: "luminosity" },
|
||||
],
|
||||
];
|
||||
|
||||
@@ -262,14 +263,14 @@ export default defineComponent({
|
||||
opacityNumberInputDisabled: true,
|
||||
// TODO: replace with BigUint64Array as index
|
||||
layerCache: new Map() as Map<string, LayerPanelEntry>,
|
||||
layers: [] as Array<LayerPanelEntry>,
|
||||
layerDepths: [] as Array<number>,
|
||||
layers: [] as LayerPanelEntry[],
|
||||
layerDepths: [] as number[],
|
||||
selectionRangeStartLayer: undefined as undefined | LayerPanelEntry,
|
||||
selectionRangeEndLayer: undefined as undefined | LayerPanelEntry,
|
||||
opacity: 100,
|
||||
MenuDirection,
|
||||
SeparatorType,
|
||||
LayerType,
|
||||
LayerTypeOptions,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@@ -406,13 +407,10 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
registerResponseHandler(ResponseType.DisplayFolderTreeStructure, (responseData: Response) => {
|
||||
const expandData = responseData as DisplayFolderTreeStructure;
|
||||
if (!expandData) return;
|
||||
|
||||
const path = [] as Array<bigint>;
|
||||
this.layers = [] as Array<LayerPanelEntry>;
|
||||
function recurse(folder: DisplayFolderTreeStructure, layers: Array<LayerPanelEntry>, cache: Map<string, LayerPanelEntry>) {
|
||||
subscribeJsMessage(DisplayFolderTreeStructure, (displayFolderTreeStructure) => {
|
||||
const path = [] as bigint[];
|
||||
this.layers = [] as LayerPanelEntry[];
|
||||
function recurse(folder: DisplayFolderTreeStructure, layers: LayerPanelEntry[], cache: Map<string, LayerPanelEntry>) {
|
||||
folder.children.forEach((item) => {
|
||||
// TODO: fix toString
|
||||
path.push(BigInt(item.layerId.toString()));
|
||||
@@ -422,21 +420,21 @@ export default defineComponent({
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
recurse(expandData, this.layers, this.layerCache);
|
||||
recurse(displayFolderTreeStructure, this.layers, this.layerCache);
|
||||
});
|
||||
|
||||
registerResponseHandler(ResponseType.UpdateLayer, (responseData) => {
|
||||
const updateData = responseData as UpdateLayer;
|
||||
if (updateData) {
|
||||
const responsePath = updateData.path;
|
||||
const responseLayer = updateData.data;
|
||||
subscribeJsMessage(UpdateLayer, (updateLayer) => {
|
||||
const responsePath = updateLayer.data.path;
|
||||
const responseLayer = updateLayer.data;
|
||||
|
||||
const layer = this.layerCache.get(responsePath.toString());
|
||||
if (layer) Object.assign(this.layerCache.get(responsePath.toString()), responseLayer);
|
||||
else this.layerCache.set(responsePath.toString(), responseLayer);
|
||||
this.setBlendModeForSelectedLayers();
|
||||
this.setOpacityForSelectedLayers();
|
||||
const layer = this.layerCache.get(responsePath.toString());
|
||||
if (layer) {
|
||||
Object.assign(this.layerCache.get(responsePath.toString()), responseLayer);
|
||||
} else {
|
||||
this.layerCache.set(responsePath.toString(), responseLayer);
|
||||
}
|
||||
this.setBlendModeForSelectedLayers();
|
||||
this.setOpacityForSelectedLayers();
|
||||
});
|
||||
},
|
||||
components: {
|
||||
|
||||
@@ -143,21 +143,21 @@ import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
import CheckboxInput from "@/components/widgets/inputs/CheckboxInput.vue";
|
||||
import UserInputLabel from "@/components/widgets/labels/UserInputLabel.vue";
|
||||
|
||||
export type MenuListEntries = Array<MenuListEntry>;
|
||||
export type SectionsOfMenuListEntries = Array<MenuListEntries>;
|
||||
export type MenuListEntries<Value = string> = MenuListEntry<Value>[];
|
||||
export type SectionsOfMenuListEntries<Value = string> = MenuListEntries<Value>[];
|
||||
|
||||
interface MenuListEntryData {
|
||||
value?: string;
|
||||
interface MenuListEntryData<Value = string> {
|
||||
value?: Value;
|
||||
label?: string;
|
||||
icon?: string;
|
||||
checkbox?: boolean;
|
||||
shortcut?: Array<string>;
|
||||
shortcut?: string[];
|
||||
shortcutRequiresLock?: boolean;
|
||||
action?: () => void;
|
||||
children?: SectionsOfMenuListEntries;
|
||||
}
|
||||
|
||||
export type MenuListEntry = MenuListEntryData & { ref?: typeof FloatingMenu | typeof MenuList; checked?: boolean };
|
||||
export type MenuListEntry<Value = string> = MenuListEntryData<Value> & { ref?: typeof FloatingMenu | typeof MenuList; checked?: boolean };
|
||||
|
||||
const KEYBOARD_LOCK_USE_FULLSCREEN = "This hotkey is reserved by the browser, but becomes available in fullscreen mode";
|
||||
const KEYBOARD_LOCK_SWITCH_BROWSER = "This hotkey is reserved by the browser, but becomes available in Chrome, Edge, and Opera which support the Keyboard.lock() API";
|
||||
@@ -240,7 +240,7 @@ const MenuList = defineComponent({
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
menuEntriesWithoutRefs(): Array<Array<MenuListEntryData>> {
|
||||
menuEntriesWithoutRefs(): MenuListEntryData[][] {
|
||||
const { menuEntries } = this;
|
||||
return menuEntries.map((entries) =>
|
||||
entries.map((entry) => {
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface RadioEntryData {
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
export type RadioEntries = Array<RadioEntryData>;
|
||||
export type RadioEntries = RadioEntryData[];
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
||||
@@ -70,7 +70,8 @@ import { defineComponent } from "vue";
|
||||
|
||||
import { rgbToDecimalRgb, RGB } from "@/utilities/color";
|
||||
import { panicProxy } from "@/utilities/panic-proxy";
|
||||
import { ResponseType, registerResponseHandler, Response, UpdateWorkingColors } from "@/utilities/response-handler";
|
||||
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";
|
||||
@@ -135,20 +136,17 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
registerResponseHandler(ResponseType.UpdateWorkingColors, (responseData: Response) => {
|
||||
const colorData = responseData as UpdateWorkingColors;
|
||||
if (!colorData) return;
|
||||
const { primary, secondary } = colorData;
|
||||
subscribeJsMessage(UpdateWorkingColors, (updateWorkingColors) => {
|
||||
const { primary, secondary } = updateWorkingColors;
|
||||
|
||||
this.primaryColor = { r: primary.red, g: primary.green, b: primary.blue, a: primary.alpha };
|
||||
let color = this.primaryColor;
|
||||
let button = this.getRef<HTMLButtonElement>("primaryButton");
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
this.primaryColor = primary.toRgba();
|
||||
this.secondaryColor = secondary.toRgba();
|
||||
|
||||
this.secondaryColor = { r: secondary.red, g: secondary.green, b: secondary.blue, a: secondary.alpha };
|
||||
color = this.secondaryColor;
|
||||
button = this.getRef<HTMLButtonElement>("secondaryButton");
|
||||
button.style.setProperty("--swatch-color", `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`);
|
||||
const primaryButton = this.getRef<HTMLButtonElement>("primaryButton");
|
||||
primaryButton.style.setProperty("--swatch-color", primary.toRgbaCSS());
|
||||
|
||||
const secondaryButton = this.getRef<HTMLButtonElement>("secondaryButton");
|
||||
secondaryButton.style.setProperty("--swatch-color", secondary.toRgbaCSS());
|
||||
});
|
||||
|
||||
this.updatePrimaryColor();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type Widgets = TextButtonWidget | IconButtonWidget | SeparatorWidget | PopoverButtonWidget | NumberInputWidget;
|
||||
export type WidgetRow = Array<Widgets>;
|
||||
export type WidgetLayout = Array<WidgetRow>;
|
||||
export type WidgetRow = Widgets[];
|
||||
export type WidgetLayout = WidgetRow[];
|
||||
|
||||
// Text Button
|
||||
export interface TextButtonWidget {
|
||||
|
||||
Reference in New Issue
Block a user