From 7f14965ca829323c8bdd9a3d31b0ecd4b0199497 Mon Sep 17 00:00:00 2001 From: Ivan Zuzak Date: Sun, 31 Aug 2014 21:39:16 +0200 Subject: [PATCH] Support selecting multiple rows with meta-click --- spec/editor-component-spec.coffee | 44 +++++++++++++++++++++++++++++++ src/editor-component.coffee | 31 +++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 5b30bdbdf..411b39241 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -1335,6 +1335,16 @@ describe "EditorComponent", -> gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4))) expect(editor.getSelectedScreenRange()).toEqual [[4, 0], [5, 0]] + describe "when the gutter is meta-clicked", -> + it "creates a new selection for the clicked row", -> + editor.setSelectedScreenRange([[3, 0], [3, 2]]) + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]]] + + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [5, 0]], [[6, 0], [7, 0]]] + describe "when the gutter is shift-clicked", -> beforeEach -> editor.setSelectedScreenRange([[3, 4], [4, 5]]) @@ -1366,6 +1376,40 @@ describe "EditorComponent", -> gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2))) expect(editor.getSelectedScreenRange()).toEqual [[2, 0], [7, 0]] + describe "when the gutter is meta-clicked and dragged", -> + beforeEach -> + editor.setSelectedScreenRange([[3, 0], [3, 2]]) + + describe "when dragging downward", -> + it "selects the rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]] + + describe "when dragging upward", -> + it "selects the rows between the start and end of the drag", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(4), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[3, 0], [3, 2]], [[4, 0], [7, 0]]] + + it "merges overlapping selections", -> + gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(6), metaKey: true)) + gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + nextAnimationFrame() + gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(2), metaKey: true)) + expect(editor.getSelectedScreenRanges()).toEqual [[[2, 0], [7, 0]]] + describe "when the gutter is shift-clicked and dragged", -> describe "when the shift-click is below the existing selection's tail", -> describe "when dragging downward", -> diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 17c0f831c..003e9b703 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -1,3 +1,4 @@ +_ = require 'underscore-plus' React = require 'react-atom-fork' {div, span} = require 'reactionary-atom-fork' {debounce, defaults, isEqualForProperties} = require 'underscore-plus' @@ -640,8 +641,12 @@ EditorComponent = React.createClass onGutterMouseDown: (event) -> return unless event.button is 0 # only handle the left mouse button - if event.shiftKey + {shiftKey, metaKey, ctrlKey} = event + + if shiftKey @onGutterShiftClick(event) + else if metaKey or (ctrlKey and process.platform isnt 'darwin') + @onGutterMetaClick(event) else @onGutterClick(event) @@ -658,6 +663,30 @@ EditorComponent = React.createClass else editor.setSelectedScreenRange([[clickedRow, 0], [dragRow + 1, 0]]) + onGutterMetaClick: (event) -> + {editor} = @props + clickedRow = @screenPositionForMouseEvent(event).row + + bufferRange = editor.bufferRangeForScreenRange([[clickedRow, 0], [clickedRow + 1, 0]]) + rowSelection = editor.addSelectionForBufferRange(bufferRange) + + @handleDragUntilMouseUp event, (screenPosition) -> + dragRow = screenPosition.row + + if dragRow < clickedRow # dragging up + rowSelection.setScreenRange([[dragRow, 0], [clickedRow + 1, 0]]) + else + rowSelection.setScreenRange([[clickedRow, 0], [dragRow + 1, 0]]) + + # After updating the selected screen range, merge overlapping selections + editor.mergeIntersectingSelections() + + # The merge process will possibly destroy the current selection because + # it will be merged into another one. Therefore, we need to obtain a + # reference to the new selection that contains the originally selected row + rowSelection = _.find editor.getSelections(), (selection) -> + selection.intersectsBufferRange(bufferRange) + onGutterShiftClick: (event) -> {editor} = @props clickedRow = @screenPositionForMouseEvent(event).row