From edf7ccb9446851828d14a5e730fa518c9894d4c9 Mon Sep 17 00:00:00 2001 From: Andrew Levans <121060410+ALevansSamsung@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:04:33 -0500 Subject: [PATCH] Don't resize outside timeline boundary (#119) * Don't resize outside timeline boundary * Stop resize when mouse enters selection panel * add 500px of empty space to bottom of timeline scroll view * Consolidate duplicate resize logic * use Math.min --- ui/src/assets/common.scss | 4 + ui/src/frontend/track_group_panel.ts | 52 +++-------- ui/src/frontend/track_panel.ts | 126 +++++++++++++++++---------- 3 files changed, 96 insertions(+), 86 deletions(-) diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss index f90610c15f..c98785f526 100644 --- a/ui/src/assets/common.scss +++ b/ui/src/assets/common.scss @@ -522,6 +522,10 @@ canvas.main-canvas { user-select: none; } +.scrolling-panel-container > .panels > *:last-child { + margin-bottom: 500px; +} + .panel { position: relative; // Otherwise canvas covers panel dom. diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts index 511686c125..516173d754 100644 --- a/ui/src/frontend/track_group_panel.ts +++ b/ui/src/frontend/track_group_panel.ts @@ -34,7 +34,7 @@ import { } from './icons'; import {Panel, PanelSize} from './panel'; import {Track} from './track'; -import {TrackButton, TrackContent} from './track_panel'; +import {TrackButton, TrackContent, checkTrackForResizability, resizeTrack} from './track_panel'; import {trackRegistry} from './track_registry'; import { drawVerticalLineAtTime, @@ -47,7 +47,6 @@ interface Attrs { selectable: boolean; } -const MOUSE_TARGETING_THRESHOLD_PX = 5; export class TrackGroupPanel extends Panel { private readonly trackGroupId: string; private shellWidth = 0; @@ -101,55 +100,28 @@ export class TrackGroupPanel extends Panel { get summaryTrackState(): TrackState { return assertExists(globals.state.tracks[this.trackGroupState.tracks[0]]); } + resize = (e: MouseEvent): void => { e.stopPropagation(); e.preventDefault(); - if (!this.summaryTrack) { + if (!this.summaryTrack || !this.defaultHeight) { return; } - let y = this.summaryTrack.getHeight(); - const mouseMoveEvent = (evMove: MouseEvent): void => { - evMove.preventDefault(); - y += evMove.movementY; - if (this.attrs && this.defaultHeight) { - const newMultiplier = y / this.defaultHeight; - if (newMultiplier < 1) { - this.summaryTrackState.scaleFactor = 1; - } else { - this.summaryTrackState.scaleFactor = newMultiplier; - } - globals.rafScheduler.scheduleFullRedraw(); - } - }; - const mouseUpEvent = (): void => { - document.removeEventListener('mousemove', mouseMoveEvent); - document.removeEventListener('mouseup', mouseUpEvent); - }; - document.addEventListener('mousemove', mouseMoveEvent); - document.addEventListener('mouseup', mouseUpEvent); - document.removeEventListener('mousedown', this.resize); + resizeTrack( + e, + this.summaryTrack, + this.summaryTrackState, + this.defaultHeight); }; onmousemove(e: MouseEvent) { - if (this.summaryTrack && this.summaryTrack.supportsResizing) { - if (e.currentTarget instanceof HTMLElement && - e.pageY - e.currentTarget.getBoundingClientRect().top >= - e.currentTarget.clientHeight - MOUSE_TARGETING_THRESHOLD_PX - ) { - document.addEventListener('mousedown', this.resize); - e.currentTarget.style.cursor = 'row-resize'; - return; - } else if (e.currentTarget instanceof HTMLElement) { - e.currentTarget.style.cursor = 'unset'; - } + if (this.summaryTrack) { + checkTrackForResizability(e, this.summaryTrack, this.resize); } - document.removeEventListener('mousedown', this.resize); } onmouseleave(e: MouseEvent) { - if (this.summaryTrack && this.summaryTrack.supportsResizing && - e.currentTarget instanceof HTMLElement) { - e.currentTarget.style.cursor = 'unset'; - document.removeEventListener('mousedown', this.resize); + if (this.summaryTrack) { + checkTrackForResizability(e, this.summaryTrack, this.resize); } } diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts index cb7ebdb9f4..68e2a02e3b 100644 --- a/ui/src/frontend/track_panel.ts +++ b/ui/src/frontend/track_panel.ts @@ -65,6 +65,73 @@ function isSelected(id: string) { return selectedArea.tracks.includes(id); } +const RESIZING_HEIGHT_PX = 5; +export function checkTrackForResizability( + e: MouseEvent, + track: Track, + resize: (e: MouseEvent) => void): void { + if (track.supportsResizing) { + if (e.currentTarget instanceof HTMLElement && + e.type !== 'mouseleave' && + e.pageY - e.currentTarget.getBoundingClientRect().top >= + e.currentTarget.clientHeight - RESIZING_HEIGHT_PX + ) { + const timelineElement: HTMLDivElement | null = e.currentTarget.closest('div.pan-and-zoom-content'); + timelineElement?.addEventListener('mousedown', resize); + e.currentTarget.style.cursor = 'row-resize'; + return; + } else if (e.currentTarget instanceof HTMLElement) { + const timelineElement: HTMLDivElement | null = e.currentTarget.closest('div.pan-and-zoom-content'); + timelineElement?.removeEventListener('mousedown', resize); + e.currentTarget.style.cursor = 'unset'; + } + } +} +export function resizeTrack( + e: MouseEvent, + track: Track, + trackState: TrackState, + defaultHeight: number, + pinnedCopy?: boolean ): void { + if (e.currentTarget instanceof HTMLElement) { + const timelineElement = e.currentTarget.closest('div.pan-and-zoom-content'); + if (timelineElement && timelineElement instanceof HTMLDivElement) { + let trackHeight = track.getHeight(); + let mouseY = e.clientY; + const mouseMoveEvent = (evMove: MouseEvent): void => { + evMove.preventDefault(); + let movementY = evMove.clientY-mouseY; + if (pinnedCopy !== true && + isPinned(trackState.id)) { + movementY /=2; + } + trackHeight += movementY; + mouseY = evMove.clientY; + const newMultiplier = trackHeight / defaultHeight; + trackState.scaleFactor = Math.min(1, newMultiplier); + globals.rafScheduler.scheduleFullRedraw(); + }; + const mouseReturnEvent = () : void => { + timelineElement.addEventListener('mousemove', mouseMoveEvent); + timelineElement.removeEventListener('mouseenter', mouseReturnEvent); + }; + const mouseLeaveEvent = () : void => { + timelineElement.removeEventListener('mousemove', mouseMoveEvent); + timelineElement.addEventListener('mouseenter', mouseReturnEvent); + }; + const mouseUpEvent = (): void => { + timelineElement.removeEventListener('mousemove', mouseMoveEvent); + document.removeEventListener('mouseup', mouseUpEvent); + timelineElement.removeEventListener('mouseenter', mouseReturnEvent); + timelineElement.removeEventListener('mouseleave', mouseLeaveEvent); + }; + timelineElement.addEventListener('mousemove', mouseMoveEvent); + timelineElement.addEventListener('mouseleave', mouseLeaveEvent); + document.addEventListener('mouseup', mouseUpEvent); + } + } +}; + interface TrackShellAttrs { track: Track; trackState: TrackState; @@ -185,62 +252,29 @@ class TrackShell implements m.ClassComponent { }) : '')); } + resize = (e: MouseEvent): void => { e.stopPropagation(); e.preventDefault(); - if (!this.attrs) { + if (!this.attrs || !this.defaultHeight) { return; } - let y = this.attrs.track.getHeight(); - const mouseMoveEvent = (evMove: MouseEvent): void => { - if (!this.attrs) { - return; - } - evMove.preventDefault(); - let movementY = evMove.movementY; - if (this.attrs.pinnedCopy !== true && - isPinned(this.attrs.trackState.id)) { - movementY /=2; - } - y += movementY; - if (this.defaultHeight) { - const newMultiplier = y / this.defaultHeight; - if (newMultiplier < 1) { - this.attrs.trackState.scaleFactor = 1; - } else { - this.attrs.trackState.scaleFactor = newMultiplier; - } - globals.rafScheduler.scheduleFullRedraw(); - } - }; - const mouseUpEvent = (): void => { - document.removeEventListener('mousemove', mouseMoveEvent); - document.removeEventListener('mouseup', mouseUpEvent); - }; - document.addEventListener('mousemove', mouseMoveEvent); - document.addEventListener('mouseup', mouseUpEvent); - document.removeEventListener('mousedown', this.resize); - }; + resizeTrack( + e, + this.attrs.track, + this.attrs.trackState, + this.defaultHeight, + this.attrs.pinnedCopy); + }; onmousemove(e: MouseEvent) { - if (this.attrs?.track.supportsResizing) { - if (e.currentTarget instanceof HTMLElement && - e.pageY - e.currentTarget.getBoundingClientRect().top >= - e.currentTarget.clientHeight - 5) { - document.addEventListener('mousedown', this.resize); - e.currentTarget.style.cursor = 'row-resize'; - return; - } else if (e.currentTarget instanceof HTMLElement) { - e.currentTarget.style.cursor = 'unset'; - } + if (this.attrs?.track) { + checkTrackForResizability(e, this.attrs.track, this.resize); } - document.removeEventListener('mousedown', this.resize); } onmouseleave(e: MouseEvent) { - if (this.attrs?.track.supportsResizing && - e.currentTarget instanceof HTMLElement) { - e.currentTarget.style.cursor = 'unset'; - document.removeEventListener('mousedown', this.resize); + if (this.attrs?.track) { + checkTrackForResizability(e, this.attrs.track, this.resize); } }