Started modeling an interface. Early MicroUI backend.
This commit is contained in:
248
imgui.odin
248
imgui.odin
@@ -1,3 +1,249 @@
|
||||
package imgui
|
||||
|
||||
// Hello, world!
|
||||
import "core:container/queue"
|
||||
import "core:log"
|
||||
|
||||
import "microui"
|
||||
|
||||
MAX_DRAW_COMMANDS :: #config(IMGUI_MAX_DRAW_COMMANDS, 512 * 1024)
|
||||
|
||||
// TODO: SS - Consider not having 'microui' as a seperate package and instead have it (and other backends) in the 'imgui' package directly.
|
||||
|
||||
IMGUI_BACKEND_MICROUI :: #config(IMGUI_BACKEND_MICROUI, false)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
Context_Backend :: microui.Context
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
backend: ^Context_Backend,
|
||||
|
||||
draw_commands: queue.Queue(Draw_Command),
|
||||
input: Input,
|
||||
}
|
||||
|
||||
Input :: struct {
|
||||
mouse_state: Mouse_State,
|
||||
}
|
||||
|
||||
Mouse_Button_State :: enum {
|
||||
Up,
|
||||
Pressed,
|
||||
Down,
|
||||
Released,
|
||||
}
|
||||
|
||||
Mouse_State :: struct {
|
||||
pos_x, pos_y: u16,
|
||||
left, middle, right: Mouse_Button_State,
|
||||
}
|
||||
|
||||
Draw_Command :: struct {
|
||||
color: [4]u8,
|
||||
type: Draw_Command_Type,
|
||||
}
|
||||
|
||||
Draw_Command_Type :: union {
|
||||
Draw_Command_Rect,
|
||||
}
|
||||
|
||||
Draw_Command_Rect :: struct {
|
||||
x, y, width, height: u16,
|
||||
}
|
||||
|
||||
Rect :: struct {
|
||||
x, y, width, height: u16,
|
||||
}
|
||||
|
||||
init :: proc(ctx: ^Context) -> bool {
|
||||
assert(ctx != nil)
|
||||
assert(ctx.backend == nil)
|
||||
|
||||
backend, err := new(Context_Backend)
|
||||
if err != .None {
|
||||
log.errorf("Failed to allocate space for the backend. Error: %v", err)
|
||||
return false
|
||||
}
|
||||
ctx.backend = backend
|
||||
|
||||
ok := false
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
ok = microui.init(ctx.backend)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
free(backend)
|
||||
return false
|
||||
}
|
||||
|
||||
queue_err := queue.init(&ctx.draw_commands, MAX_DRAW_COMMANDS)
|
||||
if queue_err != .None {
|
||||
free(backend)
|
||||
log.errorf("Failed to allocate space for the queue of imgui draw-commands. Error: %v", queue_err)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
set_input :: proc(ctx: ^Context, input: Input) {
|
||||
ctx.input = input
|
||||
}
|
||||
|
||||
begin :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
assert(ctx.backend != nil)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
input: microui.Input
|
||||
input.mouse_x = ctx.input.mouse_state.pos_x
|
||||
input.mouse_y = ctx.input.mouse_state.pos_y
|
||||
|
||||
if ctx.input.mouse_state.left == .Pressed {
|
||||
input.mouse_buttons_pressed += { .LEFT }
|
||||
}
|
||||
else if ctx.input.mouse_state.left == .Released {
|
||||
input.mouse_buttons_released += { .LEFT }
|
||||
}
|
||||
|
||||
if ctx.input.mouse_state.middle == .Pressed {
|
||||
input.mouse_buttons_pressed += { .MIDDLE }
|
||||
}
|
||||
else if ctx.input.mouse_state.middle == .Released {
|
||||
input.mouse_buttons_released += { .MIDDLE }
|
||||
}
|
||||
|
||||
if ctx.input.mouse_state.right == .Pressed {
|
||||
input.mouse_buttons_pressed += { .RIGHT }
|
||||
}
|
||||
else if ctx.input.mouse_state.right == .Released {
|
||||
input.mouse_buttons_released += { .RIGHT }
|
||||
}
|
||||
|
||||
microui.begin(ctx.backend, input)
|
||||
}
|
||||
}
|
||||
|
||||
end :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
assert(ctx.backend != nil)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
microui.end(ctx.backend)
|
||||
}
|
||||
}
|
||||
|
||||
begin_window :: proc(ctx: ^Context, title: string, rect: Rect) -> bool {
|
||||
assert(ctx != nil)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
return microui.begin_window(ctx.backend, title, microui.Rect {
|
||||
x = i32(rect.x),
|
||||
y = i32(rect.y),
|
||||
w = i32(rect.width),
|
||||
h = i32(rect.height),
|
||||
})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
end_window :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
microui.end_window(ctx.backend)
|
||||
}
|
||||
}
|
||||
|
||||
button :: proc(ctx: ^Context, title: string) -> bool {
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
return microui.button(ctx.backend, title)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
collect :: proc(ctx: ^Context) { // TODO: SS - Come up with a better name. Adds commands from backend to queue in ctx.
|
||||
assert(ctx != nil)
|
||||
|
||||
// TODO: SS - Reduce code-repetition if possible. MicroUI asks to be "popped"/iterated like this, apparently.
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
last_cmd: ^microui.Command = nil
|
||||
for {
|
||||
dc: Draw_Command
|
||||
dc.type = nil
|
||||
|
||||
cmd := microui.pop_draw_command(ctx.backend, last_cmd)
|
||||
if cmd == nil {
|
||||
break
|
||||
}
|
||||
|
||||
last_cmd = cmd
|
||||
|
||||
#partial switch &v in &cmd.variant {
|
||||
// case ^microui.Command_Jump: {} // Internal.
|
||||
// case ^microui.Command_Clip: {
|
||||
// }
|
||||
// case ^microui.Command_Icon: {
|
||||
// }
|
||||
case ^microui.Command_Rect: {
|
||||
assert(v.rect.x >= 0)
|
||||
assert(v.rect.y >= 0)
|
||||
assert(v.rect.w >= 0)
|
||||
assert(v.rect.h >= 0)
|
||||
|
||||
dc.color = {
|
||||
v.color.r,
|
||||
v.color.g,
|
||||
v.color.b,
|
||||
v.color.a,
|
||||
}
|
||||
dc.type = Draw_Command_Rect {
|
||||
x = u16(v.rect.x),
|
||||
y = u16(v.rect.y),
|
||||
width = u16(v.rect.w),
|
||||
height = u16(v.rect.h),
|
||||
}
|
||||
}
|
||||
// case ^microui.Command_Text: {
|
||||
// }
|
||||
}
|
||||
|
||||
if dc.type != nil {
|
||||
queue.push_back(&ctx.draw_commands, dc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pop_draw_command :: proc(ctx: ^Context, out_draw_command: ^Draw_Command) -> bool {
|
||||
assert(ctx != nil)
|
||||
assert(out_draw_command != nil)
|
||||
|
||||
out_draw_command^ = {}
|
||||
|
||||
elem, ok := queue.pop_front_safe(&ctx.draw_commands)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
out_draw_command^ = elem
|
||||
return true
|
||||
}
|
||||
|
||||
shutdown :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
assert(ctx.backend != nil)
|
||||
|
||||
when IMGUI_BACKEND_MICROUI {
|
||||
microui.shutdown(ctx.backend)
|
||||
}
|
||||
|
||||
free(ctx.backend)
|
||||
ctx.backend = nil
|
||||
|
||||
queue.destroy(&ctx.draw_commands)
|
||||
}
|
||||
150
microui/microui.odin
Normal file
150
microui/microui.odin
Normal file
@@ -0,0 +1,150 @@
|
||||
package microui_wrapper
|
||||
|
||||
import "core:log"
|
||||
|
||||
import mu "vendor:microui"
|
||||
|
||||
Context :: mu.Context
|
||||
|
||||
@(private) set_clipboard :: proc(userdata: rawptr, text: string) -> bool {
|
||||
return false
|
||||
}
|
||||
@(private) get_clipboard :: proc(userdata: rawptr) -> (string, bool) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@(private) text_width :: proc(font: mu.Font, text: string) -> i32 {
|
||||
// log.warnf("TODO: SS - Implement %v.", #procedure)
|
||||
return 0 // TEMP: SS
|
||||
}
|
||||
@(private) text_height :: proc(font: mu.Font) -> i32 {
|
||||
// log.warnf("TODO: SS - Implement %v.", #procedure)
|
||||
return 0 // TEMP: SS
|
||||
}
|
||||
|
||||
Command :: mu.Command
|
||||
Command_Jump :: mu.Command_Jump // Internal. Should not be used externally.
|
||||
Command_Clip :: mu.Command_Clip
|
||||
Command_Icon :: mu.Command_Icon
|
||||
Command_Rect :: mu.Command_Rect
|
||||
Command_Text :: mu.Command_Text
|
||||
Rect :: mu.Rect
|
||||
|
||||
Input :: struct {
|
||||
mouse_x, mouse_y: u16,
|
||||
mouse_buttons_pressed: mu.Mouse_Set,
|
||||
mouse_buttons_released: mu.Mouse_Set,
|
||||
}
|
||||
|
||||
init :: proc(ctx: ^Context) -> bool {
|
||||
assert(ctx != nil)
|
||||
|
||||
clipboard_user_data: rawptr = nil // TEMP: SS
|
||||
mu.init(ctx, set_clipboard, get_clipboard, clipboard_user_data)
|
||||
ctx.text_width = text_width
|
||||
ctx.text_height = text_height
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
begin :: proc(ctx: ^Context, input: Input) {
|
||||
assert(ctx != nil)
|
||||
|
||||
{ // Apply input.
|
||||
mx := i32(input.mouse_x)
|
||||
my := i32(input.mouse_y)
|
||||
mu.input_mouse_move(ctx, mx, my)
|
||||
|
||||
for b in input.mouse_buttons_pressed {
|
||||
mu.input_mouse_down(ctx, mx, my, b)
|
||||
}
|
||||
for b in input.mouse_buttons_released {
|
||||
mu.input_mouse_up(ctx, mx, my, b)
|
||||
}
|
||||
}
|
||||
|
||||
// Now, begin.
|
||||
mu.begin(ctx)
|
||||
|
||||
{ // TEMP: SS - Just trying it out.
|
||||
// if mu.begin_window(
|
||||
// ctx,
|
||||
// "Test",
|
||||
// mu.Rect {
|
||||
// x = 0, y = 20,
|
||||
// w = 256, h = 256,
|
||||
// },
|
||||
// opt = {}
|
||||
// ) {
|
||||
// mu.label(ctx, "First:")
|
||||
// if mu.button(ctx, "Button1") == {.SUBMIT} {
|
||||
// log.infof("Button1 pressed")
|
||||
// }
|
||||
|
||||
// mu.label(ctx, "Second:");
|
||||
// if mu.button(ctx, "Button2") == {.SUBMIT} {
|
||||
// mu.open_popup(ctx, "My Popup");
|
||||
// }
|
||||
|
||||
// if (mu.begin_popup(ctx, "My Popup")) {
|
||||
// mu.label(ctx, "Hello world!")
|
||||
|
||||
// if mu.button(ctx, "Button3") == {.SUBMIT} {
|
||||
// log.infof("Button3 pressed")
|
||||
// }
|
||||
|
||||
// mu.end_popup(ctx)
|
||||
// }
|
||||
|
||||
|
||||
// mu.end_window(ctx)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
end :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
mu.end(ctx)
|
||||
}
|
||||
|
||||
|
||||
begin_window :: proc(ctx: ^Context, title: string, rect: mu.Rect) -> bool {
|
||||
assert(ctx != nil)
|
||||
|
||||
opt := mu.Options {
|
||||
// .NO_CLOSE, // TEMP: SS - Some weird bug is happening. Windows are closing randomly, but this helps..
|
||||
}
|
||||
|
||||
return mu.begin_window(
|
||||
ctx,
|
||||
title,
|
||||
rect,
|
||||
opt,
|
||||
)
|
||||
}
|
||||
|
||||
end_window :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
|
||||
mu.end_window(ctx)
|
||||
}
|
||||
|
||||
button :: proc(ctx: ^Context, title: string) -> bool {
|
||||
return mu.button(ctx, title) == {.SUBMIT}
|
||||
}
|
||||
|
||||
pop_draw_command :: proc(ctx: ^Context, last_cmd: ^mu.Command) -> ^mu.Command {
|
||||
cmd: ^mu.Command = last_cmd
|
||||
if !mu.next_command(ctx, &cmd) {
|
||||
return nil
|
||||
}
|
||||
|
||||
assert(cmd != nil)
|
||||
return cmd
|
||||
}
|
||||
|
||||
shutdown :: proc(ctx: ^Context) {
|
||||
assert(ctx != nil)
|
||||
|
||||
ctx^ = {}
|
||||
}
|
||||
Reference in New Issue
Block a user