initial commit

This commit is contained in:
2024-06-06 00:07:31 +02:00
commit 01f44645d6
13 changed files with 4972 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
ols.json
# Ignore examples dir
examples/
# But Unignore .odin files in it
examples/!*.odin
+8
View File
@@ -0,0 +1,8 @@
Copyright 2024 - IllusionMan1212
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+11
View File
@@ -0,0 +1,11 @@
# libui-ng Odin Bindings
Handwritten Odin bindings for [libui-ng](https://github.com/libui-ng/libui-ng/)
Based on upstream commit: 49b04c4cf8ae4d3e38e389f446ef75170eb62762
## Examples
The upstream examples have been reimplemented using Odin and they work identical to the originals (with the exception of the multithreaded example)
## TODO
- [ ] Support and test Windows
- [ ] Support and test Mac?
+307
View File
@@ -0,0 +1,307 @@
package libui_controlgallery
import "core:fmt"
import "../../"
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.Quit()
return true
}
onShouldQuit :: proc "c" (data: rawptr) -> b32 {
mainwin = transmute(^libui.Window)data
libui.ControlDestroy(cast(^libui.Control)mainwin)
return true
}
makeBasicControlsPage :: proc() -> ^libui.Control {
vbox := libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
hbox := libui.NewHorizontalBox()
libui.BoxSetPadded(hbox, true)
libui.BoxAppend(vbox, cast(^libui.Control)hbox, false)
libui.BoxAppend(hbox,
cast(^libui.Control)libui.NewButton("Button"),
false)
libui.BoxAppend(hbox,
cast(^libui.Control)libui.NewCheckbox("Checkbox"),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewLabel("This is a label.\nLabels can span multiple lines."),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewHorizontalSeparator(),
false)
group := libui.NewGroup("Entries")
libui.GroupSetMargined(group, true)
libui.BoxAppend(vbox, cast(^libui.Control)group, true)
entryForm := libui.NewForm()
libui.FormSetPadded(entryForm, true)
libui.GroupSetChild(group, cast(^libui.Control)entryForm)
libui.FormAppend(entryForm,
"Entry",
cast(^libui.Control)libui.NewEntry(),
false)
libui.FormAppend(entryForm,
"Password Entry",
cast(^libui.Control)libui.NewPasswordEntry(),
false)
libui.FormAppend(entryForm,
"Search Entry",
cast(^libui.Control)libui.NewSearchEntry(),
false)
libui.FormAppend(entryForm,
"Multiline Entry",
cast(^libui.Control)libui.NewMultilineEntry(),
true)
libui.FormAppend(entryForm,
"Multiline Entry No Wrap",
cast(^libui.Control)libui.NewNonWrappingMultilineEntry(),
true)
return cast(^libui.Control)vbox
}
spinbox: ^libui.Spinbox
slider: ^libui.Slider
pbar: ^libui.ProgressBar
onSpinboxChanged :: proc "c" (s: ^libui.Spinbox, data: rawptr) {
libui.SliderSetValue(slider, libui.SpinboxValue(s))
libui.ProgressBarSetValue(pbar, libui.SpinboxValue(s))
}
onSliderChanged :: proc "c" (s: ^libui.Slider, data: rawptr) {
libui.SpinboxSetValue(spinbox, libui.SliderValue(s))
libui.ProgressBarSetValue(pbar, libui.SliderValue(s))
}
makeNumbersPage :: proc() -> ^libui.Control {
hbox := libui.NewHorizontalBox()
libui.BoxSetPadded(hbox, true)
group := libui.NewGroup("Numbers")
libui.GroupSetMargined(group, true)
libui.BoxAppend(hbox, cast(^libui.Control)group, true)
vbox := libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.GroupSetChild(group, cast(^libui.Control)vbox)
spinbox = libui.NewSpinbox(0, 100)
slider = libui.NewSlider(0, 100)
pbar = libui.NewProgressBar()
libui.SpinboxOnChanged(spinbox, onSpinboxChanged, nil)
libui.SliderOnChanged(slider, onSliderChanged, nil)
libui.BoxAppend(vbox, cast(^libui.Control)spinbox, false)
libui.BoxAppend(vbox, cast(^libui.Control)slider, false)
libui.BoxAppend(vbox, cast(^libui.Control)pbar, false)
ip := libui.NewProgressBar()
libui.ProgressBarSetValue(ip, -1)
libui.BoxAppend(vbox, cast(^libui.Control)ip, false)
group = libui.NewGroup("Lists")
libui.GroupSetMargined(group, true)
libui.BoxAppend(hbox, cast(^libui.Control)group, true)
vbox = libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.GroupSetChild(group, cast(^libui.Control)vbox)
cbox := libui.NewCombobox()
libui.ComboboxAppend(cbox, "Combobox Item 1")
libui.ComboboxAppend(cbox, "Combobox Item 2")
libui.ComboboxAppend(cbox, "Combobox Item 3")
libui.BoxAppend(vbox, cast(^libui.Control)cbox, false)
ecbox := libui.NewEditableCombobox()
libui.EditableComboboxAppend(ecbox, "Editable Item 1")
libui.EditableComboboxAppend(ecbox, "Editable Item 2")
libui.EditableComboboxAppend(ecbox, "Editable Item 3")
libui.BoxAppend(vbox, cast(^libui.Control)ecbox, false)
rb := libui.NewRadioButtons()
libui.RadioButtonsAppend(rb, "Radio Button 1")
libui.RadioButtonsAppend(rb, "Radio Button 2")
libui.RadioButtonsAppend(rb, "Radio Button 3")
libui.BoxAppend(vbox, cast(^libui.Control)rb, false)
return cast(^libui.Control)hbox
}
mainwin: ^libui.Window
onOpenFileClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
entry := transmute(^libui.Entry)data
filename := libui.OpenFile(mainwin)
if filename == nil {
libui.EntrySetText(entry, "(cancelled)")
return
}
libui.EntrySetText(entry, filename)
libui.FreeText(filename)
}
onOpenFolderClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
entry := transmute(^libui.Entry)data
filename := libui.OpenFolder(mainwin)
if filename == nil {
libui.EntrySetText(entry, "(cancelled)")
return
}
libui.EntrySetText(entry, filename)
libui.FreeText(filename)
}
onSaveFileClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
entry := transmute(^libui.Entry)data
filename := libui.SaveFile(mainwin)
if filename == nil {
libui.EntrySetText(entry, "(cancelled)")
return
}
libui.EntrySetText(entry, filename)
libui.FreeText(filename)
}
onMsgBoxClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
libui.MsgBox(mainwin, "This is a normal message box.", "More detailed information can be shown here.")
}
onMsgBoxErrorClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
libui.MsgBoxError(mainwin, "This message box describes an error.", "More detailed information can be shown here.")
}
makeDataChoosersPage :: proc() -> ^libui.Control {
hbox := libui.NewHorizontalBox()
libui.BoxSetPadded(hbox, true)
vbox := libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.BoxAppend(hbox, cast(^libui.Control)vbox, false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewDatePicker(),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewTimePicker(),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewDateTimePicker(),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewFontButton(),
false)
libui.BoxAppend(vbox,
cast(^libui.Control)libui.NewColorButton(),
false)
libui.BoxAppend(hbox,
cast(^libui.Control)libui.NewVerticalSeparator(),
false)
vbox = libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.BoxAppend(hbox, cast(^libui.Control)vbox, true)
grid := libui.NewGrid()
libui.GridSetPadded(grid, true)
libui.BoxAppend(vbox, cast(^libui.Control)grid, false)
button := libui.NewButton(" Open File ")
entry := libui.NewEntry()
libui.EntrySetReadOnly(entry, true)
libui.ButtonOnClicked(button, onOpenFileClicked, entry)
libui.GridAppend(grid, cast(^libui.Control)button,
0, 0, 1, 1,
false, .Fill, false, .Fill)
libui.GridAppend(grid, cast(^libui.Control)entry,
1, 0, 1, 1,
true, .Fill, false, .Fill)
button = libui.NewButton("Open Folder")
entry = libui.NewEntry()
libui.EntrySetReadOnly(entry, true)
libui.ButtonOnClicked(button, onOpenFolderClicked, entry)
libui.GridAppend(grid, cast(^libui.Control)button,
0, 1, 1, 1,
false, .Fill, false, .Fill)
libui.GridAppend(grid, cast(^libui.Control)entry,
1, 1, 1, 1,
true, .Fill, false, .Fill)
button = libui.NewButton(" Save File ")
entry = libui.NewEntry()
libui.EntrySetReadOnly(entry, true)
libui.ButtonOnClicked(button, onSaveFileClicked, entry)
libui.GridAppend(grid, cast(^libui.Control)button,
0, 2, 1, 1,
false, .Fill, false, .Fill)
libui.GridAppend(grid, cast(^libui.Control)entry,
1, 2, 1, 1,
true, .Fill, false, .Fill)
msggrid := libui.NewGrid()
libui.GridSetPadded(msggrid, true)
libui.GridAppend(grid, cast(^libui.Control)msggrid,
0, 3, 2, 1,
false, .Center, false, .Start)
button = libui.NewButton("Message Box")
libui.ButtonOnClicked(button, onMsgBoxClicked, nil)
libui.GridAppend(msggrid, cast(^libui.Control)button,
0, 0, 1, 1,
false, .Fill, false, .Fill)
button = libui.NewButton("Error Box")
libui.ButtonOnClicked(button, onMsgBoxErrorClicked, nil)
libui.GridAppend(msggrid, cast(^libui.Control)button,
1, 0, 1, 1,
false, .Fill, false, .Fill)
return cast(^libui.Control)hbox
}
main :: proc() {
options: libui.InitOptions
err := libui.Init(&options)
if err != nil {
fmt.eprintln("error initializing libui: %s", err)
libui.FreeInitError(err)
return
}
mainwin = libui.NewWindow("libui Control Gallery", 640, 480, true)
libui.WindowOnClosing(mainwin, onClosing, nil)
libui.OnShouldQuit(onShouldQuit, mainwin)
tab := libui.NewTab()
libui.WindowSetChild(mainwin, cast(^libui.Control)tab)
libui.WindowSetMargined(mainwin, true)
libui.TabAppend(tab, "Basic Controls", makeBasicControlsPage())
libui.TabSetMargined(tab, 0, true)
libui.TabAppend(tab, "Numbers and Lists", makeNumbersPage())
libui.TabSetMargined(tab, 1, true)
libui.TabAppend(tab, "Data Choosers", makeDataChoosersPage())
libui.TabSetMargined(tab, 2, true)
libui.ControlShow(cast(^libui.Control)mainwin)
libui.Main()
}
+121
View File
@@ -0,0 +1,121 @@
package libui_datetime
import "base:runtime"
import "core:fmt"
import "core:c/libc"
import "../../"
dtboth, dtdate, dttime: ^libui.DateTimePicker
timeFormat :: proc(d: ^libui.DateTimePicker) -> cstring {
if d == dtboth {
return "%c"
} else if d == dtdate {
return "%x"
} else if d == dttime {
return "%X"
}
return ""
}
onChanged :: proc "c" (d: ^libui.DateTimePicker, data: rawptr) {
context = runtime.default_context()
time: libc.tm
buf: [64]i8
libui.DateTimePickerTime(d, &time)
libc.strftime(cast([^]u8)&buf, size_of(buf), timeFormat(d), &time)
libui.LabelSetText(transmute(^libui.Label)data, transmute(cstring)&buf[0])
}
onClicked :: proc "c" (b: ^libui.Button, data: rawptr) {
t: libc.time_t
tmbuf: libc.tm
now := cast(bool)transmute(int)data
t = 0
if (now) {
t = libc.time(nil)
}
tmbuf = libc.localtime(&t)^
if (now) {
libui.DateTimePickerSetTime(dtdate, tmbuf)
libui.DateTimePickerSetTime(dttime, tmbuf)
} else {
libui.DateTimePickerSetTime(dtboth, tmbuf)
}
}
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.Quit()
return true
}
main :: proc() {
o: libui.InitOptions
g: ^libui.Grid
l: ^libui.Label
b: ^libui.Button
err := libui.Init(&o)
if err != nil {
fmt.eprintln("error initializing ui: %s", err)
libui.FreeInitError(err)
return
}
w := libui.NewWindow("Date / Time", 320, 240, false)
libui.WindowSetMargined(w, true)
g = libui.NewGrid()
libui.GridSetPadded(g, true)
libui.WindowSetChild(w, cast(^libui.Control)g)
dtboth = libui.NewDateTimePicker()
dtdate = libui.NewDatePicker()
dttime = libui.NewTimePicker()
libui.GridAppend(g, cast(^libui.Control)dtboth,
0, 0, 2, 1,
true, .Fill, false, .Fill)
libui.GridAppend(g, cast(^libui.Control)dtdate,
0, 1, 1, 1,
true, .Fill, false, .Fill)
libui.GridAppend(g, cast(^libui.Control)dttime,
1, 1, 1, 1,
true, .Fill, false, .Fill)
l = libui.NewLabel("")
libui.GridAppend(g, cast(^libui.Control)l,
0, 2, 2, 1,
true, .Center, false, .Fill)
libui.DateTimePickerOnChanged(dtboth, onChanged, l)
l = libui.NewLabel("")
libui.GridAppend(g, cast(^libui.Control)l,
0, 3, 1, 1,
true, .Center, false, .Fill)
libui.DateTimePickerOnChanged(dtdate, onChanged, l)
l = libui.NewLabel("")
libui.GridAppend(g, cast(^libui.Control)l,
1, 3, 1, 1,
true, .Center, false, .Fill)
libui.DateTimePickerOnChanged(dttime, onChanged, l)
b = libui.NewButton("Now")
libui.ButtonOnClicked(b, onClicked, cast(rawptr)uintptr(1))
libui.GridAppend(g, cast(^libui.Control)b,
0, 4, 1, 1,
true, .Fill, true, .End)
b = libui.NewButton("Unix epoch")
libui.ButtonOnClicked(b, onClicked, cast(rawptr)uintptr(0))
libui.GridAppend(g, cast(^libui.Control)b,
1, 4, 1, 1,
true, .Fill, true, .End)
libui.WindowOnClosing(w, onClosing, nil)
libui.ControlShow(cast(^libui.Control)w)
libui.Main()
}
+214
View File
@@ -0,0 +1,214 @@
package libui_drawtext
import "core:c"
import "core:fmt"
import "../../"
mainwin: ^libui.Window
area: ^libui.Area
fontButton: ^libui.FontButton
alignment: ^libui.Combobox
systemFont: ^libui.Checkbox
attrstr: ^libui.AttributedString
appendWithAttribute :: proc(what: cstring, attr: ^libui.Attribute, attr2: ^libui.Attribute) {
start := libui.AttributedStringLen(attrstr^)
end := start + len(what)
libui.AttributedStringAppendUnattributed(attrstr, what)
libui.AttributedStringSetAttribute(attrstr, attr, start, end)
if attr2 != nil {
libui.AttributedStringSetAttribute(attrstr, attr2, start, end)
}
}
makeAttributedString :: proc() {
attrstr = libui.NewAttributedString("Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout objects.\n" + "uiAttributedString lets you have a variety of attributes: ")
attr := libui.NewFamilyAttribute("Courier New")
appendWithAttribute("font family", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewSizeAttribute(18)
appendWithAttribute("font size", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewWeightAttribute(.Bold)
appendWithAttribute("font weight", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewItalicAttribute(.Italic)
appendWithAttribute("font italicness", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewStretchAttribute(.Condensed)
appendWithAttribute("font stretch", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewColorAttribute(0.75, 0.25, 0.5, 0.75)
appendWithAttribute("text color", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewBackgroundAttribute(0.5, 0.5, 0.25, 0.5)
appendWithAttribute("text background color", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
attr = libui.NewUnderlineAttribute(.Single)
appendWithAttribute("underline style", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, ", ")
libui.AttributedStringAppendUnattributed(attrstr, "and ")
attr = libui.NewUnderlineAttribute(.Double)
attr2 := libui.NewUnderlineColorAttribute(.Custom, 1.0, 0.0, 0.5, 1.0)
appendWithAttribute("underline color", attr, attr2)
libui.AttributedStringAppendUnattributed(attrstr, ". ")
libui.AttributedStringAppendUnattributed(attrstr, "Furthermore, there are attributes allowing for ")
attr = libui.NewUnderlineAttribute(.Suggestion)
attr2 = libui.NewUnderlineColorAttribute(.Spelling, 0, 0, 0, 0)
appendWithAttribute("special underlines for indicating spelling errors", attr, attr2)
libui.AttributedStringAppendUnattributed(attrstr, " (and other types of errors) ")
libui.AttributedStringAppendUnattributed(attrstr, "and control over OpenType features such as ligatures (for instance, ")
otf := libui.NewOpenTypeFeatures()
libui.OpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 0)
attr = libui.NewFeaturesAttribute(otf)
appendWithAttribute("afford", attr, nil)
libui.AttributedStringAppendUnattributed(attrstr, " vs. ")
libui.OpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 1)
attr = libui.NewFeaturesAttribute(otf)
appendWithAttribute("afford", attr, nil)
libui.FreeOpenTypeFeatures(otf)
libui.AttributedStringAppendUnattributed(attrstr, ").\n")
libui.AttributedStringAppendUnattributed(attrstr, "Use the controls opposite to the text to control properties of the text.")
}
handlerDraw :: proc(a: ^libui.AreaHandler, area: ^libui.Area, p: ^libui.AreaDrawParams) {
defaultFont: libui.FontDescriptor
params: libui.DrawTextLayoutParams
useSystemFont := libui.CheckboxChecked(systemFont)
params.String = attrstr
if useSystemFont {
libui.LoadControlFont(&defaultFont)
} else {
libui.FontButtonFont(fontButton, &defaultFont)
}
params.DefaultFont = &defaultFont
params.Width = p.AreaWidth
params.Align = cast(libui.DrawTextAlign)libui.ComboboxSelected(alignment)
textLayout := libui.DrawNewTextLayout(&params)
libui.DrawText(p.Context, textLayout, 0, 0)
libui.DrawFreeTextLayout(textLayout)
libui.FreeFontButtonFont(&defaultFont)
}
handlerMouseEvent :: proc(a: ^libui.AreaHandler, area: ^libui.Area, e: ^libui.AreaMouseEvent) {
// do nothing
}
handlerMouseCrossed :: proc(ah: ^libui.AreaHandler, a: ^libui.Area, left: c.int) {
// do nothing
}
handlerDragBroken :: proc(ah: ^libui.AreaHandler, a: ^libui.Area) {
// do nothing
}
handlerKeyEvent :: proc(ah: ^libui.AreaHandler, a: ^libui.Area, e: ^libui.AreaKeyEvent) -> c.int {
// reject all keys
return 0
}
onFontChanged :: proc "c" (b: ^libui.FontButton, data: rawptr) {
libui.AreaQueueRedrawAll(area)
}
onComboboxSelected :: proc "c" (b: ^libui.Combobox, data: rawptr) {
libui.AreaQueueRedrawAll(area)
}
onCheckboxToggled :: proc "c" (b: ^libui.Checkbox, data: rawptr) {
libui.AreaQueueRedrawAll(area)
}
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.ControlDestroy(cast(^libui.Control)mainwin)
libui.Quit()
return false
}
shouldQuit :: proc "c" (data: rawptr) -> b32 {
libui.ControlDestroy(cast(^libui.Control)mainwin)
return true
}
main :: proc() {
handler: libui.AreaHandler
o: libui.InitOptions
handler.Draw = handlerDraw
handler.MouseEvent = handlerMouseEvent
handler.MouseCrossed = handlerMouseCrossed
handler.DragBroken = handlerDragBroken
handler.KeyEvent = handlerKeyEvent
err := libui.Init(&o)
if err != nil {
fmt.eprintln("error initializing ui: %s", err)
libui.FreeInitError(err)
return
}
libui.OnShouldQuit(shouldQuit, nil)
makeAttributedString()
mainwin = libui.NewWindow("libui Text-Drawing Example", 640, 480, true)
libui.WindowSetMargined(mainwin, true)
libui.WindowOnClosing(mainwin, onClosing, nil)
hbox := libui.NewHorizontalBox()
libui.BoxSetPadded(hbox, true)
libui.WindowSetChild(mainwin, cast(^libui.Control)hbox)
vbox := libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.BoxAppend(hbox, cast(^libui.Control)vbox, false)
fontButton = libui.NewFontButton()
libui.FontButtonOnChanged(fontButton, onFontChanged, nil)
libui.BoxAppend(vbox, cast(^libui.Control)fontButton, false)
form := libui.NewForm()
libui.FormSetPadded(form, true)
// on OS X if this is set to 1 then the window can't resize does the form not have the concept of stretchy trailing space?
libui.BoxAppend(vbox, cast(^libui.Control)form, false)
alignment = libui.NewCombobox()
// note that the items match with the values of the uiDrawTextAlign values
libui.ComboboxAppend(alignment, "Left")
libui.ComboboxAppend(alignment, "Center")
libui.ComboboxAppend(alignment, "Right")
libui.ComboboxSetSelected(alignment, 0) // start with left alignment
libui.ComboboxOnSelected(alignment, onComboboxSelected, nil)
libui.FormAppend(form, "Alignment", cast(^libui.Control)alignment, false)
systemFont = libui.NewCheckbox("")
libui.CheckboxOnToggled(systemFont, onCheckboxToggled, nil)
libui.FormAppend(form, "System Font", cast(^libui.Control)systemFont, false)
area = libui.NewArea(&handler)
libui.BoxAppend(hbox, cast(^libui.Control)area, true)
libui.ControlShow(cast(^libui.Control)mainwin)
libui.Main()
libui.FreeAttributedString(attrstr)
libui.Uninit()
}
+32
View File
@@ -0,0 +1,32 @@
package libui_hello_world
import "core:fmt"
import libui "../../"
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.Quit()
return true
}
main :: proc() {
o: libui.InitOptions
err := libui.Init(&o)
if err != nil {
fmt.eprintln("Error initializing libui-ng: %s", err)
libui.FreeInitError(err)
return
}
// Create a new window
w := libui.NewWindow("Hello World!", 300, 30, false)
libui.WindowOnClosing(w, onClosing, nil)
l := libui.NewLabel("Hello, World!")
libui.WindowSetChild(w, cast(^libui.Control)l)
libui.ControlShow(cast(^libui.Control)w)
libui.Main()
libui.Uninit()
}
+297
View File
@@ -0,0 +1,297 @@
package libui_histogram
import "core:c"
import "core:math/rand"
import "core:fmt"
import "../../"
mainwin: ^libui.Window
histogram: ^libui.Area
handler: libui.AreaHandler
datapoints: [10]^libui.Spinbox
colorButton: ^libui.ColorButton
currentPoint: c.int = -1
// some metrics
xoffLeft :: 20 /* histogram margins */
yoffTop :: 20
xoffRight :: 20
yoffBottom :: 20
pointRadius :: 5
// and some colors
// names and values from https://msdn.microsoft.com/en-us/library/windows/desktop/dd370907%28v=vs.85%29.aspx
colorWhite :: 0xFFFFFF
colorBlack :: 0x000000
colorDodgerBlue :: 0x1E90FF
// helper to quickly set a brush color
setSolidBrush :: proc(brush: ^libui.DrawBrush, color: c.uint32_t, alpha: c.double) {
component: c.uint8_t
brush.Type = .Solid
component = cast(c.uint8_t)((color >> 16) & 0xFF)
brush.R = (cast(c.double)component) / 255
component = cast(c.uint8_t)((color >> 8) & 0xFF)
brush.G = (cast(c.double)component) / 255
component = cast(c.uint8_t)(color & 0xFF)
brush.B = (cast(c.double)component) / 255
brush.A = alpha
}
pointLocations :: proc(width: c.double, height: c.double, xs: [^]c.double, ys: [^]c.double) {
xincr: c.double
yincr: c.double
n: c.int
xincr = width / 9 // 10 - 1 to make the last point be at the end
yincr = height / 100
for i in 0..<10 {
// get the value of the point
n = libui.SpinboxValue(datapoints[i])
// because y=0 is the top but n=0 is the bottom, we need to flip
n = 100 - n
xs[i] = xincr * f64(i)
ys[i] = yincr * f64(n)
}
}
constructGraph :: proc(width: c.double, height: c.double, extend: bool) -> ^libui.DrawPath {
xs: [10]c.double
ys: [10]c.double
pointLocations(width, height, raw_data(&xs), raw_data(&ys))
path := libui.DrawNewPath(.Winding)
libui.DrawPathNewFigure(path, xs[0], ys[0])
for i in 1..<10 {
libui.DrawPathLineTo(path, xs[i], ys[i])
}
if (extend) {
libui.DrawPathLineTo(path, width, height)
libui.DrawPathLineTo(path, 0, height)
libui.DrawPathCloseFigure(path)
}
libui.DrawPathEnd(path)
return path
}
graphSize :: proc(clientWidth: c.double, clientHeight: c.double, graphWidth: ^c.double, graphHeight: ^c.double) {
graphWidth^ = clientWidth - xoffLeft - xoffRight
graphHeight^ = clientHeight - yoffTop - yoffBottom
}
handlerDraw :: proc(a: ^libui.AreaHandler, area: ^libui.Area, p: ^libui.AreaDrawParams) {
brush: libui.DrawBrush
// initialize sp to 0 to avoid passing garbage to uiDrawStroke()
// for example, we don't use dashing
sp: libui.DrawStrokeParams
m: libui.DrawMatrix
graphWidth: c.double
graphHeight: c.double
graphR: c.double
graphG: c.double
graphB: c.double
graphA: c.double
// fill the area with white
setSolidBrush(&brush, colorWhite, 1.0)
path := libui.DrawNewPath(.Winding)
libui.DrawPathAddRectangle(path, 0, 0, p.AreaWidth, p.AreaHeight)
libui.DrawPathEnd(path)
libui.DrawFill(p.Context, path, &brush)
libui.DrawFreePath(path)
// figure out dimensions
graphSize(p.AreaWidth, p.AreaHeight, &graphWidth, &graphHeight)
// make a stroke for both the axes and the histogram line
sp.Cap = .Flat
sp.Join = .Miter
sp.Thickness = 2
sp.MiterLimit = libui.DrawDefaultMiterLimit
// draw the axes
setSolidBrush(&brush, colorBlack, 1.0)
path = libui.DrawNewPath(.Winding)
libui.DrawPathNewFigure(path,
xoffLeft, yoffTop)
libui.DrawPathLineTo(path,
xoffLeft, yoffTop + graphHeight)
libui.DrawPathLineTo(path,
xoffLeft + graphWidth, yoffTop + graphHeight)
libui.DrawPathEnd(path)
libui.DrawStroke(p.Context, path, &brush, &sp)
libui.DrawFreePath(path)
// now transform the coordinate space so (0, 0) is the top-left corner of the graph
libui.DrawMatrixSetIdentity(&m)
libui.DrawMatrixTranslate(&m, xoffLeft, yoffTop)
libui.DrawTransform(p.Context, &m)
// now get the color for the graph itself and set up the brush
libui.ColorButtonColor(colorButton, &graphR, &graphG, &graphB, &graphA)
brush.Type = .Solid
brush.R = graphR
brush.G = graphG
brush.B = graphB
// we set brush.A below to different values for the fill and stroke
// now create the fill for the graph below the graph line
path = constructGraph(graphWidth, graphHeight, true)
brush.A = graphA / 2
libui.DrawFill(p.Context, path, &brush)
libui.DrawFreePath(path)
// now draw the histogram line
path = constructGraph(graphWidth, graphHeight, false)
brush.A = graphA
libui.DrawStroke(p.Context, path, &brush, &sp)
libui.DrawFreePath(path)
// now draw the point being hovered over
if currentPoint != -1 {
xs: [10]c.double
ys: [10]c.double
pointLocations(graphWidth, graphHeight, raw_data(&xs), raw_data(&ys))
path = libui.DrawNewPath(.Winding)
libui.DrawPathNewFigureWithArc(path,
xs[currentPoint], ys[currentPoint],
pointRadius,
0, 6.23,
0)
libui.DrawPathEnd(path)
// use the same brush as for the histogram lines
libui.DrawFill(p.Context, path, &brush)
libui.DrawFreePath(path)
}
}
inPoint :: proc(x: c.double, y: c.double, xtest: c.double, ytest: c.double) -> b32 {
x := x
y := y
x -= xoffLeft
y -= yoffTop
return (x >= xtest - pointRadius) &&
(x <= xtest + pointRadius) &&
(y >= ytest - pointRadius) &&
(y <= ytest + pointRadius)
}
handlerMouseEvent :: proc(a: ^libui.AreaHandler, area: ^libui.Area, e: ^libui.AreaMouseEvent) {
graphWidth: c.double
graphHeight: c.double
i: c.int
xs: [10]c.double
ys: [10]c.double
graphSize(e.AreaWidth, e.AreaHeight, &graphWidth, &graphHeight)
pointLocations(graphWidth, graphHeight, raw_data(&xs), raw_data(&ys))
for i in 0..<10 {
if (inPoint(e.X, e.Y, xs[i], ys[i])) {
break
}
}
if i == 10 { // not in a point
i = -1
}
currentPoint = i
libui.AreaQueueRedrawAll(histogram)
}
handlerMouseCrossed :: proc(ah: ^libui.AreaHandler, a: ^libui.Area, left: c.int) {
// do nothing
}
handlerDragBroken :: proc(ah: ^libui.AreaHandler, a: ^libui.Area) {
// do nothing
}
handlerKeyEvent :: proc(ah: ^libui.AreaHandler, a: ^libui.Area, e: ^libui.AreaKeyEvent) -> c.int {
// reject all keys
return 0
}
onDatapointChanged :: proc "c" (s: ^libui.Spinbox, data: rawptr) {
libui.AreaQueueRedrawAll(histogram)
}
onColorChanged :: proc "c" (b: ^libui.ColorButton, data: rawptr) {
libui.AreaQueueRedrawAll(histogram)
}
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.ControlDestroy(cast(^libui.Control)mainwin)
libui.Quit()
return false
}
shouldQuit :: proc "c" (data: rawptr) -> b32 {
libui.ControlDestroy(cast(^libui.Control)mainwin)
return true
}
main :: proc() {
o: libui.InitOptions
brush: libui.DrawBrush
handler.Draw = handlerDraw
handler.MouseEvent = handlerMouseEvent
handler.MouseCrossed = handlerMouseCrossed
handler.DragBroken = handlerDragBroken
handler.KeyEvent = handlerKeyEvent
err := libui.Init(&o)
if err != nil {
fmt.eprintln("error initializing ui: %s", err)
libui.FreeInitError(err)
return
}
libui.OnShouldQuit(shouldQuit, nil)
mainwin = libui.NewWindow("libui Histogram Example", 640, 480, true)
libui.WindowSetMargined(mainwin, true)
libui.WindowOnClosing(mainwin, onClosing, nil)
hbox := libui.NewHorizontalBox()
libui.BoxSetPadded(hbox, true)
libui.WindowSetChild(mainwin, cast(^libui.Control)hbox)
vbox := libui.NewVerticalBox()
libui.BoxSetPadded(vbox, true)
libui.BoxAppend(hbox, cast(^libui.Control)vbox, false)
for i in 0..<10 {
rng := rand.int_max(101)
datapoints[i] = libui.NewSpinbox(0, 100)
libui.SpinboxSetValue(datapoints[i], auto_cast rng)
libui.SpinboxOnChanged(datapoints[i], onDatapointChanged, nil)
libui.BoxAppend(vbox, cast(^libui.Control)datapoints[i], false)
}
colorButton = libui.NewColorButton()
setSolidBrush(&brush, colorDodgerBlue, 1.0)
libui.ColorButtonSetColor(colorButton,
brush.R,
brush.G,
brush.B,
brush.A)
libui.ColorButtonOnChanged(colorButton, onColorChanged, nil)
libui.BoxAppend(vbox, cast(^libui.Control)colorButton, false)
histogram = libui.NewArea(&handler)
libui.BoxAppend(hbox, cast(^libui.Control)histogram, true)
libui.ControlShow(cast(^libui.Control)mainwin)
libui.Main()
libui.Uninit()
}
+79
View File
@@ -0,0 +1,79 @@
package libui_multithread
import "base:runtime"
import "core:c/libc"
import "core:thread"
import "core:sync"
import "core:mem"
import "core:time"
import "../../"
// BUG: The program segfaults on exit. Not sure how to fix it.
e: ^libui.MultilineEntry
cv: sync.Cond
m: sync.Mutex
timeThread: ^thread.Thread
sayTime :: proc "c" (data: rawptr) {
context = runtime.default_context()
s := transmute(cstring)data
libui.MultilineEntryAppend(e, s)
delete(s)
}
threadproc :: proc() {
sync.lock(&m)
for !sync.cond_wait_with_timeout(&cv, &m, time.Second) {
s: cstring
t := libc.time(nil)
base := transmute(cstring)libc.ctime(&t)
mem.copy(&s, &base, len(base))
libui.QueueMain(sayTime, transmute(rawptr)s)
}
}
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
context = runtime.default_context()
sync.cond_broadcast(&cv)
thread.join(timeThread)
libui.Quit()
return true
}
saySomething :: proc "c" (b: ^libui.Button, data: rawptr) {
libui.MultilineEntryAppend(e, "Saying something\n")
}
main :: proc() {
o: libui.InitOptions
if libui.Init(&o) != nil {
return
}
w := libui.NewWindow("Hello", 320, 240, false)
libui.WindowSetMargined(w, true)
b := libui.NewVerticalBox()
libui.BoxSetPadded(b, true)
libui.WindowSetChild(w, cast(^libui.Control)b)
e = libui.NewMultilineEntry()
libui.MultilineEntrySetReadOnly(e, true)
btn := libui.NewButton("Say Something")
libui.ButtonOnClicked(btn, saySomething, nil)
libui.BoxAppend(b, cast(^libui.Control)btn, false)
libui.BoxAppend(b, cast(^libui.Control)e, true)
timeThread = thread.create_and_start(threadproc)
libui.WindowOnClosing(w, onClosing, nil)
libui.ControlShow(cast(^libui.Control)w)
libui.Main()
}
+57
View File
@@ -0,0 +1,57 @@
package libui_timer
import "core:c"
import "core:c/libc"
import "core:fmt"
import "../../"
e: ^libui.MultilineEntry
sayTime :: proc "c" (data: rawptr) -> c.int {
t := libc.time(nil)
s := libc.ctime(&t)
libui.MultilineEntryAppend(e, cast(cstring)s)
return 1
}
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.Quit()
return true
}
saySomething :: proc "c" (b: ^libui.Button, data: rawptr) {
libui.MultilineEntryAppend(e, "Saying something\n")
}
main :: proc() {
o: libui.InitOptions
if libui.Init(&o) != nil {
fmt.println("Failed to initialize libui")
return
}
w := libui.NewWindow("Hello", 320, 240, false)
libui.WindowSetMargined(w, true)
b := libui.NewVerticalBox()
libui.BoxSetPadded(b, true)
libui.WindowSetChild(w, cast(^libui.Control)b)
e = libui.NewMultilineEntry()
libui.MultilineEntrySetReadOnly(e, true)
btn := libui.NewButton("Say Something")
libui.ButtonOnClicked(btn, saySomething, nil)
libui.BoxAppend(b, cast(^libui.Control)btn, false)
libui.BoxAppend(b, cast(^libui.Control)e, true)
libui.Timer(1000, sayTime, nil)
libui.WindowOnClosing(w, onClosing, nil)
libui.ControlShow(cast(^libui.Control)w)
libui.Main()
}
+29
View File
@@ -0,0 +1,29 @@
package libui_window
import "core:fmt"
import "../../"
onClosing :: proc "c" (w: ^libui.Window, data: rawptr) -> b32 {
libui.Quit()
return true
}
main :: proc() {
o: libui.InitOptions
err := libui.Init(&o)
if err != nil {
fmt.eprintln("Error initializing libui-ng: %s", err)
libui.FreeInitError(err)
return
}
// Create a new window
w := libui.NewWindow("Window Title", 300, 30, false)
libui.WindowOnClosing(w, onClosing, nil)
libui.ControlShow(cast(^libui.Control)w)
libui.Main()
libui.Uninit()
}
BIN
View File
Binary file not shown.
+3811
View File
File diff suppressed because it is too large Load Diff