8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
16#include "imgui/imgui.h"
35 float item_width = 0.0f;
36 float item_height = 0.0f;
38 float row_start_x = 0.0f;
42 const float font_size = ImGui::GetFontSize();
43 if (font_size <= 0.0f) {
50 float max_width,
float min_height,
51 float max_height,
float preferred_width,
52 float aspect_ratio,
float spacing) {
54 layout.spacing = spacing;
55 const auto width_for_columns = [avail_width, spacing](
int columns) {
56 return (avail_width - spacing *
static_cast<float>(columns - 1)) /
57 static_cast<float>(columns);
60 layout.columns = std::max(1,
static_cast<int>((avail_width + spacing) /
61 (preferred_width + spacing)));
63 layout.item_width = width_for_columns(layout.columns);
64 while (layout.columns > 1 && layout.item_width < min_width) {
66 layout.item_width = width_for_columns(layout.columns);
69 layout.item_width = std::min(layout.item_width, max_width);
70 layout.item_width = std::min(layout.item_width, avail_width);
72 std::clamp(layout.item_width * aspect_ratio, min_height, max_height);
74 const float row_width =
75 layout.item_width *
static_cast<float>(layout.columns) +
76 spacing *
static_cast<float>(layout.columns - 1);
77 layout.row_start_x = ImGui::GetCursorPosX();
78 if (row_width < avail_width) {
79 layout.row_start_x += (avail_width - row_width) * 0.5f;
85ImVec4
ScaleColor(
const ImVec4& color,
float scale,
float alpha) {
86 return ImVec4(color.x * scale, color.y * scale, color.z * scale, alpha);
89ImVec4
ScaleColor(
const ImVec4& color,
float scale) {
93ImVec4
WithAlpha(ImVec4 color,
float alpha) {
138 "Edit overworld maps, entrances, and properties",
139 absl::StrFormat(
"%s+1", ctrl),
false,
true},
142 "Design dungeon rooms, layouts, and mechanics",
143 absl::StrFormat(
"%s+2", ctrl),
false,
true},
146 "Modify tiles, palettes, and graphics sets",
147 absl::StrFormat(
"%s+3", ctrl),
false,
true},
150 "Edit sprite graphics and properties", absl::StrFormat(
"%s+4", ctrl),
154 "Edit dialogue, signs, and text", absl::StrFormat(
"%s+5", ctrl),
false,
158 "Configure music and sound effects", absl::StrFormat(
"%s+6", ctrl),
162 "Edit color palettes and animations", absl::StrFormat(
"%s+7", ctrl),
166 "Edit title screen and ending screens", absl::StrFormat(
"%s+8", ctrl),
170 "Write and edit assembly code", absl::StrFormat(
"%s+9", ctrl),
false,
174 "Direct ROM memory editing and comparison",
175 absl::StrFormat(
"%s+0", ctrl),
false,
true},
178 "Test and debug your ROM in real-time with live debugging",
179 absl::StrFormat(
"%s+Shift+E", ctrl),
false,
true},
182 "Configure AI agent, collaboration, and automation",
183 absl::StrFormat(
"%s+Shift+A", ctrl),
false,
false},
191 if (p_open && *p_open && !
is_open_) {
201 bool editor_selected =
false;
202 bool* window_open = p_open ? p_open : &
is_open_;
206 ImGuiViewport* viewport = ImGui::GetMainViewport();
207 ImVec2 center = viewport->GetCenter();
208 ImVec2 view_size = viewport->WorkSize;
209 float target_width = std::clamp(view_size.x * 0.9f, 520.0f, 980.0f);
210 float target_height = std::clamp(view_size.y * 0.88f, 420.0f, 760.0f);
211 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
212 ImGui::SetNextWindowSize(ImVec2(target_width, target_height),
213 ImGuiCond_Appearing);
214 ImGui::SetNextWindowSizeConstraints(
215 ImVec2(420.0f, 360.0f),
216 ImVec2(view_size.x * 0.98f, view_size.y * 0.95f));
218 if (ImGui::Begin(
"Editor Selection", window_open,
219 ImGuiWindowFlags_NoCollapse)) {
237 const float compact_scale =
238 ImGui::GetContentRegionAvail().x < 620.0f ? 0.85f : 1.0f;
239 const float min_width =
244 const float min_height =
249 const float spacing = ImGui::GetStyle().ItemSpacing.x;
250 const float aspect_ratio = min_height / std::max(min_width, 1.0f);
252 ImGui::GetContentRegionAvail().x, min_width, max_width, min_height,
253 max_height, min_width, aspect_ratio, spacing);
256 for (
size_t i = 0; i <
editors_.size(); ++i) {
258 ImGui::SetCursorPosX(layout.row_start_x);
263 ImVec2(layout.item_width, layout.item_height));
267 editor_selected =
true;
280 if (column < layout.columns) {
281 ImGui::SameLine(0.0f, layout.spacing);
295 if (p_open && !(*p_open)) {
302 return editor_selected;
310 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[2]);
312 ImGui::TextColored(accent,
ICON_MD_EDIT " Select an Editor");
316 ImGui::TextColored(text_secondary,
317 "Choose an editor to begin working on your ROM. "
318 "You can open multiple editors simultaneously.");
329 const float max_width =
332 const float spacing = ImGui::GetStyle().ItemSpacing.x;
333 const float avail_width = ImGui::GetContentRegionAvail().x;
334 const bool stack_items = avail_width < min_width * 1.6f;
338 layout.item_width = avail_width;
339 layout.item_height = height;
340 layout.spacing = spacing;
343 avail_width, min_width, max_width, height, height, min_width,
344 height / std::max(min_width, 1.0f), spacing);
350 auto it = std::find_if(
352 [type](
const EditorInfo& info) { return info.type == type; });
356 ImGui::SetCursorPosX(layout.row_start_x);
360 gui::StyleColorGuard btn_guard(
361 {{ImGuiCol_Button,
ScaleColor(base_color, 0.5f, 0.7f)},
362 {ImGuiCol_ButtonHovered,
ScaleColor(base_color, 0.7f, 0.9f)},
363 {ImGuiCol_ButtonActive,
WithAlpha(base_color, 1.0f)}});
365 if (ImGui::Button(absl::StrCat(it->icon,
" ", it->name).c_str(),
366 ImVec2(layout.item_width, layout.item_height))) {
370 if (ImGui::IsItemHovered()) {
371 ImGui::SetTooltip(
"%s", it->description);
375 if (column < layout.columns) {
376 ImGui::SameLine(0.0f, layout.spacing);
390 const ImVec2& card_size) {
391 ImGui::PushID(index);
398 ImFont* text_font = ImGui::GetFont();
399 const float text_font_size = ImGui::GetFontSize();
401 const ImGuiStyle& style = ImGui::GetStyle();
402 const float line_height = ImGui::GetTextLineHeight();
403 const float padding_x = std::max(style.FramePadding.x, card_size.x * 0.06f);
404 const float padding_y = std::max(style.FramePadding.y, card_size.y * 0.08f);
406 const float footer_height = info.shortcut.empty() ? 0.0f : line_height;
407 const float footer_spacing =
408 info.shortcut.empty() ? 0.0f : style.ItemSpacing.y;
409 const float available_icon_height = card_size.y - padding_y * 2.0f -
410 line_height - footer_height -
412 const float min_icon_radius = line_height * 0.9f;
413 float max_icon_radius = card_size.y * 0.24f;
414 max_icon_radius = std::max(max_icon_radius, min_icon_radius);
415 const float icon_radius = std::clamp(available_icon_height * 0.5f,
416 min_icon_radius, max_icon_radius);
418 const ImVec2 cursor_pos = ImGui::GetCursorScreenPos();
419 ImDrawList* draw_list = ImGui::GetWindowDrawList();
420 const ImVec2 icon_center(cursor_pos.x + card_size.x * 0.5f,
421 cursor_pos.y + padding_y + icon_radius);
422 float title_y = icon_center.y + icon_radius + style.ItemSpacing.y;
423 const float footer_y = cursor_pos.y + card_size.y - padding_y - footer_height;
424 if (title_y + line_height > footer_y - style.ItemSpacing.y) {
425 title_y = footer_y - line_height - style.ItemSpacing.y;
433 ImU32 color_top = ImGui::GetColorU32(
ScaleColor(base_color, 0.4f, 0.85f));
434 ImU32 color_bottom = ImGui::GetColorU32(
ScaleColor(base_color, 0.2f, 0.9f));
437 draw_list->AddRectFilledMultiColor(
439 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y), color_top,
440 color_top, color_bottom, color_bottom);
444 is_recent ? ImGui::GetColorU32(
WithAlpha(base_color, 1.0f))
446 const float rounding = std::max(style.FrameRounding, card_size.y * 0.05f);
447 const float border_thickness =
448 is_recent ? std::max(2.0f, style.FrameBorderSize + 1.0f)
449 : std::max(1.0f, style.FrameBorderSize);
452 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
453 border_color, rounding, 0, border_thickness);
457 const float badge_radius =
458 std::clamp(line_height * 0.6f, line_height * 0.4f, line_height);
459 ImVec2 badge_pos(cursor_pos.x + card_size.x - padding_x - badge_radius,
460 cursor_pos.y + padding_y + badge_radius);
461 draw_list->AddCircleFilled(badge_pos, badge_radius,
462 ImGui::GetColorU32(base_color), 16);
463 const ImU32 star_color = ImGui::GetColorU32(text_primary);
464 const ImVec2 star_size =
465 text_font->CalcTextSizeA(text_font_size, FLT_MAX, 0.0f,
ICON_MD_STAR);
466 const ImVec2 star_pos(badge_pos.x - star_size.x * 0.5f,
467 badge_pos.y - star_size.y * 0.5f);
468 draw_list->AddText(text_font, text_font_size, star_pos, star_color,
473 ImVec4 button_bg = ImGui::GetStyleColorVec4(ImGuiCol_Button);
475 gui::StyleColorGuard card_btn_guard(
476 {{ImGuiCol_Button, button_bg},
477 {ImGuiCol_ButtonHovered,
ScaleColor(base_color, 0.3f, 0.5f)},
478 {ImGuiCol_ButtonActive,
ScaleColor(base_color, 0.5f, 0.7f)}});
481 ImGui::Button(absl::StrCat(
"##", info.name).c_str(), card_size);
482 bool is_hovered = ImGui::IsItemHovered();
485 ImU32 icon_bg = ImGui::GetColorU32(base_color);
486 draw_list->AddCircleFilled(icon_center, icon_radius, icon_bg, 32);
489 ImFont* icon_font = ImGui::GetFont();
490 if (ImGui::GetIO().Fonts->Fonts.size() > 2) {
491 icon_font = ImGui::GetIO().Fonts->Fonts[2];
492 }
else if (ImGui::GetIO().Fonts->Fonts.size() > 1) {
493 icon_font = ImGui::GetIO().Fonts->Fonts[1];
495 ImGui::PushFont(icon_font);
496 const float icon_font_size = ImGui::GetFontSize();
497 const ImVec2 icon_size =
498 icon_font->CalcTextSizeA(icon_font_size, FLT_MAX, 0.0f, info.icon);
500 const ImVec2 icon_text_pos(icon_center.x - icon_size.x * 0.5f,
501 icon_center.y - icon_size.y * 0.5f);
502 draw_list->AddText(icon_font, icon_font_size, icon_text_pos,
503 ImGui::GetColorU32(text_primary), info.icon);
506 const ImVec2 name_size =
507 text_font->CalcTextSizeA(text_font_size, FLT_MAX, 0.0f, info.name);
508 float name_x = cursor_pos.x + (card_size.x - name_size.x) * 0.5f;
509 const float name_min_x = cursor_pos.x + padding_x;
510 const float name_max_x = cursor_pos.x + card_size.x - padding_x;
511 name_x = std::clamp(name_x, name_min_x, name_max_x);
512 const ImVec2 name_pos(name_x, title_y);
513 const ImVec4 name_clip(name_min_x, cursor_pos.y + padding_y, name_max_x,
515 draw_list->AddText(text_font, text_font_size, name_pos,
516 ImGui::GetColorU32(base_color), info.name,
nullptr, 0.0f,
520 if (!info.shortcut.empty()) {
521 const ImVec2 shortcut_pos(cursor_pos.x + padding_x, footer_y);
522 const ImVec4 shortcut_clip(cursor_pos.x + padding_x, footer_y,
523 cursor_pos.x + card_size.x - padding_x,
524 cursor_pos.y + card_size.y - padding_y);
525 draw_list->AddText(text_font, text_font_size, shortcut_pos,
526 ImGui::GetColorU32(text_secondary),
527 info.shortcut.c_str(),
nullptr, 0.0f, &shortcut_clip);
532 ImU32 glow_color = ImGui::GetColorU32(
ScaleColor(base_color, 1.0f, 0.18f));
533 draw_list->AddRectFilled(
535 ImVec2(cursor_pos.x + card_size.x, cursor_pos.y + card_size.y),
536 glow_color, rounding);
541 const float tooltip_width = std::clamp(card_size.x * 1.4f, 240.0f, 340.0f);
542 ImGui::SetNextWindowSize(ImVec2(tooltip_width, 0), ImGuiCond_Always);
543 ImGui::BeginTooltip();
544 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
545 ImGui::TextColored(base_color,
"%s %s", info.icon, info.name);
548 ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + tooltip_width - 20.0f);
549 ImGui::TextWrapped(
"%s", info.description);
550 ImGui::PopTextWrapPos();
551 if (!info.shortcut.empty()) {
554 info.shortcut.c_str());
558 ImGui::TextColored(accent,
ICON_MD_STAR " Recently used");
592 std::istringstream ss(data);
594 while (std::getline(ss, line) &&
596 int type_int = std::stoi(line);
610 std::ostringstream ss;
612 ss << static_cast<int>(type) <<
"\n";
void DrawEditorPanel(const EditorInfo &info, int index, const ImVec2 &card_size)
void SaveRecentEditors()
Save recently used editors to settings.
void LoadRecentEditors()
Load recently used editors from settings.
void DrawQuickAccessButtons()
std::vector< EditorType > recent_editors_
std::function< void(EditorType)> selection_callback_
std::vector< EditorInfo > editors_
static constexpr int kMaxRecentEditors
void MarkRecentlyUsed(EditorType type)
Mark an editor as recently used.
EditorType selected_editor_
bool Show(bool *p_open=nullptr)
Show the dialog.
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_EMOJI_EMOTIONS
#define ICON_MD_DATA_ARRAY
#define ICON_MD_VIDEOGAME_ASSET
#define ICON_MD_CHAT_BUBBLE
#define ICON_MD_MUSIC_NOTE
#define ICON_MD_COLOR_LENS
#define ICON_MD_SMART_TOY
ImVec4 ScaleColor(const ImVec4 &color, float scale, float alpha)
constexpr float kEditorSelectCardWidthMaxFactor
ImVec4 WithAlpha(ImVec4 color, float alpha)
constexpr float kEditorSelectRecentBaseWidth
constexpr float kEditorSelectCardHeightMaxFactor
constexpr float kEditorSelectCardBaseHeight
float GetEditorSelectScale()
constexpr float kEditorSelectCardBaseWidth
constexpr float kEditorSelectRecentBaseHeight
constexpr float kEditorSelectRecentWidthMaxFactor
constexpr float kEditorSelectBaseFontSize
ImVec4 GetEditorAccentColor(EditorType type, const gui::Theme &theme)
GridLayout ComputeGridLayout(float avail_width, float min_width, float max_width, float min_height, float max_height, float preferred_width, float aspect_ratio, float spacing)
const char * GetCtrlDisplayName()
Get the display name for the primary modifier key.
ImVec4 ConvertColorToImVec4(const Color &color)
void SaveFile(const std::string &filename, const std::string &contents)
std::string LoadFileFromConfigDir(const std::string &filename)
Loads a file from the user's config directory.