Replace Selection interface with CanvasRange
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Rob Watson 2022-02-04 08:35:54 +01:00
parent b64f0b4daa
commit 3dcc1edc62
6 changed files with 107 additions and 110 deletions

View File

@ -49,8 +49,8 @@ const initialState: State = {
viewport: { start: 0, end: 0 },
overviewPeaks: from([]),
waveformPeaks: from([]),
selectionCanvas: { start: 0, end: 0 },
viewportCanvas: { start: 0, end: 0 },
selectionCanvas: { x1: 0, x2: 0 },
viewportCanvas: { x1: 0, x2: 0 },
position: { currentTime: 0, frame: 0, percent: 0 },
audioSrc: '',
videoSrc: '',

View File

@ -9,8 +9,8 @@ const initialState: State = {
viewport: { start: 0, end: 441000 },
overviewPeaks: from([]),
waveformPeaks: from([]),
selectionCanvas: { start: 0, end: 0 },
viewportCanvas: { start: 0, end: CanvasLogicalWidth },
selectionCanvas: { x1: 0, x2: 0 },
viewportCanvas: { x1: 0, x2: CanvasLogicalWidth },
position: { currentTime: 0, frame: 0, percent: 0 },
audioSrc: '',
videoSrc: '',

View File

@ -1,7 +1,7 @@
import { MediaSet } from './generated/media_set';
import { Observable } from 'rxjs';
import { SelectionChangeEvent } from './HudCanvas';
import { SelectionMode, CanvasLogicalWidth } from './HudCanvasState';
import { CanvasRange, SelectionMode, CanvasLogicalWidth } from './HudCanvasState';
import { PlayState } from './Player';
import { zoomViewportIn, zoomViewportOut } from './helpers/zoom';
import frameToWaveformCanvasX from './helpers/frameToWaveformCanvasX';
@ -15,12 +15,6 @@ export interface FrameRange {
end: number;
}
// TODO: rename to x1, x2
interface CanvasRange {
start: number;
end: number;
}
interface Position {
currentTime: number;
frame: number;
@ -224,10 +218,10 @@ function setViewport(state: State, { viewport }: SetViewportAction): State {
...state,
viewport: viewport,
viewportCanvas: {
start: Math.round(
x1: Math.round(
(viewport.start / mediaSet.audioFrames) * CanvasLogicalWidth
),
end: Math.round(
x2: Math.round(
(viewport.end / mediaSet.audioFrames) * CanvasLogicalWidth
),
},
@ -298,10 +292,10 @@ function handleViewportChanged(
const newViewport = {
start: Math.round(
mediaSet.audioFrames * (canvasRange.start / CanvasLogicalWidth)
mediaSet.audioFrames * (canvasRange.x1 / CanvasLogicalWidth)
),
end: Math.round(
mediaSet.audioFrames * (canvasRange.end / CanvasLogicalWidth)
mediaSet.audioFrames * (canvasRange.x2 / CanvasLogicalWidth)
),
};
@ -331,8 +325,8 @@ function handleWaveformSelectionChanged(
const framesPerPixel = (viewport.end - viewport.start) / CanvasLogicalWidth;
const newSelection = {
start: Math.round(viewport.start + canvasRange.start * framesPerPixel),
end: Math.round(viewport.start + canvasRange.end * framesPerPixel),
start: Math.round(viewport.start + canvasRange.x1 * framesPerPixel),
end: Math.round(viewport.start + canvasRange.x2 * framesPerPixel),
};
let currentTime = state.currentTime;
@ -433,8 +427,8 @@ function selectionToWaveformCanvasRange(
);
if (x1 == x2) {
return { start: 0, end: 0 };
return { x1: 0, x2: 0 };
}
return { start: x1 || 0, end: x2 || CanvasLogicalWidth };
return { x1: x1 || 0, x2: x2 || CanvasLogicalWidth };
}

View File

@ -4,8 +4,8 @@ import {
State,
SelectionMode,
HoverState,
Selection,
EmptySelectionAction,
CanvasRange,
} from './HudCanvasState';
import constrainNumeric from './helpers/constrainNumeric';
export { EmptySelectionAction } from './HudCanvasState';
@ -24,17 +24,17 @@ interface Props {
emptySelectionAction: EmptySelectionAction;
styles: Styles;
position: number | null;
selection: Selection;
selection: CanvasRange;
onSelectionChange: (selectionState: SelectionChangeEvent) => void;
}
export interface SelectionChangeEvent {
selection: Selection;
selection: CanvasRange;
mode: SelectionMode;
prevMode: SelectionMode;
}
const emptySelection: Selection = { start: 0, end: 0 };
const emptySelection: CanvasRange = { x1: 0, x2: 0 };
const initialState: State = {
width: 0,
@ -134,9 +134,9 @@ export const HudCanvas: React.FC<Props> = ({
state.hoverState == HoverState.OverSelection ? '0.15' : '0.13';
ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
ctx.rect(
currentSelection.start,
currentSelection.x1,
borderLineWidth,
currentSelection.end - currentSelection.start,
currentSelection.x2 - currentSelection.x1,
canvas.height - borderLineWidth * 2
);
ctx.fill();
@ -146,8 +146,8 @@ export const HudCanvas: React.FC<Props> = ({
const hoverX = state.hoverX;
if (
(hoverX != 0 && hoverX < currentSelection.start) ||
hoverX > currentSelection.end
(hoverX != 0 && hoverX < currentSelection.x1) ||
hoverX > currentSelection.x2
) {
ctx.beginPath();
ctx.strokeStyle = hoverPositionStrokeStyle;

View File

@ -9,8 +9,8 @@ const initialState = {
width: 5000,
emptySelectionAction: EmptySelectionAction.SelectNothing,
hoverX: 0,
selection: { start: 0, end: 0 },
origSelection: { start: 0, end: 0 },
selection: { x1: 0, x2: 0 },
origSelection: { x1: 0, x2: 0 },
mousedownX: 0,
mode: SelectionMode.Normal,
prevMode: SelectionMode.Normal,
@ -24,9 +24,9 @@ describe('stateReducer', () => {
it('sets the selection', () => {
const state = stateReducer(
{ ...initialState },
{ type: 'setselection', x: 0, selection: { start: 100, end: 200 } }
{ type: 'setselection', x: 0, selection: { x1: 100, x2: 200 } }
);
expect(state.selection).toEqual({ start: 100, end: 200 });
expect(state.selection).toEqual({ x1: 100, x2: 200 });
expect(state.shouldPublish).toBeFalsy();
});
});
@ -35,30 +35,30 @@ describe('stateReducer', () => {
{
name: 'entering resizing start',
x: 995,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
wantMode: SelectionMode.ResizingStart,
wantSelection: { start: 1000, end: 2000 },
wantSelection: { x1: 1000, x2: 2000 },
},
{
name: 'entering resizing end',
x: 2003,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
wantMode: SelectionMode.ResizingEnd,
wantSelection: { start: 1000, end: 2000 },
wantSelection: { x1: 1000, x2: 2000 },
},
{
name: 'entering dragging',
x: 1500,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
wantMode: SelectionMode.Dragging,
wantSelection: { start: 1000, end: 2000 },
wantSelection: { x1: 1000, x2: 2000 },
},
{
name: 'entering selecting',
x: 10,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
wantMode: SelectionMode.Selecting,
wantSelection: { start: 10, end: 10 },
wantSelection: { x1: 10, x2: 10 },
},
])('mousedown', ({ name, x, selection, wantMode, wantSelection }) => {
test(`${name} generates the expected state`, () => {
@ -77,24 +77,24 @@ describe('stateReducer', () => {
{
name: 're-entering normal mode',
x: 1200,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
emptySelectionAction: EmptySelectionAction.SelectNothing,
wantSelection: { start: 1000, end: 2000 },
wantSelection: { x1: 1000, x2: 2000 },
},
{
name: 'when nothing is selected and emptySelectionAction is SelectNothing',
x: 1200,
selection: { start: 1000, end: 1000 },
selection: { x1: 1000, x2: 1000 },
emptySelectionAction: EmptySelectionAction.SelectNothing,
wantSelection: { start: 1200, end: 1200 },
wantSelection: { x1: 1200, x2: 1200 },
},
{
// TODO: broken
name: 'when nothing is selected and emptySelectionAction is SelectPrevious',
x: 1200,
selection: { start: 1000, end: 2000 },
selection: { x1: 1000, x2: 2000 },
emptySelectionAction: EmptySelectionAction.SelectPrevious,
wantSelection: { start: 1000, end: 2000 },
wantSelection: { x1: 1000, x2: 2000 },
},
])(
'mouseup',
@ -122,14 +122,14 @@ describe('stateReducer', () => {
const state = stateReducer(
{
...initialState,
selection: { start: 2000, end: 3000 },
selection: { x1: 2000, x2: 3000 },
mode: SelectionMode.Dragging,
mousedownX: 475,
},
{ type: 'mouseleave', x: 500 }
);
expect(state.mode).toEqual(SelectionMode.Dragging);
expect(state.selection).toEqual({ start: 2000, end: 3000 });
expect(state.selection).toEqual({ x1: 2000, x2: 3000 });
expect(state.mousedownX).toEqual(475);
expect(state.hoverX).toEqual(0);
expect(state.shouldPublish).toBeFalsy();
@ -143,7 +143,7 @@ describe('stateReducer', () => {
name: 'hovering over selection start',
mode: SelectionMode.Normal,
x: 997,
selection: { start: 1000, end: 3000 },
selection: { x1: 1000, x2: 3000 },
wantHoverState: HoverState.OverSelectionStart,
shouldPublish: false,
},
@ -151,7 +151,7 @@ describe('stateReducer', () => {
name: 'hovering over selection end',
mode: SelectionMode.Normal,
x: 3009,
selection: { start: 1000, end: 3000 },
selection: { x1: 1000, x2: 3000 },
wantHoverState: HoverState.OverSelectionEnd,
shouldPublish: false,
},
@ -159,7 +159,7 @@ describe('stateReducer', () => {
name: 'hovering over selection',
mode: SelectionMode.Normal,
x: 1200,
selection: { start: 1000, end: 3000 },
selection: { x1: 1000, x2: 3000 },
wantHoverState: HoverState.OverSelection,
shouldPublish: false,
},
@ -167,7 +167,7 @@ describe('stateReducer', () => {
name: 'hovering elsewhere',
mode: SelectionMode.Normal,
x: 300,
selection: { start: 1000, end: 3000 },
selection: { x1: 1000, x2: 3000 },
wantHoverState: HoverState.Normal,
shouldPublish: false,
},
@ -177,8 +177,8 @@ describe('stateReducer', () => {
mode: SelectionMode.Selecting,
x: 3005,
mousedownX: 2000,
selection: { start: 2000, end: 3000 },
wantSelection: { start: 2000, end: 3005 },
selection: { x1: 2000, x2: 3000 },
wantSelection: { x1: 2000, x2: 3005 },
shouldPublish: true,
},
{
@ -186,8 +186,8 @@ describe('stateReducer', () => {
mode: SelectionMode.Selecting,
x: 1995,
mousedownX: 2000,
selection: { start: 2000, end: 2002 },
wantSelection: { start: 1995, end: 2000 },
selection: { x1: 2000, x2: 2002 },
wantSelection: { x1: 1995, x2: 2000 },
shouldPublish: true,
},
// Dragging mode
@ -196,9 +196,9 @@ describe('stateReducer', () => {
mode: SelectionMode.Dragging,
x: 1220,
mousedownX: 1200,
selection: { start: 1000, end: 1500 },
origSelection: { start: 1000, end: 1500 },
wantSelection: { start: 1020, end: 1520 },
selection: { x1: 1000, x2: 1500 },
origSelection: { x1: 1000, x2: 1500 },
wantSelection: { x1: 1020, x2: 1520 },
shouldPublish: true,
},
{
@ -206,9 +206,9 @@ describe('stateReducer', () => {
mode: SelectionMode.Dragging,
x: 30,
mousedownX: 50,
selection: { start: 10, end: 210 },
origSelection: { start: 10, end: 210 },
wantSelection: { start: 0, end: 200 },
selection: { x1: 10, x2: 210 },
origSelection: { x1: 10, x2: 210 },
wantSelection: { x1: 0, x2: 200 },
shouldPublish: true,
},
{
@ -216,9 +216,9 @@ describe('stateReducer', () => {
mode: SelectionMode.Dragging,
x: 1400,
mousedownX: 1250,
selection: { start: 4800, end: 4900 },
origSelection: { start: 4800, end: 4900 },
wantSelection: { start: 4900, end: 5000 },
selection: { x1: 4800, x2: 4900 },
origSelection: { x1: 4800, x2: 4900 },
wantSelection: { x1: 4900, x2: 5000 },
shouldPublish: true,
},
// ResizingStart mode
@ -226,18 +226,18 @@ describe('stateReducer', () => {
name: 'when not crossing over',
mode: SelectionMode.ResizingStart,
x: 2020,
selection: { start: 2000, end: 3000 },
origSelection: { start: 2000, end: 3000 },
wantSelection: { start: 2020, end: 3000 },
selection: { x1: 2000, x2: 3000 },
origSelection: { x1: 2000, x2: 3000 },
wantSelection: { x1: 2020, x2: 3000 },
shouldPublish: true,
},
{
name: 'when crossing over',
mode: SelectionMode.ResizingStart,
x: 2010,
selection: { start: 2000, end: 2002 },
origSelection: { start: 2000, end: 2002 },
wantSelection: { start: 2002, end: 2010 },
selection: { x1: 2000, x2: 2002 },
origSelection: { x1: 2000, x2: 2002 },
wantSelection: { x1: 2002, x2: 2010 },
shouldPublish: true,
},
// ResizingEnd mode
@ -245,18 +245,18 @@ describe('stateReducer', () => {
name: 'when not crossing over',
mode: SelectionMode.ResizingEnd,
x: 2007,
selection: { start: 1000, end: 2000 },
origSelection: { start: 1000, end: 2000 },
wantSelection: { start: 1000, end: 2007 },
selection: { x1: 1000, x2: 2000 },
origSelection: { x1: 1000, x2: 2000 },
wantSelection: { x1: 1000, x2: 2007 },
shouldPublish: true,
},
{
name: 'when crossing over',
mode: SelectionMode.ResizingEnd,
x: 1995,
selection: { start: 2000, end: 2002 },
origSelection: { start: 2000, end: 2002 },
wantSelection: { start: 1995, end: 2000 },
selection: { x1: 2000, x2: 2002 },
origSelection: { x1: 2000, x2: 2002 },
wantSelection: { x1: 1995, x2: 2000 },
shouldPublish: true,
},
])(
@ -267,7 +267,7 @@ describe('stateReducer', () => {
x,
mousedownX = 0,
selection,
origSelection = { start: 0, end: 0 },
origSelection = { x1: 0, x2: 0 },
wantSelection = selection,
wantHoverState = HoverState.Normal,
shouldPublish,

View File

@ -3,6 +3,11 @@ import constrainNumeric from './helpers/constrainNumeric';
export const CanvasLogicalWidth = 2000;
export const CanvasLogicalHeight = 500;
export interface CanvasRange {
x1: number;
x2: number;
}
export enum HoverState {
Normal,
OverSelectionStart,
@ -15,11 +20,6 @@ export enum EmptySelectionAction {
SelectPrevious,
}
export interface Selection {
start: number;
end: number;
}
export enum SelectionMode {
Normal,
Selecting,
@ -32,8 +32,8 @@ export interface State {
width: number;
emptySelectionAction: EmptySelectionAction;
hoverX: number;
selection: Selection;
origSelection: Selection;
selection: CanvasRange;
origSelection: CanvasRange;
mousedownX: number;
mode: SelectionMode;
prevMode: SelectionMode;
@ -47,7 +47,7 @@ interface SelectionAction {
x: number;
// TODO: selection is only used for the setselection SelectionAction. Improve
// the typing here.
selection?: Selection;
selection?: CanvasRange;
}
export const stateReducer = (
@ -62,7 +62,7 @@ export const stateReducer = (
{ x, type, selection: selectionToSet }: SelectionAction
): State => {
let mode: SelectionMode;
let newSelection: Selection;
let newSelection: CanvasRange;
let hoverX: number;
let mousedownX: number;
let cursorClass: string;
@ -71,7 +71,7 @@ export const stateReducer = (
switch (type) {
case 'setselection':
newSelection = selectionToSet || { start: 0, end: 0 };
newSelection = selectionToSet || { x1: 0, x2: 0 };
mousedownX = prevMousedownX;
mode = SelectionMode.Normal;
cursorClass = 'cursor-auto';
@ -97,7 +97,7 @@ export const stateReducer = (
mode = SelectionMode.Dragging;
cursorClass = 'cursor-move';
} else {
newSelection = { start: x, end: x };
newSelection = { x1: x, x2: x };
mode = SelectionMode.Selecting;
cursorClass = 'cursor-col-resize';
}
@ -114,10 +114,10 @@ export const stateReducer = (
hoverState = HoverState.Normal;
if (
newSelection.start == newSelection.end &&
newSelection.x1 == newSelection.x2 &&
emptySelectionAction == EmptySelectionAction.SelectNothing
) {
newSelection = { start: x, end: x };
newSelection = { x1: x, x2: x };
}
break;
@ -161,13 +161,13 @@ export const stateReducer = (
mode = SelectionMode.Selecting;
if (x < prevMousedownX) {
newSelection = {
start: x,
end: prevMousedownX,
x1: x,
x2: prevMousedownX,
};
} else {
newSelection = {
start: prevMousedownX,
end: x,
x1: prevMousedownX,
x2: x,
};
}
@ -178,15 +178,15 @@ export const stateReducer = (
cursorClass = 'cursor-move';
const diff = x - prevMousedownX;
const selectionWidth = origSelection.end - origSelection.start;
const selectionWidth = origSelection.x2 - origSelection.x1;
let start = Math.max(0, origSelection.start + diff);
let start = Math.max(0, origSelection.x1 + diff);
let end = start + selectionWidth;
if (end > width) {
end = width;
start = end - selectionWidth;
}
newSelection = { start: start, end: end };
newSelection = { x1: start, x2: end };
break;
}
@ -195,10 +195,10 @@ export const stateReducer = (
cursorClass = 'cursor-col-resize';
const start = constrainNumeric(x, width);
if (start > origSelection.end) {
newSelection = { start: origSelection.end, end: start };
if (start > origSelection.x2) {
newSelection = { x1: origSelection.x2, x2: start };
} else {
newSelection = { ...origSelection, start: start };
newSelection = { ...origSelection, x1: start };
}
break;
@ -208,13 +208,13 @@ export const stateReducer = (
cursorClass = 'cursor-col-resize';
const end = constrainNumeric(x, width);
if (end < origSelection.start) {
if (end < origSelection.x1) {
newSelection = {
start: end,
end: origSelection.start,
x1: end,
x2: origSelection.x1,
};
} else {
newSelection = { ...origSelection, end: x };
newSelection = { ...origSelection, x2: x };
}
break;
@ -250,14 +250,17 @@ export const stateReducer = (
const hoverOffset = 10;
const isHoveringSelectionStart = (x: number, selection: Selection): boolean => {
return x > selection.start - hoverOffset && x < selection.start + hoverOffset;
const isHoveringSelectionStart = (
x: number,
selection: CanvasRange
): boolean => {
return x > selection.x1 - hoverOffset && x < selection.x1 + hoverOffset;
};
const isHoveringSelectionEnd = (x: number, selection: Selection): boolean => {
return x > selection.end - hoverOffset && x < selection.end + hoverOffset;
const isHoveringSelectionEnd = (x: number, selection: CanvasRange): boolean => {
return x > selection.x2 - hoverOffset && x < selection.x2 + hoverOffset;
};
const isHoveringSelection = (x: number, selection: Selection): boolean => {
return x >= selection.start && x <= selection.end;
const isHoveringSelection = (x: number, selection: CanvasRange): boolean => {
return x >= selection.x1 && x <= selection.x2;
};