Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ftl/core/editing.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ editing-image-occlusion-rectangle-tool = Rectangle
editing-image-occlusion-ellipse-tool = Ellipse
editing-image-occlusion-polygon-tool = Polygon
editing-image-occlusion-text-tool = Text
editing-image-occlusion-fill-tool = Fill with colour
editing-image-occlusion-toggle-mask-editor = Toggle Mask Editor
editing-image-occlusion-reset = Reset Image Occlusion
editing-image-occlusion-confirm-reset = Are you sure you want to reset this image occlusion?
Expand Down
14 changes: 2 additions & 12 deletions rslib/src/image_occlusion/imageocclusion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,9 @@ pub fn get_image_cloze_data(text: &str) -> String {
}
for property in occlusion.properties {
match property.name.as_str() {
"left" => {
"left" | "top" | "angle" | "fill" => {
if !property.value.is_empty() {
result.push_str(&format!("data-left=\"{}\" ", property.value));
}
}
"top" => {
if !property.value.is_empty() {
result.push_str(&format!("data-top=\"{}\" ", property.value));
}
}
"angle" => {
if !property.value.is_empty() {
result.push_str(&format!("data-angle=\"{}\" ", property.value));
result.push_str(&format!("data-{}=\"{}\" ", property.name, property.value));
}
}
"width" => {
Expand Down
3 changes: 3 additions & 0 deletions ts/lib/components/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import FormatAlignCenter_ from "@mdi/svg/svg/format-align-center.svg?component";
import formatAlignCenter_ from "@mdi/svg/svg/format-align-center.svg?url";
import FormatBold_ from "@mdi/svg/svg/format-bold.svg?component";
import formatBold_ from "@mdi/svg/svg/format-bold.svg?url";
import FormatColorFill_ from "@mdi/svg/svg/format-color-fill.svg?component";
import formatColorFill_ from "@mdi/svg/svg/format-color-fill.svg?url";
import HighlightColor_ from "@mdi/svg/svg/format-color-highlight.svg?component";
import highlightColor_ from "@mdi/svg/svg/format-color-highlight.svg?url";
import TextColor_ from "@mdi/svg/svg/format-color-text.svg?component";
Expand Down Expand Up @@ -264,6 +266,7 @@ export const mdiEllipseOutline = { url: ellipseOutline_, component: EllipseOutli
export const mdiEye = { url: eye_, component: Eye_ };
export const mdiFormatAlignCenter = { url: formatAlignCenter_, component: FormatAlignCenter_ };
export const mdiFormatBold = { url: formatBold_, component: FormatBold_ };
export const mdiFormatColorFill = { url: formatColorFill_, component: FormatColorFill_ };
export const mdiFormatItalic = { url: formatItalic_, component: FormatItalic_ };
export const mdiFormatUnderline = { url: formatUnderline_, component: FormatUnderline_ };
export const mdiGroup = { url: group_, component: Group_ };
Expand Down
51 changes: 40 additions & 11 deletions ts/routes/image-occlusion/Toolbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { Callback } from "@tslib/typing";
import { singleCallback } from "@tslib/typing";
import { getContext, onDestroy, onMount } from "svelte";
import type { Readable } from "svelte/store";
import { writable, type Readable } from "svelte/store";

import DropdownItem from "$lib/components/DropdownItem.svelte";
import Icon from "$lib/components/Icon.svelte";
Expand All @@ -33,7 +33,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
opacityStateStore,
} from "./store";
import { drawEllipse, drawPolygon, drawRectangle, drawText } from "./tools/index";
import { makeMaskTransparent } from "./tools/lib";
import { makeMaskTransparent, SHAPE_MASK_COLOR } from "./tools/lib";
import { enableSelectable, stopDraw } from "./tools/lib";
import {
alignTools,
Expand All @@ -42,7 +42,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
zoomTools,
} from "./tools/more-tools";
import { toggleTranslucentKeyCombination } from "./tools/shortcuts";
import { tools } from "./tools/tool-buttons";
import { tools, type ActiveTool } from "./tools/tool-buttons";
import { drawCursor } from "./tools/tool-cursor";
import { removeUnfinishedPolygon } from "./tools/tool-polygon";
import { undoRedoTools, undoStack } from "./tools/tool-undo-redo";
Expand All @@ -54,10 +54,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onWheelDrag,
onWheelDragX,
} from "./tools/tool-zoom";
import { fillMask } from "./tools/tool-fill";

export let canvas;
export let iconSize;
export let activeTool = "cursor";
export let activeTool: ActiveTool = "cursor";
let showAlignTools = false;
let leftPos = 82;
let maskOpacity = false;
Expand All @@ -72,6 +73,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const controlKey = "Control";
const shiftKey = "Shift";
let removeHandlers: Callback;
let colourRef: HTMLInputElement | undefined;
const colour = writable(SHAPE_MASK_COLOR);

function onClick(event: MouseEvent) {
const upperCanvas = document.querySelector(".upper-canvas");
Expand Down Expand Up @@ -168,7 +171,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}

const handleToolChanges = (newActiveTool: string) => {
const handleToolChanges = (newActiveTool: ActiveTool, clicked: boolean = false) => {
disableFunctions();
enableSelectable(canvas, true);
// remove unfinished polygon when switching to other tools
Expand All @@ -193,6 +196,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
handleToolChanges(activeTool);
});
break;
case "fill-mask":
if (clicked) {
colourRef?.click();
}
fillMask(canvas, colour);
break;
}
};

Expand Down Expand Up @@ -231,16 +240,30 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
});
</script>

<div class="tool-bar-container">
<datalist id="colour-palette">
<option value={SHAPE_MASK_COLOR}></option>
</datalist>

<input
type="color"
bind:this={colourRef}
style:display="none"
list="colour-palette"
value={SHAPE_MASK_COLOR}
on:input={(e) => ($colour = e.currentTarget!.value)}
/>

<div class="tool-bar-container" style:--fill-tool-colour={$colour}>
{#each tools as tool}
{@const active = activeTool == tool.id}
<IconButton
class="tool-icon-button {activeTool == tool.id ? 'active-tool' : ''}"
{iconSize}
class="tool-icon-button {active ? 'active-tool' : ''} {tool.id}"
iconSize={iconSize * (tool["iconSizeMult"] ?? 1)}
tooltip="{tool.tooltip()} ({getPlatformString(tool.shortcut)})"
active={activeTool === tool.id}
{active}
on:click={() => {
activeTool = tool.id;
handleToolChanges(activeTool);
handleToolChanges(activeTool, true);
}}
>
<Icon icon={tool.icon} />
Expand All @@ -250,7 +273,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
keyCombination={tool.shortcut}
on:action={() => {
activeTool = tool.id;
handleToolChanges(activeTool);
handleToolChanges(activeTool, true);
}}
/>
{/if}
Expand Down Expand Up @@ -551,6 +574,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
padding-bottom: 100px;
}

:global(.fill-mask svg) {
fill: var(--fill-tool-colour) !important;
stroke: black;
stroke-width: 1px;
}

:global([dir="rtl"] .tool-bar-container) {
left: unset;
right: 2px;
Expand Down
4 changes: 2 additions & 2 deletions ts/routes/image-occlusion/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ function drawShapes(
context,
size,
shape,
fill: properties.inActiveShapeColor,
fill: shape.fill ?? properties.inActiveShapeColor,
stroke: properties.inActiveBorder.color,
strokeWidth: properties.inActiveBorder.width,
});
Expand Down Expand Up @@ -358,7 +358,7 @@ function drawShape({
maxWidth + TEXT_PADDING,
totalHeight + TEXT_PADDING,
);
ctx.fillStyle = "#000";
ctx.fillStyle = shape.fill ?? "#000";
for (const line of linePositions) {
ctx.fillText(line.text, line.x, line.y);
}
Expand Down
1 change: 1 addition & 0 deletions ts/routes/image-occlusion/shapes/from-cloze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ function extractShapeFromRenderedCloze(cloze: HTMLDivElement): Shape | null {
scale: cloze.dataset.scale,
fs: cloze.dataset.fontSize,
angle: cloze.dataset.angle,
...(cloze.dataset.fill == null ? {} : { fill: cloze.dataset.fill }),
};
return buildShape(type, props);
}
Expand Down
4 changes: 3 additions & 1 deletion ts/routes/image-occlusion/shapes/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ export class Text extends Shape {
text = "",
scaleX = 1,
scaleY = 1,
fill = TEXT_COLOR,
fontSize,
...rest
}: ConstructorParams<Text> = {}) {
super(rest);
this.fill = TEXT_COLOR;
this.fill = fill;
this.text = text;
this.scaleX = scaleX;
this.scaleY = scaleY;
Expand All @@ -38,6 +39,7 @@ export class Text extends Shape {
// scaleX and scaleY are guaranteed to be equal since we lock the aspect ratio
scale: floatToDisplay(this.scaleX),
fs: this.fontSize ? floatToDisplay(this.fontSize) : undefined,
...(this.fill === TEXT_COLOR ? {} : { fill: this.fill }),
};
}

Expand Down
15 changes: 15 additions & 0 deletions ts/routes/image-occlusion/tools/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ export const unGroupShapes = (canvas: fabric.Canvas): void => {
redraw(canvas);
};

/** Check for the target within a (potentially nested) group
* NOTE: assumes that masks do not overlap */
export const findTargetInGroup = (group: fabric.Group, p: fabric.Point): fabric.Object | undefined => {
if (!group) { return; }
const point = fabric.util.transformPoint(p, fabric.util.invertTransform(group.calcOwnMatrix()));
for (const shape of group.getObjects()) {
if (shape instanceof fabric.Group) {
const ret = findTargetInGroup(shape, point);
if (ret) { return ret; }
} else if (shape.containsPoint(point)) {
return shape;
}
}
};

const copyItem = (canvas: fabric.Canvas): void => {
const activeObject = canvas.getActiveObject();
if (!activeObject) {
Expand Down
1 change: 1 addition & 0 deletions ts/routes/image-occlusion/tools/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const rectangleKeyCombination = "R";
export const ellipseKeyCombination = "E";
export const polygonKeyCombination = "P";
export const textKeyCombination = "T";
export const fillKeyCombination = "C";
export const magnifyKeyCombination = "M";
export const undoKeyCombination = "Control+Z";
export const redoKeyCombination = "Control+Y";
Expand Down
13 changes: 12 additions & 1 deletion ts/routes/image-occlusion/tools/tool-buttons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as tr from "@generated/ftl";
import {
mdiCursorDefaultOutline,
mdiEllipseOutline,
mdiFormatColorFill,
mdiRectangleOutline,
mdiTextBox,
mdiVectorPolygonVariant,
Expand All @@ -14,6 +15,7 @@ import {
import {
cursorKeyCombination,
ellipseKeyCombination,
fillKeyCombination,
polygonKeyCombination,
rectangleKeyCombination,
textKeyCombination,
Expand Down Expand Up @@ -50,4 +52,13 @@ export const tools = [
tooltip: tr.editingImageOcclusionTextTool,
shortcut: textKeyCombination,
},
];
{
id: "fill-mask",
icon: mdiFormatColorFill,
iconSizeMult: 1.4,
tooltip: tr.editingImageOcclusionFillTool,
shortcut: fillKeyCombination,
},
] as const;

export type ActiveTool = typeof tools[number]["id"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL this is possible in TypeScript! Thank you for the typing improvements too. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor suggestion: if fill is set to #000000, we could clear the explicit value.

Not sure if i understood correctly 🤔 The fill attr isn't saved in the cloze if its set to the default mask colour (#ffeba2), like the angle attr when its 0. If its changed to be cleared when set to #000000 then users' existing io notes using the default mask colour would have their notes bloated with it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that's a thinko on my part - from https://github.com/ankitects/anki/pull/4048/files#diff-a1adc784721911f5063bdb67703c3f2892d9fb58d5ee1738e0df305f7062f5caR361 I assumed #000 was the default. If our default is a color the user is unlikely to be able to pick easily, no point trying to remove it if it's the default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, was mistaken as well. The text "clozes" behave differently, they don't save the fill attr when its the default text colour (#000000) and regular masks don't save it when its the default mask colour

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If our default is a color the user is unlikely to be able to pick easily

The datalist element should be able to provide preset options for the colour input, and seems to work in external/mobile browsers but not in anki itself, even though its chromium version should support it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing Qt overlooked something when trying to integrate it into their own color picker implementation.

Anyway, thank you as always for your contributions :-)

28 changes: 28 additions & 0 deletions ts/routes/image-occlusion/tools/tool-fill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

import { fabric } from "fabric";

import { get, type Readable } from "svelte/store";
import { findTargetInGroup, stopDraw } from "./lib";
import { undoStack } from "./tool-undo-redo";

export const fillMask = (canvas: fabric.Canvas, colourStore: Readable<string>): void => {
// remove selectable for shapes
canvas.discardActiveObject();
canvas.forEachObject(function(o) {
o.selectable = false;
});
canvas.selectionColor = "rgba(0, 0, 0, 0)";
stopDraw(canvas);

canvas.on("mouse:down", function(o) {
const target = o.target instanceof fabric.Group
? findTargetInGroup(o.target, canvas.getPointer(o.e) as fabric.Point)
: o.target;
const colour = get(colourStore);
if (!target || target.fill === colour) { return; }
target.fill = colour;
undoStack.onObjectModified();
});
};
2 changes: 1 addition & 1 deletion ts/routes/image-occlusion/tools/tool-polygon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export const modifiedPolygon = (canvas: fabric.Canvas, polygon: fabric.Polygon):
});

const polygon1 = new fabric.Polygon(transformedPoints, {
fill: SHAPE_MASK_COLOR,
fill: polygon.fill ?? SHAPE_MASK_COLOR,
objectCaching: false,
stroke: BORDER_COLOR,
strokeWidth: 1,
Expand Down