diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bba19fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Local build outputs +game_msan +game +*.bin diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5ac5c1c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "odin-imgui"] + path = odin-imgui + url = https://github.com/Capati/odin-imgui.git diff --git a/clay-odin/clay.odin b/clay-odin/clay.odin new file mode 100644 index 0000000..9332f8b --- /dev/null +++ b/clay-odin/clay.odin @@ -0,0 +1,601 @@ +package clay + +import "core:c" + +when ODIN_OS == .Windows { + foreign import Clay "windows/clay.lib" +} else when ODIN_OS == .Linux { + foreign import Clay "linux/clay.a" +} else when ODIN_OS == .Darwin { + when ODIN_ARCH == .arm64 { + foreign import Clay "macos-arm64/clay.a" + } else { + foreign import Clay "macos/clay.a" + } +} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import Clay "wasm/clay.o" +} + +String :: struct { + isStaticallyAllocated: c.bool, + length: c.int32_t, + chars: [^]c.char, +} + +StringSlice :: struct { + length: c.int32_t, + chars: [^]c.char, + baseChars: [^]c.char, +} + +Vector2 :: [2]c.float + +Dimensions :: struct { + width: c.float, + height: c.float, +} + +Arena :: struct { + nextAllocation: uintptr, + capacity: c.size_t, + memory: [^]c.char, +} + +BoundingBox :: struct { + x: c.float, + y: c.float, + width: c.float, + height: c.float, +} + +Color :: [4]c.float + +CornerRadius :: struct { + topLeft: c.float, + topRight: c.float, + bottomLeft: c.float, + bottomRight: c.float, +} + +ElementId :: struct { + id: u32, + offset: u32, + baseId: u32, + stringId: String, +} + +ElementIdArray :: struct { + capacity: i32, + length: i32, + internalArray: [^]ElementId, +} + +when ODIN_OS == .Windows { + EnumBackingType :: u32 +} else { + EnumBackingType :: u8 +} + +RenderCommandType :: enum EnumBackingType { + None, + Rectangle, + Border, + Text, + Image, + ScissorStart, + ScissorEnd, + OverlayColorStart, + OverlayColorEnd, + Custom, +} + +RectangleElementConfig :: struct { + color: Color, +} + +TextWrapMode :: enum EnumBackingType { + Words, + Newlines, + None, +} + +TextAlignment :: enum EnumBackingType { + Left, + Center, + Right, +} + +TextElementConfig :: struct { + userData: rawptr, + textColor: Color, + fontId: u16, + fontSize: u16, + letterSpacing: u16, + lineHeight: u16, + wrapMode: TextWrapMode, + textAlignment: TextAlignment, +} + +AspectRatioElementConfig :: struct { + aspectRatio: f32, +} + +ImageElementConfig :: struct { + imageData: rawptr, +} + +CustomElementConfig :: struct { + customData: rawptr, +} + +BorderWidth :: struct { + left: u16, + right: u16, + top: u16, + bottom: u16, + betweenChildren: u16, +} + +BorderElementConfig :: struct { + color: Color, + width: BorderWidth, +} + +TransitionData :: struct { + boundingBox: BoundingBox, + backgroundColor: Color, + overlayColor: Color, + borderColor: Color, + borderWidth: BorderWidth, +} + +TransitionState :: enum c.int { + Idle, + Entering, + Transitioning, + Exiting, +} + +TransitionProperty :: enum c.int { + X, + Y, + Width, + Height, + BackgroundColor, + OverlayColor, + CornerRadius, + BorderColor, + BorderWidth, +} + +TransitionPropertyFlags :: bit_set[TransitionProperty;c.int] +TransitionPropertyPosition :: TransitionPropertyFlags{.X, .Y} +TransitionPropertyDimensions :: TransitionPropertyFlags{.Width, .Height} +TransitionPropertyBoundingBox :: TransitionPropertyPosition + TransitionPropertyDimensions +TransitionPropertyBorder :: TransitionPropertyFlags{.BorderColor, .BorderWidth} + +TransitionCallbackArguments :: struct { + transitionState: TransitionState, + initial: TransitionData, + current: ^TransitionData, + target: TransitionData, + elapsedTime: f32, + duration: f32, + properties: TransitionPropertyFlags, +} + +TransitionEnterTriggerType :: enum EnumBackingType { + SkipOnFirstParentFrame, + TriggerOnFirstParentFrame, +} + +TransitionExitTriggerType :: enum EnumBackingType { + SkipWhenParentExits, + TriggerWhenParentExits, +} + +TransitionInteractionHandlingType :: enum EnumBackingType { + DisableInteractionsWhileTransitioningPosition, + AllowInteractionsWhileTransitioningPosition, +} + +ExitTransitionSiblingOrdering :: enum EnumBackingType { + UnderneathSiblings, + NaturalOrder, + AboveSiblings, +} + +TransitionElementConfig :: struct { + handler: proc "c" (args: TransitionCallbackArguments) -> bool, + duration: f32, + properties: TransitionPropertyFlags, + interactionHandling: TransitionInteractionHandlingType, + enter: struct { + setInitialState: proc "c" (initialState: TransitionData, properties: TransitionPropertyFlags) -> TransitionData, + trigger: TransitionEnterTriggerType, + }, + exit: struct { + setFinalState: proc "c" (finalState: TransitionData, properties: TransitionPropertyFlags) -> TransitionData, + trigger: TransitionExitTriggerType, + siblingOrdering: ExitTransitionSiblingOrdering, + }, +} + +ClipElementConfig :: struct { + horizontal: bool, // clip overflowing elements on the "X" axis + vertical: bool, // clip overflowing elements on the "Y" axis + childOffset: Vector2, // offsets the [X,Y] positions of all child elements, primarily for scrolling containers +} + +FloatingAttachPointType :: enum EnumBackingType { + LeftTop, + LeftCenter, + LeftBottom, + CenterTop, + CenterCenter, + CenterBottom, + RightTop, + RightCenter, + RightBottom, +} + +FloatingAttachPoints :: struct { + element: FloatingAttachPointType, + parent: FloatingAttachPointType, +} + +PointerCaptureMode :: enum EnumBackingType { + Capture, + Passthrough, +} + +FloatingAttachToElement :: enum EnumBackingType { + None, + Parent, + ElementWithId, + Root, +} + +FloatingClipToElement :: enum EnumBackingType { + None, + AttachedParent, +} + +FloatingElementConfig :: struct { + offset: Vector2, + expand: Dimensions, + parentId: u32, + zIndex: i16, + attachment: FloatingAttachPoints, + pointerCaptureMode: PointerCaptureMode, + attachTo: FloatingAttachToElement, + clipTo: FloatingClipToElement, +} + +TextRenderData :: struct { + stringContents: StringSlice, + textColor: Color, + fontId: u16, + fontSize: u16, + letterSpacing: u16, + lineHeight: u16, +} + +RectangleRenderData :: struct { + backgroundColor: Color, + cornerRadius: CornerRadius, +} + +ImageRenderData :: struct { + backgroundColor: Color, + cornerRadius: CornerRadius, + imageData: rawptr, +} + +CustomRenderData :: struct { + backgroundColor: Color, + cornerRadius: CornerRadius, + customData: rawptr, +} + +ClipRenderData :: struct { + horizontal: bool, + vertical: bool, +} + +OverlayColorRenderData :: struct { + color: Color, +} + +BorderRenderData :: struct { + color: Color, + cornerRadius: CornerRadius, + width: BorderWidth, +} + +RenderCommandData :: struct #raw_union { + rectangle: RectangleRenderData, + text: TextRenderData, + image: ImageRenderData, + custom: CustomRenderData, + border: BorderRenderData, + clip: ClipRenderData, + overlayColor: OverlayColorRenderData, +} + +RenderCommand :: struct { + boundingBox: BoundingBox, + renderData: RenderCommandData, + userData: rawptr, + id: u32, + zIndex: i16, + commandType: RenderCommandType, +} + +ScrollContainerData :: struct { + // Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout. + // Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling. + scrollPosition: ^Vector2, + scrollContainerDimensions: Dimensions, + contentDimensions: Dimensions, + config: ClipElementConfig, + // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. + found: bool, +} + +ElementData :: struct { + boundingBox: BoundingBox, + found: bool, +} + +PointerDataInteractionState :: enum EnumBackingType { + PressedThisFrame, + Pressed, + ReleasedThisFrame, + Released, +} + +PointerData :: struct { + position: Vector2, + state: PointerDataInteractionState, +} + +SizingType :: enum EnumBackingType { + Fit, + Grow, + Percent, + Fixed, +} + +SizingConstraintsMinMax :: struct { + min: c.float, + max: c.float, +} + +SizingConstraints :: struct #raw_union { + sizeMinMax: SizingConstraintsMinMax, + sizePercent: c.float, +} + +SizingAxis :: struct { + // Note: `min` is used for CLAY_SIZING_PERCENT, slightly different to clay.h due to lack of C anonymous unions + constraints: SizingConstraints, + type: SizingType, +} + +Sizing :: struct { + width: SizingAxis, + height: SizingAxis, +} + +Padding :: struct { + left: u16, + right: u16, + top: u16, + bottom: u16, +} + +LayoutDirection :: enum EnumBackingType { + LeftToRight, + TopToBottom, +} + +LayoutAlignmentX :: enum EnumBackingType { + Left, + Right, + Center, +} + +LayoutAlignmentY :: enum EnumBackingType { + Top, + Bottom, + Center, +} + +ChildAlignment :: struct { + x: LayoutAlignmentX, + y: LayoutAlignmentY, +} + +LayoutConfig :: struct { + sizing: Sizing, + padding: Padding, + childGap: u16, + childAlignment: ChildAlignment, + layoutDirection: LayoutDirection, +} + +ClayArray :: struct($type: typeid) { + capacity: i32, + length: i32, + internalArray: [^]type, +} + +ElementDeclaration :: struct { + layout: LayoutConfig, + backgroundColor: Color, + overlayColor: Color, + cornerRadius: CornerRadius, + aspectRatio: AspectRatioElementConfig, + image: ImageElementConfig, + floating: FloatingElementConfig, + custom: CustomElementConfig, + clip: ClipElementConfig, + border: BorderElementConfig, + transition: TransitionElementConfig, + userData: rawptr, +} + +ErrorType :: enum EnumBackingType { + TextMeasurementFunctionNotProvided, + ArenaCapacityExceeded, + ElementsCapacityExceeded, + TextMeasurementCapacityExceeded, + DuplicateId, + FloatingContainerParentNotFound, + PercentageOver1, + InternalError, + UnbalancedOpenClose, +} + +ErrorData :: struct { + errorType: ErrorType, + errorText: String, + userData: rawptr, +} + +ErrorHandler :: struct { + handler: proc "c" (errorData: ErrorData), + userData: rawptr, +} + +Context :: struct {} // opaque structure, only use as a pointer + +@(link_prefix = "Clay_", default_calling_convention = "c") +foreign Clay { + _OpenElement :: proc() --- + _OpenElementWithId :: proc(id: ElementId) --- + _CloseElement :: proc() --- + MinMemorySize :: proc() -> u32 --- + CreateArenaWithCapacityAndMemory :: proc(capacity: c.size_t, offset: [^]u8) -> Arena --- + SetPointerState :: proc(position: Vector2, pointerDown: bool) --- + GetPointerState :: proc() -> PointerData --- + Initialize :: proc(arena: Arena, layoutDimensions: Dimensions, errorHandler: ErrorHandler) -> ^Context --- + GetCurrentContext :: proc() -> ^Context --- + SetCurrentContext :: proc(ctx: ^Context) --- + UpdateScrollContainers :: proc(enableDragScrolling: bool, scrollDelta: Vector2, deltaTime: c.float) --- + SetLayoutDimensions :: proc(dimensions: Dimensions) --- + BeginLayout :: proc() --- + EndLayout :: proc(deltaTime: c.float) -> ClayArray(RenderCommand) --- + GetOpenElementId :: proc() -> u32 --- + GetElementId :: proc(id: String) -> ElementId --- + GetElementIdWithIndex :: proc(id: String, index: u32) -> ElementId --- + GetElementData :: proc(id: ElementId) -> ElementData --- + Hovered :: proc() -> bool --- + OnHover :: proc(onHoverFunction: proc "c" (id: ElementId, pointerData: PointerData, userData: rawptr), userData: rawptr) --- + PointerOver :: proc(id: ElementId) -> bool --- + GetPointerOverIds :: proc() -> ElementIdArray --- + GetScrollOffset :: proc() -> Vector2 --- + GetScrollContainerData :: proc(id: ElementId) -> ScrollContainerData --- + SetMeasureTextFunction :: proc(measureTextFunction: proc "c" (text: StringSlice, config: ^TextElementConfig, userData: rawptr) -> Dimensions, userData: rawptr) --- + SetQueryScrollOffsetFunction :: proc(queryScrollOffsetFunction: proc "c" (elementId: u32, userData: rawptr) -> Vector2, userData: rawptr) --- + RenderCommandArray_Get :: proc(array: ^ClayArray(RenderCommand), index: i32) -> ^RenderCommand --- + SetDebugModeEnabled :: proc(enabled: bool) --- + IsDebugModeEnabled :: proc() -> bool --- + SetCullingEnabled :: proc(enabled: bool) --- + GetMaxElementCount :: proc() -> i32 --- + SetMaxElementCount :: proc(maxElementCount: i32) --- + GetMaxMeasureTextCacheWordCount :: proc() -> i32 --- + SetMaxMeasureTextCacheWordCount :: proc(maxMeasureTextCacheWordCount: i32) --- + ResetMeasureTextCache :: proc() --- + EaseOut :: proc(arguments: TransitionCallbackArguments) -> bool --- +} + +@(link_prefix = "Clay_", default_calling_convention = "c", private) +foreign Clay { + _ConfigureOpenElement :: proc(config: ElementDeclaration) --- + _HashString :: proc(key: String, seed: u32) -> ElementId --- + _HashStringWithOffset :: proc(key: String, index: u32, seed: u32) -> ElementId --- + _OpenTextElement :: proc(text: String, textConfig: TextElementConfig) --- +} + +ConfigureOpenElement :: proc(config: ElementDeclaration) -> bool { + _ConfigureOpenElement(config) + return true +} + +@(deferred_none = _CloseElement) +UI_WithId :: proc(id: ElementId) -> proc(config: ElementDeclaration) -> bool { + _OpenElementWithId(id) + return ConfigureOpenElement +} + +@(deferred_none = _CloseElement) +UI_AutoId :: proc() -> proc(config: ElementDeclaration) -> bool { + _OpenElement() + return ConfigureOpenElement +} + +UI :: proc { + UI_WithId, + UI_AutoId, +} + +Text :: proc { + TextStatic, + TextDynamic, +} + +TextStatic :: proc($text: string, config: TextElementConfig) { + wrapped := MakeString(text) + wrapped.isStaticallyAllocated = true + _OpenTextElement(wrapped, config) +} + +TextDynamic :: proc(text: string, config: TextElementConfig) { + _OpenTextElement(MakeString(text), config) +} + +PaddingAll :: proc(allPadding: u16) -> Padding { + return {left = allPadding, right = allPadding, top = allPadding, bottom = allPadding} +} + +BorderOutside :: proc(width: u16) -> BorderWidth { + return {width, width, width, width, 0} +} + +BorderAll :: proc(width: u16) -> BorderWidth { + return {width, width, width, width, width} +} + +CornerRadiusAll :: proc(radius: f32) -> CornerRadius { + return CornerRadius{radius, radius, radius, radius} +} + +SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax = {}) -> SizingAxis { + return SizingAxis{type = SizingType.Fit, constraints = {sizeMinMax = sizeMinMax}} +} + +SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax = {}) -> SizingAxis { + return SizingAxis{type = SizingType.Grow, constraints = {sizeMinMax = sizeMinMax}} +} + +SizingFixed :: proc(size: c.float) -> SizingAxis { + return SizingAxis{type = SizingType.Fixed, constraints = {sizeMinMax = {size, size}}} +} + +SizingPercent :: proc(sizePercent: c.float) -> SizingAxis { + return SizingAxis{type = SizingType.Percent, constraints = {sizePercent = sizePercent}} +} + +MakeString :: proc(label: string) -> String { + return String{chars = raw_data(label), length = cast(c.int)len(label)} +} + +ID :: proc(label: string, index: u32 = 0) -> ElementId { + return _HashString(MakeString(label), index) +} + +ID_LOCAL :: proc(label: string, index: u32 = 0) -> ElementId { + return _HashStringWithOffset(MakeString(label), index, GetOpenElementId()) +} diff --git a/clay-odin/linux/clay.a b/clay-odin/linux/clay.a new file mode 100644 index 0000000..5cc4ea7 Binary files /dev/null and b/clay-odin/linux/clay.a differ diff --git a/clay-odin/macos-arm64/clay.a b/clay-odin/macos-arm64/clay.a new file mode 100644 index 0000000..26abfbb Binary files /dev/null and b/clay-odin/macos-arm64/clay.a differ diff --git a/clay-odin/macos/clay.a b/clay-odin/macos/clay.a new file mode 100644 index 0000000..342ea43 Binary files /dev/null and b/clay-odin/macos/clay.a differ diff --git a/clay-odin/wasm/clay.o b/clay-odin/wasm/clay.o new file mode 100644 index 0000000..ae8bacb Binary files /dev/null and b/clay-odin/wasm/clay.o differ diff --git a/clay-odin/windows/clay.lib b/clay-odin/windows/clay.lib new file mode 100644 index 0000000..5dabf70 Binary files /dev/null and b/clay-odin/windows/clay.lib differ diff --git a/imgui_impl_raylib/imgui_impl_raylib.odin b/imgui_impl_raylib/imgui_impl_raylib.odin new file mode 100644 index 0000000..96845c4 --- /dev/null +++ b/imgui_impl_raylib/imgui_impl_raylib.odin @@ -0,0 +1,470 @@ +package imgui_impl_raylib + +// Based on the raylib extras rlImGui: https://github.com/raylib-extras/rlImGui/blob/main/rlImGui.cpp +/* Usage: + +import imgui_rl "imgui_impl_raylib" +import imgui "../../odin-imgui" + +main :: proc() { + rl.SetConfigFlags({ rl.ConfigFlag.WINDOW_RESIZABLE }) + rl.InitWindow(800, 600, "raylib basic window") + defer rl.CloseWindow() + + imgui.CreateContext(nil) + defer imgui.DestroyContext(nil) + + imgui_rl.init() + defer imgui_rl.shutdown() + + imgui_rl.build_font_atlas() + + for !rl.WindowShouldClose() { + imgui_rl.process_events() + imgui_rl.new_frame() + imgui.NewFrame() + + rl.BeginDrawing() + rl.ClearBackground(rl.RAYWHITE) + + imgui.ShowDemoWindow(nil) + + imgui.Render() + imgui_rl.render_draw_data(imgui.GetDrawData()) + + rl.EndDrawing() + } +} +*/ + +import "core:c" +import "core:mem" +import "core:math" + +// Follow build instruction for imgui bindings in: https://gitlab.com/L-4/odin-imgui +import imgui "../odin-imgui" +import rl "vendor:raylib" + +current_mouse_cursor: imgui.MouseCursor = imgui.MouseCursor.COUNT +mouse_cursor_map: [imgui.MouseCursor.COUNT]rl.MouseCursor + +last_frame_focused := false +last_control_pressed := false +last_shift_pressed := false +last_alt_pressed := false +last_super_pressed := false + +raylib_key_map: map[rl.KeyboardKey]imgui.Key = {} + +init :: proc() -> bool { + setup_globals() + setup_keymap() + setup_mouse_cursor() + setup_backend() + + return true +} + +build_font_atlas :: proc() -> mem.Allocator_Error { + io: ^imgui.IO = imgui.GetIO() + + pixels: ^c.uchar + width, height: c.int + imgui.FontAtlas_GetTexDataAsRGBA32(io.Fonts, &pixels, &width, &height, nil) + image: rl.Image = rl.GenImageColor(width, height, rl.BLANK) + mem.copy(image.data, pixels, int(width * height * 4)) + + font_texture: ^rl.Texture2D = transmute(^rl.Texture)io.Fonts.TexID + if font_texture != nil && font_texture.id != 0 { + rl.UnloadTexture(font_texture^) + mem.free(font_texture) + } + + font_texture = cast(^rl.Texture2D)(mem.alloc(size_of(rl.Texture2D), align_of(rl.Texture2D)) or_return) + font_texture^ = rl.LoadTextureFromImage(image) + rl.UnloadImage(image) + io.Fonts.TexID = font_texture + + return nil +} + +shutdown :: proc() { + io: ^imgui.IO = imgui.GetIO() + font_texture: ^rl.Texture2D = transmute(^rl.Texture)io.Fonts.TexID + + if font_texture != nil && font_texture.id != 0 { + rl.UnloadTexture(font_texture^) + mem.free(font_texture) + } + + io.Fonts.TexID = nil +} + +new_frame :: proc() { + io: ^imgui.IO = imgui.GetIO() + + if rl.IsWindowFullscreen() { + monitor := rl.GetCurrentMonitor() + io.DisplaySize.x = f32(rl.GetMonitorWidth(monitor)) + io.DisplaySize.y = f32(rl.GetMonitorHeight(monitor)) + } else { + io.DisplaySize.x = f32(rl.GetScreenWidth()) + io.DisplaySize.y = f32(rl.GetScreenHeight()) + } + + io.DisplayFramebufferScale = rl.GetWindowScaleDPI() + io.DeltaTime = rl.GetFrameTime() + + if io.WantSetMousePos { + rl.SetMousePosition(c.int(io.MousePos.x), c.int(io.MousePos.y)) + } else { + mouse_pos := rl.GetMousePosition() + imgui.IO_AddMousePosEvent(io, mouse_pos.x, mouse_pos.y) + } + + set_mouse_event :: proc(io: ^imgui.IO, rl_mouse: rl.MouseButton, imgui_mouse: c.int) { + if rl.IsMouseButtonPressed(rl_mouse) { + imgui.IO_AddMouseButtonEvent(io, imgui_mouse, true) + } else if rl.IsMouseButtonReleased(rl_mouse) { + imgui.IO_AddMouseButtonEvent(io, imgui_mouse, false) + } + } + + set_mouse_event(io, rl.MouseButton.LEFT, c.int(imgui.MouseButton.Left)) + set_mouse_event(io, rl.MouseButton.RIGHT, c.int(imgui.MouseButton.Right)) + set_mouse_event(io, rl.MouseButton.MIDDLE, c.int(imgui.MouseButton.Middle)) + set_mouse_event(io, rl.MouseButton.FORWARD, c.int(imgui.MouseButton.Middle) + 1) + set_mouse_event(io, rl.MouseButton.BACK, c.int(imgui.MouseButton.Middle) + 2) + + mouse_wheel := rl.GetMouseWheelMoveV() + imgui.IO_AddMouseWheelEvent(io, mouse_wheel.x, mouse_wheel.y) + + if imgui.ConfigFlag.NoMouseCursorChange not_in io.ConfigFlags { + imgui_cursor: imgui.MouseCursor = imgui.GetMouseCursor() + if imgui_cursor != current_mouse_cursor || io.MouseDrawCursor { + current_mouse_cursor = imgui_cursor + if io.MouseDrawCursor || imgui_cursor == imgui.MouseCursor.None { + rl.HideCursor() + } else { + rl.ShowCursor() + if c.int(imgui_cursor) > -1 && imgui_cursor < imgui.MouseCursor.COUNT { + rl.SetMouseCursor(mouse_cursor_map[imgui_cursor]) + } else { + rl.SetMouseCursor(rl.MouseCursor.DEFAULT) + } + } + } + } +} + +render_draw_data :: proc(draw_data: ^imgui.DrawData) { + rl.rlDrawRenderBatchActive() + rl.rlDisableBackfaceCulling() + + command_lists: []^imgui.DrawList = mem.slice_ptr(draw_data.CmdLists.Data, int(draw_data.CmdLists.Size)) + for command_list in command_lists { + cmd_slice: []imgui.DrawCmd = mem.slice_ptr(command_list.CmdBuffer.Data, int(command_list.CmdBuffer.Size)) + for i in 0.. bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_CONTROL) || rl.IsKeyDown(rl.KeyboardKey.LEFT_CONTROL); } +is_shift_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_SHIFT) || rl.IsKeyDown(rl.KeyboardKey.LEFT_SHIFT); } +is_alt_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_ALT) || rl.IsKeyDown(rl.KeyboardKey.LEFT_ALT); } +is_super_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_SUPER) || rl.IsKeyDown(rl.KeyboardKey.LEFT_SUPER); } + +process_events :: proc() -> bool { + io: ^imgui.IO = imgui.GetIO() + + focused := rl.IsWindowFocused() + if (focused != last_frame_focused) { + imgui.IO_AddFocusEvent(io, focused) + } + last_frame_focused = focused + + // Handle modifers for key evets so that shortcuts work + ctrl_down := is_control_down() + if ctrl_down != last_control_pressed { + imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Ctrl, ctrl_down) + } + last_control_pressed = ctrl_down + + shift_down := is_shift_down() + if shift_down != last_shift_pressed { + imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Shift, shift_down) + } + last_shift_pressed = shift_down + + alt_down := is_alt_down() + if alt_down != last_alt_pressed { + imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Alt, alt_down) + } + last_alt_pressed = alt_down + + super_down := is_super_down() + if super_down != last_super_pressed { + imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Super, super_down) + } + last_super_pressed = super_down + + // Get pressed keys, they are in event order + key_id: rl.KeyboardKey = rl.GetKeyPressed() + for key_id != rl.KeyboardKey.KEY_NULL { + key, ok := raylib_key_map[key_id] + if ok { + imgui.IO_AddKeyEvent(io, key, true) + } + + key_id = rl.GetKeyPressed() + } + + // Check for released keys + for key in raylib_key_map { + if rl.IsKeyReleased(key) { + imgui.IO_AddKeyEvent(io, raylib_key_map[key], false) + } + } + + // Add text input in order + pressed: rune = rl.GetCharPressed() + for pressed != 0 { + imgui.IO_AddInputCharacter(io, u32(pressed)) + pressed = rl.GetCharPressed() + } + + return true +} + +@private +setup_globals :: proc() { + last_frame_focused = rl.IsWindowFocused() + last_control_pressed = false + last_shift_pressed = false + last_alt_pressed = false + last_super_pressed = false +} + +@private +setup_keymap :: proc() { + raylib_key_map[rl.KeyboardKey.APOSTROPHE] = imgui.Key.Apostrophe + raylib_key_map[rl.KeyboardKey.COMMA] = imgui.Key.Comma + raylib_key_map[rl.KeyboardKey.MINUS] = imgui.Key.Minus + raylib_key_map[rl.KeyboardKey.PERIOD] = imgui.Key.Period + raylib_key_map[rl.KeyboardKey.SLASH] = imgui.Key.Slash + raylib_key_map[rl.KeyboardKey.ZERO] = imgui.Key._0 + raylib_key_map[rl.KeyboardKey.ONE] = imgui.Key._1 + raylib_key_map[rl.KeyboardKey.TWO] = imgui.Key._2 + raylib_key_map[rl.KeyboardKey.THREE] = imgui.Key._3 + raylib_key_map[rl.KeyboardKey.FOUR] = imgui.Key._4 + raylib_key_map[rl.KeyboardKey.FIVE] = imgui.Key._5 + raylib_key_map[rl.KeyboardKey.SIX] = imgui.Key._6 + raylib_key_map[rl.KeyboardKey.SEVEN] = imgui.Key._7 + raylib_key_map[rl.KeyboardKey.EIGHT] = imgui.Key._8 + raylib_key_map[rl.KeyboardKey.NINE] = imgui.Key._9 + raylib_key_map[rl.KeyboardKey.SEMICOLON] = imgui.Key.Semicolon + raylib_key_map[rl.KeyboardKey.EQUAL] = imgui.Key.Equal + raylib_key_map[rl.KeyboardKey.A] = imgui.Key.A + raylib_key_map[rl.KeyboardKey.B] = imgui.Key.B + raylib_key_map[rl.KeyboardKey.C] = imgui.Key.C + raylib_key_map[rl.KeyboardKey.D] = imgui.Key.D + raylib_key_map[rl.KeyboardKey.E] = imgui.Key.E + raylib_key_map[rl.KeyboardKey.F] = imgui.Key.F + raylib_key_map[rl.KeyboardKey.G] = imgui.Key.G + raylib_key_map[rl.KeyboardKey.H] = imgui.Key.H + raylib_key_map[rl.KeyboardKey.I] = imgui.Key.I + raylib_key_map[rl.KeyboardKey.J] = imgui.Key.J + raylib_key_map[rl.KeyboardKey.K] = imgui.Key.K + raylib_key_map[rl.KeyboardKey.L] = imgui.Key.L + raylib_key_map[rl.KeyboardKey.M] = imgui.Key.M + raylib_key_map[rl.KeyboardKey.N] = imgui.Key.N + raylib_key_map[rl.KeyboardKey.O] = imgui.Key.O + raylib_key_map[rl.KeyboardKey.P] = imgui.Key.P + raylib_key_map[rl.KeyboardKey.Q] = imgui.Key.Q + raylib_key_map[rl.KeyboardKey.R] = imgui.Key.R + raylib_key_map[rl.KeyboardKey.S] = imgui.Key.S + raylib_key_map[rl.KeyboardKey.T] = imgui.Key.T + raylib_key_map[rl.KeyboardKey.U] = imgui.Key.U + raylib_key_map[rl.KeyboardKey.V] = imgui.Key.V + raylib_key_map[rl.KeyboardKey.W] = imgui.Key.W + raylib_key_map[rl.KeyboardKey.X] = imgui.Key.X + raylib_key_map[rl.KeyboardKey.Y] = imgui.Key.Y + raylib_key_map[rl.KeyboardKey.Z] = imgui.Key.Z + raylib_key_map[rl.KeyboardKey.SPACE] = imgui.Key.Space + raylib_key_map[rl.KeyboardKey.ESCAPE] = imgui.Key.Escape + raylib_key_map[rl.KeyboardKey.ENTER] = imgui.Key.Enter + raylib_key_map[rl.KeyboardKey.TAB] = imgui.Key.Tab + raylib_key_map[rl.KeyboardKey.BACKSPACE] = imgui.Key.Backspace + raylib_key_map[rl.KeyboardKey.INSERT] = imgui.Key.Insert + raylib_key_map[rl.KeyboardKey.DELETE] = imgui.Key.Delete + raylib_key_map[rl.KeyboardKey.RIGHT] = imgui.Key.RightArrow + raylib_key_map[rl.KeyboardKey.LEFT] = imgui.Key.LeftArrow + raylib_key_map[rl.KeyboardKey.DOWN] = imgui.Key.DownArrow + raylib_key_map[rl.KeyboardKey.UP] = imgui.Key.UpArrow + raylib_key_map[rl.KeyboardKey.PAGE_UP] = imgui.Key.PageUp + raylib_key_map[rl.KeyboardKey.PAGE_DOWN] = imgui.Key.PageDown + raylib_key_map[rl.KeyboardKey.HOME] = imgui.Key.Home + raylib_key_map[rl.KeyboardKey.END] = imgui.Key.End + raylib_key_map[rl.KeyboardKey.CAPS_LOCK] = imgui.Key.CapsLock + raylib_key_map[rl.KeyboardKey.SCROLL_LOCK] = imgui.Key.ScrollLock + raylib_key_map[rl.KeyboardKey.NUM_LOCK] = imgui.Key.NumLock + raylib_key_map[rl.KeyboardKey.PRINT_SCREEN] = imgui.Key.PrintScreen + raylib_key_map[rl.KeyboardKey.PAUSE] = imgui.Key.Pause + raylib_key_map[rl.KeyboardKey.F1] = imgui.Key.F1 + raylib_key_map[rl.KeyboardKey.F2] = imgui.Key.F2 + raylib_key_map[rl.KeyboardKey.F3] = imgui.Key.F3 + raylib_key_map[rl.KeyboardKey.F4] = imgui.Key.F4 + raylib_key_map[rl.KeyboardKey.F5] = imgui.Key.F5 + raylib_key_map[rl.KeyboardKey.F6] = imgui.Key.F6 + raylib_key_map[rl.KeyboardKey.F7] = imgui.Key.F7 + raylib_key_map[rl.KeyboardKey.F8] = imgui.Key.F8 + raylib_key_map[rl.KeyboardKey.F9] = imgui.Key.F9 + raylib_key_map[rl.KeyboardKey.F10] = imgui.Key.F10 + raylib_key_map[rl.KeyboardKey.F11] = imgui.Key.F11 + raylib_key_map[rl.KeyboardKey.F12] = imgui.Key.F12 + raylib_key_map[rl.KeyboardKey.LEFT_SHIFT] = imgui.Key.LeftShift + raylib_key_map[rl.KeyboardKey.LEFT_CONTROL] = imgui.Key.LeftCtrl + raylib_key_map[rl.KeyboardKey.LEFT_ALT] = imgui.Key.LeftAlt + raylib_key_map[rl.KeyboardKey.LEFT_SUPER] = imgui.Key.LeftSuper + raylib_key_map[rl.KeyboardKey.RIGHT_SHIFT] = imgui.Key.RightShift + raylib_key_map[rl.KeyboardKey.RIGHT_CONTROL] = imgui.Key.RightCtrl + raylib_key_map[rl.KeyboardKey.RIGHT_ALT] = imgui.Key.RightAlt + raylib_key_map[rl.KeyboardKey.RIGHT_SUPER] = imgui.Key.RightSuper + raylib_key_map[rl.KeyboardKey.KB_MENU] = imgui.Key.Menu + raylib_key_map[rl.KeyboardKey.LEFT_BRACKET] = imgui.Key.LeftBracket + raylib_key_map[rl.KeyboardKey.BACKSLASH] = imgui.Key.Backslash + raylib_key_map[rl.KeyboardKey.RIGHT_BRACKET] = imgui.Key.RightBracket + raylib_key_map[rl.KeyboardKey.GRAVE] = imgui.Key.GraveAccent + raylib_key_map[rl.KeyboardKey.KP_0] = imgui.Key.Keypad0 + raylib_key_map[rl.KeyboardKey.KP_1] = imgui.Key.Keypad1 + raylib_key_map[rl.KeyboardKey.KP_2] = imgui.Key.Keypad2 + raylib_key_map[rl.KeyboardKey.KP_3] = imgui.Key.Keypad3 + raylib_key_map[rl.KeyboardKey.KP_4] = imgui.Key.Keypad4 + raylib_key_map[rl.KeyboardKey.KP_5] = imgui.Key.Keypad5 + raylib_key_map[rl.KeyboardKey.KP_6] = imgui.Key.Keypad6 + raylib_key_map[rl.KeyboardKey.KP_7] = imgui.Key.Keypad7 + raylib_key_map[rl.KeyboardKey.KP_8] = imgui.Key.Keypad8 + raylib_key_map[rl.KeyboardKey.KP_9] = imgui.Key.Keypad9 + raylib_key_map[rl.KeyboardKey.KP_DECIMAL] = imgui.Key.KeypadDecimal + raylib_key_map[rl.KeyboardKey.KP_DIVIDE] = imgui.Key.KeypadDivide + raylib_key_map[rl.KeyboardKey.KP_MULTIPLY] = imgui.Key.KeypadMultiply + raylib_key_map[rl.KeyboardKey.KP_SUBTRACT] = imgui.Key.KeypadSubtract + raylib_key_map[rl.KeyboardKey.KP_ADD] = imgui.Key.KeypadAdd + raylib_key_map[rl.KeyboardKey.KP_ENTER] = imgui.Key.KeypadEnter + raylib_key_map[rl.KeyboardKey.KP_EQUAL] = imgui.Key.KeypadEqual +} + +@private +setup_mouse_cursor :: proc() { + mouse_cursor_map[imgui.MouseCursor.Arrow] = rl.MouseCursor.ARROW; + mouse_cursor_map[imgui.MouseCursor.TextInput] = rl.MouseCursor.IBEAM; + mouse_cursor_map[imgui.MouseCursor.Hand] = rl.MouseCursor.POINTING_HAND; + mouse_cursor_map[imgui.MouseCursor.ResizeAll] = rl.MouseCursor.RESIZE_ALL; + mouse_cursor_map[imgui.MouseCursor.ResizeEW] = rl.MouseCursor.RESIZE_EW; + mouse_cursor_map[imgui.MouseCursor.ResizeNESW] = rl.MouseCursor.RESIZE_NESW; + mouse_cursor_map[imgui.MouseCursor.ResizeNS] = rl.MouseCursor.RESIZE_NS; + mouse_cursor_map[imgui.MouseCursor.ResizeNWSE] = rl.MouseCursor.RESIZE_NWSE; + mouse_cursor_map[imgui.MouseCursor.NotAllowed] = rl.MouseCursor.NOT_ALLOWED; +} + +@private +setup_backend :: proc() { + io: ^imgui.IO = imgui.GetIO() + io.BackendPlatformName = "imgui_impl_raylib" + + io.BackendFlags |= { imgui.BackendFlag.HasMouseCursors } + + io.MousePos = { 0, 0 } + + io.SetClipboardTextFn = set_clip_text_callback + io.GetClipboardTextFn = get_clip_text_callback + + io.ClipboardUserData = nil +} + +@private +set_clip_text_callback :: proc "c" (user_data: rawptr, text: cstring) { + rl.SetClipboardText(text) +} + +@private +get_clip_text_callback :: proc "c" (user_data: rawptr) -> cstring { + return rl.GetClipboardText() +} \ No newline at end of file diff --git a/odin-imgui b/odin-imgui new file mode 160000 index 0000000..0615247 --- /dev/null +++ b/odin-imgui @@ -0,0 +1 @@ +Subproject commit 0615247fab19bc7317fba308aff5de087cc2dd39