Skip to content

Commit ceb53ab

Browse files
committed
paint-gradient-editor: add new-p0 click, double-click-to-delete extra stops
- Clicking the axis before p0 on a radial gradient promotes that position as the new p0, pushing the old p0 colour into extra stops as the first middle stop, then opens the p0 colour picker via a synthetic click. - Double-clicking an extra stop handle deletes it (same path as drag-off, via shared removeExtraStop() helper); guarded by attachDragMoved so a click-select then click-drag sequence is not misread as a double-click.
1 parent 1e776ea commit ceb53ab

1 file changed

Lines changed: 45 additions & 5 deletions

File tree

addons/paint-gradient-editor/userscript.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,27 @@ export default async function ({ addon, msg, console }) {
745745
// Pool grows as needed; excess handles are hidden when extraStops shrinks.
746746
const extraHandlePool = [];
747747

748+
// Cleanly remove extra stop at idx: rebuild all selected-item gradients without it
749+
// (so the WeakSet warm-up count stays consistent), then splice and re-sync.
750+
const removeExtraStop = (idx) => {
751+
const previewExtra = extraStops.filter((_, i) => i !== idx);
752+
const cp = colorProp();
753+
for (const item of selectedLayers()) {
754+
if (!item[cp]?.gradient) continue;
755+
const liveStops = item[cp].gradient.stops;
756+
const c0 = liveStops[0].color;
757+
const c1 = liveStops[liveStops.length - 1].color;
758+
item[cp].gradient = { stops: [c0, ...previewExtra.map((s) => s.color), c1], radial: item[cp].gradient.radial };
759+
const g2 = item[cp].gradient;
760+
g2.stops[0].offset = stops.p0;
761+
for (let i = 0; i < previewExtra.length; i++) g2.stops[i + 1].offset = previewExtra[i].offset;
762+
g2.stops[g2.stops.length - 1].offset = stops.p1;
763+
}
764+
extraStops.splice(idx, 1);
765+
applyAllStops();
766+
syncOverlay();
767+
};
768+
748769
const makeExtraStopHandle = (poolIndex) => {
749770
const g = document.createElementNS(NS, "g");
750771
g._ringR = 9;
@@ -762,6 +783,15 @@ export default async function ({ addon, msg, console }) {
762783
openExtraColorPicker(poolIndex, e.clientX, e.clientY, g);
763784
});
764785

786+
// Double-click deletes this stop.
787+
g.addEventListener("dblclick", (e) => {
788+
if (addon.self.disabled || attachDragMoved) return;
789+
e.stopPropagation();
790+
picker.close();
791+
removeExtraStop(poolIndex);
792+
triggerUndo();
793+
});
794+
765795
// Drag: constrained to axis between neighbours.
766796
// If dragged > 25px perpendicular to the axis, enter "pending delete" state.
767797
// Visual: handle is hidden and the gradient previews without this stop.
@@ -856,9 +886,7 @@ export default async function ({ addon, msg, console }) {
856886
pendingDeleteSuppressedRing = false;
857887
picker.close();
858888
}
859-
extraStops.splice(poolIndex, 1);
860-
applyAllStops();
861-
syncOverlay();
889+
removeExtraStop(poolIndex);
862890
} else {
863891
// Restore visibility in case we entered pending-delete then moved back.
864892
g.style.visibility = "";
@@ -907,9 +935,9 @@ export default async function ({ addon, msg, console }) {
907935
setPickerHighlight(groupEl);
908936
picker.open(
909937
extraStops[idx].color,
910-
(css) => {
938+
(color) => {
911939
if (extraStops[idx]) {
912-
extraStops[idx].color = css;
940+
extraStops[idx].color = color;
913941
applyAllStops();
914942
syncOverlay();
915943
}
@@ -978,6 +1006,18 @@ export default async function ({ addon, msg, console }) {
9781006
const op = toSVG(fc.origin);
9791007
const dp = toSVG(fc.destination);
9801008
const axisLenPx = Math.hypot(dp.x - op.x, dp.y - op.y);
1009+
// Clicking before p0 in a radial gradient: promote the click position as the new p0,
1010+
// pushing the current p0 into extra stops so the rest of the gradient is preserved.
1011+
if (isRadial && t < stops.p0) {
1012+
extraStops.unshift({ color: c0css, offset: stops.p0 });
1013+
stops.p0 = clamp(crampedToOffset(0, extraStops.length + 2, t, axisLenPx), 0, stops.p0 - 0.01);
1014+
applyAllStops();
1015+
syncOverlay();
1016+
triggerUndo();
1017+
attachDragMoved = false;
1018+
p0Handle.g.dispatchEvent(new MouseEvent("click", { bubbles: true, clientX: e.clientX, clientY: e.clientY }));
1019+
return;
1020+
}
9811021
const newInnerIdx = extraStops.filter((s) => s.offset < t).length;
9821022
const newInnerCount = isRadial ? extraStops.length + 2 : extraStops.length + 1;
9831023
const innerIdxForNew = isRadial ? newInnerIdx + 1 : newInnerIdx;

0 commit comments

Comments
 (0)