Files
HanjaKanaMun/src/ui/ui.odin
T
SansanandClaude Opus 5.5 8214fda8f6 Add Katakana, Korean and Thai games and difficulty levels
- decks package: Hiragana, Katakana, Hangul jamo and Thai consonants
  (Thai readings carry the letter's name word, since sounds repeat)
- Per-script fonts: Jua for Hangul, Mali for Thai (both OFL)
- Memory game works with any deck; Easy 3x4, Normal 4x6, Hard 4x8
- Language menus pick a deck and difficulty, with number/arrow key shortcuts
- Tests cover every deck, difficulty, and font glyph coverage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:33:16 +02:00

96 lines
3.4 KiB
Odin

package ui
import raylib "vendor:raylib"
import assets "../assets"
import audio "../audio"
import themes "../themes"
Font_Style :: assets.Font_Style
TEXT_SPACING :: 1
@(private)
any_hovered: bool
// Call at the start of each frame's drawing
begin_frame :: proc() {
any_hovered = false
}
// Call at the end of each frame's drawing
end_frame :: proc() {
raylib.SetMouseCursor(any_hovered ? .POINTING_HAND : .DEFAULT)
}
// Mark that something clickable is under the mouse (shows the hand cursor)
set_hovered :: proc() {
any_hovered = true
}
measure_text :: proc(text: cstring, size: f32, style: Font_Style = .REGULAR) -> raylib.Vector2 {
return raylib.MeasureTextEx(assets.get_font(style), text, size, TEXT_SPACING)
}
draw_text :: proc(text: cstring, pos: raylib.Vector2, size: f32, color: raylib.Color, style: Font_Style = .REGULAR) {
raylib.DrawTextEx(assets.get_font(style), text, pos, size, TEXT_SPACING, color)
}
// Draws text horizontally centered on center_x with its top at y
draw_text_centered :: proc(text: cstring, center_x, y, size: f32, color: raylib.Color,
style: Font_Style = .REGULAR, shadow := false) {
w := measure_text(text, size, style).x
pos := raylib.Vector2{center_x - w / 2, y}
if shadow {
draw_text(text, pos + {0, size * 0.05}, size, raylib.Fade(themes.BLACK, 0.18), style)
}
draw_text(text, pos, size, color, style)
}
// Draws text centered in a rectangle
draw_text_in_rect :: proc(text: cstring, rect: raylib.Rectangle, size: f32, color: raylib.Color,
style: Font_Style = .REGULAR) {
dims := measure_text(text, size, style)
pos := raylib.Vector2{rect.x + (rect.width - dims.x) / 2, rect.y + (rect.height - dims.y) / 2}
draw_text(text, pos, size, color, style)
}
draw_title :: proc(text: cstring, y: f32, color: raylib.Color, size: f32 = themes.TITLE_FONT_SIZE) {
draw_text_centered(text, themes.SCREEN_WIDTH / 2, y, size, color, style = .BOLD, shadow = true)
}
// A rounded button with hover, press and shadow states. Returns true when clicked
// or when its hotkey is pressed.
button :: proc(rect: raylib.Rectangle, text: cstring, color: raylib.Color,
text_color := themes.WHITE, font_size: f32 = themes.BUTTON_FONT_SIZE,
hotkey := raylib.KeyboardKey.KEY_NULL, selected := false) -> bool {
hovered := raylib.CheckCollisionPointRec(raylib.GetMousePosition(), rect)
pressed := hovered && raylib.IsMouseButtonDown(.LEFT)
if hovered { set_hovered() }
lift: f32 = 0
fill := color
if hovered {
lift = 2
fill = raylib.ColorBrightness(color, 0.12)
}
if pressed {
lift = -1
fill = raylib.ColorBrightness(color, -0.08)
}
shadow := raylib.Rectangle{rect.x, rect.y + 4, rect.width, rect.height}
raylib.DrawRectangleRounded(shadow, 0.4, 12, raylib.Fade(themes.BLACK, 0.18))
body := raylib.Rectangle{rect.x, rect.y - lift, rect.width, rect.height}
raylib.DrawRectangleRounded(body, 0.4, 12, fill)
if selected {
ring := raylib.Rectangle{body.x - 4, body.y - 4, body.width + 8, body.height + 8}
raylib.DrawRectangleRoundedLinesEx(ring, 0.4, 12, 3, color)
}
draw_text_in_rect(text, body, font_size, text_color, style = .BOLD)
clicked := hovered && raylib.IsMouseButtonPressed(.LEFT)
if hotkey != .KEY_NULL && raylib.IsKeyPressed(hotkey) { clicked = true }
if clicked { audio.play(.CLICK) }
return clicked
}