/* * Copyright (c) 2025 Aldrik Ramaekers * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ui.hpp" #include "imgui.h" #include "locales.hpp" #include "strops.hpp" ImFont* fontBold; ImFont* fontBig; static ui_status current_status; void ui_draw_status() { float region_width = ImGui::GetContentRegionAvail().x; float text_width = ImGui::CalcTextSize(current_status.text).x; if (current_status.loading) { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + region_width - text_width - 20.0f); ImGui::Text("%c", "|/-\\"[(int)(ImGui::GetTime() / 0.1f) & 3]); return; } if (current_status.visible) { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + region_width - text_width); ImGui::PushStyleColor(ImGuiCol_Text, current_status.color); ImGui::TextUnformatted(current_status.text); ImGui::PopStyleColor(); } ImGuiIO& io = ImGui::GetIO(); current_status.time += io.DeltaTime; if (current_status.time >= STATUS_FLASH_INTERVAL && current_status.flash_count < STATUS_MAX_FLASHES) { current_status.visible = !current_status.visible; if (current_status.visible) current_status.flash_count++; current_status.time = 0.0f; } if (current_status.time >= STATUS_DURATION) { current_status.text[0] = 0; } } void ui_set_status_ex(const char* txt, int color) { current_status.flash_count = 0; current_status.visible = true; current_status.time = 0.0f; current_status.color = color; current_status.loading = false; strops_copy(current_status.text, txt, STATUS_TEXT_LEN); } void ui_set_status_error(const char* txt) { ui_set_status_ex(txt, COLOR_ERROR); } void ui_set_status(const char* txt) { ui_set_status_ex(txt, COLOR_DEFAULT); } void ui_set_status_loading(bool loading) { current_status.visible = true; current_status.time = 0.0f; current_status.loading = loading; } ui_status ui_get_status() { return current_status; } void ui_helper_draw_required_tag() { ImDrawList* draw_list = ImGui::GetWindowDrawList(); const char* text = localize("form.required"); ImVec2 text_pos = ImGui::GetCursorScreenPos(); ImVec2 text_size = ImGui::CalcTextSize(text); text_pos.y += text_size.y/4.0f; ImVec4 bg_color = ImVec4(0.9f, 0.235f, 0.235f, 0.4f); // Red background ImVec4 text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // White text float rounding = 2.0f; float padding = 2.0f; // Background rectangle ImVec2 bg_min = ImVec2(text_pos.x - padding, text_pos.y - padding); ImVec2 bg_max = ImVec2(text_pos.x + text_size.x + padding, text_pos.y + text_size.y + padding); draw_list->AddRectFilled(bg_min, bg_max, ImColor(bg_color), rounding); // Foreground text ImGui::PushStyleColor(ImGuiCol_Text, text_color); ImGui::TextUnformatted(text); ImGui::PopStyleColor(); } int TextInputWithAutocomplete(const char* label, const char* hint, char* buffer, size_t buf_size, char* suggestions[], int suggestion_count) { int result = -1; static bool is_open = false; ImGui::InputTextWithHint(label, hint, buffer, buf_size); if (buffer[0] == '\0' && is_open) is_open = false; if (suggestion_count == 0 && is_open) is_open = false; bool is_active = ImGui::IsItemActive(); if (is_active && buffer[0] != '\0' && suggestion_count > 0) { is_open = true; } if (is_open) { ImGui::BeginChild("autocomplete_popup", ImVec2(0, 10.0f + suggestion_count*23.0f), true); { ImVec2 win_pos = ImGui::GetWindowPos(); ImVec2 win_size = ImGui::GetWindowSize(); ImVec2 mouse_pos = ImGui::GetMousePos(); bool mouse_clicked_outside = !is_active && ImGui::IsMouseClicked(0) && (mouse_pos.x < win_pos.x || mouse_pos.x > win_pos.x + win_size.x || mouse_pos.y < win_pos.y || mouse_pos.y > win_pos.y + win_size.y); if (mouse_clicked_outside) is_open = false; for (int i = 0; i < suggestion_count; ++i) { ImGui::PushID(i); if (ImGui::Selectable(suggestions[i])) { // Copy selected suggestion to buffer strops_copy(buffer, suggestions[i], buf_size); buffer[buf_size - 1] = '\0'; result = i; is_open = false; } ImGui::PopID(); } ImGui::EndChild(); } } return result; }