diff --git a/.gitmodules b/.gitmodules index de3396a..6ca336f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "ui/libui"] path = ui/libui - url = https://github.com/ba0f3/libui.git + url = https://github.com/libui-ng/libui-ng branch = master diff --git a/ui.nim b/ui.nim index 5eb0d0a..e11cb0d 100644 --- a/ui.nim +++ b/ui.nim @@ -1,6 +1,8 @@ import ui/rawui +export rawui.Align + type Widget* = ref object of RootRef ## abstract Widget base class. internalImpl*: pointer @@ -16,21 +18,44 @@ proc init*() = freeInitError(err) raise newException(ValueError, msg) +proc uninit*() = + rawui.uninit() + proc quit*() = rawui.quit() proc mainLoop*() = rawui.main() - rawui.uninit() -proc pollingMainLoop*(poll: proc(timeout: int); timeout: int) = +export rawui.main +export rawui.mainSteps +export rawui.mainStep + +proc pollingMainLoop*(poll: proc(timeout: int); timeout: int) {.deprecated: "Write your own loop please".} = ## Can be used to merge an async event loop with UI's event loop. ## Implemented using timeouts and polling because that's the only ## thing that truely composes. rawui.mainSteps() while true: poll(timeout) - discard rawui.mainStep(0)# != 0: break - rawui.uninit() + if rawui.mainStep(0) == 0: break + +proc queueMain*(f: proc (): void {.cdecl.}) = + rawui.queueMain(cast[proc (_:pointer): void {.cdecl.}](f), nil) # Not sure if this cast works or not + +proc queueMain*[T](f: proc (_:T): void {.cdecl.}, p: T) = + rawui.queueMain(cast[proc (_:pointer): void {.cdecl.}](f), cast[pointer](p)) + + +# Not included due to bad implementation +# proc timer*(milliseconds: cint) = +# rawui.timer(milliseconds) + +proc onShouldQuit*(f: proc (): bool {.cdecl.}) = + rawui.onShouldQuit(cast[proc (_:pointer): cint {.cdecl.}](f), nil) # Not sure if this cast works or not + +proc onShouldQuit*[T](f: proc (_:T): bool {.cdecl.}, p: T) = + rawui.onShouldQuit(cast[proc (_:pointer): cint {.cdecl.}](f), cast[pointer](p)) + template newFinal(result) = #proc finalize(x: type(result)) {.nimcall.} = @@ -52,7 +77,21 @@ template genImplProcs(t: untyped) {.dirty.}= func impl*(b: t): `Raw t` = cast[`Raw t`](b.internalImpl) func `impl=`*(b: t, r: `Raw t`) = b.internalImpl = pointer(r) +# ------------------- Grid ------------------------ +type + Grid* = ref object of Widget + impl*: ptr rawui.Grid + +proc add*[SomeWidget: Widget](t: Grid; c: SomeWidget, left: cint, top: cint, xspan: cint, yspan: cint, hexpand: cint, halign: Align, vexpand: cint, valign: Align) = + gridAppend t.impl, c.impl, left, top, xspan, yspan, hexpand, halign, vexpand, valign + +proc newGrid*(padded = false): Grid = + newFinal(result) + result.impl = rawui.newGrid() + result.impl.gridSetPadded(padded.cint) + # ------------------- Button -------------------------------------- + type Button* = ref object of Widget onclick*: proc () {.closure.} @@ -79,23 +118,21 @@ proc newButton*(text: string; onclick: proc() = nil): Button = type RadioButtons* = ref object of Widget - onRadioButtonClick*: proc() {.closure.} - -voidCallback(wrapOnRadioButtonClick, RadioButtons, RadioButtons, onRadioButtonClick) - -genImplProcs(RadioButtons) + impl*: ptr rawui.RadioButtons + onselected*: proc () proc add*(r: RadioButtons; text: string) = radioButtonsAppend(r.impl, text) +proc selected*(r: RadioButtons): int = radioButtonsSelected(r.impl) +proc `selected=`*(r: RadioButtons; n: int) = radioButtonsSetSelected r.impl, cint n -proc radioButtonsSelected*(r: RadioButtons): int = - radioButtonsSelected(r.impl) +voidCallback wraprbOnSelected, RadioButtons, RadioButtons, onselected -proc newRadioButtons*(onclick: proc() = nil): RadioButtons = - newFinal(result) +proc newRadioButtons*(onSelected: proc() = nil): RadioButtons = + newFinal result result.impl = rawui.newRadioButtons() - result.impl.radioButtonsOnSelected(wrapOnRadioButtonClick, cast[pointer](result)) - result.onRadioButtonClick = onclick + result.onSelected = onSelected + radioButtonsOnSelected(result.impl, wraprbOnSelected, cast[pointer](result)) # ----------------- Window ------------------------------------------- @@ -140,6 +177,9 @@ proc setChild*(w: Window; child: Widget) = windowSetChild(w.impl, child.impl) w.child = child +proc `child=`*(w: Window; c: Widget) = + w.setChild(c) + proc openFile*(parent: Window): string = let x = openFile(parent.impl) result = $x @@ -167,7 +207,10 @@ proc add*(b: Box; child: Widget; stretchy=false) = boxAppend(b.impl, child.impl, cint(stretchy)) b.children.add child -proc delete*(b: Box; index: int) = boxDelete(b.impl, index.cint) +proc delete*(b: Box; index: int) = + boxDelete(b.impl, index.cint) + b.children.delete(index) + proc padded*(b: Box): bool = boxPadded(b.impl) != 0 proc `padded=`*(b: Box; x: bool) = boxSetPadded(b.impl, x.cint) @@ -249,17 +292,29 @@ proc newLabel*(text: string): Label = type Tab* = ref object of Widget + impl*: ptr rawui.Tab + marginedDefault*: bool children*: seq[Widget] genImplProcs(Tab) -proc add*(t: Tab; name: string; c: Widget) = - tabAppend t.impl, name, c.impl - t.children.add c +proc add*[SomeWidget: Widget](t: Tab; name: string; child: SomeWidget) = + tabAppend t.impl, name, child.impl + tabSetMargined(t.impl, tabNumPages(t.impl)-1, cint(t.marginedDefault)) + t.children.add child + +proc add*[SomeWidget: Widget](t: Tab; name: string; child: SomeWidget; margined: bool) = + add(t,name,child) + tabSetMargined(t.impl, tabNumPages(t.impl)-1, cint(margined)) + +proc insertAt*[SomeWidget: Widget](t: Tab; name: string; at: int; child: SomeWidget) = + tabInsertAt(t.impl, name, at.uint64, child.impl) + tabSetMargined(t.impl, at.uint64, cint(t.marginedDefault)) + t.children.insert(child, at) -proc insertAt*(t: Tab; name: string; at: int; c: Widget) = - tabInsertAt(t.impl, name, at.cint, c.impl) - t.children.insert(c, at) +proc insertAt*[SomeWidget: Widget](t: Tab; name: string; at: int; child: SomeWidget; margined: bool) = + insertAt(t,name,at,child) + tabSetMargined(t.impl, at.uint64, cint(margined)) proc delete*(t: Tab; index: int) = tabDelete(t.impl, index.cint) @@ -270,9 +325,10 @@ proc margined*(t: Tab; page: int): bool = tabMargined(t.impl, page.cint) != 0 proc `margined=`*(t: Tab; page: int; x: bool) = tabSetMargined(t.impl, page.cint, cint(x)) -proc newTab*(): Tab = +proc newTab*(margined = false): Tab = newFinal result result.impl = rawui.newTab() + result.marginedDefault = margined result.children = @[] # ------------- Group -------------------------------------------------- @@ -286,9 +342,9 @@ genImplProcs(Group) proc title*(g: Group): string = $groupTitle(g.impl) proc `title=`*(g: Group; title: string) = groupSetTitle(g.impl, title) -proc `child=`*(g: Group; c: Widget) = - groupSetChild(g.impl, c.impl) - g.child = c +proc `child=`*[SomeWidget: Widget](g: Group; child: SomeWidget) = + groupSetChild(g.impl, child.impl) + g.child = child proc margined*(g: Group): bool = groupMargined(g.impl) != 0 proc `margined=`*(g: Group; x: bool) = groupSetMargined(g.impl, x.cint) @@ -346,6 +402,8 @@ genImplProcs(ProgressBar) proc `value=`*(p: ProgressBar; n: int) = progressBarSetValue p.impl, n.cint +proc value*(p: Progressbar; n:int): int = progressBarValue(p.impl) + proc newProgressBar*(): ProgressBar = newFinal result result.impl = rawui.newProgressBar() @@ -467,17 +525,18 @@ type genImplProcs(Menu) -template addMenuItemImpl(ex) = +template addMenuItemImpl(ex; skip_click=false) = newFinal result result.impl = ex - menuItemOnClicked(result.impl, wrapmeOnclicked, cast[pointer](result)) + when not skip_click: + menuItemOnClicked(result.impl, wrapmeOnclicked, cast[pointer](result)) m.children.add result -proc addItem*(m: Menu; name: string, onclicked: proc()): MenuItem {.discardable.} = +proc addItem*(m: Menu; name: string, onclicked: proc() = nil): MenuItem {.discardable.} = addMenuItemImpl(menuAppendItem(m.impl, name)) result.onclicked = onclicked -proc addCheckItem*(m: Menu; name: string, onclicked: proc()): MenuItem {.discardable.} = +proc addCheckItem*(m: Menu; name: string, onclicked: proc() = nil): MenuItem {.discardable.} = addMenuItemImpl(menuAppendCheckItem(m.impl, name)) result.onclicked = onclicked @@ -491,10 +550,11 @@ proc wrapOnShouldQuit(data: pointer): cint {.cdecl.} = if result == 1: GC_unref c -proc addQuitItem*(m: Menu, shouldQuit: proc(): bool): MenuItem {.discardable.} = - newFinal result - result.impl = menuAppendQuitItem(m.impl) - m.children.add result +proc addQuitItem*(m: Menu): MenuItem {.discardable.} = + addMenuItemImpl(menuAppendQuitItem(m.impl), skip_click=true) + +proc addQuitItem*(m: Menu, shouldQuit: proc(): bool): MenuItem {.discardable, deprecated:"Register menu action yourself".} = + result = addQuitItem(m) var cl = ShouldQuitClosure(fn: shouldQuit) GC_ref cl onShouldQuit(wrapOnShouldQuit, cast[pointer](cl)) @@ -586,6 +646,75 @@ proc newTable*(params: ptr TableParams): Table = newFinal result result.impl = rawui.newTable(params) +# -------------------- Area ---------------------------------------- + +type + AreaObj = object of Widget + handler: AreaHandler + onDraw*: proc (drawParams: ptr AreaDrawParams) + onMouseEvent*: proc (event: ptr AreaMouseEvent) + onMouseCrossed*: proc (left: cint) + onDragBroken*: proc () + onKeyEvent*: proc (event: ptr AreaKeyEvent): cint + Area* = ref AreaObj + +genImplProcs(Area) + +# template void2Callback(name, param0typ, supertyp, basetyp; on: untyped; param2typ:typedesc=void) = +# proc name(handler: ptr param0typ, w: ptr rawui.supertyp; data: param2typ) {.cdecl.} = +# let widget = cast[basetyp](w) +# if widget.on != nil: widget.on(param2typ) + +# let wrapOnDraw = void2Callback(AreaHandler, Area, Area, onDraw, ptr rawui.AreaDrawParams) +# void2Callback(wrapOnMouseEvent, AreaHandler, Area, Area, onMouseEvent, ptr rawui.AreaMouseEvent) +# void2Callback(wrapOnMouseCrossed, AreaHandler, Area, Area, onMouseCrossed, cint) +# void2Callback(wrapOnDragBroken, AreaHandler, Area, Area, onDragBroken) +# void2Callback(wrapOnKeyEvent, AreaHandler, Area, Area, onKeyEvent, ptr rawui.AreaKeyEvent) + +template wrapAreaCallback(on; param2typ:typedesc=void, rettyp: typedesc = void): untyped = + when param2typ is void: + block: + proc generated_handler(phandler: ptr AreaHandler, _: ptr rawui.Area): rettyp {.cdecl.} = + let widget = cast[ptr Area](cast[int](phandler) - offsetOf(AreaObj, handler)) + if widget.on != nil: widget.on() + else: default(rettyp) + generated_handler + else: + block: + proc generated_handler(phandler: ptr AreaHandler, _: ptr rawui.Area, params: param2typ): rettyp {.cdecl.} = + let widget = cast[ptr Area](cast[int](phandler) - offsetOf(AreaObj, handler)) + if widget.on != nil: widget.on(params) + else: default(rettyp) + generated_handler + +proc initHandler(a: Area) = + var handler: AreaHandler + handler.draw = wrapAreaCallback(onDraw, ptr AreaDrawParams) + handler.mouseEvent = wrapAreaCallback(onMouseEvent, ptr AreaMouseEvent) + handler.mouseCrossed = wrapAreaCallback(onMouseCrossed, cint) + handler.dragBroken = wrapAreaCallback(onDragBroken) + handler.keyEvent = wrapAreaCallback(onKeyEvent, ptr AreaKeyEvent, cint) + + a.handler = handler + +proc newArea*(): Area = + newFinal result + result.initHandler() + result.impl = rawui.newArea(result.handler.addr) + +proc newScrollingArea*(width: cint, height: cint): Area = + newFinal result + result.initHandler() + result.impl = rawui.newScrollingArea(result.handler.addr, width, height) + +proc `size=`*(a: Area, width: cint, height: cint) = + a.impl.areaSetSize(width, height) + +proc queueRedrawAll*(a: Area) = + rawui.areaQueueRedrawAll(a.impl) + +proc scrollTo*(a: Area; x: cdouble; y: cdouble; width: cdouble; height: cdouble) = + rawui.areaScrollTo(a.impl, x, y, width, height) # -------------------- Generics ------------------------------------ diff --git a/ui/rawui.nim b/ui/rawui.nim index e605e91..80548ac 100644 --- a/ui/rawui.nim +++ b/ui/rawui.nim @@ -48,12 +48,12 @@ else: {.passL: r"-lcomctl32".} {.passL: r"-ld2d1".} {.passL: r"-ldwrite".} - {.passL: r"-lUxTheme".} - {.passL: r"-lUsp10".} + {.passL: r"-luxtheme".} + {.passL: r"-lusp10".} {.passL: r"-lgdi32".} {.passL: r"-luser32".} {.passL: r"-lkernel32".} - {.link: r"..\res\resources.o".} + {.link: r"../res/resources.o".} when defined(vcc): {.passC: "/EHsc".} @@ -90,7 +90,7 @@ type ForEachStop, InitOptions* = object - size*: csize + size*: csize_t {.deadCodeElim: on.} @@ -153,7 +153,7 @@ proc controlEnable*(a2: ptr Control) {.cdecl, importc: "uiControlEnable", mylib.} proc controlDisable*(a2: ptr Control) {.cdecl, importc: "uiControlDisable", mylib.} -proc allocControl*(n: csize; oSsig: uint32; typesig: uint32; typenamestr: cstring): ptr Control {. +proc allocControl*(n: csize_t; oSsig: uint32; typesig: uint32; typenamestr: cstring): ptr Control {. cdecl, importc: "uiAllocControl", mylib.} proc freeControl*(a2: ptr Control) {.cdecl, importc: "uiFreeControl", mylib.} @@ -652,7 +652,7 @@ type y1*: cdouble outerRadius*: cdouble stops*: ptr DrawBrushGradientStop - numStops*: csize + numStops*: csize_t DrawBrushGradientStop* = object pos*: cdouble @@ -667,7 +667,7 @@ type thickness*: cdouble miterLimit*: cdouble dashes*: ptr cdouble - numDashes*: csize + numDashes*: csize_t dashPhase*: cdouble @@ -855,23 +855,23 @@ proc attributeFeatures*(a: ptr Attribute): ptr OpenTypeFeatures {.cdecl, importc type AttributedStringForEachAttributeFunc* = proc (s: ptr AttributedString; - a: ptr Attribute; start: csize; `end`: csize; data: pointer): ForEach + a: ptr Attribute; start: csize_t; `end`: csize_t; data: pointer): ForEach proc newAttributedString*(initialString: cstring): ptr AttributedString {.cdecl, importc: "uiNewAttributedString", mylib.} proc freeAttributedString*(s: ptr AttributedString) {.cdecl, importc: "uiFreeAttributedString", mylib.} proc attributedStringString*(s: ptr AttributedString): cstring {.cdecl, importc: "uiAttributedStringString", mylib.} -proc attributedStringLen*(s: ptr AttributedString): csize {.cdecl, importc: "uiAttributedStringLen", mylib.} +proc attributedStringLen*(s: ptr AttributedString): csize_t {.cdecl, importc: "uiAttributedStringLen", mylib.} proc attributedStringAppendUnattributed*(s: ptr AttributedString; str: cstring) {.cdecl, importc: "uiAttributedStringAppendUnattributed", mylib.} proc attributedStringInsertAtUnattributed*(s: ptr AttributedString; - str: cstring; at: csize) {.cdecl, importc: "uiAttributedStringInsertAtUnattributed", mylib.} -proc attributedStringDelete*(s: ptr AttributedString; start: csize; `end`: csize) {.cdecl, importc: "uiAttributedStringDelete", mylib.} + str: cstring; at: csize_t) {.cdecl, importc: "uiAttributedStringInsertAtUnattributed", mylib.} +proc attributedStringDelete*(s: ptr AttributedString; start: csize_t; `end`: csize_t) {.cdecl, importc: "uiAttributedStringDelete", mylib.} proc attributedStringSetAttribute*(s: ptr AttributedString; a: ptr Attribute; - start: csize; `end`: csize) {.cdecl, importc: "uiAttributedStringSetAttribute", mylib.} + start: csize_t; `end`: csize_t) {.cdecl, importc: "uiAttributedStringSetAttribute", mylib.} proc attributedStringForEachAttribute*(s: ptr AttributedString; f: AttributedStringForEachAttributeFunc; data: pointer) {.cdecl, importc: "uiDruiAttributedStringForEachAttributeawSave", mylib.} -proc attributedStringNumGraphemes*(s: ptr AttributedString): csize {.cdecl, importc: "uiAttributedStringNumGraphemes", mylib.} -proc attributedStringByteIndexToGrapheme*(s: ptr AttributedString; pos: csize): csize {.cdecl, importc: "uiAttributedStringByteIndexToGrapheme", mylib.} -proc attributedStringGraphemeToByteIndex*(s: ptr AttributedString; pos: csize): csize {.cdecl, importc: "uiAttributedStringGraphemeToByteIndex", mylib.} +proc attributedStringNumGraphemes*(s: ptr AttributedString): csize_t {.cdecl, importc: "uiAttributedStringNumGraphemes", mylib.} +proc attributedStringByteIndexToGrapheme*(s: ptr AttributedString; pos: csize_t): csize_t {.cdecl, importc: "uiAttributedStringByteIndexToGrapheme", mylib.} +proc attributedStringGraphemeToByteIndex*(s: ptr AttributedString; pos: csize_t): csize_t {.cdecl, importc: "uiAttributedStringGraphemeToByteIndex", mylib.} type FontDescriptor* = object