mirror of
https://github.com/ankitects/anki.git
synced 2025-11-08 05:37:12 -05:00
Issues: - The `change` event was not dispatched in MaskEditor.svelte when an undo/redo was performed. Therefore, if the user then closed the editor or switched to another note without performing an operation that would cause the `change` event to be dispatched, the undone or redone changes were not saved to DB. - When `IOMode.kind === "edit"` (i.e., Edit Current or Browse), the beginning of the undo history was a blank canvas, not a canvas with existing masks. Therefore, if you continued to undo to the beginning of the history, the masks that existed when you opened the editor would be lost, and they would not be restored even when you performed a redo. - In the 'Add' dialog, the undo history was not reset when starting to create a new IO note after adding an IO note. Also add a small UI improvement: The undo/redo buttons are now disabled when there is no action to undo/redo.
364 lines
10 KiB
Svelte
364 lines
10 KiB
Svelte
<!--
|
|
Copyright: Ankitects Pty Ltd and contributors
|
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
-->
|
|
<script>
|
|
import { bridgeCommand } from "@tslib/bridgecommand";
|
|
import * as tr from "@tslib/ftl";
|
|
import DropdownItem from "components/DropdownItem.svelte";
|
|
import IconButton from "components/IconButton.svelte";
|
|
import Popover from "components/Popover.svelte";
|
|
import WithFloating from "components/WithFloating.svelte";
|
|
|
|
import {
|
|
mdiChevronDown,
|
|
mdiEye,
|
|
mdiFormatAlignCenter,
|
|
mdiRefresh,
|
|
mdiSquare,
|
|
mdiViewDashboard,
|
|
} from "./icons";
|
|
import { hideAllGuessOne } from "./store";
|
|
import { drawEllipse, drawPolygon, drawRectangle } from "./tools/index";
|
|
import { makeMaskTransparent } from "./tools/lib";
|
|
import { enableSelectable, stopDraw } from "./tools/lib";
|
|
import {
|
|
alignTools,
|
|
deleteDuplicateTools,
|
|
groupUngroupTools,
|
|
zoomTools,
|
|
} from "./tools/more-tools";
|
|
import { tools } from "./tools/tool-buttons";
|
|
import { undoRedoTools, undoStack } from "./tools/tool-undo-redo";
|
|
|
|
export let canvas;
|
|
export let instance;
|
|
export let iconSize;
|
|
export let activeTool = "cursor";
|
|
let showAlignTools = false;
|
|
let leftPos = 82;
|
|
let maksOpacity = false;
|
|
let showFloating = false;
|
|
|
|
document.addEventListener("click", (event) => {
|
|
const upperCanvas = document.querySelector(".upper-canvas");
|
|
if (event.target == upperCanvas) {
|
|
showAlignTools = false;
|
|
}
|
|
});
|
|
|
|
// handle tool changes after initialization
|
|
$: if (instance && canvas) {
|
|
disableFunctions();
|
|
enableSelectable(canvas, true);
|
|
|
|
switch (activeTool) {
|
|
case "magnify":
|
|
enableSelectable(canvas, false);
|
|
instance.resume();
|
|
break;
|
|
case "draw-rectangle":
|
|
drawRectangle(canvas);
|
|
break;
|
|
case "draw-ellipse":
|
|
drawEllipse(canvas);
|
|
break;
|
|
case "draw-polygon":
|
|
drawPolygon(canvas, instance);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const disableFunctions = () => {
|
|
instance.pause();
|
|
stopDraw(canvas);
|
|
canvas.selectionColor = "rgba(100, 100, 255, 0.3)";
|
|
};
|
|
|
|
const setOcclusionFieldForDesktop = () => {
|
|
const clist = document.body.classList;
|
|
if (
|
|
clist.contains("isLin") ||
|
|
clist.contains("isMac") ||
|
|
clist.contains("isWin")
|
|
) {
|
|
globalThis.setOcclusionFieldInner();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<div class="tool-bar-container">
|
|
{#each tools as tool}
|
|
<IconButton
|
|
class="tool-icon-button {activeTool == tool.id ? 'active-tool' : ''}"
|
|
{iconSize}
|
|
active={activeTool === tool.id}
|
|
on:click={() => {
|
|
activeTool = tool.id;
|
|
}}
|
|
>
|
|
{@html tool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="top-tool-bar-container">
|
|
<div class="undo-redo-button" on:click={() => (showFloating = !showFloating)}>
|
|
{#if $hideAllGuessOne}
|
|
<IconButton class="top-tool-icon-button left-border-radius" {iconSize}>
|
|
{@html mdiViewDashboard}
|
|
</IconButton>
|
|
{:else}
|
|
<IconButton class="top-tool-icon-button left-border-radius" {iconSize}>
|
|
{@html mdiSquare}
|
|
</IconButton>
|
|
{/if}
|
|
|
|
<WithFloating
|
|
show={showFloating}
|
|
closeOnInsideClick
|
|
inline
|
|
style="line-height: unset !important"
|
|
on:close={() => (showFloating = false)}
|
|
>
|
|
<IconButton
|
|
class="top-tool-icon-button right-border-radius dropdown-tool-mode"
|
|
slot="reference"
|
|
>
|
|
{@html mdiChevronDown}
|
|
</IconButton>
|
|
|
|
<Popover slot="floating" --popover-padding-inline="0">
|
|
<DropdownItem
|
|
on:click={() => {
|
|
$hideAllGuessOne = true;
|
|
setOcclusionFieldForDesktop();
|
|
}}
|
|
>
|
|
<span>{tr.notetypesHideAllGuessOne()}</span>
|
|
</DropdownItem>
|
|
<DropdownItem
|
|
on:click={() => {
|
|
$hideAllGuessOne = false;
|
|
setOcclusionFieldForDesktop();
|
|
}}
|
|
>
|
|
<span>{tr.notetypesHideOneGuessOne()}</span>
|
|
</DropdownItem>
|
|
</Popover>
|
|
</WithFloating>
|
|
</div>
|
|
|
|
<!-- refresh for changing image -->
|
|
<div class="undo-redo-button">
|
|
<IconButton
|
|
class="top-tool-icon-button icon-border-radius"
|
|
{iconSize}
|
|
on:click={() => {
|
|
bridgeCommand("addImageForOcclusion");
|
|
}}
|
|
>
|
|
{@html mdiRefresh}
|
|
</IconButton>
|
|
</div>
|
|
|
|
<!-- undo & redo tools -->
|
|
<div class="undo-redo-button">
|
|
{#each undoRedoTools as tool}
|
|
<IconButton
|
|
class="top-tool-icon-button {tool.name === 'undo'
|
|
? 'left-border-radius'
|
|
: 'right-border-radius'}"
|
|
{iconSize}
|
|
on:click={tool.action}
|
|
disabled={tool.name === "undo"
|
|
? !$undoStack.undoable
|
|
: !$undoStack.redoable}
|
|
>
|
|
{@html tool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
</div>
|
|
|
|
<!-- zoom tools -->
|
|
<div class="tool-button-container">
|
|
{#each zoomTools as tool}
|
|
<IconButton
|
|
class="top-tool-icon-button {tool.name === 'zoomOut'
|
|
? 'left-border-radius'
|
|
: ''} {tool.name === 'zoomReset' ? 'right-border-radius' : ''}"
|
|
{iconSize}
|
|
on:click={() => {
|
|
tool.action(instance);
|
|
}}
|
|
>
|
|
{@html tool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="tool-button-container">
|
|
<!-- opacity tools -->
|
|
<IconButton
|
|
class="top-tool-icon-button left-border-radius"
|
|
{iconSize}
|
|
on:click={() => {
|
|
maksOpacity = !maksOpacity;
|
|
makeMaskTransparent(canvas, maksOpacity);
|
|
}}
|
|
>
|
|
{@html mdiEye}
|
|
</IconButton>
|
|
|
|
<!-- cursor tools -->
|
|
{#each deleteDuplicateTools as tool}
|
|
<IconButton
|
|
class="top-tool-icon-button {tool.name === 'duplicate'
|
|
? 'right-border-radius'
|
|
: ''}"
|
|
{iconSize}
|
|
on:click={() => {
|
|
tool.action(canvas);
|
|
}}
|
|
>
|
|
{@html tool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
</div>
|
|
|
|
<div class="tool-button-container">
|
|
<!-- group & ungroup tools -->
|
|
{#each groupUngroupTools as tool}
|
|
<IconButton
|
|
class="top-tool-icon-button {tool.name === 'group'
|
|
? 'left-border-radius'
|
|
: ''}"
|
|
{iconSize}
|
|
on:click={() => {
|
|
tool.action(canvas);
|
|
}}
|
|
>
|
|
{@html tool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
|
|
<IconButton
|
|
class="top-tool-icon-button dropdown-tool right-border-radius"
|
|
{iconSize}
|
|
on:click={(e) => {
|
|
showAlignTools = !showAlignTools;
|
|
leftPos = e.pageX - 100;
|
|
}}
|
|
>
|
|
{@html mdiFormatAlignCenter}
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
|
|
<div class:show={showAlignTools} class="dropdown-content" style="left:{leftPos}px;">
|
|
{#each alignTools as alignTool}
|
|
<IconButton
|
|
class="top-tool-icon-button"
|
|
{iconSize}
|
|
on:click={() => {
|
|
alignTool.action(canvas);
|
|
}}
|
|
>
|
|
{@html alignTool.icon}
|
|
</IconButton>
|
|
{/each}
|
|
</div>
|
|
|
|
<style>
|
|
.top-tool-bar-container {
|
|
display: flex;
|
|
overflow-y: scroll;
|
|
z-index: 99;
|
|
margin-left: 106px;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.undo-redo-button {
|
|
margin-right: 2px;
|
|
display: flex;
|
|
}
|
|
|
|
.tool-button-container {
|
|
margin-left: 2px;
|
|
margin-right: 2px;
|
|
display: flex;
|
|
}
|
|
|
|
:global(.left-border-radius) {
|
|
border-radius: 5px 0 0 5px !important;
|
|
}
|
|
|
|
:global(.right-border-radius) {
|
|
border-radius: 0 5px 5px 0 !important;
|
|
}
|
|
|
|
:global(.top-tool-icon-button) {
|
|
border: unset;
|
|
display: inline;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin: unset;
|
|
padding: 6px !important;
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
.dropdown-content {
|
|
display: none;
|
|
position: absolute;
|
|
z-index: 100;
|
|
top: 40px;
|
|
margin-top: 1px;
|
|
}
|
|
|
|
.show {
|
|
display: block;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 0.1em !important;
|
|
height: 0.1em !important;
|
|
}
|
|
|
|
.tool-bar-container {
|
|
position: fixed;
|
|
top: 42px;
|
|
left: 2px;
|
|
height: 100%;
|
|
border-right: 1px solid var(--border);
|
|
overflow-y: auto;
|
|
width: 32px;
|
|
z-index: 99;
|
|
background: var(--canvas-elevated);
|
|
padding-bottom: 100px;
|
|
}
|
|
|
|
:global(.tool-icon-button) {
|
|
border: unset;
|
|
display: block;
|
|
width: 32px;
|
|
height: 32px;
|
|
margin: unset;
|
|
padding: 6px !important;
|
|
}
|
|
|
|
:global(.active-tool) {
|
|
color: white !important;
|
|
background: var(--button-primary-bg) !important;
|
|
}
|
|
|
|
:global(.icon-border-radius) {
|
|
border-radius: 5px !important;
|
|
}
|
|
|
|
:global(.dropdown-tool-mode) {
|
|
height: 38px !important;
|
|
display: inline;
|
|
}
|
|
</style>
|