diff options
| author | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-09-20 19:16:15 +0200 |
|---|---|---|
| committer | Aldrik Ramaekers <aldrikboy@gmail.com> | 2025-09-20 19:16:15 +0200 |
| commit | 214852f61cd3b9fda257044e9d822b94b4be7e5d (patch) | |
| tree | aa570a16623f1c7eae94474c80b484815ea85c88 /src/ui | |
| parent | 3402cba0fe6fa1b89a029c612b622e7a4771d901 (diff) | |
refactor ui widgets
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/helpers.cpp | 74 | ||||
| -rw-r--r-- | src/ui/imgui_extensions.cpp | 414 | ||||
| -rw-r--r-- | src/ui/ui_contacts.cpp | 163 | ||||
| -rw-r--r-- | src/ui/ui_expenses.cpp | 123 | ||||
| -rw-r--r-- | src/ui/ui_invoices.cpp | 193 | ||||
| -rw-r--r-- | src/ui/ui_projects.cpp | 17 |
6 files changed, 465 insertions, 519 deletions
diff --git a/src/ui/helpers.cpp b/src/ui/helpers.cpp index 07302b7..2447270 100644 --- a/src/ui/helpers.cpp +++ b/src/ui/helpers.cpp @@ -93,77 +93,3 @@ 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; -} 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 diff --git a/src/ui/ui_contacts.cpp b/src/ui/ui_contacts.cpp index 92dd21a..b471f58 100644 --- a/src/ui/ui_contacts.cpp +++ b/src/ui/ui_contacts.cpp @@ -18,6 +18,7 @@ #include <stdlib.h> #include "strops.hpp" +#include "config.hpp" #include "ui.hpp" #include "imgui.h" #include "administration.hpp" @@ -29,76 +30,15 @@ static contact selected_for_removal; static contact active_contact; -void ui_draw_address_form(address* buffer) +void ui_draw_address_form(address* buffer, a_err last_err) { - const char* selected_country = NULL; - float widthAvailable = ImGui::GetContentRegionAvail().x; - - char id[MAX_LEN_ADDRESS]; - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.address1"), buffer); - ImGui::InputTextWithHint(id, localize("contact.form.address1"), buffer->address1, IM_ARRAYSIZE(buffer->address1)); - ImGui::SameLine();ui_helper_draw_required_tag(); - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.address2"), buffer); - ImGui::InputTextWithHint(id, localize("contact.form.address2"), buffer->address2, IM_ARRAYSIZE(buffer->address2)); - //ImGui::SameLine();ui_helper_draw_required_tag(); - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.city"), buffer); - ImGui::InputTextWithHint(id, localize("contact.form.city"), buffer->city, IM_ARRAYSIZE(buffer->city)); - ImGui::SameLine();ui_helper_draw_required_tag(); - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.postal"), buffer); - ImGui::InputTextWithHint(id, localize("contact.form.postal"), buffer->postal, IM_ARRAYSIZE(buffer->postal)); - ImGui::SameLine();ui_helper_draw_required_tag(); - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.region"), buffer); - ImGui::InputTextWithHint(id, localize("contact.form.region"), buffer->region, IM_ARRAYSIZE(buffer->region)); - ImGui::SameLine();ui_helper_draw_required_tag(); - - // 5. Country dropdown. - ImGui::SetNextItemWidth(widthAvailable*0.5f); - const char* countries[] = { localize("country.AT"),localize("country.BE"),localize("country.BG"),localize("country.HR"),localize("country.CY"),localize("country.CZ"),localize("country.DK"),localize("country.EE"),localize("country.FI"),localize("country.FR"),localize("country.DE"),localize("country.GR"),localize("country.HU"),localize("country.IE"),localize("country.IT"),localize("country.LV"),localize("country.LT"),localize("country.LU"),localize("country.MT"),localize("country.NL"),localize("country.PL"),localize("country.PT"),localize("country.RO"),localize("country.SK"),localize("country.SI"),localize("country.ES"),localize("country.SE") }; - const char* country_codes[] = { - "AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", - "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", - "PL", "PT", "RO", "SK", "SI", "ES", "SE" - }; - s32 country_count = sizeof(countries) / sizeof(countries[0]); - if (selected_country == 0) { - for (int i = 0; i < country_count; i++) - { - if (strcmp(country_codes[i], buffer->country_code) == 0) - { - selected_country = countries[i]; - break; - } - } - } + ImGui::FormInputTextWithErrorHint(localize("contact.form.address1"), buffer, buffer->address1, IM_ARRAYSIZE(buffer->address1), last_err & A_ERR_MISSING_ADDRESS1); + ImGui::FormInputTextWithErrorHint(localize("contact.form.address2"), buffer, buffer->address2, IM_ARRAYSIZE(buffer->address2), 0); + ImGui::FormInputTextWithErrorHint(localize("contact.form.city"), buffer, buffer->city, IM_ARRAYSIZE(buffer->city), last_err & A_ERR_MISSING_CITY); + ImGui::FormInputTextWithErrorHint(localize("contact.form.postal"), buffer, buffer->postal, IM_ARRAYSIZE(buffer->postal), last_err & A_ERR_MISSING_POSTAL); + ImGui::FormInputTextWithErrorHint(localize("contact.form.region"), buffer, buffer->region, IM_ARRAYSIZE(buffer->region), 0); - int selected_country_index = -1; - snprintf(id, sizeof(id), "%s##%p", localize("contact.form.country"), buffer); - if (ImGui::BeginCombo(id, selected_country)) - { - for (int n = 0; n < IM_ARRAYSIZE(countries); 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 (selected_country_index != -1) { - strops_copy(buffer->country_code, country_codes[selected_country_index], IM_ARRAYSIZE(buffer->country_code)); - } - ImGui::SameLine();ui_helper_draw_required_tag(); + ImGui::FormCountryCombo(buffer, buffer->country_code, IM_ARRAYSIZE(buffer->country_code)); } void ui_setup_contacts() @@ -108,89 +48,34 @@ void ui_setup_contacts() memset(&selected_for_removal, 0, sizeof(contact)); } -void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0) +void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false) { + a_err last_err = administration_contact_is_valid(*buffer); + ImGui::PushID(buffer); ImGui::Spacing(); - float widthAvailable = ImGui::GetContentRegionAvail().x; - ImGui::BeginDisabled(); - // 1. Identifier - //ImGui::SetNextItemWidth(widthAvailable*0.2f); - //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id)); - if (!viewing_only) ImGui::EndDisabled(); - // 2. Full name - ImGui::SetNextItemWidth(widthAvailable*0.5f); - if (with_autocomplete) { - 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 = TextInputWithAutocomplete(localize("contact.form.fullname"), localize("contact.form.fullname"), - buffer->name, IM_ARRAYSIZE(buffer->name), (char**)autocomplete_strings, autocomplete_count); - - if (on_autocomplete) { - *on_autocomplete = autocomplete_index != -1; - } + if (with_autocomplete) ImGui::FormContactAutocomplete(buffer, last_err & A_ERR_MISSING_NAME); + else ImGui::FormInputTextWithErrorHint(localize("contact.form.fullname"), buffer, buffer->name, IM_ARRAYSIZE(buffer->name), last_err & A_ERR_MISSING_NAME); - if (autocomplete_index != -1) - { - memcpy(buffer, &autocomplete_list[autocomplete_index], sizeof(contact)); - } + ui_draw_address_form(&buffer->address, last_err); - for (int i = 0; i < autocomplete_count; i++) - { - free(autocomplete_strings[i]); - } - } - else ImGui::InputTextWithHint(localize("contact.form.fullname"), localize("contact.form.fullname"), buffer->name, IM_ARRAYSIZE(buffer->name)); - ImGui::SameLine();ui_helper_draw_required_tag(); - - // 3. Address line 1, 4. address line 2, 5. country - ui_draw_address_form(&buffer->address); - - // 6. Contact type dropdown. - 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>(buffer->type); - if (ImGui::Combo(localize("contact.form.type"), ¤tItem, customer_types, IM_ARRAYSIZE(customer_types))) - { - buffer->type = static_cast<contact_type>(currentItem); - } + ImGui::FormContactTypeCombo(&buffer->type); // Fields only required for businesses. if (buffer->type == contact_type::CONTACT_BUSINESS) { - // 7. Tax number - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("contact.form.taxnumber"), localize("contact.form.taxnumber"), buffer->taxid, IM_ARRAYSIZE(buffer->taxid)); - - // 8. Business number / Chamber of commerce - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("contact.form.businessnumber"), localize("contact.form.businessnumber"), buffer->businessid, IM_ARRAYSIZE(buffer->businessid)); + ImGui::FormInputTextWithErrorHint(localize("contact.form.taxnumber"), buffer, buffer->taxid, IM_ARRAYSIZE(buffer->taxid), last_err & A_ERR_MISSING_TAXID); + ImGui::FormInputTextWithErrorHint(localize("contact.form.businessnumber"), buffer, buffer->businessid, IM_ARRAYSIZE(buffer->businessid), last_err & A_ERR_MISSING_BUSINESSID); } - // 9. Email - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("contact.form.email"), localize("contact.form.email"), buffer->email, IM_ARRAYSIZE(buffer->email)); - - // 10. Phone number - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("contact.form.phonenumber"), localize("contact.form.phonenumber"), buffer->phone_number, IM_ARRAYSIZE(buffer->phone_number)); - - // 11. Bank account. - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("contact.form.bankaccount"), localize("contact.form.bankaccount"), buffer->bank_account, IM_ARRAYSIZE(buffer->bank_account)); + ImGui::FormInputTextWithErrorHint(localize("contact.form.email"), buffer, buffer->email, IM_ARRAYSIZE(buffer->email), last_err & A_ERR_MISSING_EMAIL); + ImGui::FormInputTextWithErrorHint(localize("contact.form.phonenumber"), buffer, buffer->phone_number, IM_ARRAYSIZE(buffer->phone_number), 0); + ImGui::FormInputTextWithErrorHint(localize("contact.form.bankaccount"), buffer, buffer->bank_account, IM_ARRAYSIZE(buffer->bank_account), 0); if (viewing_only) ImGui::EndDisabled(); ImGui::PopID(); @@ -198,7 +83,7 @@ void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_ void draw_contact_form(contact* buffer, bool viewing_only = false) { - draw_contact_form_ex(buffer, viewing_only, false, 0); + draw_contact_form_ex(buffer, viewing_only, false); } @@ -313,7 +198,8 @@ static void ui_draw_contacts_create() draw_contact_form(&active_contact); - bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS; + a_err contact_validation_err = administration_contact_is_valid(active_contact); + bool can_save = contact_validation_err == A_ERR_SUCCESS; if (!can_save) ImGui::BeginDisabled(); // Save button ImGui::Spacing(); @@ -332,7 +218,8 @@ static void ui_draw_contacts_update() draw_contact_form(&active_contact); - bool can_save = administration_contact_is_valid(active_contact) == A_ERR_SUCCESS; + a_err contact_validation_err = administration_contact_is_valid(active_contact); + bool can_save = contact_validation_err == A_ERR_SUCCESS; if (!can_save) ImGui::BeginDisabled(); // Save button ImGui::Spacing(); diff --git a/src/ui/ui_expenses.cpp b/src/ui/ui_expenses.cpp index b195442..7d94eaf 100644 --- a/src/ui/ui_expenses.cpp +++ b/src/ui/ui_expenses.cpp @@ -32,94 +32,13 @@ static view_state current_view_state = view_state::LIST; static invoice active_invoice = {0}; static invoice selected_for_removal = {0}; -static cost_center* cost_center_list_buffer = 0; -static tax_rate* tax_rate_list_buffer = 0; -static project* project_list_buffer = 0; static billing_item* invoice_items_buffer = 0; -void ui_draw_address_form(address* buffer); -void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0); -void draw_tax_rate_selector(char* tax_rate_id, tax_rate* buffer, char* orig_country, char* dest_country); -bool draw_currency_selector(char* currency); -void draw_invoice_items_form(invoice* invoice, bool is_outgoing); - -void draw_costcenter_selector(char* costcenter_id, cost_center* buffer) -{ - cost_center* selected_costcenter = NULL; - u32 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); - } -} - -void draw_project_selector(char* project_id, project* buffer) -{ - project* selected_project = NULL; - u32 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); - } -} +void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false); +void draw_invoice_items_form(invoice* invoice); void ui_destroy_expenses() { - free(cost_center_list_buffer); - free(tax_rate_list_buffer); - free(project_list_buffer); free(invoice_items_buffer); } @@ -128,15 +47,6 @@ void ui_setup_expenses() current_view_state = view_state::LIST; active_invoice = administration_invoice_create_empty(); - u32 costcenter_count = administration_cost_center_count(); - cost_center_list_buffer = (cost_center*) malloc(sizeof(cost_center) * costcenter_count); - - u32 tax_rate_count = administration_tax_rate_count(); - tax_rate_list_buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count); - - u32 project_count = administration_project_count(); - project_list_buffer = (project*) malloc(sizeof(project) * project_count); - u32 invoice_items_count = MAX_BILLING_ITEMS; invoice_items_buffer = (billing_item*)malloc(sizeof(billing_item) * invoice_items_count); } @@ -145,17 +55,9 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false) { if (viewing_only) ImGui::BeginDisabled(); - // 1. Identifier - //ImGui::SetNextItemWidth(widthAvailable*0.2f); - //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id)); - - // 2. Sequential number ImGui::Text("%s: %s", localize("invoice.form.invoicenumber"), buffer->sequential_number); - - // 3. Billed to (you) ImGui::Text("%s: %s", localize("invoice.form.billedTo"), buffer->customer.name); - // 4. Invoice issued at tm issued_at_date = *gmtime(&buffer->issued_at); if (ImGui::DatePicker("##issuedAt", issued_at_date)) { @@ -164,7 +66,6 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false) ImGui::SameLine(); ImGui::Text(localize("invoice.form.issuedat")); - // 5. Invoice expires at tm expires_at_date = *gmtime(&buffer->expires_at); if (ImGui::DatePicker("##expiresAt", expires_at_date)) { @@ -173,7 +74,6 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false) ImGui::SameLine(); ImGui::Text(localize("invoice.form.expiresat")); - // 6. Product/service delivered at tm delivered_at_date = *gmtime(&buffer->delivered_at); if (ImGui::DatePicker("##deliveredAt", delivered_at_date)) { @@ -184,31 +84,26 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false) ImGui::Separator(); - // 7. Supplier information ImGui::Text(localize("invoice.form.supplier")); - draw_contact_form_ex(&buffer->supplier, false, true, 0); + draw_contact_form_ex(&buffer->supplier, false, true); - // 8. (optional) shipping address. ImGui::Checkbox(localize("invoice.form.triangulation"), &buffer->is_triangulation); if (buffer->is_triangulation) { ImGui::Spacing(); ImGui::Text(localize("invoice.form.shippinginformation")); - draw_contact_form_ex(&buffer->addressee, 0,0,0); + draw_contact_form_ex(&buffer->addressee, 0,0); } ImGui::Separator(); - // 9. Project selection - draw_project_selector(buffer->project_id, project_list_buffer); + ImGui::FormProjectCombo(buffer->project_id); + ImGui::FormCostCenterCombo(buffer->cost_center_id); - // 10. Cost center selection - draw_costcenter_selector(buffer->cost_center_id, cost_center_list_buffer); ImGui::Separator(); ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); - // 11. New billing item button. bool max_items_reached = administration_billing_item_count(buffer) >= MAX_BILLING_ITEMS; if (max_items_reached) ImGui::BeginDisabled(); if (ImGui::Button(localize(localize("invoice.form.add")))) @@ -218,17 +113,15 @@ static void draw_expense_form(invoice* buffer, bool viewing_only = false) } if (max_items_reached) ImGui::EndDisabled(); - // 12. Dropdown for invoice currency. ImGui::SameLine(); ImGui::Text("| %s: ", localize("invoice.form.currency")); ImGui::SameLine(); - if (draw_currency_selector(buffer->currency)) + if (ImGui::FormCurrencyCombo(buffer->currency)) { administration_invoice_set_currency(buffer, buffer->currency); } - // 13. Invoice items form - draw_invoice_items_form(buffer, false); + draw_invoice_items_form(buffer); if (viewing_only) ImGui::EndDisabled(); } diff --git a/src/ui/ui_invoices.cpp b/src/ui/ui_invoices.cpp index c3c6b8c..f061e3f 100644 --- a/src/ui/ui_invoices.cpp +++ b/src/ui/ui_invoices.cpp @@ -33,19 +33,13 @@ static view_state current_view_state = view_state::LIST; static invoice active_invoice = {0}; static invoice selected_for_removal = {0}; -static tax_rate* tax_rate_list_buffer = 0; static billing_item* invoice_items_buffer = 0; -static project* project_list_buffer = 0; -void ui_draw_address_form(address* buffer); -void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false, bool* on_autocomplete = 0); -void draw_project_selector(char* project_id, project* buffer); +void draw_contact_form_ex(contact* buffer, bool viewing_only = false, bool with_autocomplete = false); void ui_destroy_invoices() { - free(tax_rate_list_buffer); free(invoice_items_buffer); - free(project_list_buffer); } void ui_setup_invoices() @@ -53,129 +47,11 @@ void ui_setup_invoices() current_view_state = view_state::LIST; active_invoice = administration_invoice_create_empty(); - u32 tax_rate_count = administration_tax_rate_count(); - tax_rate_list_buffer = (tax_rate*) malloc(sizeof(tax_rate) * tax_rate_count); - u32 invoice_items_count = MAX_BILLING_ITEMS; invoice_items_buffer = (billing_item*)malloc(sizeof(billing_item) * invoice_items_count); - - u32 project_count = administration_project_count(); - project_list_buffer = (project*) malloc(sizeof(project) * project_count); -} - -void draw_tax_rate_selector(char* tax_rate_id, tax_rate* buffer, char* orig_country, char* dest_country) -{ - tax_rate* selected_tax_rate = NULL; - char* country_codes[2] = {orig_country, dest_country}; - u32 tax_rate_count = administration_tax_rate_get_by_country(buffer, 2, 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); - } } -bool draw_currency_selector(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 draw_invoice_items_form(invoice* invoice, bool is_outgoing) +void draw_invoice_items_form(invoice* invoice) { billing_item* buffer = invoice_items_buffer; u32 invoice_items = administration_billing_item_get_all_for_invoice(invoice, buffer); @@ -212,22 +88,7 @@ void draw_invoice_items_form(invoice* invoice, bool is_outgoing) ImGui::InputFloat("##amount", &item.amount, 0.0f, 0.0f, "%.0f"); ImGui::SameLine(); - // Toggle between X and % - { - const char* items[] = { "X", "%" }; - if (ImGui::BeginCombo("Mode", items[item.amount_is_percentage])) { - for (int n = 0; n < 2; n++) { - bool is_selected = (n == (int)item.amount_is_percentage); - if (ImGui::Selectable(items[n], is_selected)) { - item.amount_is_percentage = n; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - } + ImGui::FormToggleCombo(&item.amount_is_percentage, "X", "%"); ImGui::TableSetColumnIndex(2); ImGui::PushItemWidth(-1); @@ -243,31 +104,15 @@ void draw_invoice_items_form(invoice* invoice, bool is_outgoing) ImGui::InputFloat("##discount", &item.discount, 0.0f, 0.0f, "%.2f"); ImGui::SameLine(); - // Toggle between currency and % - { - const char* items[] = { item.currency, "%" }; - if (ImGui::BeginCombo("Mode##discountMode", items[item.discount_is_percentage])) { - for (int n = 0; n < 2; n++) { - bool is_selected = (n == (int)item.discount_is_percentage); - if (ImGui::Selectable(items[n], is_selected)) { - item.discount_is_percentage = n; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - } + ImGui::FormToggleCombo(&item.amount_is_percentage, item.currency, "%"); ImGui::TableSetColumnIndex(5); ImGui::Text("%.2f %s", item.net, item.currency); ImGui::TableSetColumnIndex(6); ImGui::PushItemWidth(-1); - draw_tax_rate_selector(item.tax_rate_id, tax_rate_list_buffer, - administration_company_info_get().address.country_code, - is_outgoing ? invoice->customer.address.country_code : invoice->supplier.address.country_code); + ImGui::FormTaxRateCombo(item.tax_rate_id, invoice->customer.address.country_code, invoice->supplier.address.country_code); + ImGui::PopItemWidth(); ImGui::TableSetColumnIndex(7); @@ -329,17 +174,9 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false) { ImGui::BeginDisabled(); - // 1. Identifier - //ImGui::SetNextItemWidth(widthAvailable*0.2f); - //ImGui::InputText(localize("contact.form.identifier"), buffer->id, IM_ARRAYSIZE(buffer->id)); - - // 2. Sequential number ImGui::Text("%s: %s", localize("invoice.form.invoicenumber"), buffer->sequential_number); - - // 3. Supplier (you) ImGui::Text("%s: %s", localize("invoice.form.supplier"), buffer->supplier.name); - // 4. Invoice issued at tm issued_at_date = *gmtime(&buffer->issued_at); if (ImGui::DatePicker("##issuedAt", issued_at_date)) { @@ -348,7 +185,6 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false) ImGui::SameLine(); ImGui::Text(localize("invoice.form.issuedat")); - // 5. Invoice expires at tm expires_at_date = *gmtime(&buffer->expires_at); if (ImGui::DatePicker("##expiresAt", expires_at_date)) { @@ -358,7 +194,6 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false) ImGui::Text(localize("invoice.form.expiresat")); if (!viewing_only) ImGui::EndDisabled(); - // 6. Product/service delivered at tm delivered_at_date = *gmtime(&buffer->delivered_at); if (ImGui::DatePicker("##deliveredAt", delivered_at_date)) { @@ -369,27 +204,23 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false) ImGui::Separator(); - // 7. Customer information ImGui::Text(localize("invoice.form.billinginformation")); - draw_contact_form_ex(&buffer->customer, false, true, 0); + draw_contact_form_ex(&buffer->customer, false, true); - // 8. (optional) shipping address. ImGui::Checkbox(localize("invoice.form.triangulation"), &buffer->is_triangulation); if (buffer->is_triangulation) { ImGui::Spacing(); ImGui::Text(localize("invoice.form.shippinginformation")); - draw_contact_form_ex(&buffer->addressee, 0,0,0); + draw_contact_form_ex(&buffer->addressee, 0,0); } ImGui::Separator(); - // 9. Project selection - draw_project_selector(buffer->project_id, project_list_buffer); + ImGui::FormProjectCombo(buffer->project_id); ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); - // 11. New billing item button. bool max_items_reached = administration_billing_item_count(buffer) >= MAX_BILLING_ITEMS; if (max_items_reached) ImGui::BeginDisabled(); if (ImGui::Button(localize(localize("invoice.form.add")))) @@ -399,17 +230,15 @@ static void draw_invoice_form(invoice* buffer, bool viewing_only = false) } if (max_items_reached) ImGui::EndDisabled(); - // 12. Dropdown for invoice currency. ImGui::SameLine(); ImGui::Text("| %s: ", localize("invoice.form.currency")); ImGui::SameLine(); - if (draw_currency_selector(buffer->currency)) + if (ImGui::FormCurrencyCombo(buffer->currency)) { administration_invoice_set_currency(buffer, buffer->currency); } - // 13. Invoice items form - draw_invoice_items_form(buffer, true); + draw_invoice_items_form(buffer); if (viewing_only) ImGui::EndDisabled(); } diff --git a/src/ui/ui_projects.cpp b/src/ui/ui_projects.cpp index dddf881..37cf5d8 100644 --- a/src/ui/ui_projects.cpp +++ b/src/ui/ui_projects.cpp @@ -35,8 +35,10 @@ void ui_setup_projects() static void draw_project_form() { + float widthAvailable = ImGui::GetContentRegionAvail().x; + bool viewing_only = (current_view_state == view_state::VIEW); static const char* selected_country = NULL; - + if (ImGui::Button(localize("form.back"))) { current_view_state = view_state::LIST; active_project = administration_project_create_empty(); @@ -44,20 +46,15 @@ static void draw_project_form() return; } ImGui::Spacing(); - - bool viewing_only = (current_view_state == view_state::VIEW); - ImGui::BeginDisabled(); - - float widthAvailable = ImGui::GetContentRegionAvail().x; + + a_err last_err = administration_project_is_valid(active_project); ImGui::SetNextItemWidth(widthAvailable*0.2f); ImGui::InputText(localize("project.form.identifier"), active_project.id, IM_ARRAYSIZE(active_project.id)); if (!viewing_only) ImGui::EndDisabled(); - - ImGui::SetNextItemWidth(widthAvailable*0.5f); - ImGui::InputTextWithHint(localize("project.form.description"), localize("project.form.description"), active_project.description, IM_ARRAYSIZE(active_project.description)); - ImGui::SameLine();ui_helper_draw_required_tag(); + + ImGui::FormInputTextWithErrorHint(localize("project.form.description"), &active_project, active_project.description, IM_ARRAYSIZE(active_project.description), last_err & A_ERR_MISSING_DESCRIPTION); if (viewing_only) ImGui::EndDisabled(); |
