From 7e2b4f7da5de53a9ade05cda202eaee12129906a Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 15 Oct 2017 17:57:09 +0100 Subject: [PATCH] Changed console to status bar --- CliClient/app/app-gui.js | 131 +++++++++++++++------------ CliClient/app/app.js | 14 +-- CliClient/app/command-rm.js | 2 +- CliClient/app/gui/ConsoleWidget.js | 20 ++++ CliClient/app/gui/StatusBarWidget.js | 112 +++++++++++++++++++++++ CliClient/locales/en_GB.po | 10 +- CliClient/locales/fr_FR.po | 18 ++-- CliClient/locales/joplin.pot | 10 +- 8 files changed, 232 insertions(+), 85 deletions(-) create mode 100644 CliClient/app/gui/ConsoleWidget.js create mode 100644 CliClient/app/gui/StatusBarWidget.js diff --git a/CliClient/app/app-gui.js b/CliClient/app/app-gui.js index 3e7d77820..20829ce66 100644 --- a/CliClient/app/app-gui.js +++ b/CliClient/app/app-gui.js @@ -13,7 +13,6 @@ const Renderer = require('tkwidgets/framework/Renderer.js'); const BaseWidget = require('tkwidgets/BaseWidget.js'); const ListWidget = require('tkwidgets/ListWidget.js'); const TextWidget = require('tkwidgets/TextWidget.js'); -const ConsoleWidget = require('tkwidgets/ConsoleWidget.js'); const HLayoutWidget = require('tkwidgets/HLayoutWidget.js'); const VLayoutWidget = require('tkwidgets/VLayoutWidget.js'); const ReduxRootWidget = require('tkwidgets/ReduxRootWidget.js'); @@ -23,6 +22,8 @@ const WindowWidget = require('tkwidgets/WindowWidget.js'); const NoteWidget = require('./gui/NoteWidget.js'); const FolderListWidget = require('./gui/FolderListWidget.js'); const NoteListWidget = require('./gui/NoteListWidget.js'); +const StatusBarWidget = require('./gui/StatusBarWidget.js'); +const ConsoleWidget = require('./gui/ConsoleWidget.js'); class AppGui { @@ -41,7 +42,7 @@ class AppGui { this.renderer_ = new Renderer(this.term(), this.rootWidget_); this.renderer_.on('renderDone', async (event) => { - if (this.widget('console').hasFocus) this.widget('console').resetCursor(); + //if (this.widget('console').hasFocus) this.widget('console').resetCursor(); }); this.app_.on('modelAction', async (event) => { @@ -58,9 +59,11 @@ class AppGui { this.lastShortcutKeyTime_ = 0; cliUtils.setStdout((...object) => { - for (let i = 0; i < object.length; i++) { - this.widget('console').bufferPush(object[i]); - } + return this.stdout(...object); + + // for (let i = 0; i < object.length; i++) { + // this.widget('console').bufferPush(object[i]); + // } }); } @@ -127,24 +130,21 @@ class AppGui { const consoleWidget = new ConsoleWidget(); consoleWidget.hStretch = true; - consoleWidget.name = 'console'; - consoleWidget.prompt = chalk.cyan('Joplin >') + ' '; - consoleWidget.on('accept', async (event) => { - consoleWidget.promptVisible = false; - await this.processCommand(event.input, 'console'); - consoleWidget.promptVisible = true; - }); + + const statusBar = new StatusBarWidget(); + statusBar.hStretch = true; const hLayout = new HLayoutWidget(); hLayout.name = 'hLayout'; hLayout.addChild(folderList, { type: 'stretch', factor: 1 }); hLayout.addChild(noteList, { type: 'stretch', factor: 1 }); - hLayout.addChild(noteText, { type: 'stretch', factor: 1 }); + hLayout.addChild(noteText, { type: 'stretch', factor: 2 }); const vLayout = new VLayoutWidget(); vLayout.name = 'vLayout'; vLayout.addChild(hLayout, { type: 'stretch', factor: 1 }); - vLayout.addChild(consoleWidget, { type: 'fixed', factor: 6 }); + vLayout.addChild(consoleWidget, { type: 'fixed', factor: 3 }); + vLayout.addChild(statusBar, { type: 'fixed', factor: 1 }); const win1 = new WindowWidget(); win1.addChild(vLayout); @@ -156,8 +156,6 @@ class AppGui { setupShortcuts() { const shortcuts = {}; - const consoleWidget = this.widget('console'); - shortcuts['DELETE'] = { description: _('Delete a note'), action: 'rm $n', @@ -169,9 +167,9 @@ class AppGui { action: 'todo toggle $n', } - shortcuts['c'] = { + shortcuts[':'] = { description: _('Enter the console'), - action: () => { consoleWidget.focus(); } + isDocOnly: true, }; shortcuts['ESC'] = { @@ -191,24 +189,19 @@ class AppGui { }, } - shortcuts['nt'] = { - description: _('Create a new todo'), - action: () => { consoleWidget.focus('mktodo '); }, - } - shortcuts['nn'] = { description: _('Create a new note'), - action: () => { consoleWidget.focus('mknote '); }, + action: { type: 'prompt', initialText: 'mknote ' }, } shortcuts['nt'] = { description: _('Create a new todo'), - action: () => { consoleWidget.focus('mktodo '); }, + action: { type: 'prompt', initialText: 'mktodo ' }, } shortcuts['nb'] = { description: _('Create a new notebook'), - action: () => { consoleWidget.focus('mkbook '); }, + action: { type: 'prompt', initialText: 'mkbook ' }, } shortcuts['CTRL_JCTRL_Z'] = { @@ -225,30 +218,32 @@ class AppGui { } maximizeConsole(doMaximize = true) { - const consoleWidget = this.widget('console'); + // const consoleWidget = this.widget('console'); - if (consoleWidget.isMaximized__ === undefined) { - consoleWidget.isMaximized__ = false; - } + // if (consoleWidget.isMaximized__ === undefined) { + // consoleWidget.isMaximized__ = false; + // } - if (consoleWidget.isMaximized__ === doMaximize) return; + // if (consoleWidget.isMaximized__ === doMaximize) return; - let constraints = { - type: 'fixed', - factor: !doMaximize ? 5 : this.widget('vLayout').height - 4, - }; + // let constraints = { + // type: 'fixed', + // factor: !doMaximize ? 5 : this.widget('vLayout').height - 4, + // }; - consoleWidget.isMaximized__ = doMaximize; + // consoleWidget.isMaximized__ = doMaximize; - this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints); + // this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints); } minimizeConsole() { - this.maximizeConsole(false); + //this.maximizeConsole(false); } consoleIsMaximized() { - return this.widget('console').isMaximized__ === true; + return false; + + //return this.widget('console').isMaximized__ === true; } widget(name) { @@ -323,7 +318,7 @@ class AppGui { try { await this.app().execCommand(args); } catch (error) { - this.widget('console').bufferPush(error.message); + this.stdout(error.message); } } @@ -346,7 +341,7 @@ class AppGui { // Any key after which a shortcut is not possible. isSpecialKey(name) { - return ['ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB', 'PAGE_UP', 'PAGE_DOWN'].indexOf(name) >= 0; + return [':', 'ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB', 'PAGE_UP', 'PAGE_DOWN'].indexOf(name) >= 0; } fullScreen(enable = true) { @@ -360,6 +355,15 @@ class AppGui { } } + stdout(...object) { + for (let i = 0; i < object.length; i++) { + const v = typeof object[i] === 'object' ? JSON.stringify(object[i]) : object[i]; + this.widget('console').addItem(v); + } + + if (object.length) this.widget('statusBar').setItemAt(0, object[object.length-1]); + } + async start() { const term = this.term(); @@ -368,7 +372,7 @@ class AppGui { try { this.renderer_.start(); - const consoleWidget = this.widget('console'); + const statusBar = this.widget('statusBar'); term.grabInput(); @@ -390,7 +394,7 @@ class AppGui { if (name === 'CTRL_C' ) { const cmd = this.app().currentCommand(); if (!cmd || !cmd.cancellable() || this.commandCancelCalled_) { - consoleWidget.bufferPush(_('Press Ctrl+D or type "exit" to exit the application')); + this.stdout(_('Press Ctrl+D or type "exit" to exit the application')); } else { this.commandCancelCalled_ = true; await cmd.cancel(); @@ -415,21 +419,34 @@ class AppGui { this.lastShortcutKeyTime_ = now; - // Don't process shortcut keys if the console is active, except if the shortcut - // starts with CTRL (eg. CTRL+J CTRL+Z to maximize the console window). - if (!consoleWidget.hasFocus || (this.currentShortcutKeys_.length && this.currentShortcutKeys_[0].indexOf('CTRL') === 0)) { - this.logger().debug('Now: ' + name + ', Keys: ', this.currentShortcutKeys_); + if (name === ':' && !statusBar.promptActive) { + const cmd = await statusBar.prompt(); + await this.processCommand(cmd); + } else { + // Don't process shortcut keys if the console is active, except if the shortcut + // starts with CTRL (eg. CTRL+J CTRL+Z to maximize the console window). + if (!statusBar.promptActive || (this.currentShortcutKeys_.length && this.currentShortcutKeys_[0].indexOf('CTRL') === 0)) { + this.logger().debug('Now: ' + name + ', Keys: ', this.currentShortcutKeys_); - const shortcutKey = this.currentShortcutKeys_.join(''); - if (shortcutKey in this.shortcuts_) { - const cmd = this.shortcuts_[shortcutKey].action; - if (!cmd.isDocOnly) { - this.currentShortcutKeys_ = []; - if (typeof cmd === 'function') { - cmd(); - } else { - consoleWidget.bufferPush(cmd); - await this.processCommand(cmd); + const shortcutKey = this.currentShortcutKeys_.join(''); + if (shortcutKey in this.shortcuts_) { + const cmd = this.shortcuts_[shortcutKey].action; + if (!cmd.isDocOnly) { + this.currentShortcutKeys_ = []; + if (typeof cmd === 'function') { + await cmd(); + } else if (typeof cmd === 'object') { + if (cmd.type === 'prompt') { + const commandString = await statusBar.prompt(cmd.initialText ? cmd.initialText : ''); + this.stdout(commandString); + await this.processCommand(commandString); + } else { + throw new Error('Unknown command: ' + JSON.stringify(cmd)); + } + } else { + this.stdout(cmd); + await this.processCommand(cmd); + } } } } diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 4d7210218..1d1a35793 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -270,12 +270,8 @@ class Application { } setupCommand(cmd) { - const consoleWidget = this.gui_.widget('console'); - cmd.setStdout((...object) => { - for (let i = 0; i < object.length; i++) { - consoleWidget.bufferPush(object[i]); - } + this.gui().stdout(...object); }); cmd.setDispatcher((action) => { @@ -283,15 +279,13 @@ class Application { }); cmd.setPrompt(async (message, options) => { - consoleWidget.focus(); - if (options.type == 'boolean') { message += ' (' + options.answers.join('/') + ')'; } - - var answer = await consoleWidget.waitForResult(message + ' '); - if (options.type == 'boolean') { + const answer = await this.gui().widget('statusBar').prompt('', message + ' '); + + if (options.type === 'boolean') { if (answer === null) return false; return answer === '' || answer.toLowerCase() == options.answers[0].toLowerCase(); } diff --git a/CliClient/app/command-rm.js b/CliClient/app/command-rm.js index 3399536df..5e9008aed 100644 --- a/CliClient/app/command-rm.js +++ b/CliClient/app/command-rm.js @@ -43,7 +43,7 @@ class Command extends BaseCommand { const notes = await app().loadItems(BaseModel.TYPE_NOTE, pattern); if (!notes.length) throw new Error(_('Cannot find "%s".', pattern)); - const ok = force ? true : await this.prompt(_('%d notes match this pattern. Delete them?', notes.length)); + const ok = force ? true : await this.prompt(notes.length > 1 ? _('%d notes match this pattern. Delete them?', notes.length) : _('Delete note?')); if (!ok) return; let ids = notes.map((n) => n.id); await Note.batchDelete(ids); diff --git a/CliClient/app/gui/ConsoleWidget.js b/CliClient/app/gui/ConsoleWidget.js new file mode 100644 index 000000000..af0e22753 --- /dev/null +++ b/CliClient/app/gui/ConsoleWidget.js @@ -0,0 +1,20 @@ +const ListWidget = require('tkwidgets/ListWidget.js'); + +class ConsoleWidget extends ListWidget { + + constructor() { + super(); + } + + get name() { + return 'console'; + } + + addItem(v) { + super.addItem(v); + this.currentIndex = this.items.length - 1; + } + +} + +module.exports = ConsoleWidget; \ No newline at end of file diff --git a/CliClient/app/gui/StatusBarWidget.js b/CliClient/app/gui/StatusBarWidget.js new file mode 100644 index 000000000..8fe9faa2e --- /dev/null +++ b/CliClient/app/gui/StatusBarWidget.js @@ -0,0 +1,112 @@ +const BaseWidget = require('tkwidgets/BaseWidget.js'); +const chalk = require('chalk'); + +class StatusBarWidget extends BaseWidget { + + constructor() { + super(); + + this.promptState_ = null; + this.inputEventEmitter_ = null; + this.history_ = []; + this.items_ = []; + } + + get name() { + return 'statusBar'; + } + + setItemAt(index, text) { + this.items_[index] = text; + this.invalidate(); + } + + async prompt(initialText = '', promptString = ':') { + if (this.promptState_) throw new Error('Another prompt already active'); + + this.root.globalDisableKeyboard(this); + + this.promptState_ = { + promise: null, + initialText: initialText, + promptString: promptString, + }; + + this.promptState_.promise = new Promise((resolve, reject) => { + this.promptState_.resolve = resolve; + this.promptState_.reject = reject; + }); + + this.invalidate(); + + return this.promptState_.promise; + } + + get promptActive() { + return !!this.promptState_; + } + + get history() { + return this.history_; + } + + render() { + super.render(); + this.innerClear(); + + const textStyle = chalk.bgBlueBright.white; + + this.term.drawHLine(this.absoluteInnerX, this.absoluteInnerY, this.innerWidth, textStyle(' ')); + + this.term.moveTo(this.absoluteInnerX, this.absoluteInnerY); + + if (this.promptActive) { + + this.term.write(textStyle(this.promptState_.promptString)); + + this.term.showCursor(true); + + let options = { + cancelable: true, + history: this.history, + default: this.promptState_.initialText, + style: this.term.innerStyle.bgBrightBlue.white, // NOTE: Need to use TK style for this as inputField is not compatible with chalk + }; + + this.inputEventEmitter_ = this.term.inputField(options, (error, input) => { + let resolveResult = null; + const resolveFn = this.promptState_.resolve; + + if (error) { + this.logger().error('StatusBar: inputField error:', error); + } else { + if (input === undefined) { + // User cancel + } else { + resolveResult = input; + if (input && input.trim() != '') this.history_.push(input); + } + } + + this.inputEventEmitter_ = null; + this.term.showCursor(false); + this.promptState_ = null; + this.root.globalEnableKeyboard(this); + this.invalidate(); + + // Only callback once everything has been cleaned up and reset + resolveFn(resolveResult); + }); + + } else { + + for (let i = 0; i < this.items_.length; i++) { + this.term.write(textStyle(this.items_[i])); + } + + } + } + +} + +module.exports = StatusBarWidget; \ No newline at end of file diff --git a/CliClient/locales/en_GB.po b/CliClient/locales/en_GB.po index 44a01e8a2..40b394aa7 100644 --- a/CliClient/locales/en_GB.po +++ b/CliClient/locales/en_GB.po @@ -27,10 +27,10 @@ msgstr "" msgid "Exit the console" msgstr "" -msgid "Create a new todo" +msgid "Create a new note" msgstr "" -msgid "Create a new note" +msgid "Create a new todo" msgstr "" msgid "Create a new notebook" @@ -321,6 +321,9 @@ msgstr "" msgid "%d notes match this pattern. Delete them?" msgstr "" +msgid "Delete note?" +msgstr "" + msgid "Searches for the given in all the notes." msgstr "" @@ -630,9 +633,6 @@ msgstr "" msgid "Cancel" msgstr "" -msgid "Delete note?" -msgstr "" - msgid "Attach file" msgstr "" diff --git a/CliClient/locales/fr_FR.po b/CliClient/locales/fr_FR.po index 3db1ac88a..de9e8c100 100644 --- a/CliClient/locales/fr_FR.po +++ b/CliClient/locales/fr_FR.po @@ -30,14 +30,14 @@ msgstr "" msgid "Exit the console" msgstr "Quitter le logiciel." -#, fuzzy -msgid "Create a new todo" -msgstr "Créer une nouvelle tâche." - #, fuzzy msgid "Create a new note" msgstr "Créer une note." +#, fuzzy +msgid "Create a new todo" +msgstr "Créer une nouvelle tâche." + #, fuzzy msgid "Create a new notebook" msgstr "Créer un carnet." @@ -359,6 +359,9 @@ msgstr "Supprime les objets sans demander la confirmation." msgid "%d notes match this pattern. Delete them?" msgstr "%d notes correspondent à ce motif. Les supprimer ?" +msgid "Delete note?" +msgstr "Supprimer la note ?" + msgid "Searches for the given in all the notes." msgstr "Chercher le motif dans toutes les notes." @@ -690,9 +693,6 @@ msgstr "" msgid "Cancel" msgstr "Annulation..." -msgid "Delete note?" -msgstr "Supprimer la note ?" - msgid "Attach file" msgstr "Attacher un fichier" @@ -744,6 +744,10 @@ msgstr "" msgid "Welcome" msgstr "Bienvenue" +#, fuzzy +#~ msgid "Last command: %s" +#~ msgstr "Commande invalide : \"%s\"" + #~ msgid "Done editing." #~ msgstr "Edition terminée." diff --git a/CliClient/locales/joplin.pot b/CliClient/locales/joplin.pot index 44a01e8a2..40b394aa7 100644 --- a/CliClient/locales/joplin.pot +++ b/CliClient/locales/joplin.pot @@ -27,10 +27,10 @@ msgstr "" msgid "Exit the console" msgstr "" -msgid "Create a new todo" +msgid "Create a new note" msgstr "" -msgid "Create a new note" +msgid "Create a new todo" msgstr "" msgid "Create a new notebook" @@ -321,6 +321,9 @@ msgstr "" msgid "%d notes match this pattern. Delete them?" msgstr "" +msgid "Delete note?" +msgstr "" + msgid "Searches for the given in all the notes." msgstr "" @@ -630,9 +633,6 @@ msgstr "" msgid "Cancel" msgstr "" -msgid "Delete note?" -msgstr "" - msgid "Attach file" msgstr ""