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

Commit cabbc3e

Browse files
committed
make tab preview based on idle mouse time
- Fix #8860 - Auditors: @bsclifton, @bradleyrichter
1 parent 6ec02d8 commit cabbc3e

File tree

14 files changed

+104
-17
lines changed

14 files changed

+104
-17
lines changed

app/common/constants/settingsEnums.js

+8
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ const tabCloseAction = {
2727
PARENT: 'parent'
2828
}
2929

30+
// timing in milliseconds
31+
const tabPreviewTiming = {
32+
LONG: 2000,
33+
NORMAL: 1000,
34+
SHORT: 500
35+
}
36+
3037
const fullscreenOption = {
3138
ALWAYS_ASK: 'alwaysAsk',
3239
ALWAYS_ALLOW: 'alwaysAllow'
@@ -42,6 +49,7 @@ module.exports = {
4249
newTabMode,
4350
bookmarksToolbarMode,
4451
tabCloseAction,
52+
tabPreviewTiming,
4553
fullscreenOption,
4654
autoplayOption
4755
}

app/extensions/brave/locales/en-US/preferences.properties

+4
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ paintTabs=Show tabs in page theme color
192192
tabsPerTabPage=Number of tabs per tab set:
193193
tabCloseAction=When closing an active tab:
194194
showTabPreviews=Show tab previews on hover
195+
tabPreviewTiming=Time to wait before previewing a tab
196+
long=Long
197+
normal=Normal
198+
short=Short
195199
showHistoryMatches=Show history matches
196200
showBookmarkMatches=Show bookmark matches
197201
showOpenedTabMatches=Show tab matches

app/renderer/components/tabs/tab.js

+33-15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const windowStore = require('../../../../js/stores/windowStore')
2626
// Constants
2727
const dragTypes = require('../../../../js/constants/dragTypes')
2828
const messages = require('../../../../js/constants/messages')
29+
const settings = require('../../../../js/constants/settings')
2930

3031
// Styles
3132
const styles = require('../styles/tab')
@@ -42,10 +43,12 @@ const {getTabBreakpoint, tabUpdateFrameRate} = require('../../lib/tabUtil')
4243
const {isWindows} = require('../../../common/lib/platformUtil')
4344
const {getCurrentWindowId} = require('../../currentWindow')
4445
const UrlUtil = require('../../../../js/lib/urlutil')
46+
const getSetting = require('../../../../js/settings').getSetting
4547

4648
class Tab extends ImmutableComponent {
4749
constructor () {
4850
super()
51+
this.onMouseMove = this.onMouseMove.bind(this)
4952
this.onMouseEnter = this.onMouseEnter.bind(this)
5053
this.onMouseLeave = this.onMouseLeave.bind(this)
5154
this.onUpdateTabSize = this.onUpdateTabSize.bind(this)
@@ -163,27 +166,43 @@ class Tab extends ImmutableComponent {
163166
!this.props.frame.get('provisionalLocation').startsWith('chrome-extension://mnojpmjdmbbfmejpflffifhffcmidifd/'))
164167
}
165168

166-
onMouseLeave () {
169+
onMouseLeave (e) {
170+
windowActions.setTabHoverState(this.props.frame.get('key'), false)
171+
167172
if (this.props.previewTabs) {
168-
window.clearTimeout(this.hoverTimeout)
169-
windowActions.setPreviewFrame(null)
173+
clearTimeout(this.mouseTimeout)
174+
const tabAsRelatedTarget = /^tab_/i.test(e.relatedTarget.classList)
175+
176+
// We are taking for granted that user hovering over another tab
177+
// means that he wants to sequentially preview a set of tabs,
178+
// so if previewMode was set by defined mouse idle time,
179+
// only cancel previewMode if the next event doesn't happen in another tab.
180+
if (!tabAsRelatedTarget) {
181+
windowActions.setPreviewFrame(null)
182+
windowActions.setPreviewMode(false)
183+
}
170184
}
171-
windowActions.setTabHoverState(this.props.frame.get('key'), false)
172185
}
173186

174187
onMouseEnter (e) {
175-
// relatedTarget inside mouseenter checks which element before this event was the pointer on
176-
// if this element has a tab-like class, then it's likely that the user was previewing
177-
// a sequency of tabs. Called here as previewMode.
178-
const previewMode = /tab(?!pages)/i.test(e.relatedTarget.classList)
188+
windowActions.setTabHoverState(this.props.frame.get('key'), true)
179189

180-
// If user isn't in previewMode, we add a bit of delay to avoid tab from flashing out
181-
// as reported here: https://github.com/brave/browser-laptop/issues/1434
190+
this.mouseTimeout = null
191+
if (this.props.previewTabs && this.props.previewMode) {
192+
windowActions.setPreviewFrame(this.props.frame.get('key'))
193+
}
194+
}
195+
196+
onMouseMove (e) {
197+
// previewMode is only triggered if mouse is idle over a tab
198+
// for a given amount of time based on timing defined in prefs->tabs
182199
if (this.props.previewTabs) {
183-
this.hoverTimeout =
184-
window.setTimeout(windowActions.setPreviewFrame.bind(null, this.props.frame.get('key')), previewMode ? 0 : 200)
200+
clearTimeout(this.mouseTimeout)
201+
this.mouseTimeout = setTimeout(() => {
202+
windowActions.setPreviewFrame(this.props.frame.get('key'))
203+
windowActions.setPreviewMode(true)
204+
}, getSetting(settings.TAB_PREVIEW_TIMING))
185205
}
186-
windowActions.setTabHoverState(this.props.frame.get('key'), true)
187206
}
188207

189208
onAuxClick (e) {
@@ -288,6 +307,7 @@ class Tab extends ImmutableComponent {
288307
partOfFullPageSet: this.props.partOfFullPageSet || !!this.props.tabWidth
289308
})}
290309
style={this.props.tabWidth ? { flex: `0 0 ${this.props.tabWidth}px` } : {}}
310+
onMouseMove={this.onMouseMove}
291311
onMouseEnter={this.onMouseEnter}
292312
onMouseLeave={this.onMouseLeave}>
293313
{
@@ -369,8 +389,6 @@ class Tab extends ImmutableComponent {
369389
}
370390

371391
const paymentsEnabled = () => {
372-
const getSetting = require('../../../../js/settings').getSetting
373-
const settings = require('../../../../js/constants/settings')
374392
return getSetting(settings.PAYMENTS_ENABLED)
375393
}
376394

app/renderer/components/tabs/tabs.js

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class Tabs extends ImmutableComponent {
152152
frame={frame}
153153
key={'tab-' + frame.get('key')}
154154
paintTabs={this.props.paintTabs}
155+
previewMode={this.props.previewMode}
155156
previewTabs={this.props.previewTabs}
156157
isActive={this.props.activeFrameKey === frame.get('key')}
157158
onTabClosedWithMouse={this.onTabClosedWithMouse}

app/renderer/components/tabs/tabsToolbar.js

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class TabsToolbar extends ImmutableComponent {
4545
? <PinnedTabs sites={this.props.sites}
4646
activeFrameKey={this.props.activeFrameKey}
4747
paintTabs={this.props.paintTabs}
48+
previewMode={this.props.previewMode}
4849
previewTabs={this.props.previewTabs}
4950
dragData={this.props.dragData}
5051
tabPageIndex={this.props.tabPageIndex}
@@ -59,6 +60,7 @@ class TabsToolbar extends ImmutableComponent {
5960
shouldAllowWindowDrag={this.props.shouldAllowWindowDrag}
6061
dragData={this.props.dragData}
6162
paintTabs={this.props.paintTabs}
63+
previewMode={this.props.previewMode}
6264
previewTabs={this.props.previewTabs}
6365
tabsPerTabPage={this.props.tabsPerTabPage}
6466
activeFrameKey={this.props.activeFrameKey}

docs/state.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ AppStore
228228
'tabs.close-action': string, // one of: parent, lastActive, next
229229
'tabs.paint-tabs': boolean, // true if the page theme color and favicon color should be used for tabs
230230
'tabs.show-tab-previews': boolean, // true to show tab previews
231+
'tabs.preview-timing': boolean, // how much in milliseconds user should wait before tab preview is fired
231232
'tabs.switch-to-new-tabs': boolean, // true if newly opened tabs should be focused immediately
232233
'tabs.tabs-per-page': number // number of tabs per tab page
233234
},
@@ -657,8 +658,9 @@ WindowStore
657658
},
658659
size: array, // last known window size [x, y]
659660
tabs: {
660-
tabPageIndex: number, // index of the current tab page
661+
previewMode: boolean, // whether or not tab preview should be fired based on mouse idle time
661662
previewTabPageIndex: number // index of the tab being previewed
663+
tabPageIndex: number, // index of the current tab page
662664
},
663665
},
664666
widevinePanelDetail: {

docs/windowActions.md

+11
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,17 @@ This is done when hovering over a tab.
223223

224224

225225

226+
### setPreviewMode(shouldEnablePreview)
227+
228+
Dispatches a message to the store to set preview mode.
229+
This will check whether setPreviewFrame should be fired or not.
230+
231+
**Parameters**
232+
233+
**shouldEnablePreview**: `Boolean`, true if user enters in previewMode state
234+
235+
236+
226237
### setTabPageIndex(index)
227238

228239
Dispatches a message to the store to set the tab page index.

js/about/preferences.js

+22-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ const messages = require('../constants/messages')
3434
const settings = require('../constants/settings')
3535
const {changeSetting} = require('../../app/renderer/lib/settingsUtil')
3636
const {passwordManagers, extensionIds} = require('../constants/passwordManagers')
37-
const {startsWithOption, newTabMode, bookmarksToolbarMode, tabCloseAction, fullscreenOption, autoplayOption} = require('../../app/common/constants/settingsEnums')
37+
const {
38+
startsWithOption,
39+
newTabMode,
40+
bookmarksToolbarMode,
41+
tabCloseAction,
42+
fullscreenOption,
43+
autoplayOption,
44+
tabPreviewTiming
45+
} = require('../../app/common/constants/settingsEnums')
3846

3947
const aboutActions = require('./aboutActions')
4048
const appActions = require('../actions/appActions')
@@ -359,6 +367,19 @@ class TabsTab extends ImmutableComponent {
359367
<SettingCheckbox dataL10nId='switchToNewTabs' prefKey={settings.SWITCH_TO_NEW_TABS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
360368
<SettingCheckbox dataL10nId='paintTabs' prefKey={settings.PAINT_TABS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
361369
<SettingCheckbox dataL10nId='showTabPreviews' prefKey={settings.SHOW_TAB_PREVIEWS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
370+
{
371+
getSetting(settings.SHOW_TAB_PREVIEWS, this.props.settings)
372+
? <SettingItem dataL10nId='tabPreviewTiming'>
373+
<SettingDropdown
374+
value={getSetting(settings.TAB_PREVIEW_TIMING, this.props.settings)}
375+
onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.TAB_PREVIEW_TIMING)}>
376+
<option data-l10n-id='long' value={tabPreviewTiming.LONG} />
377+
<option data-l10n-id='normal' value={tabPreviewTiming.NORMAL} />
378+
<option data-l10n-id='short' value={tabPreviewTiming.SHORT} />
379+
</SettingDropdown>
380+
</SettingItem>
381+
: null
382+
}
362383
<SettingItem dataL10nId='dashboardSettingsTitle'>
363384
<SettingCheckbox dataL10nId='dashboardShowImages' prefKey={settings.SHOW_DASHBOARD_IMAGES} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
364385
</SettingItem>

js/actions/windowActions.js

+13
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ const windowActions = {
272272
})
273273
},
274274

275+
/**
276+
* Dispatches a message to the store to set preview mode.
277+
* This will check whether setPreviewFrame should be fired or not.
278+
*
279+
* @param {Boolean} shouldEnablePreview - true if user enters in previewMode state
280+
*/
281+
setPreviewMode: function (shouldEnablePreview) {
282+
dispatch({
283+
actionType: windowConstants.WINDOW_SET_PREVIEW_MODE,
284+
shouldEnablePreview
285+
})
286+
},
287+
275288
/**
276289
* Dispatches a message to the store to set the tab page index.
277290
*

js/components/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ class Main extends ImmutableComponent {
851851
paintTabs={getSetting(settings.PAINT_TABS)}
852852
shouldAllowWindowDrag={shouldAllowWindowDrag}
853853
dragData={this.props.appState.getIn(['dragData', 'type']) === dragTypes.TAB && this.props.appState.get('dragData')}
854+
previewMode={this.props.windowState.getIn(['ui', 'tabs', 'previewMode'])}
854855
previewTabs={getSetting(settings.SHOW_TAB_PREVIEWS)}
855856
tabsPerTabPage={tabsPerPage}
856857
tabPageIndex={this.props.windowState.getIn(['ui', 'tabs', 'tabPageIndex'])}

js/constants/appConfig.js

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ module.exports = {
138138
'tabs.tabs-per-page': 20,
139139
'tabs.close-action': 'parent',
140140
'tabs.show-tab-previews': true,
141+
'tabs.preview-timing': 2000,
141142
'tabs.show-dashboard-images': true,
142143
'privacy.history-suggestions': true,
143144
'privacy.bookmark-suggestions': true,

js/constants/settings.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const settings = {
2929
PAINT_TABS: 'tabs.paint-tabs',
3030
TABS_PER_PAGE: 'tabs.tabs-per-page',
3131
SHOW_TAB_PREVIEWS: 'tabs.show-tab-previews',
32+
TAB_PREVIEW_TIMING: 'tabs.preview-timing',
3233
SHOW_DASHBOARD_IMAGES: 'tabs.show-dashboard-images',
3334
// Privacy Tab
3435
HISTORY_SUGGESTIONS: 'privacy.history-suggestions',

js/constants/windowConstants.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const windowConstants = {
1111
WINDOW_CLOSE_FRAMES: _,
1212
WINDOW_SET_FOCUSED_FRAME: _,
1313
WINDOW_SET_PREVIEW_FRAME: _,
14+
WINDOW_SET_PREVIEW_MODE: _,
1415
WINDOW_SET_PREVIEW_TAB_PAGE_INDEX: _,
1516
WINDOW_SET_TAB_PAGE_INDEX: _,
1617
WINDOW_SET_TAB_BREAKPOINT: _,

js/stores/windowStore.js

+3
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ const doAction = (action) => {
348348
case windowConstants.WINDOW_CLEAR_CLOSED_FRAMES:
349349
windowState = windowState.set('closedFrames', new Immutable.List())
350350
break
351+
case windowConstants.WINDOW_SET_PREVIEW_MODE:
352+
windowState = windowState.setIn(['ui', 'tabs', 'previewMode'], action.shouldEnablePreview)
353+
break
351354
case windowConstants.WINDOW_SET_PREVIEW_FRAME:
352355
windowState = windowState.merge({
353356
previewFrameKey: action.frameKey != null && action.frameKey !== windowState.get('activeFrameKey')

0 commit comments

Comments
 (0)