Started modeling an interface. Early MicroUI backend.
This commit is contained in:
248
imgui.odin
248
imgui.odin
@@ -1,3 +1,249 @@
|
|||||||
package imgui
|
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