Function: updateCoordInput() dalam key.js
Called: After every layer property change (drag, input modification, etc)
Purpose: Sync ALL input fields to reflect current layer selection state
// Handles:
updateCoordInput();
→ Reads: window.selected, selector.selectedLayers[], multiDragState
→ Writes: ALL input elements (xCoord, yCoord, rotation, scale, etc)
→ Logic: Single vs Multi-select display different values ("mixed" indicator)Function: getSelectedLayers() dalam studiopose.js
Returns: Array<Layer> of currently selected layers
const selected = getSelectedLayers();
// Returns array with 1 to N layers depending on:
// 1. Multi-select via selector.selectedLayers[]
// 2. Independent touch drag via multiDragState
// 3. Single selection via window.selected (fallback)Button: "Nyalakan Seleksi" dalam Panel2
Handler: Selector.toggleSelector() dalam selector.js
Result: Enable/disable multi-select mode with drag selection
User Input: "Kepala" pada layerName field
↓
handleLayerName("Kepala")
↓
selected.name = "Kepala"
↓
updateCoordInput()
↓
Input Fields Updated
↓
Panel2 menulayer synchronized
User Input: "45" pada rotation slider
↓
handleRotation("45")
↓
for (const layer of getSelectedLayers()) {
layer.rotation = 45;
}
↓
updateCoordInput()
↓
Input Fields Show: "45" (all same) or "mixed" (different)
↓
All Panels Synchronized
User drags layer on Panel1
↓
onLayerPointerMove(e)
↓
multiDragState[pointerId].layer.x += dx
multiDragState[pointerId].layer.y += dy
↓
updateCoordInput() // Called at end of onLayerPointerMove
↓
Input Fields: xCoord, yCoord updated
↓
If multi-select: shows average position
if (selectedLayers.length === 1) {
// Single-select: Show exact value
xCoordInput.value = "100";
xCoordInput.style.color = "inherit";
}
else {
// Multi-select: Check if all same
const allSame = selectedLayers.every(l => l.x === selectedLayers[0].x);
if (allSame) {
xCoordInput.value = "100"; // Exact value (all same)
xCoordInput.style.color = "inherit";
} else {
xCoordInput.value = "mixed (125)"; // Average position
xCoordInput.style.color = "#ff9800"; // Orange = mixed
}
}// Single layer
layerNameInput.value = "Kepala"; // String atau array item
// Multi-select: concatenate all names
const allNames = selectedLayers.flatMap(l => {
return Array.isArray(l.name) ? l.name : [l.name];
});
layerNameInput.value = allNames.join(', ');
// Result: "Head, Body, Foot"// In onLayerPointerDown(e, layer):
if (isTouch) {
multiDragState.set(e.pointerId, {
type: 'independent',
layer: layer, // Each pointer has its OWN layer
lastX: e.clientX,
lastY: e.clientY
});
}
// Result: Multiple fingers can drag DIFFERENT layers simultaneously1. touchstart → onLayerPointerDown()
↓ Create multiDragState entry for this pointerId
2. touchmove → onLayerPointerMove()
↓ Update layer position based on pointerId's state
↓ Call updateCoordInput()
3. touchend → onLayerPointerUp()
↓ Remove multiDragState entry for this pointerId
All these functions now:
- ✅ Support multi-select (apply to ALL selected)
- ✅ Call
updateCoordInput()at end - ✅ Record history for undo/redo
✅ handleLayerName(value)
→ Split by comma if needed
→ Apply to all selected layers
→ Concatenate in display
✅ handleXCoord(value)
→ Support center origin conversion
→ Multi-select: relative move
✅ handleYCoord(value)
→ Support center origin conversion
→ Multi-select: relative move
✅ handleRotation(value)
→ Apply to all
→ Parse modulo 360
✅ handleScale(value)
→ Apply to all
✅ handleSkewX/Y(value)
→ Apply to all
→ Sync slider ↔ input
✅ handleFlipHorizontal/Vertical(value)
→ Apply to all
→ Show indeterminate if mixed
✅ handleOpacity(value)
→ Apply to all selected layersCheck:
// In browser console:
console.log(getSelectedLayers()); // Should have layers
// In studiopose.js onLayerPointerMove, verify updateCoordInput() calledCheck:
// Verify selector exists:
window.selectorInstance // Should be defined
window.__selectorActive // Should toggle between true/falseCheck:
// Get a layer:
layers[0].name // Could be "Kepala" (string) or ["Kepala", "Body"] (array)
// Both supported! Display concatenates themCheck:
// Verify comparison logic:
const allSame = selectedLayers.every(l => l.x === selectedLayers[0].x);
// If allSame = false but values look same, might be float precision issue// Add this to onLayerPointerMove():
console.log('Before updateCoordInput');
updateCoordInput();
console.log('After updateCoordInput');// In getSelectedLayers():
console.log('selector.selectedLayers:', window.selectorInstance?.selectedLayers);
console.log('window.selected:', window.selected);
console.log('Result:', getSelectedLayers());// In handleRotation():
const allLayers = getSelectedLayers();
console.log('Rotating', allLayers.length, 'layers');
for (const layer of allLayers) {
console.log('Setting', layer.name, 'rotation to', value);
layer.rotation = value;
}// In updateCoordInput():
const names = Array.isArray(layer.name) ? layer.name : [layer.name];
console.log('Names:', names);
layerNameInput.value = names.join(', ');
console.log('Display:', layerNameInput.value);Key Files Modified:
1. js/studiocharacter/key.js
→ updateCoordInput() - CENTRAL SYNC POINT
→ getSelectedLayers() - GET SELECTION
2. js/studiocharacter/studiopose.js
→ handleLayerName()
→ handleXCoord(), handleYCoord()
→ handleRotation(), handleScale()
→ handleSkewX/Y(), handleFlipX/Y()
→ onLayerPointerMove() - ALREADY GOOD
→ getSelectedLayers() - ALREADY EXISTS
3. js/studiocharacter/layer.js
→ No changes needed (already supports array name)
4. js/studiocharacter/selector.js
→ No changes needed (already supports multi-select)
1. Click layer → highlight
2. Drag layer → position updates
3. Type rotation value → applies
4. All inputs reflect layer state ✓
1. Click "Nyalakan Seleksi"
2. Draw box around 3 layers
3. Drag one → all move
4. Panel2 shows: "Layer1, Layer2, Layer3" ✓
1. Select layer
2. Type "Head, Body, Foot"
3. Split and apply ✓
4. Display shows all names ✓
1. Two fingers on canvas
2. Drag first finger → layer1 moves
3. Drag second finger → layer2 moves
4. Both move independently ✓
// Quick access to properties:
layer.name // String or array
layer.x, layer.y // Position (handles parent relative)
layer.rotation // 0-360
layer.scale // Multiplier (1 = normal size)
layer.isFlipX // Boolean
layer.isFlipY // Boolean
layer.skewX, layer.skewY // Skew values
layer.opacity // 0-1 (1 = fully opaque)
layer.element // DOM reference
layer.element.clientWidth, .clientHeight // Size
// Selection:
window.selected // Single selected layer
window.selectorInstance.selectedLayers[] // Multi-selected
// Input elements:
document.getElementById('xCoord') // X position input
document.getElementById('yCoord') // Y position input
document.getElementById('layerName') // Layer name input
document.getElementById('rotation') // Rotation slider
document.getElementById('scale') // Scale input
// ... etc for all properties// Debounce approach:
let updateTimeout;
function updateCoordInputDebounced() {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(updateCoordInput, 50); // 50ms delay
}// Cache selected layers:
let selectedCache = [];
let selectedCacheDirty = true;
function getSelectedLayersCached() {
if (selectedCacheDirty) {
selectedCache = getSelectedLayers();
selectedCacheDirty = false;
}
return selectedCache;
}
// Mark cache dirty when selection changes:
// In selector.toggleSelector(), addLayerClickHandler(), etc:
selectedCacheDirty = true;- ✅ Works with existing history system
- ✅ Works with existing export functions
- ✅ Works with center origin mode
- ✅ Works with opacity/background settings
- ✅ Backward compatible (single-select still works)
⚠️ Multi-select requires selector button to toggle ON
| Feature | Before | After | Notes |
|---|---|---|---|
| Single-layer drag | ✅ | ✅ | All properties update |
| Multi-layer drag | ❌ | ✅ | Group drag & independent touch |
| Input sync (X, Y) | ✅ | ✅ | Plus all other properties |
| Input sync (all props) | ❌ | ✅ | Rotation, scale, flip, skew, opacity |
| Multi-select | Limited | ✅ | Proper toggle with visual feedback |
| Layer name array | ❌ | ✅ | "Head, Body, Foot" format |
| Touchscreen multi-touch | ❌ | ✅ | Independent pointer drag |
| Mixed value display | ❌ | ✅ | Shows "mixed" when different |
| History recording | ✅ | ✅ | All operations logged |
- Check browser console for JS errors
- Check that files saved correctly
- Reload page (Ctrl+R or Cmd+R)
- Clear browser cache (F12 → Storage → Clear All)
- Test in incognito/private mode
- Check Network tab to verify JS files loaded
- Set breakpoints in key.js updateCoordInput()
Last Updated: 2025-02-09 Status: Ready for testing