From 36b32c5a234a99e543b51a2de3d4f2fadfbc8774 Mon Sep 17 00:00:00 2001 From: Productive2 <48047721+Productive2@users.noreply.github.com> Date: Sun, 21 Feb 2021 00:44:56 +0100 Subject: [PATCH] Added horizontal scrolling (for single/all line) --- examples/editor.nim | 1 + src/jale/editor.nim | 42 +++++++++++++++++++++++++++++++++++++----- src/jale/renderer.nim | 24 +++++++++++++----------- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/examples/editor.nim b/examples/editor.nim index d5a2918..a4fd24a 100644 --- a/examples/editor.nim +++ b/examples/editor.nim @@ -22,6 +22,7 @@ e.bindKey("ctrl+s"): e.prompt = "" e.populateDefaults(enterSubmits = false, ctrlForVerticalMove = false) +e.scrollMode = sbAllScroll let result = e.read() if save and paramCount() > 0: writeFile(paramStr(1), result) diff --git a/src/jale/editor.nim b/src/jale/editor.nim index 83311c3..27e8c4d 100644 --- a/src/jale/editor.nim +++ b/src/jale/editor.nim @@ -16,11 +16,15 @@ type EditorState = enum esOutside, esTyping, esFinishing, esQuitting + ScrollBehavior* = enum + sbSingleScroll, sbAllScroll, sbWrap + LineEditor* = ref object # permanents keystrokes*: Event[int] events*: Event[JaleEvent] prompt*: string + scrollMode*: ScrollBehavior # permanent internals: none @@ -31,6 +35,7 @@ type state: EditorState rendered: int # how many lines were printed last full refresh forceRedraw: bool + hscroll: int # getter/setter sorts @@ -61,6 +66,8 @@ proc newLineEditor*: LineEditor = result.lastKeystroke = -1 result.forceRedraw = false result.state = esOutside + result.scrollMode = sbSingleScroll + result.hscroll = 0 # priv/pub methods @@ -72,20 +79,31 @@ proc reset(editor: LineEditor) = editor.content = newMultiline() editor.lastKeystroke = -1 editor.forceRedraw = false + editor.hscroll = 0 -proc render(editor: LineEditor, line: int = -1, hscroll: bool = true) = +proc render(editor: LineEditor, line: int = -1) = + ## Assumes that the cursor is already on the right line then + ## proceeds to render the line-th line of the editor (if -1, will check + ## the y). var y = line if y == -1: y = editor.content.Y - + + # the prompt's length is assumed to be always padded let prompt = if y == 0: editor.prompt else: " ".repeat(editor.prompt.len()) - renderLine(prompt, editor.content.getLine(y), 0) + let content = editor.content.getLine(y) + + if editor.scrollMode == sbAllScroll or + (editor.scrollMode == sbSingleScroll and y == editor.content.Y): + renderLine(prompt, content, editor.hscroll) + else: + renderLine(prompt, content, 0) proc fullRender(editor: LineEditor) = # from the top cursor pos, it draws the entire multiline prompt, then # moves cursor to current y for i in countup(0, editor.content.high()): - editor.render(i, false) + editor.render(i) if i < editor.rendered: cursorDown(1) else: @@ -118,7 +136,7 @@ proc read*(editor: LineEditor): string = while editor.state == esTyping: # refresh current line every time - setCursorXPos(editor.content.X + editor.prompt.len()) + setCursorXPos(editor.content.X - editor.hscroll + editor.prompt.len()) # get key (with escapes) let key = getKey() # record y pos @@ -127,6 +145,20 @@ proc read*(editor: LineEditor): string = editor.lastKeystroke = key editor.keystrokes.call(key) editor.events.call(jeKeypress) + # autoscroll horizontally based on current scroll and x pos + + # last x rendered + let lastX = terminalWidth() - editor.prompt.len() + editor.hscroll - 1 + # first x rendered + let firstX = editor.hscroll + + # x squished into boundaries + let boundX = min(max(firstX, editor.content.X), lastX) + if editor.content.X != boundX: + editor.hscroll += editor.content.X - boundX + if editor.scrollMode == sbAllScroll: + editor.forceRedraw = true + # redraw everything if y changed if editor.forceRedraw or preY != editor.content.Y: # move to the top diff --git a/src/jale/renderer.nim b/src/jale/renderer.nim index 3d5efa1..386fd44 100644 --- a/src/jale/renderer.nim +++ b/src/jale/renderer.nim @@ -5,16 +5,18 @@ import strutils import terminal -proc renderLine*(prompt: string, content: string, hscroll: int = 0) = +proc renderLine*(prompt: string, text: string, hscroll: int = 0) = + eraseLine() setCursorXPos(0) - var content = prompt & content - if content.len() < terminalWidth(): - content &= " ".repeat(terminalWidth() - content.len() - 1) - if content.len() > terminalWidth(): - var lower = hscroll - var upper = hscroll + terminalWidth() - 1 - if upper > content.high(): - upper = content.high() - content = content[lower..upper] - write stdout, content + var lower = hscroll + var upper = hscroll + terminalWidth() - prompt.len() - 1 + if upper > text.high(): + upper = text.high() + if lower < -1: + raise newException(Defect, "negative hscroll submitted to renderLine") + if lower > text.high(): + write stdout, prompt + else: + let content = prompt & text[lower..upper] + write stdout, content