aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.odin256
-rw-r--r--src/time.odin24
-rw-r--r--src/ui_implementation.odin389
3 files changed, 628 insertions, 41 deletions
diff --git a/src/main.odin b/src/main.odin
index 8199e65..698f044 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -1,11 +1,24 @@
package main
+import "../lib/oui"
import "core:fmt"
import "core:math"
import "core:slice"
+import "core:runtime"
import "core:strings"
import rl "vendor:raylib"
+UBUNTU_MONO := #load("../res/UbuntuMono-Regular.ttf")
+
+font : rl.Font
+big_font : rl.Font
+small_font : rl.Font
+
+FRACT_MIN : f32 = 0.0
+FRACT_MAX : f32 = 1.0
+
+c0 : ^oui.Context
+
main :: proc() {
// TODO: Replace the dynamic array of Workday-pointers with
@@ -92,18 +105,20 @@ main :: proc() {
SetWindowMinSize(width, height)
// Loading fonts - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- font_size :: 18
- font: Font = LoadFontEx("res/UbuntuMono-Regular.ttf", font_size, nil, 0)
- defer UnloadFont(font)
-
- small_font_size :: 14
- small_font: Font = LoadFontEx("res/UbuntuMono-Regular.ttf", small_font_size, nil, 0)
- defer UnloadFont(small_font)
- big_font_size :: 24
- big_font: Font = LoadFontEx("res/UbuntuMono-Regular.ttf", big_font_size, nil, 0)
+ small_font = LoadFontFromMemory(".ttf", raw_data(UBUNTU_MONO), i32(len(UBUNTU_MONO)), 14, nil, 0)
+ font = LoadFontFromMemory(".ttf", raw_data(UBUNTU_MONO), i32(len(UBUNTU_MONO)), 18, nil, 0)
+ big_font = LoadFontFromMemory(".ttf", raw_data(UBUNTU_MONO), i32(len(UBUNTU_MONO)), 24, nil, 0)
defer UnloadFont(big_font)
+ defer UnloadFont(font)
+ defer UnloadFont(small_font)
+
+ // oui stuff
+ c0 = new(oui.Context)
+ defer free(c0)
+ oui.context_init(c0, 2048, 2048 * 8)
+ defer oui.context_destroy(c0)
// Setting up the timelines
@@ -122,6 +137,8 @@ main :: proc() {
for !WindowShouldClose() { // MAIN LOOP ---- MAIN LOOP ---- MAIN LOOP ---- MAIN LOOP
+ free_all(context.temp_allocator)
+
if IsWindowResized() {
height = GetScreenHeight()
width = GetScreenWidth()
@@ -142,16 +159,194 @@ main :: proc() {
// clicking will put a white border around the timeblock,
// and display information about the block in the
// bottom left of the screen.
-
+
+ mousePosition: = rl.GetMousePosition()
+ oui.set_cursor(c0, int(mousePosition.x), int(mousePosition.y))
+ if rl.IsMouseButtonPressed(rl.MouseButton(0)) do oui.set_button(c0, .Left, true)
+ if rl.IsMouseButtonReleased(rl.MouseButton(0)) do oui.set_button(c0, .Left, false)
// DRAW
// ------------------------------------------
BeginDrawing()
- ClearBackground(BGCOLOR)
- DrawTextEx(font, "Date", {20, 8}, font_size, 0, RAYWHITE);
- DrawTextEx(font, "Calltime", {105, 8}, font_size, 0, RAYWHITE);
- DrawTextEx(font, "Wraptime", {f32(width)-83, 8}, font_size, 0, RAYWHITE);
+ ClearBackground(rl.RED)
+
+when true {
+ // hotloop
+ oui.begin_layout(c0)
+
+
+ master_container := panel()
+ master_container.id = oui.push_id(c0, "big_mr_boss_man") // Make ID for master thing just because.
+ // Does not need to be freed because master.
+ master_container.layout = .Absolute
+ master_container.sort_children = true
+ master_container.layout_size = {int(GetScreenWidth()), int(GetScreenHeight())}
+
+ {
+ top_bar := panel_line(master_container, theme.background_bar, 30)
+ top_bar.id = oui.push_id(c0, "top_bar") // Make ID for anything that will have children.
+ defer oui.pop_id(c0) // They must be pop'ed before the next equal-in-hiarchy item
+ top_bar.layout_margin = 10
+
+ oui.item_insert(top_bar, label("Date", font, sizings.date, .Center))
+ oui.item_insert(top_bar, label("Calltimes", font, sizings.call, .Center))
+
+ top_bar.layout_cut_children = .Right
+ oui.item_insert(top_bar, label("Price", font, sizings.price, .Center))
+ oui.item_insert(top_bar, label("Wrap", font, sizings.wrap, .Center))
+
+ top_bar.layout_cut_children = .Fill
+ oui.item_insert(top_bar, label("Timeline", font, 0, .Center))
+ }
+
+ {
+ bottom_bar := panel(theme.background_bar)
+ bottom_bar.id = oui.push_id(c0, "bottom_bar")
+ defer oui.pop_id(c0)
+ bottom_bar.layout_cut_children = .Left
+ master_container.layout_cut_children = .Bottom
+ bottom_bar.z_index = 10 // Makes this render over/after the middle section
+ bottom_bar.layout_size.y = 50
+ bottom_bar.layout_margin = 10 // Spacing from edges
+ bottom_bar.layout_cut_gap = 5 // Spacing between children
+ oui.item_insert(master_container, bottom_bar)
+
+ price_reason := label("Reason for price of highlighted timeblock", font, 300,)
+ bottom_bar.layout_cut_children = .Left
+ oui.item_insert(bottom_bar, price_reason)
+
+ totals := panel(theme.background_bar)
+ totals.layout_cut_children = .Top
+ totals.layout_size.x = 50
+ bottom_bar.layout_cut_children = .Right
+ oui.item_insert(bottom_bar, totals)
+
+ pre_sos_price := label("120 000 kr", small_font, 0, .Right)
+ pre_sos_price.layout_size.y = 11
+ oui.item_insert(totals, pre_sos_price)
+
+ post_sos_price := label("160 000 kr", big_font, 300, .Right)
+ post_sos_price.layout_size.y = 27
+ oui.item_insert(totals, post_sos_price)
+ }
+
+ {
+ middle_section := panel(theme.background)
+ middle_section.id = oui.push_id(c0, "middle_section")
+ defer oui.pop_id(c0)
+ middle_section.layout_margin = 10 // Spacing from edges
+ middle_section.layout_cut_gap = sizings.inter_timeline // Spacing between children
+ master_container.layout_cut_children = .Fill
+ oui.item_insert(master_container, middle_section)
+
+
+ // - - - - WORKDAYS - - - -
+
+ FRACT_MAX = workdays[0].fractions[0].start
+ FRACT_MIN = FRACT_MAX // TODO: Optimize this. It doesn't need to re-calculated every frame
+
+ for day, i in &workdays {
+
+ for fract, i in day.fractions {
+ if fract.start < FRACT_MIN do FRACT_MIN = fract.start
+ if fract.end > FRACT_MAX do FRACT_MAX = fract.end
+ if i+1 == day.total_timeblocks do break
+ }
+
+ line := panel_line(middle_section, theme.background)
+ line.layout_cut_children = .Left
+ line.layout_cut_gap = 0
+ line.layout_margin = 0
+ line.layout_size.y = sizings.timeline
+ oui.item_insert(line, label(dayprint(day.call), font, sizings.date, .Center))
+ oui.item_insert(line, label(clockprint(day.call), font, sizings.call, .Center))
+
+ line.layout_cut_children = .Right
+ oui.item_insert(line, label("3500 kr", font, sizings.price, .Center))
+ oui.item_insert(line, label(clockprint(day.wrap), font, sizings.wrap, .Center))
+
+ line.layout_cut_children = .Fill
+ timeline(line, &day)
+ }
+ new_workday := button("+", 100)
+ middle_section.layout_cut_children = .Top
+ oui.item_insert(middle_section, new_workday)
+ if oui.is_clicked(c0, new_workday) do fmt.println("NEW WORKDAY!")
+
+
+
+ // - - - - SIZINGS EDITOR - - - -
+ {
+ oui.item_insert(middle_section, label("Sizings Editor", big_font, 100, .Center))
+
+ // To loop over the members of a struct you need to do this goofy shit:
+ info := runtime.type_info_base(type_info_of(Sizings))
+ st := info.variant.(runtime.Type_Info_Struct)
+ root := uintptr(&sizings)
+ for offset, i in st.offsets {
+
+ line := panel_line(middle_section, theme.background, 25)
+ line.id = oui.push_id(c0, fmt.tprintf("sizings_line_%d", i))
+ defer oui.pop_id(c0)
+
+ oui.item_insert(line, label(st.names[i], font, ))
+
+ // To then access the member of the struct you're looping over
+ // you need to do this shit:
+ // v----------------------v
+ current_value := cast(^int) (root+offset)
+
+ oui.item_insert(line, slider_int(fmt.tprintf("sizings-%d", i), fmt.tprintf("%d", current_value^), 300, current_value, 0, 200))
+ }
+ output_button := button("output sizings", 100)
+ middle_section.layout_cut_children = .Top
+ oui.item_insert(middle_section, output_button)
+ if oui.is_clicked(c0, output_button) do fmt.printf("%#v", sizings)
+ }
+
+
+ // - - - - THEME EDITOR - - - -
+ {
+ oui.item_insert(middle_section, label("Theme Editor", big_font, 100, .Center))
+
+ // To loop over the members of a struct you need to do this goofy shit:
+ info := runtime.type_info_base(type_info_of(Theme))
+ st := info.variant.(runtime.Type_Info_Struct)
+ root := uintptr(&theme)
+ for offset, i in st.offsets {
+
+ line := panel_line(middle_section, theme.background, 25)
+ line.layout_cut_gap = 10
+ line.id = oui.push_id(c0, fmt.tprintf("line_%d", i))
+ defer oui.pop_id(c0)
+
+ oui.item_insert(line, label(st.names[i], font, ))
+
+ // To then access the member of the struct you're looping over
+ // you need to do this shit:
+ // v------------------------v
+ color_sliders(line, cast(^Color) (root+offset))
+ }
+ output_theme_button := button("output theme", 100)
+ middle_section.layout_cut_children = .Top
+ oui.item_insert(middle_section, output_theme_button)
+ if oui.is_clicked(c0, output_theme_button) do fmt.printf("%#v", theme)
+ }
+
+ }
+
+
+ oui.end_layout(c0)
+
+ ui_draw(master_container)
+
+ oui.process(c0)
+
+} else {
+ DrawTextEx(font, "Date", {20, 8}, f32(font.baseSize), 0, RAYWHITE);
+ DrawTextEx(font, "Calltime", {105, 8}, f32(font.baseSize), 0, RAYWHITE);
+ DrawTextEx(font, "Wraptime", {f32(width)-83, 8}, f32(font.baseSize), 0, RAYWHITE);
for day, i in workdays {
@@ -164,17 +359,17 @@ main :: proc() {
// (At least, given how lunch breaks are currently implemented,
// as holes in the workday)
- DrawRectangle(10, DAY_HEIGHT*i32(i+1)-4, width-20, DAY_HEIGHT-1, PBGCOLOR)
+ DrawRectangle(10, DAY_HEIGHT*i32(i+1)-4, width-20, DAY_HEIGHT-1, theme.background_bar)
for block, j in day.blocks {
if j == day.total_timeblocks do break
- block_color: = GREEN
+ block_color: = theme.price_100
switch {
case block.value > 2.1:
- block_color = PURPLE
+ block_color = theme.price_300
case block.value > 1.6:
- block_color = RED
+ block_color = theme.price_200
case block.value > 1.1:
- block_color = ORANGE
+ block_color = theme.price_150
}
DrawRectangle(TIMELINE_START+i32(math.round(day.fractions[j].start*f32(width+TIMELINE_END-TIMELINE_START))),
@@ -188,28 +383,21 @@ main :: proc() {
copy(wrap_text, clockprint(day.wrap))
copy(date_text, toString(day.call))
- text_height = math.round(f32(i+1)*DAY_HEIGHT+(DAY_HEIGHT-font_size)*0.25)
+ text_height := math.round(f32(i+1)*DAY_HEIGHT+(DAY_HEIGHT-f32(font.baseSize))*0.25)
- DrawTextEx(font, cstring(&date_text[0]), {20, text_height}, font_size, 0, RAYWHITE);
- DrawTextEx(font, cstring(&wrap_text[0]), {f32(width)-70, text_height}, font_size, 0, RAYWHITE);
+ DrawTextEx(font, cstring(&date_text[0]), {20, text_height}, f32(font.baseSize), 0, RAYWHITE);
+ DrawTextEx(font, cstring(&wrap_text[0]), {f32(width)-70, text_height}, f32(font.baseSize), 0, RAYWHITE);
if i == len(workdays)-1 {
- DrawTextEx(big_font, "+", {20, DAY_HEIGHT*f32(i+2)}, big_font_size, 0, RAYWHITE)
+ DrawTextEx(big_font, "+", {20, DAY_HEIGHT*f32(i+2)}, f32(big_font.baseSize), 0, RAYWHITE)
}
}
- DrawRectangle(0, height-50, width+10, 60, PBGCOLOR)
-
- DrawTextEx(small_font, total_sum, {f32(width)-120, f32(height)-43}, small_font_size, 0, RAYWHITE);
- DrawTextEx(big_font, inc_soc, {f32(width)-120, f32(height)-29}, big_font_size, 0, RAYWHITE);
+ DrawRectangle(0, height-50, width+10, 60, theme.background_bar)
+ DrawTextEx(small_font, total_sum, {f32(width)-120, f32(height)-43}, f32(small_font.baseSize), 0, RAYWHITE);
+ DrawTextEx(big_font, inc_soc, {f32(width)-120, f32(height)-29}, f32(big_font.baseSize), 0, RAYWHITE);
+}
EndDrawing()
}
}
-
-BGCOLOR : rl.Color : {30, 30, 30, 255}
-PBGCOLOR : rl.Color : {40, 40, 40, 255}
-
-DAY_HEIGHT :: 35
-TIMELINE_START :: 175
-TIMELINE_END :: -85
diff --git a/src/time.odin b/src/time.odin
index 468f5d8..90dca49 100644
--- a/src/time.odin
+++ b/src/time.odin
@@ -90,7 +90,7 @@ new_workday :: proc(previous_wrap : Moment,
// Paragraph 6.7 says that up to 2 hours of unused warned overtime counts as worktime,
// though so that at least one hour of the unused overtime is not counted.
// (It's unclear if an 8-hour day that ends 3 hours in counts as having 5 hours of unused overtime)
- max(clamp(sub(planned_wrap, {0, 1, 0}), wrap, add(wrap, {0, 2, 0})),
+ time_max(time_clamp(sub(planned_wrap, {0, 1, 0}), wrap, add(wrap, {0, 2, 0})),
add(call, {0, 4, 0})), 1, ""}
// ^ Minimum 4 hour day ^
@@ -170,7 +170,7 @@ new_workday :: proc(previous_wrap : Moment,
if getweekday(block.start) == .Sunday do upvalue(&block, 2, "Sunday") // Sundays are +100%
if !(less(call, Moment{0, 7, call.day, call.month, call.year}) &&
- less(min(add(call, Delta{0,8,0}), wrap), Moment{0, 17, call.day, call.month, call.year} )) {
+ less(time_min(add(call, Delta{0,8,0}), wrap), Moment{0, 17, call.day, call.month, call.year} )) {
// This was added for rule 6.11c, but in a world without a defined normal workday,
// that rule is already covered already by 6.11g, so this is empty.
}
@@ -489,7 +489,7 @@ maxDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta {
if sortable(delta_a) > sortable(delta_b) do return delta_a
return delta_b
}
-max :: proc{maxDelta, maxMoment}
+time_max :: proc{maxDelta, maxMoment}
minMoment :: proc(moment_a: Moment, moment_b: Moment) -> Moment {
if sortable(moment_a) < sortable(moment_b) do return moment_a
@@ -499,15 +499,15 @@ minDelta :: proc(delta_a: Delta, delta_b: Delta) -> Delta {
if sortable(delta_a) < sortable(delta_b) do return delta_a
return delta_b
}
-min :: proc{minDelta, minMoment}
+time_min :: proc{minDelta, minMoment}
clampMoment :: proc(moment: Moment, moment_min: Moment, moment_max: Moment) -> Moment {
- return min(max(moment, moment_min), moment_max)
+ return time_min(time_max(moment, moment_min), moment_max)
}
clampDelta :: proc(delta: Delta, delta_min: Delta, delta_max: Delta) -> Delta {
- return min(max(delta, delta_min), delta_max)
+ return time_min(time_max(delta, delta_min), delta_max)
}
-clamp :: proc{clampMoment, clampDelta}
+time_clamp :: proc{clampMoment, clampDelta}
greatMoment :: proc(moment_a: Moment, moment_b: Moment) -> bool {
return bool(sortable(moment_a) > sortable(moment_b))
@@ -702,6 +702,16 @@ clockprintTimeblock :: proc(block: Timeblock) -> string {
}
clockprint :: proc{clockprintTimeblock, clockprintMoment}
+dayprintMoment :: proc(moment: Moment) -> string {
+ using moment
+ return fmt.tprintf("%4i-%2i-%2i", year, month, day)
+}
+dayprintTimeblock :: proc(block: Timeblock) -> string {
+ using block
+ return fmt.tprintf("%s -> %s", dayprint(start), dayprint(end))
+}
+dayprint :: proc{dayprintTimeblock, dayprintMoment}
+
popBlock :: proc(workday: ^Workday, index: int, count: int = 1) {
using workday
when ODIN_DEBUG do fmt.printf("popBlock() running to remove %i block(s) from index %i\n", count, index)
diff --git a/src/ui_implementation.odin b/src/ui_implementation.odin
new file mode 100644
index 0000000..6839b57
--- /dev/null
+++ b/src/ui_implementation.odin
@@ -0,0 +1,389 @@
+package main
+
+import "../lib/oui"
+import "core:strings"
+import "core:fmt"
+import rl "vendor:raylib"
+
+
+Theme :: struct {
+ background: rl.Color,
+ background_bar: rl.Color,
+ button: rl.Color,
+ button_hover: rl.Color,
+ button_click: rl.Color,
+ base: rl.Color,
+ slider_bar: rl.Color,
+ text: rl.Color,
+ price_100: rl.Color,
+ price_150: rl.Color,
+ price_200: rl.Color,
+ price_300: rl.Color,
+}
+
+theme : Theme = {
+ background = {25 , 27 , 29 , 255,},
+ background_bar = {43 , 43 , 48 , 255,},
+ button = {91 , 91 , 204, 255,},
+ button_hover = {91 , 91 , 204, 255,},
+ button_click = {91 , 91 , 204, 255,},
+ base = {60 , 60 , 60 , 255,},
+ slider_bar = {91 , 91 , 204, 255,},
+ text = {255, 255, 255, 252,},
+ price_100 = {30 , 240, 30 , 255,},
+ price_150 = {240, 200, 30 , 255,},
+ price_200 = {240, 30 , 30 , 255,},
+ price_300 = {240, 30 , 240, 255,},
+}
+
+Sizings :: struct {
+ date: int,
+ call: int,
+ wrap: int,
+ price: int,
+ lunch: int,
+ timeline: int,
+ inter_timeline: int,
+}
+sizings : Sizings = {
+ date = 110,
+ call = 85,
+ wrap = 90,
+ price = 100,
+ lunch = 100,
+ timeline = 32,
+ inter_timeline = 5,
+}
+
+DAY_HEIGHT :: 35 // Only here for legacy UI
+TIMELINE_START :: 175 // Only here for legacy UI
+TIMELINE_END :: -85 // Only here for legacy UI
+
+Item :: oui.Item
+Call :: oui.Call
+
+Data_Element :: enum int {
+ Panel,
+ Button,
+ SliderU8,
+ SliderInt,
+ Label,
+ Text_Input,
+ Timeblock,
+ Timeline,
+// ...
+}
+
+Data_Head :: struct {
+ subtype: Data_Element,
+}
+
+Data_Panel :: struct {
+ using _: Data_Head,
+ color: rl.Color,
+}
+
+Data_Button :: struct {
+ using _: Data_Head,
+ text: string,
+ selected: bool,
+}
+
+Data_SliderInt :: struct {
+ using _: Data_Head,
+ text: string,
+ value: ^int,
+ min: int,
+ max: int,
+}
+
+Data_SliderU8 :: struct {
+ using _: Data_Head,
+ text: string,
+ value: ^u8,
+}
+
+Data_Label :: struct {
+ using _: Data_Head,
+ text: string,
+ font: rl.Font,
+ font_size: i32,
+ alignment: Text_Alignment,
+}
+
+Data_Timeline :: struct {
+ using _: Data_Head,
+ day: ^Workday,
+}
+
+panel :: proc(color : rl.Color = rl.RED) -> ^Item {
+ item := oui.item_make(c0)
+
+ data := oui.alloc_typed(c0, item, Data_Panel)
+ data.subtype = .Panel
+ data.color = color
+
+ return item
+}
+
+panel_line :: proc(parent: ^Item, color : rl.Color, height: int = 40) -> (item: ^Item) {
+ item = oui.item_make(c0)
+ item.layout_cut_children = .Left
+ item.layout_size.y = height
+
+ old := parent.layout_cut_children
+ parent.layout_cut_children = .Top
+ oui.item_insert(parent, item)
+ parent.layout_cut_children = old
+
+ data := oui.alloc_typed(c0, item, Data_Panel)
+ data.subtype = .Panel
+ data.color = color
+
+ return
+}
+
+button :: proc(text: string, width: int, selected := false) -> ^Item {
+ item := oui.item_make(c0)
+ item.layout_size = {width, 35}
+ item.callback = button_callback
+ item.id = oui.gen_id(c0, text)
+
+ data := oui.alloc_typed(c0, item, Data_Button)
+ data.subtype = .Button
+ data.text = text
+ data.selected = selected
+
+ return item
+}
+button_callback :: proc(ctxt: ^oui.Context, item: ^Item, event: Call) -> int {
+ data := cast(^Data_Button) item.handle
+
+ #partial switch event {
+ case .Cursor_Handle:
+ //return int(Cursor_Type.Hand)
+ }
+
+ return -1
+}
+
+slider_int :: proc(id: string, text: string, width: int, value: ^int, min: int, max: int) -> ^Item {
+ item := oui.item_make(c0)
+ item.layout_size = {width, 25}
+ item.id = oui.gen_id(c0, id)
+ item.callback = slider_int_callback
+
+ data := oui.alloc_typed(c0, item, Data_SliderInt)
+ data.subtype = .SliderInt
+ data.text = text
+ data.value = value
+ data.min = min
+ data.max = max
+
+ return item
+}
+slider_int_callback :: proc(ctxt: ^oui.Context, item: ^Item, event: Call) -> int {
+ data := cast(^Data_SliderInt) item.handle
+ rect := item.bounds
+
+ #partial switch event {
+ case .Left_Capture:
+ cursor_position := clamp(oui.get_cursor(c0).x, rect.l, rect.r)
+
+ data.value^ = int(f32(data.min) + (f32(data.max - data.min) * (f32(cursor_position - rect.l) / f32(rect.r - rect.l))))
+ }
+
+ return -1
+}
+
+slider_u8 :: proc(id: string, text: string, width: int, value: ^u8) -> ^Item {
+ item := oui.item_make(c0)
+ item.layout_size = {width, 25}
+ item.id = oui.gen_id(c0, id)
+ item.callback = slider_u8_callback
+
+ data := oui.alloc_typed(c0, item, Data_SliderU8)
+ data.subtype = .SliderU8
+ data.text = text
+ data.value = value
+
+ return item
+}
+slider_u8_callback :: proc(ctxt: ^oui.Context, item: ^Item, event: Call) -> int {
+ data := cast(^Data_SliderU8) item.handle
+ rect := item.bounds
+
+ #partial switch event {
+ case .Left_Capture:
+ cursor_position := clamp(oui.get_cursor(c0).x, rect.l, rect.r)
+
+ data.value^ = u8(255*(f32(cursor_position - rect.l) / f32(rect.r - rect.l)))
+ }
+
+ return -1
+}
+color_sliders :: proc(parent: ^Item, color: ^rl.Color) {
+ width :: 167
+ oui.item_insert(parent, slider_u8("slider_r", fmt.tprintf("%d", color.r), width, &color.r))
+ oui.item_insert(parent, slider_u8("slider_g", fmt.tprintf("%d", color.g), width, &color.g))
+ oui.item_insert(parent, slider_u8("slider_b", fmt.tprintf("%d", color.b), width, &color.b))
+ oui.item_insert(parent, slider_u8("slider_a", fmt.tprintf("%d", color.a), width, &color.a))
+}
+
+timeline :: proc(parent: ^Item, day: ^Workday) -> ^Item {
+ item := oui.item_make(c0)
+
+ data := oui.alloc_typed(c0, item, Data_Timeline)
+ data.subtype = .Timeline
+ data.day = day
+
+ oui.item_insert(parent, item)
+ return item
+}
+
+Text_Alignment :: enum int {
+ // Techically called justification, but text_alignment is more self-explanatory.
+ Left,
+ Right,
+ Center,
+}
+label :: proc(text: string, font: rl.Font, width: int = 150, alignment: Text_Alignment = .Left) -> ^Item {
+ item := oui.item_make(c0)
+ item.layout_size = {width, 25}
+
+ data := oui.alloc_typed(c0, item, Data_Label)
+ data.subtype = .Label
+ data.text = text
+ data.font = font
+ data.alignment = alignment
+
+ return item
+}
+calculate_text_alignment :: proc(text: cstring, font: rl.Font, alignment: Text_Alignment, rect: oui.RectI, spacing: f32 = 0) -> (output: [2]int) {
+
+ measurement := rl.MeasureTextEx(font, text, f32(font.baseSize), spacing)
+
+ switch alignment {
+ case .Left:
+ output.x = rect.l
+ case .Right:
+ output.x = rect.r - int(measurement.x)
+ case .Center:
+ output.x = (rect.l+(rect.r-rect.l)/2) - int(measurement.x/2)
+ }
+
+ output.y = (rect.t+(rect.b-rect.t)/2) - int(measurement.y/2)
+
+ return
+}
+i2f :: proc "contextless" (input: [2]int) -> rl.Vector2 {
+ return { f32(input.x), f32(input.y) }
+}
+f2i :: proc "contextless" (input: [2]f32) -> [2]int {
+ return { int(input.x), int(input.y) }
+}
+
+// recursive loop
+ui_draw_children :: proc(item: ^oui.Item) {
+ list := oui.children_list(c0, item)
+ for kid in list {
+ ui_draw(kid)
+ }
+}
+
+ui_draw :: proc(item: ^oui.Item) {
+ head := cast(^Data_Head) item.handle
+ rect := item.bounds
+
+ //fmt.println(rect, head, item)
+
+ if head == nil {
+ ui_draw_children(item)
+ return
+ }
+
+ #partial switch head.subtype {
+ //case .Panel_Root:
+ // ... render any type of item
+ case .Button:
+ data := cast(^Data_Button) item.handle
+ text_spacing := clamp(item.anim.hot - item.anim.active, 0, item.anim.hot)*2
+ rl.DrawRectangle(i32(rect.l), i32(rect.t), i32(rect.r-rect.l), i32(rect.b-rect.t), theme.button)
+
+ text := strings.clone_to_cstring(data.text, context.temp_allocator)
+ position := calculate_text_alignment(text, font, .Center, rect, text_spacing)
+
+ rl.DrawTextEx(font, text, i2f(position), f32(font.baseSize), text_spacing, theme.text);
+
+ //fmt.println(item.anim)
+
+ case .SliderInt:
+ data := cast(^Data_SliderInt) head
+ rl.DrawRectangle(i32(rect.l), i32(rect.t), i32(rect.r-rect.l), i32(rect.b-rect.t), theme.base)
+ rl.DrawRectangle(i32(rect.l+1), i32(rect.t+1), i32(f32(rect.r-rect.l)*(f32(data.value^)/f32(data.max))-2), i32(rect.b-rect.t-2), theme.slider_bar)
+
+ text := strings.clone_to_cstring(data.text, context.temp_allocator)
+ position := calculate_text_alignment(text, font, .Center, rect)
+
+ rl.DrawTextEx(font, text, i2f(position), f32(font.baseSize), 0.0, theme.text);
+
+ case .SliderU8:
+ data := cast(^Data_SliderU8) head
+ rl.DrawRectangle(i32(rect.l), i32(rect.t), i32(rect.r-rect.l), i32(rect.b-rect.t), theme.base)
+ rl.DrawRectangle(i32(rect.l+1), i32(rect.t+1), i32(f32(rect.r-rect.l)*(f32(data.value^)/255)-2), i32(rect.b-rect.t-2), theme.slider_bar)
+
+ text := strings.clone_to_cstring(data.text, context.temp_allocator)
+ position := calculate_text_alignment(text, font, .Center, rect)
+
+ rl.DrawTextEx(font, text, i2f(position), f32(font.baseSize), 0.0, theme.text);
+
+ case .Panel:
+ data := cast(^Data_Panel) head
+ rl.DrawRectangle(i32(rect.l), i32(rect.t), i32(rect.r-rect.l), i32(rect.b-rect.t), data.color)
+ ui_draw_children(item)
+
+ case .Label:
+ data := cast(^Data_Label) item.handle
+ text := strings.clone_to_cstring(data.text, context.temp_allocator)
+ position := calculate_text_alignment(text, data.font, data.alignment, rect)
+
+ rl.DrawTextEx(data.font, text, i2f(position), f32(data.font.baseSize), 0.0, theme.text);
+
+ case .Timeline:
+ data := cast(^Data_Timeline) item.handle
+
+ width := int(f32(rect.r - rect.l)/(FRACT_MAX - FRACT_MIN))
+
+ for fracts, i in data.day.fractions {
+
+ color := theme.price_100
+ value := data.day.blocks[i].value
+
+ switch {
+ case value>2.1:
+ color = theme.price_300
+ case value>1.6:
+ color = theme.price_200
+ case value>1.1:
+ color = theme.price_150
+ }
+
+
+
+ rl.DrawRectangle(i32(rect.l + int(f32(width)*fracts.start) - int(f32(width)*FRACT_MIN)),
+ i32(rect.t),
+ i32(f32(width) * (fracts.end - fracts.start)+0.99),
+ i32(rect.b - rect.t),
+ color)
+ // Dark middle of blocks, glowing edge. Disabled for now.
+ /*rl.DrawRectangle(i32(rect.l + int(f32(width)*fracts.start) - int(f32(width)*FRACT_MIN) + 1),
+ i32(rect.t) + 1,
+ i32(f32(width+1) * (fracts.end - fracts.start)-1.01),
+ i32(rect.b - rect.t)-2,
+ {0,0,0,100})*/
+ if i+1 == data.day.total_timeblocks {
+ break
+ }
+ }
+ }
+} \ No newline at end of file