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 } } } }