From dd5c65b5f9368c181e2e22039e33370c95fd7b81 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sun, 26 Jan 2014 17:25:59 -0500 Subject: [PATCH 001/407] Add commands to move directionally between panes --- spec/pane-container-view-spec.coffee | 86 ++++++++++++ src/pane-container-view.coffee | 16 +++ src/positionally-aware-pane.coffee | 194 +++++++++++++++++++++++++++ src/workspace-view.coffee | 17 +++ 4 files changed, 313 insertions(+) create mode 100644 src/positionally-aware-pane.coffee diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 667d3bd6a..7ed6ffea2 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -265,3 +265,89 @@ describe "PaneContainerView", -> pane1.remove() pane2.remove() expect(activeItemChangedHandler).not.toHaveBeenCalled() + + describe "changing focus directionally between panes", -> + [pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = [] + + beforeEach -> + # Set up a grid of 9 panes, in the following arrangement, where the + # numbers correspond to the variable names below. + # + # ------- + # |1|2|3| + # ------- + # |4|5|6| + # ------- + # |7|8|9| + # ------- + + container = new PaneContainerView + pane1 = container.getRoot() + pane1.activateItem(new TestView('1')) + pane4 = pane1.splitDown(new TestView('4')) + pane7 = pane4.splitDown(new TestView('7')) + + pane2 = pane1.splitRight(new TestView('2')) + pane3 = pane2.splitRight(new TestView('3')) + + pane5 = pane4.splitRight(new TestView('5')) + pane6 = pane5.splitRight(new TestView('6')) + + pane8 = pane7.splitRight(new TestView('8')) + pane9 = pane8.splitRight(new TestView('9')) + + container.height(400) + container.width(400) + container.attachToDom() + + describe ".focusPaneAbove()", -> + describe "when there are multiple rows above the focused pane", -> + it "focuses up to the adjacent row", -> + pane8.focus() + container.focusPaneAbove() + expect(pane5.activeItem).toMatchSelector ':focus' + + describe "when there are no rows above the focused pane", -> + it "keeps the current pane focused", -> + pane2.focus() + container.focusPaneAbove() + expect(pane2.activeItem).toMatchSelector ':focus' + + describe ".focusPaneBelow()", -> + describe "when there are multiple rows below the focused pane", -> + it "focuses down to the adjacent row", -> + pane2.focus() + container.focusPaneBelow() + expect(pane5.activeItem).toMatchSelector ':focus' + + describe "when there are no rows below the focused pane", -> + it "keeps the current pane focused", -> + pane8.focus() + container.focusPaneBelow() + expect(pane8.activeItem).toMatchSelector ':focus' + + describe ".focusPaneOnLeft()", -> + describe "when there are multiple columns to the left of the focused pane", -> + it "focuses left to the adjacent column", -> + pane6.focus() + container.focusPaneOnLeft() + expect(pane5.activeItem).toMatchSelector ':focus' + + describe "when there are no columns to the left of the focused pane", -> + it "keeps the current pane focused", -> + pane4.focus() + container.focusPaneOnLeft() + expect(pane4.activeItem).toMatchSelector ':focus' + + describe ".focusPaneOnRight()", -> + describe "when there are multiple columns to the right of the focused pane", -> + it "focuses right to the adjacent column", -> + pane4.focus() + container.focusPaneOnRight() + expect(pane5.activeItem).toMatchSelector ':focus' + + describe "when there are no columns to the right of the focused pane", -> + it "keeps the current pane focused", -> + pane6.focus() + container.focusPaneOnRight() + expect(pane6.activeItem).toMatchSelector ':focus' diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 8d0d1b0e8..f7ef29bb6 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -2,6 +2,7 @@ Delegator = require 'delegato' {$, View} = require './space-pen-extensions' PaneView = require './pane-view' PaneContainer = require './pane-container' +PositionallyAwarePane = require './positionally-aware-pane' # Private: Manages the list of panes within a {WorkspaceView} module.exports = @@ -98,3 +99,18 @@ class PaneContainerView extends View focusPreviousPane: -> @model.activatePreviousPane() + + focusPaneAbove: -> + @positionallyAwarePaneForActivePane().focusPaneAbove() + + focusPaneBelow: -> + @positionallyAwarePaneForActivePane().focusPaneBelow() + + focusPaneOnLeft: -> + @positionallyAwarePaneForActivePane().focusPaneOnLeft() + + focusPaneOnRight: -> + @positionallyAwarePaneForActivePane().focusPaneOnRight() + + positionallyAwarePaneForActivePane: -> + new PositionallyAwarePane(@getActivePane(), @getPanes()) diff --git a/src/positionally-aware-pane.coffee b/src/positionally-aware-pane.coffee new file mode 100644 index 000000000..7158936b5 --- /dev/null +++ b/src/positionally-aware-pane.coffee @@ -0,0 +1,194 @@ +_ = require 'underscore-plus' + +# Private: Wraps a {Pane} to decorate it with knowledge of its physicial +# location relative to all other {Pane}s. +# +# Intended as a helper for {PaneContainerView}. +module.exports = +class PositionallyAwarePane + + # Creates a {PositionallyAwarePane}. + # + # * pane: + # The {Pane} that needs to gain some positional awareness. + # * allPanes: + # The collection of all {Pane}s. + constructor: (@pane, @allPanes) -> + + focusPaneAbove: -> + @bestChoiceForVerticalNavigation(@panesInAdjecentRowAbove())?.focus() + + focusPaneBelow: -> + @bestChoiceForVerticalNavigation(@panesInAdjecentRowBelow())?.focus() + + focusPaneOnLeft: -> + @bestChoiceForHorizontalNavigation(@panesInAdjecentColumnOnLeft())?.focus() + + focusPaneOnRight: -> + @bestChoiceForHorizontalNavigation(@panesInAdjecentColumnOnRight())?.focus() + + focus: -> + @pane.focus() + + width: -> + @pane.width() + + height: -> + @pane.height() + + xLeft: -> + @pane.offset().left + + xCenter: -> + @xLeft() + @width()/2 + + xRight: -> + @xLeft() + @width() + + yTop: -> + @pane.offset().top + + yCenter: -> + @yTop() + @height()/2 + + yBottom: -> + @yTop() + @height() + + ### Internal ### + + panesInAdjecentRowAbove: -> + allPanesAbove = @otherPanes().filter (pane) => @isBelow(pane) + yBottomValues = _.map allPanesAbove, (pane) -> pane.yBottom() + maxYBottom = _.max yBottomValues + panesVerticallyNearest = allPanesAbove.filter (pane) -> + pane.yBottom() == maxYBottom + + panesInAdjecentRowBelow: -> + allPanesBelow = @otherPanes().filter (pane) => @isAbove(pane) + + yTopValues = _.map allPanesBelow, (pane) -> pane.yTop() + minYTop = _.min yTopValues + panesVerticallyNearest = allPanesBelow.filter (pane) -> + pane.yTop() == minYTop + + panesInAdjecentColumnOnLeft: -> + allPanesOnLeft = @otherPanes().filter (pane) => @isRightOf(pane) + xRightValues = _.map allPanesOnLeft, (pane) -> pane.xRight() + maxXRight = _.max xRightValues + panesHorizontallyNearest = allPanesOnLeft.filter (pane) -> + pane.xRight() == maxXRight + + # Internal + panesInAdjecentColumnOnRight: -> + allPanesOnRight = @otherPanes().filter (pane) => @isLeftOf(pane) + xLeftValues = _.map allPanesOnRight, (pane) -> pane.xLeft() + minXLeft = _.min xLeftValues + panesHorizontallyNearest = allPanesOnRight.filter (pane) -> + pane.xLeft() == minXLeft + + # Determine whether this pane is above the given pane. + # + # * otherPane: + # The {PositionallyAwarePane} to compare to this pane. + # + # Returns true if this pane is above otherPane; otherwise, false. + isAbove: (otherPane) -> + otherPaneYTop = otherPane.yTop() + @overlap() + otherPaneYTop >= @yBottom() + + # Determine whether this pane is below the given pane. + # + # * otherPane: + # The {PositionallyAwarePane} to compare to this pane. + # + # Returns true if this pane is below otherPane; otherwise, false. + isBelow: (otherPane) -> + otherPaneYBottom = otherPane.yBottom() - @overlap() + otherPaneYBottom <= @yTop() + + # Determine whether this pane is to the left of the given pane. + # + # * otherPane: + # The {PositionallyAwarePane} to compare to this pane. + # + # Returns true if this pane is to the left of otherPane; otherwise, false. + isLeftOf: (otherPane) -> + otherPaneXLeft = otherPane.xLeft() + @overlap() + otherPaneXLeft >= @xRight() + + # Determine whether this pane is to the right of the given pane. + # + # * otherPane: + # The {PositionallyAwarePane} to compare to this pane. + # + # Returns true if this pane is to the right of otherPane; otherwise, false. + isRightOf: (otherPane) -> + otherPaneXRight = otherPane.xRight() - @overlap() + otherPaneXRight <= @xLeft() + + # The adjacent column may include several panes. When navigating left or right + # from this pane, find the pane in the adjacent column that is the most + # appropriate destination. + # + # * panes: + # An Array of {PositionallyAwarePane}s in the column adjacent to this pane. + # + # Returns a PositionallyAwarePane. + bestChoiceForHorizontalNavigation: (panes) -> + _.find panes, (pane) => + pane.yTop() <= @yCenter() and @yCenter() <= pane.yBottom() + + # The adjacent row may include several panes. When navigating up or down from + # this pane, find the pane in the adjacent row that is the most appropriate + # destination. + # + # * panes: + # An Array of {PositionallyAwarePane}s in the row adjacent to this pane. + # + # Returns a PositionallyAwarePane. + bestChoiceForVerticalNavigation: (panes) -> + _.find panes, (pane) => + pane.xLeft() <= @xCenter() and @xCenter() <= pane.xRight() + + # In theory, if two panes are side-by-side, then the rightmost x coordinate of + # the pane on the left should be less than or equal to the leftmost x + # coordinate of the pane on the right. For example, assume we have two panes: + # + # ----- + # |1|2| + # ----- + # + # If the rightmost x coordinate of Pane #1 is 400, then the leftmost x + # coordinate of Pane #2 should be at least 400. In practice, this isn't always + # true. Sometimes there seems to be a small "overlap" between the two panes. + # If Pane #1's rightmost x coordinate is 400, then Pane 2's leftmost x + # coordinate might be 399.2 (for example). + # + # A similar issue occurs for the y coordinates. + # + # To cope with this issue, this method provides a rough guess as to the + # amount of overlap between panes. + # + # Returns a Number. + overlap: -> + 2 + + # Returns an Array of {PositionallyAwarePane}s for all of the other panes, + # excluding this pane. + otherPanes: -> + _.map @allPanes, (pane) -> new PositionallyAwarePane(pane, @allPanes) + + coordinates: -> + xLeft: @xLeft() + xCenter: @xCenter() + xRight: @xRight() + yTop: @yTop() + yCenter: @yCenter() + yBottom: @yBottom() + + logDebugInfo: -> + console.log "Coordinates for this pane:" + console.log @coordinates() + + console.log "Coordinates for other panes:" + @otherPanes().forEach (pane) -> console.log pane.coordinates() diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index fffebd826..1bd2ac4a9 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -121,6 +121,10 @@ class WorkspaceView extends View @command 'window:focus-next-pane', => @focusNextPane() @command 'window:focus-previous-pane', => @focusPreviousPane() + @command 'window:focus-pane-above', => @focusPaneAbove() + @command 'window:focus-pane-below', => @focusPaneBelow() + @command 'window:focus-pane-on-left', => @focusPaneOnLeft() + @command 'window:focus-pane-on-right', => @focusPaneOnRight() @command 'window:save-all', => @saveAll() @command 'window:toggle-invisibles', => atom.config.toggle("editor.showInvisibles") @@ -245,6 +249,19 @@ class WorkspaceView extends View # Public: Focuses the next pane by id. focusNextPane: -> @model.activateNextPane() + # Public: Focuses the pane directly above the currently-focused pane. + focusPaneAbove: -> @panes.focusPaneAbove() + + # Public: Focuses the pane directly below the currently-focused pane. + focusPaneBelow: -> @panes.focusPaneBelow() + + # Public: Focuses the pane directly to the left of the currently-focused pane. + focusPaneOnLeft: -> @panes.focusPaneOnLeft() + + # Public: Focuses the pane directly to the right of the currently-focused + # pane. + focusPaneOnRight: -> @panes.focusPaneOnRight() + # Public: # # FIXME: Difference between active and focused pane? From f322143272c5bd4ac151afd9000a6e2d5cca08c0 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sun, 26 Jan 2014 17:27:41 -0500 Subject: [PATCH 002/407] Place pane navigation specs in proximity to each other --- spec/pane-container-view-spec.coffee | 52 ++++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 7ed6ffea2..28fbd56a6 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -27,32 +27,6 @@ describe "PaneContainerView", -> afterEach -> atom.deserializers.remove(TestView) - describe ".focusNextPane()", -> - it "focuses the pane following the focused pane or the first pane if no pane has focus", -> - container.attachToDom() - container.focusNextPane() - expect(pane1.activeItem).toMatchSelector ':focus' - container.focusNextPane() - expect(pane2.activeItem).toMatchSelector ':focus' - container.focusNextPane() - expect(pane3.activeItem).toMatchSelector ':focus' - container.focusNextPane() - expect(pane1.activeItem).toMatchSelector ':focus' - - describe ".focusPreviousPane()", -> - it "focuses the pane preceding the focused pane or the last pane if no pane has focus", -> - container.attachToDom() - container.getPanes()[0].focus() # activate first pane - - container.focusPreviousPane() - expect(pane3.activeItem).toMatchSelector ':focus' - container.focusPreviousPane() - expect(pane2.activeItem).toMatchSelector ':focus' - container.focusPreviousPane() - expect(pane1.activeItem).toMatchSelector ':focus' - container.focusPreviousPane() - expect(pane3.activeItem).toMatchSelector ':focus' - describe ".getActivePane()", -> it "returns the most-recently focused pane", -> focusStealer = $$ -> @div tabindex: -1, "focus stealer" @@ -266,6 +240,32 @@ describe "PaneContainerView", -> pane2.remove() expect(activeItemChangedHandler).not.toHaveBeenCalled() + describe ".focusNextPane()", -> + it "focuses the pane following the focused pane or the first pane if no pane has focus", -> + container.attachToDom() + container.focusNextPane() + expect(pane1.activeItem).toMatchSelector ':focus' + container.focusNextPane() + expect(pane2.activeItem).toMatchSelector ':focus' + container.focusNextPane() + expect(pane3.activeItem).toMatchSelector ':focus' + container.focusNextPane() + expect(pane1.activeItem).toMatchSelector ':focus' + + describe ".focusPreviousPane()", -> + it "focuses the pane preceding the focused pane or the last pane if no pane has focus", -> + container.attachToDom() + container.getPanes()[0].focus() # activate first pane + + container.focusPreviousPane() + expect(pane3.activeItem).toMatchSelector ':focus' + container.focusPreviousPane() + expect(pane2.activeItem).toMatchSelector ':focus' + container.focusPreviousPane() + expect(pane1.activeItem).toMatchSelector ':focus' + container.focusPreviousPane() + expect(pane3.activeItem).toMatchSelector ':focus' + describe "changing focus directionally between panes", -> [pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = [] From 8c75f425e71859f7f4561b0ea06e557f2606ba44 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Sun, 26 Jan 2014 17:41:48 -0500 Subject: [PATCH 003/407] Add keymaps for moving directionally between panes - Add keymaps for the new commands. Since the new commands are all about moving in a specific direction between panes, use the up, down, left, and right keys in the keymaps. - Change the keymaps for the existing commands (since the new commands for moving left and right are now using the old keymaps for moving to the previous and next pane respectively). Use "p" instead of "left" in the keymap for focusing the *p*revious pane. Use "n" instead of "right" in the keymap for focusing the *n*ext pane. --- keymaps/darwin.cson | 8 ++++++-- keymaps/win32.cson | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index a112bfbf7..c93079efa 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -75,8 +75,12 @@ 'cmd-k right': 'pane:split-right' # Atom Specific 'cmd-k cmd-w': 'pane:close' # Atom Specific 'cmd-k alt-cmd-w': 'pane:close-other-items' # Atom Specific - 'cmd-k cmd-left': 'window:focus-previous-pane' - 'cmd-k cmd-right': 'window:focus-next-pane' + 'cmd-k cmd-p': 'window:focus-previous-pane' + 'cmd-k cmd-n': 'window:focus-next-pane' + 'cmd-k cmd-up': 'window:focus-pane-above' + 'cmd-k cmd-down': 'window:focus-pane-below' + 'cmd-k cmd-left': 'window:focus-pane-on-left' + 'cmd-k cmd-right': 'window:focus-pane-on-right' 'cmd-1': 'pane:show-item-1' 'cmd-2': 'pane:show-item-2' 'cmd-3': 'pane:show-item-3' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index 3b6a230a5..9c78e3622 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -47,8 +47,12 @@ 'ctrl-k right': 'pane:split-right' # Atom Specific 'ctrl-k ctrl-w': 'pane:close' # Atom Specific 'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific - 'ctrl-k ctrl-left': 'window:focus-previous-pane' - 'ctrl-k ctrl-right': 'window:focus-next-pane' + 'ctrl-k ctrl-p': 'window:focus-previous-pane' + 'ctrl-k ctrl-n': 'window:focus-next-pane' + 'ctrl-k ctrl-up': 'window:focus-pane-above' + 'ctrl-k ctrl-down': 'window:focus-pane-below' + 'ctrl-k ctrl-left': 'window:focus-pane-on-left' + 'ctrl-k ctrl-right': 'window:focus-pane-on-right' '.workspace .editor': # Windows specific From 8772e45a3906a38ef91d9653518e59ae5db467ad Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Mon, 27 Jan 2014 06:41:49 -0500 Subject: [PATCH 004/407] Fix typo in method names --- src/positionally-aware-pane.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/positionally-aware-pane.coffee b/src/positionally-aware-pane.coffee index 7158936b5..62960b6f5 100644 --- a/src/positionally-aware-pane.coffee +++ b/src/positionally-aware-pane.coffee @@ -16,16 +16,16 @@ class PositionallyAwarePane constructor: (@pane, @allPanes) -> focusPaneAbove: -> - @bestChoiceForVerticalNavigation(@panesInAdjecentRowAbove())?.focus() + @bestChoiceForVerticalNavigation(@panesInAdjacentRowAbove())?.focus() focusPaneBelow: -> - @bestChoiceForVerticalNavigation(@panesInAdjecentRowBelow())?.focus() + @bestChoiceForVerticalNavigation(@panesInAdjacentRowBelow())?.focus() focusPaneOnLeft: -> - @bestChoiceForHorizontalNavigation(@panesInAdjecentColumnOnLeft())?.focus() + @bestChoiceForHorizontalNavigation(@panesInAdjacentColumnOnLeft())?.focus() focusPaneOnRight: -> - @bestChoiceForHorizontalNavigation(@panesInAdjecentColumnOnRight())?.focus() + @bestChoiceForHorizontalNavigation(@panesInAdjacentColumnOnRight())?.focus() focus: -> @pane.focus() @@ -56,14 +56,14 @@ class PositionallyAwarePane ### Internal ### - panesInAdjecentRowAbove: -> + panesInAdjacentRowAbove: -> allPanesAbove = @otherPanes().filter (pane) => @isBelow(pane) yBottomValues = _.map allPanesAbove, (pane) -> pane.yBottom() maxYBottom = _.max yBottomValues panesVerticallyNearest = allPanesAbove.filter (pane) -> pane.yBottom() == maxYBottom - panesInAdjecentRowBelow: -> + panesInAdjacentRowBelow: -> allPanesBelow = @otherPanes().filter (pane) => @isAbove(pane) yTopValues = _.map allPanesBelow, (pane) -> pane.yTop() @@ -71,7 +71,7 @@ class PositionallyAwarePane panesVerticallyNearest = allPanesBelow.filter (pane) -> pane.yTop() == minYTop - panesInAdjecentColumnOnLeft: -> + panesInAdjacentColumnOnLeft: -> allPanesOnLeft = @otherPanes().filter (pane) => @isRightOf(pane) xRightValues = _.map allPanesOnLeft, (pane) -> pane.xRight() maxXRight = _.max xRightValues @@ -79,7 +79,7 @@ class PositionallyAwarePane pane.xRight() == maxXRight # Internal - panesInAdjecentColumnOnRight: -> + panesInAdjacentColumnOnRight: -> allPanesOnRight = @otherPanes().filter (pane) => @isLeftOf(pane) xLeftValues = _.map allPanesOnRight, (pane) -> pane.xLeft() minXLeft = _.min xLeftValues From fc5bc1632dd1e157c51d508f21170c942c3375f2 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 12:08:34 -0800 Subject: [PATCH 005/407] Shrink the nearest pane code --- src/pane-container-view.coffee | 49 ++++++-- src/positionally-aware-pane.coffee | 194 ----------------------------- 2 files changed, 42 insertions(+), 201 deletions(-) delete mode 100644 src/positionally-aware-pane.coffee diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index f7ef29bb6..2f52c8e4f 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -2,7 +2,6 @@ Delegator = require 'delegato' {$, View} = require './space-pen-extensions' PaneView = require './pane-view' PaneContainer = require './pane-container' -PositionallyAwarePane = require './positionally-aware-pane' # Private: Manages the list of panes within a {WorkspaceView} module.exports = @@ -101,16 +100,52 @@ class PaneContainerView extends View @model.activatePreviousPane() focusPaneAbove: -> - @positionallyAwarePaneForActivePane().focusPaneAbove() + @nearestPaneInDirection('above')?.focus() focusPaneBelow: -> - @positionallyAwarePaneForActivePane().focusPaneBelow() + @nearestPaneInDirection('below')?.focus() focusPaneOnLeft: -> - @positionallyAwarePaneForActivePane().focusPaneOnLeft() + @nearestPaneInDirection('left')?.focus() focusPaneOnRight: -> - @positionallyAwarePaneForActivePane().focusPaneOnRight() + @nearestPaneInDirection('right')?.focus() - positionallyAwarePaneForActivePane: -> - new PositionallyAwarePane(@getActivePane(), @getPanes()) + nearestPaneInDirection: (direction) -> + pane = @getActivePane() + box = @boundingBoxForPane(pane) + panes = @getPanes() + .filter (otherPane) => + otherBox = @boundingBoxForPane(otherPane) + switch direction + when 'left' then otherBox.right.x <= box.left.x + when 'right' then otherBox.left.x >= box.right.x + when 'above' then otherBox.bottom.y <= box.top.y + when 'below' then otherBox.top.y >= box.bottom.y + .sort (paneA, paneB) => + boxA = @boundingBoxForPane(paneA) + boxB = @boundingBoxForPane(paneB) + switch direction + when 'left' + @distanceBetweenPoints(box.left, boxA.right) - @distanceBetweenPoints(box.left, boxB.right) + when 'right' + @distanceBetweenPoints(box.right, boxA.left) - @distanceBetweenPoints(box.right, boxB.left) + when 'above' + @distanceBetweenPoints(box.top, boxA.bottom) - @distanceBetweenPoints(box.top, boxB.bottom) + when 'below' + @distanceBetweenPoints(box.bottom, boxA.top) - @distanceBetweenPoints(box.bottom, boxB.top) + + panes[0] + + boundingBoxForPane: (pane) -> + boundingBox = pane[0].getBoundingClientRect() + + left: {x: boundingBox.left, y: boundingBox.top} + right: {x: boundingBox.right, y: boundingBox.top} + top: {x: boundingBox.left, y: boundingBox.top} + bottom: {x: boundingBox.left, y: boundingBox.bottom} + + distanceBetweenPoints: (pointA, pointB) -> + x = pointB.x - pointA.x + y = pointB.y - pointA.y + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); diff --git a/src/positionally-aware-pane.coffee b/src/positionally-aware-pane.coffee deleted file mode 100644 index 62960b6f5..000000000 --- a/src/positionally-aware-pane.coffee +++ /dev/null @@ -1,194 +0,0 @@ -_ = require 'underscore-plus' - -# Private: Wraps a {Pane} to decorate it with knowledge of its physicial -# location relative to all other {Pane}s. -# -# Intended as a helper for {PaneContainerView}. -module.exports = -class PositionallyAwarePane - - # Creates a {PositionallyAwarePane}. - # - # * pane: - # The {Pane} that needs to gain some positional awareness. - # * allPanes: - # The collection of all {Pane}s. - constructor: (@pane, @allPanes) -> - - focusPaneAbove: -> - @bestChoiceForVerticalNavigation(@panesInAdjacentRowAbove())?.focus() - - focusPaneBelow: -> - @bestChoiceForVerticalNavigation(@panesInAdjacentRowBelow())?.focus() - - focusPaneOnLeft: -> - @bestChoiceForHorizontalNavigation(@panesInAdjacentColumnOnLeft())?.focus() - - focusPaneOnRight: -> - @bestChoiceForHorizontalNavigation(@panesInAdjacentColumnOnRight())?.focus() - - focus: -> - @pane.focus() - - width: -> - @pane.width() - - height: -> - @pane.height() - - xLeft: -> - @pane.offset().left - - xCenter: -> - @xLeft() + @width()/2 - - xRight: -> - @xLeft() + @width() - - yTop: -> - @pane.offset().top - - yCenter: -> - @yTop() + @height()/2 - - yBottom: -> - @yTop() + @height() - - ### Internal ### - - panesInAdjacentRowAbove: -> - allPanesAbove = @otherPanes().filter (pane) => @isBelow(pane) - yBottomValues = _.map allPanesAbove, (pane) -> pane.yBottom() - maxYBottom = _.max yBottomValues - panesVerticallyNearest = allPanesAbove.filter (pane) -> - pane.yBottom() == maxYBottom - - panesInAdjacentRowBelow: -> - allPanesBelow = @otherPanes().filter (pane) => @isAbove(pane) - - yTopValues = _.map allPanesBelow, (pane) -> pane.yTop() - minYTop = _.min yTopValues - panesVerticallyNearest = allPanesBelow.filter (pane) -> - pane.yTop() == minYTop - - panesInAdjacentColumnOnLeft: -> - allPanesOnLeft = @otherPanes().filter (pane) => @isRightOf(pane) - xRightValues = _.map allPanesOnLeft, (pane) -> pane.xRight() - maxXRight = _.max xRightValues - panesHorizontallyNearest = allPanesOnLeft.filter (pane) -> - pane.xRight() == maxXRight - - # Internal - panesInAdjacentColumnOnRight: -> - allPanesOnRight = @otherPanes().filter (pane) => @isLeftOf(pane) - xLeftValues = _.map allPanesOnRight, (pane) -> pane.xLeft() - minXLeft = _.min xLeftValues - panesHorizontallyNearest = allPanesOnRight.filter (pane) -> - pane.xLeft() == minXLeft - - # Determine whether this pane is above the given pane. - # - # * otherPane: - # The {PositionallyAwarePane} to compare to this pane. - # - # Returns true if this pane is above otherPane; otherwise, false. - isAbove: (otherPane) -> - otherPaneYTop = otherPane.yTop() + @overlap() - otherPaneYTop >= @yBottom() - - # Determine whether this pane is below the given pane. - # - # * otherPane: - # The {PositionallyAwarePane} to compare to this pane. - # - # Returns true if this pane is below otherPane; otherwise, false. - isBelow: (otherPane) -> - otherPaneYBottom = otherPane.yBottom() - @overlap() - otherPaneYBottom <= @yTop() - - # Determine whether this pane is to the left of the given pane. - # - # * otherPane: - # The {PositionallyAwarePane} to compare to this pane. - # - # Returns true if this pane is to the left of otherPane; otherwise, false. - isLeftOf: (otherPane) -> - otherPaneXLeft = otherPane.xLeft() + @overlap() - otherPaneXLeft >= @xRight() - - # Determine whether this pane is to the right of the given pane. - # - # * otherPane: - # The {PositionallyAwarePane} to compare to this pane. - # - # Returns true if this pane is to the right of otherPane; otherwise, false. - isRightOf: (otherPane) -> - otherPaneXRight = otherPane.xRight() - @overlap() - otherPaneXRight <= @xLeft() - - # The adjacent column may include several panes. When navigating left or right - # from this pane, find the pane in the adjacent column that is the most - # appropriate destination. - # - # * panes: - # An Array of {PositionallyAwarePane}s in the column adjacent to this pane. - # - # Returns a PositionallyAwarePane. - bestChoiceForHorizontalNavigation: (panes) -> - _.find panes, (pane) => - pane.yTop() <= @yCenter() and @yCenter() <= pane.yBottom() - - # The adjacent row may include several panes. When navigating up or down from - # this pane, find the pane in the adjacent row that is the most appropriate - # destination. - # - # * panes: - # An Array of {PositionallyAwarePane}s in the row adjacent to this pane. - # - # Returns a PositionallyAwarePane. - bestChoiceForVerticalNavigation: (panes) -> - _.find panes, (pane) => - pane.xLeft() <= @xCenter() and @xCenter() <= pane.xRight() - - # In theory, if two panes are side-by-side, then the rightmost x coordinate of - # the pane on the left should be less than or equal to the leftmost x - # coordinate of the pane on the right. For example, assume we have two panes: - # - # ----- - # |1|2| - # ----- - # - # If the rightmost x coordinate of Pane #1 is 400, then the leftmost x - # coordinate of Pane #2 should be at least 400. In practice, this isn't always - # true. Sometimes there seems to be a small "overlap" between the two panes. - # If Pane #1's rightmost x coordinate is 400, then Pane 2's leftmost x - # coordinate might be 399.2 (for example). - # - # A similar issue occurs for the y coordinates. - # - # To cope with this issue, this method provides a rough guess as to the - # amount of overlap between panes. - # - # Returns a Number. - overlap: -> - 2 - - # Returns an Array of {PositionallyAwarePane}s for all of the other panes, - # excluding this pane. - otherPanes: -> - _.map @allPanes, (pane) -> new PositionallyAwarePane(pane, @allPanes) - - coordinates: -> - xLeft: @xLeft() - xCenter: @xCenter() - xRight: @xRight() - yTop: @yTop() - yCenter: @yCenter() - yBottom: @yBottom() - - logDebugInfo: -> - console.log "Coordinates for this pane:" - console.log @coordinates() - - console.log "Coordinates for other panes:" - @otherPanes().forEach (pane) -> console.log pane.coordinates() From 32cd0ee97288817c08bcd23e986e5a344b9adf9c Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 12:10:15 -0800 Subject: [PATCH 006/407] Make distance a local method --- src/pane-container-view.coffee | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 2f52c8e4f..fbb77c262 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -112,6 +112,11 @@ class PaneContainerView extends View @nearestPaneInDirection('right')?.focus() nearestPaneInDirection: (direction) -> + distance: (pointA, pointB) -> + x = pointB.x - pointA.x + y = pointB.y - pointA.y + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + pane = @getActivePane() box = @boundingBoxForPane(pane) panes = @getPanes() @@ -126,14 +131,10 @@ class PaneContainerView extends View boxA = @boundingBoxForPane(paneA) boxB = @boundingBoxForPane(paneB) switch direction - when 'left' - @distanceBetweenPoints(box.left, boxA.right) - @distanceBetweenPoints(box.left, boxB.right) - when 'right' - @distanceBetweenPoints(box.right, boxA.left) - @distanceBetweenPoints(box.right, boxB.left) - when 'above' - @distanceBetweenPoints(box.top, boxA.bottom) - @distanceBetweenPoints(box.top, boxB.bottom) - when 'below' - @distanceBetweenPoints(box.bottom, boxA.top) - @distanceBetweenPoints(box.bottom, boxB.top) + when 'left' then @distance(box.left, boxA.right) - @distance(box.left, boxB.right) + when 'right' then @distance(box.right, boxA.left) - @distance(box.right, boxB.left) + when 'above' then @distance(box.top, boxA.bottom) - @distance(box.top, boxB.bottom) + when 'below' then @distance(box.bottom, boxA.top) - @distance(box.bottom, boxB.top) panes[0] @@ -144,8 +145,3 @@ class PaneContainerView extends View right: {x: boundingBox.right, y: boundingBox.top} top: {x: boundingBox.left, y: boundingBox.top} bottom: {x: boundingBox.left, y: boundingBox.bottom} - - distanceBetweenPoints: (pointA, pointB) -> - x = pointB.x - pointA.x - y = pointB.y - pointA.y - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); From e3f0e11aa8542472980e839c7edbe61755341ffd Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 12:14:30 -0800 Subject: [PATCH 007/407] Remove trailing ; --- src/pane-container-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index fbb77c262..4e5b535b4 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -115,7 +115,7 @@ class PaneContainerView extends View distance: (pointA, pointB) -> x = pointB.x - pointA.x y = pointB.y - pointA.y - Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) pane = @getActivePane() box = @boundingBoxForPane(pane) From 97330d19f35efad5ce0899d3fa91efa9888402f7 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 13:10:07 -0800 Subject: [PATCH 008/407] Fix method name bug --- src/pane-container-view.coffee | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 4e5b535b4..8f84be5be 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -131,10 +131,10 @@ class PaneContainerView extends View boxA = @boundingBoxForPane(paneA) boxB = @boundingBoxForPane(paneB) switch direction - when 'left' then @distance(box.left, boxA.right) - @distance(box.left, boxB.right) - when 'right' then @distance(box.right, boxA.left) - @distance(box.right, boxB.left) - when 'above' then @distance(box.top, boxA.bottom) - @distance(box.top, boxB.bottom) - when 'below' then @distance(box.bottom, boxA.top) - @distance(box.bottom, boxB.top) + when 'left' then distance(box.left, boxA.right) - distance(box.left, boxB.right) + when 'right' then distance(box.right, boxA.left) - distance(box.right, boxB.left) + when 'above' then distance(box.top, boxA.bottom) - distance(box.top, boxB.bottom) + when 'below' then distance(box.bottom, boxA.top) - distance(box.bottom, boxB.top) panes[0] From 9176e12f5866385eec2af98735f0e468507a0358 Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 13:20:54 -0800 Subject: [PATCH 009/407] Actually fix the method name bug For real this time --- src/pane-container-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 8f84be5be..c80cbf9ba 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -112,7 +112,7 @@ class PaneContainerView extends View @nearestPaneInDirection('right')?.focus() nearestPaneInDirection: (direction) -> - distance: (pointA, pointB) -> + distance = (pointA, pointB) -> x = pointB.x - pointA.x y = pointB.y - pointA.y Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) From 99f25267a030329399879a75b448b8e4b187846a Mon Sep 17 00:00:00 2001 From: probablycorey Date: Wed, 29 Jan 2014 16:52:42 -0800 Subject: [PATCH 010/407] Make sure the filePath is never null or undefined --- src/project.coffee | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/project.coffee b/src/project.coffee index 62f5f0dbe..51d7e78d9 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -143,9 +143,10 @@ class Project extends Model # # Returns a promise that resolves to an {Editor}. open: (filePath, options={}) -> - filePath = @resolve(filePath) - resource = null - _.find @openers, (opener) -> resource = opener(filePath, options) + filePath = @resolve(filePath) ? '' + resource = opener(filePath, options) for opener in @openers when !resource + + console.log resource if resource Q(resource) @@ -155,11 +156,10 @@ class Project extends Model # Private: Only be used in specs openSync: (filePath, options={}) -> - filePath = @resolve(filePath) - for opener in @openers - return resource if resource = opener(filePath, options) + filePath = @resolve(filePath) ? '' + resource = opener(filePath, options) for opener in @openers when !resource - @buildEditorForBuffer(@bufferForPathSync(filePath), options) + resource or @buildEditorForBuffer(@bufferForPathSync(filePath), options) # Public: Retrieves all {Editor}s for all open files. # From fae28d8c21c34e4cb4d9dd2c8692ff23d660f4aa Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 3 Feb 2014 14:51:15 +0800 Subject: [PATCH 011/407] Upgrade apm for proxy support. --- vendor/apm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/apm b/vendor/apm index 739a3ec59..490e9aee6 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 739a3ec593ff32dab65e4e1b4682994cb8300324 +Subproject commit 490e9aee661b20728f3681a225ef322bf3fbb4eb From c1f3aa14cdea17b54fe364969f9ffa3c9a35947a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Mon, 3 Feb 2014 16:03:59 +0800 Subject: [PATCH 012/407] Do not send synchronous messages in index.html. --- src/browser/atom-application.coffee | 4 ++++ src/browser/atom-window.coffee | 11 ++++++++++- static/index.html | 10 +++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index affabc0ae..d1d3fa49e 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -1,6 +1,7 @@ AtomWindow = require './atom-window' ApplicationMenu = require './application-menu' AtomProtocolHandler = require './atom-protocol-handler' +BrowserWindow = require 'browser-window' Menu = require 'menu' autoUpdater = require 'auto-updater' app = require 'app' @@ -195,6 +196,9 @@ class AtomApplication ipc.on 'command', (processId, routingId, command) => @emit(command) + ipc.on 'window-command', (processId, routingId, command) => + BrowserWindow.fromProcessIdAndRoutingId(processId, routingId).emit(command) + # Public: Executes the given command. # # If it isn't handled globally, delegate to the currently focused window. diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index 4ca0ab3ad..868938a8b 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -5,6 +5,7 @@ dialog = require 'dialog' ipc = require 'ipc' path = require 'path' fs = require 'fs' +url = require 'url' _ = require 'underscore-plus' # Private: @@ -43,7 +44,7 @@ class AtomWindow @browserWindow.loadSettings = loadSettings @browserWindow.once 'window:loaded', => @loaded = true - @browserWindow.loadUrl "file://#{@resourcePath}/static/index.html" + @browserWindow.loadUrl @getUrl(loadSettings) @browserWindow.focusOnWebView() if @isSpec @openPath(pathToOpen, initialLine) @@ -51,6 +52,14 @@ class AtomWindow setupNodePath: (resourcePath) -> process.env['NODE_PATH'] = path.resolve(resourcePath, 'exports') + getUrl: (loadSettingsObj) -> + loadSettings = JSON.stringify(loadSettingsObj) + url.format + protocol: 'file' + pathname: "#{@resourcePath}/static/index.html" + slashes: true + query: {loadSettings} + getInitialPath: -> @browserWindow.loadSettings.initialPath diff --git a/static/index.html b/static/index.html index ebc054082..eaccafd9c 100644 --- a/static/index.html +++ b/static/index.html @@ -6,15 +6,19 @@