code:
#include "Core/EventCommands/Dialog_ShowPicture.hpp"

#include "Core/CommonUI/GroupBox.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Database/Database.hpp"

#include <imgui.h>
#include <imgui_internal.h>
#include <tuple>

std::tuple<bool, bool> Dialog_ShowPicture::draw() {
  if (isOpen()) {
    ImGui::OpenPopup("###ShowPicture");
  }
  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  const auto maxSize = ImVec2{(ImGui::CalcTextSize("#").x * 60) + (ImGui::GetStyle().FramePadding.x * 2), (ImGui::GetFrameHeightWithSpacing() * 16) + (ImGui::GetStyle().FramePadding.y * 2)};
  ImGui::SetNextWindowSize(maxSize, ImGuiCond_Appearing);
  ImGui::SetNextWindowSizeConstraints(maxSize, {FLT_MAX, FLT_MAX});

  if (ImGui::BeginPopupModal(std::format("{}###ShowPicture", m_name).c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
    drawPickers();
    ImGui::BeginVertical("##show_picture_main_layout", ImGui::GetContentRegionAvail(), 0);
    {
      ImGui::BeginVertical("##show_picture_inner_layout", {-1, 0}, 0);
      {
        GroupBox pictureGroup(trNOOP("Picture"), "##show_picture_image_group", {-1, 0});
        if (pictureGroup.begin()) {
          GroupBox numberGroupBox(trNOOP("Number"), "##show_picture_number", {ImGui::GetContentRegionAvail().x * 0.25f, 0});
          if (numberGroupBox.begin()) {
            ImGui::SetNextItemWidth(-1.f);
            if (ImGui::SpinInt("##show_picture_number_input", &m_number, 1, 100)) {
              m_number = std::clamp(m_number, 1, 999);
            }
          }
          numberGroupBox.end();
          ImGui::SameLine();
          GroupBox imageGroupBox(trNOOP("Image"), "##show_picture_image", {-1, 0});
          if (imageGroupBox.begin()) {
            if (ImGui::EllipsesButton(std::format("{}##show_picture_image_button", Database::imageText(m_imageName)).c_str(), {-1, 0})) {
              m_imagePicker->setImageInfo(m_imageName);
              m_imagePicker->setOpen(true);
            }
          }
          imageGroupBox.end();
        }
        pictureGroup.end();
        GroupBox positionGroup(trNOOP("Position"), "##show_picture_position", {-1, 0});
        if (positionGroup.begin()) {
          ImGui::BeginVertical("##show_picture_position_layout", {-1, 0});
          {
            GroupBox originGroup(trNOOP("Origin"), "##show_picture_origin", {-1, 0});
            if (originGroup.begin()) {
              ImGui::SetNextItemWidth(-1.f);
              if (ImGui::BeginCombo("##show_picture_origin", DecodeEnumName(magic_enum::enum_value<PictureOrigin>(m_origin)).c_str())) {
                for (auto origin : magic_enum::enum_values<PictureOrigin>()) {
                  bool selected = m_origin == static_cast<int>(origin);
                  if (ImGui::Selectable(DecodeEnumName(origin).c_str(), selected)) {
                    m_origin = static_cast<int>(origin);
                  }
                  if (selected) {
                    ImGui::SetItemDefaultFocus();
                  }
                }
                ImGui::EndCombo();
              }
            }
            originGroup.end();
            ImGui::RadioButton(trNOOP("Direct designation"), &m_type, 0);
            ImGui::BeginHorizontal("##show_picture_position_direct_desig_layout", {-1, 0});
            {
              ImGui::Dummy({ImGui::GetFrameHeightWithSpacing(), 0});
              ImGui::BeginHorizontal("##show_picture_position_direct_desig_right_layout", {-1, 0});
              {
                ImGui::BeginDisabled(m_type != 0);
                {

                  GroupBox directDesigXGroup(trNOOP("X"), "##show_picture_direct_desig_x_group", {ImGui::GetContentRegionAvail().x * 0.5f, 0});
                  if (directDesigXGroup.begin()) {
                    ImGui::SetNextItemWidth(-1.f);
                    ImGui::SpinInt("##show_picture_direct_desig_x", &m_xConstant, 1, 100, m_type == 0 ? nullptr : "");
                  }
                  directDesigXGroup.end();
                  GroupBox directDesigYGroup(trNOOP("Y"), "##show_picture_direct_desig_y_group", {ImGui::GetContentRegionAvail().x, 0});
                  if (directDesigYGroup.begin()) {
                    ImGui::SetNextItemWidth(-1.f);
                    ImGui::SpinInt("##show_picture_direct_desig_y", &m_yConstant, 1, 100, m_type == 0 ? nullptr : "");
                  }
                  directDesigYGroup.end();
                }
                ImGui::EndDisabled();
              }
              ImGui::EndHorizontal();
            }
            ImGui::EndHorizontal();
            ImGui::RadioButton(trNOOP("Designation with variables"), &m_type, 1);
            ImGui::BeginHorizontal("##show_picture_position_var_desig_layout", {-1, 0});
            {
              ImGui::Dummy({ImGui::GetFrameHeightWithSpacing(), 0});
              ImGui::BeginHorizontal("##show_picture_position_var_desig_right_layout", {-1, 0});
              {
                ImGui::BeginDisabled(m_type != 1);
                {

                  GroupBox varDesigXGroup(trNOOP("X"), "##show_picture_var_desig_x_group", {ImGui::GetContentRegionAvail().x * 0.5f, 0});
                  if (varDesigXGroup.begin()) {
                    ImGui::SetNextItemWidth(-1.f);
                    if (ImGui::EllipsesButton(std::format("{}##show_picture_direct_var_x", m_type == 1 ? Database::instance()->variableNameAndId(m_xVariable) : "").c_str(), {-1, 0})) {
                      m_isPickingY = false;
                      m_varPicker.emplace(trNOOP("Variables"), Database::instance()->system.variables(), m_xVariable);
                      m_varPicker->setOpen(true);
                    }
                  }
                  varDesigXGroup.end();
                  GroupBox varDesigYGroup(trNOOP("Y"), "##show_picture_var_desig_y_group", {ImGui::GetContentRegionAvail().x, 0});
                  if (varDesigYGroup.begin()) {
                    ImGui::SetNextItemWidth(-1.f);
                    if (ImGui::EllipsesButton(std::format("{}##show_picture_direct_var_y", m_type == 1 ? Database::instance()->variableNameAndId(m_yVariable) : "").c_str(), {-1, 0})) {
                      m_isPickingY = true;
                      m_varPicker.emplace(trNOOP("Variables"), Database::instance()->system.variables(), m_yVariable);
                      m_varPicker->setOpen(true);
                    }
                  }
                  varDesigYGroup.end();
                }
                ImGui::EndDisabled();
              }
              ImGui::EndHorizontal();
            }
            ImGui::EndHorizontal();
          }
          ImGui::EndVertical();
        }
        positionGroup.end();
      }
      ImGui::EndVertical();

      ImGui::Spring();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
      ImGui::BeginHorizontal("##show_picture_button_layout", {-1, 0}, 0);
      {
        ImGui::Spring();
        if (const auto ret = ImGui::ButtonGroup("##show_picture_buttons", {trNOOP("OK"), trNOOP("Cancel")}); ret == 0) {
          m_confirmed = true;
          m_command->number = m_number;
          m_command->imageName = m_imageName;
          m_command->origin = static_cast<PictureOrigin>(m_origin);
          m_command->type = static_cast<PictureDesignationSource>(m_type);
          if (m_command->type == PictureDesignationSource::Direct_designation) {
            m_command->value1 = m_xConstant;
            m_command->value2 = m_yConstant;
          } else {
            m_command->value1 = m_xVariable;
            m_command->value2 = m_yVariable;
          }
          m_command->zoomX = m_zoomX;
          m_command->zoomY = m_zoomY;
          m_command->opacityValue = m_opacityValue;
          m_command->blendMode = static_cast<Blend>(m_blendMode);
          ImGui::CloseCurrentPopup();
          setOpen(false);
        } else if (ret == 1) {
          ImGui::CloseCurrentPopup();
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();
    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}

void Dialog_ShowPicture::drawPickers() {
  if (m_varPicker) {
    if (const auto [closed, confirmed] = m_varPicker->draw(); closed) {
      if (confirmed) {
        if (m_isPickingY) {
          m_yVariable = m_varPicker->selection();
        } else {
          m_xVariable = m_varPicker->selection();
        }
      }
      m_varPicker.reset();
    }
  }

  if (const auto [closed, confirmed] = m_imagePicker->draw(); closed) {
    if (confirmed) {
      m_imagePicker->accept();
      m_imageName = m_imagePicker->selectedImage();
    }
  }
}


code:
#pragma once
#include <memory>

#include "Core/CommonUI/ImagePicker.hpp"
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/ShowPicture.hpp"

struct Dialog_ShowPicture : IEventDialogController {
  Dialog_ShowPicture() = delete;
  explicit Dialog_ShowPicture(const std::string& name, const std::shared_ptr<ShowPictureCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , m_command(cmd) {
    if (cmd == nullptr) {
      m_command = std::make_shared<ShowPictureCommand>();
    }
    m_number = m_command->number;
    m_imageName = m_command->imageName;
    m_origin = static_cast<int>(m_command->origin);
    m_type = static_cast<int>(m_command->type);
    m_xVariable = m_command->value1;
    m_yVariable = m_command->value2;
    m_zoomX = m_command->zoomX;
    m_zoomY = m_command->zoomY;
    m_opacityValue = m_command->opacityValue;
    m_blendMode = static_cast<int>(m_command->blendMode);

    m_imagePicker.emplace(ImagePicker::PickerMode::Picture, m_imageName, m_imageName);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return m_command; };

private:
  void drawPickers();
  int m_number;
  std::string m_imageName;
  int m_origin;
  int m_type;
  int m_xVariable; // direct X value or indirect from global variables
  int m_yVariable; // direct Y value or indirect from global variables
  int m_zoomX;
  int m_zoomY;
  int m_opacityValue;
  int m_blendMode;

  bool m_isPickingY{false};
  int m_xConstant{0};
  int m_yConstant{0};

  bool m_confirmed{false};
  std::optional<VariableSwitchPicker> m_varPicker;
  std::shared_ptr<ShowPictureCommand> m_command;
  std::optional<ImagePicker> m_imagePicker;
};

repo_name: Antidote/openrpgeditor

license: mit


code:
#include "Core/EventCommands/Dialog_ShowScrollingText.hpp"

#include "Core/CommonUI/GroupBox.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"

#include <imgui.h>
#include <imgui_internal.h>
#include <misc/cpp/imgui_stdlib.h>
#include <tuple>

std::tuple<bool, bool> Dialog_ShowScrollingText::draw() {
  if (isOpen()) {
    ImGui::OpenPopup("###ShowScrollingText");
  }
  const auto maxSize = ImVec2{ImGui::CalcTextSize("#").x * 64 + (ImGui::GetStyle().FramePadding.x * 2), (ImGui::GetFrameHeightWithSpacing() * 12) + (ImGui::GetStyle().FramePadding.y * 2)};
  ImGui::SetNextWindowSize(maxSize, ImGuiCond_Appearing);
  ImGui::SetNextWindowSizeConstraints(maxSize, {FLT_MAX, FLT_MAX});
  if (ImGui::BeginPopupModal(std::format("{}###ShowScrollingText", m_name).c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::BeginVertical("##show_scrolling_text_main_layout", ImGui::GetContentRegionAvail(), 0);
    {
      GroupBox textGroupBox(trNOOP("Text"), "##text_group", {-1, 0});
      if (textGroupBox.begin()) {
        ImGui::InputTextMultiline("##show_scrolling_text_input", &m_textLine, {-1, ImGui::GetTextLineHeightWithSpacing() * 7 + ImGui::GetStyle().FramePadding.y});
      }
      textGroupBox.end();
      ImGui::Spring();
      GroupBox speedGroupBox(trNOOP("Speed"), "##speed_group", {-1, 0});
      if (speedGroupBox.begin()) {
        ImGui::SetNextItemWidth(-1);
        if (ImGui::SpinInt("##show_scrolling_text_speed", &m_speed)) {
          m_speed = std::clamp(m_speed, 1, 8);
        }
      }
      speedGroupBox.end();
      ImGui::Spring();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
      ImGui::BeginHorizontal("##show_scrolling_text_button_layout", {-1, 0}, 0);
      {
        ImGui::Checkbox(trNOOP("No Fast Forward"), &m_noFast);
        ImGui::Spring();
        if (const auto ret = ImGui::ButtonGroup("##show_text_buttons", {trNOOP("Preview..."), trNOOP("OK"), trNOOP("Cancel")}, false, {trNOOP("UNIMPLEMENTED")}); ret == 0) {

        } else if (ret == 1) {
          m_confirmed = true;
          m_command->speed = m_speed;
          m_command->noFast = m_noFast;
          for (const auto split = splitString(m_textLine, '\n'); const auto& str : split) {
            m_command->text.emplace_back();
            m_command->text.back()->text = str;
          }
          setOpen(false);
        } else if (ret == 2) {
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();
    ImGui::EndPopup();
  }

  return {!m_open, m_confirmed};
}


code:
#pragma once
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Core/Log.hpp"
#include "Database/EventCommands/ScrollingText.hpp"

struct Dialog_ShowScrollingText : IEventDialogController {
  Dialog_ShowScrollingText() = delete;
  explicit Dialog_ShowScrollingText(const std::string& name, const std::shared_ptr<ShowScrollTextCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , m_command(cmd) {
    if (cmd == nullptr) {
      m_command.reset(new ShowScrollTextCommand());
    }
    m_speed = m_command->speed;
    m_noFast = m_command->noFast;

    for (const auto& str : m_command->text) {
      m_textLine += !m_textLine.empty() ? "\n" + str->text : str->text;
    }
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return m_command; };

private:
  int m_speed;
  bool m_noFast;
  std::string m_textLine;

  bool m_confirmed{false};
  std::shared_ptr<ShowScrollTextCommand> m_command;
};


code:
#include "Core/EventCommands/Dialog_ShowText.hpp"

#include "Core/CommonUI/GroupBox.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/Log.hpp"
#include "imgui.h"
#include "imgui_internal.h"
#include "misc/cpp/imgui_stdlib.h"

#include <iostream>
#include <tuple>

std::tuple<bool, bool> Dialog_ShowText::draw() {
  if (isOpen()) {
    ImGui::OpenPopup("###ShowText");
  }
  const auto imageSize = ImGui::GetDPIScaledSize(FaceSheet::faceWidth(), FaceSheet::faceHeight());
  m_faceButtonTexture.setSize(imageSize.x, imageSize.y);

  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  const auto minimumWidth = ImGui::CalcTextSize("#").x * 60;
  const auto minimumWindowSize = ImVec2{imageSize.x + minimumWidth + (ImGui::GetStyle().FramePadding.x * 2), imageSize.y + (ImGui::GetTextLineHeight() * 9) + (ImGui::GetStyle().FramePadding.y * 2)};
  ImGui::SetNextWindowSizeConstraints(minimumWindowSize, {FLT_MAX, FLT_MAX});
  ImGui::SetNextWindowSize(minimumWindowSize, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(std::format("{}###ShowText", m_name).c_str(), &m_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar)) {
    drawPickers();
    if (!m_faceSheet && !m_faceImage.empty()) {
      m_faceSheet.emplace(m_faceImage);
      auto offset = m_faceSheet->getFaceRect(m_faceIndex).min;
      offset.xr() *= m_faceSheet->texture().width();
      offset.yr() *= m_faceSheet->texture().height();
      m_faceButtonTexture.setTexturesToComposite({
          {
              m_faceSheet->texture(),
              Point{m_faceSheet->faceWidth(), m_faceSheet->faceHeight()},
              offset.toPoint(),
              true,
          },
      });
    }
    ImGui::BeginVertical("##show_text_layout", ImGui::GetContentRegionAvail(), 0);
    {
      ImGui::BeginHorizontal("##show_text_layout_upper", {-1, -1}, 0);
      {
        GroupBox faceGroupBox(trNOOP("Face"), "##face_group");
        if (faceGroupBox.begin()) {
          if (ImGui::ImageButton("##face_button", m_faceButtonTexture.get(), imageSize)) {
            m_facePicker.emplace(m_faceImage, m_faceIndex);
            m_facePicker->setOpen(true);
          }
        }
        faceGroupBox.end();

        ImGui::BeginVertical("##show_text_layout_text", {-1, -1}, 0);
        {
          GroupBox textGroupBox(trNOOP("Text"), "##text_group", {-1, 0});
          if (textGroupBox.begin()) {
            ImGui::InputTextMultiline("##show_text_multiline", &m_textLine, ImVec2{ImGui::GetContentRegionAvail().x, (ImGui::GetTextLineHeight() * 4) + (ImGui::GetStyle().FramePadding.y * 2)});
          }
          textGroupBox.end();
          ImGui::BeginHorizontal("##show_text_window_settings", {-1, -1}, 0);
          {
            GroupBox backgroundGroup(trNOOP("Background"), "##show_text_background", {ImGui::GetContentRegionAvail().x * .5f, 0});
            if (backgroundGroup.begin()) {
              ImGui::SetNextItemWidth(-1);
              if (ImGui::BeginCombo("##showtext_background", DecodeEnumName(m_background).c_str())) {
                for (auto& bg : magic_enum::enum_values<TextBackground>()) {
                  if (ImGui::Selectable(DecodeEnumName(bg).c_str(), m_background == bg)) {
                    m_background = bg;
                    if (m_background == bg)
                      ImGui::SetItemDefaultFocus();
                  }
                }
                ImGui::EndCombo();
              }
            }
            backgroundGroup.end();
            GroupBox positionGroup(trNOOP("Window Position"), "##show_text_window_position", {ImGui::GetContentRegionAvail().x, 0});
            if (positionGroup.begin()) {
              ImGui::SetNextItemWidth(-1);
              if (ImGui::BeginCombo("##show_text_windowpos", DecodeEnumName(m_position).c_str())) {
                for (auto& pos : magic_enum::enum_values<TextWindowPosition>()) {
                  if (ImGui::Selectable(DecodeEnumName(pos).c_str(), m_position == pos)) {
                    m_position = pos;
                    if (m_position == pos)
                      ImGui::SetItemDefaultFocus();
                  }
                }
                ImGui::EndCombo();
              }
            }
            positionGroup.end();
          }
          ImGui::EndHorizontal();
        }
        ImGui::EndVertical();
      }
      ImGui::EndHorizontal();
      ImGui::Spring();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
      ImGui::BeginHorizontal("##show_text_buttons", {-1, -1}, 0);
      {
        if (ImGui::Checkbox(trNOOP("Batch Entry"), &m_batchEntry)) {
          if (!m_batchEntry && std::ranges::count(m_textLine, '\n') >= 4) {
            int newlineCount = 0;
            size_t pos = 0;
            while ((pos = m_textLine.find('\n', pos)) != std::string::npos) {
              newlineCount++;
              if (newlineCount == 4) {
                m_textLine.erase(pos, m_textLine.length() - pos);
                break;
              }
              pos++;
            }
          }
        }
        ImGui::Spring();
        auto ret = ImGui::ButtonGroup("##show_text_buttons", {trNOOP("Preview..."), trNOOP("OK"), trNOOP("Cancel")}, false, {trNOOP("UNIMPLEMENTED")});
        if ((ImGui::IsKeyDown(ImGuiKey_Enter) || ImGui::IsKeyDown(ImGuiKey_KeypadEnter)) && !m_batchEntry && std::ranges::count(m_textLine, '\n') >= 4) {
          ret = 1;
        }
        if (ret == 0) {
          // TODO: Implement text preview
        } else if (ret == 1) {
          // OK
          m_confirmed = true;
          m_command->faceImage = m_faceImage;
          m_command->faceIndex = m_faceIndex;
          m_command->background = m_background;
          m_command->position = m_position;
          m_command->textLine = m_textLine;
          ImGui::CloseCurrentPopup();
          setOpen(false);
        } else if (ret == 2) { // Cancel
          ImGui::CloseCurrentPopup();
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();
    ImGui::EndPopup();
  }
  return std::make_tuple(!m_open, m_confirmed);
}

void Dialog_ShowText::drawPickers() {
  if (m_facePicker) {
    if (const auto [closed, confirmed] = m_facePicker->draw(); closed) {
      if (confirmed) {
        m_faceImage = m_facePicker->selectedSheet();
        m_faceSheet.reset();
        m_faceIndex = m_facePicker->faceIndex();
      }
      m_facePicker.reset();
    }
  }
}


code:
#pragma once
#include "Core/CommonUI/CharacterPicker.hpp"
#include "Core/CommonUI/FacePicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Core/Graphics/CheckerboardTexture.hpp"
#include "Core/Graphics/CheckeredCompositeTexture.hpp"
#include "Core/Graphics/FaceSheet.hpp"
#include "Core/Log.hpp"
#include "Database/EventCommands/Plugin.hpp"
#include "Database/EventCommands/ShowText.hpp"

struct Dialog_ShowText : IEventDialogController {
  Dialog_ShowText() = delete;
  explicit Dialog_ShowText(const std::string& name, const std::shared_ptr<ShowTextCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , m_command(cmd) {
    if (cmd == nullptr) {
      m_command.reset(new ShowTextCommand());
    }
    m_faceImage = m_command->faceImage;
    m_faceIndex = m_command->faceIndex;
    m_background = m_command->background;
    m_position = m_command->position;
    for (const auto& str : m_command->text) {
      m_textLine += (m_textLine.empty() ? "" : "\n") + str->text;
    };
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return m_command; };
  std::vector<std::shared_ptr<IEventCommand>> getBatchCommands() override {
    for (auto& commands : m_moreCommands) {
      std::shared_ptr<IEventCommand> sharedCommand = commands;
      m_eventCommands.push_back(sharedCommand);
    }
    return m_eventCommands;
  };

private:
  void drawPickers();
  std::string m_faceImage;
  int m_faceIndex;
  TextBackground m_background;
  TextWindowPosition m_position;
  std::string m_textLine;

  bool m_batchEntry{false};
  CharacterPicker m_characterPicker{CharacterPicker::PickerMode::Character};
  CheckeredCompositeTexture m_faceButtonTexture;

  std::optional<FaceSheet> m_faceSheet;
  std::optional<FacePicker> m_facePicker;
  std::vector<std::shared_ptr<ShowTextCommand>> m_moreCommands;
  std::vector<std::shared_ptr<IEventCommand>> m_eventCommands;

  bool m_confirmed{false};
  std::shared_ptr<ShowTextCommand> m_command;
};


code:
#include "Core/EventCommands/Dialog_StopSE.hpp"
#include <tuple>

std::tuple<bool, bool> Dialog_StopSE::draw() {
  if (isOpen()) {
    setOpen(false);
    m_confirmed = true;
  }
  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/StopSE.hpp"

struct Dialog_StopSE : IEventDialogController {
  Dialog_StopSE() = delete;
  explicit Dialog_StopSE(const std::string& name, const std::shared_ptr<StopSECommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new StopSECommand());
    }
    m_open = false;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  bool m_confirmed{true};
  std::shared_ptr<StopSECommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/Dialog_TintPicture.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_TintPicture::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{400, 290}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Picture");
    ImGui::SetNextItemWidth(75);
    if (ImGui::InputInt("##tintpicture_picture_id", &m_picture, 1, 100)) {
      if (m_picture < 1)
        m_picture = 1;
      if (m_picture > 999)
        m_picture = 999;
    }
    ImGui::SeparatorText("Color Tone");

    // Color Picker
    color_picker.draw();

    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
    ImGui::PushItemWidth(380);
    if (ImGui::BeginCombo("##tintpicture_presets", "Future presets go in this list")) {
      // TODO: Template system for tint screen
      ImGui::EndCombo();
    }
    ImGui::SeparatorText("Duration");
    ImGui::SetNextItemWidth(100);
    if (ImGui::InputInt("##tintpicture_duration", &m_duration)) {
      if (m_duration < 1)
        m_duration = 1;
      if (m_duration > 999)
        m_duration = 999;
    }
    ImGui::SameLine();
    ImGui::Text("frames 1/60 sec");
    ImGui::SameLine();
    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 80.f);
    ImGui::Checkbox("Wait for Completion", &m_waitForCompletion);

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->color.r = color_picker.r();
      command->color.g = color_picker.g();
      command->color.b = color_picker.b();
      command->color.gray = color_picker.gray();
      command->duration = m_duration;
      command->waitForCompletion = m_waitForCompletion;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/ColorTonePicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/TintPicture.hpp"

struct Dialog_TintPicture : IEventDialogController {
  Dialog_TintPicture() = delete;
  explicit Dialog_TintPicture(const std::string& name, const std::shared_ptr<TintPictureCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new TintPictureCommand());
    }
    m_picture = command->picture;
    r = command->color.r;
    g = command->color.g;
    b = command->color.b;
    gray = command->color.gray;
    m_duration = command->duration;
    m_waitForCompletion = command->waitForCompletion;
    color_picker.setValues(r, g, b, gray);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_picture;
  int r;
  int g;
  int b;
  int gray;
  int m_duration;
  bool m_waitForCompletion;

  bool m_confirmed{false};
  std::shared_ptr<TintPictureCommand> command;
  std::tuple<bool, bool> result;

  ColorTonePicker color_picker;
};


code:
#include "Core/EventCommands/Dialog_TintScreen.hpp"

#include "Database/Database.hpp"
#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_TintScreen::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{400, 250}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Color Tone");
    color_picker.draw();

    ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 4.f);
    ImGui::PushItemWidth(380);
    if (ImGui::BeginCombo("##tintscreen_presets", m_currentTemplate == -1 ? "" : Database::instance()->templates.templates.at(m_currentTemplate).name().c_str())) {
      int index{0};
      for (auto& templ : Database::instance()->templates.templateList(Template::TemplateType::Tint)) {
        if (!templ.parameters().empty()) {
          bool is_selected = m_currentTemplate == index;
          if (ImGui::Selectable(templ.name().c_str(), is_selected)) {
            m_currentTemplate = index;
            color_picker.setValues(templ.parameters().at(0), templ.parameters().at(1), templ.parameters().at(2), templ.parameters().at(3));

            if (is_selected)
              ImGui::SetItemDefaultFocus();
          }
          index++;
        } else {
          if (ImGui::Selectable(("Error loading template: " + templ.name()).c_str(), false)) {}
        }
      }
      ImGui::EndCombo();
    }
    ImGui::SeparatorText("Duration");
    ImGui::SetNextItemWidth(100);
    if (ImGui::InputInt("##tintscreen_duration", &m_duration)) {
      if (m_duration < 1)
        m_duration = 1;
      if (m_duration > 999)
        m_duration = 999;
    }
    ImGui::SameLine();
    ImGui::Text("frames 1/60 sec");
    ImGui::SameLine();
    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 80.f);
    ImGui::Checkbox("Wait for Completion", &m_waitForCompletion);

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->color.r = color_picker.r();
      command->color.g = color_picker.g();
      command->color.b = color_picker.b();
      command->color.gray = color_picker.gray();
      command->duration = m_duration;
      command->waitForCompletion = m_waitForCompletion;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/ColorTonePicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/TintScreen.hpp"
#include <algorithm>

struct Dialog_TintScreen : IEventDialogController {
  Dialog_TintScreen() = delete;
  explicit Dialog_TintScreen(const std::string& name, const std::shared_ptr<TintScreenCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new TintScreenCommand());
    }
    r = command->color.r;
    g = command->color.g;
    b = command->color.b;
    gray = command->color.gray;
    m_duration = command->duration;
    m_waitForCompletion = command->waitForCompletion;

    color_picker.setValues(r, g, b, gray);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int r;
  int g;
  int b;
  int gray;
  int m_duration;
  int m_currentTemplate{-1};
  bool m_waitForCompletion;

  bool m_confirmed{false};
  std::shared_ptr<TintScreenCommand> command;
  std::tuple<bool, bool> result;

  ColorTonePicker color_picker;
};


code:
#include "Core/EventCommands/Dialog_TransferPlayer.hpp"

#include "Core/CommonUI/GroupBox.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Database/Database.hpp"
#include <imgui.h>
#include <imgui_internal.h>

#include <tuple>

std::tuple<bool, bool> Dialog_TransferPlayer::draw() {
  if (isOpen()) {
    ImGui::OpenPopup("###TransferPlayer");
  }
  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  const auto maxSize = ImVec2{(ImGui::CalcTextSize("#").x * 40) + (ImGui::GetStyle().FramePadding.x * 2), (ImGui::GetFrameHeightWithSpacing() * 16) + (ImGui::GetStyle().FramePadding.y * 2)};
  ImGui::SetNextWindowSize(maxSize, ImGuiCond_Appearing);
  ImGui::SetNextWindowSizeConstraints(maxSize, {FLT_MAX, FLT_MAX});

  if (ImGui::BeginPopupModal(std::format("{}###TransferPlayer", m_name).c_str(), &m_open, ImGuiWindowFlags_NoResize)) {
    drawPickers();
    ImGui::BeginVertical("##transfer_player_main_layout", ImGui::GetContentRegionAvail(), 0);
    {
      GroupBox locationGroup(trNOOP("Location"), "##transfer_player_location_group", {-1, 0});
      if (locationGroup.begin()) {
        ImGui::RadioButton(trNOOP("Direct Designation"), &m_mode, 0);
        ImGui::Dummy({ImGui::GetFrameHeight(), ImGui::GetFrameHeight()});
        ImGui::SameLine();
        ImGui::BeginDisabled(m_mode != 0);
        {
          ImGui::PushID("#transfer_coord_selection");
          if (ImGui::EllipsesButton(m_mode == 0 ? std::format("{} ({},{})", Database::instance()->mapNameOrId(m_mapId), m_x, m_y).c_str() : "", ImVec2{-1, 0})) {
            m_eventTilePicker.emplace(Database::instance()->mapInfos.currentMap()->id());
            m_eventTilePicker->setOpen(true);
          }
          ImGui::PopID();
        }
        ImGui::EndDisabled();
        ImGui::RadioButton(trNOOP("Designation with variables"), &m_mode, 1);
        ImGui::BeginDisabled(m_mode != 1);
        {
          ImGui::BeginHorizontal("##transfer_player_location_variables", {-1, 0}, 0);
          {
            ImGui::Dummy({ImGui::GetFrameHeight(), ImGui::GetFrameHeight()});
            ImGui::BeginVertical("##transfer_player_location_variables_inner", {-1, 0}, 0);
            {
              GroupBox mapIdGroup(trNOOP("Map ID"), "##transfer_player_location_variables_ID", {-1, 0});
              if (mapIdGroup.begin()) {
                ImGui::PushID("##transfer_player_variables_mapId");
                if (ImGui::EllipsesButton(m_mode == 1 ? Database::instance()->variableNameAndId(m_mapIdVar).c_str() : "", {-1, 0})) {
                  m_varSelection = 0;
                  m_variablePicker.emplace("Variables", Database::instance()->system.variables(), m_mapIdVar);
                  m_variablePicker->setOpen(true);
                }
                ImGui::PopID();
              }
              mapIdGroup.end();
              GroupBox xPosGroup("X", "##transfer_player_location_variables_x", {-1, 0});
              if (xPosGroup.begin()) {
                ImGui::PushID("##transfer_player_variables_x");
                if (ImGui::EllipsesButton(m_mode == 1 ? Database::instance()->variableNameAndId(m_xVar).c_str() : "", {-1, 0})) {
                  m_varSelection = 1;
                  m_variablePicker.emplace(trNOOP("Variables"), Database::instance()->system.variables(), m_xVar);
                  m_variablePicker->setOpen(true);
                }
                ImGui::PopID();
              }
              xPosGroup.end();
              GroupBox yPosGroup("Y", "##transfer_player_location_variables_y", {-1, 0});
              if (yPosGroup.begin()) {
                ImGui::PushID("##transfer_player_variables_y");
                if (ImGui::EllipsesButton(m_mode == 1 ? Database::instance()->variableNameAndId(m_yVar).c_str() : "", {-1, 0})) {
                  m_varSelection = 2;
                  m_variablePicker.emplace(trNOOP("Variables"), Database::instance()->system.variables(), m_yVar);
                  m_variablePicker->setOpen(true);
                }
                ImGui::PopID();
              }
              yPosGroup.end();
            }
            ImGui::EndVertical();
          }
          ImGui::EndHorizontal();
        }
        ImGui::EndDisabled();
        ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
        GroupBox directionGroup(trNOOP("Direction"), "##transfer_player_direction", {ImGui::GetContentRegionAvail().x * 0.5f, 0});
        if (directionGroup.begin()) {
          ImGui::PushItemWidth(-1);
          if (ImGui::BeginCombo("##direction_selection", DecodeEnumName(magic_enum::enum_cast<Direction>(m_direction).value_or(Direction::Retain)).c_str())) {
            for (auto& direction : magic_enum::enum_values<Direction>()) {
              bool selected = m_direction == static_cast<int>(direction);
              if (ImGui::Selectable(DecodeEnumName(direction).c_str(), selected)) {
                m_direction = static_cast<int>(direction);
              }
              if (selected) {
                ImGui::SetItemDefaultFocus();
              }
            }
            ImGui::EndCombo();
          }
        }
        directionGroup.end();
        ImGui::SameLine();
        GroupBox fadeGroup(trNOOP("Fade"), "##transfer_player_fade", {ImGui::GetContentRegionAvail().x, 0});
        if (fadeGroup.begin()) {
          ImGui::PushItemWidth(-1);
          if (ImGui::BeginCombo("##fade_selection", DecodeEnumName(magic_enum::enum_cast<Fade>(m_fade).value_or(Fade::Black)).c_str())) {
            for (auto& fade : magic_enum::enum_values<Fade>()) {
              bool selected = m_fade == static_cast<int>(fade);
              if (ImGui::Selectable(DecodeEnumName(fade).c_str(), selected)) {
                m_fade = static_cast<int>(fade);
              }
              if (selected) {
                ImGui::SetItemDefaultFocus();
              }
            }
            ImGui::EndCombo();
          }
        }
        fadeGroup.end();
      }
      locationGroup.end();

      ImGui::Spring();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
      ImGui::BeginHorizontal("##transfer_player_button_layout", {-1, 0}, 0);
      {
        ImGui::Spring();

        if (const auto ret = ImGui::ButtonGroup("##transfer_player_buttons", {trNOOP("OK"), trNOOP("Cancel")}); ret == 0) {
          m_confirmed = true;

          m_command->mode = static_cast<TransferMode>(m_mode);
          if (m_command->mode == TransferMode::Variable_Designation) {
            m_command->mapId = m_mapIdVar;
            m_command->x = m_xVar;
            m_command->y = m_yVar;
          } else {
            m_command->mapId = m_mapId;
            m_command->x = m_x;
            m_command->y = m_y;
          }
          m_command->direction = static_cast<Direction>(m_direction);
          m_command->fade = static_cast<Fade>(m_fade);

          ImGui::CloseCurrentPopup();
          setOpen(false);
        } else if (ret == 1) {
          ImGui::CloseCurrentPopup();
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();
    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}

void Dialog_TransferPlayer::drawPickers() {
  if (m_variablePicker) {
    if (const auto [closed, confirmed] = m_variablePicker->draw(); closed) {
      if (confirmed) {
        switch (m_varSelection) {
        case 0:
          m_mapIdVar = m_variablePicker->selection();
          break;
        case 1:
          m_xVar = m_variablePicker->selection();
          break;
        case 2:
          m_yVar = m_variablePicker->selection();
          break;
        default:
          break;
        }
      }
      m_variablePicker.reset();
    }
  }

  if (m_eventTilePicker) {
    if (const auto [closed, confirmed] = m_eventTilePicker->draw(); closed) {
      if (confirmed) {
        m_mapId = m_eventTilePicker->selectedMap();
        m_x = m_eventTilePicker->selectedTile().x();
        m_y = m_eventTilePicker->selectedTile().y();
      }
      m_eventTilePicker.reset();
    }
  }
}


code:
#pragma once
#include "Core/CommonUI/EventTilePicker.hpp"
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/TransferPlayer.hpp"

struct Dialog_TransferPlayer : IEventDialogController {
  Dialog_TransferPlayer() = delete;
  explicit Dialog_TransferPlayer(const std::string& name, const std::shared_ptr<TransferPlayerCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , m_command(cmd) {
    if (cmd == nullptr) {
      m_command.reset(new TransferPlayerCommand());
    }
    m_mode = static_cast<int>(m_command->mode);

    if (m_command->mode == TransferMode::Variable_Designation) {
      m_mapIdVar = m_command->mapId;
      m_xVar = m_command->x;
      m_yVar = m_command->y;
    } else {
      m_mapId = m_command->mapId;
      m_x = m_command->x;
      m_y = m_command->y;
    }
    m_direction = static_cast<int>(m_command->direction);
    m_fade = static_cast<int>(m_command->fade);
  }
  void drawPickers();
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return m_command; };

private:
  int m_mode;
  int m_mapId;
  int m_x;
  int m_y;
  int m_direction;
  int m_fade;

  int m_mapIdVar{1};
  int m_xVar{1};
  int m_yVar{1};
  int m_varSelection;

  bool m_confirmed{false};
  std::shared_ptr<TransferPlayerCommand> m_command;
  std::optional<VariableSwitchPicker> m_variablePicker;
  std::optional<EventTilePicker> m_eventTilePicker;
};


code:
#include "Core/EventCommands/Dialog_Wait.hpp"

#include "Core/CommonUI/GroupBox.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Database/Database.hpp"

#include <imgui.h>
#include <imgui_internal.h>

#include <tuple>

std::tuple<bool, bool> Dialog_Wait::draw() {
  if (isOpen()) {
    ImGui::OpenPopup("###Wait");
  }
  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  const auto maxSize =
      ImVec2{ImGui::CalcTextSize("#############################").x + (ImGui::GetStyle().FramePadding.x * 2), (ImGui::GetFrameHeightWithSpacing() * 5) + (ImGui::GetStyle().FramePadding.y * 2)};
  ImGui::SetNextWindowSize(maxSize, ImGuiCond_Appearing);
  ImGui::SetNextWindowSizeConstraints(maxSize, {FLT_MAX, FLT_MAX});
  if (ImGui::BeginPopupModal(std::format("{}###Wait", m_name).c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::BeginVertical("##wait_main_layout", ImGui::GetContentRegionAvail(), 0);
    {
      GroupBox labelGroupBox(trNOOP("Duration"), "##wait_duration_group", {-1, 0}, nullptr, ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiWindowFlags_AlwaysAutoResize);
      if (labelGroupBox.begin()) {
        ImGui::SetNextItemWidth(-1);
        if (ImGui::SpinInt("##wait_duration_input", &m_waitDuration, 1, 100, trNOOP("%4d frames (1/60 sec)"))) {
          m_waitDuration = std::clamp(m_waitDuration, 1, 253);
        }
      }
      labelGroupBox.end();
      ImGui::Spring();
      ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, ImGui::GetDPIScaledValue(1.5f));
      ImGui::BeginHorizontal("##wait_buttons_layout", {-1, -1}, 0);
      {
        ImGui::Spring();
        ImGui::SetNextItemWidth(-1);
        if (const auto ret = ImGui::ButtonGroup("##wait_buttons", {trNOOP("OK"), trNOOP("Cancel")}); ret == 0) {
          m_confirmed = true;
          m_command->duration = m_waitDuration;
          ImGui::CloseCurrentPopup();
          setOpen(false);
        } else if (ret == 1) {
          ImGui::CloseCurrentPopup();
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();
    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/ObjectPicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/Class.hpp"
#include "Database/EventCommands/ChangeNickname.hpp"
#include "Database/EventCommands/Wait.hpp"

struct Dialog_Wait : IEventDialogController {
  Dialog_Wait() = delete;
  explicit Dialog_Wait(const std::string& name, const std::shared_ptr<WaitCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , m_command(cmd) {
    if (cmd == nullptr) {
      m_command.reset(new WaitCommand());
    }
    m_waitDuration = m_command->duration;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return m_command; };

private:
  int m_waitDuration;
  bool m_confirmed{false};
  std::shared_ptr<WaitCommand> m_command;
};


code:
#pragma once
#include "Core/CommonUI/IDialogController.hpp"
#include "Database/EventCommands/IEventCommand.hpp"
#include <string>

struct IEventDialogController : IDialogController {
  explicit IEventDialogController(const std::string& name)
  : IDialogController(name) {}
  std::tuple<bool, bool> draw() override = 0;
  virtual std::shared_ptr<IEventCommand> getCommand() { return nullptr; }
  virtual std::vector<std::shared_ptr<IEventCommand>> getBatchCommands() { return std::vector<std::shared_ptr<IEventCommand>>{}; }
  virtual std::vector<std::shared_ptr<IEventCommand>> getBatchCommands(std::vector<std::shared_ptr<IEventCommand>>& list) { return std::vector<std::shared_ptr<IEventCommand>>{}; }
  virtual void setParentIndent(int indent) { m_parentIndent.emplace(indent); }
  [[nodiscard]] virtual std::optional<int> getParentIndent() const { return m_parentIndent; }
  virtual std::vector<std::shared_ptr<IEventCommand>> getTemplateCommands(EventCode code, int intParam1 = 0) { return std::vector<std::shared_ptr<IEventCommand>>{}; }
  virtual bool isCurrentElseBranch() { return false; }
  virtual void setCurrentElseBranch() {};

protected:
  std::optional<int> m_parentIndent;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementChangeBlendMode.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementChangeBlendMode::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{105, 95}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Blend");
    ImGui::PushItemWidth((75));
    if (ImGui::BeginCombo("##showpicture_blendmode", DecodeEnumName(magic_enum::enum_value<Blend>(m_blendMode)).c_str())) {
      for (auto& blend : magic_enum::enum_values<Blend>()) {
        bool is_selected = m_blendMode == magic_enum::enum_index(blend).value();
        if (ImGui::Selectable(DecodeEnumName(magic_enum::enum_name(blend)).c_str(), is_selected)) {
          m_blendMode = magic_enum::enum_index(blend).value();
          if (is_selected)
            ImGui::SetItemDefaultFocus();
        }
      }
      ImGui::EndCombo();
    }

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->mode = static_cast<Blend>(m_blendMode);
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/BlendMode.hpp"

struct Dialog_MovementChangeBlendMode : IEventDialogController {
  Dialog_MovementChangeBlendMode() = delete;
  explicit Dialog_MovementChangeBlendMode(const std::string& name, const std::shared_ptr<MovementChangeBlendModeCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementChangeBlendModeCommand());
    }
    m_blendMode = static_cast<int>(command->mode);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_blendMode;
  bool m_confirmed{false};
  std::shared_ptr<MovementChangeBlendModeCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementChangeFrequency.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementChangeFrequency::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{140, 97}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Frequency");
    ImGui::PushItemWidth((100));
    if (ImGui::BeginCombo("##movement_frequency_selection", DecodeEnumName(static_cast<MovementFrequency>(m_frequency)).c_str())) {
      for (auto& freq : magic_enum::enum_values<MovementFrequency>()) {
        bool is_selected = (m_frequency == static_cast<int>(freq));
        if (ImGui::Selectable(DecodeEnumName(freq).c_str(), is_selected)) {
          m_frequency = static_cast<int>(freq);
          if (is_selected)
            ImGui::SetItemDefaultFocus();
        }
      }
      ImGui::EndCombo();
    }

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->frequency = m_frequency;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/Frequency.hpp"

struct MainWindow;
struct Dialog_MovementChangeFrequency : IEventDialogController {
  Dialog_MovementChangeFrequency() = delete;
  explicit Dialog_MovementChangeFrequency(const std::string& name, const std::shared_ptr<MovementFrequencyCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {

      command.reset(new MovementFrequencyCommand());
    }
    m_frequency = command->frequency;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_frequency;
  bool m_confirmed{false};
  std::shared_ptr<MovementFrequencyCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementChangeImage.hpp"
#include "Core/Application.hpp"

#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/Log.hpp"
#include "imgui.h"

#include <tuple>

std::tuple<bool, bool> Dialog_MovementChangeImage::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }

  if (!m_actorButton) {
    m_actorButton.emplace();
    m_actorButton->setSize(80, 102);
  }

  if (!m_actorButton->hasCompositeTextures() && m_characterSheet) {
    auto [min, max] = m_characterSheet.value().getRectForCharacter(m_character, 1);

    m_actorButton->setTexturesToComposite({{m_characterSheet->texture(),
                                            {m_characterSheet->characterWidth(), m_characterSheet->characterHeight()},
                                            {static_cast<int>(min.x() * m_characterSheet->texture().width()), static_cast<int>(min.y() * m_characterSheet->texture().height())}}});
  } else if (!m_characterSheet) {
    m_actorButton->clear();
  }

  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowSize(ImVec2{ImGui::GetDPIScaledValue(200), ImGui::GetDPIScaledValue(200)}, ImGuiCond_Appearing);
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize)) {

    auto [closed, confirmed] = m_characterPicker.draw();
    if (closed) {
      if (confirmed) {
        m_characterPicker.accept();
        m_character = m_characterPicker.character();
        m_image = m_characterPicker.selectedSheet();
        m_characterSheet = CharacterSheet(m_image);
        m_actorButton->clear();
      }
    }
    ImGui::BeginVertical("changeimage_vertical", ImVec2{ImGui::GetDPIScaledValue(200.f), ImGui::GetDPIScaledValue(150.f)});
    {
      ImGui::Spring();
      auto cursorPos = ImGui::GetCursorPos();
      if (ImGui::ImageButton("##event_image", m_actorButton->get(), static_cast<ImVec2>(m_actorButton->size()))) {
        m_characterPicker.setCharacterInfo(m_image, m_character);
        m_characterPicker.setOpen(true);
      }
      // if (m_characterSheet->texture()) {
      //   if (m_characterSheet->characterWidth() < 72 || m_characterSheet->characterHeight() < 96) {
      //     ImGui::SetCursorPos(cursorPos + (ImVec2{m_characterSheet->characterWidth() / 2.f, m_characterSheet->characterHeight() / 2.f}));
      //   } else {
      //     ImGui::SetCursorPos(cursorPos);
      //   }
      //   const auto [min, max] = m_characterSheet->getRectForCharacter(m_character);
      // ImGui::Image(m_characterSheet->texture(), ImVec2{static_cast<float>(m_characterSheet->characterWidth()), static_cast<float>(m_characterSheet->characterHeight())}, min, max);
      //  }
      ImGui::BeginHorizontal(std::format("##buttons_character_picker{}", reinterpret_cast<uintptr_t>(this)).c_str(), ImGui::GetContentRegionAvail());
      {
        ImGui::Spring();
        const int ret = ImGui::ButtonGroup(std::format("##ok_cancel_character_picker{}", reinterpret_cast<uintptr_t>(this)).c_str(), {
                                                                                trNOOP("OK"),
                                                                                trNOOP("Cancel"),
                                                                                });
        if (ret == 0) {
          m_confirmed = true;
          command->image = m_image;
          command->character = m_character;
          ImGui::CloseCurrentPopup();
          setOpen(false);
        } else if (ret == 1) {
          ImGui::CloseCurrentPopup();
          setOpen(false);
        }
      }
      ImGui::EndHorizontal();
    }
    ImGui::EndVertical();

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/CharacterPicker.hpp"
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Core/Graphics/CharacterSheet.hpp"
#include "Core/Graphics/CheckeredCompositeTexture.hpp"
#include "Database/EventCommands/MovementRoute/ChangeImage.hpp"

struct MainWindow;
struct Dialog_MovementChangeImage : IEventDialogController {
  Dialog_MovementChangeImage() = delete;
  explicit Dialog_MovementChangeImage(const std::string& name, const std::shared_ptr<MovementChangeImageCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementChangeImageCommand());
    }
    m_image = command->image;
    m_character = command->character;
    m_characterSheet = CharacterSheet(m_image);
    if (!m_actorButton->hasCompositeTextures() && m_characterSheet) {
      auto [min, max] = m_characterSheet.value().getRectForCharacter(m_character, 1);

      m_actorButton->setTexturesToComposite({{m_characterSheet->texture(),
                                              {m_characterSheet->characterWidth(), m_characterSheet->characterHeight()},
                                              {static_cast<int>(min.x() * m_characterSheet->texture().width()), static_cast<int>(min.y() * m_characterSheet->texture().height())}}});
    }
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  std::string m_image;
  int m_character{0};

  std::optional<CharacterSheet> m_characterSheet;
  CheckerboardTexture m_buttonBack{80, 102, CellSizes::_64, 255, 200};
  CharacterPicker m_characterPicker;
  std::optional<CheckeredCompositeTexture> m_actorButton;

  bool m_confirmed{false};
  std::shared_ptr<MovementChangeImageCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementChangeOpacity.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementChangeOpacity::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{135, 95}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Opacity");
    ImGui::SetNextItemWidth(100);
    if (ImGui::InputInt("##movement_opacity_input", &m_opacity)) {
      if (m_opacity < 0)
        m_opacity = 0;
      if (m_opacity > 255)
        m_opacity = 255;
    }

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->opacity = m_opacity;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/ChangeOpacity.hpp"
#include "Database/EventCommands/MovementRoute/Wait.hpp"

struct MainWindow;
struct Dialog_MovementChangeOpacity : IEventDialogController {
  Dialog_MovementChangeOpacity() = delete;
  explicit Dialog_MovementChangeOpacity(const std::string& name, const std::shared_ptr<MovementChangeOpacityCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementChangeOpacityCommand());
    }
    m_opacity = command->opacity;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_opacity;
  bool m_confirmed{false};
  std::shared_ptr<MovementChangeOpacityCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementChangeSpeed.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementChangeSpeed::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{140, 97}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Speed");
    ImGui::PushItemWidth((100));

    if (ImGui::BeginCombo("##movement_frequency_selection", DecodeEnumName(static_cast<MovementSpeed>(m_speed)).c_str())) {
      for (auto& speed : magic_enum::enum_values<MovementSpeed>()) {
        bool is_selected = (m_speed == static_cast<int>(speed));
        if (ImGui::Selectable(DecodeEnumName(speed).c_str(), is_selected)) {
          m_speed = static_cast<int>(speed);
          if (is_selected)
            ImGui::SetItemDefaultFocus();
        }
      }
      ImGui::EndCombo();
    }

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->speed = m_speed;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/Speed.hpp"
struct MainWindow;
struct Dialog_MovementChangeSpeed : IEventDialogController {
  Dialog_MovementChangeSpeed() = delete;
  explicit Dialog_MovementChangeSpeed(const std::string& name, const std::shared_ptr<MovementSpeedCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementSpeedCommand());
    }
    m_speed = command->speed;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_speed;
  bool m_confirmed{false};
  std::shared_ptr<MovementSpeedCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementJump.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementJump::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{235, 110}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::SeparatorText("Offset");
    ImGui::BeginGroup();
    {
      ImGui::Text("X:");
      ImGui::SetNextItemWidth(100);
      if (ImGui::InputInt("##moveroute_jump_x", &m_jump_x)) {
        if (m_jump_x < -99)
          m_jump_x = -99;
        if (m_jump_x > 999)
          m_jump_x = 999;
      }
      ImGui::EndGroup();
    }
    ImGui::SameLine();
    ImGui::SetCursorPosY(ImGui::GetCursorPos().y - 3.f);
    ImGui::BeginGroup();
    {
      ImGui::Text("Y:");
      ImGui::SetNextItemWidth(100);
      if (ImGui::InputInt("##moveroute_jump_y", &m_jump_y)) {
        if (m_jump_x < -99)
          m_jump_x = -99;
        if (m_jump_x > 999)
          m_jump_x = 999;
      }
      ImGui::EndGroup();
    }
    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->x = m_jump_x;
      command->y = m_jump_y;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/Jump.hpp"

struct MainWindow;
struct Dialog_MovementJump : IEventDialogController {
  Dialog_MovementJump() = delete;
  explicit Dialog_MovementJump(const std::string& name, const std::shared_ptr<MovementJumpCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementJumpCommand());
    }
    m_jump_x = command->x;
    m_jump_y = command->y;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_jump_x;
  int m_jump_y;
  bool m_confirmed{false};
  std::shared_ptr<MovementJumpCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementScript.hpp"
#include "Core/Application.hpp"

#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/Log.hpp"
#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementScript::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
    // SetOpen(false);
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{ImGui::GetDPIScaledValue(1400), 600}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open)) {

    static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
    ImGui::PushFont(App::APP->getMonoFont());
    m_textEditor.Render("##no_title", ImVec2{ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x, ImGui::GetContentRegionAvail().y - 32 - ImGui::GetStyle().FramePadding.y});
    ImGui::PopFont();
    if (ImGui::Button("OK")) {
      auto script = m_textEditor.GetText();
      std::vector<std::string> scripts = splitString(script, '\n');

      command->script = scripts.front();
      ImGui::CloseCurrentPopup();
      setOpen(false);
      m_confirmed = true;
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/Script.hpp"

struct MainWindow;
struct Dialog_MovementScript : IEventDialogController {
  Dialog_MovementScript() = delete;
  explicit Dialog_MovementScript(const std::string& name, const std::shared_ptr<MovementScriptCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementScriptCommand());
    }
    m_Text = command->script;
    m_textEditor.SetLanguageDefinition(TextEditor::LanguageDefinition::Javascript());
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  TextEditor m_textEditor;
  std::string m_Text;
  bool m_confirmed{false};
  std::shared_ptr<MovementScriptCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementSwitchOFF.hpp"
#include "Core/Log.hpp"

std::tuple<bool, bool> Dialog_MovementSwitchOFF::draw() {

  if (picker) {
    auto [closed, confirmed] = picker->draw();
    if (closed) {
      if (confirmed) {
        m_open = closed;
        m_confirmed = confirmed;
        command->id = picker->selection();
      }
      picker.reset();
    }
  }
  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/Database.hpp"
#include "Database/EventCommands/MovementRoute/SwitchOFF.hpp"

struct MainWindow;
struct Dialog_MovementSwitchOFF : IEventDialogController {
  Dialog_MovementSwitchOFF() = delete;
  explicit Dialog_MovementSwitchOFF(const std::string& name, const std::shared_ptr<MovementSwitchOFFCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new MovementSwitchOFFCommand());
    }
    picker.emplace("Switches", Database::instance()->system.switches(), command->id);
    picker->setOpen(true);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

  MainWindow* m_project = nullptr;

private:
  bool m_confirmed{false};
  std::shared_ptr<MovementSwitchOFFCommand> command;
  std::optional<VariableSwitchPicker> picker;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementSwitchON.hpp"
#include "Core/Log.hpp"

std::tuple<bool, bool> Dialog_MovementSwitchON::draw() {

  if (picker) {
    auto [closed, confirmed] = picker->draw();
    if (closed) {
      if (confirmed) {
        m_open = closed;
        m_confirmed = confirmed;
        command->id = picker->selection();
      }
      picker.reset();
    }
  }
  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/VariableSwitchPicker.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/Database.hpp"
#include "Database/EventCommands/MovementRoute/SwitchON.hpp"

struct MainWindow;
struct Dialog_MovementSwitchON : IEventDialogController {
  Dialog_MovementSwitchON() = delete;
  explicit Dialog_MovementSwitchON(const std::string& name, const std::shared_ptr<MovementSwitchONCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new MovementSwitchONCommand());
    }
    picker.emplace("Switches", Database::instance()->system.switches(), command->id);
    picker->setOpen(true);
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

  MainWindow* m_project = nullptr;

private:
  bool m_confirmed{false};
  std::shared_ptr<MovementSwitchONCommand> command;
  std::optional<VariableSwitchPicker> picker;
  std::tuple<bool, bool> result;
};


code:
#include "Core/EventCommands/MovementRoute/Dialog_MovementWait.hpp"
#include "Core/Application.hpp"

#include "imgui.h"
#include <tuple>

std::tuple<bool, bool> Dialog_MovementWait::draw() {
  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{221, 97}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {

    ImGui::SeparatorText("Duration");
    ImGui::SetNextItemWidth(100);
    if (ImGui::InputInt("##movement_wait_input", &m_waitDuration)) {
      if (m_waitDuration < 1)
        m_waitDuration = 1;
      if (m_waitDuration > 999)
        m_waitDuration = 999;
    }
    ImGui::SameLine();
    ImGui::Text("frames 1/60 sec");

    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->duration = m_waitDuration;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }

    ImGui::EndPopup();
  }

  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/TextEditor.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Database/EventCommands/MovementRoute/Wait.hpp"

struct MainWindow;
struct Dialog_MovementWait : IEventDialogController {
  Dialog_MovementWait() = delete;
  explicit Dialog_MovementWait(const std::string& name, const std::shared_ptr<MovementWaitCommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (command == nullptr) {
      command.reset(new MovementWaitCommand());
    }
    m_waitDuration = command->duration;
  }
  std::tuple<bool, bool> draw() override;

  std::shared_ptr<IEventCommand> getCommand() override { return command; };

private:
  int m_waitDuration;
  bool m_confirmed{false};
  std::shared_ptr<MovementWaitCommand> command;
  std::tuple<bool, bool> result;
};


code:
#include "Dialogue_MovementPlaySE.hpp"
#include "Core/ImGuiExt/imgui-knobs.h"
#include "Core/ImGuiExt/ImGuiUtils.hpp"
#include "Core/Log.hpp"
#include "imgui.h"

#include "imgui_internal.h"
#include <IconsFontAwesome6.h>

using namespace std::string_view_literals;
std::tuple<bool, bool> Dialog_MovementPlaySE::draw() {

  if (isOpen()) {
    ImGui::OpenPopup(m_name.c_str());
  }
  ImVec2 center = ImGui::GetMainViewport()->GetCenter();
  ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImVec2{610, 380}, ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal(m_name.c_str(), &m_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize)) {
    ImGui::BeginGroup();
    {

      // Audio List
      ImVec2 tablePos = ImGui::GetCursorPos();
      if (ImGui::BeginTable("##se_audio_contents", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY, ImVec2{500, 555})) {

        ImGui::TableSetupScrollFreeze(1, 0);
        ImGui::TableSetupColumn("File");
        ImGui::TableNextRow();
        ImGui::TableNextColumn();

        ImGui::BeginDisabled(m_audioDir.value().isParentDirectory());
        if (ImGui::Selectable("\u21B0 ..", false, static_cast<int>(ImGuiSelectableFlags_SelectOnNav) | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
          if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
            m_audioDir.value().moveUp();
            m_audios = m_audioDir->getDirectoryContents();
            m_folders = m_audioDir.value().getDirectories();
            m_selected = 0;
            m_selectedFolder = -1;
          }
        }
        ImGui::EndDisabled();
        for (int i = 0; i < m_folders.size(); ++i) {
          const auto& folderName = std::format("{} {}", ICON_FA_FOLDER_OPEN, m_folders[i]);
          if (ImGui::Selectable(folderName.c_str(), m_selectedFolder == i, static_cast<int>(ImGuiSelectableFlags_SelectOnNav) | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
            m_selectedFolder = i;
          }
          if (m_selectedFolder == i && ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
            m_audioDir->setDirectory(i);
            m_folders = m_audioDir.value().getDirectories();
            m_audios = m_audioDir.value().getDirectoryContents();
            m_selected = 0;
            m_selectedFolder = -1;
          }
        }

        if (ImGui::SelectableWithBorder("None", m_selected == 0, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {
          m_selected = 0;
          m_audio.setName("");
        }

        for (int n = 0; n < m_audios.size(); n++) {
          ImGui::TableNextColumn();
          const bool isSelected = (m_selected == n + 1);
          if (ImGui::SelectableWithBorder(m_audios.at(n).c_str(), isSelected, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {

            m_selected = n + 1;
            m_audio.setName(m_audioDir.value().isParentDirectory() ? m_audios.at(m_selected - 1) : m_audioDir.value().getPathPrefix() + '/' + m_audios.at(m_selected - 1));
            if (ImGui::GetMouseClickedCount(ImGuiMouseButton_Left) >= 2) {
              playAudio(m_audio.name().c_str());
            }
            if (isSelected)
              ImGui::SetItemDefaultFocus();
          }
        }
        ImGui::EndTable();
      }
    }
    ImGui::EndGroup();
    ImGui::SameLine();
    ImGui::BeginGroup();
    {
      if (ImGui::Button("Play", ImVec2(100, 0))) {
        if (m_selected > 0)
          playAudio(m_audio.name().c_str());
      }
      if (ImGui::Button("Stop", ImVec2(100, 0))) {
        stopAudio();
      }

      ImGui::Separator();
      int volume = m_audio.volume();
      if (ImGuiKnobs::KnobInt("Volume", &volume, 0, 100, 1, "%d%", ImGuiKnobVariant_WiperDot, 200.f, ImGuiKnobFlags_AlwaysClamp)) {
        m_audio.setVolume(volume);
        setVolume(m_audio.volume());
      }
      ImGui::Separator();
      int pitch = m_audio.pitch();
      if (ImGuiKnobs::KnobInt("Pitch", &pitch, 50, 150, 1, "%d%", ImGuiKnobVariant_WiperDot, 200.f, ImGuiKnobFlags_AlwaysClamp)) {
        m_audio.setPitch(pitch);
        setPitch(m_audio.pitch());
      }
      ImGui::Separator();
      int pan = m_audio.pan();
      if (ImGuiKnobs::KnobInt("Pan", &pan, -100, 100, 1, "%d%", ImGuiKnobVariant_WiperDot, 200.f, ImGuiKnobFlags_AlwaysClamp)) {
        m_audio.setPan(pan);
        setPanning(m_audio.pan());
      }
    }

    ImGui::EndGroup();
    if (ImGui::Button("OK")) {
      m_confirmed = true;
      command->se = m_audio;
      ImGui::CloseCurrentPopup();
      setOpen(false);
    }
    ImGui::SameLine();
    if (ImGui::Button("Cancel")) {
      setOpen(false);
    }
    ImGui::EndPopup();
  }
  return std::make_tuple(!m_open, m_confirmed);
}


code:
#pragma once
#include "Core/CommonUI/Directory.hpp"
#include "Core/EventCommands/IEventDialogController.hpp"
#include "Core/Log.hpp"
#include "Core/ResourceManager.hpp"
#include "Core/Settings.hpp"
#include "Core/Sound.hpp"
#include "Database/Database.hpp"
#include "Database/EventCommands/MovementRoute/PlaySE.hpp"

#include <iostream>
#include <SFML/Audio.hpp>

namespace fs = std::filesystem;
struct Dialog_MovementPlaySE : IEventDialogController {
  Dialog_MovementPlaySE() = delete;
  explicit Dialog_MovementPlaySE(const std::string& name, const std::shared_ptr<MovementPlaySECommand>& cmd = nullptr)
  : IEventDialogController(name)
  , command(cmd) {
    if (cmd == nullptr) {
      command.reset(new MovementPlaySECommand());
    }
    m_audio = command->se;
    m_audioDir.emplace("audio/se", ".ogg", m_audio.name());
    m_audios = m_audioDir.value().getDirectoryContents();
    m_folders = m_audioDir.value().getDirectories();

    std::string audioName = m_audioDir->getFileName(m_audio.name());
    for (int i = 0; i < m_audios.size(); ++i) {
      if (audioName == m_audios[i]) {
        m_selected = i + 1;
      }
    }
  }
  std::tuple<bool, bool> draw() override;
  [[nodiscard]] std::shared_ptr<IEventCommand> getCommand() override { return command; }

private:
  bool m_confirmed{false};

  int m_selected = 0;
  Audio m_audio;

  Sound m_sound;

  int m_selectedFolder{-1};
  std::optional<Directory> m_audioDir;
  std::vector<std::string> m_folders;

  std::shared_ptr<MovementPlaySECommand> command;
  std::tuple<bool, bool> result;
  std::vector<std::string> m_audios;

  bool playAudio(const std::string& path) {
    // Load and play music
    m_sound = Sound(ResourceManager::instance()->loadSE(path));
    setVolume(m_audio.volume());
    setPanning(m_audio.pan());
    setPitch(m_audio.pitch());
    m_sound.play();
    return true;
  }
  void setVolume(int volume) {
    m_sound.setVolume(static_cast<float>(volume)); // 0% to 100%
  }
  void setPanning(int value) { m_sound.setPan(static_cast<float>(value) / 100.0f); }
  void setPitch(int value) { m_sound.setPitch(value / 100.f); }
  void stopAudio() { m_sound.stop(); }
};


code:
#include "Core/EventEditor.hpp"

#include "Core/ImGuiExt/ImGuiNotify.hpp"
#include "Core/ImGuiExt/ImGuiUtils.hpp"

#include "Database/Event.hpp"
#include "Database/EventCommands/EventDummy.hpp"
#include "Database/EventPage.hpp"
#include "Database/EventParser.hpp"

#include <format>
#include <string>

#include "Database/Database.hpp"
#include "imgui.h"
#include "imgui_internal.h"
#include <clip.h>

static clip::format OREEventPageFormat = -1;
std::tuple<bool, bool> EventEditor::draw() {
  if (OREEventPageFormat == -1) {
    OREEventPageFormat = clip::register_format("application/ore-EventPage");
  }

  if (!m_event || !m_open) {
    return {!m_open, m_confirmed};
  }
  std::string title = std::format("{}###event_editor_{}_{}", trFormat("Event {} - ID {}", m_event->name(), m_event->id()), Database::instance()->mapInfos.currentMap()->id(), m_event->id());
  ImGui::SetNextWindowSize(ImGui::GetDPIScaledSize(2048, 1280), ImGuiCond_Once);
  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Once, {0.5f, 0.5f});
  if (ImGui::Begin(title.c_str(), &m_open)) {
    ImGui::BeginGroup();
    {
      char tmpName[4096];
      float oldY = ImGui::GetCursorPosY();
      strncpy(tmpName, m_event->name().c_str(), 4096);
      if (ImGui::LabelOverLineEdit("##orpg_event_editor_event_name", "Name:", tmpName, 4096, (200), nullptr, ImGuiInputTextFlags_None)) {
        m_event->setName(tmpName);
      }
      ImGui::SameLine();
      ImGui::SetCursorPosY(oldY - 4);
      strncpy(tmpName, m_event->note().c_str(), 4096);
      if (ImGui::LabelOverLineEdit("##orpg_event_editor_event_note", "Note:", tmpName, 4096, (200))) {
        m_event->setNote(tmpName);
      }
    }
    ImGui::EndGroup();
    ImGui::SameLine();
    ImGui::BeginGroup();
    {
      if (ImGui::Button(trNOOP("Copy\nEvent Page"))) {
        clip::lock l;
        nlohmann::ordered_json pageJson = *m_event->page(m_selectedPage);
        auto v = pageJson.dump();
        l.set_data(OREEventPageFormat, v.data(), v.size());
        m_hasFormat = true;
      }
      ImGui::SameLine();
      /* TODO: Undo/Clipboard stack */
      ImGui::BeginDisabled(!m_hasFormat);
      if (ImGui::Button(trNOOP("Paste\nEvent Page"))) {
        clip::lock l;
        if (l.is_convertible(OREEventPageFormat)) {
          auto len = l.get_data_length(OREEventPageFormat);
          std::string page;
          page.resize(len);
          if (l.get_data(OREEventPageFormat, page.data(), len)) {
            nlohmann::ordered_json pageJson = nlohmann::ordered_json::parse(page);
            EventPage pastedPage = pageJson.get<EventPage>();
            m_event->addPage(pastedPage);
          }
        }
        m_hasFormat = false;
      }
      ImGui::EndDisabled();
      ImGui::SameLine();
      if (ImGui::Button(trNOOP("Save as\nTemplate"))) {
        m_templatePicker = ObjectPicker(tr("Templates"), Database::instance()->templates.templateList(Template::TemplateType::Event), 0);
      }
      ImGui::SameLine();
      if (ImGui::Button(trNOOP("Insert\n Locale Keys"))) {
        m_maxLocaleLines = 0;
        for (auto& cmd : m_event->page(m_selectedPage)->list()) {
          if (cmd->hasStringReference("{}", SearchType::Text) && cmd->code() != EventCode::When_Selected) {
            m_maxLocaleLines++;
          }
        }
        ImGui::OpenPopup("Localization");
      }
      if (ImGui::IsItemHovered()) {
        ImGui::BeginTooltip();
        {
          ImGui::TextUnformatted(trNOOP(
              "Inserts string keys into all ShowText or ShowChoice commands on the page.\n- Keys must be seperated by line.\n- Key total must equal dialogue total.\nThis feature is temporary until "
              "a proper feature is introduced."));
          ImGui::EndTooltip();
        }
      }
    }
    if (m_templatePicker) {
      auto [closed, confirmed] = m_templatePicker->draw();
      if (closed) {
        if (confirmed) {
          nlohmann::ordered_json eventJson;
          EventParser parser;
          EventParser::serialize(eventJson, *m_event);
          if (m_templatePicker.value().selection() == 0) {
            Database::instance()->templates.addTemplate(Template(Database::instance()->templates.templates.size() + 1,
                                                                 tr("New Event Template") + " " + std::to_string(Database::instance()->templates.templates.size() + 1), "",
                                                                 Template::TemplateType::Event, eventJson.dump(), {}));
          } else {
            Database::instance()->templates.templates.at(m_templatePicker.value().selection() - 1).setCommands(eventJson.dump());
          }
          if (Database::instance()->templates.serialize(Database::instance()->basePath + "data/Templates.json")) {
            ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Success, trNOOP("Saved event as template successfully!")});
          } else {
            ImGui::InsertNotification(ImGuiToast{ImGuiToastType::Error, trNOOP("Failed to saved event as template!")});
          }
          Database::instance()->templates.templates.back().setCommands(eventJson.dump());
        }
        m_templatePicker.reset();
      }
    }
    ImGui::EndGroup();
    ImGui::BeginGroup();
    if (ImGui::BeginTabBar("##orpg_event_editor_page", ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_TabListPopupButton | ImGuiTabBarFlags_FittingPolicyScroll)) {
      int erasedIdx = 0;
      bool erased = false;
      for (int i = 0; i < m_event->pageCount(); ++i) {
        if (!m_event->page(i)) {
          continue;
        }
        if (i == m_selectedPage && !m_event->page(i)->editor()->isSelected()) {
          m_event->page(i)->editor()->select();
        }
        auto [del, selected] = m_event->page(i)->editor()->draw(m_event->pageCount() > 1, i);

        if (del) {
          erasedIdx = i;
          erased = true;
        }

        if (selected && !del) {
          m_selectedPage = i;
          m_event->renderer()->setPage(i);
        }
      }
      const auto curTabBar = ImGui::GetCurrentTabBar();
      std::vector<std::pair<int, int>> reorderTable;
      auto& pages = m_event->pages();
      int selectedPageId = static_cast<EVPage*>(m_event->page(m_selectedPage)->editor())->tabItem()->ID;
      for (int i = 0; i < pages.size(); i++) {
        auto pageEditor = static_cast<EVPage*>(m_event->page(i)->editor());
        for (int j = 0; j < curTabBar->Tabs.size(); j++) {
          int id = curTabBar->Tabs[j].ID == curTabBar->ReorderRequestTabId ? curTabBar->Tabs[curTabBar->Tabs[j].IndexDuringLayout].ID : curTabBar->Tabs[j].ID;
          if (pageEditor->tabItem()->ID == id) {
            if (selectedPageId == pageEditor->tabItem()->ID) {
              m_selectedPage = j;
            }
            reorderTable.emplace_back(id, j);
            break;
          }
        }
      }

      if (!std::ranges::is_sorted(reorderTable, [](const auto& a, const auto& b) { return a.second < b.second; })) {
        std::ranges::sort(pages, [&reorderTable](const EventPage& a, const EventPage& b) {
          auto itA = std::ranges::find_if(reorderTable, [&a](const std::pair<int, int>& a2) { return a2.first == static_cast<EVPage*>(a.editor())->tabItem()->ID; });
          auto itB = std::ranges::find_if(reorderTable, [&b](const std::pair<int, int>& a2) { return a2.first == static_cast<EVPage*>(b.editor())->tabItem()->ID; });
          return itA->second < itB->second;
        });
        m_event->renderer()->setPage(m_selectedPage);
        // We need to let the event know it's been modified since we did it ourselves.
        m_event->setModified();
      }

      if (ImGui::TabItemButton(" + ", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoReorder)) {
        m_event->addPage({});
        m_selectedPage = m_event->pageCount() - 1;
        m_event->renderer()->setPage(m_selectedPage);
      }
      if (erased) {
        m_event->removePage(erasedIdx);
        if (erasedIdx == m_selectedPage) {
          m_selectedPage--;
          if (m_selectedPage >= 0) {
            m_event->renderer()->setPage(m_selectedPage);
          } else {
            m_selectedPage = 0;
            m_event->renderer()->setPage(0);
          }
        } else if (m_selectedPage > erasedIdx) {
          m_selectedPage--;
          m_event->renderer()->setPage(m_selectedPage);
        }
      }
      ImGui::EndTabBar();
    }
    ImGui::EndGroup();
    drawLocalization();
  }
  ImGui::End();

  return {!m_open, m_confirmed};
}
void EventEditor::drawLocalization() {
  ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
  ImGui::SetNextWindowSize(ImGui::GetDPIScaledSize(750, 650), ImGuiCond_Appearing);
  if (ImGui::BeginPopupModal("Localization", NULL, ImGuiWindowFlags_NoScrollbar)) {
    ImGui::TextUnformatted(m_maxLocaleLines == 0 ? trNOOP("No text found") : std::format("{} / {} keys required", m_localeLinesRequired, m_maxLocaleLines).c_str());
    if (ImGui::InputTextMultiline("##orpg_eventeditor_localization_multiline", &m_localizationInput,
                                  ImVec2{ImGui::GetContentRegionAvail().x - ImGui::GetStyle().FramePadding.x, ImGui::GetContentRegionAvail().y - ImGui::GetStyle().FramePadding.y * 5}))

    {
      int count = 0;
      for (char c : m_localizationInput) {
        if (c == '\n') {
          ++count;
        }
      }
      m_localeLinesRequired = count;
    }
    ImGui::BeginGroup();
    {
      ImGui::BeginDisabled(m_localeLinesRequired > m_maxLocaleLines || m_localeLinesRequired < m_maxLocaleLines);
      if (ImGui::Button("OK")) {
        m_localeConfirm = true;
        int index{0};

        int mapId = Database::instance()->mapInfos.currentMap()->id();
        int eventId = id();
        int pageId = m_selectedPage + 1;

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

        std::vector<std::string> lines = splitString(m_localizationInput, '\n');
        std::vector<std::string> choiceLines;

        int localeIndex{1};
        for (auto& line : lines) {
          for (int i = 0; i < Database::instance()->locales.locales.size(); i++) {
            if (Database::instance()->locales.locales.at(i).first == trim(line)) {
              Database::instance()->locales.locales.at(i).first = std::format("Map{}-EV{}-Page{}-{}", mapId, eventId, pageId, localeIndex);
              localeIndex++;
            }
          }
        }

        for (auto& cmd : m_event->page(m_selectedPage)->list()) {
          if (cmd->code() == EventCode::Show_Choices) {
            if (cmd->hasStringReference("{}", SearchType::Text)) {
              if (!m_choiceParsing) {
                m_choiceParsing = true;
              }

              std::shared_ptr<ShowChoiceCommand> choiceCmd = std::static_pointer_cast<ShowChoiceCommand>(cmd);
              int showChoiceIndex{0};
              for (auto& choiceStr : choiceCmd->choices) {
                // choiceCmd->choices.at(setStringReference("{}", lines.at(index), SearchType::Text);
                int idx = stoi(lines.at(index));
                choiceCmd->choices.at(showChoiceIndex) = "{" + trim(Database::instance()->locales.locales.at(idx).first) + "}";
                if (m_choiceParsing) {
                  choiceLines.insert(choiceLines.begin() + showChoiceIndex, choiceStr);
                } else {
                  choiceLines.push_back(choiceStr);
                }
                showChoiceIndex++;
                index++;
              }
            }
          } else if (cmd->code() == EventCode::Show_Text) {
            if (cmd->hasStringReference("{}", SearchType::Text)) {
              int idx = stoi(lines.at(index));
              if (idx < Database::instance()->locales.locales.size()) {
                cmd->setStringReference("{}", "{" + trim(Database::instance()->locales.locales.at(idx).first) + "}", SearchType::Text);
              }
              index++;
            }
          }
          if (cmd->code() == EventCode::When_Selected) {
            if (cmd->hasStringReference("{}", SearchType::Text)) {
              cmd->setStringReference("", choiceLines.at(0), SearchType::Text);
              choiceLines.erase(choiceLines.begin(), choiceLines.begin() + 1);
            }
          }
          if (cmd->code() == EventCode::End_del_ShowChoices && m_choiceParsing) {
            m_choiceParsing = false;
          }
        }
        m_choiceParsing = false;
        m_maxLocaleLines = 0;
        m_localeLinesRequired = 0;

        Database::instance()->locales.saveCurrentLocale();
        m_localizationInput.clear();
        ImGui::CloseCurrentPopup();
      }
      ImGui::EndDisabled();
      ImGui::SameLine();
      if (ImGui::Button("Cancel")) {
        m_localeConfirm = false;
        m_localizationInput.clear();
        m_maxLocaleLines = 0;
        m_localeLinesRequired = 0;
        ImGui::CloseCurrentPopup();
      }
      ImGui::EndGroup();
    }
    ImGui::EndPopup();
  }
}
IEventEditor* IEventEditor::create(Event* ev) { return new EventEditor(ev); }


code:
#pragma once

#include "Core/CommonUI/ObjectPicker.hpp"
#include "Core/EventEditor/EVPage.hpp"
#include "Database/Event.hpp"
#include "Database/IEventEditor.hpp"
#include "Database/Template.hpp"

#include <vector>

struct MainWindow;

class EventEditor final : public IEventEditor {
public:
  bool isOpen() const override { return m_open; }
  void open() override { m_open = true; };
  bool confirmed() const override { return m_confirmed; }

  void accept() const override {
    if (m_event) {
      m_event->acceptChanges();
    }
  }

  int getSelectedPage() const override { return m_selectedPage; }

  std::tuple<bool, bool> draw() override;

  void drawLocalization();

protected:
  friend IEventEditor* IEventEditor::create(Event* ev);

  explicit EventEditor(Event* event)
  : IEventEditor(event) {}

  void eventPointerInvalidated() override {};

private:
  std::optional<ObjectPicker<Template>> m_templatePicker;

  std::string m_localizationInput;
  int m_localeLinesRequired{0};
  int m_maxLocaleLines{10};
  bool m_localeConfirm{false};
  bool m_choiceParsing{true};

  int m_selectedPage = 0;
  bool m_open{false};
  bool m_confirmed{false};
  bool m_doSwap{false};
  bool m_hasFormat{false};
  int m_swapTarget{-1};
  int m_swapDestination{-1};
};


code:
//
// Created by antidote on 4/11/25.
//

#include "ConditionsGroup.hpp"


code:
#pragma once

class ConditionsGroup : public IWidget {
public:

  void draw();
private:
};