Last active
July 22, 2025 17:46
-
-
Save naranyala/c974b6158e82ed9120ed0c57e43c815c to your computer and use it in GitHub Desktop.
for the community, a modal dialog implementation of raylib using odin programming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import "core:fmt" | |
| import "core:strings" | |
| import rl "vendor:raylib" | |
| // Package-level variable for animation state | |
| scale: f32 = 0 | |
| ui_modal :: proc( | |
| title: string, | |
| message: string, | |
| is_open: ^bool, | |
| buttons: []string, | |
| bounds: rl.Rectangle = {0, 0, 400, 300}, | |
| anim_speed: f32 = 0.15, | |
| ) -> int { | |
| // Create modifiable copy of bounds | |
| modal_bounds := bounds | |
| // Center if using default position | |
| if modal_bounds.x == 0 && modal_bounds.y == 0 { | |
| modal_bounds.x = (f32(rl.GetScreenWidth()) - modal_bounds.width) / 2 | |
| modal_bounds.y = (f32(rl.GetScreenHeight()) - modal_bounds.height) / 2 | |
| } | |
| // Animation state | |
| target_scale := is_open^ ? 1.0 : 0.0 | |
| scale += (f32(target_scale) - scale) * anim_speed | |
| // Early exit if closed | |
| if scale <= 0.01 && !is_open^ { | |
| return -1 | |
| } | |
| mouse := rl.GetMousePosition() | |
| result := -1 | |
| // Constants for layout | |
| PADDING :: 20 | |
| TITLE_HEIGHT :: 40 | |
| BUTTON_HEIGHT :: 40 | |
| BUTTON_SPACING :: 10 | |
| // Draw overlay | |
| if scale > 0 { | |
| rl.DrawRectangle( | |
| 0, | |
| 0, | |
| rl.GetScreenWidth(), | |
| rl.GetScreenHeight(), | |
| rl.Color{0, 0, 0, u8(200 * scale)}, | |
| ) | |
| } | |
| // Draw modal content | |
| if scale > 0.01 { | |
| // Calculate animated bounds | |
| scaled_bounds := modal_bounds | |
| scaled_bounds.width *= scale | |
| scaled_bounds.height *= scale | |
| scaled_bounds.x = modal_bounds.x + (modal_bounds.width - scaled_bounds.width) / 2 | |
| scaled_bounds.y = modal_bounds.y + (modal_bounds.height - scaled_bounds.height) / 2 | |
| // Main panel | |
| rl.DrawRectangleRec(scaled_bounds, rl.WHITE) | |
| rl.DrawRectangleLinesEx(scaled_bounds, 2, rl.BLACK) | |
| // Title area | |
| title_rect := rl.Rectangle { | |
| scaled_bounds.x + PADDING, | |
| scaled_bounds.y + PADDING, | |
| scaled_bounds.width - 2 * PADDING, | |
| TITLE_HEIGHT, | |
| } | |
| rl.DrawTextEx( | |
| rl.GetFontDefault(), | |
| strings.clone_to_cstring(title), | |
| rl.Vector2{title_rect.x, title_rect.y}, | |
| 24, | |
| 1, | |
| rl.BLACK, | |
| ) | |
| // Message area - using DrawTextEx with word wrap | |
| msg_pos := rl.Vector2{scaled_bounds.x + PADDING, scaled_bounds.y + PADDING + TITLE_HEIGHT} | |
| msg_width := scaled_bounds.width - 2 * PADDING | |
| rl.DrawTextEx( | |
| rl.GetFontDefault(), | |
| strings.clone_to_cstring(message), | |
| msg_pos, | |
| 20, | |
| 1, | |
| rl.DARKGRAY, | |
| ) | |
| // Buttons | |
| button_width := | |
| (scaled_bounds.width - 2 * PADDING - f32(len(buttons) - 1) * BUTTON_SPACING) / | |
| f32(len(buttons)) | |
| button_y := scaled_bounds.y + scaled_bounds.height - BUTTON_HEIGHT - PADDING | |
| for button, i in buttons { | |
| button_rect := rl.Rectangle { | |
| scaled_bounds.x + PADDING + f32(i) * (button_width + BUTTON_SPACING), | |
| button_y, | |
| button_width, | |
| BUTTON_HEIGHT, | |
| } | |
| // Button state | |
| hovered := rl.CheckCollisionPointRec(mouse, button_rect) | |
| color := hovered ? rl.SKYBLUE : rl.LIGHTGRAY | |
| // Draw button | |
| rl.DrawRectangleRec(button_rect, color) | |
| rl.DrawRectangleLinesEx(button_rect, 1, rl.BLACK) | |
| // Center text | |
| btn_text := strings.clone_to_cstring(button) | |
| defer delete(btn_text) | |
| text_width := rl.MeasureText(btn_text, 20) | |
| rl.DrawText( | |
| btn_text, | |
| i32(button_rect.x + (button_rect.width - f32(text_width)) / 2), | |
| i32(button_rect.y + BUTTON_HEIGHT / 2 - 10), | |
| 20, | |
| rl.BLACK, | |
| ) | |
| // Handle click | |
| if hovered && rl.IsMouseButtonReleased(.LEFT) { | |
| result = i | |
| is_open^ = false | |
| } | |
| } | |
| // Close when clicking outside | |
| if rl.IsMouseButtonPressed(.LEFT) && !rl.CheckCollisionPointRec(mouse, scaled_bounds) { | |
| is_open^ = false | |
| } | |
| } | |
| return result | |
| } | |
| main :: proc() { | |
| rl.InitWindow(800, 600, "Modal Demo") | |
| defer rl.CloseWindow() | |
| show_modal := false | |
| last_result := -1 | |
| for !rl.WindowShouldClose() { | |
| if rl.IsKeyPressed(.F) { | |
| show_modal = !show_modal | |
| } | |
| rl.BeginDrawing() | |
| defer rl.EndDrawing() | |
| rl.ClearBackground(rl.RAYWHITE) | |
| rl.DrawText("Press F to toggle modal", 20, 20, 20, rl.BLACK) | |
| // Show modal | |
| result := ui_modal( | |
| "Confirm Action", | |
| "Are you sure you want to proceed?\nThis action cannot be undone.\n\nPlease confirm your choice:", | |
| &show_modal, | |
| []string{"Yes", "No", "Cancel"}, | |
| anim_speed = 0.2, | |
| ) | |
| if result >= 0 { | |
| last_result = result | |
| } | |
| if last_result >= 0 { | |
| status := fmt.tprintf("Last selection: %d", last_result) | |
| status_cstr := strings.clone_to_cstring(status) | |
| defer delete(status_cstr) | |
| rl.DrawText(status_cstr, 20, 50, 20, rl.BLACK) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment