diff options
Diffstat (limited to 'src/ui/imgui_extensions.cpp')
| -rw-r--r-- | src/ui/imgui_extensions.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/src/ui/imgui_extensions.cpp b/src/ui/imgui_extensions.cpp new file mode 100644 index 0000000..5fdc3a7 --- /dev/null +++ b/src/ui/imgui_extensions.cpp @@ -0,0 +1,414 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "ui.hpp" +#include "strops.hpp" +#include "config.hpp" +#include "locales.hpp" +#include "administration.hpp" + +namespace ImGui +{ + void FormInputTextWithErrorHint(const char* hint, void* data, char* buffer, size_t buf_size, bool has_error) + { + float widthAvailable = ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(widthAvailable*0.5f); + + char id[MAX_LEN_LONG_DESC]; + snprintf(id, sizeof(id), "%s##%p", hint, data); + + if (has_error) { + ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f); + } + ImGui::InputTextWithHint(id, hint, buffer, buf_size); + if (has_error) { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x); + ImGui::TextColored(ImVec4(1, 0, 0, 1), "*"); + } + } + + void FormCountryCombo(void* data, char* buffer, size_t buf_size) + { + const char* selected_country = 0; + char id[MAX_LEN_LONG_DESC]; + float widthAvailable = ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(widthAvailable*0.5f); + + const char* countries[30]; + for (int x = 0; x < country_count; x++) + { + char locale_str[20]; + snprintf(locale_str, 20, "country.%s", country_codes[x]); + countries[x] = localize(locale_str); + } + + for (int i = 0; i < country_count; i++) + { + if (strcmp(country_codes[i], buffer) == 0) + { + selected_country = countries[i]; + break; + } + } + bool has_a_selection = selected_country != 0; + + int selected_country_index = -1; + snprintf(id, sizeof(id), "%s##%p", localize("contact.form.country"), data); + + if (!has_a_selection) { + ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f); + } + + if (ImGui::BeginCombo(id, selected_country)) + { + for (int n = 0; n < country_count; n++) + { + bool is_selected = (selected_country == countries[n]); + if (ImGui::Selectable(countries[n], is_selected)) { + selected_country = countries[n]; + selected_country_index = n; + } + } + ImGui::EndCombo(); + } + + if (!has_a_selection) { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x); + ImGui::TextColored(ImVec4(1, 0, 0, 1), "*"); + } + + if (selected_country_index != -1) { + strops_copy(buffer, country_codes[selected_country_index], buf_size); + } + } + + void FormContactTypeCombo(contact_type* type) + { + float widthAvailable = ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(widthAvailable*0.5f); + const char* customer_types[2] = { localize("contact.form.type.business"), localize("contact.form.type.consumer") }; + int currentItem = static_cast<int>(*type); + if (ImGui::Combo(localize("contact.form.type"), ¤tItem, customer_types, IM_ARRAYSIZE(customer_types))) + { + *type = static_cast<contact_type>(currentItem); + } + } + + + int TextInputWithAutocomplete(const char* hint, char* buffer, size_t buf_size, char* suggestions[], int suggestion_count, bool has_error) + { + int result = -1; + static bool is_open = false; + + if (has_error) { + ImGui::PushStyleColor(ImGuiCol_Border, COLOR_ERROR_OUTLINE); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5f); + } + ImGui::InputTextWithHint(hint, hint, buffer, buf_size); + if (has_error) { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::CalcTextSize("*").x); + ImGui::TextColored(ImVec4(1, 0, 0, 1), "*"); + } + + + 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; + } + + void FormContactAutocomplete(contact* buffer, bool has_error) + { + float widthAvailable = ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(widthAvailable*0.5f); + + contact autocomplete_list[5]; + int autocomplete_count = administration_contact_get_autocompletions(autocomplete_list, 5, buffer->name); + char* autocomplete_strings[5]; + + for (int i = 0; i < autocomplete_count; i++) + { + autocomplete_strings[i] = (char*)malloc(200); + snprintf(autocomplete_strings[i], 200, "%s (%s %s)", autocomplete_list[i].name, autocomplete_list[i].address.address1, autocomplete_list[i].address.address2); + } + + int autocomplete_index = ImGui::TextInputWithAutocomplete(localize("contact.form.fullname"), + buffer->name, IM_ARRAYSIZE(buffer->name), (char**)autocomplete_strings, autocomplete_count, has_error); + + if (autocomplete_index != -1) + { + memcpy(buffer, &autocomplete_list[autocomplete_index], sizeof(contact)); + } + + for (int i = 0; i < autocomplete_count; i++) + { + free(autocomplete_strings[i]); + } + } + + void FormCostCenterCombo(char* costcenter_id) + { + u32 costcenter_count = administration_cost_center_count(); + cost_center* buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count); + + cost_center* selected_costcenter = NULL; + costcenter_count = administration_cost_center_get_all(buffer); + + // Select cost center by given id. + if (strlen(costcenter_id) > 0) + { + for (u32 i = 0; i < costcenter_count; i++) + { + if (strcmp(buffer[i].id, costcenter_id) == 0) + { + selected_costcenter = &buffer[i]; + break; + } + } + } + + int selected_costcenter_index = -1; + if (ImGui::BeginCombo(localize("invoice.form.costcenter"), selected_costcenter == NULL ? NULL : localize(selected_costcenter->description))) + { + for (u32 n = 0; n < costcenter_count; n++) + { + bool is_selected = selected_costcenter && strcmp(selected_costcenter->id, buffer[n].id) == 0; + if (ImGui::Selectable(localize(buffer[n].description), is_selected)) { + selected_costcenter_index = n; + } + } + ImGui::EndCombo(); + } + + if (selected_costcenter_index != -1) { + strops_copy(costcenter_id, buffer[selected_costcenter_index].id, MAX_LEN_ID); + } + + free(buffer); + } + + void FormProjectCombo(char* project_id) + { + u32 project_count = administration_project_count(); + project* buffer = (project*) malloc(sizeof(project) * project_count); + + project* selected_project = NULL; + project_count = administration_project_get_all(buffer); + + // Select project by given id. + if (strlen(project_id) > 0) + { + for (u32 i = 0; i < project_count; i++) + { + if (strcmp(buffer[i].id, project_id) == 0) + { + selected_project = &buffer[i]; + break; + } + } + } + + int selected_project_index = -1; + if (ImGui::BeginCombo(localize("invoice.form.project"), selected_project == NULL ? NULL : selected_project->description)) + { + for (u32 n = 0; n < project_count; n++) + { + bool is_selected = selected_project && strcmp(selected_project->id, buffer[n].id) == 0; + if (ImGui::Selectable(buffer[n].description, is_selected)) { + selected_project_index = n; + } + } + ImGui::EndCombo(); + } + + if (selected_project_index != -1) { + strops_copy(project_id, buffer[selected_project_index].id, MAX_LEN_ID); + } + + free(buffer); + } + + void FormTaxRateCombo(char* tax_rate_id, char* orig_country, char* dest_country) + { + u32 tax_rate_count = administration_tax_rate_count(); + tax_rate* buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count); + + tax_rate* selected_tax_rate = NULL; + char* tax_country_codes[2] = {orig_country, dest_country}; + tax_rate_count = administration_tax_rate_get_by_country(buffer, strcmp(orig_country, dest_country) == 0 ? 1 : 2, tax_country_codes); + + // Select tax rate by given id. + if (strlen(tax_rate_id) > 0) + { + for (u32 i = 0; i < tax_rate_count; i++) + { + if (strcmp(buffer[i].id, tax_rate_id) == 0) + { + selected_tax_rate = &buffer[i]; + break; + } + } + } + + int selected_tax_rate_index = -1; + char rate_str_buf[40]; + rate_str_buf[0] = 0; + if (selected_tax_rate) + { + if (strcmp(selected_tax_rate->country_code, "00") == 0) { + char category_code_desc[MAX_LEN_LONG_DESC]; + snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", selected_tax_rate->category_code); + snprintf(rate_str_buf, 40, "%s", localize(category_code_desc)); + } + else snprintf(rate_str_buf, 40, "%s/%.1f%%", selected_tax_rate->country_code, selected_tax_rate->rate); + } + + if (ImGui::BeginCombo("##Tax Bracket", rate_str_buf)) + { + for (u32 n = 0; n < tax_rate_count; n++) + { + bool is_selected = selected_tax_rate && strcmp(selected_tax_rate->id, buffer[n].id) == 0; + + if (strcmp(buffer[n].country_code, "00") == 0) { + char category_code_desc[MAX_LEN_LONG_DESC]; + snprintf(category_code_desc, MAX_LEN_LONG_DESC, "taxcategory.%s", buffer[n].category_code); + snprintf(rate_str_buf, 40, "%s", localize(category_code_desc)); + } + else snprintf(rate_str_buf, 40, "%s/%.1f%%", buffer[n].country_code, buffer[n].rate); + + if (ImGui::Selectable(rate_str_buf, is_selected)) { + selected_tax_rate_index = n; + } + } + ImGui::EndCombo(); + } + + if (selected_tax_rate_index != -1) { + strops_copy(tax_rate_id, buffer[selected_tax_rate_index].id, MAX_LEN_ID); + } + + free(buffer); + } + + bool FormCurrencyCombo(char* currency) + { + int currentCurrency = 0; + bool result = false; + + // Top 15 most traded currencies + all EU official currencies + const char* currencies[] = { + // Top 15 + "EUR", "USD", "JPY", "GBP", "AUD", "CAD", "CHF", + "CNY", "HKD", "NZD", "SEK", "KRW", "SGD", "NOK", "MXN", + + // Additional EU currencies + "BGN", // Bulgarian Lev + "CZK", // Czech Koruna + "DKK", // Danish Krone + "HUF", // Hungarian Forint + "PLN", // Polish Zloty + "RON", // Romanian Leu + // "HRK", // Croatian Kuna (legacy, replaced by EUR in 2023) + }; + int currency_count = sizeof(currencies) / sizeof(char*); + + if (strlen(currency) > 0) + { + for (int i = 0; i < currency_count; i++) + { + if (strcmp(currencies[i], currency) == 0) + { + currentCurrency = i; + break; + } + } + } + + ImGui::SetNextItemWidth(100.0f); + if (ImGui::BeginCombo("##currency", currencies[currentCurrency])) + { + for (int n = 0; n < IM_ARRAYSIZE(currencies); n++) + { + bool isSelected = (currentCurrency == n); + if (ImGui::Selectable(currencies[n], isSelected)) + { + result = true; + strops_copy(currency, currencies[n], MAX_LEN_CURRENCY); + } + + if (isSelected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + return result; + } + + void FormToggleCombo(bool *buffer, char* option1, char* option2) + { + const char* items[] = { option1, option2 }; + if (ImGui::BeginCombo("Mode", items[*buffer])) { + for (int n = 0; n < 2; n++) { + bool is_selected = (n == (int)*buffer); + if (ImGui::Selectable(items[n], is_selected)) { + *buffer = n; + } + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + } +}
\ No newline at end of file |
