xP: support adding formatting from keyboard
Just like in xC, only with some indication.
This commit is contained in:
parent
67d52a2d89
commit
f2d8de3ab9
9
xC.c
9
xC.c
|
@ -15010,21 +15010,28 @@ process_formatting_escape (const struct pollfd *fd, struct app_context *ctx)
|
|||
|
||||
if (buf->len != 1)
|
||||
goto error;
|
||||
|
||||
// Letters mostly taken from their caret escapes + HTML element names.
|
||||
// Additionally, 'm' stands for mono, 'x' for cross, 'r' for reset.
|
||||
switch (buf->str[0])
|
||||
{
|
||||
case 'b' ^ 96:
|
||||
case 'b': CALL_ (ctx->input, insert, "\x02"); break;
|
||||
case 'c': CALL_ (ctx->input, insert, "\x03"); break;
|
||||
case 'q':
|
||||
case 'm': CALL_ (ctx->input, insert, "\x11"); break;
|
||||
case 'v': CALL_ (ctx->input, insert, "\x16"); break;
|
||||
case 'i' ^ 96:
|
||||
case 'i':
|
||||
case ']': CALL_ (ctx->input, insert, "\x1d"); break;
|
||||
case 's' ^ 96:
|
||||
case 's':
|
||||
case 'x' ^ 96:
|
||||
case 'x':
|
||||
case '^': CALL_ (ctx->input, insert, "\x1e"); break;
|
||||
case 'u' ^ 96:
|
||||
case 'u':
|
||||
case '_': CALL_ (ctx->input, insert, "\x1f"); break;
|
||||
case 'v': CALL_ (ctx->input, insert, "\x16"); break;
|
||||
case 'r':
|
||||
case 'o': CALL_ (ctx->input, insert, "\x0f"); break;
|
||||
|
||||
|
|
|
@ -687,34 +687,25 @@ let BufferContainer = {
|
|||
}
|
||||
|
||||
let Toolbar = {
|
||||
insert: formatting => {
|
||||
format: formatting => {
|
||||
let textarea = document.getElementById('input')
|
||||
if (textarea === null)
|
||||
return
|
||||
|
||||
const [start, end] = [textarea.selectionStart, textarea.selectionEnd]
|
||||
if (start === end) {
|
||||
textarea.setRangeText(formatting)
|
||||
textarea.setSelectionRange(
|
||||
start + formatting.length, end + formatting.length)
|
||||
} else {
|
||||
textarea.setRangeText(
|
||||
formatting + textarea.value.substr(start, end) + formatting)
|
||||
}
|
||||
textarea.focus()
|
||||
if (textarea !== null)
|
||||
Input.format(textarea, formatting)
|
||||
},
|
||||
|
||||
view: vnode => {
|
||||
let indicator = undefined
|
||||
let indicators = []
|
||||
if (bufferLog === undefined && !bufferAutoscroll)
|
||||
indicator = m('.indicator', {}, '⇩')
|
||||
indicators.push(m('.indicator', {}, '⇩'))
|
||||
if (Input.formatting)
|
||||
indicators.push(m('.indicator', {}, '#'))
|
||||
return m('.toolbar', {}, [
|
||||
indicator,
|
||||
m('button', {onclick: event => Toolbar.insert('\u0002')},
|
||||
indicators,
|
||||
m('button', {onclick: event => Toolbar.format('\u0002')},
|
||||
m('b', {}, 'B')),
|
||||
m('button', {onclick: event => Toolbar.insert('\u001D')},
|
||||
m('button', {onclick: event => Toolbar.format('\u001D')},
|
||||
m('i', {}, 'I')),
|
||||
m('button', {onclick: event => Toolbar.insert('\u001F')},
|
||||
m('button', {onclick: event => Toolbar.format('\u001F')},
|
||||
m('u', {}, 'U')),
|
||||
m('button', {onclick: event => bufferToggleLog()},
|
||||
bufferLog === undefined ? 'Log' : 'Hide log'),
|
||||
|
@ -924,6 +915,21 @@ let Input = {
|
|||
return true
|
||||
},
|
||||
|
||||
formatting: false,
|
||||
|
||||
format: (textarea, formatting) => {
|
||||
const [start, end] = [textarea.selectionStart, textarea.selectionEnd]
|
||||
if (start === end) {
|
||||
textarea.setRangeText(formatting)
|
||||
textarea.setSelectionRange(
|
||||
start + formatting.length, end + formatting.length)
|
||||
} else {
|
||||
textarea.setRangeText(
|
||||
formatting + textarea.value.substr(start, end) + formatting)
|
||||
}
|
||||
textarea.focus()
|
||||
},
|
||||
|
||||
onKeyDown: event => {
|
||||
// TODO: And perhaps on other actions, too.
|
||||
rpc.send({command: 'Active'})
|
||||
|
@ -935,7 +941,29 @@ let Input = {
|
|||
let textarea = event.currentTarget
|
||||
let handled = false
|
||||
let success = true
|
||||
if (hasShortcutModifiers(event)) {
|
||||
if (Input.formatting) {
|
||||
Input.formatting = false
|
||||
|
||||
// Like process_formatting_escape() within xC.
|
||||
handled = true
|
||||
switch (event.key) {
|
||||
case 'b': Input.format(textarea, '\u0002'); break
|
||||
case 'c': Input.format(textarea, '\u0003'); break
|
||||
case 'q':
|
||||
case 'm': Input.format(textarea, '\u0011'); break
|
||||
case 'v': Input.format(textarea, '\u0016'); break
|
||||
case 'i':
|
||||
case ']': Input.format(textarea, '\u001D'); break
|
||||
case 's':
|
||||
case 'x':
|
||||
case '^': Input.format(textarea, '\u001E'); break
|
||||
case 'u':
|
||||
case '_': Input.format(textarea, '\u001F'); break
|
||||
case 'r':
|
||||
case 'o': Input.format(textarea, '\u000F'); break
|
||||
default: success = false
|
||||
}
|
||||
} else if (hasShortcutModifiers(event)) {
|
||||
handled = true
|
||||
switch (event.key) {
|
||||
case 'b': success = Input.backward(textarea); break
|
||||
|
@ -947,6 +975,7 @@ let Input = {
|
|||
case '>': success = Input.last(b, textarea); break
|
||||
case 'p': success = Input.previous(b, textarea); break
|
||||
case 'n': success = Input.next(b, textarea); break
|
||||
case 'm': success = Input.formatting = true; break
|
||||
default: handled = false
|
||||
}
|
||||
} else if (!event.altKey && !event.ctrlKey && !event.metaKey &&
|
||||
|
@ -964,15 +993,20 @@ let Input = {
|
|||
event.preventDefault()
|
||||
},
|
||||
|
||||
onStateChange: event => {
|
||||
Completions.reset()
|
||||
Input.formatting = false
|
||||
},
|
||||
|
||||
view: vnode => {
|
||||
return m('textarea#input', {
|
||||
rows: 1,
|
||||
onkeydown: Input.onKeyDown,
|
||||
oninput: event => Completions.reset(),
|
||||
oninput: Input.onStateChange,
|
||||
// Sadly only supported in Firefox as of writing.
|
||||
onselectionchange: event => Completions.reset(),
|
||||
onselectionchange: Input.onStateChange,
|
||||
// The list of completions is scrollable without receiving focus.
|
||||
onblur: event => Completions.reset(),
|
||||
onblur: Input.onStateChange,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue