Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

[WIP] make use of mouse velocity for vertical tab preview #8841

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions app/renderer/components/tabs/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,18 @@ const {isIntermediateAboutPage} = require('../../../../js/lib/appUrlUtil')
const contextMenus = require('../../../../js/contextMenus')
const dnd = require('../../../../js/dnd')
const throttle = require('../../../../js/lib/throttle')
const {getTabBreakpoint, tabUpdateFrameRate} = require('../../lib/tabUtil')
const {getTabBreakpoint, tabUpdateFrameRate, getMouseVelocity} = require('../../lib/tabUtil')
const {isWindows} = require('../../../common/lib/platformUtil')
const {getCurrentWindowId} = require('../../currentWindow')
const UrlUtil = require('../../../../js/lib/urlutil')
// const previewTimestamp = require('../../../../js/constants/config').tabs.maxAllowedTimesIdleMouseOverTab

class Tab extends ImmutableComponent {
constructor () {
super()
this.onMouseEnter = this.onMouseEnter.bind(this)
this.onMouseLeave = this.onMouseLeave.bind(this)
this.onMouseMove = this.onMouseMove.bind(this)
this.onUpdateTabSize = this.onUpdateTabSize.bind(this)
}
get frame () {
Expand Down Expand Up @@ -165,7 +167,6 @@ class Tab extends ImmutableComponent {

onMouseLeave () {
if (this.props.previewTabs) {
window.clearTimeout(this.hoverTimeout)
windowActions.setPreviewFrame(null)
}
windowActions.setTabHoverState(this.frame, false)
Expand All @@ -175,17 +176,36 @@ class Tab extends ImmutableComponent {
// relatedTarget inside mouseenter checks which element before this event was the pointer on
// if this element has a tab-like class, then it's likely that the user was previewing
// a sequency of tabs. Called here as previewMode.
const previewMode = /tab(?!pages)/i.test(e.relatedTarget.classList)

// If user isn't in previewMode, we add a bit of delay to avoid tab from flashing out
// as reported here: https://github.com/brave/browser-laptop/issues/1434
if (this.props.previewTabs) {
this.hoverTimeout =
window.setTimeout(windowActions.setPreviewFrame.bind(null, this.frame), previewMode ? 0 : 200)
// TODO this RegExp can be improved.
const previewMode = /tab(?!stoolbar)|tab(?=area|id|title)|closetab/i.test(e.relatedTarget.classList)
if (previewMode) {
windowActions.setPreviewFrame(this.frame)
}
windowActions.setTabHoverState(this.frame, true)
}

onMouseMove (e) {
getMouseVelocity(e, this.lastMouseMoveEvent, velocity => {
if (velocity < 1) {
// Brad: the number below is the number of times
// mouse is stopped over a tab. It is counted in milliseconds
// so it can be stopped even if you feel it doesn't.
// changing the number means that mouse should be stopped X times
// before preview happens.

// Note that this is only useful for vertical mouse positions i.e. from
// webview to urlbar and vice-versa. Horizontal tabs are always triggered
// when the latest item you hovered is another tab.
const magicBradNumber = 150
if (++this.timesMouseIsIdle >= magicBradNumber) {
windowActions.setPreviewFrame(this.frame)
this.timesMouseIsIdle = 0
}
}
})
}

onAuxClick (e) {
this.onClickTab(e)
}
Expand Down Expand Up @@ -240,6 +260,14 @@ class Tab extends ImmutableComponent {
}

componentDidMount () {
// When tab is ready there's no mouse over it.
this.timesMouseIsIdle = 0
// Grab Mouse details to be used on tab previews
this.lastMouseMoveEvent = {
timestamp: null,
updatedX: null,
updatedY: null
}
this.onUpdateTabSize()
this.tabNode.addEventListener('auxclick', this.onAuxClick.bind(this))
window.addEventListener('resize', throttle(this.onUpdateTabSize, tabUpdateFrameRate))
Expand Down Expand Up @@ -286,6 +314,7 @@ class Tab extends ImmutableComponent {
partOfFullPageSet: this.props.partOfFullPageSet || !!this.props.tabWidth
})}
style={this.props.tabWidth ? { flex: `0 0 ${this.props.tabWidth}px` } : {}}
onMouseMove={this.onMouseMove}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}>
{
Expand Down
30 changes: 30 additions & 0 deletions app/renderer/lib/tabUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,33 @@ module.exports.updateTabPageIndex = (state, frameProps) => {

return state
}

/**
* Get the mouse velocity
* @param e The current mouse event
* @param lastE The last mouse event
* @param cb Callback function to execute
*/
module.exports.getMouseVelocity = (e, lastE, cb) => {
if (lastE.timestamp === null) {
lastE.timestamp = Date.now()
lastE.updatedX = e.clientX
lastE.updatedY = e.clientY
return
}

const now = Date.now()
const distanceX = e.clientX - lastE.updatedX
const distanceY = e.clientY - lastE.updatedY
const interval = now - lastE.timestamp
const velocity = Math.sqrt((distanceX * distanceX) + (distanceY * distanceY)) / interval

// velocity is defined as travelled dist / amount of time
// Which we get by using Pythagorean theorem
// => distance^2 = distanceX^2 + distanceY^2
cb(Math.round(velocity))

lastE.timestamp = now
lastE.updatedX = e.clientX
lastE.updatedY = e.clientY
}
3 changes: 3 additions & 0 deletions js/constants/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,8 @@ module.exports = {
},
tabs: {
maxAllowedNewSessions: 9
// TODO: once Brad defines a number, add here
// because magic numbers are evil
// ,maxAllowedTimesIdleMouseOverTab: 10
}
}