diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-24 19:42:29 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-24 19:43:30 +0200 |
commit | 0c738c94e14c213562f69ff6e376c19fb0487201 (patch) | |
tree | 0d61bc0651f9120bbc7245e774dab04701a3d16e | |
parent | b42cb50ee0213deb568b6c92965e7e193122c963 (diff) | |
download | chawan-0c738c94e14c213562f69ff6e376c19fb0487201.tar.gz |
select: use a separate module (again)
Now we dispatch to select objects from the pager object too, just to make things even more confusing. Well, it works better than the previous arrangement, in that trying to use unimplemented movements now just throws instead of moving around the container. Yay for OOP (?)
-rw-r--r-- | src/local/client.nim | 2 | ||||
-rw-r--r-- | src/local/container.nim | 402 | ||||
-rw-r--r-- | src/local/pager.nim | 6 | ||||
-rw-r--r-- | src/local/select.nim | 315 |
4 files changed, 374 insertions, 351 deletions
diff --git a/src/local/client.nim b/src/local/client.nim index f32e758d..33bb44e3 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -34,6 +34,7 @@ import loader/response import local/container import local/lineedit import local/pager +import local/select import local/term import monoucha/constcharp import monoucha/fromjs @@ -820,6 +821,7 @@ proc addJSModules(client: Client; ctx: JSContext) = ctx.addConfigModule() ctx.addPagerModule() ctx.addContainerModule() + ctx.addSelectModule() ctx.addCookieModule() func getClient(client: Client): Client {.jsfget: "client".} = diff --git a/src/local/container.nim b/src/local/container.nim index 81f5ffb0..6bdfe64b 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -15,6 +15,7 @@ import loader/headers import loader/loaderiface import loader/request import loader/response +import local/select import monoucha/javascript import monoucha/jsregex import monoucha/jstypes @@ -122,7 +123,7 @@ type dispw*: int # same as CanvasImage.dispw erry*: int # same as CanvasImage.offy % 6 - Container* = ref object + Container* = ref object of RootObj # note: this is not the same as source.request.url (but should be synced # with buffer.url) url* {.jsget.}: URL @@ -173,7 +174,7 @@ type events*: Deque[ContainerEvent] startpos: Option[CursorPosition] redirectDepth*: int - select*: Select + select* {.jsget.}: Select currentSelection {.jsget.}: Highlight tmpJumpMark: PagePos jumpMark: PagePos @@ -188,23 +189,6 @@ type luctx: LUContext redraw*: bool - Select = ref object - container: Container - options: seq[string] - multiple: bool - oselected: seq[int] # old selection - selected: seq[int] # new selection - cursor: int # cursor distance from y - maxw: int # widest option - maxh: int # maximum height on screen (yes the naming is dumb) - si: int # first index to display - # location on screen - #TODO make this absolute - x: int - y: int - redraw*: bool - bpos: seq[int] - jsDestructor(Highlight) jsDestructor(Container) @@ -214,275 +198,6 @@ proc updateCursor(container: Container) proc cursorLastLine*(container: Container) proc triggerEvent(container: Container; t: ContainerEventType) -proc queueDraw(select: Select) = - select.redraw = true - -proc windowChange(select: Select; height: int) = - select.maxh = height - 2 - if select.y + select.options.len >= select.maxh: - select.y = height - select.options.len - if select.y < 0: - select.si = -select.y - select.y = 0 - if select.selected.len > 0: - let i = select.selected[0] - if select.si > i: - select.si = i - elif select.si + select.maxh < i: - select.si = max(i - select.maxh, 0) - select.queueDraw() - -# index of option currently under cursor -func hover(select: Select): int = - return select.cursor + select.si - -func dispheight(select: Select): int = - return select.maxh - select.y - -proc `hover=`(select: Select; i: int) = - let i = clamp(i, 0, select.options.high) - if i >= select.si + select.dispheight: - select.si = i - select.dispheight + 1 - select.cursor = select.dispheight - 1 - elif i < select.si: - select.si = i - select.cursor = 0 - else: - select.cursor = i - select.si - -proc cursorDown(select: Select) = - if select.hover < select.options.high and - select.cursor + select.y < select.maxh - 1: - inc select.cursor - select.queueDraw() - elif select.si < select.options.len - select.maxh: - inc select.si - select.queueDraw() - -proc cursorUp(select: Select) = - if select.cursor > 0: - dec select.cursor - select.queueDraw() - elif select.si > 0: - dec select.si - select.queueDraw() - elif select.multiple and select.cursor > -1: - select.cursor = -1 - -proc close(select: Select) = - let container = select.container - container.select = nil - -proc cancel(select: Select) = - let container = select.container - container.iface.select(select.oselected).then(proc(res: ClickResult) = - container.onclick(res, save = false)) - select.close() - -proc submit(select: Select) = - let container = select.container - container.iface.select(select.selected).then(proc(res: ClickResult) = - container.onclick(res, save = false)) - select.close() - -proc click(select: Select) = - if not select.multiple: - select.selected = @[select.hover] - select.submit() - elif select.cursor == -1: - select.submit() - else: - var k = select.selected.len - let i = select.hover - for j in 0 ..< select.selected.len: - if select.selected[j] >= i: - k = j - break - if k < select.selected.len and select.selected[k] == i: - select.selected.delete(k) - else: - select.selected.insert(i, k) - select.queueDraw() - -proc cursorLeft(select: Select) = - select.submit() - -proc cursorRight(select: Select) = - select.click() - -proc getCursorX*(select: Select): int = - if select.cursor == -1: - return select.x - return select.x + 1 - -proc getCursorY*(select: Select): int = - return select.y + 1 + select.cursor - -proc cursorFirstLine(select: Select) = - if select.cursor != 0 or select.si != 0: - select.cursor = 0 - select.si = 0 - select.queueDraw() - -proc cursorLastLine(select: Select) = - if select.hover < select.options.len: - select.cursor = select.dispheight - 1 - select.si = max(select.options.len - select.maxh, 0) - select.queueDraw() - -proc cursorNextMatch(select: Select; regex: Regex; wrap: bool) = - var j = -1 - for i in select.hover + 1 ..< select.options.len: - if regex.exec(select.options[i]).success: - j = i - break - if j != -1: - select.hover = j - select.queueDraw() - elif wrap: - for i in 0 ..< select.hover: - if regex.exec(select.options[i]).success: - j = i - break - if j != -1: - select.hover = j - select.queueDraw() - -proc cursorPrevMatch(select: Select; regex: Regex; wrap: bool) = - var j = -1 - for i in countdown(select.hover - 1, 0): - if regex.exec(select.options[i]).success: - j = i - break - if j != -1: - select.hover = j - select.queueDraw() - elif wrap: - for i in countdown(select.options.high, select.hover): - if regex.exec(select.options[i]).success: - j = i - break - if j != -1: - select.hover = j - select.queueDraw() - -proc pushCursorPos(select: Select) = - select.bpos.add(select.hover) - -proc popCursorPos(select: Select; nojump = false) = - select.hover = select.bpos.pop() - if not nojump: - select.queueDraw() - -const HorizontalBar = "\u2500" -const VerticalBar = "\u2502" -const CornerTopLeft = "\u250C" -const CornerTopRight = "\u2510" -const CornerBottomLeft = "\u2514" -const CornerBottomRight = "\u2518" - -proc drawBorders(display: var FixedGrid; sx, ex, sy, ey: int; - upmore, downmore: bool) = - for y in sy .. ey: - var x = 0 - while x < sx: - if display[y * display.width + x].str == "": - display[y * display.width + x].str = " " - inc x - else: - #x = display[y * display.width + x].str.twidth(x) - inc x - # Draw corners. - let tl = if upmore: VerticalBar else: CornerTopLeft - let tr = if upmore: VerticalBar else: CornerTopRight - let bl = if downmore: VerticalBar else: CornerBottomLeft - let br = if downmore: VerticalBar else: CornerBottomRight - const fmt = Format() - display[sy * display.width + sx].str = tl - display[sy * display.width + ex].str = tr - display[ey * display.width + sx].str = bl - display[ey * display.width + ex].str = br - display[sy * display.width + sx].format = fmt - display[sy * display.width + ex].format = fmt - display[ey * display.width + sx].format = fmt - display[ey * display.width + ex].format = fmt - # Draw top, bottom borders. - let ups = if upmore: " " else: HorizontalBar - let downs = if downmore: " " else: HorizontalBar - for x in sx + 1 .. ex - 1: - display[sy * display.width + x].str = ups - display[ey * display.width + x].str = downs - display[sy * display.width + x].format = fmt - display[ey * display.width + x].format = fmt - if upmore: - display[sy * display.width + sx + (ex - sx) div 2].str = ":" - if downmore: - display[ey * display.width + sx + (ex - sx) div 2].str = ":" - # Draw left, right borders. - for y in sy + 1 .. ey - 1: - display[y * display.width + sx].str = VerticalBar - display[y * display.width + ex].str = VerticalBar - display[y * display.width + sx].format = fmt - display[y * display.width + ex].format = fmt - -proc drawSelect*(select: Select; display: var FixedGrid) = - if display.width < 2 or display.height < 2: - return # border does not fit... - # Max width, height with one row/column on the sides. - let mw = display.width - 2 - let mh = display.height - 2 - var sy = select.y - let si = select.si - var ey = min(sy + select.options.len, mh) + 1 - var sx = select.x - if sx + select.maxw >= mw: - sx = display.width - select.maxw - if sx < 0: - # This means the widest option is wider than the available screen. - # w3m simply cuts off the part that doesn't fit, and we do that too, - # but I feel like this may not be the best solution. - sx = 0 - var ex = min(sx + select.maxw, mw) + 1 - let upmore = select.si > 0 - let downmore = select.si + mh < select.options.len - drawBorders(display, sx, ex, sy, ey, upmore, downmore) - if select.multiple and not upmore: - display[sy * display.width + sx].str = "X" - # move inside border - inc sy - inc sx - var k = 0 - var format = Format() - while k < select.selected.len and select.selected[k] < si: - inc k - for y in sy ..< ey: - let i = y - sy + si - var j = 0 - var x = sx - let dls = y * display.width - if k < select.selected.len and select.selected[k] == i: - format.flags.incl(ffReverse) - inc k - else: - format.flags.excl(ffReverse) - while j < select.options[i].len: - let pj = j - let u = select.options[i].nextUTF8(j) - let nx = x + u.twidth(x) - if nx > ex: - break - display[dls + x].str = select.options[i].substr(pj, j - 1) - display[dls + x].format = format - inc x - while x < nx: - display[dls + x].str = "" - display[dls + x].format = format - inc x - while x < ex: - display[dls + x].str = " " - display[dls + x].format = format - inc x - proc newContainer*(config: BufferConfig; loaderConfig: LoaderClientConfig; url: URL; request: Request; luctx: LUContext; attrs: WindowAttributes; title: string; redirectDepth: int; flags: set[ContainerFlag]; @@ -1000,28 +715,16 @@ proc setCursorXYCenter(container: Container; x, y: int; refresh = true) container.centerColumn() proc cursorDown(container: Container; n = 1) {.jsfunc.} = - if container.select != nil: - container.select.cursorDown() - else: - container.setCursorY(container.cursory + n) + container.setCursorY(container.cursory + n) proc cursorUp(container: Container; n = 1) {.jsfunc.} = - if container.select != nil: - container.select.cursorUp() - else: - container.setCursorY(container.cursory - n) + container.setCursorY(container.cursory - n) proc cursorLeft(container: Container; n = 1) {.jsfunc.} = - if container.select != nil: - container.select.cursorLeft() - else: - container.setCursorX(container.cursorFirstX() - n) + container.setCursorX(container.cursorFirstX() - n) proc cursorRight(container: Container; n = 1) {.jsfunc.} = - if container.select != nil: - container.select.cursorRight() - else: - container.setCursorX(container.cursorLastX() + n) + container.setCursorX(container.cursorLastX() + n) proc cursorLineBegin(container: Container) {.jsfunc.} = container.setCursorX(-1) @@ -1275,20 +978,14 @@ proc markPos*(container: Container) = container.jumpMark = pos proc cursorFirstLine(container: Container) {.jsfunc.} = - if container.select != nil: - container.select.cursorFirstLine() - else: - container.markPos0() - container.setCursorY(0) - container.markPos() + container.markPos0() + container.setCursorY(0) + container.markPos() proc cursorLastLine*(container: Container) {.jsfunc.} = - if container.select != nil: - container.select.cursorLastLine() - else: - container.markPos0() - container.setCursorY(container.numLines - 1) - container.markPos() + container.markPos0() + container.setCursorY(container.numLines - 1) + container.markPos() proc cursorTop(container: Container; i = 1) {.jsfunc.} = container.markPos0() @@ -1815,9 +1512,7 @@ proc remoteCancel*(container: Container) = container.alert("Canceled loading") proc cancel*(container: Container) {.jsfunc.} = - if container.select != nil: - container.select.cancel() - elif container.loadState == lsLoading: + if container.loadState == lsLoading: container.loadState = lsCanceled if container.iface != nil: container.remoteCancel() @@ -1856,21 +1551,30 @@ proc reshape(container: Container): EmptyPromise {.jsfunc.} = return container.requestLines() ) +proc selectFinish(opaque: RootRef; select: Select; res: SubmitResult) = + let container = Container(opaque) + case res + of srCancel: + container.iface.select(select.oselected).then(proc(res: ClickResult) = + container.onclick(res, save = false) + ) + of srSubmit: + container.iface.select(select.selected).then(proc(res: ClickResult) = + container.onclick(res, save = false) + ) + container.select = nil + proc displaySelect(container: Container; selectResult: SelectResult) = - container.select = Select( - container: container, - multiple: selectResult.multiple, - options: selectResult.options, - oselected: selectResult.selected, - selected: selectResult.selected, - x: container.acursorx, - y: container.acursory + container.select = newSelect( + selectResult.multiple, + selectResult.options, + selectResult.selected, + container.acursorx, + container.acursory, + container.height, + selectFinish, + container ) - for opt in container.select.options.mitems: - opt.mnormalize() - container.select.maxw = max(container.select.maxw, opt.width()) - container.select.windowChange(container.height) - container.select.queueDraw() proc onclick(container: Container; res: ClickResult; save: bool) = if res.repaint: @@ -1887,13 +1591,10 @@ proc onclick(container: Container; res: ClickResult; save: bool) = container.onReadLine(res.readline.get) proc click*(container: Container) {.jsfunc.} = - if container.select != nil: - container.select.click() - else: - if container.iface == nil: - return - container.iface.click(container.cursorx, container.cursory) - .then(proc(res: ClickResult) = container.onclick(res, save = false)) + if container.iface == nil: + return + container.iface.click(container.cursorx, container.cursory) + .then(proc(res: ClickResult) = container.onclick(res, save = false)) proc saveLink*(container: Container) {.jsfunc.} = if container.iface == nil: @@ -1912,17 +1613,18 @@ proc saveSource*(container: Container) {.jsfunc.} = )) proc windowChange*(container: Container; attrs: WindowAttributes) = - if attrs.width != container.width or attrs.height - 1 != container.height: - container.width = attrs.width - container.height = attrs.height - 1 - if container.iface != nil: - var attrs = attrs - # subtract status line height - attrs.height -= 1 - attrs.heightPx -= attrs.ppl - container.iface.windowChange(attrs).then(proc() = - container.needslines = true - ) + container.width = attrs.width + container.height = attrs.height - 1 + if container.iface != nil: + var attrs = attrs + # subtract status line height + attrs.height -= 1 + attrs.heightPx -= attrs.ppl + container.iface.windowChange(attrs).then(proc() = + container.needslines = true + ) + if container.select != nil: + container.select.windowChange(container.height) proc peek(container: Container) {.jsfunc.} = container.alert($container.url) diff --git a/src/local/pager.nim b/src/local/pager.nim index 3e0c21a0..ec242472 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -27,6 +27,7 @@ import loader/request import loader/response import local/container import local/lineedit +import local/select import local/term import monoucha/fromjs import monoucha/javascript @@ -231,7 +232,10 @@ proc reflect(ctx: JSContext; this_val: JSValue; argc: cint; proc getter(ctx: JSContext; pager: Pager; a: JSAtom): JSValue {.jsgetprop.} = if pager.container != nil: - let cval = ctx.toJS(pager.container) + let cval = if pager.container.select != nil: + ctx.toJS(pager.container.select) + else: + ctx.toJS(pager.container) let val = JS_GetProperty(ctx, cval, a) if JS_IsFunction(ctx, val): let func_data = @[cval, val] diff --git a/src/local/select.nim b/src/local/select.nim new file mode 100644 index 00000000..70726e7c --- /dev/null +++ b/src/local/select.nim @@ -0,0 +1,315 @@ +import monoucha/javascript +import monoucha/jsregex +import types/cell +import utils/luwrap +import utils/strwidth +import utils/twtuni + +type + SelectFinish* = proc(opaque: RootRef; select: Select; res: SubmitResult) + {.nimcall.} + + Select* = ref object + options: seq[string] + multiple: bool + oselected*: seq[int] # old selection + selected*: seq[int] # new selection + cursor: int # cursor distance from y + maxw: int # widest option + maxh: int # maximum height on screen (yes the naming is dumb) + si: int # first index to display + # location on screen + #TODO make this absolute + x: int + y: int + redraw*: bool + bpos: seq[int] + opaque: RootRef + finishImpl: SelectFinish + + SubmitResult* = enum + srCancel, srSubmit + +jsDestructor(Select) + +proc queueDraw(select: Select) = + select.redraw = true + +# index of option currently under cursor +func hover(select: Select): int = + return select.cursor + select.si + +func dispheight(select: Select): int = + return select.maxh - select.y + +proc `hover=`(select: Select; i: int) = + let i = clamp(i, 0, select.options.high) + if i >= select.si + select.dispheight: + select.si = i - select.dispheight + 1 + select.cursor = select.dispheight - 1 + elif i < select.si: + select.si = i + select.cursor = 0 + else: + select.cursor = i - select.si + +proc cursorDown(select: Select) {.jsfunc.} = + if select.hover < select.options.high and + select.cursor + select.y < select.maxh - 1: + inc select.cursor + select.queueDraw() + elif select.si < select.options.len - select.maxh: + inc select.si + select.queueDraw() + +proc cursorUp(select: Select) {.jsfunc.} = + if select.cursor > 0: + dec select.cursor + select.queueDraw() + elif select.si > 0: + dec select.si + select.queueDraw() + elif select.multiple and select.cursor > -1: + select.cursor = -1 + +proc cancel(select: Select) {.jsfunc.} = + select.finishImpl(select.opaque, select, srCancel) + +proc submit(select: Select) {.jsfunc.} = + select.finishImpl(select.opaque, select, srSubmit) + +proc click(select: Select) {.jsfunc.} = + if not select.multiple: + select.selected = @[select.hover] + select.submit() + elif select.cursor == -1: + select.submit() + else: + var k = select.selected.len + let i = select.hover + for j in 0 ..< select.selected.len: + if select.selected[j] >= i: + k = j + break + if k < select.selected.len and select.selected[k] == i: + select.selected.delete(k) + else: + select.selected.insert(i, k) + select.queueDraw() + +proc cursorLeft(select: Select) {.jsfunc.} = + select.submit() + +proc cursorRight(select: Select) {.jsfunc.} = + select.click() + +proc getCursorX*(select: Select): int = + if select.cursor == -1: + return select.x + return select.x + 1 + +proc getCursorY*(select: Select): int = + return select.y + 1 + select.cursor + +proc cursorFirstLine(select: Select) {.jsfunc.} = + if select.cursor != 0 or select.si != 0: + select.cursor = 0 + select.si = 0 + select.queueDraw() + +proc cursorLastLine(select: Select) {.jsfunc.} = + if select.hover < select.options.len: + select.cursor = select.dispheight - 1 + select.si = max(select.options.len - select.maxh, 0) + select.queueDraw() + +proc cursorNextMatch*(select: Select; regex: Regex; wrap: bool) = + var j = -1 + for i in select.hover + 1 ..< select.options.len: + if regex.exec(select.options[i]).success: + j = i + break + if j != -1: + select.hover = j + select.queueDraw() + elif wrap: + for i in 0 ..< select.hover: + if regex.exec(select.options[i]).success: + j = i + break + if j != -1: + select.hover = j + select.queueDraw() + +proc cursorPrevMatch*(select: Select; regex: Regex; wrap: bool) = + var j = -1 + for i in countdown(select.hover - 1, 0): + if regex.exec(select.options[i]).success: + j = i + break + if j != -1: + select.hover = j + select.queueDraw() + elif wrap: + for i in countdown(select.options.high, select.hover): + if regex.exec(select.options[i]).success: + j = i + break + if j != -1: + select.hover = j + select.queueDraw() + +proc pushCursorPos*(select: Select) = + select.bpos.add(select.hover) + +proc popCursorPos*(select: Select; nojump = false) = + select.hover = select.bpos.pop() + if not nojump: + select.queueDraw() + +const HorizontalBar = "\u2500" +const VerticalBar = "\u2502" +const CornerTopLeft = "\u250C" +const CornerTopRight = "\u2510" +const CornerBottomLeft = "\u2514" +const CornerBottomRight = "\u2518" + +proc drawBorders(display: var FixedGrid; sx, ex, sy, ey: int; + upmore, downmore: bool) = + for y in sy .. ey: + var x = 0 + while x < sx: + if display[y * display.width + x].str == "": + display[y * display.width + x].str = " " + inc x + else: + #x = display[y * display.width + x].str.twidth(x) + inc x + # Draw corners. + let tl = if upmore: VerticalBar else: CornerTopLeft + let tr = if upmore: VerticalBar else: CornerTopRight + let bl = if downmore: VerticalBar else: CornerBottomLeft + let br = if downmore: VerticalBar else: CornerBottomRight + const fmt = Format() + display[sy * display.width + sx].str = tl + display[sy * display.width + ex].str = tr + display[ey * display.width + sx].str = bl + display[ey * display.width + ex].str = br + display[sy * display.width + sx].format = fmt + display[sy * display.width + ex].format = fmt + display[ey * display.width + sx].format = fmt + display[ey * display.width + ex].format = fmt + # Draw top, bottom borders. + let ups = if upmore: " " else: HorizontalBar + let downs = if downmore: " " else: HorizontalBar + for x in sx + 1 .. ex - 1: + display[sy * display.width + x].str = ups + display[ey * display.width + x].str = downs + display[sy * display.width + x].format = fmt + display[ey * display.width + x].format = fmt + if upmore: + display[sy * display.width + sx + (ex - sx) div 2].str = ":" + if downmore: + display[ey * display.width + sx + (ex - sx) div 2].str = ":" + # Draw left, right borders. + for y in sy + 1 .. ey - 1: + display[y * display.width + sx].str = VerticalBar + display[y * display.width + ex].str = VerticalBar + display[y * display.width + sx].format = fmt + display[y * display.width + ex].format = fmt + +proc drawSelect*(select: Select; display: var FixedGrid) = + if display.width < 2 or display.height < 2: + return # border does not fit... + # Max width, height with one row/column on the sides. + let mw = display.width - 2 + let mh = display.height - 2 + var sy = select.y + let si = select.si + var ey = min(sy + select.options.len, mh) + 1 + var sx = select.x + if sx + select.maxw >= mw: + sx = display.width - select.maxw + if sx < 0: + # This means the widest option is wider than the available screen. + # w3m simply cuts off the part that doesn't fit, and we do that too, + # but I feel like this may not be the best solution. + sx = 0 + var ex = min(sx + select.maxw, mw) + 1 + let upmore = select.si > 0 + let downmore = select.si + mh < select.options.len + drawBorders(display, sx, ex, sy, ey, upmore, downmore) + if select.multiple and not upmore: + display[sy * display.width + sx].str = "X" + # move inside border + inc sy + inc sx + var k = 0 + var format = Format() + while k < select.selected.len and select.selected[k] < si: + inc k + for y in sy ..< ey: + let i = y - sy + si + var j = 0 + var x = sx + let dls = y * display.width + if k < select.selected.len and select.selected[k] == i: + format.flags.incl(ffReverse) + inc k + else: + format.flags.excl(ffReverse) + while j < select.options[i].len: + let pj = j + let u = select.options[i].nextUTF8(j) + let nx = x + u.twidth(x) + if nx > ex: + break + display[dls + x].str = select.options[i].substr(pj, j - 1) + display[dls + x].format = format + inc x + while x < nx: + display[dls + x].str = "" + display[dls + x].format = format + inc x + while x < ex: + display[dls + x].str = " " + display[dls + x].format = format + inc x + +proc windowChange*(select: Select; height: int) = + select.maxh = height - 2 + if select.y + select.options.len >= select.maxh: + select.y = height - select.options.len + if select.y < 0: + select.si = -select.y + select.y = 0 + if select.selected.len > 0: + let i = select.selected[0] + if select.si > i: + select.si = i + elif select.si + select.maxh < i: + select.si = max(i - select.maxh, 0) + select.queueDraw() + +proc newSelect*(multiple: bool; options: seq[string]; selected: seq[int]; + x, y, height: int; finishImpl: SelectFinish; opaque: RootRef): Select = + let select = Select( + multiple: multiple, + options: options, + oselected: selected, + selected: selected, + x: x, + y: y, + finishImpl: finishImpl, + opaque: opaque + ) + select.windowChange(height) + for opt in select.options.mitems: + opt.mnormalize() + select.maxw = max(select.maxw, opt.width()) + select.windowChange(height) + select.queueDraw() + return select + +proc addSelectModule*(ctx: JSContext) = + ctx.registerType(Select) |