code:
#include "Core/DatabaseEditor/DBEnemiesTab.hpp"
#include "Core/Application.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Database/Enemies.hpp"
#include "imgui.h"
#include "imgui_internal.h"

DBEnemiesTab::DBEnemiesTab(Enemies& Enemies, DatabaseEditor* parent)
: IDBEditorTab(parent)
, m_enemies(Enemies) {
  m_selectedEnemy = m_enemies.enemy(1);
  if (m_selectedEnemy) {
    m_traitsEditor.setTraits(&m_selectedEnemy->traits());
    m_actionsEditor.setActions(&m_selectedEnemy->actions());
  }
}

void DBEnemiesTab::draw() {
  if (!m_characterButtonTexture) {
    m_characterButtonTexture.emplace();
    m_characterButtonTexture->setSize(340, 200);
    if (!m_characterSheet) {
      m_characterSheet.emplace(m_selectedEnemy->battlerName());
    }
    const auto& [uv0, uv1] = m_characterSheet->getRectForCharacter(m_selectedEnemy->battlerHue());
    const Point offset{static_cast<int>(uv0.x() * m_characterSheet.value().texture().width()), static_cast<int>(uv0.y() * m_characterSheet.value().texture().height())};
    m_characterButtonTexture->setTexturesToComposite({{m_characterSheet.value().texture(), {m_characterSheet.value().characterWidth(), m_characterSheet.value().characterHeight()}, offset}});
  }

  if (m_isDropConfirmed) {
    m_selectedEnemy->dropItems().at(m_dropIndex).setDataId(m_item.at(m_dropIndex));
    m_selectedEnemy->dropItems().at(m_dropIndex).setKind(m_dropSelection);
    m_selectedEnemy->dropItems().at(m_dropIndex).setDenominator(m_dropDenominator);
    m_isOpen = false;
    m_isDropConfirmed = false;
  }
  ImGui::BeginChild("#orpg_enemies_editor");
  {
    const auto calc = ImGui::CalcTextSize("ABCDEFGHIJKLMNOPQRSTUV");
    ImGui::BeginChild("##orpg_enemies_editor_enemies", ImVec2{calc.x + (ImGui::GetStyle().ItemSpacing.x * 2), 0}, 0, ImGuiWindowFlags_NoScrollbar);
    {
      ImGui::BeginGroup();
      {
        ImGui::SeparatorText("Enemies");
        ImGui::BeginChild("##orpg_enemies_editor_enemies_list", ImVec2{0, ImGui::GetContentRegionMax().y - ((calc.y + ImGui::GetStyle().ItemSpacing.y) * 4)});
        {
          ImGui::BeginGroup();
          {
            for (auto& enemy : m_enemies.enemies()) {
              if (enemy.id() == 0) {
                continue;
              }
              if (ImGui::Selectable(Database::instance()->enemyNameAndId(enemy.id()).c_str(), &enemy == m_selectedEnemy) || (ImGui::IsItemFocused() && m_selectedEnemy != &enemy)) {
                m_selectedEnemy = &enemy;
                m_traitsEditor.setTraits(&m_selectedEnemy->traits());
                m_actionsEditor.setActions(&m_selectedEnemy->actions());
              }
            }
          }
          ImGui::EndGroup();
        }
        ImGui::EndChild();
        char str[4096];
        snprintf(str, 4096, "Max Enemies %i", m_enemies.count());
        ImGui::SeparatorText(str);
        if (ImGui::Button("Change Max", ImVec2{ImGui::GetContentRegionMax().x - 8, 0})) {
          m_changeIntDialogOpen = true;
          m_editMaxEnemies = m_enemies.count();
        }
      }
      ImGui::EndGroup();
    }
    ImGui::EndChild();
    ImGui::SameLine();
    ImGui::BeginChild("##orpg_enemies_editor_enemies_enemy_properties");
    {
      if (m_selectedEnemy) {
        ImGui::BeginChild("##orpg_enemies_enemy_panel_left", ImVec2{ImGui::GetContentRegionMax().x / 2, 0.f});
        {
          ImGui::BeginGroup();
          {

            ImGui::SeparatorText("General Settings");
            // Name
            ImGui::BeginGroup();
            {
              char name[4096];
              strncpy(name, m_selectedEnemy->name().c_str(), 4096);
              if (ImGui::LabelOverLineEdit(std::format("##orpg_enemies_editor_name{}", m_selectedEnemy->id()).c_str(), "Name:", name, 4096, ImGui::GetContentRegionMax().x / 2)) {
                m_selectedEnemy->setName(name);
              }
            }
            ImGui::EndGroup();
            // Image
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::TextUnformatted("Image:");
              if (ImGui::ImageButtonEx(ImGui::GetID("##orpg_database_enemies_image"), m_characterButtonTexture->get(), ImVec2(m_characterButtonTexture->size()), {0.f, 0.f}, {1.f, 1.f}, {},
                                       {1.f, 1.f, 1.f, 1.f}, ImGuiButtonFlags_PressedOnDoubleClick)) {
                m_currentSheet = &m_characterSheet.value();
                // m_characterPicker->setCharacterInfo("", m_selectedItem->iconIndex);
                // m_characterPicker->setOpen(true);
              }
            }
            ImGui::EndGroup();
            ImGui::BeginGroup();
            {
              ImGui::Text("Max HP:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(0);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_maxhp{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(0, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Max MP:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(1);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_maxmp{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(1, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::BeginGroup();
            {
              ImGui::Text("Attack:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(2);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_attack{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(2, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Defense:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(3);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_defense{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(3, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::BeginGroup();
            {
              ImGui::Text("M.Attack:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(4);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_mattack{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(4, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("M.Defense:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(5);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_mdefense{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(5, std::clamp(param, -999, 999));
              }
            }
            ImGui::EndGroup();
            ImGui::BeginGroup();
            {
              ImGui::Text("Agility:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(6);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_agility{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(6, std::clamp(param, -9999, 9999));
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Luck:");
              ImGui::SetNextItemWidth(170);
              int param = m_selectedEnemy->param(7);
              if (ImGui::InputInt(std::format("##orpg_database_enemy_luck{}", m_selectedEnemy->id()).c_str(), &param, 1, 100)) {
                m_selectedEnemy->setParam(7, std::clamp(param, -9999, 9999));
              }
            }
            ImGui::EndGroup();
            ImGui::SeparatorText("Rewards");
            ImGui::BeginGroup();
            {
              ImGui::Text("EXP:");
              ImGui::SetNextItemWidth(170);
              int exp = m_selectedEnemy->exp();
              if (ImGui::InputInt(std::format("##orpg_database_enemy_exp{}", m_selectedEnemy->id()).c_str(), &exp, 1, 100)) {
                m_selectedEnemy->setExp(std::clamp(exp, 0, 9999999));
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Gold:");
              ImGui::SetNextItemWidth(170);
              int gold = m_selectedEnemy->gold();
              if (ImGui::InputInt(std::format("##orpg_database_enemy_gold{}", m_selectedEnemy->id()).c_str(), &gold, 1, 100)) {
                m_selectedEnemy->setGold(std::clamp(gold, 0, 9999999));
              }
            }
            ImGui::EndGroup();
            ImGui::Text("Drop Items");
            // Drop Item 1
            ImGui::BeginGroup();
            {
              ImGui::SetNextItemWidth(160);
              ImGui::PushID(std::format("##orpg_database_enemy_drops_1{}", m_selectedEnemy->id()).c_str());
              if (ImGui::Button(m_selectedEnemy->dropItems().at(0).kind() > 0 ? getDropString(0, m_selectedEnemy->dropItems().at(0).kind()).c_str() : "", ImVec2{ImGui::GetContentRegionMax().x, 0})) {
                m_dropIndex = 0;
                m_isOpen = true;
              }
              ImGui::PopID();
            }
            ImGui::EndGroup();
            // Drop Item 2
            ImGui::BeginGroup();
            {
              ImGui::SetNextItemWidth(160);
              ImGui::PushID(std::format("##orpg_database_enemy_drops_2{}", m_selectedEnemy->id()).c_str());
              if (ImGui::Button(m_selectedEnemy->dropItems().at(1).kind() > 0 ? getDropString(1, m_selectedEnemy->dropItems().at(1).kind()).c_str() : "", ImVec2{ImGui::GetContentRegionMax().x, 0})) {
                m_dropIndex = 1;
                m_isOpen = true;
              }
              ImGui::PopID();
            }
            ImGui::EndGroup();
            // Drop Item 3
            ImGui::BeginGroup();
            {
              ImGui::SetNextItemWidth(160);
              ImGui::PushID(std::format("##orpg_database_enemy_drops_3{}", m_selectedEnemy->id()).c_str());
              if (ImGui::Button(m_selectedEnemy->dropItems().at(2).kind() > 0 ? getDropString(2, m_selectedEnemy->dropItems().at(2).kind()).c_str() : "", ImVec2{ImGui::GetContentRegionMax().x, 0})) {
                m_dropIndex = 2;
                m_isOpen = true;
              }
              ImGui::PopID();
            }
            ImGui::EndGroup();
            // Action Patterns
          }
          ImGui::EndGroup();
        }
        ImGui::EndChild();
        ImGui::SameLine();
        ImGui::BeginChild("##orpg_enemies_enemy_panel_right");
        {
          m_actionsEditor.draw(m_parent);
          m_traitsEditor.draw(m_parent);
        }
        ImGui::EndChild();
        ImGui::BeginChild("##orpg_weapons_weapon_panel_bottomright");
        {
          ImGui::SeparatorText("Note:");
          char note[8192];
          strncpy(note, m_selectedEnemy->note().c_str(), IM_ARRAYSIZE(note));
          if (ImGui::InputTextMultiline(std::format("##orpg_database_enemies_note{}", m_selectedEnemy->id()).c_str(), note, IM_ARRAYSIZE(note), ImVec2{ImGui::GetContentRegionMax().x - 16, 400})) {
            m_selectedEnemy->setNote(note);
          }
        }
        ImGui::EndChild();
      }

      ImGui::EndChild();
    }
    ImGui::EndChild();

    if (m_changeIntDialogOpen) {
      if (ImGui::Begin("Change Max Enemies", &m_changeIntDialogOpen,
                       ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_Modal | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
        ImGui::Text(
            "Specify an amount to resize the enemy list to\n"
            "This cannot be undone!");
        ImGui::InputInt("##value_input", &m_editMaxEnemies);
        ImGui::SameLine();
        if (ImGui::Button("Accept")) {
          m_changeConfirmDialogOpen = true;
        }
        ImGui::SameLine();
        if (ImGui::Button("Cancel")) {
          m_changeIntDialogOpen = false;
        }
      }
      ImGui::End();

      if (m_changeConfirmDialogOpen) {
        if (ImGui::Begin("Confirm Change", &m_changeConfirmDialogOpen,
                         ImGuiWindowFlags_NoResize | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
          ImGui::Text("Are you sure?");
          if (ImGui::Button("Yes")) {
            const int tmpId = m_selectedEnemy->id();
            m_enemies.resize(m_editMaxEnemies);
            m_selectedEnemy = m_enemies.enemy(tmpId);
            m_changeIntDialogOpen = false;
            m_changeConfirmDialogOpen = false;
          }
          ImGui::SameLine();
          if (ImGui::Button("Cancel")) {
            m_changeIntDialogOpen = false;
            m_changeConfirmDialogOpen = false;
          }
        }
        ImGui::End();
      }
    }
    drawPopup();
  }
}
void DBEnemiesTab::drawPopup() {
  if (m_isOpen) {
    ImGui::OpenPopup("Drop Item Selection");
  }

  if (ImGui::BeginPopupModal("Drop Item Selection", nullptr, ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize)) {
    if (item_picker) {
      if (const auto [closed, confirmed] = item_picker->draw(); closed) {
        if (confirmed) {
          m_item.at(0) = item_picker->selection();
        }
        item_picker.reset();
      }
    }
    if (weapon_picker) {
      if (const auto [closed, confirmed] = weapon_picker->draw(); closed) {
        if (confirmed) {
          m_item.at(1) = weapon_picker->selection();
        }
        weapon_picker.reset();
      }
    }

    if (armor_picker) {
      auto [closed, confirmed] = armor_picker->draw();
      if (closed) {
        if (confirmed) {
          m_item.at(2) = armor_picker->selection();
        }
        armor_picker.reset();
      }
    }
    ImGui::BeginChild("##orpg_dropitem_left_child", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoBackground);
    {

      if (ImGui::RadioButton("None", m_dropSelection == 0)) {
        m_dropSelection = 0;
      }
      if (ImGui::RadioButton("Item", m_dropSelection == 1)) {
        m_dropSelection = 1;
      }
      if (ImGui::RadioButton("Weapon", m_dropSelection == 2)) {
        m_dropSelection = 2;
      }
      if (ImGui::RadioButton("Armor", m_dropSelection == 3)) {
        m_dropSelection = 3;
      }
    }
    ImGui::EndChild();
    ImGui::SameLine();
    ImGui::BeginChild("##orpg_dropitem_right_child", ImVec2(0, 0), ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize, ImGuiWindowFlags_NoBackground);
    {
      // None
      ImGui::NewLine();
      ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.f);
      //  Item
      ImGui::BeginDisabled(m_dropSelection != 1);
      {
        ImGui::PushID("##orpg_dropitem_item");
        if (ImGui::Button(m_dropSelection != 1 ? "" : Database::instance()->itemNameOrId(m_item.at(0)).c_str(), ImVec2{200 - 15, 0})) {
          item_picker = ObjectPicker("Item"sv, Database::instance()->items.items(), m_item.at(0));
          item_picker->setOpen(true);
        }
        ImGui::PopID();
      }
      ImGui::EndDisabled();
      // Weapon
      ImGui::BeginDisabled(m_dropSelection != 2);
      {
        ImGui::PushID("##orpg_dropitem_weapon");
        if (ImGui::Button(m_dropSelection != 2 ? "" : Database::instance()->weaponNameOrId(m_item.at(1)).c_str(), ImVec2{200 - 15, 0})) {
          weapon_picker = ObjectPicker("Weapon"sv, Database::instance()->weapons.weaponList(), m_item.at(1));
          weapon_picker->setOpen(true);
        }
        ImGui::PopID();
      }
      ImGui::EndDisabled();
      // Armor
      ImGui::BeginDisabled(m_dropSelection != 3);
      {
        ImGui::PushID("##orpg_dropitem_armor");
        if (ImGui::Button(m_dropSelection != 3 ? "" : Database::instance()->armorNameOrId(m_item.at(2)).c_str(), ImVec2{200 - 15, 0})) {
          armor_picker = ObjectPicker("Armor"sv, Database::instance()->armors.armorList(), m_item.at(2));
          armor_picker->setOpen(true);
        }
        ImGui::PopID();
      }
      ImGui::EndDisabled();
      ImGui::BeginDisabled(m_dropSelection < 1);
      {
        ImGui::SeparatorText("Probability");
        if (ImGui::InputInt("##orpg_dropitem_denom", &m_dropDenominator)) {
          if (m_dropDenominator < 1) {
            m_dropDenominator = 1;
          }
          if (m_dropDenominator > 1000) {
            m_dropDenominator = 1000;
          }
        }
      }
      ImGui::EndDisabled();
    }
    ImGui::EndChild();
    ImGui::Separator();
    ImGui::BeginGroup();
    {
      if (ImGui::Button("OK")) {
        m_isOpen = false;
        m_isDropConfirmed = true;
        ImGui::CloseCurrentPopup();
      }
      ImGui::SameLine();
      if (ImGui::Button("Cancel")) {
        m_isOpen = false;
        ImGui::CloseCurrentPopup();
      }
      ImGui::EndGroup();
    }
    ImGui::EndPopup();
  }
}
std::string DBEnemiesTab::getDropString(const int dropIndex, const int kindId) const {
  switch (kindId) {
  case 1:
    return Database::instance()->itemNameOrId(m_selectedEnemy->dropItems().at(dropIndex).dataId());
  case 2:
    return Database::instance()->weaponNameOrId(m_selectedEnemy->dropItems().at(dropIndex).dataId());
  case 3:
    return Database::instance()->armorNameOrId(m_selectedEnemy->dropItems().at(dropIndex).dataId());
  default:
    break;
  }
  return "WAT";
}


code:
#pragma once
#include "Core/CommonUI/ActionsEditor.hpp"
#include "Core/CommonUI/TraitsEditor.hpp"
#include "Core/DatabaseEditor/IDBEditorTab.hpp"
#include "Core/Graphics/CharacterSheet.hpp"
#include "Core/Graphics/CheckeredCompositeTexture.hpp"
#include "DBCommonEventsTab.hpp"
#include "Database/Armors.hpp"
#include "Database/Enemies.hpp"
#include "Database/Items.hpp"
#include "Database/Weapons.hpp"

struct Enemies;
struct DBEnemiesTab : IDBEditorTab {
  DBEnemiesTab() = delete;
  explicit DBEnemiesTab(Enemies& enemies, DatabaseEditor* parent);
  void draw() override;

  [[nodiscard]] std::vector<Enemy>& enemies() { return m_enemies.enemies(); }
  [[nodiscard]] const std::vector<Enemy>& enemies() const { return m_enemies.enemies(); }

  [[nodiscard]] Enemy* enemy(int id) { return m_enemies.enemy(id); }
  [[nodiscard]] const Enemy* enemy(int id) const { return m_enemies.enemy(id); }
  std::vector<int>& getHeaders() override { return m_headers; }
  int getHeader(int index) override { return m_headers.at(index); }
  bool hasHeader() override { return !m_headers.empty(); }
  void setHeaderRange(int start, int end) override {
    m_categoryStart = start;
    m_categoryEnd = end;
  }
  std::string getName(int index) override { return m_enemies.enemy(index)->name(); }
  int getCount() override { return m_enemies.count(); }

private:
  int m_categoryStart;
  int m_categoryEnd;
  std::vector<int> m_headers;
  void drawPopup();
  std::string getDropString(int dropIndex, int kindId) const;
  Enemies& m_enemies;
  Enemy* m_selectedEnemy{};
  int m_editMaxEnemies{};
  float m_splitterWidth = 300.f;
  bool m_changeIntDialogOpen = false;
  bool m_changeConfirmDialogOpen = false;
  int m_dropSelection{0};
  bool m_isOpen{false};
  bool m_isDropConfirmed{false};
  CharacterSheet* m_currentSheet = nullptr;
  std::optional<CheckeredCompositeTexture> m_characterButtonTexture;
  std::optional<CharacterSheet> m_characterSheet;
  std::optional<CharacterSheet> m_characterPicker;
  TraitsEditor m_traitsEditor;
  ActionsEditor m_actionsEditor;
  std::optional<ObjectPicker<Item>> item_picker;
  std::optional<ObjectPicker<Armor>> armor_picker;
  std::optional<ObjectPicker<Weapon>> weapon_picker;
  std::vector<int> m_item{1, 1, 1};

  int m_dropIndex{0};
  int m_dropDenominator{1};
};

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "Core/DatabaseEditor/DBGameConstantsTab.hpp"
#include "Core/ImGuiExt/ImGuiNotify.hpp"

#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h"

#include "Core/DatabaseEditor.hpp"
#include "Core/MainWindow.hpp"

#include "Database/GameConstants.hpp"

template <>
inline int ObjectPicker<std::optional<CommonEvent>>::getId(const std::optional<CommonEvent>& value) {
  return value ? value->id() : 0;
}

static const std::string InvalidCommonEvent = "Invalid Common Event";
template <>
inline const std::string& ObjectPicker<std::optional<CommonEvent>>::getName(const std::optional<CommonEvent>& value) {
  return value ? value->name() : InvalidCommonEvent;
}

template <>
inline int ObjectPicker<std::optional<MapInfo>>::getId(const std::optional<MapInfo>& value) {
  return value ? value->id() : 0;
}

static const std::string InvalidMap = "Invalid Map";
template <>
inline const std::string& ObjectPicker<std::optional<MapInfo>>::getName(const std::optional<MapInfo>& value) {
  return value ? value->name() : InvalidMap;
}

void DBGameConstantsTab::drawAliasModal(const GameConstants::Type type) {
  if (m_selection != -1 && ImGui::BeginPopupModal("##alias_edit", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
    std::map<int, std::string>* map;
    std::string name;
    switch (type) {
    case GameConstants::Type::Switch:
      map = &m_constants->switches;
      name = Database::instance()->system.switche(m_selection);
      break;
    case GameConstants::Type::Variable:
      map = &m_constants->variables;
      name = Database::instance()->system.variable(m_selection);
      break;
    case GameConstants::Type::Actor:
      map = &m_constants->actors;
      name = Database::instance()->actors.actor(m_selection)->name();
      break;
    case GameConstants::Type::Class:
      map = &m_constants->classes;
      name = Database::instance()->classes.classType(m_selection)->name();
      break;
    case GameConstants::Type::Skill:
      map = &m_constants->skills;
      name = Database::instance()->skills.skill(m_selection)->name();
      break;
    case GameConstants::Type::Item:
      map = &m_constants->items;
      name = Database::instance()->items.item(m_selection)->name();
      break;
    case GameConstants::Type::Weapon:
      map = &m_constants->weapons;
      name = Database::instance()->weapons.weapon(m_selection)->name();
      break;
    case GameConstants::Type::Armor:
      map = &m_constants->armors;
      name = Database::instance()->armors.armor(m_selection)->name();
      break;
    case GameConstants::Type::Enemy:
      map = &m_constants->enemies;
      name = Database::instance()->enemies.enemy(m_selection)->name();
      break;
    case GameConstants::Type::Troop:
      map = &m_constants->troops;
      name = Database::instance()->troops.troop(m_selection)->name();
      break;
    case GameConstants::Type::State:
      map = &m_constants->states;
      name = Database::instance()->states.state(m_selection)->name();
      break;
    case GameConstants::Type::Animation:
      map = &m_constants->animations;
      name = Database::instance()->animations.animation(m_selection)->name();
      break;
    case GameConstants::Type::Tileset:
      map = &m_constants->tilesets;
      name = Database::instance()->tilesets.tileset(m_selection)->name();
      break;
    case GameConstants::Type::CommonEvent:
      map = &m_constants->commonEvents;
      name = Database::instance()->commonEvents.event(m_selection)->name();
      break;
    case GameConstants::Type::Map:
      map = &m_constants->maps;
      name = Database::instance()->mapInfos.map(m_selection)->name();
      break;
    default:
      return;
    }

    if (!m_currentAlias) {
      m_currentAlias = (*map)[m_selection];
    }

    ImGui::Text("Alias:");
    const bool isValid = m_constants->isValidName(type, m_selection, m_currentAlias.value());
    if (!isValid) {
      ImGui::SameLine();
      ImGui::Text(" Selected alias is invalid!");
      ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xFF0000FF);
    } else {
      ImGui::PushStyleColor(ImGuiCol_FrameBg, 0xFF007F00);
    }
    ImGui::Text("%s", name.c_str());
    ImGui::InputText("##alias_input", &m_currentAlias.value());
    ImGui::PopStyleColor();
    ImGui::BeginDisabled(!isValid);
    if (ImGui::Button("OK")) {
      (*map)[m_selection] = *m_currentAlias;
      m_currentAlias.reset();
      ImGui::CloseCurrentPopup();
    }
    ImGui::EndDisabled();
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      m_currentAlias.reset();
      ImGui::CloseCurrentPopup();
    }
    ImGui::EndPopup();
  }
}

void DBGameConstantsTab::setupTableHeaders() {
  ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed);
  ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
  ImGui::TableSetupColumn("Alias", ImGuiTableColumnFlags_WidthStretch);
  ImGui::TableSetupColumn("##delete", ImGuiTableColumnFlags_WidthFixed);
  ImGui::TableSetupScrollFreeze(4, 1);
  ImGui::TableHeadersRow();
}

bool DBGameConstantsTab::drawSelectable(const int id, const bool selected) {
  ImGui::TableNextRow();
  ImGui::TableNextColumn();
  if (ImGui::Selectable(std::format("{:04}", id).c_str(), selected, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
    if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
      m_openPopup = true;
    }
    return true;
  }
  if (ImGui::IsItemFocused()) {
    return true;
  }
  return false;
}

void DBGameConstantsTab::drawNameAndAliasColumns(const std::string& name, const std::string& alias) {
  ImGui::TableNextColumn();
  ImGui::Text("%s", name.c_str());
  ImGui::TableNextColumn();
  ImGui::Text("%s", alias.c_str());
}
bool DBGameConstantsTab::drawDeleteButton(int id) {
  ImGui::TableNextColumn();
  if (ImGui::Selectable((std::string(ICON_FA_DELETE_LEFT) + std::format("##delete_btn{}", id)).c_str(), false, ImGuiSelectableFlags_AllowOverlap)) {
    return true;
  }
  if (ImGui::IsItemHovered()) {
    ImGui::SetTooltip("Delete constant export");
  }
  return false;
}

void DBGameConstantsTab::draw() {
  ImGui::BeginChild("#orpg_constants_tab");
  ImGui::SeparatorText("Exported Constants");
  ImGui::Checkbox("Generate JS file", &m_constants->generateJS);
  ImGui::SameLine();
  if (ImGui::Button("Generate Now")) {
    if (m_constants->generateConstantsJS(m_parent->project()->database().basePath + "js/Constants.js")) {
      ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Success, "Generated js/Constants.js successfully!"});
    } else {
      ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Error, "Failed to serialize js/Constants.js!"});
    }
  }
  ImGui::SameLine();
  if (ImGui::Button("Save")) {
    if (m_constants->serialize(m_parent->project()->database().basePath + "data/Constants.json")) {
      ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Success, "Serialized data/Constants.json successfully!"});
    } else {
      ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Error, "Failed to serialize data/Constants.json!"});
    }
  }
  if (ImGui::BeginTabBar("##ore_constants_tabbar")) {
    if (ImGui::BeginTabItem("Variables")) {
      ImGui::Text("When exported to Constants.js all Variables will be prefixed with VAR_");
      if (ImGui::BeginTable("##ore_variable_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->variables.begin(); it != m_constants->variables.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->system.variable(id), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->variables.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_switchVariablePicker.emplace("Variables", Database::instance()->system.variables(), 1);
        m_switchVariablePicker->setOpen(true);
      }

      if (m_switchVariablePicker) {
        if (const auto [closed, confirmed] = m_switchVariablePicker->draw(); closed) {
          if (const int selection = m_switchVariablePicker->selection(); confirmed && !m_constants->variables.contains(selection)) {
            m_constants->variables[selection] = std::format("VARIABLE_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_switchVariablePicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }

      drawAliasModal(GameConstants::Type::Variable);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->variables.erase(m_selection);
      }

      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Switches")) {
      ImGui::Text("When exported to Constants.js all Switches will be prefixed with SW_");
      if (ImGui::BeginTable("##ore_switch_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->switches.begin(); it != m_constants->switches.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->system.switche(id), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->switches.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }
      if (ImGui::Button("Add")) {
        m_switchVariablePicker.emplace("Switches", Database::instance()->system.switches(), 1);
        m_switchVariablePicker->setOpen(true);
      }
      if (m_switchVariablePicker) {
        if (const auto [closed, confirmed] = m_switchVariablePicker->draw(); closed) {
          if (const int selection = m_switchVariablePicker->selection(); confirmed && !m_constants->switches.contains(selection)) {
            m_constants->switches[selection] = std::format("SWITCH_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_switchVariablePicker.reset();
        }
      }
      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Switch);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->switches.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Actors")) {
      ImGui::Text("When exported to Constants.js all Actors will be prefixed with ACT_");
      if (ImGui::BeginTable("##ore_actors_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->actors.begin(); it != m_constants->actors.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->actors.actor(id)->name(), alias);
          if (drawDeleteButton(id)) {
            m_constants->actors.erase(id);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }
      if (ImGui::Button("Add")) {
        m_actorsPicker.emplace("Actors", Database::instance()->actors.actorList(), m_selection);
        m_actorsPicker->setOpen(true);
      }
      if (m_actorsPicker) {
        if (const auto [closed, confirmed] = m_actorsPicker->draw(); closed) {
          if (const int selection = m_actorsPicker->selection(); confirmed && !m_constants->actors.contains(selection)) {
            m_constants->actors[selection] = std::format("ACTOR_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_actorsPicker.reset();
        }
      }
      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Actor);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->actors.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Classes")) {
      ImGui::Text("When exported to Constants.js all Classes will be prefixed with CLS_");
      if (ImGui::BeginTable("##ore_classes_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->classes.begin(); it != m_constants->classes.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->classes.classType(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->classes.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_classesPicker.emplace("Classes", Database::instance()->classes.classes(), m_selection);
        m_classesPicker->setOpen(true);
      }
      if (m_classesPicker) {
        if (const auto [closed, confirmed] = m_classesPicker->draw(); closed) {
          if (const int selection = m_classesPicker->selection(); confirmed && !m_constants->classes.contains(selection)) {
            m_constants->classes[selection] = std::format("CLASS_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_classesPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Class);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->classes.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Skills")) {
      ImGui::Text("When exported to Constants.js all Skills will be prefixed with SKL_");
      if (ImGui::BeginTable("##ore_skills_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->skills.begin(); it != m_constants->skills.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->skills.skill(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->skills.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_skillsPicker.emplace("Skills", Database::instance()->skills.skills(), m_selection);
        m_skillsPicker->setOpen(true);
      }
      if (m_skillsPicker) {
        if (const auto [closed, confirmed] = m_skillsPicker->draw(); closed) {
          if (const int selection = m_skillsPicker->selection(); confirmed && !m_constants->skills.contains(selection)) {
            m_constants->skills[selection] = std::format("SKILL_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_skillsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Skill);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->skills.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Items")) {
      ImGui::Text("When exported to Constants.js all Items will be prefixed with ITM_");
      if (ImGui::BeginTable("##ore_items_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->items.begin(); it != m_constants->items.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->items.item(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->items.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_itemsPicker.emplace("Items", Database::instance()->items.items(), m_selection);
        m_itemsPicker->setOpen(true);
      }
      if (m_itemsPicker) {
        if (const auto [closed, confirmed] = m_itemsPicker->draw(); closed) {
          if (const int selection = m_itemsPicker->selection(); confirmed && !m_constants->items.contains(selection)) {
            m_constants->items[selection] = std::format("ITEM_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_itemsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Item);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->items.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Weapons")) {
      ImGui::Text("When exported to Constants.js all Weapons will be prefixed with WPN_");
      if (ImGui::BeginTable("##ore_weapons_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->weapons.begin(); it != m_constants->weapons.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->weapons.weapon(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->weapons.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_weaponsPicker.emplace("Weapons", Database::instance()->weapons.weapons(), m_selection);
        m_weaponsPicker->setOpen(true);
      }
      if (m_weaponsPicker) {
        if (const auto [closed, confirmed] = m_weaponsPicker->draw(); closed) {
          if (const int selection = m_weaponsPicker->selection(); confirmed && !m_constants->weapons.contains(selection)) {
            m_constants->weapons[selection] = std::format("WEAPON_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_weaponsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Weapon);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->weapons.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Armors")) {
      ImGui::Text("When exported to Constants.js all Armors will be prefixed with ARM_");
      if (ImGui::BeginTable("##ore_armors_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->armors.begin(); it != m_constants->armors.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->armors.armor(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->armors.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_armorsPicker.emplace("Armors", Database::instance()->armors.armors(), m_selection);
        m_armorsPicker->setOpen(true);
      }
      if (m_armorsPicker) {
        if (const auto [closed, confirmed] = m_armorsPicker->draw(); closed) {
          if (const int selection = m_armorsPicker->selection(); confirmed && !m_constants->armors.contains(selection)) {
            m_constants->armors[selection] = std::format("ARMOR_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_armorsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Armor);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->armors.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Enemies")) {
      ImGui::Text("When exported to Constants.js all Enemies will be prefixed with ENM_");
      if (ImGui::BeginTable("##ore_enemies_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->enemies.begin(); it != m_constants->enemies.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->enemies.enemy(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->enemies.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_enemiesPicker.emplace("Enemies", Database::instance()->enemies.enemies(), m_selection);
        m_enemiesPicker->setOpen(true);
      }
      if (m_enemiesPicker) {
        if (const auto [closed, confirmed] = m_enemiesPicker->draw(); closed) {
          if (const int selection = m_enemiesPicker->selection(); confirmed && !m_constants->enemies.contains(selection)) {
            m_constants->enemies[selection] = std::format("ENEMY_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_enemiesPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Enemy);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->enemies.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Troops")) {
      ImGui::Text("When exported to Constants.js all Troops will be prefixed with TRP_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->troops.begin(); it != m_constants->troops.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->troops.troop(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->troops.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_troopsPicker.emplace("Troops", Database::instance()->troops.troops(), m_selection);
        m_troopsPicker->setOpen(true);
      }
      if (m_troopsPicker) {
        if (const auto [closed, confirmed] = m_troopsPicker->draw(); closed) {
          if (const int selection = m_troopsPicker->selection(); confirmed && !m_constants->troops.contains(selection)) {
            m_constants->troops[selection] = std::format("TROOP_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_troopsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Troop);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->troops.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("States")) {
      ImGui::Text("When exported to Constants.js all States will be prefixed with STA_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->states.begin(); it != m_constants->states.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->states.state(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->states.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_statesPicker.emplace("States", Database::instance()->states.states(), m_selection);
        m_statesPicker->setOpen(true);
      }
      if (m_statesPicker) {
        if (const auto [closed, confirmed] = m_statesPicker->draw(); closed) {
          if (const int selection = m_statesPicker->selection(); confirmed && !m_constants->states.contains(selection)) {
            m_constants->states[selection] = std::format("STATE_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_statesPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::State);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->states.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Animations")) {
      ImGui::Text("When exported to Constants.js all Animations will be prefixed with ANM_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->animations.begin(); it != m_constants->animations.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->animations.animation(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->animations.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_animationPicker.emplace("Animations", Database::instance()->animations.animations(), m_selection);
        m_animationPicker->setOpen(true);
      }
      if (m_animationPicker) {
        if (const auto [closed, confirmed] = m_animationPicker->draw(); closed) {
          if (const int selection = m_animationPicker->selection(); confirmed && !m_constants->animations.contains(selection)) {
            m_constants->animations[selection] = std::format("ANIMATION_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_animationPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Animation);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->animations.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Tilesets")) {
      ImGui::Text("When exported to Constants.js all Tilesets will be prefixed with TLS_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->tilesets.begin(); it != m_constants->tilesets.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->tilesets.tileset(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->tilesets.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_tilesetPicker.emplace("Tilesets", Database::instance()->tilesets.tilesets(), m_selection);
        m_tilesetPicker->setOpen(true);
      }
      if (m_tilesetPicker) {
        if (const auto [closed, confirmed] = m_tilesetPicker->draw(); closed) {
          if (const int selection = m_tilesetPicker->selection(); confirmed && !m_constants->tilesets.contains(selection)) {
            m_constants->tilesets[selection] = std::format("TILESET_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_tilesetPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Tileset);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->tilesets.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Common Events")) {
      ImGui::Text("When exported to Constants.js all Common Events will be prefixed with CMN_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->commonEvents.begin(); it != m_constants->commonEvents.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->commonEvents.event(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->commonEvents.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_commonEventPicker.emplace("Common Events", Database::instance()->commonEvents.events(), m_selection);
        m_commonEventPicker->setOpen(true);
      }
      if (m_commonEventPicker) {
        if (const auto [closed, confirmed] = m_commonEventPicker->draw(); closed) {
          if (const int selection = m_commonEventPicker->selection(); confirmed && !m_constants->commonEvents.contains(selection)) {
            m_constants->commonEvents[selection] = std::format("COMMON_EVENT_{:04}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_commonEventPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::CommonEvent);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->commonEvents.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Maps")) {
      ImGui::Text("When exported to Constants.js all Maps will be prefixed with MAP_");
      if (ImGui::BeginTable("##ore_troops_constants_table", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY,
                            ImVec2{0, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y})) {
        setupTableHeaders();
        for (auto it = m_constants->maps.begin(); it != m_constants->maps.end();) {
          auto id = it->first;
          auto alias = it->second;
          if (drawSelectable(id, m_selection == id)) {
            m_selection = id;
          }
          drawNameAndAliasColumns(Database::instance()->mapInfos.map(id)->name(), alias);
          if (drawDeleteButton(id)) {
            it = m_constants->maps.erase(it);
          } else {
            ++it;
          }
        }
        ImGui::EndTable();
      }

      if (ImGui::Button("Add")) {
        m_mapsPicker.emplace("Maps", Database::instance()->mapInfos.mapInfos(), m_selection);
        m_mapsPicker->setOpen(true);
      }
      if (m_mapsPicker) {
        if (const auto [closed, confirmed] = m_mapsPicker->draw(); closed) {
          if (const int selection = m_mapsPicker->selection(); confirmed && !m_constants->maps.contains(selection)) {
            m_constants->maps[selection] = std::format("MAP_{:03}", selection);
            m_selection = selection;
            m_openPopup = true;
          }
          m_mapsPicker.reset();
        }
      }

      if (m_openPopup) {
        ImGui::OpenPopup("##alias_edit");
        m_openPopup = false;
      }
      drawAliasModal(GameConstants::Type::Map);
      if (ImGui::IsKeyPressed(ImGuiKey_Delete) && m_selection != -1) {
        m_constants->maps.erase(m_selection);
      }
      ImGui::EndTabItem();
    }
    ImGui::EndTabBar();
  }
  ImGui::EndChild();
}


code:
#pragma once
#include "Core/CommonUI/ObjectPicker.hpp"
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Core/DatabaseEditor/IDBEditorTab.hpp"
#include "DBCommonEventsTab.hpp"
#include "Database/GameConstants.hpp"

#include <optional>

struct Actor;
struct Armor;
struct Class;
struct Skill;
struct Item;
struct Weapon;
struct Enemy;
struct Troop;
struct State;
struct Animation;
struct Tileset;
struct CommonEvent;
struct MapInfo;
struct GameConstants;
class DBGameConstantsTab final : public IDBEditorTab {
public:
  explicit DBGameConstantsTab(GameConstants& constants, DatabaseEditor* parent)
  : IDBEditorTab(parent)
  , m_constants(&constants) {}

  void draw() override;
  std::vector<int>& getHeaders() override { return m_headers; }
  int getHeader(int index) override { return m_headers.at(index); }
  bool hasHeader() override { return !m_headers.empty(); }
  void setHeaderRange(int start, int end) override {
    m_categoryStart = start;
    m_categoryEnd = end;
  }
  std::string getName(int index) override { return ""; }
  int getCount() override { return 0; }

private:
  int m_categoryStart;
  int m_categoryEnd;
  std::vector<int> m_headers;
  void drawAliasModal(GameConstants::Type type);
  static void setupTableHeaders();
  bool drawSelectable(int id, bool selected);
  static void drawNameAndAliasColumns(const std::string& name, const std::string& alias);
  static bool drawDeleteButton(int id);

  GameConstants* m_constants = nullptr;
  std::optional<VariableSwitchPicker> m_switchVariablePicker;
  std::optional<ObjectPicker<Actor>> m_actorsPicker;
  std::optional<ObjectPicker<Class>> m_classesPicker;
  std::optional<ObjectPicker<Skill>> m_skillsPicker;
  std::optional<ObjectPicker<Item>> m_itemsPicker;
  std::optional<ObjectPicker<Weapon>> m_weaponsPicker;
  std::optional<ObjectPicker<Armor>> m_armorsPicker;
  std::optional<ObjectPicker<Enemy>> m_enemiesPicker;
  std::optional<ObjectPicker<Troop>> m_troopsPicker;
  std::optional<ObjectPicker<State>> m_statesPicker;
  std::optional<ObjectPicker<Animation>> m_animationPicker;
  std::optional<ObjectPicker<Tileset>> m_tilesetPicker;
  std::optional<ObjectPicker<std::optional<CommonEvent>>> m_commonEventPicker;
  std::optional<ObjectPicker<std::optional<MapInfo>>> m_mapsPicker;

  int m_selection = -1;
  std::optional<std::string> m_currentAlias;
  bool m_openPopup = false;
};


code:
#include "Core/DatabaseEditor/DBItemsTab.hpp"
#include "Core/Application.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Database/Items.hpp"
#include "imgui.h"
#include "imgui_internal.h"

DBItemsTab::DBItemsTab(Items& items, DatabaseEditor* parent)
: IDBEditorTab(parent)
, m_items(items) {
  m_selectedItem = m_items.item(1);
  if (m_selectedItem) {
    m_effectsEditor.setEffects(&m_selectedItem->effects());
  }
}

void DBItemsTab::draw() {
  if (!m_itemButtonTexture) {
    m_itemButtonTexture.emplace();
    m_itemButtonTexture->setSize(48, 48);
    if (!m_itemSheet) {
      m_itemSheet.emplace(*m_parent->getIconSheet());
    }
  }

  if (m_selectedItem && !m_itemButtonTexture->hasCompositeTextures()) {
    const auto& [uv0, uv1] = m_itemSheet.value().rectForId(m_selectedItem->iconIndex());
    const Point offset{static_cast<int>(uv0.x() * m_itemSheet.value().texture().width()), static_cast<int>(uv0.y() * m_itemSheet.value().texture().height())};
    m_itemButtonTexture->setTexturesToComposite({{m_itemSheet.value().texture(), {m_itemSheet.value().iconWidth(), m_itemSheet.value().iconHeight()}, offset}});
  }

  if (m_animationPicker) {
    if (const auto [closed, confirmed] = m_animationPicker->draw(); closed) {
      if (confirmed) {
        m_selectedItem->setAnimationId(m_animationPicker->selection());
      }
      m_animationPicker.reset();
    }
  }

  ImGui::BeginChild("#orpg_items_editor");
  {
    const auto calc = ImGui::CalcTextSize("ABCDEFGHIJKLMNOPQRSTUV");
    ImGui::BeginChild("##orpg_items_editor_items", ImVec2{calc.x + (ImGui::GetStyle().ItemSpacing.x * 2), 0}, 0, ImGuiWindowFlags_NoScrollbar);
    {
      ImGui::BeginGroup();
      {
        ImGui::SeparatorText("Items");
        ImGui::BeginChild("##orpg_items_editor_items_list", ImVec2{0, ImGui::GetContentRegionMax().y - ((calc.y + ImGui::GetStyle().ItemSpacing.y) * 4)});
        {
          ImGui::BeginGroup();
          {
            for (auto& skill_ : m_items.items()) {
              if (skill_.id() == 0) {
                continue;
              }

              char name[4096];
              snprintf(name, 4096, "%04i %s", skill_.id(), skill_.name().c_str());
              if (ImGui::Selectable(name, &skill_ == m_selectedItem) || (ImGui::IsItemFocused() && m_selectedItem != &skill_)) {
                if (m_selectedItem != &skill_) {
                  m_selectedItem = &skill_;
                  m_effectsEditor.setEffects(&m_selectedItem->effects());
                  m_itemButtonTexture->clear();
                }
              }
            }
          }
          ImGui::EndGroup();
        }
        ImGui::EndChild();
        char str[4096];
        snprintf(str, 4096, "Max Items %i", m_items.count());
        ImGui::SeparatorText(str);
        if (ImGui::Button("Change Max", ImVec2{ImGui::GetContentRegionMax().x - 8, 0})) {
          m_changeIntDialogOpen = true;
          m_editMaxItems = m_items.count();
        }
      }
      ImGui::EndGroup();
    }
    ImGui::EndChild();
    ImGui::SameLine();
    ImGui::BeginChild("##orpg_items_editor_items_item_properties");
    {
      if (m_selectedItem) {
        ImGui::BeginChild("##orpg_items_item_panel_left", ImVec2{ImGui::GetContentRegionMax().x / 2, 0.f});
        {
          ImGui::BeginGroup();
          {
            ImGui::SeparatorText("General Settings");
            // Name
            ImGui::BeginGroup();
            {
              char name[4096];
              strncpy(name, m_selectedItem->name().c_str(), 4096);
              if (ImGui::LabelOverLineEdit("##orpg_items_editor_name", "Name:", name, 4096, ImGui::GetContentRegionMax().x / 2 - 16, nullptr, ImGuiInputTextFlags_None)) {
                m_selectedItem->setName(name);
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            // Icon
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::TextUnformatted("Icon:");
              if (ImGui::ImageButtonEx(ImGui::GetID("##orpg_database_items_image"), m_itemButtonTexture->get(), ImVec2(m_itemButtonTexture->size()), {0.f, 0.f}, {1.f, 1.f}, {}, {1.f, 1.f, 1.f, 1.f},
                                       ImGuiButtonFlags_PressedOnDoubleClick)) {
                m_currentSheet = &m_itemSheet.value();
                // m_characterPicker->setCharacterInfo("", m_selectedItem->iconIndex);
                // m_characterPicker->setOpen(true);
              }
              ImGui::SameLine();
              ImGui::SetCursorPos(ImVec2{ImGui::GetCursorPosX() + 10.f, ImGui::GetCursorPosY() - 6.f});
              ImGui::BeginGroup();
              {
                ImGui::Text("Consumable");
                bool consumable = m_selectedItem->consumable();
                if (ImGui::Checkbox("##orpg_database_items_consumable", &consumable)) {
                  m_selectedItem->setConsumable(consumable);
                }
              }
              ImGui::EndGroup();
            }
            ImGui::EndGroup();
            // Description
            ImGui::BeginGroup();
            {
              char description[4096];
              strncpy(description, m_selectedItem->description().c_str(), 4096);
              ImGui::Text("Description:");
              ImGui::InputTextMultiline("##orpg_database_items_description", description, 4096, ImVec2{360, 60});
            }
            ImGui::EndGroup();
            // Item Type, Price, Consumable
            ImGui::BeginGroup();
            {
              ImGui::Text("Item Type:");
              ImGui::SetNextItemWidth(170);
              if (ImGui::BeginCombo("##orpg_database_items_itype", DecodeEnumName(magic_enum::enum_name(static_cast<ItemType>(m_selectedItem->itypeId()))).c_str())) {
                for (auto v : magic_enum::enum_values<ItemType>()) {
                  if (ImGui::Selectable(DecodeEnumName(v).c_str(), static_cast<ItemType>(m_selectedItem->itypeId()) == v)) {
                    m_selectedItem->setItypeId(static_cast<int>(v));
                  }
                }
                ImGui::EndCombo();
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Price:");
              ImGui::SetNextItemWidth(170);
              int price = m_selectedItem->price();
              if (ImGui::InputInt("##orpg_database_items_price", &price, 1, 100)) {
                m_selectedItem->setPrice(std::clamp(price, 0, 999999));
              }
            }
            ImGui::EndGroup();
          }
          ImGui::EndGroup();
          // Scope, Occasion
          ImGui::BeginGroup();
          {
            ImGui::BeginGroup();
            {
              ImGui::Text("Scope:");
              ImGui::SetNextItemWidth(170);
              if (ImGui::BeginCombo("##orpg_database_items_scopelist", DecodeEnumName(magic_enum::enum_name(m_selectedItem->scope())).c_str())) {
                int index{0};
                for (auto& dir : magic_enum::enum_values<Scope>()) {
                  if (const bool is_selected = m_selectedItem->scope() == static_cast<Scope>(magic_enum::enum_index(dir).value());
                      ImGui::Selectable(DecodeEnumName(magic_enum::enum_name(dir)).c_str(), is_selected)) {
                    m_selectedItem->setScope(dir);
                    if (is_selected)
                      ImGui::SetItemDefaultFocus();
                  }
                  index++;
                }
                ImGui::EndCombo();
              }
            }
            ImGui::EndGroup();
            ImGui::SameLine();
            ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
            ImGui::BeginGroup();
            {
              ImGui::Text("Occasion:");
              ImGui::SetNextItemWidth(170);
              if (ImGui::BeginCombo("##orpg_database_items_occasionlist", DecodeEnumName(magic_enum::enum_name(m_selectedItem->occasion())).c_str())) {
                int index{0};
                for (auto& dir : magic_enum::enum_values<Occasion>()) {
                  if (const bool is_selected = m_selectedItem->occasion() == static_cast<Occasion>(magic_enum::enum_index(dir).value());
                      ImGui::Selectable(DecodeEnumName(magic_enum::enum_name(dir)).c_str(), is_selected)) {
                    m_selectedItem->setOccasion(dir);
                    if (is_selected)
                      ImGui::SetItemDefaultFocus();
                  }
                  index++;
                }
                ImGui::EndCombo();
              }
            }
            ImGui::EndGroup();
            ImGui::SeparatorText("Invocation");
            ImGui::BeginGroup();
            {
              ImGui::BeginGroup();
              {
                ImGui::Text("Speed:");
                ImGui::SetNextItemWidth(170);
                int speed = m_selectedItem->speed();
                if (ImGui::InputInt("##orpg_database_items_speed", &speed, 1, 100)) {
                  m_selectedItem->setSpeed(std::clamp(speed, -2000, 2000));
                }
              }
              ImGui::EndGroup();
              ImGui::SameLine();
              ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
              ImGui::BeginGroup();
              {
                ImGui::Text("Success:");
                ImGui::SetNextItemWidth(170);
                int successRate = m_selectedItem->successRate();
                if (ImGui::InputInt("##orpg_database_items_successRate", &successRate, 1, 100)) {
                  m_selectedItem->setSuccessRate(std::clamp(successRate, 0, 100));
                }
              }
              ImGui::EndGroup();
              ImGui::BeginGroup();
              {
                ImGui::Text("Repeat:");
                ImGui::SetNextItemWidth(170);
                int repeats = m_selectedItem->repeats();
                if (ImGui::InputInt("##orpg_database_items_repeats", &repeats, 1, 100)) {
                  m_selectedItem->setRepeats(std::clamp(repeats, 1, 9));
                }
              }
              ImGui::EndGroup();
              ImGui::SameLine();
              ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
              ImGui::BeginGroup();
              {
                ImGui::Text("TP Gain:");
                ImGui::SetNextItemWidth(170);
                int tpGain = m_selectedItem->tpGain();
                if (ImGui::InputInt("##orpg_database_items_tpGain", &tpGain, 1, 100)) {
                  m_selectedItem->setTpGain(std::clamp(tpGain, 0, 100));
                }
              }
              ImGui::EndGroup();
              ImGui::BeginGroup();
              {
                ImGui::Text("Hit Type:");
                ImGui::SetNextItemWidth(160);
                if (ImGui::BeginCombo("##orpg_database_items_hitType", DecodeEnumName(magic_enum::enum_name(m_selectedItem->hitType())).c_str())) {
                  int index{0};
                  for (auto& dir : magic_enum::enum_values<HitType>()) {
                    if (const bool is_selected = m_selectedItem->hitType() == static_cast<HitType>(magic_enum::enum_index(dir).value());
                        ImGui::Selectable(DecodeEnumName(magic_enum::enum_name(dir)).c_str(), is_selected)) {
                      m_selectedItem->setHitType(dir);
                      if (is_selected)
                        ImGui::SetItemDefaultFocus();
                    }
                    index++;
                  }
                  ImGui::EndCombo();
                }
              }
              ImGui::EndGroup();
              ImGui::SameLine();
              // Animation
              ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 6.f);
              ImGui::BeginGroup();
              {
                ImGui::Text("Animation:");
                ImGui::SetNextItemWidth(160);
                // Animation Button
                ImGui::PushID("##orpg_database_items_animation");
                if (ImGui::Button(m_selectedItem->animationId() == -1  ? "Normal Attack"
                                  : m_selectedItem->animationId() == 0 ? "None"
                                                                       : Database::instance()->animationName(m_selectedItem->animationId()).c_str(),
                                  ImVec2{200 - 15, 0})) {
                  m_animationPicker = ObjectPicker("Animation"sv, Database::instance()->animations.animations(), m_selectedItem->animationId());
                  m_animationPicker->setOpen(true);
                }
                ImGui::PopID();
              }
              ImGui::EndGroup();
            }
            ImGui::EndGroup();
          }
          ImGui::EndGroup();
        }
        ImGui::EndChild();

        ImGui::SameLine();
        ImGui::BeginChild("##orpg_items_item_panel_right");
        { m_effectsEditor.draw(m_parent); }
        ImGui::EndChild();
        ImGui::BeginChild("##orpg_items_item_panel_bottomright");
        {
          ImGui::SeparatorText("Note:");
          char note[8192];
          strncpy(note, m_selectedItem->note().c_str(), IM_ARRAYSIZE(note));
          if (ImGui::InputTextMultiline(std::format("##orpg_database_item_note{}", m_selectedItem->id()).c_str(), note, IM_ARRAYSIZE(note), ImVec2{ImGui::GetContentRegionMax().x - 16, 400})) {
            m_selectedItem->setNote(note);
          }
        }
        ImGui::EndChild();
      }
    }
    ImGui::EndChild();
  }
  ImGui::EndChild();

  if (m_changeIntDialogOpen) {
    if (ImGui::Begin("Change Max Items", &m_changeIntDialogOpen,
                     ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_Modal | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
      ImGui::Text(
          "Specify an amount to resize the item list to\n"
          "This cannot be undone!");
      ImGui::InputInt("##value_input", &m_editMaxItems);
      ImGui::SameLine();
      if (ImGui::Button("Accept")) {
        m_changeConfirmDialogOpen = true;
      }
      ImGui::SameLine();
      if (ImGui::Button("Cancel")) {
        m_changeIntDialogOpen = false;
      }
    }
    ImGui::End();

    if (m_changeConfirmDialogOpen) {
      if (ImGui::Begin("Confirm Change", &m_changeConfirmDialogOpen,
                       ImGuiWindowFlags_NoResize | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
        ImGui::Text("Are you sure?");
        if (ImGui::Button("Yes")) {
          const int tmpId = m_selectedItem->id();
          m_items.resize(m_editMaxItems);
          m_selectedItem = m_items.item(tmpId);
          m_changeIntDialogOpen = false;
          m_changeConfirmDialogOpen = false;
        }
        ImGui::SameLine();
        if (ImGui::Button("Cancel")) {
          m_editMaxItems = m_items.count();
          m_changeIntDialogOpen = false;
          m_changeConfirmDialogOpen = false;
        }
      }
      ImGui::End();
    }
  }
}


code:
#pragma once
#include "Core/CommonUI/EffectsEditor.hpp"
#include "Core/DatabaseEditor/IDBEditorTab.hpp"
#include "Core/Graphics/CheckeredCompositeTexture.hpp"
#include "Core/Graphics/IconSheet.hpp"
#include "DBCommonEventsTab.hpp"
#include "Database/Animation.hpp"
#include "Database/Items.hpp"

class Items;
class DBItemsTab final : public IDBEditorTab {
public:
  DBItemsTab() = delete;
  explicit DBItemsTab(Items& items, DatabaseEditor* parent);
  void draw() override;

  [[nodiscard]] std::vector<Item>& items() { return m_items.items(); }
  [[nodiscard]] const std::vector<Item>& items() const { return m_items.items(); }

  [[nodiscard]] Item* item(const int id) { return m_items.item(id); }
  [[nodiscard]] const Item* item(const int id) const { return m_items.item(id); }
  std::vector<int>& getHeaders() override { return m_headers; }
  int getHeader(int index) override { return m_headers.at(index); }
  void setHeaderRange(int start, int end) override {
    m_categoryStart = start;
    m_categoryEnd = end;
  }
  bool hasHeader() override { return !m_headers.empty(); }

  std::string getName(int index) override { return m_items.item(index)->name(); }
  int getCount() override { return m_items.count(); }

private:
  int m_categoryStart;
  int m_categoryEnd;
  std::vector<int> m_headers;
  Items& m_items;
  Item* m_selectedItem{};
  int m_editMaxItems{};
  float m_splitterWidth = 300.f;
  bool m_changeIntDialogOpen = false;
  bool m_changeConfirmDialogOpen = false;
  IconSheet* m_currentSheet = nullptr;
  std::optional<CheckeredCompositeTexture> m_itemButtonTexture;
  std::optional<IconSheet> m_itemSheet;
  std::optional<IconSheet> m_itemPicker;
  EffectsEditor m_effectsEditor;
  std::optional<ObjectPicker<Animation>> m_animationPicker;
};


code:
#include "Core/DatabaseEditor/DBLocaleTab.hpp"
#include "Core/Application.hpp"
#include "Core/DatabaseEditor.hpp"
#include "Core/ImGuiExt/ImGuiNotify.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/MainWindow.hpp"
#include "imgui.h"
#include <cstring>
#include <misc/cpp/imgui_stdlib.h>
#include <numeric>
DBLocaleTab::DBLocaleTab(Locales& locales, DatabaseEditor* parent)
: IDBEditorTab(parent)
, m_locales(&locales) {
  std::string path = Database::instance()->basePath + "locales/"; // Replace with your directory path

  try {
    if (fs::exists(path) && fs::is_directory(path)) {
      for (const auto& entry : fs::directory_iterator(path)) {
        if (is_directory(entry.status())) {
          m_localeList.push_back(entry.path().filename().generic_string());
        }
      }
    }
  } catch (const fs::filesystem_error& e) { std::cout << "Error: " << e.what() << std::endl; }

  m_jpLocales.loadMap(Database::instance()->basePath + std::format("locales/ja/Map0{:03}.json", m_selectedMap));

  for (int i = 0; i < m_locales->locales.size(); i++) {
    if (m_locales->locales.at(i).second.empty() || m_jpLocales.locales.at(i).second.empty()) {
      m_untranslatedIndices.push_back(i);
    }
  }
}

void DBLocaleTab::draw() {
  const auto calc = ImGui::CalcTextSize("ABCDEFGHIJKLMNOPQRSTUV");

  ImGui::BeginChild("##orpg_locales_navigation", ImVec2{ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x, 0}, 0, ImGuiWindowFlags_NoScrollbar);
  {
    ImGui::BeginGroup();
    {
      ImGui::BeginChild("##orpg_locales_map_navigation_bar", ImVec2{0, 30.f});
      {
        ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
        if (ImGui::BeginCombo("##orpg_locales_map_combo", Database::instance()->mapInfos.map(m_selectedMap)->name().c_str())) {
          for (int i = 1; i < Database::instance()->mapInfos.mapInfos().size(); i++) {
            bool is_selected = m_selectedMap == i;
            if (ImGui::Selectable(std::format("{}##orpg_locales_map_selectable", Database::instance()->mapInfos.map(i)->name()).c_str(), is_selected)) {
              m_selectedMap = i;
              m_editingIndex = -1;
              m_selectedIndex = -1;
              m_locales->loadMap(Database::instance()->basePath + std::format("locales/en/Map0{:03}.json", m_selectedMap));
              m_jpLocales.loadMap(Database::instance()->basePath + std::format("locales/ja/Map0{:03}.json", m_selectedMap));

              m_untranslatedIndices.clear();
              for (int i = 0; i < m_locales->locales.size(); i++) {
                if (m_locales->locales.at(i).second.empty() || m_jpLocales.locales.at(i).second.empty()) {
                  m_untranslatedIndices.push_back(i);
                }
              }
              if (is_selected) {
                ImGui::SetItemDefaultFocus();
              }
            }
          }
          ImGui::EndCombo();
        }
        ImGui::EndChild();
      }
      ImGui::BeginChild("##orpg_locales_map_navigation_subbar", ImVec2{ImGui::GetContentRegionAvail().x - (ImGui::GetStyle().FramePadding.x * 55), 80.f});
      {
        ImGui::BeginDisabled(m_editingIndex == -1);
        if (ImGui::Button("\u274C", {30, 30})) {
          m_editingIndex = -1;
        }
        ImGui::EndDisabled();
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("\u002B", {30, 30})) {
          m_locales->locales.push_back(std::make_pair(std::to_string(m_locales->locales.size() - 1), ""));
          m_jpLocales.locales.push_back(std::make_pair(std::to_string(m_locales->locales.size() - 1), ""));
          m_selectedUntranslated = m_locales->locales.size() - 1;
          m_navigator = true;
        }
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        ImGui::BeginDisabled(m_locales->locales.empty());
        if (ImGui::Button(ICON_FA_FLOPPY_DISK, {30, 30})) {
          m_editingIndex = -1;

          if (m_mainLanguageDirty) {
            m_locales->saveCurrentLocale();
          } else {
            m_jpLocales.saveCurrentLocale();
          }
          m_compareLanguageDirty = false;
          m_mainLanguageDirty = false;
        }
        ImGui::SetItemTooltip("%s", trNOOP("Save pending changes if you are editing text."));
        ImGui::SameLine();
        if (ImGui::Button("S", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\S[value]\\S[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\S[value]\\S[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Sets the speed at which the text displays."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("V", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\V[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\V[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Displays the value of a variable."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("N", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\N[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\N[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Display the name of the actor by index."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("P", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\P[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\P[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Display the name of the party member by index."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("C", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\C[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\C[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Draw the text following in the specified color index. Text color is based off Window.png system image."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("G", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\G[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\G[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Shows the currency unit."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("I", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\I[value]";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\I[value]";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Displays an icon by its index."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("{", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\{";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\{";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Increase the text by 1 step."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("}", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\}";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\}";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Decrease the text by 1 step."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("$", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\$";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\$";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Open the gold window."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button(".", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\.";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\.";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Wait for 1/4 second."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("|", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\|";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\|";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Wait for 1 second."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("!", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\!";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\!";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Wait for button input."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button(">", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\>";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\>";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Display remaining text on the same line all at once."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("<", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\<";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\<";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Cancels display all effect."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("^", {30, 30})) {
          if (m_mainLanguageEdit && m_editingIndex > -1) {
            m_locales->locales.at(m_editingIndex).second += "\\^";
          } else if (m_mainLanguageEdit == false && m_editingIndex > -1) {
            m_jpLocales.locales.at(m_editingIndex).second += "\\^";
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Do not wait for input after text display."));
        ImGui::SameLine();

        ImGui::SetNextItemWidth(100);
        if (ImGui::BeginCombo("##orpg_locales_language_selection", m_localeList.at(m_selectedLanguage).c_str())) {
          int localeIndex{0};
          for (auto& locale : m_localeList) {
            bool is_selected = m_selectedLanguage == localeIndex;
            if (ImGui::Selectable(locale.c_str(), is_selected)) {
              m_selectedLanguage = localeIndex;
              // TODO: Load selected locale
              if (is_selected) {
                ImGui::SetItemDefaultFocus();
              }
            }
            localeIndex++;
          }
          ImGui::EndCombo();
        }
        ImGui::EndDisabled();
        // Second line
        ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
        if (ImGui::Button("\u21E6", {30, 30})) {
          if (!m_untranslatedIndices.empty()) {
            if (m_selectedUntranslated == 0) {
              m_selectedUntranslated = m_untranslatedIndices.size() - 1;
            } else {
              m_selectedUntranslated--;
            }
            m_selectedIndex = m_untranslatedIndices.at(m_selectedUntranslated);
            m_navigator = true;
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Navigates to the previous untranslated pair."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("\u21E8", {30, 30})) {
          if (!m_untranslatedIndices.empty()) {
            if (m_selectedUntranslated == m_untranslatedIndices.size() - 1) {
              m_selectedUntranslated = 0;
            } else {
              m_selectedUntranslated++;
            }
            m_selectedIndex = m_untranslatedIndices.at(m_selectedUntranslated);
            m_navigator = true;
          }
        }
        ImGui::SetItemTooltip("%s", trNOOP("Navigates to the next untranslated pair."));
        ImGui::SameLine();
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 8.f);
        if (ImGui::Button("\u24E4", {30, 30})) {
          m_filterUntranslated = !m_filterUntranslated;
          m_selectedUntranslated = -1;
          m_selectedIndex = -1;
          m_navigator = false;
        }
        ImGui::SetItemTooltip("%s", trNOOP("Shows only untranslated pairs or cancels untranslated navigation."));
        ImGui::SameLine();
        ImGui::SetNextItemWidth(400);
        ImGui::InputText("##orpg_locales_search_bar", &m_searchString);

        ImGui::EndChild();
      }
      ImGui::SameLine();
      ImVec2 cursorPos = ImGui::GetCursorPos();
      ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4{0.2f, 0.3f, 0.6f, 1.0f});
      ImGui::BeginChild("##orpg_locales_text_background", ImVec2{ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x, 80}, ImGuiChildFlags_FrameStyle);
      { ImGui::EndChild(); }
      ImGui::PopStyleColor();
      if (m_selectedIndex > -1) {
        ImGui::SetCursorPos(ImVec2{cursorPos.x + 10.f, cursorPos.y});
        if (m_mainLanguageEdit) {
          ImGui::TextUnformatted(m_locales->locales.at(m_selectedIndex).second.c_str());
        } else {
          ImGui::TextUnformatted(m_jpLocales.locales.at(m_selectedIndex).second.c_str());
        }
      }
    }
    ImGui::EndGroup();

    ImGui::BeginChild("##orpg_locales_map_navigation_table",
                      ImVec2{ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x, ImGui::GetContentRegionAvail().y - ImGui::GetStyle().FramePadding.y});
    {
      if (ImGui::BeginTable("##orpg_locale_text_table", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableRowFlags_Headers | ImGuiTableFlags_ScrollY | ImGuiTableColumnFlags_WidthFixed,
                            ImVec2{ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y})) {

        ImGui::TableSetupColumn("English");
        ImGui::TableSetupColumn("Japanese");
        ImGui::TableNextRow();
        ImGui::TableNextColumn();

        int index{0};
        for (auto& pair : m_locales->locales) {

          if (!m_searchString.empty()) {
            if (pair.second.contains(m_searchString)) {
              drawTableOne(pair.second, index);
              ImGui::TableNextColumn();

              drawTableTwo(index);
              ImGui::TableNextColumn();
            } else {
              if (m_filterUntranslated) {
                if ((pair.second.empty() || m_jpLocales.locales.at(index).second.empty()) && m_filterUntranslated) {
                  drawTableOne(pair.second, index);
                  ImGui::TableNextColumn();

                  drawTableTwo(index);
                  ImGui::TableNextColumn();
                }
              }
            }
          } else {
            drawTableOne(pair.second, index);
            ImGui::TableNextColumn();
          }
          if (index < m_jpLocales.locales.size()) {
            if (m_searchString.empty() && m_filterUntranslated == false) {
              drawTableTwo(index);
              ImGui::TableNextColumn();
            }
          } else {
            // Do we add the empty indexes?
            m_jpLocales.locales.push_back(std::make_pair(std::to_string(m_jpLocales.locales.size()), "Begin editing text!"));
          }
          index++;
        }
        ImGui::EndTable();
      }
      ImGui::EndChild();
    }
    ImGui::EndChild();
  }
}

void DBLocaleTab::drawTableOne(std::string& text, int index) {
  if (m_editingIndex == index && m_mainLanguageEdit == true) {
    if (ImGui::InputTextMultiline(std::format("##orpg_locale_text_input_{}_{}", index, 1).c_str(), &text, ImVec2{600, 100}), ImGuiInputTextFlags_NoHorizontalScroll) {
      m_mainLanguageDirty = true;
    }
  } else {

    bool is_selected = m_selectedIndex == index;
    if (ImGui::Selectable(std::format("{}##orpg_locale_text_{}_{}", text, index, 1).c_str(), is_selected, 0, ImVec2{600, 80})) {
      m_mainLanguageEdit = true;
      m_selectedIndex = index;
    }
    if (!m_untranslatedIndices.empty() && m_selectedUntranslated > -1) {
      if (m_untranslatedIndices.at(m_selectedUntranslated) == index) {
        if (m_navigator) {
          ImGui::SetScrollHereY();
          m_navigator = false;
        }
      }
    }
    if (ImGui::IsItemHovered() && ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
      m_editingIndex = index;
      m_mainLanguageEdit = true;
      m_selectedIndex = -1;
    }
  }
}

void DBLocaleTab::drawTableTwo(int index) {
  if (m_editingIndex == index && m_mainLanguageEdit == false) {
    if (ImGui::InputTextMultiline(std::format("##orpg_locale_text_input_{}_{}", index, 2).c_str(), &m_jpLocales.locales.at(index).second, ImVec2{600, 100}), ImGuiInputTextFlags_NoHorizontalScroll) {
      m_compareLanguageDirty = true;
    }
  } else {
    bool is_selected = m_selectedIndex == index;
    if (ImGui::Selectable(std::format("{}##orpg_locale_text_{}_{}", m_jpLocales.locales.at(index).second, index, 2).c_str(), is_selected, 0, ImVec2{600, 80})) {
      m_selectedIndex = index;
      m_mainLanguageEdit = false;
    }
    if (ImGui::IsItemHovered() && ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
      m_editingIndex = index;
      m_selectedIndex = -1;
    }
  }
}


code:
#pragma once
#include "Core/DatabaseEditor/IDBEditorTab.hpp"
#include "Core/Graphics/Image.hpp"
#include "Database/Locales.hpp"

struct DBLocaleTab : IDBEditorTab {
  DBLocaleTab() = delete;
  explicit DBLocaleTab(Locales& locales, DatabaseEditor* parent);
  void draw() override;
  void drawTableOne(std::string& text, int index);
  void drawTableTwo(int index);
  int getHeader(int index) override { return 0; }

  std::vector<int>& getHeaders() override { return m_untranslatedIndices; }
  bool hasHeader() override { return false; }
  void setHeaderRange(int start, int end) override {}
  std::string getName(int index) override { return ""; }
  int getCount() override { return 0; }

  // TODO: Fuzzy searching

private:
  Locales* m_locales = nullptr;
  Locales m_jpLocales;

  std::string m_searchString;

  std::vector<std::string> m_localeList;
  std::vector<int> m_untranslatedIndices;

  int m_selectedMap{1};
  int m_selectedIndex{-1};
  int m_editingIndex{-1};
  int m_selectedLanguage{1};
  int m_selectedUntranslated{-1};

  bool m_mainLanguageDirty{false};
  bool m_compareLanguageDirty{false};
  bool m_mainLanguageEdit{false};
  bool m_filterUntranslated{false};
  bool m_navigator{false};
};