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

Commit

Permalink
move tab dragging window event handlers to window reducer
Browse files Browse the repository at this point in the history
  • Loading branch information
petemill committed Nov 7, 2017
1 parent 3344030 commit 745f013
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 75 deletions.
76 changes: 1 addition & 75 deletions app/renderer/components/tabs/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const PrivateIcon = require('./content/privateIcon')
const TabTitle = require('./content/tabTitle')
const CloseTabIcon = require('./content/closeTabIcon')
const {NotificationBarCaret} = require('../main/notificationBar')
var electron = require('electron')

// Actions
const appActions = require('../../../../js/actions/appActions')
Expand All @@ -40,7 +39,6 @@ const contextMenus = require('../../../../js/contextMenus')
const frameStateUtil = require('../../../../js/state/frameStateUtil')
const {hasTabAsRelatedTarget} = require('../../lib/tabUtil')
const isWindows = require('../../../common/lib/platformUtil').isWindows()
const browserWindowUtil = require('../../../common/lib/browserWindowUtil')
const {getCurrentWindowId} = require('../../currentWindow')
const {setObserver} = require('../../lib/observerUtil')
const UrlUtil = require('../../../../js/lib/urlutil')
Expand All @@ -50,24 +48,7 @@ const DRAG_DETACH_PX_THRESHOLD_INITIAL = 44
const DRAG_DETACH_PX_THRESHOLD_POSTSORT = 80
const DRAG_DETACH_MS_TIME_BUFFER = 0

// HACK mousemove will only trigger in the other window if the coords are inside the bounds but
// will trigger for this window even if the mouse is outside the window, since we started a dragEvent,
// *but* it will forward anything for globalX and globalY, so we'll send the client coords in those properties
// and send some fake coords in the clientX and clientY properties
// An alternative solution would be for the other window to just call electron API
// to get mouse cursor, and we could just send 0, 0 coords, but this reduces the spread of electron
// calls in components, and also puts the (tiny) computation in another process, freeing the other
// window to perform the animation
function createEventForSendMouseMoveInput (screenX, screenY) {
return {
type: 'mousemove',
x: 1,
y: 99,
globalX: screenX,
globalY: screenY
}
}

// HACK - see the related `createEventFromSendMouseMoveInput` in tabDraggingWindowReducer.js
function translateEventFromSendMouseMoveInput (receivedEvent) {
return (receivedEvent.x === 1 && receivedEvent.y === 99)
? { clientX: receivedEvent.screenX, clientY: receivedEvent.screenY }
Expand Down Expand Up @@ -130,68 +111,13 @@ class Tab extends React.Component {
relativeYDragStart,
this.props.singleTab
)
this.setupDragContinueEvents()

if (this.frame) {
// cancel tab preview while dragging. see #10103
windowActions.setTabHoverState(this.props.frameKey, false, false)
}
}

/// For when this tab / window is the dragging source
/// dispatch the events to the store so that the
/// other windows can receive state update of where to put the tab
setupDragContinueEvents () {
const stopDragListeningEvents = () => {
window.removeEventListener('mouseup', onTabDragComplete)
window.removeEventListener('keydown', onTabDragCancel)
window.removeEventListener('mousemove', onTabDragMove)
}
const onTabDragComplete = e => {
stopDragListeningEvents()
appActions.tabDragComplete()
}
const onTabDragCancel = e => {
if (e.keyCode === 27) { // ESC key
stopDragListeningEvents()
appActions.tabDragCancelled()
}
}

const onTabDragMove = mouseMoveEvent => {
mouseMoveEvent.preventDefault()
reportMoveToOtherWindow(mouseMoveEvent)
}
const reportMoveToOtherWindow = throttle(this.reportMoveToOtherWindow.bind(this), 4)
window.addEventListener('mouseup', onTabDragComplete)
window.addEventListener('keydown', onTabDragCancel)
window.addEventListener('mousemove', onTabDragMove)
}

/// HACK Even if the other window is 'active', it will not receive regular mousemove events
/// ...probably because there is another mousemove event in progress generated from another
/// window.
/// So send the mouse events using muon's BrowserWindow.sendInputEvent
/// This was previously done in the browser process as a result of the 'dragMoved' store action
/// but it was never smooth enough, even when reducing the throttle time
reportMoveToOtherWindow (mouseMoveEvent) {
// HACK we cannot get the new window ID (tabDragData.currentWindowId) from the store state
// when we are dragged to another window since our component will
// not be subscribed to store updates anymore as technically it
// does not exist, so...
// ...get the currently focused window... if this is flakey we could subscribe to the store
// manually (and probably create another higher order component for all this to preserve sanity)
const win = electron.remote.BrowserWindow.getActiveWindow()
if (!win || win.id === getCurrentWindowId()) {
return
}
const {x: clientX, y: clientY} = browserWindowUtil.getWindowClientPointAtCursor(win, {
x: mouseMoveEvent.screenX,
y: mouseMoveEvent.screenY
})
win.webContents.sendInputEvent(createEventForSendMouseMoveInput(clientX, clientY))
}

//
// Events for drag-sort amongst this tab group
// Run by any window that receives a dragged tab
Expand Down
89 changes: 89 additions & 0 deletions app/renderer/reducers/tabDraggingWindowReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const electron = require('electron')
const throttle = require('lodash.throttle')
const appConstants = require('../../../js/constants/appConstants')
const appActions = require('../../../js/actions/appActions')
const {getCurrentWindowId} = require('../currentWindow')
const browserWindowUtil = require('../../common/lib/browserWindowUtil')

module.exports = function (windowState, action) {
switch (action.actionType) {
case appConstants.APP_TAB_DRAG_STARTED:
setupDragContinueEvents()
break
}
return windowState
}

/// For when this tab / window is the dragging source
/// dispatch the events to the store so that the
/// other windows can receive state update of where to put the tab
function setupDragContinueEvents () {
const stopDragListeningEvents = () => {
window.removeEventListener('mouseup', onTabDragComplete)
window.removeEventListener('keydown', onTabDragCancel)
window.removeEventListener('mousemove', onTabDragMove)
}
const onTabDragComplete = e => {
stopDragListeningEvents()
appActions.tabDragComplete()
}
const onTabDragCancel = e => {
if (e.keyCode === 27) { // ESC key
stopDragListeningEvents()
appActions.tabDragCancelled()
}
}
const onTabDragMove = mouseMoveEvent => {
mouseMoveEvent.preventDefault()
reportMoveToOtherWindow(mouseMoveEvent)
}
window.addEventListener('mouseup', onTabDragComplete)
window.addEventListener('keydown', onTabDragCancel)
window.addEventListener('mousemove', onTabDragMove)
}

/// HACK Even if the other window is 'active', it will not receive regular mousemove events
/// ...probably because there is another mousemove event in progress generated from another
/// window.
/// So send the mouse events using muon's BrowserWindow.sendInputEvent
/// This was previously done in the browser process as a result of the 'dragMoved' store action
/// but it was never smooth enough, even when reducing the throttle time
const reportMoveToOtherWindow = throttle(mouseMoveEvent => {
// HACK we cannot get the new window ID (tabDragData.currentWindowId) from the store state
// when we are dragged to another window since our component will
// not be subscribed to store updates anymore as technically it
// does not exist, so...
// ...get the currently focused window... if this is flakey we could subscribe to the store
// manually (and probably create another higher order component for all this to preserve sanity)
const win = electron.remote.BrowserWindow.getActiveWindow()
if (!win || win.id === getCurrentWindowId()) {
return
}
const {x: clientX, y: clientY} = browserWindowUtil.getWindowClientPointAtCursor(win, {
x: mouseMoveEvent.screenX,
y: mouseMoveEvent.screenY
})
win.webContents.sendInputEvent(createEventForSendMouseMoveInput(clientX, clientY))
}, 4)

// HACK mousemove will only trigger in the other window if the coords are inside the bounds but
// will trigger for this window even if the mouse is outside the window, since we started a dragEvent,
// *but* it will forward anything for globalX and globalY, so we'll send the client coords in those properties
// and send some fake coords in the clientX and clientY properties
// An alternative solution would be for the other window to just call electron API
// to get mouse cursor, and we could just send 0, 0 coords, but this reduces the spread of electron
// calls in components, and also puts the (tiny) computation in another process, freeing the other
// window to perform the animation
function createEventForSendMouseMoveInput (screenX, screenY) {
return {
type: 'mousemove',
x: 1,
y: 99,
globalX: screenX,
globalY: screenY
}
}
1 change: 1 addition & 0 deletions js/stores/windowStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ const applyReducers = (state, action, immutableAction) => [
require('../../app/renderer/reducers/frameReducer'),
require('../../app/renderer/reducers/contextMenuReducer'),
require('../../app/renderer/reducers/tabContentReducer'),
require('../../app/renderer/reducers/tabDraggingWindowReducer'),
// This should be included even in production builds since you can use
// an environment variable to show the Debug menu
require('../../app/renderer/reducers/debugReducer')
Expand Down

0 comments on commit 745f013

Please sign in to comment.