Add CANCEL to the watermark editor, scope the mark's touch layer to that editor, and stop a library frame following the user onto the camera

The custom watermark captured EVERY image touch whenever the chip was ON
with text, in both modes: after switching from a library photo to the
camera there was no way to give the image back to tap-to-focus, and a
frame picked for the still rode along onto the live preview (and into the
capture).

- AdjustPanel: CANCEL chip next to ACCEPT. It drops the un-accepted draft
  and restores the last ACCEPTED mark (none if nothing was accepted).
- Viewfinder: draws the mark whenever the chip is ON with text, but only
  lets it CAPTURE touches while its editor is open (new wmEditing prop).
  A double-tap ON the mark still re-opens that editor from the library
  viewer.
- App: ACCEPT and CANCEL now close the editor (that closes the placement
  layer with it). Switching mode closes the deck panels, and leaves the
  frame behind: camera mode has no FRAME picker to undo one.

Verified on device (9a6a7277): camera tap locks AE/AF with the watermark
left ON; library polaroid no longer appears on the live preview after the
switch; CANCEL reverts the chip to CUSTOM WATERMARK OFF and closes the
panel; ACCEPT closes it and the next capture embeds the mark (amber text
found at 0.5/0.375 of the 3000x4000 export), while a capture before
ACCEPT carries none.
This commit is contained in:
2026-09-10 09:58:50 +07:00
parent 28e18893cd
commit d7a8e0681e
3 changed files with 85 additions and 15 deletions
+30 -7
View File
@@ -103,6 +103,12 @@ interface ViewfinderProps {
y: number;
};
onWmPositionChange?: (x: number, y: number) => void;
// True while the WATERMARK panel is open — that panel IS the mark's edit
// session. The mark draws whenever the CUSTOM chip is ON with text, but it
// only CAPTURES image touches (placement drags in camera mode, click-through
// to focus / zoom) while this is set, so a watermark left ON can never
// swallow the camera's tap-to-focus or the library's pinch/double-tap.
wmEditing?: boolean;
}
export interface ViewfinderHandle {
@@ -186,6 +192,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
customWm = { enabled: false, text: '', x: 0.5, y: 0.5 },
onWmPositionChange,
onWmEdit,
wmEditing = false,
photoRotation = 0,
}: ViewfinderProps,
ref
@@ -1018,11 +1025,10 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
// Armed = CUSTOM chip ON + non-empty text + live position callback. While
// armed, touches on the image place/move the text (see the wm branches in the
// camera + library gesture handlers); this is the only way to position it.
const wmArmed = !!(
customWm.enabled &&
customWm.text.trim().length > 0 &&
onWmPositionChange
);
// Drawn in both modes whenever the chip is ON with text; TOUCHED only while
// the WATERMARK panel is open (wmEditing) — see the prop comment.
const wmVisible = !!(customWm.enabled && customWm.text.trim().length > 0);
const wmArmed = !!(wmVisible && wmEditing && onWmPositionChange);
const [wmDragPos, setWmDragPos] = useState<{ x: number; y: number } | null>(null);
// Photo/window area the 0..1 fractions map onto — identical rule to
// renderGPSWatermark so both marks share the user-visible coordinate space.
@@ -1077,7 +1083,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
// Bounding box of the drawn mark (same anchor maths as renderCustomWatermark)
// so a double-tap ON the text re-opens the editor instead of moving it.
const wmBox = () => {
if (!wmArmed || !wmFont) return null;
if (!wmVisible || !wmFont) return null;
const area = wmArea();
const fracX = wmDragPos?.x ?? customWm.x;
const fracY = wmDragPos?.y ?? customWm.y;
@@ -1162,6 +1168,23 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
if (wasPinch) return; // a double-tap is always a single finger
const x = e.nativeEvent.locationX;
const y = e.nativeEvent.locationY;
// Mark drawn but its editor CLOSED: a double-tap ON the mark re-opens the
// editor (and the placement layer with it). A single tap is NOT consumed —
// this block records nothing, so the viewer rules below still run and the
// photo keeps pinch / double-tap zoom / pan.
if (!wmArmed && wmVisible && libZoom.s <= 1.01) {
const prev = lastTapRef.current;
if (
prev &&
Date.now() - prev.t < 350 &&
Math.hypot(x - prev.x, y - prev.y) < 50 &&
wmBoxHit(wmBox(), x, y)
) {
lastTapRef.current = null;
onWmEdit?.();
return;
}
}
// Watermark armed: a tap places + commits the text and skips the double-tap
// zoom bookkeeping (double-tap zoom is disabled while placing).
if (wmArmed && libZoom.s <= 1.01) {
@@ -1344,7 +1367,7 @@ const Viewfinder = forwardRef<ViewfinderHandle, ViewfinderProps>(function Viewfi
};
const renderCustomWatermark = () => {
if (!wmArmed || !wmFont) return null;
if (!wmVisible || !wmFont) return null;
const area = wmArea();
const fracX = wmDragPos?.x ?? customWm.x;
const fracY = wmDragPos?.y ?? customWm.y;