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

Activate panes on any click, not just left click #531

Merged
merged 3 commits into from
Nov 12, 2020
Merged
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
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