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

Commit

Permalink
Merge pull request #531 from atom/wl-activate-pane-on-right-click
Browse files Browse the repository at this point in the history
Activate panes on any click, not just left click
  • Loading branch information
darangi authored Nov 12, 2020
2 parents 9a9f69a + 6a152b5 commit 4bde19f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 32 deletions.
15 changes: 7 additions & 8 deletions lib/tab-bar-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,15 @@ class TabBarView
@pane.activatePreviousItem()

onMouseDown: (event) ->
@pane.activate() unless @pane.isDestroyed()

tab = @tabForElement(event.target)
return unless tab

if event.which is 3 or (event.which is 1 and event.ctrlKey is true)
@rightClickedTab?.element.classList.remove('right-clicked')
if event.button is 2 or (event.button is 0 and event.ctrlKey is true)
@rightClickedTab = tab
@rightClickedTab.element.classList.add('right-clicked')
event.preventDefault()
else if event.which is 2
else if event.button is 1
# This prevents Chromium from activating "scroll mode" when
# middle-clicking while some tabs are off-screen.
event.preventDefault()
Expand All @@ -440,14 +440,13 @@ class TabBarView
return unless tab

event.preventDefault()
if event.which is 3 or (event.which is 1 and event.ctrlKey is true)
if event.button is 2 or (event.button is 0 and event.ctrlKey is true)
# Bail out early when receiving this event, because we have already
# handled it in the mousedown handler.
return
else if event.which is 1 and not event.target.classList.contains('close-icon')
else if event.button is 0 and not event.target.classList.contains('close-icon')
@pane.activateItem(tab.item)
@pane.activate() unless @pane.isDestroyed()
else if event.which is 2
else if event.button is 1
@pane.destroyItem(tab.item)

onDoubleClick: (event) ->
Expand Down
53 changes: 53 additions & 0 deletions spec/event-helpers.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
buildMouseEvent = (type, target, {button, ctrlKey}={}) ->
event = new MouseEvent(type, {bubbles: true, cancelable: true})
Object.defineProperty(event, 'button', get: -> button) if button?
Object.defineProperty(event, 'ctrlKey', get: -> ctrlKey) if ctrlKey?
Object.defineProperty(event, 'target', get: -> target)
Object.defineProperty(event, 'srcObject', get: -> target)
spyOn(event, "preventDefault")
event

module.exports.triggerMouseEvent = (type, target, {which, ctrlKey}={}) ->
event = buildMouseEvent(arguments...)
target.dispatchEvent(event)
event

module.exports.triggerClickEvent = (target, options) ->
events = {
mousedown: buildMouseEvent('mousedown', target, options),
mouseup: buildMouseEvent('mouseup', target, options),
click: buildMouseEvent('click', target, options)
}

target.dispatchEvent(events.mousedown)
target.dispatchEvent(events.mouseup)
target.dispatchEvent(events.click)

events

module.exports.buildDragEvents = (dragged, dropTarget) ->
dataTransfer =
data: {}
setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values
getData: (key) -> @data[key]

Object.defineProperty(
dataTransfer,
'items',
get: ->
Object.keys(dataTransfer.data).map((key) -> {type: key})
)

dragStartEvent = buildMouseEvent("dragstart", dragged)
Object.defineProperty(dragStartEvent, 'dataTransfer', get: -> dataTransfer)

dropEvent = buildMouseEvent("drop", dropTarget)
Object.defineProperty(dropEvent, 'dataTransfer', get: -> dataTransfer)

[dragStartEvent, dropEvent]

module.exports.buildWheelEvent = (delta) ->
new WheelEvent("mousewheel", wheelDeltaY: delta)

module.exports.buildWheelPlusShiftEvent = (delta) ->
new WheelEvent("mousewheel", wheelDeltaY: delta, shiftKey: true)
44 changes: 20 additions & 24 deletions spec/tabs-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ temp = require 'temp'
TabBarView = require '../lib/tab-bar-view'
layout = require '../lib/layout'
main = require '../lib/main'
{triggerMouseEvent, triggerClickEvent, buildDragEvents, buildDragEnterLeaveEvents, buildWheelEvent, buildWheelPlusShiftEvent} = require "./event-helpers"
{triggerMouseEvent, triggerClickEvent, buildDragEvents, buildDragEnterLeaveEvents, buildWheelEvent, buildWheelPlusShiftEvent} = require "./event-helpers.coffee"
{buildDragEnterLeaveEvents} = require "./event-helpers"

describe "Tabs package main", ->
centerElement = null
Expand Down Expand Up @@ -258,26 +259,23 @@ describe "TabBarView", ->

describe "when a tab is clicked", ->
it "shows the associated item on the pane and focuses the pane", ->
jasmine.attachToDOM(tabBar.element) # Remove after Atom 1.2.0 is released
spyOn(pane, 'activate')

{mousedown, click} = triggerClickEvent(tabBar.tabAtIndex(0).element, which: 1)
{mousedown, click} = triggerClickEvent(tabBar.tabAtIndex(0).element, button: 0)
expect(pane.getActiveItem()).toBe(pane.getItems()[0])
# allows dragging
expect(mousedown.preventDefault).not.toHaveBeenCalled()
expect(click.preventDefault).toHaveBeenCalled()

{mousedown, click} = triggerClickEvent(tabBar.tabAtIndex(2).element, which: 1)
{mousedown, click} = triggerClickEvent(tabBar.tabAtIndex(2).element, button: 0)
expect(pane.getActiveItem()).toBe(pane.getItems()[2])
# allows dragging
expect(mousedown.preventDefault).not.toHaveBeenCalled()
expect(click.preventDefault).toHaveBeenCalled()
expect(pane.activate.callCount).toBe 2

it "closes the tab when middle clicked", ->
jasmine.attachToDOM(tabBar.element) # Remove after Atom 1.2.0 is released

{click} = triggerClickEvent(tabBar.tabForItem(editor1).element, which: 2)
{click} = triggerClickEvent(tabBar.tabForItem(editor1).element, button: 1)

expect(pane.getItems().length).toBe 2
expect(pane.getItems().indexOf(editor1)).toBe -1
Expand All @@ -288,24 +286,22 @@ describe "TabBarView", ->
expect(click.preventDefault).toHaveBeenCalled()

it "doesn't switch tab when right (or ctrl-left) clicked", ->
jasmine.attachToDOM(tabBar.element) # Remove after Atom 1.2.0 is released

spyOn(pane, 'activate')

{mousedown} = triggerClickEvent(tabBar.tabAtIndex(0).element, which: 3)
{mousedown} = triggerClickEvent(tabBar.tabAtIndex(0).element, button: 2)
expect(pane.getActiveItem()).not.toBe pane.getItems()[0]
expect(mousedown.preventDefault).toHaveBeenCalled()

{mousedown} = triggerClickEvent(tabBar.tabAtIndex(0).element, which: 1, ctrlKey: true)
{mousedown} = triggerClickEvent(tabBar.tabAtIndex(0).element, button: 0, ctrlKey: true)
expect(pane.getActiveItem()).not.toBe pane.getItems()[0]
expect(mousedown.preventDefault).toHaveBeenCalled()

expect(pane.activate).not.toHaveBeenCalled()
# We don't switch tabs, but the pane should still be activated
# because of the mouse click
expect(pane.activate).toHaveBeenCalled()

describe "when a tab's close icon is clicked", ->
it "destroys the tab's item on the pane", ->
jasmine.attachToDOM(tabBar.element) # Remove after Atom 1.2.0 is released

tabBar.tabForItem(editor1).element.querySelector('.close-icon').click()
expect(pane.getItems().length).toBe 2
expect(pane.getItems().indexOf(editor1)).toBe -1
Expand Down Expand Up @@ -543,7 +539,7 @@ describe "TabBarView", ->

describe "when tabs:close-tab is fired", ->
it "closes the active tab", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
atom.commands.dispatch(tabBar.element, 'tabs:close-tab')
expect(pane.getItems().length).toBe 2
expect(pane.getItems().indexOf(item2)).toBe -1
Expand All @@ -552,7 +548,7 @@ describe "TabBarView", ->

describe "when tabs:close-other-tabs is fired", ->
it "closes all other tabs except the active tab", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
atom.commands.dispatch(tabBar.element, 'tabs:close-other-tabs')
expect(pane.getItems().length).toBe 1
expect(tabBar.getTabs().length).toBe 1
Expand All @@ -562,7 +558,7 @@ describe "TabBarView", ->
describe "when tabs:close-tabs-to-right is fired", ->
it "closes only the tabs to the right of the active tab", ->
pane.activateItem(editor1)
triggerClickEvent(tabBar.tabForItem(editor1).element, which: 3)
triggerClickEvent(tabBar.tabForItem(editor1).element, button: 2)
atom.commands.dispatch(tabBar.element, 'tabs:close-tabs-to-right')
expect(pane.getItems().length).toBe 2
expect(tabBar.getTabs().length).toBe 2
Expand All @@ -572,7 +568,7 @@ describe "TabBarView", ->
describe "when tabs:close-tabs-to-left is fired", ->
it "closes only the tabs to the left of the active tab", ->
pane.activateItem(editor1)
triggerClickEvent(tabBar.tabForItem(editor1).element, which: 3)
triggerClickEvent(tabBar.tabForItem(editor1).element, button: 2)
atom.commands.dispatch(tabBar.element, 'tabs:close-tabs-to-left')
expect(pane.getItems().length).toBe 2
expect(tabBar.getTabs().length).toBe 2
Expand All @@ -594,7 +590,7 @@ describe "TabBarView", ->

describe "when tabs:split-up is fired", ->
it "splits the selected tab up", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
expect(atom.workspace.getCenter().getPanes().length).toBe 1

atom.commands.dispatch(tabBar.element, 'tabs:split-up')
Expand All @@ -604,7 +600,7 @@ describe "TabBarView", ->

describe "when tabs:split-down is fired", ->
it "splits the selected tab down", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
expect(atom.workspace.getCenter().getPanes().length).toBe 1

atom.commands.dispatch(tabBar.element, 'tabs:split-down')
Expand All @@ -614,7 +610,7 @@ describe "TabBarView", ->

describe "when tabs:split-left is fired", ->
it "splits the selected tab to the left", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
expect(atom.workspace.getCenter().getPanes().length).toBe 1

atom.commands.dispatch(tabBar.element, 'tabs:split-left')
Expand All @@ -624,7 +620,7 @@ describe "TabBarView", ->

describe "when tabs:split-right is fired", ->
it "splits the selected tab to the right", ->
triggerClickEvent(tabBar.tabForItem(item2).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item2).element, button: 2)
expect(atom.workspace.getCenter().getPanes().length).toBe 1

atom.commands.dispatch(tabBar.element, 'tabs:split-right')
Expand All @@ -635,7 +631,7 @@ describe "TabBarView", ->
describe "when tabs:open-in-new-window is fired", ->
describe "by right-clicking on a tab", ->
beforeEach ->
triggerClickEvent(tabBar.tabForItem(item1).element, which: 3)
triggerClickEvent(tabBar.tabForItem(item1).element, button: 2)
expect(atom.workspace.getCenter().getPanes().length).toBe 1
spyOn(atom, 'open')

Expand Down Expand Up @@ -1384,7 +1380,7 @@ describe "TabBarView", ->
runs ->
pane.activateItem(editor2)
expect(tabBar.tabForItem(editor2).element.querySelector('.title')).toHaveClass 'temp'
triggerMouseEvent('dblclick', tabBar.tabForItem(editor2).element, which: 1)
triggerMouseEvent('dblclick', tabBar.tabForItem(editor2).element, button: 0)
expect(tabBar.tabForItem(editor2).element.querySelector('.title')).not.toHaveClass 'temp'

describe "when editing a file in pending state", ->
Expand Down

0 comments on commit 4bde19f

Please sign in to comment.