main.odin ¶
1package src
2
3
4
5
6
7
8
9
10
11
12import "core:log"
13import "base:runtime"
14import "core:fmt"
15import oc "core:sys/orca"
16
17ctx: runtime.Context
18
19frameSize := oc.vec2{1200, 838}
20
21surface: oc.surface
22renderer: oc.canvas_renderer
23canvas: oc.canvas_context
24fontRegular: oc.font
25fontBold: oc.font
26ui: oc.ui_context
27textArena: oc.arena
28logArena: oc.arena
29logLines: oc.str8_list
30
31
32labelFont: ^oc.font
33
34cmd :: enum {
35 NONE,
36 SET_DARK_THEME,
37 SET_LIGHT_THEME,
38}
39
40command := cmd.NONE
41
42main :: proc() {
43 context.logger = oc.create_odin_logger()
44 ctx = context
45
46 oc.window_set_title("Orca UI Demo")
47 oc.window_set_size(frameSize)
48
49 renderer = oc.canvas_renderer_create()
50 surface = oc.canvas_surface_create(renderer)
51 canvas = oc.canvas_context_create()
52 oc.ui_init(&ui)
53
54 fonts := [2]^oc.font{&fontRegular, &fontBold}
55 fontNames := [2]string{"/OpenSans-Regular.ttf", "/OpenSans-Bold.ttf"}
56 for i in 0 ..< len(fontNames) {
57 scratch := oc.scratch_begin()
58
59 file := oc.file_open(fontNames[i], {.READ}, {})
60 if oc.file_last_error(file) != .OK {
61 log.errorf("Couldn't open file %s\n", fontNames[i])
62 }
63 size := oc.file_size(file)
64 buffer := cast([^]byte)oc.arena_push(scratch.arena, size)
65 oc.file_read(file, size, buffer)
66 oc.file_close(file)
67 ranges := [?]oc.unicode_range {
68 oc.UNICODE_BASIC_LATIN,
69 oc.UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
70 oc.UNICODE_LATIN_EXTENDED_A,
71 oc.UNICODE_LATIN_EXTENDED_B,
72 oc.UNICODE_SPECIALS,
73 }
74
75 fonts[i]^ = oc.font_create_from_memory(oc.str8_from_buffer(size, buffer), 5, &ranges[0])
76 oc.scratch_end(scratch)
77 }
78 labelFont = &fontRegular
79
80 oc.arena_init(&textArena)
81 oc.arena_init(&logArena)
82 oc.list_init(&logLines.list)
83}
84
85@(export)
86oc_on_raw_event :: proc "c" (event: ^oc.event) {
87 oc.ui_process_event(event)
88}
89
90@(export)
91oc_on_resize :: proc "c" (width, height: u32) {
92 frameSize.x = f32(width)
93 frameSize.y = f32(height)
94}
95
96log_push :: proc(line: string) {
97 oc.str8_list_push(&logArena, &logLines, line)
98}
99
100log_pushf :: proc(format: string, args: ..any) {
101 str := fmt.tprintf(format, ..args)
102 oc.str8_list_push(&logArena, &logLines, str)
103}
104
105column_begin :: proc(header: string, widthFraction: f32) {
106 style := oc.ui_style {
107 size = {{.PARENT, widthFraction, 1, 0}, {.PARENT, 1, 0, 0}},
108 layout = {axis = .Y, margin = {0, 8}, spacing = 24},
109 bgColor = ui.theme.bg1,
110 borderColor = ui.theme.border,
111 borderSize = 1,
112 roundness = ui.theme.roundnessSmall,
113 }
114
115 style_mask :=
116 oc.SIZE +
117 {
118 .LAYOUT_AXIS,
119 .LAYOUT_MARGIN_Y,
120 .LAYOUT_SPACING,
121 .BG_COLOR,
122 .BORDER_COLOR,
123 .BORDER_SIZE,
124 .ROUNDNESS,
125 }
126
127 oc.ui_style_next(style, style_mask)
128 oc.ui_box_begin_str8(header, {.DRAW_BACKGROUND, .DRAW_BORDER})
129
130 {
131 style = oc.ui_style {
132 size = {{.PARENT, 1, 0, 0}, {}},
133 layout = {align = {.CENTER, .START}},
134 }
135 oc.ui_style_next(style, {.SIZE_WIDTH, .LAYOUT_ALIGN_X})
136 oc.ui_container("header", {})
137
138 oc.ui_style_next({fontSize = 18}, {.FONT_SIZE})
139 oc.ui_label_str8(header)
140 }
141
142 style = {
143 size = {{.PARENT, 1, 0, 0}, {.PARENT, 1, 1, 0}},
144 layout = {align = {.START, .START}, margin = {16, 0}, spacing = 24},
145 }
146 oc.ui_style_next(style, oc.SIZE + {.LAYOUT_ALIGN_X, .LAYOUT_MARGIN_X, .LAYOUT_SPACING})
147 oc.ui_box_begin_str8("contents", {})
148}
149
150column_end :: proc() {
151 oc.ui_box_end()
152 oc.ui_box_end()
153}
154
155@(deferred_none = column_end)
156column :: proc(header: string, widthFraction: f32) {
157 column_begin(header, widthFraction)
158}
159
160labeled_slider :: proc(label: string, value: ^f32) {
161 oc.ui_style_next({layout = {axis = .X, spacing = 8}}, {.LAYOUT_AXIS, .LAYOUT_SPACING})
162 oc.ui_container(label, {})
163
164 oc.ui_style_match_after(
165 oc.ui_pattern_owner(),
166 {size = {{.PIXELS, 100, 0, 0}, {}}},
167 {.SIZE_WIDTH},
168 )
169 oc.ui_label_str8(label)
170
171 oc.ui_style_next({size = {{.PIXELS, 100, 0, 0}, {}}}, {.SIZE_WIDTH})
172 oc.ui_slider("slider", value)
173}
174
175
176
177@(export)
178oc_on_frame_refresh :: proc "c" () {
179 context = ctx
180
181 scratch := oc.scratch_begin()
182
183 #partial switch command {
184 case .SET_DARK_THEME:
185 oc.ui_set_theme(&oc.UI_DARK_THEME)
186 case .SET_LIGHT_THEME:
187 oc.ui_set_theme(&oc.UI_LIGHT_THEME)
188 }
189 command = .NONE
190
191 defaultStyle := oc.ui_style {
192 font = fontRegular,
193 }
194 {
195 oc.ui_frame(frameSize, defaultStyle, {.FONT})
196
197
198
199 {
200 oc.ui_menu_bar("menu_bar")
201
202 {
203 oc.ui_menu("File")
204 if oc.ui_menu_button("Quit").pressed {
205 oc.request_quit()
206 }
207 }
208
209 {
210 oc.ui_menu("Theme")
211 if oc.ui_menu_button("Dark theme").pressed {
212 command = .SET_DARK_THEME
213 }
214 if oc.ui_menu_button("Light theme").pressed {
215 command = .SET_LIGHT_THEME
216 }
217 }
218 }
219
220 {
221 oc.ui_panel("main panel", {})
222 oc.ui_style_next(
223 {
224 size = {{.PARENT, 1, 0, 0}, {.PARENT, 1, 1, 0}},
225 layout = {axis = .X, margin = 16, spacing = 16},
226 },
227 oc.SIZE + oc.LAYOUT_MARGINS + {.LAYOUT_AXIS, .LAYOUT_SPACING},
228 )
229
230 {
231 oc.ui_container("background", {.DRAW_BACKGROUND})
232 {
233 column("Widgets", f32(1.0) / 3)
234
235 {
236 oc.ui_style_next(
237 {size = {{.PARENT, 1, 0, 0}, {}}, layout = {axis = .X, spacing = 32}},
238 {.SIZE_WIDTH, .LAYOUT_AXIS, .LAYOUT_SPACING},
239 )
240 oc.ui_container("top", {})
241
242 {
243 oc.ui_style_next(
244 {layout = {axis = .Y, spacing = 24}},
245 {.LAYOUT_AXIS, .LAYOUT_SPACING},
246 )
247 oc.ui_container("top_left", {})
248
249
250
251
252 oc.ui_label("Label")
253
254
255
256
257 if oc.ui_button("Button").clicked {
258 log_push("Button clicked")
259 }
260
261 oc.ui_style_next(
262 {layout = {axis = .X, align = {{}, .CENTER}, spacing = 8}},
263 {.LAYOUT_AXIS, .LAYOUT_ALIGN_Y, .LAYOUT_SPACING},
264 )
265
266 {
267 oc.ui_container("checkbox", {})
268
269
270
271
272 @(static)
273 checked := false
274 if oc.ui_checkbox("checkbox", &checked).clicked {
275 if checked {
276 log_push("Checkbox checked")
277 } else {
278 log_push("Checkbox unchecked")
279 }
280 }
281
282 oc.ui_label("Checkbox")
283 }
284 }
285
286
287
288
289 @(static)
290 vSliderValue := f32(0)
291 @(static)
292 vSliderLoggedValue := f32(0)
293 @(static)
294 vSliderLogTime := f64(0)
295 oc.ui_style_next({size = {{}, {.PIXELS, 130, 0, 0}}}, {.SIZE_HEIGHT})
296 oc.ui_slider("v_slider", &vSliderValue)
297 now := oc.clock_time(.MONOTONIC)
298 if (now - vSliderLogTime) >= 0.2 && vSliderValue != vSliderLoggedValue {
299 log_pushf("Vertical slider moved to %f", vSliderValue)
300 vSliderLoggedValue = vSliderValue
301 vSliderLogTime = now
302 }
303
304 {
305 oc.ui_style_next(
306 {layout = {axis = .Y, spacing = 24}},
307 {.LAYOUT_AXIS, .LAYOUT_SPACING},
308 )
309 oc.ui_container("top_right", {})
310
311
312
313
314 if oc.ui_label("Tooltip").hovering {
315 oc.ui_tooltip("Hi")
316 }
317
318
319
320
321 @(static)
322 radioSelected: i32 = 0
323 options := [?]oc.str8{"Radio 1", "Radio 2"}
324 radioGroupInfo := oc.ui_radio_group_info {
325 selectedIndex = radioSelected,
326 optionCount = 2,
327 options = &options[0],
328 }
329 result := oc.ui_radio_group("radio_group", &radioGroupInfo)
330 radioSelected = result.selectedIndex
331 if result.changed {
332 log_pushf("Selected Radio %i", result.selectedIndex + 1)
333 }
334
335
336
337
338 @(static)
339 hSliderValue := f32(0)
340 @(static)
341 hSliderLoggedValue := f32(0)
342 @(static)
343 hSliderLogTime := f64(0)
344 oc.ui_style_next({size = {{.PIXELS, 130, 0, 0}, {}}}, {.SIZE_WIDTH})
345 oc.ui_slider("h_slider", &hSliderValue)
346 now = oc.clock_time(.MONOTONIC)
347 if (now - hSliderLogTime) >= 0.2 &&
348 hSliderValue != hSliderLoggedValue {
349 log_pushf("Slider moved to %f", hSliderValue)
350 hSliderLoggedValue = hSliderValue
351 hSliderLogTime = now
352 }
353 }
354 }
355
356
357
358
359 oc.ui_style_next({size = {{.PIXELS, 305, 0, 0}, {.TEXT, 0, 0, 0}}}, oc.SIZE)
360 @(static)
361 text: oc.str8
362 res := oc.ui_text_box("text", scratch.arena, text)
363 if res.changed {
364 oc.arena_clear(&textArena)
365 text = oc.str8_push_copy(&textArena, res.text)
366 }
367 if res.accepted {
368 log_pushf("Entered text \"%s\"", text)
369 }
370
371
372
373
374 @(static)
375 selected: i32 = -1
376 options := [?]oc.str8{"Option 1", "Option 2"}
377 info := oc.ui_select_popup_info {
378 selectedIndex = selected,
379 optionCount = 2,
380 options = &options[0],
381 placeholder = "Select",
382 }
383 result := oc.ui_select_popup("select", &info)
384 if result.selectedIndex != selected {
385 log_pushf("Selected %s", options[result.selectedIndex])
386 }
387 selected = result.selectedIndex
388
389
390
391
392 oc.ui_style_next(
393 {
394 size = {{.PARENT, 1, 0, 0}, {.PARENT, 1, 1, 0}},
395 bgColor = ui.theme.bg2,
396 borderColor = ui.theme.border,
397 borderSize = 1,
398 roundness = ui.theme.roundnessSmall,
399 },
400 oc.SIZE + {.BG_COLOR, .BORDER_COLOR, .BORDER_SIZE, .ROUNDNESS},
401 )
402
403 {
404 oc.ui_panel("log", {.DRAW_BACKGROUND, .DRAW_BORDER})
405
406 {
407 oc.ui_style_next(
408 {layout = {margin = 16}},
409 oc.LAYOUT_MARGINS + {.LAYOUT_SPACING},
410 )
411 oc.ui_container("contents", {})
412
413 if oc.list_empty(logLines.list) {
414 oc.ui_style_next({_color = ui.theme.text2}, {.COLOR})
415 oc.ui_label("Log")
416 }
417
418 iter: ^oc.list_elt
419 i: int
420 for logLine in oc.list_for(
421 &logLines.list,
422 &iter,
423 oc.str8_elt,
424 "listElt",
425 ) {
426 id := fmt.tprintf("%d", i)
427
428 oc.ui_container(id, {})
429 oc.ui_label_str8(logLine.string)
430
431 i += 1
432 }
433 }
434 }
435 }
436
437
438
439
440
441
442
443
444
445
446 {
447 column("Styling", f32(2.0) / 3)
448 @(static)
449 unselectedWidth := f32(16)
450 @(static)
451 unselectedHeight := f32(16)
452 @(static)
453 unselectedRoundness := f32(8)
454 @(static)
455 unselectedBgColor := oc.color{{0.086, 0.086, 0.102, 1}, .RGB}
456 @(static)
457 unselectedBorderColor := oc.color{{0.976, 0.976, 0.976, 0.35}, .RGB}
458 @(static)
459 unselectedBorderSize := f32(1)
460 @(static)
461 unselectedWhenStatus: oc.ui_status
462
463 @(static)
464 selectedWidth := f32(16)
465 @(static)
466 selectedHeight := f32(16)
467 @(static)
468 selectedRoundness := f32(8)
469 @(static)
470 selectedCenterColor := oc.color{{1, 1, 1, 1}, .RGB}
471 @(static)
472 selectedBgColor := oc.color{{0.33, 0.66, 1, 1}, .RGB}
473 @(static)
474 selectedWhenStatus: oc.ui_status
475
476 @(static)
477 labelFontColor := oc.color{{0.976, 0.976, 0.976, 1}, .RGB}
478 @(static)
479 labelFontSize := f32(14)
480
481 oc.ui_style_next(
482 {
483 size = {{.PARENT, 1, 0, 0}, {.PIXELS, 152, 0, 0}},
484 layout = {margin = {320, 16}},
485 bgColor = oc.UI_DARK_THEME.bg0,
486 roundness = oc.UI_DARK_THEME.roundnessSmall,
487 },
488 oc.SIZE + oc.LAYOUT_MARGINS + {.BG_COLOR, .ROUNDNESS},
489 )
490
491 {
492 oc.ui_container("styled_radios", {.DRAW_BACKGROUND, .DRAW_BORDER})
493 reset_next_radio_group_to_dark_theme(scratch.arena)
494
495 unselectedPattern := oc.ui_pattern{}
496 oc.ui_pattern_push(
497 scratch.arena,
498 &unselectedPattern,
499 {kind = .TAG, tag = oc.ui_tag_make_str8("radio")},
500 )
501
502 if unselectedWhenStatus != {} {
503 oc.ui_pattern_push(
504 scratch.arena,
505 &unselectedPattern,
506 {op = .AND, kind = .STATUS, status = unselectedWhenStatus},
507 )
508 }
509 oc.ui_style_match_after(
510 unselectedPattern,
511 {
512 size = {
513 {.PIXELS, unselectedWidth, 0, 0},
514 {.PIXELS, unselectedHeight, 0, 0},
515 },
516 bgColor = unselectedBgColor,
517 borderColor = unselectedBorderColor,
518 borderSize = unselectedBorderSize,
519 roundness = unselectedRoundness,
520 },
521 oc.SIZE + {.BG_COLOR, .BORDER_COLOR, .BORDER_SIZE, .ROUNDNESS},
522 )
523
524 selectedPattern := oc.ui_pattern{}
525 oc.ui_pattern_push(
526 scratch.arena,
527 &selectedPattern,
528 {kind = .TAG, tag = oc.ui_tag_make_str8("radio_selected")},
529 )
530
531 if selectedWhenStatus != {} {
532 oc.ui_pattern_push(
533 scratch.arena,
534 &selectedPattern,
535 {op = .AND, kind = .STATUS, status = selectedWhenStatus},
536 )
537 }
538 oc.ui_style_match_after(
539 selectedPattern,
540 {
541 size = {
542 {.PIXELS, selectedWidth, 0, 0},
543 {.PIXELS, selectedHeight, 0, 0},
544 },
545 _color = selectedCenterColor,
546 bgColor = selectedBgColor,
547 roundness = selectedRoundness,
548 },
549 oc.SIZE + {.COLOR, .BG_COLOR, .ROUNDNESS},
550 )
551
552 labelPattern := oc.ui_pattern{}
553 labelTag := oc.ui_tag_make_str8("label")
554 oc.ui_pattern_push(
555 scratch.arena,
556 &labelPattern,
557 {kind = .TAG, tag = labelTag},
558 )
559 oc.ui_style_match_after(
560 labelPattern,
561 {_color = labelFontColor, font = labelFont^, fontSize = labelFontSize},
562 {.COLOR, .FONT, .FONT_SIZE},
563 )
564
565 @(static)
566 selectedIndex: i32 = 0
567 options := [?]oc.str8{"I", "Am", "Stylish"}
568 radioGroupInfo := oc.ui_radio_group_info {
569 selectedIndex = selectedIndex,
570 optionCount = len(options),
571 options = &options[0],
572 }
573 result := oc.ui_radio_group("radio_group", &radioGroupInfo)
574 selectedIndex = result.selectedIndex
575 }
576
577 {
578 oc.ui_style_next(
579 {layout = {axis = .X, spacing = 32}},
580 {.LAYOUT_AXIS, .LAYOUT_SPACING},
581 )
582 oc.ui_container("controls", {})
583
584 {
585 oc.ui_style_next(
586 {layout = {axis = .Y, spacing = 16}},
587 {.LAYOUT_AXIS, .LAYOUT_SPACING},
588 )
589 oc.ui_container("unselected", {})
590
591 oc.ui_style_next({fontSize = 16}, {.FONT_SIZE})
592 oc.ui_label("Radio style")
593
594 {
595 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
596 oc.ui_container("size", {})
597
598 widthSlider := f32(unselectedWidth - 8) / 16
599 labeled_slider("Width", &widthSlider)
600 unselectedWidth = 8 + widthSlider * 16
601
602 heightSlider := f32(unselectedHeight - 8) / 16
603 labeled_slider("Height", &heightSlider)
604 unselectedHeight = 8 + heightSlider * 16
605
606 roundnessSlider := f32(unselectedRoundness - 4) / 8
607 labeled_slider("Roundness", &roundnessSlider)
608 unselectedRoundness = 4 + roundnessSlider * 8
609 }
610
611 {
612 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
613 oc.ui_container("background", {})
614
615 labeled_slider("Background R", &unselectedBgColor.r)
616 labeled_slider("Background G", &unselectedBgColor.g)
617 labeled_slider("Background B", &unselectedBgColor.b)
618 labeled_slider("Background A", &unselectedBgColor.a)
619 }
620
621 {
622 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
623 oc.ui_container("border", {})
624
625 labeled_slider("Border R", &unselectedBorderColor.r)
626 labeled_slider("Border G", &unselectedBorderColor.g)
627 labeled_slider("Border B", &unselectedBorderColor.b)
628 labeled_slider("Border A", &unselectedBorderColor.a)
629 }
630
631 borderSizeSlider := f32(unselectedBorderSize) / 5
632 labeled_slider("Border size", &borderSizeSlider)
633 unselectedBorderSize = borderSizeSlider * 5
634
635 {
636 oc.ui_style_next({layout = {spacing = 10}}, {.LAYOUT_SPACING})
637 oc.ui_container("status_override", {})
638
639 oc.ui_label("Override")
640
641 @(static)
642 statusIndex: i32 = 0
643 statusOptions := [?]oc.str8 {
644 "Always",
645 "When hovering",
646 "When active",
647 }
648 statusInfo := oc.ui_radio_group_info {
649 selectedIndex = statusIndex,
650 optionCount = len(statusOptions),
651 options = &statusOptions[0],
652 }
653 result := oc.ui_radio_group("status", &statusInfo)
654 statusIndex = result.selectedIndex
655 switch statusIndex {
656 case 0:
657 unselectedWhenStatus = {}
658 case 1:
659 unselectedWhenStatus = {.HOVER}
660 case 2:
661 unselectedWhenStatus = {.ACTIVE}
662 }
663 }
664 }
665
666 {
667 oc.ui_style_next(
668 {layout = {axis = .Y, spacing = 16}},
669 {.LAYOUT_AXIS, .LAYOUT_SPACING},
670 )
671 oc.ui_container("selected", {})
672
673 oc.ui_style_next({fontSize = 16}, {.FONT_SIZE})
674 oc.ui_label("Radio selected style")
675
676 {
677 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
678 oc.ui_container("size", {})
679
680 widthSlider := f32(selectedWidth - 8) / 16
681 labeled_slider("Width", &widthSlider)
682 selectedWidth = 8 + widthSlider * 16
683
684 heightSlider := f32(selectedHeight - 8) / 16
685 labeled_slider("Height", &heightSlider)
686 selectedHeight = 8 + heightSlider * 16
687
688 roundnessSlider := f32(selectedRoundness - 4) / 8
689 labeled_slider("Roundness", &roundnessSlider)
690 selectedRoundness = 4 + roundnessSlider * 8
691 }
692
693 {
694 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
695 oc.ui_container("color", {})
696
697 labeled_slider("Center R", &selectedCenterColor.r)
698 labeled_slider("Center G", &selectedCenterColor.g)
699 labeled_slider("Center B", &selectedCenterColor.b)
700 labeled_slider("Center A", &selectedCenterColor.a)
701 }
702
703 {
704 oc.ui_style_next({layout = {spacing = 4}}, {.LAYOUT_SPACING})
705 oc.ui_container("background", {})
706
707 labeled_slider("Background R", &selectedBgColor.r)
708 labeled_slider("Background G", &selectedBgColor.g)
709 labeled_slider("Background B", &selectedBgColor.b)
710 labeled_slider("Background A", &selectedBgColor.a)
711 }
712
713 {
714 oc.ui_style_next({layout = {spacing = 10}}, {.LAYOUT_SPACING})
715 oc.ui_container("status_override", {})
716
717 oc.ui_style_next(
718 {size = {{}, {.PIXELS, 30, 0, 0}}},
719 {.SIZE_HEIGHT},
720 )
721 oc.ui_box_make_str8("spacer", {})
722 oc.ui_label("Override")
723
724 @(static)
725 statusIndex: i32 = 0
726 statusOptions := [?]oc.str8 {
727 "Always",
728 "When hovering",
729 "When active",
730 }
731 statusInfo := oc.ui_radio_group_info {
732 selectedIndex = statusIndex,
733 optionCount = len(statusOptions),
734 options = &statusOptions[0],
735 }
736 result := oc.ui_radio_group("status", &statusInfo)
737 statusIndex = result.selectedIndex
738 switch statusIndex {
739 case 0:
740 selectedWhenStatus = {}
741 case 1:
742 selectedWhenStatus = {.HOVER}
743 case 2:
744 selectedWhenStatus = {.ACTIVE}
745 }
746 }
747 }
748
749 {
750 oc.ui_style_next(
751 {layout = {axis = .Y, spacing = 16}},
752 {.LAYOUT_AXIS, .LAYOUT_SPACING},
753 )
754 oc.ui_container("label", {})
755
756 oc.ui_style_next({fontSize = 16}, {.FONT_SIZE})
757 oc.ui_label("Label style")
758
759 {
760 oc.ui_style_next(
761 {layout = {axis = .X, spacing = 8}},
762 {.LAYOUT_AXIS, .LAYOUT_SPACING},
763 )
764 oc.ui_container("font_color", {})
765
766 oc.ui_style_match_after(
767 oc.ui_pattern_owner(),
768 {size = {{.PIXELS, 100, 0, 0}, {}}},
769 {.SIZE_WIDTH},
770 )
771 oc.ui_label("Font color")
772
773 @(static)
774 colorSelected: i32 = 0
775 colorNames := [?]oc.str8 {
776 "Default",
777 "Red",
778 "Orange",
779 "Amber",
780 "Yellow",
781 "Lime",
782 "Light Green",
783 "Green",
784 }
785 colors := [?]oc.color {
786 oc.UI_DARK_THEME.text0,
787 oc.UI_DARK_THEME.palette.red5,
788 oc.UI_DARK_THEME.palette.orange5,
789 oc.UI_DARK_THEME.palette.amber5,
790 oc.UI_DARK_THEME.palette.yellow5,
791 oc.UI_DARK_THEME.palette.lime5,
792 oc.UI_DARK_THEME.palette.lightGreen5,
793 oc.UI_DARK_THEME.palette.green5,
794 }
795 colorInfo := oc.ui_select_popup_info {
796 selectedIndex = colorSelected,
797 optionCount = len(colorNames),
798 options = &colorNames[0],
799 }
800 colorResult := oc.ui_select_popup("color", &colorInfo)
801 colorSelected = colorResult.selectedIndex
802 labelFontColor = colors[colorSelected]
803 }
804
805 {
806 oc.ui_style_next(
807 {layout = {axis = .X, spacing = 8}},
808 {.LAYOUT_AXIS, .LAYOUT_SPACING},
809 )
810 oc.ui_container("font", {})
811
812 oc.ui_style_match_after(
813 oc.ui_pattern_owner(),
814 {size = {{.PIXELS, 100, 0, 0}, {}}},
815 {.SIZE_WIDTH},
816 )
817 oc.ui_label("Font")
818
819 @(static)
820 fontSelected: i32 = 0
821 fontNames := [?]oc.str8{"Regular", "Bold"}
822 fonts := [?]^oc.font{&fontRegular, &fontBold}
823 fontInfo := oc.ui_select_popup_info {
824 selectedIndex = fontSelected,
825 optionCount = len(fontNames),
826 options = &fontNames[0],
827 }
828 fontResult := oc.ui_select_popup("font_style", &fontInfo)
829 fontSelected = fontResult.selectedIndex
830 labelFont = fonts[fontSelected]
831 }
832
833 fontSizeSlider := f32(labelFontSize - 8) / 16
834 labeled_slider("Font size", &fontSizeSlider)
835 labelFontSize = 8 + fontSizeSlider * 16
836 }
837 }
838 }
839 }
840 }
841 }
842
843 oc.canvas_context_select(canvas)
844
845 oc.set_color(ui.theme.bg0)
846 oc.clear()
847
848 oc.ui_draw()
849 oc.canvas_render(renderer, canvas, surface)
850 oc.canvas_present(renderer, surface)
851
852 oc.scratch_end(scratch)
853}
854
855
856
857reset_next_radio_group_to_dark_theme :: proc "c" (arena: ^oc.arena) {
858 defaultTag := oc.ui_tag_make_str8("radio")
859 defaultPattern := oc.ui_pattern{}
860 oc.ui_pattern_push(arena, &defaultPattern, {kind = .TAG, tag = defaultTag})
861 defaultStyle := oc.ui_style {
862 borderColor = oc.UI_DARK_THEME.text3,
863 borderSize = 1,
864 }
865 defaultMask: oc.ui_style_mask = {.BORDER_COLOR, .BORDER_SIZE}
866 oc.ui_style_match_after(defaultPattern, defaultStyle, defaultMask)
867
868 hoverPattern := oc.ui_pattern{}
869 oc.ui_pattern_push(arena, &hoverPattern, {kind = .TAG, tag = defaultTag})
870 oc.ui_pattern_push(arena, &hoverPattern, {op = .AND, kind = .STATUS, status = {.HOVER}})
871 hoverStyle := oc.ui_style {
872 bgColor = oc.UI_DARK_THEME.fill0,
873 borderColor = oc.UI_DARK_THEME.primary,
874 }
875 hoverMask: oc.ui_style_mask = {.BG_COLOR, .BORDER_COLOR}
876 oc.ui_style_match_after(hoverPattern, hoverStyle, hoverMask)
877
878 activePattern := oc.ui_pattern{}
879 oc.ui_pattern_push(arena, &activePattern, {kind = .TAG, tag = defaultTag})
880 oc.ui_pattern_push(arena, &activePattern, {op = .AND, kind = .STATUS, status = {.ACTIVE}})
881 activeStyle := oc.ui_style {
882 bgColor = oc.UI_DARK_THEME.fill1,
883 borderColor = oc.UI_DARK_THEME.primary,
884 }
885 activeMask: oc.ui_style_mask = {.BG_COLOR, .BORDER_COLOR}
886 oc.ui_style_match_after(activePattern, activeStyle, activeMask)
887
888 selectedTag := oc.ui_tag_make_str8("radio_selected")
889 selectedPattern := oc.ui_pattern{}
890 oc.ui_pattern_push(arena, &selectedPattern, {kind = .TAG, tag = selectedTag})
891 selectedStyle := oc.ui_style {
892 _color = oc.UI_DARK_THEME.palette.white,
893 bgColor = oc.UI_DARK_THEME.primary,
894 }
895 selectedMask: oc.ui_style_mask = {.COLOR, .BG_COLOR}
896 oc.ui_style_match_after(selectedPattern, selectedStyle, selectedMask)
897
898 selectedHoverPattern := oc.ui_pattern{}
899 oc.ui_pattern_push(arena, &selectedHoverPattern, {kind = .TAG, tag = selectedTag})
900 oc.ui_pattern_push(
901 arena,
902 &selectedHoverPattern,
903 {op = .AND, kind = .STATUS, status = {.HOVER}},
904 )
905 selectedHoverStyle := oc.ui_style {
906 bgColor = oc.UI_DARK_THEME.primaryHover,
907 }
908 oc.ui_style_match_after(selectedHoverPattern, selectedHoverStyle, {.BG_COLOR})
909
910 selectedActivePattern := oc.ui_pattern{}
911 oc.ui_pattern_push(arena, &selectedActivePattern, {kind = .TAG, tag = selectedTag})
912 oc.ui_pattern_push(
913 arena,
914 &selectedActivePattern,
915 {op = .AND, kind = .STATUS, status = {.ACTIVE}},
916 )
917 selectedActiveStyle := oc.ui_style {
918 bgColor = oc.UI_DARK_THEME.primaryActive,
919 }
920 oc.ui_style_match_after(selectedActivePattern, selectedActiveStyle, {.BG_COLOR})
921}