diff --git a/lib/status-bar.js b/lib/status-bar.js index 8f6fbb82..8e4526f9 100644 --- a/lib/status-bar.js +++ b/lib/status-bar.js @@ -91,7 +91,7 @@ class StatusBar extends View { } } else { this.setActiveTerminalView(nextTerminal) - if (prevTerminal && prevTerminal.panel.isVisible()) { + if ((prevTerminal && prevTerminal.panel.isVisible()) || atom.workspace.getBottomDock().isVisible()) { nextTerminal.toggle() } } @@ -397,8 +397,7 @@ class StatusBar extends View { for (let i = this.terminalViews.length - 1; i >= 0; i--) { const view = this.terminalViews[i] if (view) { - view.ptyProcess.terminate() - view.terminal.destroy() + view.destroy() } } this.detach() @@ -507,6 +506,7 @@ class StatusBar extends View { const fromIndex = parseInt(dataTransfer.getData('from-index')) const view = this.terminalViews[fromIndex] + if (!view) return view.css('height', '') view.terminal.element.style.height = atom.config.get('terminus.style.defaultPanelHeight') // const tabBar = $(event.target).closest('.tab-bar'); diff --git a/lib/status-icon.js b/lib/status-icon.js index 1b918705..06703ba4 100644 --- a/lib/status-icon.js +++ b/lib/status-icon.js @@ -114,9 +114,9 @@ class StatusIcon extends HTMLLIElement { updateName (name) { if (name !== this.getName()) { - if (name) { name = ' ' + name } - this.name.innerHTML = name - this.terminalView.emit('did-change-title') + const escapedName = name ? ' ' + name : name + this.name.innerHTML = escapedName + this.terminalView.didRename(name) } } } diff --git a/lib/terminus.js b/lib/terminus.js index b6a1d15a..3b6a2d96 100644 --- a/lib/terminus.js +++ b/lib/terminus.js @@ -1,7 +1,14 @@ module.exports = { statusBar: null, - activate () {}, + activate () { + atom.config.onDidChange('terminus.toggles.useDock', (event) => { + if (this.statusBarProvider) { + this.deactivate() + this.consumeStatusBar(this.statusBarProvider) + } + }) + }, deactivate () { if (this.statusBarTile) { @@ -11,6 +18,7 @@ module.exports = { }, consumeStatusBar (statusBarProvider) { + this.statusBarProvider = statusBarProvider this.statusBarTile = new (require('./status-bar'))(statusBarProvider) }, @@ -38,6 +46,12 @@ module.exports = { type: 'object', order: 1, properties: { + useDock: { + title: 'Use Bottom Dock', + description: 'Should the terminals be rendered in the bottom dock?', + type: 'boolean', + default: false + }, autoClose: { title: 'Close Terminal on Exit', description: 'Should the terminal close if the shell exits?', diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 00000000..5d35d366 --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,16 @@ +const isString = x => typeof x === 'string' + +// visibility for items in bottom dock +module.exports.isVisible = function (itemOrUri) { + const uri = isString(itemOrUri) ? itemOrUri : itemOrUri.getURI() + + const dock = atom.workspace.getBottomDock() + if (!dock.isVisible()) return false + + const activeItem = dock.getActivePaneItem() + if (!activeItem) return false + + return activeItem.getURI && activeItem.getURI() === uri +} + +module.exports.isUseDockEnabled = () => atom.config.get('terminus.toggles.useDock') diff --git a/lib/view.js b/lib/view.js index e5b7d69f..cfb1c8c1 100644 --- a/lib/view.js +++ b/lib/view.js @@ -3,6 +3,7 @@ const { $, View } = require('atom-space-pen-views') const Pty = require.resolve('./process') const Terminal = require('term.js') +const { isUseDockEnabled, isVisible } = require('./utils') let InputDialog = null const path = require('path') @@ -11,6 +12,8 @@ const os = require('os') let lastOpenedView = null let lastActiveElement = null +let nextId = 0 + class TerminusView extends View { static content () { this.div({ class: 'terminus terminal-view', outlet: 'terminusView' }, () => { @@ -20,11 +23,13 @@ class TerminusView extends View { this.div({ class: 'btn-group' }, () => { this.button({ outlet: 'inputBtn', class: 'btn icon icon-keyboard', click: 'inputDialog' }) }) - this.div({ class: 'btn-group right' }, () => { - this.button({ outlet: 'hideBtn', class: 'btn icon icon-chevron-down', click: 'hide' }) - this.button({ outlet: 'maximizeBtn', class: 'btn icon icon-screen-full', click: 'maximize' }) - this.button({ outlet: 'closeBtn', class: 'btn icon icon-x', click: 'destroy' }) - }) + if (!isUseDockEnabled()) { + this.div({ class: 'btn-group right' }, () => { + this.button({ outlet: 'hideBtn', class: 'btn icon icon-chevron-down', click: 'hide' }) + this.button({ outlet: 'maximizeBtn', class: 'btn icon icon-screen-full', click: 'maximize' }) + this.button({ outlet: 'closeBtn', class: 'btn icon icon-x', click: 'destroy' }) + }) + } }) }) this.div({ class: 'xterm', outlet: 'xterm' }) @@ -35,7 +40,17 @@ class TerminusView extends View { return Terminal.Terminal.focus } + getURI () { return `atom://terminus/${this.viewId}` } + + getDefaultLocation () { return 'bottom' } + + getAllowedLocations () { return ['bottom'] } + + isPermanentDockItem () { return false } + initialize (id, pwd, statusIcon, statusBar, shell, args = [], env = {}, autoRun = []) { + this.shouldUseDock = isUseDockEnabled() + if (this.shouldUseDock) { this.viewId = nextId++ } this.id = id this.pwd = pwd this.statusIcon = statusIcon @@ -56,10 +71,12 @@ class TerminusView extends View { this.updateToolbarVisibility = this.updateToolbarVisibility.bind(this) this.recieveItemOrFile = this.recieveItemOrFile.bind(this) - this.subscriptions.add(atom.tooltips.add(this.closeBtn, { title: 'Close' })) - this.subscriptions.add(atom.tooltips.add(this.hideBtn, { title: 'Hide' })) - this.subscriptions.add(this.maximizeBtn.tooltip = atom.tooltips.add(this.maximizeBtn, { title: 'Fullscreen' })) - this.inputBtn.tooltip = atom.tooltips.add(this.inputBtn, { title: 'Insert Text' }) + if (!this.shouldUseDock) { + this.subscriptions.add(atom.tooltips.add(this.closeBtn, { title: 'Close' })) + this.subscriptions.add(atom.tooltips.add(this.hideBtn, { title: 'Hide' })) + this.subscriptions.add(this.maximizeBtn.tooltip = atom.tooltips.add(this.maximizeBtn, { title: 'Fullscreen' })) + this.inputBtn.tooltip = atom.tooltips.add(this.inputBtn, { title: 'Insert Text' }) + } this.prevHeight = atom.config.get('terminus.style.defaultPanelHeight') if (this.prevHeight.indexOf('%') > 0) { @@ -67,13 +84,15 @@ class TerminusView extends View { const bottomHeight = $('atom-panel.bottom').children('.terminal-view').height() || 0 this.prevHeight = percent * ($('.item-views').height() + bottomHeight) } - this.xterm.height(0) + if (!this.shouldUseDock) { this.xterm.height(0) } this.setAnimationSpeed() this.subscriptions.add(atom.config.onDidChange('terminus.style.animationSpeed', this.setAnimationSpeed)) - this.updateToolbarVisibility() - this.subscriptions.add(atom.config.onDidChange('terminus.toggles.showToolbar', this.updateToolbarVisibility)) + if (!this.shouldUseDock) { + this.updateToolbarVisibility() + this.subscriptions.add(atom.config.onDidChange('terminus.toggles.showToolbar', this.updateToolbarVisibility)) + } const override = (event) => { if (event.originalEvent.dataTransfer.getData('terminus') === 'true') { return } @@ -112,8 +131,12 @@ class TerminusView extends View { } attach () { - if (this.panel) { return } - this.panel = atom.workspace.addBottomPanel({ item: this, visible: false }) + if (this.shouldUseDock) { + atom.workspace.open(this) + } else { + if (this.panel) { return } + this.panel = atom.workspace.addBottomPanel({ item: this, visible: false }) + } } setAnimationSpeed () { @@ -199,10 +222,11 @@ class TerminusView extends View { this.ptyProcess.on('terminus:title', title => { this.process = title + this.emit('did-change-title') }) - this.terminal.on('title', title => { - this.title = title - }) + // this.terminal.on('title', title => { + // this.title = title + // }) this.terminal.once('open', () => { this.applyStyle() @@ -215,6 +239,10 @@ class TerminusView extends View { }) } + onDidDestroy (callback) { + return this.emitter.on('did-destroy', callback) + } + destroy () { this.subscriptions.dispose() this.statusIcon.destroy() @@ -222,11 +250,13 @@ class TerminusView extends View { this.detachResizeEvents() this.detachWindowEvents() - if (this.panel.isVisible()) { - this.hide() - this.onTransitionEnd(() => this.panel.destroy()) - } else { - this.panel.destroy() + if (!this.shouldUseDock) { + if (this.panel.isVisible()) { + this.hide() + this.onTransitionEnd(() => this.panel.destroy()) + } else { + this.panel.destroy() + } } if (this.statusIcon && this.statusIcon.parentNode) { @@ -239,6 +269,9 @@ class TerminusView extends View { if (this.terminal) { this.terminal.destroy() } + + this.emit('did-destroy') + this.emitter.dispose() } maximize () { @@ -269,6 +302,8 @@ class TerminusView extends View { lastActiveElement = $(document.activeElement) } + if (this.shouldUseDock) { return this.openInDock() } + if (lastOpenedView && (lastOpenedView !== this)) { if (lastOpenedView.maximized) { this.subscriptions.remove(this.maximizeBtn.tooltip) @@ -306,7 +341,21 @@ class TerminusView extends View { this.xterm.height(this.maximized ? this.maxHeight : this.prevHeight) } + openInDock () { + this.statusBar.setActiveTerminalView(this) + this.statusIcon.activate() + atom.workspace.open(this, { activatePane: true }).then(() => { + if (!this.opened) { + this.opened = true + this.displayTerminal() + this.xterm.height('100%') + this.emit('terminus:terminal-open') + } else this.focus() + }) + } + hide () { + if (this.shouldUseDock) return this.hideInDock() if (this.terminal) { this.terminal.blur() } @@ -328,7 +377,15 @@ class TerminusView extends View { this.xterm.height(0) } + hideInDock () { + this.terminal && this.terminal.blur() + this.statusIcon.deactivate() + atom.workspace.hide(this) + } + toggle () { + if (this.shouldUseDock) return this.toggleInDock() + if (this.animating) { return } if (this.panel.isVisible()) { @@ -338,6 +395,10 @@ class TerminusView extends View { } } + toggleInDock () { + if (isVisible(this)) { this.hide() } else { this.open() } + } + input (data) { if (!this.ptyProcess.childProcess) { return } @@ -614,6 +675,8 @@ class TerminusView extends View { } resizeTerminalToView () { + if (this.shouldUseDock) return this.resizeInDock() + if (!this.panel.isVisible() && !this.tabView) { return } const { cols, rows } = this.getDimensions() @@ -625,6 +688,19 @@ class TerminusView extends View { this.terminal.resize(cols, rows) } + resizeInDock () { + if (!isVisible(this)) return + + const { cols, rows } = this.getDimensions() + if (cols > 0 && rows > 0) { + if (this.terminal) { + if (this.terminal.rows === rows && this.terminal.cols === cols) return + this.resize(cols, rows) + this.terminal.resize(cols, rows) + } + } + } + getDimensions () { const fakeRow = $('