diff --git a/style.css b/style.css index 56c15233..32913ea6 100644 --- a/style.css +++ b/style.css @@ -1053,6 +1053,11 @@ details summary:focus, background-color: var(--color-button-bg-hover); } +/* Show crosshair on canvas whenever colour picker is open */ +body.color-picker-open #renderCanvas { + cursor: crosshair; +} + /* Focus management for modal */ .modal:focus-within .modal-content { outline: 2px solid var(--color-focus-ring); diff --git a/ui/colourpicker.js b/ui/colourpicker.js index 0410ce38..7969872f 100644 --- a/ui/colourpicker.js +++ b/ui/colourpicker.js @@ -86,6 +86,7 @@ class CustomColorPicker { this.onColorChange = options.onColorChange || (() => {}); this.onClose = options.onClose || (() => {}); this.targetElement = options.target || document.body; + this.excludeFromClose = options.excludeFromClose || null; this.isOpen = false; @@ -647,6 +648,7 @@ class CustomColorPicker { // Click outside to close (guarded during eyedropper) this.outsideClickHandler = (e) => { if (this._eyedropperActive) return; // don't close while eyedropper overlay is up + if (this.excludeFromClose && this.excludeFromClose(e.target)) return; // e.g. canvas click if (this.isOpen && !this.container.contains(e.target)) { this.close(); } @@ -1829,12 +1831,13 @@ class CustomColorPicker { open(color = this.currentColor) { disableGizmos(); - + // Show first so layout has real sizes this.container.style.display = "block"; this.container.style.opacity = "1"; this.container.style.pointerEvents = "auto"; this.isOpen = true; + document.body.classList.add("color-picker-open"); // --- Positioning (unchanged) --- const colorButton = document.getElementById("colorPickerButton"); @@ -2020,6 +2023,7 @@ class CustomColorPicker { close() { this.container.style.display = "none"; this.isOpen = false; + document.body.classList.remove("color-picker-open"); document.removeEventListener("click", this.outsideClickHandler, true); } diff --git a/ui/gizmos.js b/ui/gizmos.js index a05fff85..79fed795 100644 --- a/ui/gizmos.js +++ b/ui/gizmos.js @@ -95,10 +95,31 @@ document.addEventListener("DOMContentLoaded", function () { // After color picker closes, start mesh selection pickMeshFromCanvas(); }, + excludeFromClose: (target) => { + // Don't close picker when clicking on the 3D canvas — we handle it directly + const canvas = document.getElementById("renderCanvas"); + return canvas && (canvas === target || canvas.contains(target)); + }, target: document.body, }); // Make accessible globally for translation updates window.flockColorPicker = colorPicker; + + // Direct painting: clicking/tapping the canvas while picker is open applies colour + const renderCanvas = document.getElementById("renderCanvas"); + if (renderCanvas) { + renderCanvas.addEventListener("click", (event) => { + if (!colorPicker?.isOpen) return; + // Use picker's live colour (not yet confirmed via "Use") + window.selectedColor = colorPicker.currentColor || window.selectedColor; + const canvasRect = renderCanvas.getBoundingClientRect(); + const [canvasX, canvasY] = getCanvasXAndCanvasYValues( + event, + canvasRect, + ); + applyColorAtPosition(canvasX, canvasY); + }); + } } // Attach click event to open custom color picker