yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
annotation_overlay_panel.h
Go to the documentation of this file.
1#ifndef YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
2#define YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
3
4#include <algorithm>
5#include <cstdio>
6#include <filesystem>
7#include <fstream>
8#include <string>
9#include <vector>
10#include "util/i18n/tr.h"
11
14#include "app/gui/core/icons.h"
15#include "core/project.h"
16#include "imgui/imgui.h"
17#include "nlohmann/json.hpp"
18
19namespace yaze::editor {
20
24enum class AnnotationPriority : int {
25 kNote = 0,
26 kBug = 1,
27 kBlocker = 2,
28};
29
33struct AnnotationEntry {
34 int room_id = 0;
35 std::string text;
37 std::string category; // "design", "test", "bug", etc.
38 std::string created_at;
39};
40
54class AnnotationOverlayPanel : public WindowContent {
55 public:
56 std::string GetId() const override { return "oracle.annotations"; }
57 std::string GetDisplayName() const override { return "Annotations"; }
58 std::string GetIcon() const override { return ICON_MD_NOTES; }
59 std::string GetEditorCategory() const override { return "Agent"; }
63 std::string GetWorkflowGroup() const override { return "Planning"; }
64 std::string GetWorkflowDescription() const override {
65 return "Review and edit room-level project annotations";
66 }
70
71 void SetAnnotationsPath(const std::string& path) {
72 annotations_path_ = path;
74 }
75
76 void Draw(bool* /*p_open*/) override {
77 // Lazily discover annotations path from project context
78 if (annotations_path_.empty()) {
79 if (auto* project = ContentRegistry::Context::current_project()) {
80 if (!project->code_folder.empty()) {
81 namespace fs = std::filesystem;
82 fs::path candidate =
83 fs::path(project->GetAbsolutePath(project->code_folder)) /
84 "Docs" / "Dev" / "Planning" / "annotations.json";
85 SetAnnotationsPath(candidate.string());
86 }
87 }
88 }
89
90 // Filter controls
91 ImGui::Text(tr("Room filter:"));
92 ImGui::SameLine();
93 ImGui::InputInt("##room_filter", &filter_room_id_);
94 ImGui::SameLine();
95 if (ImGui::Button(tr("All")))
96 filter_room_id_ = -1;
97
98 ImGui::SameLine();
99 const char* priorities[] = {"All", "Note", "Bug", "Blocker"};
100 ImGui::Combo(tr("Priority"), &filter_priority_, priorities, 4);
101
102 ImGui::Separator();
103
104 // Annotation list
105 ImGui::BeginChild("annotation_list", ImVec2(0, -120),
106 ImGuiChildFlags_Borders);
107
108 for (size_t i = 0; i < annotations_.size(); ++i) {
109 const auto& ann = annotations_[i];
110
111 // Apply filters
112 if (filter_room_id_ >= 0 && ann.room_id != filter_room_id_)
113 continue;
114 if (filter_priority_ > 0 &&
115 static_cast<int>(ann.priority) != (filter_priority_ - 1))
116 continue;
117
118 ImU32 dot_color = GetPriorityColor(ann.priority);
119 ImVec2 cursor = ImGui::GetCursorScreenPos();
120 ImGui::GetWindowDrawList()->AddCircleFilled(
121 ImVec2(cursor.x + 6, cursor.y + 10), 5.0f, dot_color);
122 ImGui::Dummy(ImVec2(16, 0));
123 ImGui::SameLine();
124
125 char label[128];
126 snprintf(label, sizeof(label), "Room 0x%02X: %s##ann_%zu", ann.room_id,
127 ann.text.c_str(), i);
128
129 if (ImGui::Selectable(label, selected_index_ == static_cast<int>(i))) {
130 selected_index_ = static_cast<int>(i);
131 // Copy to edit buffer
132 edit_room_ = ann.room_id;
133 snprintf(edit_text_, sizeof(edit_text_), "%s", ann.text.c_str());
134 edit_priority_ = static_cast<int>(ann.priority);
135 snprintf(edit_category_, sizeof(edit_category_), "%s",
136 ann.category.c_str());
137 }
138 }
139
140 ImGui::EndChild();
141
142 ImGui::Separator();
143
144 // Edit / Add form
145 ImGui::Text(tr("Room:"));
146 ImGui::SameLine();
147 ImGui::SetNextItemWidth(80);
148 ImGui::InputInt("##edit_room", &edit_room_);
149
150 ImGui::SameLine();
151 ImGui::Text(tr("Priority:"));
152 ImGui::SameLine();
153 ImGui::SetNextItemWidth(80);
154 const char* pri_names[] = {"Note", "Bug", "Blocker"};
155 ImGui::Combo("##edit_priority", &edit_priority_, pri_names, 3);
156
157 ImGui::InputText(tr("Text"), edit_text_, sizeof(edit_text_));
158 ImGui::InputText(tr("Category"), edit_category_, sizeof(edit_category_));
159
160 if (ImGui::Button(tr("Add"))) {
161 AnnotationEntry entry;
162 entry.room_id = edit_room_;
163 entry.text = edit_text_;
164 entry.priority = static_cast<AnnotationPriority>(edit_priority_);
165 entry.category = edit_category_;
166 annotations_.push_back(entry);
168 }
169
170 ImGui::SameLine();
171 if (selected_index_ >= 0 &&
172 selected_index_ < static_cast<int>(annotations_.size())) {
173 if (ImGui::Button(tr("Update"))) {
174 auto& ann = annotations_[selected_index_];
175 ann.room_id = edit_room_;
176 ann.text = edit_text_;
177 ann.priority = static_cast<AnnotationPriority>(edit_priority_);
178 ann.category = edit_category_;
180 }
181 ImGui::SameLine();
182 if (ImGui::Button(tr("Delete"))) {
184 selected_index_ = -1;
186 }
187 }
188 }
189
190 // ─── Public API for DungeonMapPanel integration ───────────────
191
195 std::vector<const AnnotationEntry*> GetAnnotationsForRoom(int room_id) const {
196 std::vector<const AnnotationEntry*> result;
197 for (const auto& ann : annotations_) {
198 if (ann.room_id == room_id) {
199 result.push_back(&ann);
200 }
201 }
202 return result;
203 }
204
209 int GetMaxPriorityForRoom(int room_id) const {
210 int max_pri = -1;
211 for (const auto& ann : annotations_) {
212 if (ann.room_id == room_id) {
213 max_pri = std::max(max_pri, static_cast<int>(ann.priority));
214 }
215 }
216 return max_pri;
217 }
218
219 static ImU32 GetPriorityColor(AnnotationPriority priority) {
220 switch (priority) {
222 return IM_COL32(220, 50, 50, 255); // Red
224 return IM_COL32(220, 150, 30, 255); // Orange
226 default:
227 return IM_COL32(60, 120, 220, 255); // Blue
228 }
229 }
230
231 private:
233 annotations_.clear();
234 if (annotations_path_.empty())
235 return;
236
237 std::ifstream file(annotations_path_);
238 if (!file.is_open())
239 return;
240
241 try {
242 nlohmann::json root;
243 file >> root;
244
245 if (root.contains("annotations") && root["annotations"].is_array()) {
246 for (const auto& item : root["annotations"]) {
247 AnnotationEntry entry;
248 entry.room_id = item.value("room_id", 0);
249 entry.text = item.value("text", "");
250 entry.priority =
251 static_cast<AnnotationPriority>(item.value("priority", 0));
252 entry.category = item.value("category", "");
253 entry.created_at = item.value("created_at", "");
254 annotations_.push_back(std::move(entry));
255 }
256 }
257 } catch (...) {
258 // Silently ignore parse errors
259 }
260 }
261
263 if (annotations_path_.empty())
264 return;
265
266 nlohmann::json root;
267 nlohmann::json arr = nlohmann::json::array();
268
269 for (const auto& ann : annotations_) {
270 nlohmann::json item;
271 item["room_id"] = ann.room_id;
272 item["text"] = ann.text;
273 item["priority"] = static_cast<int>(ann.priority);
274 item["category"] = ann.category;
275 item["created_at"] = ann.created_at;
276 arr.push_back(item);
277 }
278
279 root["annotations"] = arr;
280
281 std::ofstream file(annotations_path_);
282 if (file.is_open()) {
283 file << root.dump(2) << std::endl;
284 }
285 }
286
287 std::string annotations_path_;
288 std::vector<AnnotationEntry> annotations_;
289
290 // Filter state
291 int filter_room_id_ = -1;
292 int filter_priority_ = 0;
293
294 // Edit state
295 int selected_index_ = -1;
296 int edit_room_ = 0;
297 char edit_text_[256] = {0};
298 int edit_priority_ = 0;
299 char edit_category_[64] = {0};
300};
301
302} // namespace yaze::editor
303
304#endif // YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
void SetAnnotationsPath(const std::string &path)
void Draw(bool *) override
Draw the panel content.
WindowLifecycle GetWindowLifecycle() const override
Get the lifecycle category for this window.
std::string GetWorkflowGroup() const override
Optional workflow group for hack-centric actions.
std::vector< const AnnotationEntry * > GetAnnotationsForRoom(int room_id) const
Get annotations for a specific room.
int GetMaxPriorityForRoom(int room_id) const
Get the highest priority for a room (for dot color). Returns -1 if no annotations.
std::string GetWorkflowDescription() const override
Optional workflow description for menus/command palette.
std::string GetEditorCategory() const override
Editor category this panel belongs to.
std::vector< AnnotationEntry > annotations_
static ImU32 GetPriorityColor(AnnotationPriority priority)
WindowPresentationPolicy GetPresentationPolicy() const override
How this content is hosted and advertised by workspace chrome.
std::string GetDisplayName() const override
Human-readable name shown in menus and title bars.
std::string GetId() const override
Unique identifier for this panel.
std::string GetIcon() const override
Material Design icon for this panel.
#define ICON_MD_NOTES
Definition icons.h:1332
::yaze::project::YazeProject * current_project()
Get the current project instance.
Editors are the view controllers for the application.
AnnotationPriority
Annotation priority levels.
WindowLifecycle
Defines lifecycle behavior for editor windows.
@ CrossEditor
User can pin to persist across editors.
A room-level annotation entry.
static WindowPresentationPolicy OptionalPopOut()