diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index 62c9dfaea..43bd19a9f 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -141,199 +141,18 @@ describe "RootView", -> it "surrenders focus to the body", -> expect(document.activeElement).toBe $('body')[0] - describe "panes", -> + fdescribe "panes", -> [pane1, newPaneContent] = [] beforeEach -> - rootView.attachToDom() - rootView.width(800) - rootView.height(600) pane1 = rootView.find('.pane').view() - pane1.attr('id', 'pane-1') - newPaneContent = $("
New pane content
") - spyOn(newPaneContent, 'focus') - - describe "vertical splits", -> - describe "when .splitRight(view) is called on a pane", -> - it "places a new pane to the right of the current pane in a .row div", -> - expect(rootView.panes.find('.row')).not.toExist() - - pane2 = pane1.splitRight(newPaneContent) - expect(newPaneContent.focus).toHaveBeenCalled() - - expect(rootView.panes.find('.row')).toExist() - expect(rootView.panes.find('.row .pane').length).toBe 2 - [leftPane, rightPane] = rootView.panes.find('.row .pane').map -> $(this) - expect(rightPane[0]).toBe pane2[0] - expect(leftPane.attr('id')).toBe 'pane-1' - expect(rightPane.html()).toBe "
New pane content
" - - expectedColumnWidth = Math.floor(rootView.panes.width() / 2) - expect(leftPane.outerWidth()).toBe expectedColumnWidth - expect(rightPane.position().left).toBe expectedColumnWidth - expect(rightPane.outerWidth()).toBe expectedColumnWidth - - pane2.remove() - - expect(rootView.panes.find('.row')).not.toExist() - expect(rootView.panes.find('.pane').length).toBe 1 - expect(pane1.outerWidth()).toBe rootView.panes.width() - - describe "when splitLeft(view) is called on a pane", -> - it "places a new pane to the left of the current pane in a .row div", -> - expect(rootView.find('.row')).not.toExist() - - pane2 = pane1.splitLeft(newPaneContent) - expect(newPaneContent.focus).toHaveBeenCalled() - - expect(rootView.find('.row')).toExist() - expect(rootView.find('.row .pane').length).toBe 2 - [leftPane, rightPane] = rootView.find('.row .pane').map -> $(this) - expect(leftPane[0]).toBe pane2[0] - expect(rightPane.attr('id')).toBe 'pane-1' - expect(leftPane.html()).toBe "
New pane content
" - - expectedColumnWidth = Math.floor(rootView.panes.width() / 2) - expect(leftPane.outerWidth()).toBe expectedColumnWidth - expect(rightPane.position().left).toBe expectedColumnWidth - expect(rightPane.outerWidth()).toBe expectedColumnWidth - - pane2.remove() - - expect(rootView.panes.find('.row')).not.toExist() - expect(rootView.panes.find('.pane').length).toBe 1 - expect(pane1.outerWidth()).toBe rootView.panes.width() - expect(pane1.position().left).toBe 0 - - describe "horizontal splits", -> - describe "when splitUp(view) is called on a pane", -> - it "places a new pane above the current pane in a .column div", -> - expect(rootView.find('.column')).not.toExist() - - pane2 = pane1.splitUp(newPaneContent) - expect(newPaneContent.focus).toHaveBeenCalled() - - expect(rootView.find('.column')).toExist() - expect(rootView.find('.column .pane').length).toBe 2 - [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this) - expect(topPane[0]).toBe pane2[0] - expect(bottomPane.attr('id')).toBe 'pane-1' - expect(topPane.html()).toBe "
New pane content
" - - expectedRowHeight = Math.floor(rootView.panes.height() / 2) - expect(topPane.outerHeight()).toBe expectedRowHeight - expect(bottomPane.position().top).toBe expectedRowHeight - expect(bottomPane.outerHeight()).toBe expectedRowHeight - - pane2.remove() - - expect(rootView.panes.find('.column')).not.toExist() - expect(rootView.panes.find('.pane').length).toBe 1 - expect(pane1.outerHeight()).toBe rootView.panes.height() - expect(pane1.position().top).toBe 0 - - describe "when splitDown(view) is called on a pane", -> - it "places a new pane below the current pane in a .column div", -> - expect(rootView.find('.column')).not.toExist() - - pane2 = pane1.splitDown(newPaneContent) - expect(newPaneContent.focus).toHaveBeenCalled() - - expect(rootView.find('.column')).toExist() - expect(rootView.find('.column .pane').length).toBe 2 - [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this) - expect(bottomPane[0]).toBe pane2[0] - expect(topPane.attr('id')).toBe 'pane-1' - expect(bottomPane.html()).toBe "
New pane content
" - - expectedRowHeight = Math.floor(rootView.panes.height() / 2) - expect(topPane.outerHeight()).toBe expectedRowHeight - expect(bottomPane.position().top).toBe expectedRowHeight - expect(bottomPane.outerHeight()).toBe expectedRowHeight - - pane2.remove() - - expect(rootView.panes.find('.column')).not.toExist() - expect(rootView.panes.find('.pane').length).toBe 1 - expect(pane1.outerHeight()).toBe rootView.panes.height() - - describe "layout of nested vertical and horizontal splits", -> - it "lays out rows and columns with a consistent width", -> - pane1.html("1") - - pane1 - .splitLeft("2") - .splitUp("3") - .splitLeft("4") - .splitDown("5") - - row1 = rootView.panes.children(':eq(0)') - expect(row1.children().length).toBe 2 - column1 = row1.children(':eq(0)').view() - pane1 = row1.children(':eq(1)').view() - expect(column1.outerWidth()).toBe Math.round(2/3 * rootView.panes.width()) - expect(column1.outerHeight()).toBe rootView.height() - expect(pane1.outerWidth()).toBe Math.round(1/3 * rootView.panes.width()) - expect(pane1.outerHeight()).toBe rootView.height() - expect(Math.round(pane1.position().left)).toBe column1.outerWidth() - - expect(column1.children().length).toBe 2 - row2 = column1.children(':eq(0)').view() - pane2 = column1.children(':eq(1)').view() - expect(row2.outerWidth()).toBe column1.outerWidth() - expect(row2.height()).toBe 2/3 * rootView.panes.height() - expect(pane2.outerWidth()).toBe column1.outerWidth() - expect(pane2.outerHeight()).toBe 1/3 * rootView.panes.height() - expect(pane2.position().top).toBe row2.height() - - expect(row2.children().length).toBe 2 - column3 = row2.children(':eq(0)').view() - pane3 = row2.children(':eq(1)').view() - expect(column3.outerWidth()).toBe Math.round(1/3 * rootView.panes.width()) - expect(column3.outerHeight()).toBe row2.outerHeight() - # the built in rounding seems to be rounding x.5 down, but we need to go up. this sucks. - expect(Math.round(pane3.trueWidth())).toBe Math.round(1/3 * rootView.panes.width()) - expect(pane3.height()).toBe row2.outerHeight() - expect(Math.round(pane3.position().left)).toBe column3.width() - - expect(column3.children().length).toBe 2 - pane4 = column3.children(':eq(0)').view() - pane5 = column3.children(':eq(1)').view() - expect(pane4.outerWidth()).toBe column3.width() - expect(pane4.outerHeight()).toBe 1/3 * rootView.panes.height() - expect(pane5.outerWidth()).toBe column3.width() - expect(pane5.position().top).toBe pane4.outerHeight() - expect(pane5.outerHeight()).toBe 1/3 * rootView.panes.height() - - pane5.remove() - - expect(column3.parent()).not.toExist() - expect(pane2.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) - expect(pane3.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) - expect(pane4.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) - - pane4.remove() - expect(row2.parent()).not.toExist() - expect(pane1.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) - expect(pane2.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) - expect(pane3.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) - - pane3.remove() - expect(column1.parent()).not.toExist() - expect(pane2.outerHeight()).toBe rootView.panes.height() - - pane2.remove() - expect(row1.parent()).not.toExist() - expect(rootView.panes.children().length).toBe 1 - expect(rootView.panes.children('.pane').length).toBe 1 - expect(pane1.outerWidth()).toBe rootView.panes.width() describe ".focusNextPane()", -> it "focuses the wrapped view of the pane after the currently focused pane", -> class DummyView extends View @content: (number) -> @div(number, tabindex: -1) - view1 = pane1.wrappedView + view1 = pane1.find('.editor').view() view2 = new DummyView(2) view3 = new DummyView(3) pane2 = pane1.splitDown(view2) @@ -352,6 +171,190 @@ describe "RootView", -> rootView.focusNextPane() expect(view1.focus).toHaveBeenCalled() + describe "pane layout", -> + beforeEach -> + rootView.attachToDom() + rootView.width(800) + rootView.height(600) + pane1.attr('id', 'pane-1') + newPaneContent = $("
New pane content
") + spyOn(newPaneContent, 'focus') + + describe "vertical splits", -> + describe "when .splitRight(view) is called on a pane", -> + it "places a new pane to the right of the current pane in a .row div", -> + expect(rootView.panes.find('.row')).not.toExist() + + pane2 = pane1.splitRight(newPaneContent) + expect(newPaneContent.focus).toHaveBeenCalled() + + expect(rootView.panes.find('.row')).toExist() + expect(rootView.panes.find('.row .pane').length).toBe 2 + [leftPane, rightPane] = rootView.panes.find('.row .pane').map -> $(this).view() + expect(rightPane[0]).toBe pane2[0] + expect(leftPane.attr('id')).toBe 'pane-1' + expect(rightPane.currentItem).toBe newPaneContent + + expectedColumnWidth = Math.floor(rootView.panes.width() / 2) + expect(leftPane.outerWidth()).toBe expectedColumnWidth + expect(rightPane.position().left).toBe expectedColumnWidth + expect(rightPane.outerWidth()).toBe expectedColumnWidth + + pane2.remove() + + expect(rootView.panes.find('.row')).not.toExist() + expect(rootView.panes.find('.pane').length).toBe 1 + expect(pane1.outerWidth()).toBe rootView.panes.width() + + describe "when splitLeft(view) is called on a pane", -> + it "places a new pane to the left of the current pane in a .row div", -> + expect(rootView.find('.row')).not.toExist() + + pane2 = pane1.splitLeft(newPaneContent) + expect(newPaneContent.focus).toHaveBeenCalled() + + expect(rootView.find('.row')).toExist() + expect(rootView.find('.row .pane').length).toBe 2 + [leftPane, rightPane] = rootView.find('.row .pane').map -> $(this).view() + expect(leftPane[0]).toBe pane2[0] + expect(rightPane.attr('id')).toBe 'pane-1' + expect(leftPane.currentItem).toBe + + expectedColumnWidth = Math.floor(rootView.panes.width() / 2) + expect(leftPane.outerWidth()).toBe expectedColumnWidth + expect(rightPane.position().left).toBe expectedColumnWidth + expect(rightPane.outerWidth()).toBe expectedColumnWidth + + pane2.remove() + + expect(rootView.panes.find('.row')).not.toExist() + expect(rootView.panes.find('.pane').length).toBe 1 + expect(pane1.outerWidth()).toBe rootView.panes.width() + expect(pane1.position().left).toBe 0 + + describe "horizontal splits", -> + describe "when splitUp(view) is called on a pane", -> + it "places a new pane above the current pane in a .column div", -> + expect(rootView.find('.column')).not.toExist() + + pane2 = pane1.splitUp(newPaneContent) + expect(newPaneContent.focus).toHaveBeenCalled() + + expect(rootView.find('.column')).toExist() + expect(rootView.find('.column .pane').length).toBe 2 + [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this).view() + expect(topPane[0]).toBe pane2[0] + expect(bottomPane.attr('id')).toBe 'pane-1' + expect(topPane.currentItem).toBe newPaneContent + + expectedRowHeight = Math.floor(rootView.panes.height() / 2) + expect(topPane.outerHeight()).toBe expectedRowHeight + expect(bottomPane.position().top).toBe expectedRowHeight + expect(bottomPane.outerHeight()).toBe expectedRowHeight + + pane2.remove() + + expect(rootView.panes.find('.column')).not.toExist() + expect(rootView.panes.find('.pane').length).toBe 1 + expect(pane1.outerHeight()).toBe rootView.panes.height() + expect(pane1.position().top).toBe 0 + + describe "when splitDown(view) is called on a pane", -> + it "places a new pane below the current pane in a .column div", -> + expect(rootView.find('.column')).not.toExist() + + pane2 = pane1.splitDown(newPaneContent) + expect(newPaneContent.focus).toHaveBeenCalled() + + expect(rootView.find('.column')).toExist() + expect(rootView.find('.column .pane').length).toBe 2 + [topPane, bottomPane] = rootView.find('.column .pane').map -> $(this).view() + expect(bottomPane[0]).toBe pane2[0] + expect(topPane.attr('id')).toBe 'pane-1' + expect(bottomPane.currentItem).toBe newPaneContent + + expectedRowHeight = Math.floor(rootView.panes.height() / 2) + expect(topPane.outerHeight()).toBe expectedRowHeight + expect(bottomPane.position().top).toBe expectedRowHeight + expect(bottomPane.outerHeight()).toBe expectedRowHeight + + pane2.remove() + + expect(rootView.panes.find('.column')).not.toExist() + expect(rootView.panes.find('.pane').length).toBe 1 + expect(pane1.outerHeight()).toBe rootView.panes.height() + + describe "layout of nested vertical and horizontal splits", -> + it "lays out rows and columns with a consistent width", -> + pane1.showItem($("1")) + + pane1 + .splitLeft($("2")) + .splitUp($("3")) + .splitLeft($("4")) + .splitDown($("5")) + + row1 = rootView.panes.children(':eq(0)') + expect(row1.children().length).toBe 2 + column1 = row1.children(':eq(0)').view() + pane1 = row1.children(':eq(1)').view() + expect(column1.outerWidth()).toBe Math.round(2/3 * rootView.panes.width()) + expect(column1.outerHeight()).toBe rootView.height() + expect(pane1.outerWidth()).toBe Math.round(1/3 * rootView.panes.width()) + expect(pane1.outerHeight()).toBe rootView.height() + expect(Math.round(pane1.position().left)).toBe column1.outerWidth() + + expect(column1.children().length).toBe 2 + row2 = column1.children(':eq(0)').view() + pane2 = column1.children(':eq(1)').view() + expect(row2.outerWidth()).toBe column1.outerWidth() + expect(row2.height()).toBe 2/3 * rootView.panes.height() + expect(pane2.outerWidth()).toBe column1.outerWidth() + expect(pane2.outerHeight()).toBe 1/3 * rootView.panes.height() + expect(pane2.position().top).toBe row2.height() + + expect(row2.children().length).toBe 2 + column3 = row2.children(':eq(0)').view() + pane3 = row2.children(':eq(1)').view() + expect(column3.outerWidth()).toBe Math.round(1/3 * rootView.panes.width()) + expect(column3.outerHeight()).toBe row2.outerHeight() + # the built in rounding seems to be rounding x.5 down, but we need to go up. this sucks. + expect(Math.round(pane3.trueWidth())).toBe Math.round(1/3 * rootView.panes.width()) + expect(pane3.height()).toBe row2.outerHeight() + expect(Math.round(pane3.position().left)).toBe column3.width() + + expect(column3.children().length).toBe 2 + pane4 = column3.children(':eq(0)').view() + pane5 = column3.children(':eq(1)').view() + expect(pane4.outerWidth()).toBe column3.width() + expect(pane4.outerHeight()).toBe 1/3 * rootView.panes.height() + expect(pane5.outerWidth()).toBe column3.width() + expect(pane5.position().top).toBe pane4.outerHeight() + expect(pane5.outerHeight()).toBe 1/3 * rootView.panes.height() + + pane5.remove() + + expect(column3.parent()).not.toExist() + expect(pane2.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) + expect(pane3.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) + expect(pane4.outerHeight()).toBe Math.floor(1/2 * rootView.panes.height()) + + pane4.remove() + expect(row2.parent()).not.toExist() + expect(pane1.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) + expect(pane2.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) + expect(pane3.outerWidth()).toBe Math.floor(1/2 * rootView.panes.width()) + + pane3.remove() + expect(column1.parent()).not.toExist() + expect(pane2.outerHeight()).toBe rootView.panes.height() + + pane2.remove() + expect(row1.parent()).not.toExist() + expect(rootView.panes.children().length).toBe 1 + expect(rootView.panes.children('.pane').length).toBe 1 + expect(pane1.outerWidth()).toBe rootView.panes.width() + describe "keymap wiring", -> commandHandler = null beforeEach -> diff --git a/src/app/pane.coffee b/src/app/pane.coffee index 2d121f7ff..dd9a4ac10 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -22,6 +22,7 @@ class Pane extends View @command 'pane:show-next-item', @showNextItem @command 'pane:show-previous-item', @showPreviousItem + @on 'focus', => @viewForCurrentItem().focus() getItems: -> new Array(@items...) @@ -86,6 +87,9 @@ class Pane extends View view = @viewsByClassName[viewClass.name] = new viewClass(item) view + viewForCurrentItem: -> + @viewForItem(@currentItem) + serialize: -> deserializer: "Pane" wrappedView: @wrappedView?.serialize() @@ -118,7 +122,7 @@ class Pane extends View pane = new Pane(view) this[side](pane) rootView?.adjustPaneDimensions() - view.focus?() + pane.focus() pane remove: (selector, keepData) -> diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index d01ac9c03..5449f4925 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -91,39 +91,16 @@ class RootView extends View @remove() open: (path, options = {}) -> - changeFocus = options.changeFocus ? true - allowActiveEditorChange = options.allowActiveEditorChange ? false - - unless editSession = @openInExistingEditor(path, allowActiveEditorChange, changeFocus) - editSession = project.buildEditSession(path) - editor = new Editor({editSession}) - pane = new Pane(editor) - @panes.append(pane) - if changeFocus - editor.focus() + if activePane = @getActivePane() + if existingItem = activePane.itemForPath(path) + activePane.showItem(existingItem) else - @makeEditorActive(editor, changeFocus) + activePane.showItem(project.buildEditSession(path)) + else + activePane = new Pane(project.buildEditSession(path)) + @panes.append(activePane) - editSession - - openInExistingEditor: (path, allowActiveEditorChange, changeFocus) -> - if activeEditor = @getActiveEditor() - activeEditor.focus() if changeFocus - - path = project.resolve(path) if path - - if editSession = activeEditor.activateEditSessionForPath(path) - return editSession - - if allowActiveEditorChange - for editor in @getEditors() - if editSession = editor.activateEditSessionForPath(path) - @makeEditorActive(editor, changeFocus) - return editSession - - editSession = project.buildEditSession(path) - activeEditor.edit(editSession) - editSession + activePane.focus() if options.changeFocus editorFocused: (editor) -> @makeEditorActive(editor) if @panes.containsElement(editor) @@ -177,6 +154,9 @@ class RootView extends View getOpenBufferPaths: -> _.uniq(_.flatten(@getEditors().map (editor) -> editor.getOpenBufferPaths())) + getActivePane: -> + @panes.find('.pane.active').view() ? @panes.find('.pane:first').view() + getActiveEditor: -> if (editor = @panes.find('.editor.active')).length editor.view() @@ -190,7 +170,7 @@ class RootView extends View panes = @panes.find('.pane') currentIndex = panes.toArray().indexOf(@getFocusedPane()[0]) nextIndex = (currentIndex + 1) % panes.length - panes.eq(nextIndex).view().wrappedView.focus() + panes.eq(nextIndex).view().focus() getFocusedPane: -> @panes.find('.pane:has(:focus)')