Skip to content

Commit

Permalink
Snap to mouse on time-graph-navigator click
Browse files Browse the repository at this point in the history
Snap to mouse on time-graph-navigator click

Adds background PIXI rectangles to the time-graph-navigator +
vertical-navigator components.  This rectangle listens for clicks then centers the handler on the mouse and resumes 'dragging' for navigating.

Fixes: #97

Signed-off-by: Will Yang <william.yang@ericsson.com>
  • Loading branch information
William Yang authored and bhufmann committed Mar 7, 2022
1 parent 9488cae commit be37b18
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 9 deletions.
70 changes: 66 additions & 4 deletions timeline-chart/src/layer/time-graph-navigator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import { BIMath } from "../bigint-utils";
export class TimeGraphNavigator extends TimeGraphLayer {

protected navigatorHandle: TimeGraphNavigatorHandle;
protected navigatorBackground: TimeGraphNavigatorBackground;
protected selectionRange?: TimeGraphRectangle;
private _updateHandler: { (): void; (viewRange: TimelineChart.TimeGraphRange): void; (selectionRange: TimelineChart.TimeGraphRange): void; (viewRange: TimelineChart.TimeGraphRange): void; (selectionRange: TimelineChart.TimeGraphRange): void; };

afterAddToContainer() {
this._updateHandler = (): void => this.update();
this.unitController.onViewRangeChanged(this._updateHandler);
this.navigatorBackground = new TimeGraphNavigatorBackground(this.unitController, this.stateController);
this.addChild(this.navigatorBackground);
this.navigatorHandle = new TimeGraphNavigatorHandle(this.unitController, this.stateController);
this.addChild(this.navigatorHandle);
this.unitController.onSelectionRangeChange(this._updateHandler);
Expand Down Expand Up @@ -66,11 +69,21 @@ export class TimeGraphNavigatorHandle extends TimeGraphComponent<null> {

constructor(protected unitController: TimeGraphUnitController, protected stateController: TimeGraphStateController) {
super('navigator_handle');
this.addEvent('mousedown', event => {
const moveStart: TimeGraphInteractionHandler = event => {
this.mouseStartX = event.data.global.x;
this.oldViewStart = this.unitController.viewRange.start;
this.mouseIsDown = true;
this.stateController.snapped = false;
}
const moveEnd = () => {
this.mouseIsDown = false;
}
this.addEvent('mouseover', event => {
if (this.stateController.snapped) {
moveStart(event);
}
}, this._displayObject);
this.addEvent('mousedown', moveStart, this._displayObject);
this.addEvent('mousemove', event => {
if (this.mouseIsDown) {
const delta = event.data.global.x - this.mouseStartX;
Expand All @@ -83,11 +96,9 @@ export class TimeGraphNavigatorHandle extends TimeGraphComponent<null> {
}
}
}, this._displayObject);
const moveEnd: TimeGraphInteractionHandler = event => {
this.mouseIsDown = false;
}
this.addEvent('mouseup', moveEnd, this._displayObject);
this.addEvent('mouseupoutside', moveEnd, this._displayObject);
document.addEventListener('snap-x-end', moveEnd);
}

render(): void {
Expand All @@ -104,4 +115,55 @@ export class TimeGraphNavigatorHandle extends TimeGraphComponent<null> {
color: 0x777769
})
}
}

export class TimeGraphNavigatorBackground extends TimeGraphComponent<null> {

protected snapEvent: CustomEvent;
protected snapEventString: string;

constructor(protected unitController: TimeGraphUnitController, protected stateController: TimeGraphStateController) {
super("navigator_background");
this.addEvent("mousedown", event => {
let x = event.data.getLocalPosition(this._displayObject).x;
let middle = BIMath.round((x / this.stateController.canvasDisplayWidth) * Number(this.unitController.absoluteRange));
// We have horizontal offset at point of click, now we need coord for start of handler.
let hVL = this.unitController.viewRangeLength / BigInt(2);
let start0 = middle - hVL;
let max = this.unitController.absoluteRange - this.unitController.viewRangeLength;
let min = BigInt(0);
// Clamp it.
const start = BIMath.clamp(start0, min, max);
this.unitController.viewRange = {
start,
end: start + this.unitController.viewRangeLength
}
// Set snapped state
this.toggleSnappedState(true);
}, this._displayObject);
// Custom event lets handler know 'mouseup' triggers.
this.snapEvent = new CustomEvent(this.snapEventString = 'snap-x-end');
const endSnap = () => {
this.toggleSnappedState(false);
document.dispatchEvent(this.snapEvent);
}
this.addEvent('mouseup', endSnap, this._displayObject);
this.addEvent('mouseupoutside', endSnap, this._displayObject);
}

protected toggleSnappedState = (bool: boolean) => {
this.stateController.snapped = bool;
}

render(): void {
this.rect({
height: 20,
position: {
x: 0,
y: 0
},
width: this.stateController.canvasDisplayWidth,
opacity: 0
});
}
}
71 changes: 66 additions & 5 deletions timeline-chart/src/layer/time-graph-vertical-scrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TimeGraphRowController } from "../time-graph-row-controller";
export class TimeGraphVerticalScrollbar extends TimeGraphChartLayer {

protected navigatorHandle: TimeGraphVerticalScrollbarHandle;
protected navigatorBackground: TimeGraphVerticalScrollbarBackground;
protected selectionRange?: TimeGraphRectangle;

protected factor: number;
Expand All @@ -18,11 +19,14 @@ export class TimeGraphVerticalScrollbar extends TimeGraphChartLayer {
protected afterAddToContainer() {
this.updateFactor();
this.navigatorHandle = new TimeGraphVerticalScrollbarHandle(this.rowController, this.stateController, this.factor);
this.navigatorBackground = new TimeGraphVerticalScrollbarBackground(this.rowController, this.stateController, this.factor);
this.addChild(this.navigatorBackground);
this.addChild(this.navigatorHandle);
this.rowController.onVerticalOffsetChangedHandler(() => this.update());
this.rowController.onTotalHeightChangedHandler(() => {
this.updateFactor();
this.navigatorHandle.updateFactor(this.factor);
this.navigatorBackground.updateFactor(this.factor);
this.update()
});
}
Expand All @@ -38,6 +42,8 @@ export class TimeGraphVerticalScrollbar extends TimeGraphChartLayer {
update() {
this.navigatorHandle.clear();
this.navigatorHandle.render();
this.navigatorBackground.clear();
this.navigatorBackground.render();
}
}

Expand All @@ -52,23 +58,31 @@ export class TimeGraphVerticalScrollbarHandle extends TimeGraphComponent<null> {

constructor(protected rowController: TimeGraphRowController, protected stateController: TimeGraphStateController, protected factor: number) {
super('vscroll_handle');
this.addEvent('mousedown', event => {
const moveStart: TimeGraphInteractionHandler = event => {
this.mouseStartY = event.data.global.y;
this.oldVerticalOffset = this.rowController.verticalOffset
this.oldVerticalOffset = this.rowController.verticalOffset;
this.mouseIsDown = true;
this.stateController.snapped = false;
};
const moveEnd = () => {
this.mouseIsDown = false;
}
this.addEvent('mouseover', (event) => {
if (this.stateController.snapped) {
moveStart(event);
}
}, this._displayObject);
this.addEvent('mousedown', moveStart, this._displayObject);
this.addEvent('mousemove', event => {
if (this.mouseIsDown) {
const delta = (event.data.global.y - this.mouseStartY) / this.factor;
const verticalOffset = this.oldVerticalOffset + delta;
this.rowController.verticalOffset = Math.max(0, Math.min(this.rowController.totalHeight - this.stateController.canvasDisplayHeight, verticalOffset));
}
}, this._displayObject);
const moveEnd: TimeGraphInteractionHandler = event => {
this.mouseIsDown = false;
}
this.addEvent('mouseup', moveEnd, this._displayObject);
this.addEvent('mouseupoutside', moveEnd, this._displayObject);
document.addEventListener('snap-y-end', moveEnd);
}

updateFactor(factor: number) {
Expand All @@ -85,4 +99,51 @@ export class TimeGraphVerticalScrollbarHandle extends TimeGraphComponent<null> {
color: 0x777769
})
}
}

export class TimeGraphVerticalScrollbarBackground extends TimeGraphComponent<null> {

protected snapEvent: CustomEvent;
protected snapEventString: string;

constructor(protected rowController: TimeGraphRowController, protected stateController: TimeGraphStateController, protected factor: number) {
super("vscroll_background");
this.addEvent("mousedown", event => {
let yPosition = event.data.getLocalPosition(this._displayObject).y;
let vOffset = (yPosition/this.stateController.canvasDisplayHeight) * this.rowController.totalHeight;
// We have vertical offset at point of click, but need to make it the center of scrollbar.
let scrollBarHeight = (this.rowController.totalHeight * this.factor);
vOffset = vOffset - (scrollBarHeight / 2);
// Clamp it
let vOffsetClamped = Math.max(0, Math.min(this.rowController.totalHeight - this.stateController.canvasDisplayHeight, vOffset));
this.rowController.verticalOffset = vOffsetClamped;
// Set snapped state
this.toggleSnappedState(true);
}, this._displayObject);
// Emit custom event to let vertical handler know when 'mousedown' is released.
this.snapEvent = new CustomEvent(this.snapEventString = 'snap-y-end');
const endSnap = () => {
this.toggleSnappedState(false);
document.dispatchEvent(this.snapEvent);
}
this.addEvent('mouseup', endSnap, this._displayObject);
this.addEvent('mouseupoutside', endSnap, this._displayObject);
}

updateFactor(factor: number) {
this.factor = factor;
}

protected toggleSnappedState = (bool: boolean) => {
this.stateController.snapped = bool;
}

render(): void {
this.rect({
height: this.stateController.canvasDisplayHeight,
position: { x: 0, y: 0 },
width: 10,
opacity: 0
});
}
}
4 changes: 4 additions & 0 deletions timeline-chart/src/time-graph-state-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export class TimeGraphStateController {
x: number;
y: number;
};

snapped: boolean;

protected ratio: number;

Expand Down Expand Up @@ -32,6 +34,7 @@ export class TimeGraphStateController {
this.zoomChangedHandlers = [];
this.positionChangedHandlers = [];
this.canvasDisplayWidthChangedHandlers = [];
this.snapped = false;
}

protected handleZoomChange() {
Expand Down Expand Up @@ -102,4 +105,5 @@ export class TimeGraphStateController {
this._positionOffset = value;
this.handlePositionChange();
}

}

0 comments on commit be37b18

Please sign in to comment.