Shrink the nearest pane code

This commit is contained in:
probablycorey 2014-01-29 12:08:34 -08:00
parent 8772e45a39
commit fc5bc1632d
2 changed files with 42 additions and 201 deletions

View File

@ -2,7 +2,6 @@ Delegator = require 'delegato'
{$, View} = require './space-pen-extensions' {$, View} = require './space-pen-extensions'
PaneView = require './pane-view' PaneView = require './pane-view'
PaneContainer = require './pane-container' PaneContainer = require './pane-container'
PositionallyAwarePane = require './positionally-aware-pane'
# Private: Manages the list of panes within a {WorkspaceView} # Private: Manages the list of panes within a {WorkspaceView}
module.exports = module.exports =
@ -101,16 +100,52 @@ class PaneContainerView extends View
@model.activatePreviousPane() @model.activatePreviousPane()
focusPaneAbove: -> focusPaneAbove: ->
@positionallyAwarePaneForActivePane().focusPaneAbove() @nearestPaneInDirection('above')?.focus()
focusPaneBelow: -> focusPaneBelow: ->
@positionallyAwarePaneForActivePane().focusPaneBelow() @nearestPaneInDirection('below')?.focus()
focusPaneOnLeft: -> focusPaneOnLeft: ->
@positionallyAwarePaneForActivePane().focusPaneOnLeft() @nearestPaneInDirection('left')?.focus()
focusPaneOnRight: -> focusPaneOnRight: ->
@positionallyAwarePaneForActivePane().focusPaneOnRight() @nearestPaneInDirection('right')?.focus()
positionallyAwarePaneForActivePane: -> nearestPaneInDirection: (direction) ->
new PositionallyAwarePane(@getActivePane(), @getPanes()) 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));

View File

@ -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()