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
13#include "app/gui/core/icons.h"
14#include "core/project.h"
15#include "imgui/imgui.h"
16#include "nlohmann/json.hpp"
17
18namespace yaze::editor {
19
23enum class AnnotationPriority : int {
24 kNote = 0,
25 kBug = 1,
26 kBlocker = 2,
27};
28
33 int room_id = 0;
34 std::string text;
36 std::string category; // "design", "test", "bug", etc.
37 std::string created_at;
38};
39
54 public:
55 std::string GetId() const override { return "oracle.annotations"; }
56 std::string GetDisplayName() const override { return "Annotations"; }
57 std::string GetIcon() const override { return ICON_MD_NOTES; }
58 std::string GetEditorCategory() const override { return "Oracle"; }
61 }
62
63 void SetAnnotationsPath(const std::string& path) {
64 annotations_path_ = path;
66 }
67
68 void Draw(bool* /*p_open*/) override {
69 // Lazily discover annotations path from project context
70 if (annotations_path_.empty()) {
71 if (auto* project = ContentRegistry::Context::current_project()) {
72 if (!project->code_folder.empty()) {
73 namespace fs = std::filesystem;
74 fs::path candidate =
75 fs::path(project->GetAbsolutePath(project->code_folder)) /
76 "Docs" / "Dev" / "Planning" / "annotations.json";
77 SetAnnotationsPath(candidate.string());
78 }
79 }
80 }
81
82 // Filter controls
83 ImGui::Text("Room filter:");
84 ImGui::SameLine();
85 ImGui::InputInt("##room_filter", &filter_room_id_);
86 ImGui::SameLine();
87 if (ImGui::Button("All")) filter_room_id_ = -1;
88
89 ImGui::SameLine();
90 const char* priorities[] = {"All", "Note", "Bug", "Blocker"};
91 ImGui::Combo("Priority", &filter_priority_, priorities, 4);
92
93 ImGui::Separator();
94
95 // Annotation list
96 ImGui::BeginChild("annotation_list", ImVec2(0, -120),
97 ImGuiChildFlags_Borders);
98
99 for (size_t i = 0; i < annotations_.size(); ++i) {
100 const auto& ann = annotations_[i];
101
102 // Apply filters
103 if (filter_room_id_ >= 0 && ann.room_id != filter_room_id_) continue;
104 if (filter_priority_ > 0 &&
105 static_cast<int>(ann.priority) != (filter_priority_ - 1))
106 continue;
107
108 ImU32 dot_color = GetPriorityColor(ann.priority);
109 ImVec2 cursor = ImGui::GetCursorScreenPos();
110 ImGui::GetWindowDrawList()->AddCircleFilled(
111 ImVec2(cursor.x + 6, cursor.y + 10), 5.0f, dot_color);
112 ImGui::Dummy(ImVec2(16, 0));
113 ImGui::SameLine();
114
115 char label[128];
116 snprintf(label, sizeof(label), "Room 0x%02X: %s##ann_%zu", ann.room_id,
117 ann.text.c_str(), i);
118
119 if (ImGui::Selectable(label, selected_index_ == static_cast<int>(i))) {
120 selected_index_ = static_cast<int>(i);
121 // Copy to edit buffer
122 edit_room_ = ann.room_id;
123 snprintf(edit_text_, sizeof(edit_text_), "%s", ann.text.c_str());
124 edit_priority_ = static_cast<int>(ann.priority);
125 snprintf(edit_category_, sizeof(edit_category_), "%s",
126 ann.category.c_str());
127 }
128 }
129
130 ImGui::EndChild();
131
132 ImGui::Separator();
133
134 // Edit / Add form
135 ImGui::Text("Room:");
136 ImGui::SameLine();
137 ImGui::SetNextItemWidth(80);
138 ImGui::InputInt("##edit_room", &edit_room_);
139
140 ImGui::SameLine();
141 ImGui::Text("Priority:");
142 ImGui::SameLine();
143 ImGui::SetNextItemWidth(80);
144 const char* pri_names[] = {"Note", "Bug", "Blocker"};
145 ImGui::Combo("##edit_priority", &edit_priority_, pri_names, 3);
146
147 ImGui::InputText("Text", edit_text_, sizeof(edit_text_));
148 ImGui::InputText("Category", edit_category_, sizeof(edit_category_));
149
150 if (ImGui::Button("Add")) {
151 AnnotationEntry entry;
152 entry.room_id = edit_room_;
153 entry.text = edit_text_;
154 entry.priority = static_cast<AnnotationPriority>(edit_priority_);
155 entry.category = edit_category_;
156 annotations_.push_back(entry);
158 }
159
160 ImGui::SameLine();
161 if (selected_index_ >= 0 &&
162 selected_index_ < static_cast<int>(annotations_.size())) {
163 if (ImGui::Button("Update")) {
164 auto& ann = annotations_[selected_index_];
165 ann.room_id = edit_room_;
166 ann.text = edit_text_;
167 ann.priority = static_cast<AnnotationPriority>(edit_priority_);
168 ann.category = edit_category_;
170 }
171 ImGui::SameLine();
172 if (ImGui::Button("Delete")) {
174 selected_index_ = -1;
176 }
177 }
178 }
179
180 // ─── Public API for DungeonMapPanel integration ───────────────
181
185 std::vector<const AnnotationEntry*> GetAnnotationsForRoom(
186 int room_id) const {
187 std::vector<const AnnotationEntry*> result;
188 for (const auto& ann : annotations_) {
189 if (ann.room_id == room_id) {
190 result.push_back(&ann);
191 }
192 }
193 return result;
194 }
195
200 int GetMaxPriorityForRoom(int room_id) const {
201 int max_pri = -1;
202 for (const auto& ann : annotations_) {
203 if (ann.room_id == room_id) {
204 max_pri = std::max(max_pri, static_cast<int>(ann.priority));
205 }
206 }
207 return max_pri;
208 }
209
210 static ImU32 GetPriorityColor(AnnotationPriority priority) {
211 switch (priority) {
213 return IM_COL32(220, 50, 50, 255); // Red
215 return IM_COL32(220, 150, 30, 255); // Orange
217 default:
218 return IM_COL32(60, 120, 220, 255); // Blue
219 }
220 }
221
222 private:
224 annotations_.clear();
225 if (annotations_path_.empty()) return;
226
227 std::ifstream file(annotations_path_);
228 if (!file.is_open()) return;
229
230 try {
231 nlohmann::json root;
232 file >> root;
233
234 if (root.contains("annotations") && root["annotations"].is_array()) {
235 for (const auto& item : root["annotations"]) {
236 AnnotationEntry entry;
237 entry.room_id = item.value("room_id", 0);
238 entry.text = item.value("text", "");
239 entry.priority =
240 static_cast<AnnotationPriority>(item.value("priority", 0));
241 entry.category = item.value("category", "");
242 entry.created_at = item.value("created_at", "");
243 annotations_.push_back(std::move(entry));
244 }
245 }
246 } catch (...) {
247 // Silently ignore parse errors
248 }
249 }
250
252 if (annotations_path_.empty()) return;
253
254 nlohmann::json root;
255 nlohmann::json arr = nlohmann::json::array();
256
257 for (const auto& ann : annotations_) {
258 nlohmann::json item;
259 item["room_id"] = ann.room_id;
260 item["text"] = ann.text;
261 item["priority"] = static_cast<int>(ann.priority);
262 item["category"] = ann.category;
263 item["created_at"] = ann.created_at;
264 arr.push_back(item);
265 }
266
267 root["annotations"] = arr;
268
269 std::ofstream file(annotations_path_);
270 if (file.is_open()) {
271 file << root.dump(2) << std::endl;
272 }
273 }
274
275 std::string annotations_path_;
276 std::vector<AnnotationEntry> annotations_;
277
278 // Filter state
281
282 // Edit state
284 int edit_room_ = 0;
285 char edit_text_[256] = {0};
287 char edit_category_[64] = {0};
288};
289
290} // namespace yaze::editor
291
292#endif // YAZE_APP_EDITOR_ORACLE_PANELS_ANNOTATION_OVERLAY_PANEL_H
Room-level annotation management for Oracle projects.
void SetAnnotationsPath(const std::string &path)
void Draw(bool *) override
Draw the panel content.
std::vector< AnnotationEntry > annotations_
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 GetEditorCategory() const override
Editor category this panel belongs to.
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.
PanelCategory GetPanelCategory() const override
Get the lifecycle category for this panel.
std::string GetIcon() const override
Material Design icon for this panel.
Base interface for all logical panel components.
#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.
PanelCategory
Defines lifecycle behavior for editor panels.
@ CrossEditor
User can pin to persist across editors.
A room-level annotation entry.