yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
right_panel_manager.cc
Go to the documentation of this file.
2
3#include <chrono>
4#include <filesystem>
5
6#include "absl/strings/str_format.h"
13#include "app/gui/core/icons.h"
15#include "app/gui/core/style.h"
17#include "imgui/imgui.h"
18#include "util/platform_paths.h"
19
20namespace yaze {
21namespace editor {
22
23namespace {
24
25std::string ResolveAgentChatHistoryPath() {
26 auto agent_dir = util::PlatformPaths::GetAppDataSubdirectory("agent");
27 if (agent_dir.ok()) {
28 return (*agent_dir / "agent_chat_history.json").string();
29 }
31 if (docs_dir.ok()) {
32 return (*docs_dir / "agent_chat_history.json").string();
33 }
35 if (temp_dir.ok()) {
36 return (*temp_dir / "agent_chat_history.json").string();
37 }
38 return (std::filesystem::current_path() / "agent_chat_history.json").string();
39}
40
41} // namespace
42
44 switch (type) {
46 return "None";
48 return "AI Agent";
50 return "Proposals";
52 return "Settings";
54 return "Help";
56 return "Notifications";
58 return "Properties";
60 return "Project";
61 default:
62 return "Unknown";
63 }
64}
65
88
90 if (active_panel_ == type) {
91 ClosePanel();
92 } else {
93 OpenPanel(type);
94 }
95}
96
98 active_panel_ = type;
99 animating_ = true;
100 panel_animation_ = 0.0f;
101}
102
108
111 return 0.0f;
112 }
113
114 float width = 0.0f;
115 switch (active_panel_) {
117 width = agent_chat_width_;
118 break;
120 width = proposals_width_;
121 break;
123 width = settings_width_;
124 break;
125 case PanelType::kHelp:
126 width = help_width_;
127 break;
129 width = notifications_width_;
130 break;
132 width = properties_width_;
133 break;
135 width = project_width_;
136 break;
137 default:
138 width = 0.0f;
139 break;
140 }
141
142 ImGuiContext* context = ImGui::GetCurrentContext();
143 if (!context) {
144 return width;
145 }
146
147 const ImGuiViewport* viewport = ImGui::GetMainViewport();
148 if (!viewport) {
149 return width;
150 }
151
152 const float max_width = viewport->WorkSize.x * 0.35f;
153 if (max_width > 0.0f && width > max_width) {
154 width = max_width;
155 }
156
157 return width;
158}
159
161 switch (type) {
163 agent_chat_width_ = width;
164 break;
166 proposals_width_ = width;
167 break;
169 settings_width_ = width;
170 break;
171 case PanelType::kHelp:
172 help_width_ = width;
173 break;
175 notifications_width_ = width;
176 break;
178 properties_width_ = width;
179 break;
181 project_width_ = width;
182 break;
183 default:
184 break;
185 }
186}
187
190 return;
191 }
192
193 // Handle Escape key to close panel
194 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
195 ClosePanel();
196 return;
197 }
198
199 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
200 const ImGuiViewport* viewport = ImGui::GetMainViewport();
201 const float viewport_height = viewport->WorkSize.y;
202 const float viewport_width = viewport->WorkSize.x;
203 const float panel_width = GetPanelWidth();
204
205 // Use SurfaceContainer for slightly elevated panel background
206 ImVec4 panel_bg = gui::GetSurfaceContainerVec4();
207 ImVec4 panel_border = gui::GetOutlineVec4();
208
209 ImGuiWindowFlags panel_flags =
210 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
211 ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking |
212 ImGuiWindowFlags_NoNavFocus;
213
214 // Position panel on right edge, full height
215 ImGui::SetNextWindowPos(ImVec2(
216 viewport->WorkPos.x + viewport_width - panel_width, viewport->WorkPos.y));
217 ImGui::SetNextWindowSize(ImVec2(panel_width, viewport_height));
218
219 ImGui::PushStyleColor(ImGuiCol_WindowBg, panel_bg);
220 ImGui::PushStyleColor(ImGuiCol_Border, panel_border);
221 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
222 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
223
224 if (ImGui::Begin("##RightPanel", nullptr, panel_flags)) {
225 // Draw enhanced panel header
228
229 // Content area with padding
230 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f, 8.0f));
231 ImGui::BeginChild("##PanelContent", ImVec2(0, 0), false,
232 ImGuiWindowFlags_AlwaysUseWindowPadding);
233
234 // Draw panel content based on type
235 switch (active_panel_) {
238 break;
241 break;
244 break;
245 case PanelType::kHelp:
247 break;
250 break;
253 break;
256 break;
257 default:
258 break;
259 }
260
261 ImGui::EndChild();
262 ImGui::PopStyleVar(); // WindowPadding for content
263 }
264 ImGui::End();
265
266 ImGui::PopStyleVar(2);
267 ImGui::PopStyleColor(2);
268}
269
270void RightPanelManager::DrawPanelHeader(const char* title, const char* icon) {
271 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
272 const float header_height = 44.0f;
273 const float padding = 12.0f;
274
275 // Header background - slightly elevated surface
276 ImVec2 header_min = ImGui::GetCursorScreenPos();
277 ImVec2 header_max = ImVec2(header_min.x + ImGui::GetWindowWidth(),
278 header_min.y + header_height);
279
280 ImDrawList* draw_list = ImGui::GetWindowDrawList();
281 draw_list->AddRectFilled(
282 header_min, header_max,
283 ImGui::GetColorU32(gui::GetSurfaceContainerHighVec4()));
284
285 // Draw subtle bottom border
286 draw_list->AddLine(ImVec2(header_min.x, header_max.y),
287 ImVec2(header_max.x, header_max.y),
288 ImGui::GetColorU32(gui::GetOutlineVec4()), 1.0f);
289
290 // Position content within header
291 ImGui::SetCursorPosX(padding);
292 ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
293 (header_height - ImGui::GetTextLineHeight()) * 0.5f);
294
295 // Panel icon with primary color
296 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
297 ImGui::Text("%s", icon);
298 ImGui::PopStyleColor();
299
300 ImGui::SameLine();
301
302 // Panel title (use current style text color)
303 ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text));
304 ImGui::Text("%s", title);
305 ImGui::PopStyleColor();
306
307 // Right-aligned buttons
308 const float button_size = 28.0f;
309 float current_x = ImGui::GetWindowWidth() - button_size - padding;
310
311 // Close button
312 ImGui::SameLine(current_x);
313 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 4.0f); // Center vertically
314
315 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
316 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
318 ImGui::PushStyleColor(
319 ImGuiCol_ButtonActive,
320 ImVec4(gui::GetPrimaryVec4().x * 0.3f, gui::GetPrimaryVec4().y * 0.3f,
321 gui::GetPrimaryVec4().z * 0.3f, 0.4f));
322 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
323 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
324
325 if (ImGui::Button(ICON_MD_CLOSE, ImVec2(button_size, button_size))) {
326 ClosePanel();
327 }
328 if (ImGui::IsItemHovered()) {
329 ImGui::SetTooltip("Close Panel (Esc)");
330 }
331
332 // Lock Toggle (Only for Properties Panel)
334 current_x -= (button_size + 4.0f);
335 ImGui::SameLine(current_x);
336
337 // TODO: Hook up to actual lock state in SelectionPropertiesPanel
338 static bool is_locked = false;
339 ImVec4 lock_color =
341 ImGui::PushStyleColor(ImGuiCol_Text, lock_color);
342
343 if (ImGui::Button(is_locked ? ICON_MD_LOCK : ICON_MD_LOCK_OPEN,
344 ImVec2(button_size, button_size))) {
345 is_locked = !is_locked;
346 }
347 ImGui::PopStyleColor();
348
349 if (ImGui::IsItemHovered()) {
350 ImGui::SetTooltip(is_locked ? "Unlock Selection" : "Lock Selection");
351 }
352 }
353
354 ImGui::PopStyleVar();
355 ImGui::PopStyleColor(4);
356
357 // Move cursor past the header
358 ImGui::SetCursorPosY(header_height + 8.0f);
359}
360
361// =============================================================================
362// Panel Styling Helpers
363// =============================================================================
364
365bool RightPanelManager::BeginPanelSection(const char* label, const char* icon,
366 bool default_open) {
367 ImGui::PushStyleColor(ImGuiCol_Header, gui::GetSurfaceContainerHighVec4());
368 ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
370 ImGui::PushStyleColor(ImGuiCol_HeaderActive,
372 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0f, 6.0f));
373 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
374
375 // Build header text with icon if provided
376 std::string header_text;
377 if (icon) {
378 header_text = std::string(icon) + " " + label;
379 } else {
380 header_text = label;
381 }
382
383 ImGuiTreeNodeFlags flags =
384 ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth |
385 ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_FramePadding;
386 if (default_open) {
387 flags |= ImGuiTreeNodeFlags_DefaultOpen;
388 }
389
390 bool is_open = ImGui::TreeNodeEx(header_text.c_str(), flags);
391
392 ImGui::PopStyleVar(2);
393 ImGui::PopStyleColor(3);
394
395 if (is_open) {
396 ImGui::Spacing();
397 ImGui::Indent(4.0f);
398 }
399
400 return is_open;
401}
402
404 ImGui::Unindent(4.0f);
405 ImGui::TreePop();
406 ImGui::Spacing();
407}
408
410 ImGui::Spacing();
411 ImGui::PushStyleColor(ImGuiCol_Separator, gui::GetOutlineVec4());
412 ImGui::Separator();
413 ImGui::PopStyleColor();
414 ImGui::Spacing();
415}
416
417void RightPanelManager::DrawPanelLabel(const char* label) {
418 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
419 ImGui::TextUnformatted(label);
420 ImGui::PopStyleColor();
421}
422
423void RightPanelManager::DrawPanelValue(const char* label, const char* value) {
424 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
425 ImGui::Text("%s:", label);
426 ImGui::PopStyleColor();
427 ImGui::SameLine();
428 ImGui::TextUnformatted(value);
429}
430
432 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextDisabledVec4());
433 ImGui::PushTextWrapPos(ImGui::GetContentRegionAvail().x);
434 ImGui::TextWrapped("%s", text);
435 ImGui::PopTextWrapPos();
436 ImGui::PopStyleColor();
437}
438
439// =============================================================================
440// Panel Content Drawing
441// =============================================================================
442
444#ifdef YAZE_BUILD_AGENT_UI
445 const ImVec4 header_bg = gui::GetSurfaceContainerHighVec4();
446 const ImVec4 hero_text = gui::GetOnSurfaceVec4();
447 const ImVec4 accent = gui::GetPrimaryVec4();
448
449 if (!agent_chat_) {
450 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
451 ImGui::Text(ICON_MD_SMART_TOY " AI Agent Not Available");
452 ImGui::PopStyleColor();
453 ImGui::Spacing();
455 "The AI Agent is not initialized. "
456 "Open the AI Agent from View menu or use Ctrl+Shift+A.");
457 return;
458 }
459
460 bool chat_active = *agent_chat_->active();
461
462 ImGui::PushStyleColor(ImGuiCol_ChildBg, header_bg);
463 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 8.0f);
464 if (ImGui::BeginChild("AgentHero", ImVec2(0, 110), true)) {
465 ImGui::PushStyleColor(ImGuiCol_Text, hero_text);
466 ImGui::TextColored(accent, "%s AI Agent", ICON_MD_SMART_TOY);
467 ImGui::PopStyleColor();
468 ImGui::SameLine();
469 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
470 ImGui::Text("Right Sidebar");
471 ImGui::PopStyleColor();
472
473 ImGui::Spacing();
474 DrawPanelValue("Status", chat_active ? "Active" : "Inactive");
475 DrawPanelValue("Provider", "Configured via Agent Editor");
476 }
477 ImGui::EndChild();
478 ImGui::PopStyleVar();
479 ImGui::PopStyleColor();
480
481 ImGui::Spacing();
482 agent_chat_->set_active(true);
483
484 const float footer_height = ImGui::GetFrameHeightWithSpacing() * 3.5f;
485 float content_height =
486 std::max(120.0f, ImGui::GetContentRegionAvail().y - footer_height);
487
488 static int active_tab = 0; // 0 = Chat, 1 = Quick Config
489 if (ImGui::BeginTabBar("AgentSidebarTabs")) {
490 if (ImGui::BeginTabItem(ICON_MD_CHAT " Chat")) {
491 active_tab = 0;
492 ImGui::EndTabItem();
493 }
494 if (ImGui::BeginTabItem(ICON_MD_SETTINGS " Quick Config")) {
495 active_tab = 1;
496 ImGui::EndTabItem();
497 }
498 ImGui::EndTabBar();
499 }
500
501 if (active_tab == 0) {
502 if (ImGui::BeginChild("AgentChatBody", ImVec2(0, content_height), true)) {
503 agent_chat_->Draw(content_height);
504 }
505 ImGui::EndChild();
506 } else {
507 if (ImGui::BeginChild("AgentQuickConfig", ImVec2(0, content_height),
508 true)) {
509 bool auto_scroll = agent_chat_->auto_scroll();
510 bool show_ts = agent_chat_->show_timestamps();
511 bool show_reasoning = agent_chat_->show_reasoning();
512
513 ImGui::TextColored(accent, "%s Display", ICON_MD_TUNE);
514 if (ImGui::Checkbox("Auto-scroll", &auto_scroll)) {
515 agent_chat_->set_auto_scroll(auto_scroll);
516 }
517 if (ImGui::Checkbox("Show timestamps", &show_ts)) {
519 }
520 if (ImGui::Checkbox("Show reasoning traces", &show_reasoning)) {
521 agent_chat_->set_show_reasoning(show_reasoning);
522 }
523
524 ImGui::Separator();
525 ImGui::TextColored(accent, "%s Provider", ICON_MD_SMART_TOY);
527 "Change provider/model in the main Agent Editor. This sidebar shows "
528 "active chat controls.");
529 }
530 ImGui::EndChild();
531 }
532
533 // Footer actions (always visible, not clipped)
534 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 6));
535 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetPrimaryVec4());
536 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, gui::GetPrimaryHoverVec4());
537 ImGui::PushStyleColor(ImGuiCol_ButtonActive, gui::GetPrimaryActiveVec4());
538 if (ImGui::Button(ICON_MD_OPEN_IN_NEW " Focus Agent Chat", ImVec2(-1, 0))) {
539 agent_chat_->set_active(true);
541 }
542 ImGui::PopStyleColor(3);
543
544 ImVec2 half_width(ImGui::GetContentRegionAvail().x / 2 - 4, 0);
545 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerVec4());
546 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
548 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
550 if (ImGui::Button(ICON_MD_DELETE_FOREVER " Clear", half_width)) {
552 }
553 ImGui::SameLine();
554 if (ImGui::Button(ICON_MD_FILE_DOWNLOAD " Save", half_width)) {
555 agent_chat_->SaveHistory(ResolveAgentChatHistoryPath());
556 }
557 ImGui::PopStyleColor(3);
558 ImGui::PopStyleVar();
559#else
560 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
561 ImGui::Text(ICON_MD_SMART_TOY " AI Agent Not Available");
562 ImGui::PopStyleColor();
563
564 ImGui::Spacing();
566 "The AI Agent requires agent UI support. "
567 "Build with YAZE_BUILD_AGENT_UI=ON to enable.");
568#endif
569}
570
572 if (proposal_drawer_) {
573 // Set ROM and draw content inside the panel (not a separate window)
574 if (rom_) {
576 }
578 } else {
579 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
580 ImGui::Text(ICON_MD_DESCRIPTION " Proposals Not Available");
581 ImGui::PopStyleColor();
582
583 ImGui::Spacing();
585 "The proposal system is not initialized. "
586 "Proposals will appear here when the AI Agent creates them.");
587 }
588}
589
591 if (settings_panel_) {
592 // Draw settings inline (no card windows)
594 } else {
595 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
596 ImGui::Text(ICON_MD_SETTINGS " Settings Not Available");
597 ImGui::PopStyleColor();
598
599 ImGui::Spacing();
601 "Settings will be available once initialized. "
602 "This panel provides quick access to application settings.");
603 }
604}
605
607 // Context-aware editor header
609
610 // Keyboard Shortcuts section (default open)
611 if (BeginPanelSection("Keyboard Shortcuts", ICON_MD_KEYBOARD, true)) {
615 }
616
617 // Editor-specific help (default open)
618 if (BeginPanelSection("Editor Guide", ICON_MD_HELP, true)) {
621 }
622
623 // Quick Actions (collapsed by default)
624 if (BeginPanelSection("Quick Actions", ICON_MD_BOLT, false)) {
627 }
628
629 // About section (collapsed by default)
630 if (BeginPanelSection("About", ICON_MD_INFO, false)) {
633 }
634}
635
637 const char* editor_name = "No Editor Selected";
638 const char* editor_icon = ICON_MD_HELP;
639
640 switch (active_editor_type_) {
642 editor_name = "Overworld Editor";
643 editor_icon = ICON_MD_LANDSCAPE;
644 break;
646 editor_name = "Dungeon Editor";
647 editor_icon = ICON_MD_CASTLE;
648 break;
650 editor_name = "Graphics Editor";
651 editor_icon = ICON_MD_IMAGE;
652 break;
654 editor_name = "Palette Editor";
655 editor_icon = ICON_MD_PALETTE;
656 break;
658 editor_name = "Music Editor";
659 editor_icon = ICON_MD_MUSIC_NOTE;
660 break;
662 editor_name = "Screen Editor";
663 editor_icon = ICON_MD_TV;
664 break;
666 editor_name = "Sprite Editor";
667 editor_icon = ICON_MD_SMART_TOY;
668 break;
670 editor_name = "Message Editor";
671 editor_icon = ICON_MD_CHAT;
672 break;
674 editor_name = "Emulator";
675 editor_icon = ICON_MD_VIDEOGAME_ASSET;
676 break;
677 default:
678 break;
679 }
680
681 // Draw context header with editor info
682 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
683 ImGui::Text("%s %s Help", editor_icon, editor_name);
684 ImGui::PopStyleColor();
685
687}
688
690 const char* ctrl = gui::GetCtrlDisplayName();
691 DrawPanelLabel("Global");
692 ImGui::Indent(8.0f);
693 DrawPanelValue(absl::StrFormat("%s+O", ctrl).c_str(), "Open ROM");
694 DrawPanelValue(absl::StrFormat("%s+S", ctrl).c_str(), "Save ROM");
695 DrawPanelValue(absl::StrFormat("%s+Z", ctrl).c_str(), "Undo");
696 DrawPanelValue(absl::StrFormat("%s+Y", ctrl).c_str(), "Redo");
697 DrawPanelValue(absl::StrFormat("%s+B", ctrl).c_str(), "Toggle Sidebar");
698 DrawPanelValue("F1", "Help Panel");
699 DrawPanelValue("Esc", "Close Panel");
700 ImGui::Unindent(8.0f);
701 ImGui::Spacing();
702}
703
705 const char* ctrl = gui::GetCtrlDisplayName();
706 switch (active_editor_type_) {
708 DrawPanelLabel("Overworld");
709 ImGui::Indent(8.0f);
710 DrawPanelValue("1-3", "Switch World (LW/DW/SP)");
711 DrawPanelValue("Arrow Keys", "Navigate Maps");
712 DrawPanelValue("E", "Entity Mode");
713 DrawPanelValue("T", "Tile Mode");
714 DrawPanelValue("Right Click", "Pick Tile");
715 ImGui::Unindent(8.0f);
716 break;
717
719 DrawPanelLabel("Dungeon");
720 ImGui::Indent(8.0f);
721 DrawPanelValue("Delete", "Remove Object");
722 DrawPanelValue(absl::StrFormat("%s+D", ctrl).c_str(), "Duplicate");
723 DrawPanelValue("Arrow Keys", "Move Object");
724 DrawPanelValue("G", "Toggle Grid");
725 DrawPanelValue("L", "Cycle Layers");
726 ImGui::Unindent(8.0f);
727 break;
728
730 DrawPanelLabel("Graphics");
731 ImGui::Indent(8.0f);
732 DrawPanelValue("[ ]", "Previous/Next Sheet");
733 DrawPanelValue("P", "Pencil Tool");
734 DrawPanelValue("F", "Fill Tool");
735 DrawPanelValue("+ -", "Zoom In/Out");
736 ImGui::Unindent(8.0f);
737 break;
738
740 DrawPanelLabel("Palette");
741 ImGui::Indent(8.0f);
742 DrawPanelValue("Click", "Select Color");
743 DrawPanelValue("Double Click", "Edit Color");
744 DrawPanelValue("Drag", "Copy Color");
745 ImGui::Unindent(8.0f);
746 break;
747
749 DrawPanelLabel("Music");
750 ImGui::Indent(8.0f);
751 DrawPanelValue("Space", "Play/Pause");
752 DrawPanelValue("Enter", "Stop");
753 DrawPanelValue("Left/Right", "Seek");
754 ImGui::Unindent(8.0f);
755 break;
756
758 DrawPanelLabel("Message");
759 ImGui::Indent(8.0f);
760 DrawPanelValue(absl::StrFormat("%s+Enter", ctrl).c_str(),
761 "Insert Line Break");
762 DrawPanelValue("Up/Down", "Navigate Messages");
763 ImGui::Unindent(8.0f);
764 break;
765
766 default:
767 DrawPanelLabel("Editor Shortcuts");
768 ImGui::Indent(8.0f);
769 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
770 ImGui::TextWrapped("Select an editor to see specific shortcuts.");
771 ImGui::PopStyleColor();
772 ImGui::Unindent(8.0f);
773 break;
774 }
775}
776
778 switch (active_editor_type_) {
780 ImGui::PushStyleColor(ImGuiCol_Text,
781 ImGui::GetStyleColorVec4(ImGuiCol_Text));
782 ImGui::Bullet();
783 ImGui::TextWrapped("Paint tiles by selecting from Tile16 Selector");
784 ImGui::Bullet();
785 ImGui::TextWrapped(
786 "Switch between Light World, Dark World, and Special Areas");
787 ImGui::Bullet();
788 ImGui::TextWrapped(
789 "Use Entity Mode to place entrances, exits, items, and sprites");
790 ImGui::Bullet();
791 ImGui::TextWrapped("Right-click on the map to pick a tile for painting");
792 ImGui::PopStyleColor();
793 break;
794
796 ImGui::PushStyleColor(ImGuiCol_Text,
797 ImGui::GetStyleColorVec4(ImGuiCol_Text));
798 ImGui::Bullet();
799 ImGui::TextWrapped("Select rooms from the Room Selector or Room Matrix");
800 ImGui::Bullet();
801 ImGui::TextWrapped("Place objects using the Object Editor panel");
802 ImGui::Bullet();
803 ImGui::TextWrapped(
804 "Edit room headers for palette, GFX, and floor settings");
805 ImGui::Bullet();
806 ImGui::TextWrapped("Multiple rooms can be opened in separate tabs");
807 ImGui::PopStyleColor();
808 break;
809
811 ImGui::PushStyleColor(ImGuiCol_Text,
812 ImGui::GetStyleColorVec4(ImGuiCol_Text));
813 ImGui::Bullet();
814 ImGui::TextWrapped("Browse graphics sheets using the Sheet Browser");
815 ImGui::Bullet();
816 ImGui::TextWrapped("Edit pixels directly with the Pixel Editor");
817 ImGui::Bullet();
818 ImGui::TextWrapped("Choose palettes from Palette Controls");
819 ImGui::Bullet();
820 ImGui::TextWrapped("View 3D objects like rupees and crystals");
821 ImGui::PopStyleColor();
822 break;
823
825 ImGui::PushStyleColor(ImGuiCol_Text,
826 ImGui::GetStyleColorVec4(ImGuiCol_Text));
827 ImGui::Bullet();
828 ImGui::TextWrapped("Edit overworld, dungeon, and sprite palettes");
829 ImGui::Bullet();
830 ImGui::TextWrapped("Use Quick Access for color harmony tools");
831 ImGui::Bullet();
832 ImGui::TextWrapped("Changes update in real-time across all editors");
833 ImGui::PopStyleColor();
834 break;
835
837 ImGui::PushStyleColor(ImGuiCol_Text,
838 ImGui::GetStyleColorVec4(ImGuiCol_Text));
839 ImGui::Bullet();
840 ImGui::TextWrapped("Browse songs in the Song Browser");
841 ImGui::Bullet();
842 ImGui::TextWrapped("Use the tracker for playback control");
843 ImGui::Bullet();
844 ImGui::TextWrapped("Edit instruments and BRR samples");
845 ImGui::PopStyleColor();
846 break;
847
849 ImGui::PushStyleColor(ImGuiCol_Text,
850 ImGui::GetStyleColorVec4(ImGuiCol_Text));
851 ImGui::Bullet();
852 ImGui::TextWrapped("Edit all in-game dialog messages");
853 ImGui::Bullet();
854 ImGui::TextWrapped("Preview text rendering with the font atlas");
855 ImGui::Bullet();
856 ImGui::TextWrapped("Manage the compression dictionary");
857 ImGui::PopStyleColor();
858 break;
859
860 default:
861 ImGui::Bullet();
862 ImGui::TextWrapped("Open a ROM file via File > Open ROM");
863 ImGui::Bullet();
864 ImGui::TextWrapped("Select an editor from the sidebar");
865 ImGui::Bullet();
866 ImGui::TextWrapped("Use panels to access tools and settings");
867 ImGui::Bullet();
868 ImGui::TextWrapped("Save your work via File > Save ROM");
869 break;
870 }
871}
872
874 const float button_width = ImGui::GetContentRegionAvail().x;
875
876 ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0f, 6.0f));
877 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f);
878
879 // Documentation button
880 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerHighVec4());
881 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
883 if (ImGui::Button(ICON_MD_DESCRIPTION " Open Documentation",
884 ImVec2(button_width, 0))) {
885 // TODO: Open documentation URL
886 }
887 ImGui::PopStyleColor(2);
888
889 ImGui::Spacing();
890
891 // GitHub Issues button
892 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerHighVec4());
893 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
895 if (ImGui::Button(ICON_MD_BUG_REPORT " Report Issue",
896 ImVec2(button_width, 0))) {
897 // TODO: Open GitHub issues URL
898 }
899 ImGui::PopStyleColor(2);
900
901 ImGui::Spacing();
902
903 // Discord button
904 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerHighVec4());
905 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
907 if (ImGui::Button(ICON_MD_FORUM " Join Discord", ImVec2(button_width, 0))) {
908 // TODO: Open Discord invite URL
909 }
910 ImGui::PopStyleColor(2);
911
912 ImGui::PopStyleVar(2);
913}
914
916 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
917 ImGui::Text("YAZE - Yet Another Zelda3 Editor");
918 ImGui::PopStyleColor();
919
920 ImGui::Spacing();
922 "A comprehensive editor for The Legend of Zelda: "
923 "A Link to the Past ROM files.");
924
926
927 DrawPanelLabel("Credits");
928 ImGui::Spacing();
929 ImGui::Text("Written by: scawful");
930 ImGui::Text("Special Thanks: Zarby89, JaredBrian");
931
933
934 DrawPanelLabel("Links");
935 ImGui::Spacing();
936 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
937 ImGui::Text(ICON_MD_LINK " github.com/scawful/yaze");
938 ImGui::PopStyleColor();
939}
940
942 if (!toast_manager_) {
943 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
944 ImGui::Text(ICON_MD_NOTIFICATIONS_OFF " Notifications Unavailable");
945 ImGui::PopStyleColor();
946 return;
947 }
948
949 // Header actions
950 float button_width = 100.0f;
951 float avail = ImGui::GetContentRegionAvail().x;
952
953 // Mark all read button
954 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetSurfaceContainerHighVec4());
955 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
957
958 if (ImGui::Button(ICON_MD_DONE_ALL " Mark All Read",
959 ImVec2(avail * 0.5f - 4.0f, 0))) {
961 }
962 ImGui::SameLine();
963 if (ImGui::Button(ICON_MD_DELETE_SWEEP " Clear All",
964 ImVec2(avail * 0.5f - 4.0f, 0))) {
966 }
967
968 ImGui::PopStyleColor(2);
969
971
972 // Notification history
973 const auto& history = toast_manager_->GetHistory();
974
975 if (history.empty()) {
976 ImGui::Spacing();
977 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
978 ImGui::Text(ICON_MD_INBOX " No notifications");
979 ImGui::PopStyleColor();
980 ImGui::Spacing();
982 "Notifications will appear here when actions complete.");
983 return;
984 }
985
986 // Stats
987 size_t unread_count = toast_manager_->GetUnreadCount();
988 if (unread_count > 0) {
989 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
990 ImGui::Text("%zu unread", unread_count);
991 ImGui::PopStyleColor();
992 } else {
993 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
994 ImGui::Text("All caught up");
995 ImGui::PopStyleColor();
996 }
997
998 ImGui::Spacing();
999
1000 // Scrollable notification list
1001 ImGui::BeginChild("##NotificationList", ImVec2(0, 0), false,
1002 ImGuiWindowFlags_AlwaysVerticalScrollbar);
1003
1004 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
1005 auto now = std::chrono::system_clock::now();
1006
1007 // Group by time (Today, Yesterday, Older)
1008 bool shown_today = false;
1009 bool shown_yesterday = false;
1010 bool shown_older = false;
1011
1012 for (const auto& entry : history) {
1013 auto diff =
1014 std::chrono::duration_cast<std::chrono::hours>(now - entry.timestamp)
1015 .count();
1016
1017 // Time grouping headers
1018 if (diff < 24 && !shown_today) {
1019 DrawPanelLabel("Today");
1020 shown_today = true;
1021 } else if (diff >= 24 && diff < 48 && !shown_yesterday) {
1022 ImGui::Spacing();
1023 DrawPanelLabel("Yesterday");
1024 shown_yesterday = true;
1025 } else if (diff >= 48 && !shown_older) {
1026 ImGui::Spacing();
1027 DrawPanelLabel("Older");
1028 shown_older = true;
1029 }
1030
1031 // Notification item
1032 ImGui::PushID(&entry);
1033
1034 // Icon and color based on type
1035 const char* icon;
1036 ImVec4 color;
1037 switch (entry.type) {
1039 icon = ICON_MD_CHECK_CIRCLE;
1040 color = gui::ConvertColorToImVec4(theme.success);
1041 break;
1043 icon = ICON_MD_WARNING;
1044 color = gui::ConvertColorToImVec4(theme.warning);
1045 break;
1046 case ToastType::kError:
1047 icon = ICON_MD_ERROR;
1048 color = gui::ConvertColorToImVec4(theme.error);
1049 break;
1050 default:
1051 icon = ICON_MD_INFO;
1052 color = gui::ConvertColorToImVec4(theme.info);
1053 break;
1054 }
1055
1056 // Unread indicator
1057 if (!entry.read) {
1058 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetPrimaryVec4());
1059 ImGui::Text(ICON_MD_FIBER_MANUAL_RECORD);
1060 ImGui::PopStyleColor();
1061 ImGui::SameLine();
1062 }
1063
1064 // Icon
1065 ImGui::PushStyleColor(ImGuiCol_Text, color);
1066 ImGui::Text("%s", icon);
1067 ImGui::PopStyleColor();
1068 ImGui::SameLine();
1069
1070 // Message
1071 ImGui::TextWrapped("%s", entry.message.c_str());
1072
1073 // Timestamp
1074 auto diff_sec =
1075 std::chrono::duration_cast<std::chrono::seconds>(now - entry.timestamp)
1076 .count();
1077 std::string time_str;
1078 if (diff_sec < 60) {
1079 time_str = "just now";
1080 } else if (diff_sec < 3600) {
1081 time_str = absl::StrFormat("%dm ago", diff_sec / 60);
1082 } else if (diff_sec < 86400) {
1083 time_str = absl::StrFormat("%dh ago", diff_sec / 3600);
1084 } else {
1085 time_str = absl::StrFormat("%dd ago", diff_sec / 86400);
1086 }
1087
1088 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextDisabledVec4());
1089 ImGui::Text(" %s", time_str.c_str());
1090 ImGui::PopStyleColor();
1091
1092 ImGui::PopID();
1093 ImGui::Spacing();
1094 }
1095
1096 ImGui::EndChild();
1097}
1098
1100 if (properties_panel_) {
1102 } else {
1103 // Placeholder when no properties panel is set
1104 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
1105 ImGui::Text(ICON_MD_SELECT_ALL " No Selection");
1106 ImGui::PopStyleColor();
1107
1108 ImGui::Spacing();
1110 "Select an item in the editor to view and edit its properties here.");
1111
1113
1114 // Show placeholder sections for what properties would look like
1115 if (BeginPanelSection("Position & Size", ICON_MD_STRAIGHTEN, true)) {
1116 DrawPanelValue("X", "--");
1117 DrawPanelValue("Y", "--");
1118 DrawPanelValue("Width", "--");
1119 DrawPanelValue("Height", "--");
1121 }
1122
1123 if (BeginPanelSection("Appearance", ICON_MD_PALETTE, false)) {
1124 DrawPanelValue("Tile ID", "--");
1125 DrawPanelValue("Palette", "--");
1126 DrawPanelValue("Layer", "--");
1128 }
1129
1130 if (BeginPanelSection("Behavior", ICON_MD_SETTINGS, false)) {
1131 DrawPanelValue("Type", "--");
1132 DrawPanelValue("Subtype", "--");
1133 DrawPanelValue("Properties", "--");
1135 }
1136 }
1137}
1138
1140 if (project_panel_) {
1142 } else {
1143 ImGui::PushStyleColor(ImGuiCol_Text, gui::GetTextSecondaryVec4());
1144 ImGui::Text(ICON_MD_FOLDER_SPECIAL " No Project Loaded");
1145 ImGui::PopStyleColor();
1146
1147 ImGui::Spacing();
1149 "Open a .yaze project file to access project management features "
1150 "including ROM versioning, snapshots, and configuration.");
1151
1153
1154 // Placeholder for project features
1155 if (BeginPanelSection("Quick Start", ICON_MD_ROCKET_LAUNCH, true)) {
1156 ImGui::Bullet();
1157 ImGui::TextWrapped("Create a new project via File > New Project");
1158 ImGui::Bullet();
1159 ImGui::TextWrapped("Open existing .yaze project files");
1160 ImGui::Bullet();
1161 ImGui::TextWrapped("Projects track ROM versions and settings");
1163 }
1164
1165 if (BeginPanelSection("Features", ICON_MD_CHECKLIST, false)) {
1166 ImGui::Bullet();
1167 ImGui::TextWrapped("Version snapshots with Git integration");
1168 ImGui::Bullet();
1169 ImGui::TextWrapped("ROM backup and restore");
1170 ImGui::Bullet();
1171 ImGui::TextWrapped("Project-specific settings");
1172 ImGui::Bullet();
1173 ImGui::TextWrapped("Assembly code folder integration");
1175 }
1176 }
1177}
1178
1180 bool clicked = false;
1181
1182 // Helper lambda for drawing panel toggle buttons with consistent styling
1183 auto DrawPanelButton = [&](const char* icon, const char* tooltip,
1184 PanelType type) {
1185 bool is_active = IsPanelActive(type);
1186
1187 // Consistent button styling - transparent background with hover states
1188 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
1189 ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
1191 ImGui::PushStyleColor(ImGuiCol_ButtonActive,
1193 // Active = primary color, inactive = secondary text color
1194 ImGui::PushStyleColor(ImGuiCol_Text, is_active
1197
1198 if (ImGui::SmallButton(icon)) {
1199 TogglePanel(type);
1200 clicked = true;
1201 }
1202
1203 ImGui::PopStyleColor(4);
1204
1205 if (ImGui::IsItemHovered()) {
1206 ImGui::SetTooltip("%s", tooltip);
1207 }
1208 };
1209
1210 // Project button
1211 DrawPanelButton(ICON_MD_FOLDER_SPECIAL, "Project Panel", PanelType::kProject);
1212 ImGui::SameLine();
1213
1214 // Agent Chat button
1215 DrawPanelButton(ICON_MD_SMART_TOY, "AI Agent Panel", PanelType::kAgentChat);
1216 ImGui::SameLine();
1217
1218 // Help button
1219 DrawPanelButton(ICON_MD_HELP_OUTLINE, "Help Panel (F1)", PanelType::kHelp);
1220 ImGui::SameLine();
1221
1222 // Settings button
1223 DrawPanelButton(ICON_MD_SETTINGS, "Settings Panel", PanelType::kSettings);
1224 ImGui::SameLine();
1225
1226 // Properties button (last button - no SameLine after)
1227 DrawPanelButton(ICON_MD_LIST_ALT, "Properties Panel", PanelType::kProperties);
1228
1229 return clicked;
1230}
1231
1232} // namespace editor
1233} // namespace yaze
bool show_reasoning() const
Definition agent_chat.h:62
void set_auto_scroll(bool v)
Definition agent_chat.h:59
void set_show_timestamps(bool v)
Definition agent_chat.h:61
bool auto_scroll() const
Definition agent_chat.h:58
void set_show_reasoning(bool v)
Definition agent_chat.h:63
void Draw(float available_height=0.0f)
void set_active(bool active)
Definition agent_chat.h:67
bool show_timestamps() const
Definition agent_chat.h:60
absl::Status SaveHistory(const std::string &filepath)
void Draw()
Draw the panel and its contents.
float GetPanelWidth() const
Get the width of the panel when expanded.
void ClosePanel()
Close the currently active panel.
void DrawPanelValue(const char *label, const char *value)
void SetPanelWidth(PanelType type, float width)
Set panel width for a specific panel type.
void TogglePanel(PanelType type)
Toggle a specific panel on/off.
bool BeginPanelSection(const char *label, const char *icon=nullptr, bool default_open=true)
bool DrawPanelToggleButtons()
Draw toggle buttons for the status cluster.
bool IsPanelActive(PanelType type) const
Check if a specific panel is active.
void OpenPanel(PanelType type)
Open a specific panel.
void DrawPanelLabel(const char *label)
SelectionPropertiesPanel * properties_panel_
void DrawPanelDescription(const char *text)
void DrawPanelHeader(const char *title, const char *icon)
ProjectManagementPanel * project_panel_
void Draw()
Draw the properties panel content.
const std::deque< NotificationEntry > & GetHistory() const
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
static absl::StatusOr< std::filesystem::path > GetTempDirectory()
Get a temporary directory for the application.
static absl::StatusOr< std::filesystem::path > GetAppDataSubdirectory(const std::string &subdir)
Get a subdirectory within the app data folder.
static absl::StatusOr< std::filesystem::path > GetUserDocumentsSubdirectory(const std::string &subdir)
Get a subdirectory within the user documents folder.
#define ICON_MD_ROCKET_LAUNCH
Definition icons.h:1612
#define ICON_MD_NOTIFICATIONS
Definition icons.h:1335
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_LINK
Definition icons.h:1090
#define ICON_MD_INFO
Definition icons.h:993
#define ICON_MD_CHAT
Definition icons.h:394
#define ICON_MD_LANDSCAPE
Definition icons.h:1059
#define ICON_MD_WARNING
Definition icons.h:2123
#define ICON_MD_LOCK_OPEN
Definition icons.h:1142
#define ICON_MD_DONE_ALL
Definition icons.h:608
#define ICON_MD_FOLDER_SPECIAL
Definition icons.h:815
#define ICON_MD_LOCK
Definition icons.h:1140
#define ICON_MD_CHECKLIST
Definition icons.h:402
#define ICON_MD_FORUM
Definition icons.h:851
#define ICON_MD_FILE_DOWNLOAD
Definition icons.h:744
#define ICON_MD_TUNE
Definition icons.h:2022
#define ICON_MD_LIST_ALT
Definition icons.h:1095
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_BUG_REPORT
Definition icons.h:327
#define ICON_MD_CASTLE
Definition icons.h:380
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_INBOX
Definition icons.h:990
#define ICON_MD_KEYBOARD
Definition icons.h:1028
#define ICON_MD_BOLT
Definition icons.h:282
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_DESCRIPTION
Definition icons.h:539
#define ICON_MD_HELP_OUTLINE
Definition icons.h:935
#define ICON_MD_STRAIGHTEN
Definition icons.h:1871
#define ICON_MD_NOTIFICATIONS_OFF
Definition icons.h:1338
#define ICON_MD_SELECT_ALL
Definition icons.h:1680
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_OPEN_IN_NEW
Definition icons.h:1354
#define ICON_MD_TV
Definition icons.h:2032
#define ICON_MD_DELETE_FOREVER
Definition icons.h:531
#define ICON_MD_CLOSE
Definition icons.h:418
#define ICON_MD_HELP
Definition icons.h:933
#define ICON_MD_FIBER_MANUAL_RECORD
Definition icons.h:739
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define ICON_MD_DELETE_SWEEP
Definition icons.h:533
const char * GetPanelTypeName(RightPanelManager::PanelType type)
Get the name of a panel type.
const char * GetPanelTypeIcon(RightPanelManager::PanelType type)
Get the icon for a panel type.
const char * GetCtrlDisplayName()
Get the display name for the primary modifier key.
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:23
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetPrimaryActiveVec4()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextDisabledVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetSurfaceContainerHighVec4()
ImVec4 GetPrimaryHoverVec4()
ImVec4 GetOutlineVec4()
ImVec4 GetOnSurfaceVec4()
ImVec4 GetSurfaceContainerVec4()