yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
window_browser.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cctype>
6#include <string>
7#include <utility>
8#include <vector>
9
10#include "absl/strings/str_format.h"
12#include "app/gui/core/icons.h"
16#include "imgui/imgui.h"
17
18namespace yaze {
19namespace editor {
20
21namespace {
22
23std::string LowercaseCopy(std::string value) {
24 std::transform(
25 value.begin(), value.end(), value.begin(),
26 [](unsigned char c) { return static_cast<char>(::tolower(c)); });
27 return value;
28}
29
30bool MatchesWindowSearch(const std::string& query,
31 const WindowDescriptor& window) {
32 if (query.empty()) {
33 return true;
34 }
35
36 const std::string search_str = LowercaseCopy(query);
37 return LowercaseCopy(window.display_name).find(search_str) !=
38 std::string::npos ||
39 LowercaseCopy(window.card_id).find(search_str) != std::string::npos ||
40 LowercaseCopy(window.shortcut_hint).find(search_str) !=
41 std::string::npos;
42}
43
44} // namespace
45
47 : window_manager_(window_manager) {}
48
49void WindowBrowser::Draw(size_t session_id, bool* p_open) {
50 const ImGuiViewport* viewport = ImGui::GetMainViewport();
51 ImVec2 max_window_size(1600.0f, 1000.0f);
52 ImVec2 default_window_size(1080.0f, 700.0f);
53 if (viewport) {
54 max_window_size = ImVec2(std::max(760.0f, viewport->WorkSize.x * 0.95f),
55 std::max(520.0f, viewport->WorkSize.y * 0.95f));
56 default_window_size = ImVec2(
57 std::clamp(viewport->WorkSize.x * 0.72f, 880.0f, max_window_size.x),
58 std::clamp(viewport->WorkSize.y * 0.76f, 600.0f, max_window_size.y));
59 const ImVec2 center(viewport->WorkPos.x + viewport->WorkSize.x * 0.5f,
60 viewport->WorkPos.y + viewport->WorkSize.y * 0.5f);
61 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
62 }
63
64 ImGui::SetNextWindowSize(default_window_size, ImGuiCond_Appearing);
65 ImGui::SetNextWindowSizeConstraints(ImVec2(780, 520), max_window_size);
66
67 if (ImGui::Begin(
68 absl::StrFormat("%s Window Browser", ICON_MD_DASHBOARD).c_str(),
69 p_open, ImGuiWindowFlags_NoDocking)) {
70 std::vector<std::string> categories =
72 std::sort(categories.begin(), categories.end());
73 if (category_filter_ != "All" &&
74 std::find(categories.begin(), categories.end(), category_filter_) ==
75 categories.end()) {
76 category_filter_ = "All";
77 }
78
79 const auto all_windows = window_manager_.GetWindowsInSession(session_id);
80 auto count_windows = [&](const std::string& category,
81 bool visible_only) -> int {
82 int count = 0;
83 for (const auto& window_id : all_windows) {
84 const auto* window =
85 window_manager_.GetWindowDescriptor(session_id, window_id);
86 if (!window) {
87 continue;
88 }
89 if (!window->IsListedInWindowBrowser()) {
90 continue;
91 }
92 if (category != "All" && window->category != category) {
93 continue;
94 }
95 const bool visible =
96 window->visibility_flag && *window->visibility_flag;
97 if (!visible_only || visible) {
98 ++count;
99 }
100 }
101 return count;
102 };
103 auto set_filtered_windows_visible = [&](const std::string& category,
104 bool visible) {
105 for (const auto& window_id : all_windows) {
106 const auto* window =
107 window_manager_.GetWindowDescriptor(session_id, window_id);
108 if (!window) {
109 continue;
110 }
111 if (!window->IsListedInWindowBrowser()) {
112 continue;
113 }
114 if (category != "All" && window->category != category) {
115 continue;
116 }
117 if (visible) {
118 window_manager_.OpenWindow(session_id, window->card_id);
119 } else {
120 window_manager_.CloseWindow(session_id, window->card_id);
121 }
122 }
123 };
124
125 ImGui::SetNextItemWidth(
126 std::max(280.0f, ImGui::GetContentRegionAvail().x * 0.45f));
127 ImGui::InputTextWithHint(
128 "##Search",
129 absl::StrFormat("%s Search windows...", ICON_MD_SEARCH).c_str(),
131 ImGui::SameLine();
132 if (ImGui::Button(ICON_MD_CLEAR " Clear")) {
133 search_filter_[0] = '\0';
134 }
135 ImGui::SameLine();
136 if (category_filter_ == "All") {
137 if (ImGui::Button(ICON_MD_VISIBILITY " Show All")) {
138 set_filtered_windows_visible("All", true);
139 }
140 ImGui::SameLine();
141 if (ImGui::Button(ICON_MD_VISIBILITY_OFF " Hide All")) {
142 set_filtered_windows_visible("All", false);
143 }
144 } else {
145 if (ImGui::Button(ICON_MD_VISIBILITY " Show Category")) {
146 set_filtered_windows_visible(category_filter_, true);
147 }
148 ImGui::SameLine();
149 if (ImGui::Button(ICON_MD_VISIBILITY_OFF " Hide Category")) {
150 set_filtered_windows_visible(category_filter_, false);
151 }
152 }
153
154 ImGui::Separator();
155
156 const float content_height = ImGui::GetContentRegionAvail().y;
157 const float max_sidebar_width =
158 std::max(220.0f, ImGui::GetContentRegionAvail().x * 0.50f);
159 float category_sidebar_width =
161 max_sidebar_width);
162
163 if (ImGui::BeginChild("##WindowBrowserCategories",
164 ImVec2(category_sidebar_width, content_height),
165 true)) {
166 const int visible_total = count_windows("All", true);
167 const int filtered_total = count_windows("All", false);
168 std::string all_label =
169 absl::StrFormat("%s All Windows (%d/%d)", ICON_MD_DASHBOARD,
170 visible_total, filtered_total);
171 if (ImGui::Selectable(all_label.c_str(), category_filter_ == "All")) {
172 category_filter_ = "All";
173 }
174 ImGui::Separator();
175
176 for (const auto& category : categories) {
177 const int category_total = count_windows(category, false);
178 if (category_total <= 0) {
179 continue;
180 }
181 const int visible_in_category = count_windows(category, true);
182 const std::string icon =
184 std::string label =
185 absl::StrFormat("%s %s (%d/%d)", icon.c_str(), category.c_str(),
186 visible_in_category, category_total);
187 if (ImGui::Selectable(label.c_str(), category_filter_ == category)) {
188 category_filter_ = category;
189 }
190 }
191 }
192 ImGui::EndChild();
193
194 ImGui::SameLine(0.0f, 0.0f);
195
196 const float splitter_width = 6.0f;
197 const ImVec2 splitter_pos = ImGui::GetCursorScreenPos();
198 ImGui::InvisibleButton("##WindowBrowserSplitter",
199 ImVec2(splitter_width, content_height));
200 const bool splitter_hovered = ImGui::IsItemHovered();
201 const bool splitter_active = ImGui::IsItemActive();
202 if (splitter_hovered || splitter_active) {
203 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
204 }
205 if (splitter_hovered &&
206 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
209 category_sidebar_width =
211 max_sidebar_width);
212 }
213 if (splitter_active) {
214 category_sidebar_width =
215 std::clamp(category_sidebar_width + ImGui::GetIO().MouseDelta.x,
216 220.0f, max_sidebar_width);
217 window_manager_.SetWindowBrowserCategoryWidth(category_sidebar_width);
218 ImGui::SetTooltip(tr("Width: %.0f px"), category_sidebar_width);
219 }
220
221 ImVec4 splitter_color = gui::GetOutlineVec4();
222 splitter_color.w =
223 splitter_active ? 0.95f : (splitter_hovered ? 0.72f : 0.35f);
224 ImGui::GetWindowDrawList()->AddLine(
225 ImVec2(splitter_pos.x + splitter_width * 0.5f, splitter_pos.y),
226 ImVec2(splitter_pos.x + splitter_width * 0.5f,
227 splitter_pos.y + content_height),
228 ImGui::GetColorU32(splitter_color), splitter_active ? 2.0f : 1.0f);
229
230 ImGui::SameLine(0.0f, 0.0f);
231
232 if (ImGui::BeginChild("##WindowBrowserTable", ImVec2(0, content_height),
233 false)) {
234 if (ImGui::BeginTable("##WindowTable", 5,
235 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
236 ImGuiTableFlags_Borders |
237 ImGuiTableFlags_Resizable)) {
238 ImGui::TableSetupColumn("Visible", ImGuiTableColumnFlags_WidthFixed,
239 60);
240 ImGui::TableSetupColumn("Pin", ImGuiTableColumnFlags_WidthFixed,
241 gui::ScaledSize(36.0f, 0.0f).x);
242 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
243 ImGui::TableSetupColumn("Category", ImGuiTableColumnFlags_WidthFixed,
244 130);
245 ImGui::TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthFixed,
246 110);
247 ImGui::TableHeadersRow();
248
249 auto windows = (category_filter_ == "All") ? all_windows
250 : std::vector<std::string>{};
251 if (category_filter_ != "All") {
252 auto category_windows = window_manager_.GetWindowsInCategory(
253 session_id, category_filter_);
254 windows.reserve(category_windows.size());
255 for (const auto& window : category_windows) {
256 if (!window.IsListedInWindowBrowser()) {
257 continue;
258 }
259 windows.push_back(window.card_id);
260 }
261 }
262
263 for (const auto& window_id : windows) {
264 const auto* window =
265 window_manager_.GetWindowDescriptor(session_id, window_id);
266 if (!window) {
267 continue;
268 }
269 if (!window->IsListedInWindowBrowser()) {
270 continue;
271 }
272
273 if (!MatchesWindowSearch(search_filter_, *window)) {
274 continue;
275 }
276
277 ImGui::TableNextRow();
278
279 ImGui::TableNextColumn();
280 if (window->visibility_flag) {
281 bool visible = *window->visibility_flag;
282 if (ImGui::Checkbox(
283 absl::StrFormat("##vis_%s", window->card_id).c_str(),
284 &visible)) {
285 window_manager_.ToggleWindow(session_id, window->card_id);
286 }
287 }
288
289 ImGui::TableNextColumn();
290 const bool is_pinned =
291 window_manager_.IsWindowPinned(window->card_id);
292 ImVec4 pin_color =
294 if (!is_pinned) {
295 pin_color.w *= 0.72f;
296 }
297 const ImVec2 pin_size = gui::ScaledSize(30.0f, 30.0f);
298 ImGui::PushID(
299 absl::StrFormat("browser_pin_%s", window->card_id).c_str());
301 ICON_MD_PUSH_PIN, pin_size,
302 is_pinned ? "Unpin window" : "Pin window", is_pinned,
303 pin_color, "window_browser", window->card_id.c_str())) {
304 window_manager_.SetWindowPinned(session_id, window->card_id,
305 !is_pinned);
306 }
307 ImGui::PopID();
308
309 ImGui::TableNextColumn();
310 ImGui::Text("%s %s", window->icon.c_str(),
311 window->display_name.c_str());
312 if (ImGui::IsItemHovered() &&
313 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
314 window_manager_.OpenWindow(session_id, window->card_id);
315 const std::string window_name =
317 if (!window_name.empty()) {
318 ImGui::SetWindowFocus(window_name.c_str());
319 }
320 }
321
322 ImGui::TableNextColumn();
323 ImGui::TextUnformatted(window->category.c_str());
324
325 ImGui::TableNextColumn();
326 if (window->shortcut_hint.empty()) {
327 ImGui::TextDisabled("-");
328 } else {
329 ImGui::TextDisabled("%s", window->shortcut_hint.c_str());
330 }
331 }
332
333 ImGui::EndTable();
334 }
335 }
336 ImGui::EndChild();
337 }
338 ImGui::End();
339}
340
341} // namespace editor
342} // namespace yaze
WindowBrowser(WorkspaceWindowManager &window_manager)
WorkspaceWindowManager & window_manager_
void Draw(size_t session_id, bool *p_open)
Central registry for all editor cards with session awareness and dependency injection.
std::string GetWorkspaceWindowName(size_t session_id, const std::string &base_window_id) const
Resolve the exact ImGui window name for a panel by base ID.
std::vector< WindowDescriptor > GetWindowsInCategory(size_t session_id, const std::string &category) const
const WindowDescriptor * GetWindowDescriptor(size_t session_id, const std::string &base_window_id) const
std::vector< std::string > GetWindowsInSession(size_t session_id) const
void SetWindowPinned(size_t session_id, const std::string &base_window_id, bool pinned)
bool CloseWindow(size_t session_id, const std::string &base_window_id)
std::vector< std::string > GetAllWindowCategories(size_t session_id) const
static std::string GetCategoryIcon(const std::string &category)
bool OpenWindow(size_t session_id, const std::string &base_window_id)
static constexpr float GetDefaultWindowBrowserCategoryWidth()
bool IsWindowPinned(size_t session_id, const std::string &base_window_id) const
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
void SetWindowBrowserCategoryWidth(float width, bool notify=true)
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_VISIBILITY_OFF
Definition icons.h:2102
#define ICON_MD_CLEAR
Definition icons.h:416
#define ICON_MD_DASHBOARD
Definition icons.h:517
#define ICON_MD_PUSH_PIN
Definition icons.h:1529
bool MatchesWindowSearch(const std::string &query, const WindowDescriptor &window)
bool TransparentIconButton(const char *icon, const ImVec2 &size, const char *tooltip, bool is_active, const ImVec4 &active_color, const char *panel_id, const char *anim_id)
Draw a transparent icon button (hover effect only).
ImVec4 GetPrimaryVec4()
ImVec2 ScaledSize(float base_width, float base_height)
Returns a DPI-scaled version of a fixed pixel size.
Definition ui_config.h:148
ImVec4 GetTextSecondaryVec4()
ImVec4 GetOutlineVec4()
Metadata for a dockable editor window (formerly PanelInfo)