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"; }
60 std::string GetWorkflowGroup() const override { return "Planning"; }
61 std::string GetWorkflowDescription() const override {
62 return "Review and edit room-level project annotations";
63 }
67
68 void SetAnnotationsPath(const std::string& path) {
69 annotations_path_ = path;
71 }
72
73 void Draw(bool* /*p_open*/) override {
74 // Lazily discover annotations path from project context
75 if (annotations_path_.empty()) {
76 if (auto* project = ContentRegistry::Context::current_project()) {
77 if (!project->code_folder.empty()) {
78 namespace fs = std::filesystem;
79 fs::path candidate =
80 fs::path(project->GetAbsolutePath(project->code_folder)) /
81 "Docs" / "Dev" / "Planning" / "annotations.json";
82 SetAnnotationsPath(candidate.string());
83 }
84 }
85 }
86
87 // Filter controls
88 ImGui::Text(tr("Room filter:"));
89 ImGui::SameLine();
90 ImGui::InputInt("##room_filter", &filter_room_id_);
91 ImGui::SameLine();
92 if (ImGui::Button(tr("All")))
93 filter_room_id_ = -1;
94
95 ImGui::SameLine();
96 const char* priorities[] = {"All", "Note", "Bug", "Blocker"};
97 ImGui::Combo(tr("Priority"), &filter_priority_, priorities, 4);
98
99 ImGui::Separator();
100
101 // Annotation list
102 ImGui::BeginChild("annotation_list", ImVec2(0, -120),
103 ImGuiChildFlags_Borders);
104
105 for (size_t i = 0; i < annotations_.size(); ++i) {
106 const auto& ann = annotations_[i];
107
108 // Apply filters
109 if (filter_room_id_ >= 0 && ann.room_id != filter_room_id_)
110 continue;
111 if (filter_priority_ > 0 &&
112 static_cast<int>(ann.priority) != (filter_priority_ - 1))
113 continue;
114
115 ImU32 dot_color = GetPriorityColor(ann.priority);
116 ImVec2 cursor = ImGui::GetCursorScreenPos();
117 ImGui::GetWindowDrawList()->AddCircleFilled(
118 ImVec2(cursor.x + 6, cursor.y + 10), 5.0f, dot_color);
119 ImGui::Dummy(ImVec2(16, 0));
120 ImGui::SameLine();
121
122 char label[128];
123 snprintf(label, sizeof(label), "Room 0x%02X: %s##ann_%zu", ann.room_id,
124 ann.text.c_str(), i);
125
126 if (ImGui::Selectable(label, selected_index_ == static_cast<int>(i))) {
127 selected_index_ = static_cast<int>(i);
128 // Copy to edit buffer
129 edit_room_ = ann.room_id;
130 snprintf(edit_text_, sizeof(edit_text_), "%s", ann.text.c_str());
131 edit_priority_ = static_cast<int>(ann.priority);
132 snprintf(edit_category_, sizeof(edit_category_), "%s",
133 ann.category.c_str());
134 }
135 }
136
137 ImGui::EndChild();
138
139 ImGui::Separator();
140
141 // Edit / Add form
142 ImGui::Text(tr("Room:"));
143 ImGui::SameLine();
144 ImGui::SetNextItemWidth(80);
145 ImGui::InputInt("##edit_room", &edit_room_);
146
147 ImGui::SameLine();
148 ImGui::Text(tr("Priority:"));
149 ImGui::SameLine();
150 ImGui::SetNextItemWidth(80);
151 const char* pri_names[] = {"Note", "Bug", "Blocker"};
152 ImGui::Combo("##edit_priority", &edit_priority_, pri_names, 3);
153
154 ImGui::InputText(tr("Text"), edit_text_, sizeof(edit_text_));
155 ImGui::InputText(tr("Category"), edit_category_, sizeof(edit_category_));
156
157 if (ImGui::Button(tr("Add"))) {
158 AnnotationEntry entry;
159 entry.room_id = edit_room_;
160 entry.text = edit_text_;
161 entry.priority = static_cast<AnnotationPriority>(edit_priority_);
162 entry.category = edit_category_;
163 annotations_.push_back(entry);
165 }
166
167 ImGui::SameLine();
168 if (selected_index_ >= 0 &&
169 selected_index_ < static_cast<int>(annotations_.size())) {
170 if (ImGui::Button(tr("Update"))) {
171 auto& ann = annotations_[selected_index_];
172 ann.room_id = edit_room_;
173 ann.text = edit_text_;
174 ann.priority = static_cast<AnnotationPriority>(edit_priority_);
175 ann.category = edit_category_;
177 }
178 ImGui::SameLine();
179 if (ImGui::Button(tr("Delete"))) {
181 selected_index_ = -1;
183 }
184 }
185 }
186
187 // ─── Public API for DungeonMapPanel integration ───────────────
188
192 std::vector<const AnnotationEntry*> GetAnnotationsForRoom(int room_id) const {
193 std::vector<const AnnotationEntry*> result;
194 for (const auto& ann : annotations_) {
195 if (ann.room_id == room_id) {
196 result.push_back(&ann);
197 }
198 }
199 return result;
200 }
201
206 int GetMaxPriorityForRoom(int room_id) const {
207 int max_pri = -1;
208 for (const auto& ann : annotations_) {
209 if (ann.room_id == room_id) {
210 max_pri = std::max(max_pri, static_cast<int>(ann.priority));
211 }
212 }
213 return max_pri;
214 }
215
216 static ImU32 GetPriorityColor(AnnotationPriority priority) {
217 switch (priority) {
219 return IM_COL32(220, 50, 50, 255); // Red
221 return IM_COL32(220, 150, 30, 255); // Orange
223 default:
224 return IM_COL32(60, 120, 220, 255); // Blue
225 }
226 }
227
228 private:
230 annotations_.clear();
231 if (annotations_path_.empty())
232 return;
233
234 std::ifstream file(annotations_path_);
235 if (!file.is_open())
236 return;
237
238 try {
239 nlohmann::json root;
240 file >> root;
241
242 if (root.contains("annotations") && root["annotations"].is_array()) {
243 for (const auto& item : root["annotations"]) {
244 AnnotationEntry entry;
245 entry.room_id = item.value("room_id", 0);
246 entry.text = item.value("text", "");
247 entry.priority =
248 static_cast<AnnotationPriority>(item.value("priority", 0));
249 entry.category = item.value("category", "");
250 entry.created_at = item.value("created_at", "");
251 annotations_.push_back(std::move(entry));
252 }
253 }
254 } catch (...) {
255 // Silently ignore parse errors
256 }
257 }
258
260 if (annotations_path_.empty())
261 return;
262
263 nlohmann::json root;
264 nlohmann::json arr = nlohmann::json::array();
265
266 for (const auto& ann : annotations_) {
267 nlohmann::json item;
268 item["room_id"] = ann.room_id;
269 item["text"] = ann.text;
270 item["priority"] = static_cast<int>(ann.priority);
271 item["category"] = ann.category;
272 item["created_at"] = ann.created_at;
273 arr.push_back(item);
274 }
275
276 root["annotations"] = arr;
277
278 std::ofstream file(annotations_path_);
279 if (file.is_open()) {
280 file << root.dump(2) << std::endl;
281 }
282 }
283
284 std::string annotations_path_;
285 std::vector<AnnotationEntry> annotations_;
286
287 // Filter state
288 int filter_room_id_ = -1;
289 int filter_priority_ = 0;
290
291 // Edit state
292 int selected_index_ = -1;
293 int edit_room_ = 0;
294 char edit_text_[256] = {0};
295 int edit_priority_ = 0;
296 char edit_category_[64] = {0};
297};
298
299} // namespace yaze::editor
300
301#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)
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.