diff --git a/src/packages/tabs/lib/tab-bar-view.coffee b/src/packages/tabs/lib/tab-bar-view.coffee index d98d12bc1..773c9ddde 100644 --- a/src/packages/tabs/lib/tab-bar-view.coffee +++ b/src/packages/tabs/lib/tab-bar-view.coffee @@ -1,4 +1,5 @@ $ = require 'jquery' +_ = require 'underscore' SortableList = require 'sortable-list' TabView = require './tab-view' @@ -9,14 +10,16 @@ class TabBarView extends SortableList initialize: (@pane) -> super - @addTabForItem(item) for item in @pane.getItems() + @pane.on 'pane:item-added', (e, item, index) => @addTabForItem(item, index) + @pane.on 'pane:item-removed', (e, item) => @removeTabForItem(item) + @pane.on 'pane:active-item-changed', => @updateActiveTab() + + @updateActiveTab() -# @addTabForEditSession(editSession) for editSession in @editor.editSessions -# # @setActiveTab(@editor.getActiveEditSessionIndex()) -# @editor.on 'editor:active-edit-session-changed', (e, editSession, index) => @setActiveTab(index) + # @editor.on 'editor:edit-session-added', (e, editSession) => @addTabForEditSession(editSession) # @editor.on 'editor:edit-session-removed', (e, editSession, index) => @removeTabAtIndex(index) # @editor.on 'editor:edit-session-order-changed', (e, editSession, fromIndex, toIndex) => @@ -29,26 +32,45 @@ class TabBarView extends SortableList # fromTab.insertBefore(toTab) @on 'click', '.tab', (e) => - @editor.setActiveEditSessionIndex($(e.target).closest('.tab').index()) - @editor.focus() + tab = $(e.target).closest('.tab').view() + @pane.showItem(tab.item) + @pane.focus() @on 'click', '.tab .close-icon', (e) => - index = $(e.target).closest('.tab').index() - @editor.destroyEditSessionIndex(index) + tab = $(e.target).closest('.tab').view() + @pane.removeItem(tab.item) false @pane.prepend(this) - addTabForItem: (item) -> + addTabForItem: (item, index) -> tabView = new TabView(item, @pane) - @append(tabView) - @setActiveTabView(tabView) if item is @pane.currentItem + followingTab = @tabAtIndex(index) if index? + if followingTab + tabView.insertBefore(followingTab) + else + @append(tabView) - setActiveTabView: (tabView) -> + removeTabForItem: (item) -> + @tabForItem(item).remove() + + getTabs: -> + @children('.tab').toArray().map (elt) -> $(elt).view() + + tabAtIndex: (index) -> + @children(".tab:eq(#{index})").view() + + tabForItem: (item) -> + _.detect @getTabs(), (tab) -> tab.item is item + + setActiveTab: (tabView) -> unless tabView.hasClass('active') @find(".tab.active").removeClass('active') tabView.addClass('active') + updateActiveTab: -> + @setActiveTab(@tabForItem(@pane.activeItem)) + removeTabAtIndex: (index) -> @find(".tab:eq(#{index})").remove() diff --git a/src/packages/tabs/lib/tab-view.coffee b/src/packages/tabs/lib/tab-view.coffee index 166233094..351fa15ed 100644 --- a/src/packages/tabs/lib/tab-view.coffee +++ b/src/packages/tabs/lib/tab-view.coffee @@ -1,100 +1,44 @@ -$ = require 'jquery' -SortableList = require 'sortable-list' -Tab = require './tab' +{View} = require 'space-pen' +fs = require 'fs' module.exports = -class TabView extends SortableList - @activate: -> - rootView.eachEditor (editor) => - @prependToEditorPane(editor) if editor.attached - - @prependToEditorPane: (editor) -> - if pane = editor.pane() - pane.prepend(new TabView(editor)) - +class TabView extends View @content: -> - @ul class: "tabs #{@viewClass()}" + @li class: 'tab sortable', => + @span class: 'title', outlet: 'title' + @span class: 'close-icon' - initialize: (@editor) -> - super + initialize: (@item, @pane) -> + @title.text(@item.getTitle()) - @addTabForEditSession(editSession) for editSession in @editor.editSessions - @setActiveTab(@editor.getActiveEditSessionIndex()) - @editor.on 'editor:active-edit-session-changed', (e, editSession, index) => @setActiveTab(index) - @editor.on 'editor:edit-session-added', (e, editSession) => @addTabForEditSession(editSession) - @editor.on 'editor:edit-session-removed', (e, editSession, index) => @removeTabAtIndex(index) - @editor.on 'editor:edit-session-order-changed', (e, editSession, fromIndex, toIndex) => - fromTab = @find(".tab:eq(#{fromIndex})") - toTab = @find(".tab:eq(#{toIndex})") - fromTab.detach() - if fromIndex < toIndex - fromTab.insertAfter(toTab) - else - fromTab.insertBefore(toTab) +# @buffer = @editSession.buffer +# @subscribe @buffer, 'path-changed', => @updateFileName() +# @subscribe @buffer, 'contents-modified', => @updateModifiedStatus() +# @subscribe @buffer, 'saved', => @updateModifiedStatus() +# @subscribe @buffer, 'git-status-changed', => @updateModifiedStatus() +# @subscribe @editor, 'editor:edit-session-added', => @updateFileName() +# @subscribe @editor, 'editor:edit-session-removed', => @updateFileName() +# @updateFileName() +# @updateModifiedStatus() - @on 'click', '.tab', (e) => - @editor.setActiveEditSessionIndex($(e.target).closest('.tab').index()) - @editor.focus() - - @on 'click', '.tab .close-icon', (e) => - index = $(e.target).closest('.tab').index() - @editor.destroyEditSessionIndex(index) - false - - addTabForEditSession: (editSession) -> - @append(new Tab(editSession, @editor)) - - setActiveTab: (index) -> - @find(".tab.active").removeClass('active') - @find(".tab:eq(#{index})").addClass('active') - - removeTabAtIndex: (index) -> - @find(".tab:eq(#{index})").remove() - - containsEditSession: (editor, editSession) -> - for session in editor.editSessions - return true if editSession.getPath() is session.getPath() - - shouldAllowDrag: (event) -> - panes = rootView.find('.pane') - !(panes.length == 1 && panes.find('.sortable').length == 1) - - onDragStart: (event) => - super - - pane = $(event.target).closest('.pane') - paneIndex = rootView.indexOfPane(pane) - event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex - - onDrop: (event) => - super - - droppedNearTab = @getSortableElement(event) - transfer = event.originalEvent.dataTransfer - previousDraggedTabIndex = transfer.getData 'sortable-index' - - fromPaneIndex = ~~transfer.getData 'from-pane-index' - toPaneIndex = rootView.indexOfPane($(event.target).closest('.pane')) - fromPane = $(rootView.find('.pane')[fromPaneIndex]) - fromEditor = fromPane.find('.editor').view() - draggedTab = fromPane.find(".#{TabView.viewClass()} .sortable:eq(#{previousDraggedTabIndex})") - - if draggedTab.is(droppedNearTab) - fromEditor.focus() - return - - if fromPaneIndex == toPaneIndex - droppedNearTab = @getSortableElement(event) - fromIndex = draggedTab.index() - toIndex = droppedNearTab.index() - toIndex++ if fromIndex > toIndex - fromEditor.moveEditSessionToIndex(fromIndex, toIndex) - fromEditor.focus() + updateModifiedStatus: -> + if @buffer.isModified() + @toggleClass('file-modified') unless @isModified + @isModified = true else - toEditor = rootView.find(".pane:eq(#{toPaneIndex}) > .editor").view() - if @containsEditSession(toEditor, fromEditor.editSessions[draggedTab.index()]) - fromEditor.focus() - else - fromEditor.moveEditSessionToEditor(draggedTab.index(), toEditor, droppedNearTab.index() + 1) - toEditor.focus() + @removeClass('file-modified') if @isModified + @isModified = false + + updateFileName: -> + fileNameText = @editSession.buffer.getBaseName() + if fileNameText? + duplicates = @editor.getEditSessions().filter (session) -> fileNameText is session.buffer.getBaseName() + if duplicates.length > 1 + directory = fs.base(fs.directory(@editSession.getPath())) + fileNameText = "#{fileNameText} - #{directory}" if directory + else + fileNameText = 'untitled' + + @fileName.text(fileNameText) + @fileName.attr('title', @editSession.getPath()) diff --git a/src/packages/tabs/lib/tab.coffee b/src/packages/tabs/lib/tab.coffee deleted file mode 100644 index 9a7e8e3ab..000000000 --- a/src/packages/tabs/lib/tab.coffee +++ /dev/null @@ -1,40 +0,0 @@ -{View} = require 'space-pen' -fs = require 'fs' - -module.exports = -class Tab extends View - @content: (editSession) -> - @li class: 'tab sortable', => - @span class: 'file-name', outlet: 'fileName' - @span class: 'close-icon' - - initialize: (@editSession, @editor) -> - @buffer = @editSession.buffer - @subscribe @buffer, 'path-changed', => @updateFileName() - @subscribe @buffer, 'contents-modified', => @updateModifiedStatus() - @subscribe @buffer, 'saved', => @updateModifiedStatus() - @subscribe @editor, 'editor:edit-session-added', => @updateFileName() - @subscribe @editor, 'editor:edit-session-removed', => @updateFileName() - @updateFileName() - @updateModifiedStatus() - - updateModifiedStatus: -> - if @buffer.isModified() - @toggleClass('file-modified') unless @isModified - @isModified = true - else - @removeClass('file-modified') if @isModified - @isModified = false - - updateFileName: -> - fileNameText = @editSession.buffer.getBaseName() - if fileNameText? - duplicates = @editor.getEditSessions().filter (session) -> fileNameText is session.buffer.getBaseName() - if duplicates.length > 1 - directory = fs.base(fs.directory(@editSession.getPath())) - fileNameText = "#{fileNameText} - #{directory}" if directory - else - fileNameText = 'untitled' - - @fileName.text(fileNameText) - @fileName.attr('title', @editSession.getPath()) diff --git a/src/packages/tabs/lib/tabs.coffee b/src/packages/tabs/lib/tabs.coffee new file mode 100644 index 000000000..ba2da6b3a --- /dev/null +++ b/src/packages/tabs/lib/tabs.coffee @@ -0,0 +1,5 @@ +TabBarView = require './tab-bar-view' + +module.exports = + activate: -> + rootView.eachPane (pane) => new TabBarView(pane) diff --git a/src/packages/tabs/package.cson b/src/packages/tabs/package.cson index 0e40dfd74..1c24d65ba 100644 --- a/src/packages/tabs/package.cson +++ b/src/packages/tabs/package.cson @@ -1 +1 @@ -'main': 'lib/tab-view' +'main': 'lib/tabs' diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index 00efe0324..07ad016a9 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -1,93 +1,99 @@ $ = require 'jquery' _ = require 'underscore' RootView = require 'root-view' +Pane = require 'pane' +PaneContainer = require 'pane-container' +TabBarView = require 'tabs/lib/tab-bar-view' fs = require 'fs' +{View} = require 'space-pen' -describe "TabView", -> - [editor, buffer, tabs] = [] - +describe "Tabs package main", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - rootView.open('sample.txt') - rootView.simulateDomAttachment() window.loadPackage("tabs") - editor = rootView.getActiveEditor() - tabs = rootView.find('.tabs').view() - describe "@activate", -> - it "appends a status bear to all existing and new editors", -> + describe ".activate()", -> + it "appends a tab bar all existing and new panes", -> expect(rootView.panes.find('.pane').length).toBe 1 expect(rootView.panes.find('.pane > .tabs').length).toBe 1 - editor.splitRight() + rootView.getActivePane().splitRight() expect(rootView.find('.pane').length).toBe 2 expect(rootView.panes.find('.pane > .tabs').length).toBe 2 - describe ".initialize()", -> - it "creates a tab for each edit session on the editor to which the tab-strip belongs", -> - expect(editor.editSessions.length).toBe 2 - expect(tabs.find('.tab').length).toBe 2 +fdescribe "TabBarView", -> + [item1, item2, editSession1, editSession2, pane, tabBar] = [] - expect(tabs.find('.tab:eq(0) .file-name').text()).toBe editor.editSessions[0].buffer.getBaseName() - expect(tabs.find('.tab:eq(1) .file-name').text()).toBe editor.editSessions[1].buffer.getBaseName() + class TestView extends View + @content: (title) -> @div title + initialize: (@title) -> + getTitle: -> @title - it "highlights the tab for the current active edit session", -> - expect(editor.getActiveEditSessionIndex()).toBe 1 - expect(tabs.find('.tab:eq(1)')).toHaveClass 'active' + beforeEach -> + item1 = new TestView('Item 1') + item2 = new TestView('Item 2') + editSession1 = project.buildEditSession('sample.js') + paneContainer = new PaneContainer + pane = new Pane(item1, editSession1, item2) + pane.showItem(item2) + paneContainer.append(pane) + tabBar = new TabBarView(pane) - it "sets the title on each tab to be the full path of the edit session", -> - expect(tabs.find('.tab:eq(0) .file-name').attr('title')).toBe editor.editSessions[0].getPath() - expect(tabs.find('.tab:eq(1) .file-name').attr('title')).toBe editor.editSessions[1].getPath() + describe ".initialize(pane)", -> + it "creates a tab for each item on the tab bar's parent pane", -> + expect(pane.getItems().length).toBe 3 + expect(tabBar.find('.tab').length).toBe 3 - describe "when the active edit session changes", -> - it "highlights the tab for the newly-active edit session", -> - editor.setActiveEditSessionIndex(0) - expect(tabs.find('.active').length).toBe 1 - expect(tabs.find('.tab:eq(0)')).toHaveClass 'active' + expect(tabBar.find('.tab:eq(0) .title').text()).toBe item1.getTitle() + expect(tabBar.find('.tab:eq(1) .title').text()).toBe editSession1.getTitle() + expect(tabBar.find('.tab:eq(2) .title').text()).toBe item2.getTitle() - editor.setActiveEditSessionIndex(1) - expect(tabs.find('.active').length).toBe 1 - expect(tabs.find('.tab:eq(1)')).toHaveClass 'active' + it "highlights the tab for the active pane item", -> + expect(tabBar.find('.tab:eq(2)')).toHaveClass 'active' - describe "when a new edit session is created", -> - it "adds a tab for the new edit session", -> - rootView.open('two-hundred.txt') - expect(tabs.find('.tab').length).toBe 3 - expect(tabs.find('.tab:eq(2) .file-name').text()).toBe 'two-hundred.txt' + describe "when the active pane item changes", -> + it "highlights the tab for the new active pane item", -> + pane.showItem(item1) + expect(tabBar.find('.active').length).toBe 1 + expect(tabBar.find('.tab:eq(0)')).toHaveClass 'active' - describe "when the edit session's buffer has an undefined path", -> - it "makes the tab text 'untitled'", -> - rootView.open() - expect(tabs.find('.tab').length).toBe 3 - expect(tabs.find('.tab:eq(2) .file-name').text()).toBe 'untitled' + pane.showItem(item2) + expect(tabBar.find('.active').length).toBe 1 + expect(tabBar.find('.tab:eq(2)')).toHaveClass 'active' - it "removes the tab's title", -> - rootView.open() - expect(tabs.find('.tab').length).toBe 3 - expect(tabs.find('.tab:eq(2) .file-name').attr('title')).toBeUndefined() + describe "when a new item is added to the pane", -> + ffit "adds a tab for the new item at the same index as the item in the pane", -> + pane.showItem(item1) + item3 = new TestView('Item 3') + pane.showItem(item3) + expect(tabBar.find('.tab').length).toBe 4 + expect(tabBar.tabAtIndex(1).find('.title')).toHaveText 'Item 3' - describe "when an edit session is removed", -> - it "removes the tab for the removed edit session", -> - editor.setActiveEditSessionIndex(0) - editor.destroyActiveEditSession() - expect(tabs.find('.tab').length).toBe 1 - expect(tabs.find('.tab:eq(0) .file-name').text()).toBe 'sample.txt' + describe "when an item is removed from the pane", -> + it "removes the item's tab from the tab bar", -> + pane.removeItem(item2) + expect(tabBar.getTabs().length).toBe 2 + expect(tabBar.find('.tab:contains(Item 2)')).not.toExist() describe "when a tab is clicked", -> - it "activates the associated edit session", -> - expect(editor.getActiveEditSessionIndex()).toBe 1 - tabs.find('.tab:eq(0)').click() - expect(editor.getActiveEditSessionIndex()).toBe 0 - tabs.find('.tab:eq(1)').click() - expect(editor.getActiveEditSessionIndex()).toBe 1 + it "shows the associated item on the pane and focuses the pane", -> + spyOn(pane, 'focus') - it "focuses the associated editor", -> - rootView.attachToDom() - expect(editor).toMatchSelector ":has(:focus)" - editor.splitRight() - expect(editor).not.toMatchSelector ":has(:focus)" - tabs.find('.tab:eq(0)').click() - expect(editor).toMatchSelector ":has(:focus)" + tabBar.tabAtIndex(0).click() + expect(pane.activeItem).toBe pane.getItems()[0] + + tabBar.tabAtIndex(2).click() + expect(pane.activeItem).toBe pane.getItems()[2] + + expect(pane.focus.callCount).toBe 2 + + describe "when a tab's close icon is clicked", -> + it "removes the tab's item from the pane", -> + tabBar.tabForItem(item1).find('.close-icon').click() + expect(pane.getItems().length).toBe 2 + expect(pane.getItems().indexOf(item1)).toBe -1 + expect(tabBar.getTabs().length).toBe 2 + expect(tabBar.find('.tab:contains(Item 1)')).not.toExist() describe "when a file name associated with a tab changes", -> [buffer, oldPath, newPath] = [] @@ -110,21 +116,6 @@ describe "TabView", -> waitsFor "file to be renamed", -> tabFileName.text() == "renamed-file.txt" - describe "when the close icon is clicked", -> - it "closes the selected non-active edit session", -> - activeSession = editor.activeEditSession - expect(editor.getActiveEditSessionIndex()).toBe 1 - tabs.find('.tab .close-icon:eq(0)').click() - expect(editor.getActiveEditSessionIndex()).toBe 0 - expect(editor.activeEditSession).toBe activeSession - - it "closes the selected active edit session", -> - firstSession = editor.getEditSessions()[0] - expect(editor.getActiveEditSessionIndex()).toBe 1 - tabs.find('.tab .close-icon:eq(1)').click() - expect(editor.getActiveEditSessionIndex()).toBe 0 - expect(editor.activeEditSession).toBe firstSession - describe "when two tabs have the same file name", -> [tempPath] = []