diff --git a/examples/genuiusg.nim b/examples/genuiusg.nim new file mode 100644 index 0000000..8b4007d --- /dev/null +++ b/examples/genuiusg.nim @@ -0,0 +1,99 @@ +import "../ui", "../ui/genui" + +# The macro is a fairly simple substitution +# It follows one of three patterns: +# (arguments, for, widget, creator)[arguments, for, add, function]: +# +# %(arguments, for, widget, creator)[arguments, for, add, function]: +# +# %[arguments, for, add, function]: +# +# "String" +# +# Both ()-arguments and []-arguments can be omitted +# If the widget has no children the : must be omitted +# Identifiers create a var statement assigning the widget to the identifier, or assign the widget to the identifier if it already exists +# Using % you can add widget created previously, it takes the same add options and children as any other widget +# The string pattern is used for widgets which have an add function for string values, such as radio-, and comboboxes. + +# This is an example of a simple function which creates a piece of UI +# You will notice it uses result% to bind the RadioButtons widget created to the result +proc getRadioBox():RadioButtons = + genui: + result%RadioButtons: + "Radio Button 1" + "Radio Button 2" + "Radio Button 3" +# This is a longer example which creates the same UI as in the controllgallery2 example +proc main*() = + var mainwin: Window + var spinbox: Spinbox + var slider: Slider + var progressbar: ProgressBar + + # This gets the widget from the previously defined function and adds callback to it + var radioBox = getRadioBox() + radioBox.onselected= proc()= + echo radioBox.selected + + # This is another way to create a callback, it will be assigned to the widgets later + proc update(value: int) = + spinbox.value = value + slider.value = value + progressBar.value = value + + # Since Window uses setChild instead of add it can't be put inside the genui macro. + # NOTE: Group uses "child=" to set it's child but has a template to make it work. + # Adding more children to a Group widget would result in only the last being shown + mainwin = newWindow("libui Control Gallery", 640, 480, true) + mainwin.margined = true + mainwin.onClosing = (proc (): bool = return true) + + # This is where the magic happens. Note that most of the parameter names are included, + # this is a stylistic choice which I find makes the code easier to read. The notable + # exception to this is for widgets which take only a string as it's fairly obvious what it's used for + genui: + # This vertical box is attached to the box variable which is later used to add it to the mainWin + box%VerticalBox(padded = true): + HorizontalBox(padded = true)[stretchy = true]: + Group("Basic Controls"): + VerticalBox(padded = true): + Button("Button") + Checkbox("Checkbox") + Entry("Entry") + HorizontalSeparator + VerticalBox(padded = true)[stretchy = true]: + Group("Numbers", margined = true): + VerticalBox(padded = true): + # These are the three widgets which variables was declared earlier and used in the callback + spinbox%Spinbox(min = 0, max = 100, onchanged = update) + slider%Slider(min = 0, max = 100, onchanged = update) + progressbar%ProgressBar + Group("Lists", margined = true): + VerticalBox(padded = true): + Combobox: + "Combobox Item 1" + "Combobox Item 2" + "Combobox Item 3" + EditableCombobox: + "Editable Item 1" + "Editable Item 2" + "Editable Item 3" + # This does not create a new widget but adds in the radio box created earlier + %radioBox + # Tabs are a bit strange as their add function has two required arguments + # Here the name parameter must be included fully qualified in order to be properly added + Tab(margined = true)[stretchy = true]: + HorizontalBox[name = "Page 1"]: + Label("Welcome to page 1") + HorizontalBox[name = "Page 2"]: + Label("Welcome to page 2") + HorizontalBox[name = "Page 3"]: + Label("Welcome to page 3") + + mainwin.setChild(box) + show(mainwin) + mainLoop() + +init() +main() \ No newline at end of file diff --git a/ui.nim b/ui.nim index df8de97..b9672a4 100644 --- a/ui.nim +++ b/ui.nim @@ -215,15 +215,26 @@ proc newLabel*(text: string): Label = type Tab* = ref object of Widget impl*: ptr rawui.Tab + marginedDefault*: bool children*: seq[Widget] -proc add*[SomeWidget: Widget](t: Tab; name: string; c: SomeWidget) = - 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 insertAt*[SomeWidget: Widget](t: Tab; name: string; at: int; c: SomeWidget) = - tabInsertAt(t.impl, name, at.uint64, c.impl) - t.children.insert(c, at) +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*[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) @@ -234,9 +245,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 -------------------------------------------------- @@ -249,9 +261,9 @@ type proc title*(g: Group): string = $groupTitle(g.impl) proc `title=`*(g: Group; title: string) = groupSetTitle(g.impl, title) -proc `child=`*[SomeWidget: Widget](g: Group; c: SomeWidget) = - 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) @@ -306,6 +318,8 @@ type 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() @@ -370,12 +384,20 @@ proc newEditableCombobox*(onchanged: proc () = nil): EditableCombobox = type RadioButtons* = ref object of Widget impl*: ptr rawui.RadioButtons + onselected*: proc () proc add*(r: RadioButtons; text: string) = radioButtonsAppend(r.impl, text) -proc newRadioButtons*(): RadioButtons = +proc selected*(r: RadioButtons): int = radioButtonsSelected(r.impl) +proc `selected=`*(r: RadioButtons; n: int) = radioButtonsSetSelected r.impl, cint n + +voidCallback wraprbOnSelected, RadioButtons, RadioButtons, onselected + +proc newRadioButtons*(onSelected: proc() = nil): RadioButtons = newFinal result result.impl = rawui.newRadioButtons() + result.onSelected = onSelected + radioButtonsOnSelected(result.impl, wraprbOnSelected, cast[pointer](result)) # ------------------------ MultilineEntry ------------------------------ diff --git a/ui/genui.nim b/ui/genui.nim new file mode 100644 index 0000000..b0dd3b6 --- /dev/null +++ b/ui/genui.nim @@ -0,0 +1,153 @@ +import macros, typetraits, tables, unicode, "../ui" + +proc `[]`(s: NimNode, x: Slice[int]): seq[NimNode] = + ## slice operation for NimNodes. + var a = x.a + var L = x.b - a + 1 + newSeq(result, L) + for i in 0..