added text tool
This commit is contained in:
parent
0b35b34e61
commit
fbd0730f01
138
frontend/app.js
138
frontend/app.js
|
|
@ -620,6 +620,15 @@ function noteApp() {
|
||||||
drawingRedoStack: [],
|
drawingRedoStack: [],
|
||||||
drawingDraft: null,
|
drawingDraft: null,
|
||||||
drawingIsPointerDown: false,
|
drawingIsPointerDown: false,
|
||||||
|
// Text tool — pending input state. The HTML overlay <input> binds to drawingTextValue
|
||||||
|
// while drawingTextActive is true; on commit a 'text' op is pushed to drawingOps.
|
||||||
|
drawingTextActive: false,
|
||||||
|
drawingTextDocX: 0, // commit position in DOC space
|
||||||
|
drawingTextDocY: 0,
|
||||||
|
drawingTextCssX: 0, // overlay <input> position in CSS pixels (canvas-relative)
|
||||||
|
drawingTextCssY: 0,
|
||||||
|
drawingTextValue: '',
|
||||||
|
drawingTextDocFontSize: 24, // font size in DOC pixels (auto-scales to display)
|
||||||
/** True after the PNG from disk has been decoded into _drawingBaseImage; false after Clear. */
|
/** True after the PNG from disk has been decoded into _drawingBaseImage; false after Clear. */
|
||||||
drawingHasRasterFromFile: false,
|
drawingHasRasterFromFile: false,
|
||||||
/** Prevents overlapping drawingSave() runs (Ctrl+S + autosave + fast retries). */
|
/** Prevents overlapping drawingSave() runs (Ctrl+S + autosave + fast retries). */
|
||||||
|
|
@ -3029,6 +3038,17 @@ function noteApp() {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (op.type === 'text') {
|
||||||
|
const text = op.text;
|
||||||
|
if (typeof text !== 'string' || text === '') return;
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = op.color;
|
||||||
|
ctx.font = `${op.fontSize}px sans-serif`;
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.fillText(text, op.x, op.y);
|
||||||
|
ctx.restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (op.type === 'stroke') {
|
if (op.type === 'stroke') {
|
||||||
const pts = op.points;
|
const pts = op.points;
|
||||||
if (!pts || pts.length < 2) return;
|
if (!pts || pts.length < 2) return;
|
||||||
|
|
@ -3257,6 +3277,8 @@ function noteApp() {
|
||||||
this.drawingRedoStack = [];
|
this.drawingRedoStack = [];
|
||||||
this.drawingDraft = null;
|
this.drawingDraft = null;
|
||||||
this.drawingIsPointerDown = false;
|
this.drawingIsPointerDown = false;
|
||||||
|
this.drawingTextActive = false;
|
||||||
|
this.drawingTextValue = '';
|
||||||
this._drawingBaseImage = null;
|
this._drawingBaseImage = null;
|
||||||
this.drawingHasRasterFromFile = false;
|
this.drawingHasRasterFromFile = false;
|
||||||
// Reset to defaults; replaced by the loaded PNG's natural size below.
|
// Reset to defaults; replaced by the loaded PNG's natural size below.
|
||||||
|
|
@ -3535,6 +3557,8 @@ function noteApp() {
|
||||||
this._drawingDisposeOps(this.drawingRedoStack);
|
this._drawingDisposeOps(this.drawingRedoStack);
|
||||||
this.drawingOps = [];
|
this.drawingOps = [];
|
||||||
this.drawingRedoStack = [];
|
this.drawingRedoStack = [];
|
||||||
|
this.drawingTextActive = false;
|
||||||
|
this.drawingTextValue = '';
|
||||||
this.drawingRedraw();
|
this.drawingRedraw();
|
||||||
this._drawingScheduleAutosave();
|
this._drawingScheduleAutosave();
|
||||||
},
|
},
|
||||||
|
|
@ -3554,6 +3578,11 @@ function noteApp() {
|
||||||
this.drawingFill(e);
|
this.drawingFill(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (tool === 'text') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.drawingTextStart(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._drawingCancelAutosave();
|
this._drawingCancelAutosave();
|
||||||
canvas.setPointerCapture(e.pointerId);
|
canvas.setPointerCapture(e.pointerId);
|
||||||
this._drawingPointerId = e.pointerId;
|
this._drawingPointerId = e.pointerId;
|
||||||
|
|
@ -3678,6 +3707,115 @@ function noteApp() {
|
||||||
this._drawingScheduleAutosave();
|
this._drawingScheduleAutosave();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text tool — single-line, commit-once. Clicking the canvas spawns a floating <input>
|
||||||
|
* at the click position; pressing Enter or losing focus rasterizes the text into a
|
||||||
|
* 'text' op (rendered in DOC space via ctx.fillText so it auto-scales with the canvas).
|
||||||
|
* Esc cancels without writing. Each commit pushes one op so undo/redo handles it just
|
||||||
|
* like any other shape — no separate "edit text" mode, no DOM cleanup gymnastics.
|
||||||
|
*/
|
||||||
|
drawingTextStart(e) {
|
||||||
|
const canvas = this._drawingCanvasEl;
|
||||||
|
if (!canvas) return;
|
||||||
|
// If a previous input was still open (e.g. mobile pointerdown before blur fires),
|
||||||
|
// commit it first so we never lose typed text.
|
||||||
|
if (this.drawingTextActive) {
|
||||||
|
this.drawingTextCommit();
|
||||||
|
}
|
||||||
|
// CSS coords are stored relative to the WRAP (the input's offset parent), not the
|
||||||
|
// canvas: the wrap is a flex container that centers the canvas, so when the canvas
|
||||||
|
// is narrower than the wrap there are gutters on either side that would otherwise
|
||||||
|
// shift the floating input away from the click point.
|
||||||
|
const wrap = this.$refs.drawingCanvasWrap || canvas.parentElement;
|
||||||
|
const wrapRect = wrap.getBoundingClientRect();
|
||||||
|
const { x: docX, y: docY } = this._drawingCanvasCoords(e);
|
||||||
|
this.drawingTextDocX = docX;
|
||||||
|
this.drawingTextDocY = docY;
|
||||||
|
this.drawingTextCssX = e.clientX - wrapRect.left;
|
||||||
|
this.drawingTextCssY = e.clientY - wrapRect.top;
|
||||||
|
this.drawingTextDocFontSize = Math.max(14, this.drawingLineWidth * 6);
|
||||||
|
this.drawingTextValue = '';
|
||||||
|
this.drawingTextActive = true;
|
||||||
|
this.drawingDraft = null;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const el = document.getElementById('drawing-text-input');
|
||||||
|
if (!el) return;
|
||||||
|
// Wipe any leftover text from a previous session before focusing — contenteditable
|
||||||
|
// doesn't auto-clear from setting drawingTextValue alone.
|
||||||
|
el.textContent = '';
|
||||||
|
el.focus();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Live-preview handler for the text tool. The contenteditable div is invisible
|
||||||
|
* (color: transparent), so the only thing the user actually SEES of their typing
|
||||||
|
* is whatever drawingRedraw paints from drawingDraft. Pushing the in-progress
|
||||||
|
* text into drawingDraft means the live preview goes through the very same
|
||||||
|
* ctx.fillText call path as the final commit — guaranteeing zero pixel drift.
|
||||||
|
*/
|
||||||
|
drawingTextOnInput(e) {
|
||||||
|
if (!this.drawingTextActive) return;
|
||||||
|
const text = (e.target.textContent || '');
|
||||||
|
this.drawingTextValue = text;
|
||||||
|
if (text.length === 0) {
|
||||||
|
this.drawingDraft = null;
|
||||||
|
} else {
|
||||||
|
this.drawingDraft = {
|
||||||
|
type: 'text',
|
||||||
|
color: this.drawingColor,
|
||||||
|
x: this.drawingTextDocX,
|
||||||
|
y: this.drawingTextDocY,
|
||||||
|
text,
|
||||||
|
fontSize: this.drawingTextDocFontSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this._drawingScheduleRedraw();
|
||||||
|
},
|
||||||
|
|
||||||
|
drawingTextCommit() {
|
||||||
|
if (!this.drawingTextActive) return;
|
||||||
|
// Read straight from the DOM so we capture the very last keystroke even if the
|
||||||
|
// @input event hasn't fired yet (it usually has, but Enter can race it).
|
||||||
|
const el = document.getElementById('drawing-text-input');
|
||||||
|
const raw = el ? (el.textContent || '') : (this.drawingTextValue || '');
|
||||||
|
const text = raw.trim();
|
||||||
|
const docX = this.drawingTextDocX;
|
||||||
|
const docY = this.drawingTextDocY;
|
||||||
|
const fontSize = this.drawingTextDocFontSize;
|
||||||
|
const color = this.drawingColor;
|
||||||
|
this.drawingTextActive = false;
|
||||||
|
this.drawingTextValue = '';
|
||||||
|
this.drawingDraft = null;
|
||||||
|
if (el) el.textContent = '';
|
||||||
|
if (!text) {
|
||||||
|
this.drawingRedraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._drawingDisposeOps(this.drawingRedoStack);
|
||||||
|
this.drawingRedoStack = [];
|
||||||
|
this.drawingOps.push({
|
||||||
|
type: 'text',
|
||||||
|
color,
|
||||||
|
x: docX,
|
||||||
|
y: docY,
|
||||||
|
text,
|
||||||
|
fontSize,
|
||||||
|
});
|
||||||
|
this.drawingRedraw();
|
||||||
|
this._drawingScheduleAutosave();
|
||||||
|
},
|
||||||
|
|
||||||
|
drawingTextCancel() {
|
||||||
|
if (!this.drawingTextActive) return;
|
||||||
|
this.drawingTextActive = false;
|
||||||
|
this.drawingTextValue = '';
|
||||||
|
this.drawingDraft = null;
|
||||||
|
const el = document.getElementById('drawing-text-input');
|
||||||
|
if (el) el.textContent = '';
|
||||||
|
this.drawingRedraw();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist the flattened canvas to disk (Ctrl+S, autosave). Same feedback as saveNote:
|
* Persist the flattened canvas to disk (Ctrl+S, autosave). Same feedback as saveNote:
|
||||||
* header "Saved" only — never clears stroke undo/redo or reloads the image; stacks reset when
|
* header "Saved" only — never clears stroke undo/redo or reloads the image; stacks reset when
|
||||||
|
|
|
||||||
|
|
@ -2802,6 +2802,17 @@
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.5 14.5c0 1-1.5 3-1.5 3s-1.5-2-1.5-3a1.5 1.5 0 113 0z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.5 14.5c0 1-1.5 3-1.5 3s-1.5-2-1.5-3a1.5 1.5 0 113 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="p-2 rounded-lg border transition-colors"
|
||||||
|
:style="drawingTool === 'text' ? 'background-color: var(--accent-primary); color: white; border-color: var(--accent-primary);' : 'background-color: var(--bg-tertiary); color: var(--text-secondary); border-color: var(--border-primary);'"
|
||||||
|
@click="drawingTool = 'text'"
|
||||||
|
:title="t('drawing.tool_text')"
|
||||||
|
>
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 4h14M12 4v16M8 20h8" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="p-2 rounded-lg border transition-colors"
|
class="p-2 rounded-lg border transition-colors"
|
||||||
|
|
@ -3130,7 +3141,7 @@
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
x-ref="drawingCanvasWrap"
|
x-ref="drawingCanvasWrap"
|
||||||
class="flex-1 w-full min-h-0 min-w-0 flex items-center justify-center"
|
class="flex-1 w-full min-h-0 min-w-0 flex items-center justify-center relative"
|
||||||
style="min-height: 0; background-color: var(--bg-secondary);"
|
style="min-height: 0; background-color: var(--bg-secondary);"
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
|
|
@ -3140,12 +3151,54 @@
|
||||||
? 'touch-action: none; cursor: copy; background: #fff;'
|
? 'touch-action: none; cursor: copy; background: #fff;'
|
||||||
: drawingTool === 'fill'
|
: drawingTool === 'fill'
|
||||||
? 'touch-action: none; cursor: cell; background: #fff;'
|
? 'touch-action: none; cursor: cell; background: #fff;'
|
||||||
|
: drawingTool === 'text'
|
||||||
|
? 'touch-action: none; cursor: text; background: #fff;'
|
||||||
: 'touch-action: none; cursor: crosshair; background: #fff;'"
|
: 'touch-action: none; cursor: crosshair; background: #fff;'"
|
||||||
@pointerdown.prevent="drawingPointerDown($event)"
|
@pointerdown.prevent="drawingPointerDown($event)"
|
||||||
@pointermove.prevent="drawingPointerMove($event)"
|
@pointermove.prevent="drawingPointerMove($event)"
|
||||||
@pointerup.prevent="drawingPointerUp($event)"
|
@pointerup.prevent="drawingPointerUp($event)"
|
||||||
@pointercancel.prevent="drawingPointerUp($event)"
|
@pointercancel.prevent="drawingPointerUp($event)"
|
||||||
></canvas>
|
></canvas>
|
||||||
|
<!-- Text-tool floating editor — only visible while placing text.
|
||||||
|
The contenteditable's typed glyphs are kept INVISIBLE
|
||||||
|
(color: transparent) on purpose: HTML and canvas text-rendering
|
||||||
|
land glyphs on subtly different pixels even with identical font /
|
||||||
|
size / line-height, so showing both would always result in a tiny
|
||||||
|
jump on commit. Instead we render the typed text live on the
|
||||||
|
canvas (via drawingDraft), so what the user sees while typing IS
|
||||||
|
the canvas output — pixel-identical to the rasterized op on Enter.
|
||||||
|
Only the caret (caret-color) and the dashed outline remain
|
||||||
|
visible to indicate where the text will land. -->
|
||||||
|
<div
|
||||||
|
id="drawing-text-input"
|
||||||
|
x-show="drawingTextActive"
|
||||||
|
contenteditable="plaintext-only"
|
||||||
|
spellcheck="false"
|
||||||
|
@keydown.enter.prevent="drawingTextCommit()"
|
||||||
|
@keydown.escape.prevent="drawingTextCancel()"
|
||||||
|
@blur="drawingTextCommit()"
|
||||||
|
@input="drawingTextOnInput($event)"
|
||||||
|
:style="`position: absolute;
|
||||||
|
left: ${drawingTextCssX}px;
|
||||||
|
top: ${drawingTextCssY}px;
|
||||||
|
font-size: ${Math.max(12, drawingTextDocFontSize * ((($refs.drawingCanvas && $refs.drawingCanvas.clientWidth) || drawingDocW) / drawingDocW))}px;
|
||||||
|
line-height: 1;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: transparent;
|
||||||
|
caret-color: ${drawingColor};
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
outline: 1px dashed ${drawingColor};
|
||||||
|
outline-offset: -1px;
|
||||||
|
box-shadow: none;
|
||||||
|
z-index: 5;
|
||||||
|
min-width: 4ch;
|
||||||
|
white-space: pre;`"
|
||||||
|
x-cloak
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@
|
||||||
"tool_eraser": "Radierer (malt mit Hintergrundfarbe)",
|
"tool_eraser": "Radierer (malt mit Hintergrundfarbe)",
|
||||||
"tool_eyedropper": "Pipette — Farbe vom Bild aufnehmen",
|
"tool_eyedropper": "Pipette — Farbe vom Bild aufnehmen",
|
||||||
"tool_fill": "Farbeimer — verbundenen Bereich mit Farbe füllen",
|
"tool_fill": "Farbeimer — verbundenen Bereich mit Farbe füllen",
|
||||||
|
"tool_text": "Text — auf die Leinwand klicken, um zu schreiben",
|
||||||
"clear": "Leeren",
|
"clear": "Leeren",
|
||||||
"clear_hint": "Durch leeres Bild ersetzen",
|
"clear_hint": "Durch leeres Bild ersetzen",
|
||||||
"clear_title": "Durch leere Leinwand ersetzen?",
|
"clear_title": "Durch leere Leinwand ersetzen?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "Eraser (paints with background colour)",
|
"tool_eraser": "Eraser (paints with background colour)",
|
||||||
"tool_eyedropper": "Eyedropper — pick a colour from the canvas",
|
"tool_eyedropper": "Eyedropper — pick a colour from the canvas",
|
||||||
"tool_fill": "Paint bucket — fill connected area with colour",
|
"tool_fill": "Paint bucket — fill connected area with colour",
|
||||||
|
"tool_text": "Text — click on the canvas to type",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"clear_hint": "Replace with a blank image",
|
"clear_hint": "Replace with a blank image",
|
||||||
"clear_title": "Replace with a blank canvas?",
|
"clear_title": "Replace with a blank canvas?",
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@
|
||||||
"tool_eraser": "Eraser (paints with background color)",
|
"tool_eraser": "Eraser (paints with background color)",
|
||||||
"tool_eyedropper": "Eyedropper — pick a color from the canvas",
|
"tool_eyedropper": "Eyedropper — pick a color from the canvas",
|
||||||
"tool_fill": "Paint bucket — fill connected area with color",
|
"tool_fill": "Paint bucket — fill connected area with color",
|
||||||
|
"tool_text": "Text — click on the canvas to type",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"clear_hint": "Replace with a blank image",
|
"clear_hint": "Replace with a blank image",
|
||||||
"clear_title": "Replace with a blank canvas?",
|
"clear_title": "Replace with a blank canvas?",
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@
|
||||||
"tool_eraser": "Borrador (pinta con el color de fondo)",
|
"tool_eraser": "Borrador (pinta con el color de fondo)",
|
||||||
"tool_eyedropper": "Cuentagotas — tomar color del lienzo",
|
"tool_eyedropper": "Cuentagotas — tomar color del lienzo",
|
||||||
"tool_fill": "Cubo de pintura — rellenar área conectada con color",
|
"tool_fill": "Cubo de pintura — rellenar área conectada con color",
|
||||||
|
"tool_text": "Texto — haz clic en el lienzo para escribir",
|
||||||
"clear": "Borrar",
|
"clear": "Borrar",
|
||||||
"clear_hint": "Sustituir por imagen en blanco",
|
"clear_hint": "Sustituir por imagen en blanco",
|
||||||
"clear_title": "¿Sustituir por lienzo en blanco?",
|
"clear_title": "¿Sustituir por lienzo en blanco?",
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@
|
||||||
"tool_eraser": "Gomme (peint avec la couleur de fond)",
|
"tool_eraser": "Gomme (peint avec la couleur de fond)",
|
||||||
"tool_eyedropper": "Pipette — choisir une couleur sur le canevas",
|
"tool_eyedropper": "Pipette — choisir une couleur sur le canevas",
|
||||||
"tool_fill": "Pot de peinture — remplir la zone connectée avec la couleur",
|
"tool_fill": "Pot de peinture — remplir la zone connectée avec la couleur",
|
||||||
|
"tool_text": "Texte — cliquez sur le canevas pour écrire",
|
||||||
"clear": "Effacer",
|
"clear": "Effacer",
|
||||||
"clear_hint": "Remplacer par une image vide",
|
"clear_hint": "Remplacer par une image vide",
|
||||||
"clear_title": "Remplacer par un canevas vide ?",
|
"clear_title": "Remplacer par un canevas vide ?",
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@
|
||||||
"tool_eraser": "Radír (háttérszínnel fest)",
|
"tool_eraser": "Radír (háttérszínnel fest)",
|
||||||
"tool_eyedropper": "Pipetta — szín mintavétele a vászonról",
|
"tool_eyedropper": "Pipetta — szín mintavétele a vászonról",
|
||||||
"tool_fill": "Festékesvödör — összefüggő terület kitöltése színnel",
|
"tool_fill": "Festékesvödör — összefüggő terület kitöltése színnel",
|
||||||
|
"tool_text": "Szöveg — kattintson a vászonra a beíráshoz",
|
||||||
"clear": "Törlés",
|
"clear": "Törlés",
|
||||||
"clear_hint": "Üres képre cserélés",
|
"clear_hint": "Üres képre cserélés",
|
||||||
"clear_title": "Lecseréled üres vászonra?",
|
"clear_title": "Lecseréled üres vászonra?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "Gomma (disegna col colore di sfondo)",
|
"tool_eraser": "Gomma (disegna col colore di sfondo)",
|
||||||
"tool_eyedropper": "Contagocce — prendi un colore dal canvas",
|
"tool_eyedropper": "Contagocce — prendi un colore dal canvas",
|
||||||
"tool_fill": "Secchiello — riempi l'area connessa con il colore",
|
"tool_fill": "Secchiello — riempi l'area connessa con il colore",
|
||||||
|
"tool_text": "Testo — clicca sulla tela per scrivere",
|
||||||
"clear": "Svuota",
|
"clear": "Svuota",
|
||||||
"clear_hint": "Sostituisci con immagine vuota",
|
"clear_hint": "Sostituisci con immagine vuota",
|
||||||
"clear_title": "Sostituire con tela bianca?",
|
"clear_title": "Sostituire con tela bianca?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "消しゴム(背景色で塗る)",
|
"tool_eraser": "消しゴム(背景色で塗る)",
|
||||||
"tool_eyedropper": "スポイト — キャンバスから色を取得",
|
"tool_eyedropper": "スポイト — キャンバスから色を取得",
|
||||||
"tool_fill": "塗りつぶし — つながった領域を色で塗りつぶす",
|
"tool_fill": "塗りつぶし — つながった領域を色で塗りつぶす",
|
||||||
|
"tool_text": "テキスト — キャンバスをクリックして入力",
|
||||||
"clear": "クリア",
|
"clear": "クリア",
|
||||||
"clear_hint": "白紙の画像に置き換え",
|
"clear_hint": "白紙の画像に置き換え",
|
||||||
"clear_title": "白紙のキャンバスに置き換えますか?",
|
"clear_title": "白紙のキャンバスに置き換えますか?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "Ластик (рисует цветом фона)",
|
"tool_eraser": "Ластик (рисует цветом фона)",
|
||||||
"tool_eyedropper": "Пипетка — взять цвет с холста",
|
"tool_eyedropper": "Пипетка — взять цвет с холста",
|
||||||
"tool_fill": "Заливка — заполнить связанную область цветом",
|
"tool_fill": "Заливка — заполнить связанную область цветом",
|
||||||
|
"tool_text": "Текст — нажмите на холст, чтобы ввести",
|
||||||
"clear": "Очистить",
|
"clear": "Очистить",
|
||||||
"clear_hint": "Заменить пустым изображением",
|
"clear_hint": "Заменить пустым изображением",
|
||||||
"clear_title": "Заменить пустым холстом?",
|
"clear_title": "Заменить пустым холстом?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "Radirka (riše z barvo ozadja)",
|
"tool_eraser": "Radirka (riše z barvo ozadja)",
|
||||||
"tool_eyedropper": "Kapalka — vzemi barvo s platna",
|
"tool_eyedropper": "Kapalka — vzemi barvo s platna",
|
||||||
"tool_fill": "Vedro za barvanje — zapolni povezano območje z barvo",
|
"tool_fill": "Vedro za barvanje — zapolni povezano območje z barvo",
|
||||||
|
"tool_text": "Besedilo — kliknite na platno za pisanje",
|
||||||
"clear": "Počisti",
|
"clear": "Počisti",
|
||||||
"clear_hint": "Zamenjaj s prazno sliko",
|
"clear_hint": "Zamenjaj s prazno sliko",
|
||||||
"clear_title": "Zamenjaj s praznim platnom?",
|
"clear_title": "Zamenjaj s praznim platnom?",
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@
|
||||||
"tool_eraser": "橡皮擦(使用背景色绘制)",
|
"tool_eraser": "橡皮擦(使用背景色绘制)",
|
||||||
"tool_eyedropper": "吸管 — 从画布取色",
|
"tool_eyedropper": "吸管 — 从画布取色",
|
||||||
"tool_fill": "油漆桶 — 用颜色填充连通区域",
|
"tool_fill": "油漆桶 — 用颜色填充连通区域",
|
||||||
|
"tool_text": "文本 — 点击画布开始输入",
|
||||||
"clear": "清除",
|
"clear": "清除",
|
||||||
"clear_hint": "替换为空白图像",
|
"clear_hint": "替换为空白图像",
|
||||||
"clear_title": "替换为空白画布?",
|
"clear_title": "替换为空白画布?",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue