Skip to content

Commit 6bab4c4

Browse files
committed
Add slider knob hover interaction and regression test
1 parent c9ed2d9 commit 6bab4c4

3 files changed

Lines changed: 98 additions & 9 deletions

File tree

js/drawing/resolution_master_draw_methods.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const drawingMethods = {
1919
const props = node.properties;
2020
const margin = 10;
2121
const spacing = this.getManualSpacing();
22+
this.sliderKnobAreas = {};
2223

2324
let currentY = this.getManualContentStartY();
2425

@@ -348,7 +349,7 @@ export const drawingMethods = {
348349
const sliderWidth = node.size[0] - sliderX - valueWidth - margin;
349350

350351
this.controls.snapSlider = { x: sliderX, y, w: sliderWidth, h: 28 };
351-
this.drawSlider(ctx, sliderX, y, sliderWidth, 28, props.snapValue, props.action_slider_snap_min, props.action_slider_snap_max, props.action_slider_snap_step);
352+
this.drawSlider(ctx, sliderX, y, sliderWidth, 28, props.snapValue, props.action_slider_snap_min, props.action_slider_snap_max, props.action_slider_snap_step, 'snapSlider');
352353
const snapValueX = sliderX + sliderWidth + gap;
353354
this.controls.snapValueArea = { x: snapValueX, y, w: valueWidth, h: 28 };
354355

@@ -852,7 +853,7 @@ export const drawingMethods = {
852853
ctx.fillText(label, margin, y);
853854

854855
this.controls[controlName] = { x: margin, y: y + 10, w, h: 25 };
855-
this.drawSlider(ctx, margin, y + 10, w, 25, value, min, max, step);
856+
this.drawSlider(ctx, margin, y + 10, w, 25, value, min, max, step, controlName);
856857

857858
ctx.textAlign = "right";
858859
ctx.fillText(value.toString(), node.size[0] - margin, y + 25);
@@ -947,25 +948,41 @@ export const drawingMethods = {
947948
}
948949
},
949950

950-
drawSlider(ctx, x, y, w, h, value, min, max, step) {
951+
drawSlider(ctx, x, y, w, h, value, min, max, step, controlName = null) {
951952
ctx.fillStyle = "#222";
952953
ctx.beginPath();
953954
ctx.roundRect(x, y + h / 2 - 3, w, 6, 3);
954955
ctx.fill();
955956
const pos = Math.max(0, Math.min(1, (value - min) / (max - min)));
956957
const knobX = x + w * pos;
957958
const knobY = y + h / 2;
959+
if (controlName) {
960+
this.sliderKnobAreas = this.sliderKnobAreas || {};
961+
this.sliderKnobAreas[controlName] = {
962+
x: knobX - 10,
963+
y: knobY - 10,
964+
w: 20,
965+
h: 20
966+
};
967+
}
968+
const isKnobHovered = controlName && this.hoverElement === controlName;
958969

970+
ctx.save();
971+
if (isKnobHovered) {
972+
ctx.shadowColor = "rgba(255, 255, 255, 0.32)";
973+
ctx.shadowBlur = 6;
974+
}
959975
const grad = ctx.createLinearGradient(knobX - 7, knobY - 7, knobX + 7, knobY + 7);
960-
grad.addColorStop(0, "#e0e0e0");
961-
grad.addColorStop(1, "#c0c0c0");
976+
grad.addColorStop(0, isKnobHovered ? "#fafafa" : "#e0e0e0");
977+
grad.addColorStop(1, isKnobHovered ? "#dddddd" : "#c0c0c0");
962978
ctx.fillStyle = grad;
963-
ctx.strokeStyle = "#111";
964-
ctx.lineWidth = 1;
979+
ctx.strokeStyle = isKnobHovered ? "rgba(255, 255, 255, 0.72)" : "#111";
980+
ctx.lineWidth = isKnobHovered ? 1.4 : 1;
965981
ctx.beginPath();
966982
ctx.arc(knobX, knobY, 8, 0, 2 * Math.PI);
967983
ctx.fill();
968984
ctx.stroke();
985+
ctx.restore();
969986
},
970987

971988
drawDropdown(ctx, x, y, w, h, text, hover = false) {
@@ -1169,7 +1186,7 @@ export const drawingMethods = {
11691186
if (config.controlType === 'slider') {
11701187
this.controls[config.mainControl] = { x: currentX, y, w: layout.sliderWidth, h: 28 };
11711188
this.drawSlider(ctx, currentX, y, layout.sliderWidth, 28,
1172-
props[config.valueProperty], config.min, config.max, config.step);
1189+
props[config.valueProperty], config.min, config.max, config.step, config.mainControl);
11731190
currentX += layout.sliderWidth + layout.gap;
11741191
} else if (config.controlType === 'dropdown') {
11751192
this.controls[config.mainControl] = { x: currentX, y, w: layout.dropdownWidth, h: 28 };

js/interaction/resolution_master_interaction_methods.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,8 +513,16 @@ export const interactionMethods = {
513513
} else if (this.controls.canvas2dTopHandle && this.isPointInControl(relX, relY, this.controls.canvas2dTopHandle)) {
514514
newHover = 'canvas2dTopHandle';
515515
} else {
516+
for (const sliderName in this.sliderKnobAreas || {}) {
517+
if (this.isPointInControl(relX, relY, this.sliderKnobAreas[sliderName])) {
518+
newHover = sliderName;
519+
break;
520+
}
521+
}
516522
for (const element in this.controls) {
517-
if (element !== 'canvas2dRightHandle' && element !== 'canvas2dTopHandle' &&
523+
if (!newHover
524+
&& !element.endsWith('Slider')
525+
&& element !== 'canvas2dRightHandle' && element !== 'canvas2dTopHandle' &&
518526
this.isPointInControl(relX, relY, this.controls[element])) {
519527
newHover = element;
520528
break;

tests/js/output_value_drawing.test.mjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,67 @@ test("scaling numeric values use the persistent editable value background", () =
220220
assert.equal(ctx.rectangles[0].fillStyle, "rgba(0,0,0,0)");
221221
assert.equal(ctx.rectangles[0].strokeStyle, "rgba(205, 210, 220, 0.18)");
222222
});
223+
224+
test("slider knob gets a brighter gradient and glow on direct hover", () => {
225+
const gradients = [];
226+
let knobPaint = null;
227+
let currentShape = null;
228+
const stateStack = [];
229+
const ctx = {
230+
fillStyle: "",
231+
strokeStyle: "",
232+
lineWidth: 1,
233+
shadowColor: "",
234+
shadowBlur: 0,
235+
save() {
236+
stateStack.push({
237+
fillStyle: this.fillStyle,
238+
strokeStyle: this.strokeStyle,
239+
lineWidth: this.lineWidth,
240+
shadowColor: this.shadowColor,
241+
shadowBlur: this.shadowBlur
242+
});
243+
},
244+
restore() {
245+
Object.assign(this, stateStack.pop());
246+
},
247+
beginPath() {
248+
currentShape = null;
249+
},
250+
roundRect() {
251+
currentShape = "track";
252+
},
253+
arc() {
254+
currentShape = "knob";
255+
},
256+
fill() {
257+
if (currentShape === "knob") {
258+
knobPaint = {
259+
fillStyle: this.fillStyle,
260+
shadowColor: this.shadowColor,
261+
shadowBlur: this.shadowBlur
262+
};
263+
}
264+
},
265+
stroke() {},
266+
createLinearGradient() {
267+
const gradient = {
268+
stops: [],
269+
addColorStop(offset, color) {
270+
this.stops.push([offset, color]);
271+
}
272+
};
273+
gradients.push(gradient);
274+
return gradient;
275+
}
276+
};
277+
const controller = { hoverElement: "snapSlider", sliderKnobAreas: {} };
278+
Object.assign(controller, drawingMethods);
279+
280+
controller.drawSlider(ctx, 0, 0, 100, 28, 50, 0, 100, 1, "snapSlider");
281+
282+
assert.deepEqual(controller.sliderKnobAreas.snapSlider, { x: 40, y: 4, w: 20, h: 20 });
283+
assert.deepEqual(gradients[0].stops, [[0, "#fafafa"], [1, "#dddddd"]]);
284+
assert.equal(knobPaint.shadowColor, "rgba(255, 255, 255, 0.32)");
285+
assert.equal(knobPaint.shadowBlur, 6);
286+
});

0 commit comments

Comments
 (0)