diff --git a/justfile b/justfile index 94a7629..a96f1ae 100644 --- a/justfile +++ b/justfile @@ -42,7 +42,7 @@ run: build [windows] build_gfx: compile_shaders_directx if not exist bin\ mkdir bin - cl /nologo -Zi /c /Fdbin\ /Fobin\ /Ivendor\ /DWIN32_LEAN_AND_MEAN /D_FONT_WIDTH=12 /D_FONT_HEIGHT=24 /DED_GFX_IMPLEMENTATION /std:c11 /TC vendor\pcleavelin\gfx.h + cl /nologo -Zi /c /Fdbin\ /Fobin\ /Ivendor\ /DWIN32_LEAN_AND_MEAN /D_FONT_WIDTH=16 /D_FONT_HEIGHT=32 /DED_GFX_IMPLEMENTATION /std:c11 /TC vendor\pcleavelin\gfx.h [windows] compile_shaders_directx: diff --git a/shaders/text_atlas_fragment.glsl b/shaders/text_atlas_fragment.glsl index de14d3a..93a2af9 100644 --- a/shaders/text_atlas_fragment.glsl +++ b/shaders/text_atlas_fragment.glsl @@ -2,6 +2,7 @@ struct VertexOutput { vec4 position; + vec4 color; vec2 tex_coord; }; @@ -14,5 +15,5 @@ out vec4 color; void main() { float text_color = texture(atlas_texture, out_vertex.tex_coord).r; - color = vec4(text_color * vec3(1,1,1), text_color); + color = vec4(out_vertex.color.rgb * text_color, text_color); } diff --git a/shaders/text_atlas_vertex.glsl b/shaders/text_atlas_vertex.glsl index f062f29..0df50ef 100644 --- a/shaders/text_atlas_vertex.glsl +++ b/shaders/text_atlas_vertex.glsl @@ -7,6 +7,7 @@ struct Vertex { struct VertexOutput { vec4 position; + vec4 color; vec2 tex_coord; }; @@ -16,6 +17,7 @@ struct Glyph { vec2 target_position; float y_offset; float _haha_alignment; + vec4 color; }; layout(std430, binding = 0) readonly buffer VertexBlock { @@ -50,6 +52,7 @@ void main() { device_position.y = -device_position.y; out_vertex.position = device_position; + out_vertex.color = glyph.color; out_vertex.tex_coord = atlas_position; gl_Position = device_position; diff --git a/src/gfx/gfx.odin b/src/gfx/gfx.odin index f35c3fc..ee5dd3a 100644 --- a/src/gfx/gfx.odin +++ b/src/gfx/gfx.odin @@ -1,6 +1,7 @@ package gfx; import "core:strings" +import "core:fmt" import c "core:c" when ODIN_OS_STRING == "unix" { @@ -41,6 +42,8 @@ foreign gfx { @(link_name="gfx_frame_width") frame_width :: proc "c" (gfx_cx: ^cx) -> u32 --- @(link_name="gfx_frame_height") frame_height :: proc "c" (gfx_cx: ^cx) -> u32 --- + @(link_name="gfx_mouse_x") mouse_x :: proc "c" (gfx_cx: ^cx) -> i32 --- + @(link_name="gfx_mouse_y") mouse_y :: proc "c" (gfx_cx: ^cx) -> i32 --- @(link_name="gfx_init_context") init_context :: proc "c" (frame_func: GfxFrameFunc, width: u32, height: u32) -> ^cx --- @(link_name="gfx_run_events") run_events :: proc "c" (gfx_cx: ^cx) --- @@ -54,15 +57,18 @@ foreign gfx { } queue_text :: proc(gfx_cx: ^cx, text: string, position: [2]f32, max_x: f32, max_y: f32, color: [4]f32) { + ctext_data := strings.clone_to_cstring(text) text_ := gfx_string { - data = raw_data(text), - len = len(text), + data = transmute([^]u8)ctext_data, + len = len(ctext_data), }; p := []f32 { position[0], position[1] } c := []f32 { color[0], color[1], color[2], color[3] } gfx_queue_text(gfx_cx, text_, raw_data(p), max_x, max_y, raw_data(c)) + + delete(ctext_data) } queue_ui_rect :: proc(gfx_cx: ^cx, position: [2]f32, size: [2]f32, border_size: f32, color: [4]f32) { diff --git a/src/main.odin b/src/main.odin index fa30cc1..513279d 100644 --- a/src/main.odin +++ b/src/main.odin @@ -1,81 +1,160 @@ package main -import "core:runtime"; -import "core:fmt"; -import "core:os"; -import stbtt "vendor:stb/truetype"; +import "core:runtime" +import "core:fmt" +import "core:os" +import stbtt "vendor:stb/truetype" +import ui "vendor:microui" -import "gfx"; +import "gfx" gfx_cx: ^gfx.cx = nil +ui_cx: ui.Context + +last_mouse_left_down := false; +last_mouse_right_down := false; + +rasterized_font_height: i32 = 16 frame_func :: proc "c" (mouse_x: int, mouse_y: int, mouse_left_down: bool, mouse_right_down: bool) { - context = runtime.default_context(); + context = runtime.default_context() + frame_width := gfx.frame_width(gfx_cx) + frame_height := gfx.frame_height(gfx_cx) - for y in 0..<5 { - for x in 0..<5 { - gfx.queue_text(gfx_cx, "1234", { f32(x)*100, f32(x)*24 + f32(y)*26}, 1000, 1000, { 1, 1, 1, 1 }); + ui.input_mouse_move(&ui_cx, i32(mouse_x), i32(mouse_y)); + if mouse_left_down && !last_mouse_left_down { + ui.input_mouse_down(&ui_cx, i32(mouse_x), i32(mouse_y), .LEFT); + } else if !mouse_left_down && last_mouse_left_down { + ui.input_mouse_up(&ui_cx, i32(mouse_x), i32(mouse_y), .LEFT); + } + + last_mouse_left_down = mouse_left_down; + last_mouse_right_down = mouse_right_down; + + ui.begin(&ui_cx) + { + defer ui.end(&ui_cx) + + if ui.begin_window(&ui_cx, "canvas", ui.Rect{ 0, 0, i32(frame_width), i32(frame_height) }, opt = { .EXPANDED, .NO_CLOSE, .NO_RESIZE, .NO_INTERACT, .NO_TITLE }) { + defer ui.end_window(&ui_cx) + + ui.layout_row(&ui_cx, []i32 { 180, -1 }, -1) + ui.begin_panel(&ui_cx, "top panel", opt = { .AUTO_SIZE }) + { + defer ui.end_panel(&ui_cx) + + ui.layout_row(&ui_cx, []i32 { -1 }, 24); + + if ui.button(&ui_cx, "Mock Channel 1") >= { .SUBMIT } { + fmt.println("mock channel 1") + } + if ui.button(&ui_cx, "Mock Channel 2") >= { .SUBMIT } { + fmt.println("mock channel 2") + } + if ui.button(&ui_cx, "Mock Channel 3") >= { .SUBMIT } { + fmt.println("mock channel 3") + } + } + + ui.layout_begin_column(&ui_cx); + { + defer ui.layout_end_column(&ui_cx); + + ui.layout_row(&ui_cx, []i32 { -1 }, -64); + + ui.begin_panel(&ui_cx, "channel view") + { + defer ui.end_panel(&ui_cx) + ui.text(&ui_cx, "TODO: put slack channel messages here") + } + + ui.layout_row(&ui_cx, []i32 { -1 }, -1); + ui.begin_panel(&ui_cx, "text edit view") + { + defer ui.end_panel(&ui_cx) + ui.text(&ui_cx, "TODO: add text box here") + } + } + } + + canvas := ui.get_container(&ui_cx, "canvas"); + if canvas != nil { + canvas.rect.w = i32(frame_width); + canvas.rect.h = i32(frame_height); } } - frame_width := gfx.frame_width(gfx_cx); - frame_height := gfx.frame_height(gfx_cx); + cmd: ^ui.Command = nil + for { + variant, has_next := ui.next_command_iterator(&ui_cx, &cmd) + if !has_next { + break + } - gfx.queue_ui_rect(gfx_cx, { f32(frame_width/2-32), f32(frame_height/2-32) }, { 64, 64 }, 8, { 1, 0, 1, 1 }); + switch v in variant { + case ^ui.Command_Text: { + gfx.queue_text(gfx_cx, v.str, { f32(v.pos.x), f32(v.pos.y-rasterized_font_height) }, 1000, 1000, { f32(v.color.r)/255.0, f32(v.color.g)/255.0, f32(v.color.r)/255.0, f32(v.color.a)/255.0 }) + } + case ^ui.Command_Rect: { + gfx.queue_ui_rect(gfx_cx, { f32(v.rect.x), f32(v.rect.y) }, { f32(v.rect.w), f32(v.rect.h) }, 0, { f32(v.color.r)/255.0, f32(v.color.g)/255.0, f32(v.color.r)/255.0, f32(v.color.a)/255.0 }) + } + case ^ui.Command_Icon: {} + case ^ui.Command_Clip: {} + case ^ui.Command_Jump: {} + } + } } main :: proc() { gfx_cx = gfx.init_context(frame_func, 800, 600) - font_data, success := os.read_entire_file_from_filename("./bin/JetBrainsMono-Medium.ttf"); + font_data, success := os.read_entire_file_from_filename("./bin/JetBrainsMono-Medium.ttf") if !success { - fmt.eprintln("failed to read font file"); - os.exit(1); + fmt.eprintln("failed to read font file") + os.exit(1) } - font: stbtt.fontinfo; + font: stbtt.fontinfo if stbtt.InitFont(&font, raw_data(font_data), 0) == false { - fmt.eprintln("failed to init font"); - os.exit(1); + fmt.eprintln("failed to init font") + os.exit(1) } - bitmap_size: i32 = 512; - bitmap := make([]u8, bitmap_size * bitmap_size); + bitmap_size: i32 = 512 + bitmap := make([]u8, bitmap_size * bitmap_size) - rasterized_font_height: i32 = 24; - - ascent, descent, line_gap: i32; - scale := stbtt.ScaleForPixelHeight(&font, f32(rasterized_font_height * 2)); - stbtt.GetFontVMetrics(&font, &ascent, &descent, &line_gap); + ascent, descent, line_gap: i32 + scale := stbtt.ScaleForPixelHeight(&font, f32(rasterized_font_height * 2)) + stbtt.GetFontVMetrics(&font, &ascent, &descent, &line_gap) gfx.add_glyph(gfx_cx, gfx.GpuGlyph { size = {f32(rasterized_font_height/4), 1}, y_offset = f32(-rasterized_font_height), - }); + }) - x := rasterized_font_height / 4; - y: i32 = 0; + x := rasterized_font_height / 4 + y: i32 = 0 for i in 33..<33+96 { - width, height, xoff, yoff: i32; + width, height, xoff, yoff: i32 glyph_bitmap := stbtt.GetCodepointBitmap( - &font, scale, scale, rune(i), &width, &height, &xoff, &yoff); + &font, scale, scale, rune(i), &width, &height, &xoff, &yoff) if (x + width) >= bitmap_size { - x = 0; - y += i32(f32(ascent - descent + line_gap) * scale); + x = 0 + y += i32(f32(ascent - descent + line_gap) * scale) } - xxx: i32 = x; + xxx: i32 = x for xx in 0.. i32 { + return (rasterized_font_height/2) * i32(len(str)) + (rasterized_font_height/4) * 4 + } + ui_cx.text_height = proc(font: ui.Font) -> i32 { + return rasterized_font_height + } + + // ui_cx.style = &ui.Style { + // size = ui.Vec2 { 0, 0 }, + // padding = 5, + // spacing = 4, + // indent = 24, + // title_height = rasterized_font_height, + // scrollbar_size = 12, + // thumb_size = 8, + // colors = [ui.Color_Type]ui.Color { + // .TEXT = ui.Color { 230, 230, 230, 255 }, + // .BORDER = ui.Color { 25, 25, 25, 255 }, + // .WINDOW_BG = ui.Color { 50, 50, 50, 255 }, + // .TITLE_BG = ui.Color { 25, 25, 25, 255 }, + // .TITLE_TEXT = ui.Color { 240, 240, 240, 255 }, + // .PANEL_BG = ui.Color { 0, 0, 0, 0 }, + // .BUTTON = ui.Color { 75, 75, 75, 255 }, + // .BUTTON_HOVER = ui.Color { 95, 95, 95, 255 }, + // .BUTTON_FOCUS = ui.Color { 115, 115, 115, 255 }, + // .BASE = ui.Color { 30, 30, 30, 255 }, + // .BASE_HOVER = ui.Color { 35, 35, 35, 255 }, + // .BASE_FOCUS = ui.Color { 40, 40, 40, 255 }, + // .SCROLL_BASE = ui.Color { 43, 43, 43, 255 }, + // .SCROLL_THUMB = ui.Color { 30, 30, 30, 255 }, + // }, + // }; + + frame_func(0,0,false,false); + frame_func(0,0,false,false); for(gfx.keep_running) { gfx.run_events(gfx_cx) diff --git a/vendor/pcleavelin/file_io.h b/vendor/pcleavelin/file_io.h index 0ea69f1..07745b1 100644 --- a/vendor/pcleavelin/file_io.h +++ b/vendor/pcleavelin/file_io.h @@ -56,9 +56,13 @@ VOID CALLBACK FileIOCompletionRoutine( printf("Number of bytes: %li", dwNumberOfBytesTransfered); } -uint64_t get_file_size(string filePath) { +uint64_t get_file_size(string file_path) { + char *file_path_with_sentinel = malloc(file_path.len + 1); + memcpy(file_path_with_sentinel, file_path.data, file_path.len + 1); + file_path_with_sentinel[file_path.len] = 0; + // FIXME: convert to null teminated string - HANDLE hFile = CreateFile(filePath.data, + HANDLE hFile = CreateFile(file_path_with_sentinel, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -69,12 +73,17 @@ uint64_t get_file_size(string filePath) { LARGE_INTEGER size; assert(GetFileSizeEx(hFile, &size) && "get_file_size: failed to get file size"); + free(file_path_with_sentinel); return size.LowPart; } -bool load_file(string filePath, size_t size, void *buffer) { +bool load_file(string file_path, size_t size, void *buffer) { + char *file_path_with_sentinel = malloc(file_path.len + 1); + memcpy(file_path_with_sentinel, file_path.data, file_path.len + 1); + file_path_with_sentinel[file_path.len] = 0; + // FIXME: convert to null teminated string - HANDLE hFile = CreateFile(filePath.data, + HANDLE hFile = CreateFile(file_path_with_sentinel, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -93,6 +102,7 @@ bool load_file(string filePath, size_t size, void *buffer) { } assert(bytesRead == size && "load_file: didn't one-shot read the whole file"); + free(file_path_with_sentinel); return true; } #elif defined(__APPLE__) diff --git a/vendor/pcleavelin/gfx.h b/vendor/pcleavelin/gfx.h index 0b7bc95..de7d1c7 100644 --- a/vendor/pcleavelin/gfx.h +++ b/vendor/pcleavelin/gfx.h @@ -219,9 +219,11 @@ typedef struct { } gfx_context_t; static gfx_context_t _gfx_context; -#ifdef ED_GFX_IMPLEMENTATION +void gfx_queue_text(gfx_context_t *cx, string text, float position[2], + float max_x, float max_y, float color[4]); void gfx_update_buffer(gfx_context_t *cx, size_t buffer_index, const void *data, size_t len); +#ifdef ED_GFX_IMPLEMENTATION #if defined(__APPLE__) static void _metal_gfx_present(_metal_gfx_context *cx); @@ -1307,6 +1309,25 @@ static LRESULT CALLBACK _win32_gfx_window_proc(HWND window, UINT message, WPARAM _gfx_context.frame_height = height; break; } + case WM_LBUTTONDOWN: + { + _gfx_context.backend.mouse_left_down = true; + break; + } + case WM_LBUTTONUP: + { + _gfx_context.backend.mouse_left_down = false; + break; + } + case WM_MOUSEMOVE: + { + UINT mouse_x = LOWORD(lparam); + UINT mouse_y = HIWORD(lparam); + + _gfx_context.backend.mouse_x = mouse_x; + _gfx_context.backend.mouse_y = mouse_y; + break; + } case WM_PAINT: { _win32_gfx_present(&_gfx_context.backend); @@ -1344,6 +1365,7 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) { WNDCLASS win_class = {0}; win_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; win_class.hInstance = instance; + win_class.hCursor = LoadCursor(NULL, IDC_ARROW); win_class.lpszClassName = "GFXWindowClass"; win_class.lpfnWndProc = _win32_gfx_window_proc; assert(RegisterClass(&win_class) && "_win32_gfx_init_context: RegisterClass failed"); @@ -1375,7 +1397,7 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) { pixel_format_desc.iPixelType = PFD_TYPE_RGBA; pixel_format_desc.cColorBits = 32; pixel_format_desc.cAlphaBits = 32; - pixel_format_desc.cDepthBits = 24; + pixel_format_desc.cDepthBits = 0; int pixel_format_id = ChoosePixelFormat(device_context, &pixel_format_desc); assert(pixel_format_id && "_win32_gfx_init_context: ChoosePixelFormat failed"); @@ -1472,12 +1494,12 @@ _win32_gfx_init_context(uint32_t width, uint32_t height) { } static void _win32_gfx_send_events(_win32_gfx_context *cx) { + _win32_gfx_present(cx); + MSG message; GetMessage(&message, 0, 0 ,0); TranslateMessage(&message); DispatchMessage(&message); - - _win32_gfx_present(cx); } static void _win32_gfx_present(_win32_gfx_context *cx) { @@ -1749,6 +1771,14 @@ uint32_t gfx_frame_height(gfx_context_t *cx) { return cx->frame_height; } +int32_t gfx_mouse_x(gfx_context_t *cx) { + return cx->backend.mouse_x; +} + +int32_t gfx_mouse_y(gfx_context_t *cx) { + return cx->backend.mouse_y; +} + void *gfx_init_context(_gfx_frame_func frame_func, uint32_t width, uint32_t height) { #if defined(__APPLE__)