diff options
-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) |