diff options
author | bptato <nincsnevem662@gmail.com> | 2022-11-28 19:52:10 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-11-28 23:00:06 +0100 |
commit | eb2e57c97eb67eec19f068e294a8f6d1375c82f5 (patch) | |
tree | 87156c515f6ee9a63f58dc080184bd3127ce6836 /src/buffer | |
parent | 8af10b8b74fd29fe4c9debcd5cbecfaddf53a7b5 (diff) | |
download | chawan-eb2e57c97eb67eec19f068e294a8f6d1375c82f5.tar.gz |
Add textarea
Editing is implemented using an external editor (like vi).
Diffstat (limited to 'src/buffer')
-rw-r--r-- | src/buffer/buffer.nim | 98 | ||||
-rw-r--r-- | src/buffer/container.nim | 49 |
2 files changed, 89 insertions, 58 deletions
diff --git a/src/buffer/buffer.nim b/src/buffer/buffer.nim index 2bc67d7c..d7061ce8 100644 --- a/src/buffer/buffer.nim +++ b/src/buffer/buffer.nim @@ -17,7 +17,7 @@ import css/cssparser import css/mediaquery import css/sheet import css/stylednode -import config/bufferconfig +import config/config import html/dom import html/tags import html/htmlparser @@ -56,7 +56,6 @@ type lasttimeout: int timeout: int readbufsize: int - input: HTMLInputElement contenttype: string lines: FlexibleGrid rendered: bool @@ -239,7 +238,7 @@ func getLink(node: StyledNode): HTMLAnchorElement = #TODO ::before links? const ClickableElements = { - TAG_A, TAG_INPUT, TAG_OPTION, TAG_BUTTON + TAG_A, TAG_INPUT, TAG_OPTION, TAG_BUTTON, TAG_TEXTAREA } func getClickable(styledNode: StyledNode): Element = @@ -267,6 +266,7 @@ func cursorBytes(buffer: Buffer, y: int, cc: int): int = return i proc findPrevLink*(buffer: Buffer, cursorx, cursory: int): tuple[x, y: int] {.proxy.} = + if cursory >= buffer.lines.len: return (-1, -1) let line = buffer.lines[cursory] var i = line.findFormatN(cursorx) - 1 var link: Element = nil @@ -324,6 +324,7 @@ proc findPrevLink*(buffer: Buffer, cursorx, cursory: int): tuple[x, y: int] {.pr return (-1, -1) proc findNextLink*(buffer: Buffer, cursorx, cursory: int): tuple[x, y: int] {.proxy.} = + if cursory >= buffer.lines.len: return (-1, -1) let line = buffer.lines[cursory] var i = line.findFormatN(cursorx) - 1 var link: Element = nil @@ -350,6 +351,7 @@ proc findNextLink*(buffer: Buffer, cursorx, cursory: int): tuple[x, y: int] {.pr return (-1, -1) proc findPrevMatch*(buffer: Buffer, regex: Regex, cursorx, cursory: int, wrap: bool): BufferMatch {.proxy.} = + if cursory >= buffer.lines.len: return template return_if_match = if res.success and res.captures.len > 0: let cap = res.captures[^1] @@ -377,6 +379,7 @@ proc findPrevMatch*(buffer: Buffer, regex: Regex, cursorx, cursory: int, wrap: b dec y proc findNextMatch*(buffer: Buffer, regex: Regex, cursorx, cursory: int, wrap: bool): BufferMatch {.proxy.} = + if cursory >= buffer.lines.len: return template return_if_match = if res.success and res.captures.len > 0: let cap = res.captures[0] @@ -471,6 +474,7 @@ proc updateHover*(buffer: Buffer, cursorx, cursory: int): UpdateHoverResult {.pr buffer.prevnode = thisnode proc loadResource(buffer: Buffer, document: Document, elem: HTMLLinkElement) = + if elem.href == "": return let url = parseUrl(elem.href, document.location.some) if url.isSome: let url = url.get @@ -514,8 +518,7 @@ proc setupSource(buffer: Buffer): ConnectResult = buffer.location = source.location case source.t of CLONE: - buffer.istream = connectSocketStream(source.clonepid) - SocketStream(buffer.istream).source.getFd().setBlocking(false) + buffer.istream = connectSocketStream(source.clonepid, blocking = false) if buffer.istream == nil: result.code = -2 return @@ -574,7 +577,7 @@ proc load*(buffer: Buffer): tuple[atend: bool, lines, bytes: int] {.proxy.} = var s = newString(buffer.readbufsize) try: buffer.istream.readStr(buffer.readbufsize, s) - result = (s.len < buffer.readbufsize, buffer.lines.len, bytes) + result = (s.len == 0, buffer.lines.len, bytes) if buffer.readbufsize < BufferSize: buffer.readbufsize = min(BufferSize, buffer.readbufsize * 2) except IOError: @@ -671,12 +674,14 @@ proc constructEntryList(form: HTMLFormElement, submitter: Element = nil, encodin "UTF-8" entrylist.add((name, charset)) else: - if field.tagType == TAG_INPUT: + case field.tagType + of TAG_INPUT: entrylist.add((name, HTMLInputElement(field).value)) - elif field.tagType == TAG_BUTTON: + of TAG_BUTTON: entrylist.add((name, HTMLButtonElement(field).value)) - else: - assert false + of TAG_TEXTAREA: + entrylist.add((name, HTMLTextAreaElement(field).value)) + else: assert false, "Tag type " & $field.tagType & " not accounted for in constructEntryList" if field.tagType == TAG_TEXTAREA or field.tagType == TAG_INPUT and HTMLInputElement(field).inputType in {INPUT_TEXT, INPUT_SEARCH}: if field.attr("dirname") != "": @@ -803,39 +808,46 @@ type ReadSuccessResult* = object open*: Option[Request] repaint*: bool +proc implicitSubmit(buffer: Buffer, input: HTMLInputElement): Option[Request] = + if input.form != nil and input.form.canSubmitImplicitly(): + return submitForm(input.form, input.form) + proc readSuccess*(buffer: Buffer, s: string): ReadSuccessResult {.proxy.} = - if buffer.input != nil: - let input = buffer.input - case input.inputType - of INPUT_SEARCH: - input.value = s - input.invalid = true - buffer.do_reshape() - result.repaint = true - if input.form != nil: - let submitaction = submitForm(input.form, input) - if submitaction.isSome: - result.open = submitaction - of INPUT_TEXT, INPUT_PASSWORD: - input.value = s - input.invalid = true - buffer.do_reshape() - result.repaint = true - of INPUT_FILE: - let cdir = parseUrl("file://" & getCurrentDir() & DirSep) - let path = parseUrl(s, cdir) - if path.issome: - input.file = path + if buffer.document.focus != nil: + case buffer.document.focus.tagType + of TAG_INPUT: + let input = HTMLInputElement(buffer.document.focus) + case input.inputType + of INPUT_SEARCH, INPUT_TEXT, INPUT_PASSWORD: + input.value = s input.invalid = true buffer.do_reshape() result.repaint = true + result.open = buffer.implicitSubmit(input) + of INPUT_FILE: + let cdir = parseUrl("file://" & getCurrentDir() & DirSep) + let path = parseUrl(s, cdir) + if path.issome: + input.file = path + input.invalid = true + buffer.do_reshape() + result.repaint = true + result.open = buffer.implicitSubmit(input) + else: discard + of TAG_TEXTAREA: + let textarea = HTMLTextAreaElement(buffer.document.focus) + textarea.value = s + textarea.invalid = true + buffer.do_reshape() + result.repaint = true else: discard - buffer.input = nil + buffer.restore_focus type ReadLineResult* = object prompt*: string value*: string hide*: bool + area*: bool type ClickResult* = object open*: Option[Request] @@ -848,6 +860,7 @@ proc click*(buffer: Buffer, cursorx, cursory: int): ClickResult {.proxy.} = case clickable.tagType of TAG_SELECT: buffer.set_focus clickable + result.repaint = true of TAG_A: buffer.restore_focus let url = parseUrl(HTMLAnchorElement(clickable).href, clickable.document.baseUrl.some) @@ -877,24 +890,32 @@ proc click*(buffer: Buffer, cursorx, cursory: int): ClickResult {.proxy.} = result.repaint = true buffer.do_reshape() of BUTTON_BUTTON: discard + of TAG_TEXTAREA: + buffer.set_focus clickable + let textarea = HTMLTextAreaElement(clickable) + result.readline = some(ReadLineResult( + value: textarea.value, + area: true + )) of TAG_INPUT: buffer.restore_focus let input = HTMLInputElement(clickable) case input.inputType of INPUT_SEARCH: - buffer.input = input + buffer.set_focus input result.readline = some(ReadLineResult( prompt: "SEARCH: ", value: input.value )) of INPUT_TEXT, INPUT_PASSWORD: - buffer.input = input + buffer.set_focus input result.readline = some(ReadLineResult( prompt: "TEXT: ", value: input.value, hide: input.inputType == INPUT_PASSWORD )) of INPUT_FILE: + buffer.set_focus input var path = if input.file.issome: input.file.get.path.serialize_unicode() else: @@ -930,12 +951,12 @@ proc click*(buffer: Buffer, cursorx, cursory: int): ClickResult {.proxy.} = buffer.restore_focus proc readCanceled*(buffer: Buffer) {.proxy.} = - buffer.input = nil + buffer.restore_focus proc findAnchor*(buffer: Buffer, anchor: string): bool {.proxy.} = return buffer.document != nil and buffer.document.getElementById(anchor) != nil -proc getLines*(buffer: Buffer, w: Slice[int]): seq[SimpleFlexibleLine] {.proxy.} = +proc getLines*(buffer: Buffer, w: Slice[int]): tuple[numLines: int, lines: seq[SimpleFlexibleLine]] {.proxy.} = var w = w if w.b < 0 or w.b > buffer.lines.high: w.b = buffer.lines.high @@ -944,7 +965,8 @@ proc getLines*(buffer: Buffer, w: Slice[int]): seq[SimpleFlexibleLine] {.proxy.} var line = SimpleFlexibleLine(str: buffer.lines[y].str) for f in buffer.lines[y].formats: line.formats.add(SimpleFormatCell(format: f.format, pos: f.pos)) - result.add(line) + result.lines.add(line) + result.numLines = buffer.lines.len proc passFd*(buffer: Buffer) {.proxy.} = let fd = SocketStream(buffer.pistream).recvFileHandle() diff --git a/src/buffer/container.nim b/src/buffer/container.nim index 65b1ae59..9a0ff802 100644 --- a/src/buffer/container.nim +++ b/src/buffer/container.nim @@ -8,7 +8,6 @@ when defined(posix): import buffer/buffer import buffer/cell -import config/bufferconfig import config/config import io/request import io/window @@ -33,7 +32,7 @@ type ContainerEventType* = enum NO_EVENT, FAIL, SUCCESS, NEEDS_AUTH, REDIRECT, ANCHOR, NO_ANCHOR, UPDATE, - READ_LINE, OPEN, INVALID_COMMAND, STATUS, ALERT + READ_LINE, READ_AREA, OPEN, INVALID_COMMAND, STATUS, ALERT ContainerEvent* = object case t*: ContainerEventType @@ -41,6 +40,8 @@ type prompt*: string value*: string password*: bool + of READ_AREA: + tvalue*: string of OPEN: request*: Request of ANCHOR, NO_ANCHOR: @@ -71,7 +72,7 @@ type bpos: seq[CursorPosition] highlights: seq[Highlight] parent*: Container - process*: Pid + process* {.jsget.}: Pid loadinfo*: string lines: SimpleFlexibleGrid lineshift: int @@ -93,7 +94,7 @@ proc newBuffer*(dispatcher: Dispatcher, config: Config, source: BufferSource, ti let istream = dispatcher.forkserver.istream ostream.swrite(FORK_BUFFER) ostream.swrite(source) - ostream.swrite(config.loadBufferConfig()) + ostream.swrite(config.getBufferConfig()) ostream.swrite(attrs) ostream.swrite(dispatcher.mainproc) ostream.flush() @@ -252,13 +253,17 @@ proc triggerEvent(container: Container, t: ContainerEventType) = proc updateCursor(container: Container) +proc setNumLines(container: Container, lines: int) = + container.numLines = lines + container.updateCursor() + proc requestLines*(container: Container, w = container.lineWindow) = - container.iface.getLines(w).then(proc(res: seq[SimpleFlexibleLine]) = + container.iface.getLines(w).then(proc(res: tuple[numLines: int, lines: seq[SimpleFlexibleLine]]) = container.lines.setLen(w.len) container.lineshift = w.a - for y in 0 ..< min(res.len, w.len): - container.lines[y] = res[y] - container.updateCursor() + for y in 0 ..< min(res.lines.len, w.len): + container.lines[y] = res.lines[y] + container.setNumLines(res.numLines) container.redraw = true let cw = container.fromy ..< container.fromy + container.height if w.a in cw or w.b in cw or cw.a in w or cw.b in w: @@ -544,8 +549,8 @@ proc cursorNextMatch*(container: Container, regex: Regex, wrap: bool) {.jsfunc.} container.iface .findNextMatch(regex, container.cursorx, container.cursory, wrap) .then(proc(res: BufferMatch) = + container.setCursorXY(res.x, res.y) if container.hlon: - container.setCursorXY(res.x, res.y) container.clearSearchHighlights() let ex = res.x + res.str.width() - 1 let hl = Highlight(x: res.x, y: res.y, endx: ex, endy: res.y, clear: true) @@ -556,8 +561,8 @@ proc cursorPrevMatch*(container: Container, regex: Regex, wrap: bool) {.jsfunc.} container.iface .findPrevMatch(regex, container.cursorx, container.cursory, wrap) .then(proc(res: BufferMatch) = + container.setCursorXY(res.x, res.y) if container.hlon: - container.setCursorXY(res.x, res.y) container.clearSearchHighlights() let ex = res.x + res.str.width() - 1 let hl = Highlight(x: res.x, y: res.y, endx: ex, endy: res.y, clear: true) @@ -568,10 +573,6 @@ proc setLoadInfo(container: Container, msg: string) = container.loadinfo = msg container.triggerEvent(STATUS) -proc setNumLines(container: Container, lines: int) = - container.numLines = lines - container.updateCursor() - proc alert(container: Container, msg: string) = container.triggerEvent(ContainerEvent(t: ALERT, msg: msg)) @@ -677,12 +678,20 @@ proc click*(container: Container) {.jsfunc.} = container.triggerEvent(ContainerEvent(t: OPEN, request: res.open.get)) if res.readline.isSome: let rl = res.readline.get - container.triggerEvent( - ContainerEvent( - t: READ_LINE, - prompt: rl.prompt, - value: rl.value, - password: rl.hide))) + if rl.area: + container.triggerEvent( + ContainerEvent( + t: READ_AREA, + tvalue: rl.value + )) + else: + container.triggerEvent( + ContainerEvent( + t: READ_LINE, + prompt: rl.prompt, + value: rl.value, + password: rl.hide + ))) proc windowChange*(container: Container, attrs: WindowAttributes) = container.attrs = attrs |