mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Improved UI usability
This commit is contained in:
parent
3704f5be27
commit
1abab26041
@ -3,6 +3,7 @@ import { Folder } from 'lib/models/folder.js';
|
||||
import { Note } from 'lib/models/note.js';
|
||||
import { cliUtils } from './cli-utils.js';
|
||||
import { reducer, defaultState } from 'lib/reducer.js';
|
||||
import { _ } from 'lib/locale.js';
|
||||
|
||||
const tk = require('terminal-kit');
|
||||
const termutils = require('tkwidgets/framework/termutils.js');
|
||||
@ -45,11 +46,16 @@ class AppGui {
|
||||
});
|
||||
|
||||
this.shortcuts_ = this.setupShortcuts();
|
||||
|
||||
this.inputMode_ = AppGui.INPUT_MODE_NORMAL;
|
||||
|
||||
this.currentShortcutKeys_ = '';
|
||||
this.lastShortcutKeyTime_ = 0;
|
||||
}
|
||||
|
||||
buildUi() {
|
||||
this.rootWidget_ = new ReduxRootWidget(this.store_);
|
||||
this.rootWidget_.name = 'rootWidget';
|
||||
this.rootWidget_.name = 'root';
|
||||
|
||||
const folderList = new FolderListWidget();
|
||||
folderList.setStyle({ borderBottomWidth: 1 });
|
||||
@ -123,7 +129,7 @@ class AppGui {
|
||||
const vLayout = new VLayoutWidget();
|
||||
vLayout.name = 'vLayout';
|
||||
vLayout.addChild(hLayout, { type: 'stretch', factor: 1 });
|
||||
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 5 });
|
||||
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 6 });
|
||||
|
||||
const win1 = new WindowWidget();
|
||||
win1.addChild(vLayout);
|
||||
@ -137,39 +143,101 @@ class AppGui {
|
||||
|
||||
const consoleWidget = this.widget('console');
|
||||
|
||||
shortcuts['DELETE'] = 'rm $n';
|
||||
shortcuts['DELETE'] = {
|
||||
description: _('Delete a note'),
|
||||
action: 'rm $n',
|
||||
};
|
||||
|
||||
shortcuts[' '] = 'todo toggle $n';
|
||||
|
||||
shortcuts['c'] = () => {
|
||||
consoleWidget.focus();
|
||||
shortcuts[' '] = {
|
||||
friendlyName: 'SPACE',
|
||||
description: _('Set a todo as completed / not completed'),
|
||||
action: 'todo toggle $n',
|
||||
}
|
||||
|
||||
shortcuts['ENTER'] = () => {
|
||||
const w = this.widget('mainWindow').focusedWidget();
|
||||
if (w.name == 'folderList') {
|
||||
this.widget('noteList').focus();
|
||||
} else if (w.name == 'noteList') {
|
||||
this.processCommand('edit $n');
|
||||
}
|
||||
shortcuts['c'] = {
|
||||
description: _('Enter the console'),
|
||||
action: () => { consoleWidget.focus(); }
|
||||
};
|
||||
|
||||
shortcuts['ESC'] = {
|
||||
description: _('Exit the console'),
|
||||
isDocOnly: true,
|
||||
};
|
||||
|
||||
shortcuts['ENTER'] = {
|
||||
description: null,
|
||||
action: () => {
|
||||
const w = this.widget('mainWindow').focusedWidget();
|
||||
if (w.name == 'folderList') {
|
||||
this.widget('noteList').focus();
|
||||
} else if (w.name == 'noteList') {
|
||||
this.processCommand('edit $n');
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
shortcuts[':nn'] = () => {
|
||||
consoleWidget.focus('mknote ');
|
||||
shortcuts['nt'] = {
|
||||
description: _('Create a new todo'),
|
||||
action: () => { consoleWidget.focus('mktodo '); },
|
||||
}
|
||||
|
||||
shortcuts[':nt'] = () => {
|
||||
consoleWidget.focus('mktodo ');
|
||||
shortcuts['nn'] = {
|
||||
description: _('Create a new note'),
|
||||
action: () => { consoleWidget.focus('mknote '); },
|
||||
}
|
||||
|
||||
shortcuts[':nb'] = () => {
|
||||
consoleWidget.focus('mkbook ');
|
||||
shortcuts['nt'] = {
|
||||
description: _('Create a new todo'),
|
||||
action: () => { consoleWidget.focus('mktodo '); },
|
||||
}
|
||||
|
||||
shortcuts['nb'] = {
|
||||
description: _('Create a new notebook'),
|
||||
action: () => { consoleWidget.focus('mkbook '); },
|
||||
}
|
||||
|
||||
shortcuts['CTRL_JCTRL_Z'] = {
|
||||
friendlyName: 'Ctrl+J Ctrl+Z',
|
||||
description: _('Maximise/minimise the console'),
|
||||
action: () => { this.toggleMaximizeConsole(); },
|
||||
}
|
||||
|
||||
return shortcuts;
|
||||
}
|
||||
|
||||
toggleMaximizeConsole() {
|
||||
this.maximizeConsole(!this.consoleIsMaximized());
|
||||
}
|
||||
|
||||
maximizeConsole(doMaximize = true) {
|
||||
const consoleWidget = this.widget('console');
|
||||
|
||||
if (consoleWidget.isMaximized__ === undefined) {
|
||||
consoleWidget.isMaximized__ = false;
|
||||
}
|
||||
|
||||
if (consoleWidget.isMaximized__ === doMaximize) return;
|
||||
|
||||
let constraints = {
|
||||
type: 'fixed',
|
||||
factor: !doMaximize ? 5 : this.widget('vLayout').height() - 4,
|
||||
};
|
||||
|
||||
consoleWidget.isMaximized__ = doMaximize;
|
||||
|
||||
this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
|
||||
}
|
||||
|
||||
minimizeConsole() {
|
||||
this.maximizeConsole(false);
|
||||
}
|
||||
|
||||
consoleIsMaximized() {
|
||||
return this.widget('console').isMaximized__ === true;
|
||||
}
|
||||
|
||||
widget(name) {
|
||||
if (name === 'root') return this.rootWidget_;
|
||||
return this.rootWidget_.childByName(name);
|
||||
}
|
||||
|
||||
@ -185,6 +253,10 @@ class AppGui {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
shortcuts() {
|
||||
return this.shortcuts_;
|
||||
}
|
||||
|
||||
term() {
|
||||
return this.term_;
|
||||
}
|
||||
@ -218,30 +290,6 @@ class AppGui {
|
||||
cmd = cmd.trim();
|
||||
if (!cmd.length) return;
|
||||
|
||||
const consoleWidget = this.widget('console');
|
||||
|
||||
if (cmd === ':m') {
|
||||
if (consoleWidget.isMaximized__ === undefined) {
|
||||
consoleWidget.isMaximized__ = false;
|
||||
}
|
||||
|
||||
let constraints = {
|
||||
type: 'fixed',
|
||||
factor: consoleWidget.isMaximized__ ? 5 : this.widget('vLayout').height() - 4,
|
||||
};
|
||||
|
||||
consoleWidget.isMaximized__ = !consoleWidget.isMaximized__;
|
||||
|
||||
this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
|
||||
|
||||
return;
|
||||
} else if (cmd[0] === ':') {
|
||||
if (this.shortcuts_[cmd]) {
|
||||
this.shortcuts_[cmd]();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let note = this.widget('noteList').currentItem;
|
||||
let folder = this.widget('folderList').currentItem;
|
||||
let args = cliUtils.splitCommandString(cmd);
|
||||
@ -260,7 +308,7 @@ class AppGui {
|
||||
try {
|
||||
await this.app().execCommand(args);
|
||||
} catch (error) {
|
||||
consoleWidget.bufferPush(error.message);
|
||||
this.widget('console').bufferPush(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,6 +329,10 @@ class AppGui {
|
||||
this.widget('noteText').text = text;
|
||||
}
|
||||
|
||||
isSpecialKey(name) {
|
||||
return ['ENTER', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'DELETE', 'BACKSPACE', 'ESCAPE', 'TAB', 'SHIFT_TAB'].indexOf(name) >= 0;
|
||||
}
|
||||
|
||||
async start() {
|
||||
const term = this.term();
|
||||
|
||||
@ -302,16 +354,31 @@ class AppGui {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!consoleWidget.hasFocus()) {
|
||||
if (name == ':') {
|
||||
consoleWidget.focus(':');
|
||||
} else if (name in this.shortcuts_) {
|
||||
const cmd = this.shortcuts_[name];
|
||||
if (typeof cmd === 'function') {
|
||||
cmd();
|
||||
} else {
|
||||
consoleWidget.bufferPush(cmd);
|
||||
await this.processCommand(cmd);
|
||||
const now = (new Date()).getTime();
|
||||
|
||||
if (now - this.lastShortcutKeyTime_ > 1000 || this.isSpecialKey(name)) {
|
||||
this.currentShortcutKeys_ = name;
|
||||
} else {
|
||||
this.currentShortcutKeys_ += name;
|
||||
}
|
||||
|
||||
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_.indexOf('CTRL') === 0) {
|
||||
this.logger().debug('Now: ' + name + ', Keys: ' + this.currentShortcutKeys_);
|
||||
|
||||
if (this.currentShortcutKeys_ in this.shortcuts_) {
|
||||
const cmd = this.shortcuts_[this.currentShortcutKeys_].action;
|
||||
if (!cmd.isDocOnly) {
|
||||
this.currentShortcutKeys_ = '';
|
||||
if (typeof cmd === 'function') {
|
||||
cmd();
|
||||
} else {
|
||||
consoleWidget.bufferPush(cmd);
|
||||
await this.processCommand(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,4 +400,7 @@ class AppGui {
|
||||
|
||||
}
|
||||
|
||||
AppGui.INPUT_MODE_NORMAL = 1;
|
||||
AppGui.INPUT_MODE_META = 2;
|
||||
|
||||
module.exports = AppGui;
|
@ -37,6 +37,10 @@ class Application {
|
||||
this.eventEmitter_ = new EventEmitter();
|
||||
}
|
||||
|
||||
gui() {
|
||||
return this.gui_;
|
||||
}
|
||||
|
||||
logger() {
|
||||
return this.logger_;
|
||||
}
|
||||
@ -49,6 +53,10 @@ class Application {
|
||||
return this.currentFolder_;
|
||||
}
|
||||
|
||||
commandStdoutMaxWidth() {
|
||||
return 78;
|
||||
}
|
||||
|
||||
async refreshCurrentFolder() {
|
||||
let newFolder = null;
|
||||
|
||||
|
@ -3,8 +3,9 @@ import { app } from './app.js';
|
||||
import { renderCommandHelp } from './help-utils.js';
|
||||
import { Database } from 'lib/database.js';
|
||||
import { Setting } from 'lib/models/setting.js';
|
||||
import { wrap } from 'lib/string-utils.js';
|
||||
import { _ } from 'lib/locale.js';
|
||||
import { ReportService } from 'lib/services/report.js';
|
||||
import { cliUtils } from './cli-utils.js';
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@ -17,18 +18,54 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const commands = args['command'] ? [app().findCommandByName(args['command'])] : app().commands();
|
||||
const stdoutWidth = app().commandStdoutMaxWidth();
|
||||
|
||||
let output = [];
|
||||
for (let n in commands) {
|
||||
if (!commands.hasOwnProperty(n)) continue;
|
||||
const command = commands[n];
|
||||
output.push(renderCommandHelp(command));
|
||||
if (args.command === 'shortcuts') {
|
||||
const shortcuts = app().gui().shortcuts();
|
||||
|
||||
let rows = [];
|
||||
|
||||
for (let n in shortcuts) {
|
||||
if (!shortcuts.hasOwnProperty(n)) continue;
|
||||
const shortcut = shortcuts[n];
|
||||
if (!shortcut.description) continue;
|
||||
n = shortcut.friendlyName ? shortcut.friendlyName : n;
|
||||
rows.push([n, shortcut.description]);
|
||||
}
|
||||
|
||||
cliUtils.printArray(this.stdout.bind(this), rows);
|
||||
} else if (args.command) {
|
||||
const command = app().findCommandByName(args['command']);
|
||||
if (!command) throw new Error(_('Cannot find "%s".', args.command));
|
||||
|
||||
this.stdout(renderCommandHelp(command, stdoutWidth));
|
||||
} else {
|
||||
const commands = app().commands();
|
||||
let commandNames = [];
|
||||
for (let n in commands) {
|
||||
if (!commands.hasOwnProperty(n)) continue;
|
||||
const command = commands[n];
|
||||
commandNames.push(command.name());
|
||||
}
|
||||
|
||||
commandNames.sort();
|
||||
|
||||
let lines = [];
|
||||
lines.push(_('Type `help [command]` for more information about a command.'));
|
||||
lines.push('');
|
||||
lines.push(_('The possible commands are:'));
|
||||
lines.push('');
|
||||
lines.push(commandNames.join(', '));
|
||||
lines.push('');
|
||||
lines.push(_('To maximise/minimise the console, press Ctrl+J Ctrl+Z.'));
|
||||
lines.push(_('To enter the console, press C'));
|
||||
lines.push(_('To exit the console, press ESCAPE'));
|
||||
lines.push(_('To view a list of available shortcuts type `help shortcuts`'));
|
||||
|
||||
this.stdout(wrap(lines.join("\n"), '', stdoutWidth));
|
||||
}
|
||||
|
||||
output.sort();
|
||||
|
||||
this.stdout(output.join("\n\n"));
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,22 +2,22 @@ require('source-map-support').install();
|
||||
require('babel-plugin-transform-runtime');
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { wrap } from 'lib/string-utils.js';
|
||||
import { fileExtension, basename, dirname } from 'lib/path-utils.js';
|
||||
import wrap_ from 'word-wrap';
|
||||
import { _, setLocale, languageCode } from 'lib/locale.js';
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
const MAX_WIDTH = 78;
|
||||
const INDENT = ' ';
|
||||
|
||||
function wrap(text, indent) {
|
||||
return wrap_(text, {
|
||||
width: MAX_WIDTH - indent.length,
|
||||
indent: indent,
|
||||
});
|
||||
}
|
||||
// function wrap(text, indent, width) {
|
||||
// return wrap_(text, {
|
||||
// width: width - indent.length,
|
||||
// indent: indent,
|
||||
// });
|
||||
// }
|
||||
|
||||
function renderOptions(options, baseIndent) {
|
||||
function renderOptions(options, baseIndent, width) {
|
||||
let output = [];
|
||||
const optionColWidth = getOptionColWidth(options);
|
||||
|
||||
@ -26,7 +26,7 @@ function renderOptions(options, baseIndent) {
|
||||
const flag = option[0];
|
||||
const indent = baseIndent + INDENT + ' '.repeat(optionColWidth + 2);
|
||||
|
||||
let r = wrap(option[1], indent);
|
||||
let r = wrap(option[1], indent, width);
|
||||
r = r.substr(flag.length + (baseIndent + INDENT).length);
|
||||
r = baseIndent + INDENT + flag + r;
|
||||
output.push(r);
|
||||
@ -35,15 +35,17 @@ function renderOptions(options, baseIndent) {
|
||||
return output.join("\n");
|
||||
}
|
||||
|
||||
function renderCommandHelp(cmd) {
|
||||
function renderCommandHelp(cmd, width = null) {
|
||||
if (width === null) width = MAX_WIDTH;
|
||||
|
||||
const baseIndent = '';
|
||||
|
||||
let output = [];
|
||||
output.push(baseIndent + cmd.usage());
|
||||
output.push('');
|
||||
output.push(wrap(cmd.description(), baseIndent + INDENT));
|
||||
output.push(wrap(cmd.description(), baseIndent + INDENT, width));
|
||||
|
||||
const optionString = renderOptions(cmd.options(), baseIndent);
|
||||
const optionString = renderOptions(cmd.options(), baseIndent, width);
|
||||
|
||||
if (optionString) {
|
||||
output.push('');
|
||||
|
@ -15,6 +15,30 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Delete a note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set a todo as completed / not completed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "Exit the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new todo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new notebook"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximise/minimise the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "[Cancel]"
|
||||
msgstr ""
|
||||
|
||||
@ -180,6 +204,24 @@ msgstr ""
|
||||
msgid "Displays usage information."
|
||||
msgstr ""
|
||||
|
||||
msgid "Type `help [command]` for more information about a command."
|
||||
msgstr ""
|
||||
|
||||
msgid "The possible commands are:"
|
||||
msgstr ""
|
||||
|
||||
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z."
|
||||
msgstr ""
|
||||
|
||||
msgid "To enter the console, press C"
|
||||
msgstr ""
|
||||
|
||||
msgid "To exit the console, press ESCAPE"
|
||||
msgstr ""
|
||||
|
||||
msgid "To view a list of available shortcuts type `help shortcuts`"
|
||||
msgstr ""
|
||||
|
||||
msgid "Imports an Evernote notebook file (.enex file)."
|
||||
msgstr ""
|
||||
|
||||
|
@ -15,6 +15,37 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.3\n"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete a note"
|
||||
msgstr "Supprimer la note"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Set a todo as completed / not completed"
|
||||
msgstr "Tâches non-complétées et récentes"
|
||||
|
||||
msgid "Enter the console"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
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 notebook"
|
||||
msgstr "Créer un carnet."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Maximise/minimise the console"
|
||||
msgstr "Quitter le logiciel."
|
||||
|
||||
#, fuzzy
|
||||
msgid "[Cancel]"
|
||||
msgstr "Annulation..."
|
||||
@ -197,6 +228,24 @@ msgstr "Afficher l'URL de l'emplacement de la note."
|
||||
msgid "Displays usage information."
|
||||
msgstr "Affiche les informations de version"
|
||||
|
||||
msgid "Type `help [command]` for more information about a command."
|
||||
msgstr ""
|
||||
|
||||
msgid "The possible commands are:"
|
||||
msgstr ""
|
||||
|
||||
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z."
|
||||
msgstr ""
|
||||
|
||||
msgid "To enter the console, press C"
|
||||
msgstr ""
|
||||
|
||||
msgid "To exit the console, press ESCAPE"
|
||||
msgstr ""
|
||||
|
||||
msgid "To view a list of available shortcuts type `help shortcuts`"
|
||||
msgstr ""
|
||||
|
||||
msgid "Imports an Evernote notebook file (.enex file)."
|
||||
msgstr "Importer un carnet Evernote (fichier .enex)."
|
||||
|
||||
|
@ -15,6 +15,30 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Delete a note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set a todo as completed / not completed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "Exit the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new todo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new notebook"
|
||||
msgstr ""
|
||||
|
||||
msgid "Maximise/minimise the console"
|
||||
msgstr ""
|
||||
|
||||
msgid "[Cancel]"
|
||||
msgstr ""
|
||||
|
||||
@ -180,6 +204,24 @@ msgstr ""
|
||||
msgid "Displays usage information."
|
||||
msgstr ""
|
||||
|
||||
msgid "Type `help [command]` for more information about a command."
|
||||
msgstr ""
|
||||
|
||||
msgid "The possible commands are:"
|
||||
msgstr ""
|
||||
|
||||
msgid "To maximise/minimise the console, press Ctrl+J Ctrl+Z."
|
||||
msgstr ""
|
||||
|
||||
msgid "To enter the console, press C"
|
||||
msgstr ""
|
||||
|
||||
msgid "To exit the console, press ESCAPE"
|
||||
msgstr ""
|
||||
|
||||
msgid "To view a list of available shortcuts type `help shortcuts`"
|
||||
msgstr ""
|
||||
|
||||
msgid "Imports an Evernote notebook file (.enex file)."
|
||||
msgstr ""
|
||||
|
||||
|
@ -113,4 +113,13 @@ function escapeFilename(s, maxLength = 32) {
|
||||
return output.substr(0, maxLength);
|
||||
}
|
||||
|
||||
export { removeDiacritics, escapeFilename };
|
||||
function wrap(text, indent, width) {
|
||||
const wrap_ = require('word-wrap');
|
||||
|
||||
return wrap_(text, {
|
||||
width: width - indent.length,
|
||||
indent: indent,
|
||||
});
|
||||
}
|
||||
|
||||
export { removeDiacritics, escapeFilename, wrap };
|
Loading…
Reference in New Issue
Block a user