yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
command_palette.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <chrono>
6#include <filesystem>
7#include <fstream>
8
9#include "absl/strings/str_format.h"
15#include "core/project.h"
16#include "util/json.h"
17#include "util/log.h"
19
20namespace yaze {
21namespace editor {
22
23void CommandPalette::AddCommand(const std::string& name,
24 const std::string& category,
25 const std::string& description,
26 const std::string& shortcut,
27 std::function<void()> callback) {
28 CommandEntry entry;
29 entry.name = name;
30 entry.category = category;
31 entry.description = description;
32 entry.shortcut = shortcut;
33 entry.callback = callback;
35 commands_[name] = entry;
36}
37
39 commands_.clear();
40 providers_.clear();
42}
43
45 std::unique_ptr<CommandProvider> provider) {
46 if (!provider)
47 return;
48 const std::string id = provider->ProviderId();
49 if (id.empty()) {
50 LOG_WARN("CommandPalette",
51 "Provider with empty id refused; skipping registration");
52 return;
53 }
54 // Replace any prior provider with the same id (warn so duplicate-id bugs
55 // don't hide silently).
56 for (auto it = providers_.begin(); it != providers_.end(); ++it) {
57 if ((*it)->ProviderId() == id) {
58 LOG_WARN("CommandPalette", "Replacing existing CommandProvider '%s'",
59 id.c_str());
61 providers_.erase(it);
62 break;
63 }
64 }
65 CommandProvider* raw = provider.get();
66 providers_.push_back(std::move(provider));
68 raw->Provide(this);
70}
71
72void CommandPalette::UnregisterProvider(const std::string& provider_id) {
73 RemoveProviderCommands(provider_id);
74 providers_.erase(
75 std::remove_if(providers_.begin(), providers_.end(),
76 [&](const std::unique_ptr<CommandProvider>& p) {
77 return p && p->ProviderId() == provider_id;
78 }),
79 providers_.end());
80}
81
82void CommandPalette::RefreshProvider(const std::string& provider_id) {
83 for (auto& provider : providers_) {
84 if (provider && provider->ProviderId() == provider_id) {
85 RemoveProviderCommands(provider_id);
86 current_provider_id_ = provider_id;
87 provider->Provide(this);
89 return;
90 }
91 }
92}
93
95 // Snapshot ids so we don't iterate while mutating.
96 std::vector<std::string> ids;
97 ids.reserve(providers_.size());
98 for (const auto& provider : providers_) {
99 if (provider)
100 ids.push_back(provider->ProviderId());
101 }
102 for (const auto& id : ids) {
103 RefreshProvider(id);
104 }
105}
106
107void CommandPalette::RemoveProviderCommands(const std::string& provider_id) {
108 if (provider_id.empty())
109 return;
110 for (auto it = commands_.begin(); it != commands_.end();) {
111 if (it->second.provider_id == provider_id) {
112 it = commands_.erase(it);
113 } else {
114 ++it;
115 }
116 }
117}
118
119void CommandPalette::RecordUsage(const std::string& name) {
120 auto it = commands_.find(name);
121 if (it != commands_.end()) {
122 it->second.usage_count++;
123 it->second.last_used_ms =
124 std::chrono::duration_cast<std::chrono::milliseconds>(
125 std::chrono::system_clock::now().time_since_epoch())
126 .count();
127 }
128}
129
130/*static*/ int CommandPalette::FuzzyScore(const std::string& text,
131 const std::string& query) {
132 if (query.empty())
133 return 0;
134
135 int score = 0;
136 size_t text_idx = 0;
137 size_t query_idx = 0;
138
139 std::string text_lower = text;
140 std::string query_lower = query;
141 std::transform(text_lower.begin(), text_lower.end(), text_lower.begin(),
142 ::tolower);
143 std::transform(query_lower.begin(), query_lower.end(), query_lower.begin(),
144 ::tolower);
145
146 // Exact match bonus
147 if (text_lower == query_lower)
148 return 1000;
149
150 // Starts with bonus
151 if (text_lower.find(query_lower) == 0)
152 return 500;
153
154 // Contains bonus
155 if (text_lower.find(query_lower) != std::string::npos)
156 return 250;
157
158 // Fuzzy match - characters in order
159 while (text_idx < text_lower.length() && query_idx < query_lower.length()) {
160 if (text_lower[text_idx] == query_lower[query_idx]) {
161 score += 10;
162 query_idx++;
163 }
164 text_idx++;
165 }
166
167 // Penalty if not all characters matched
168 if (query_idx != query_lower.length())
169 return 0;
170
171 return score;
172}
173
174std::vector<CommandEntry> CommandPalette::SearchCommands(
175 const std::string& query) {
176 std::vector<std::pair<int, CommandEntry>> scored;
177
178 for (const auto& [name, entry] : commands_) {
179 int score = FuzzyScore(entry.name, query);
180
181 // Also check category and description
182 score += FuzzyScore(entry.category, query) / 2;
183 score += FuzzyScore(entry.description, query) / 4;
184
185 // Frecency bonus (frequency + recency)
186 score += entry.usage_count * 2;
187
188 auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
189 std::chrono::system_clock::now().time_since_epoch())
190 .count();
191 int64_t age_ms = now_ms - entry.last_used_ms;
192 if (age_ms < 60000) { // Used in last minute
193 score += 50;
194 } else if (age_ms < 3600000) { // Last hour
195 score += 25;
196 }
197
198 if (score > 0) {
199 scored.push_back({score, entry});
200 }
201 }
202
203 // Sort by score descending
204 std::sort(scored.begin(), scored.end(),
205 [](const auto& a, const auto& b) { return a.first > b.first; });
206
207 std::vector<CommandEntry> results;
208 for (const auto& [score, entry] : scored) {
209 results.push_back(entry);
210 }
211
212 return results;
213}
214
215std::vector<CommandEntry> CommandPalette::GetRecentCommands(int limit) {
216 std::vector<CommandEntry> recent;
217
218 for (const auto& [name, entry] : commands_) {
219 if (entry.usage_count > 0) {
220 recent.push_back(entry);
221 }
222 }
223
224 std::sort(recent.begin(), recent.end(),
225 [](const CommandEntry& a, const CommandEntry& b) {
226 return a.last_used_ms > b.last_used_ms;
227 });
228
229 if (recent.size() > static_cast<size_t>(limit)) {
230 recent.resize(limit);
231 }
232
233 return recent;
234}
235
236std::vector<CommandEntry> CommandPalette::GetFrequentCommands(int limit) {
237 std::vector<CommandEntry> frequent;
238
239 for (const auto& [name, entry] : commands_) {
240 if (entry.usage_count > 0) {
241 frequent.push_back(entry);
242 }
243 }
244
245 std::sort(frequent.begin(), frequent.end(),
246 [](const CommandEntry& a, const CommandEntry& b) {
247 return a.usage_count > b.usage_count;
248 });
249
250 if (frequent.size() > static_cast<size_t>(limit)) {
251 frequent.resize(limit);
252 }
253
254 return frequent;
255}
256
257void CommandPalette::SaveHistory(const std::string& filepath) {
258 try {
259 yaze::Json j;
260 j["version"] = 1;
261 j["commands"] = yaze::Json::object();
262
263 for (const auto& [name, entry] : commands_) {
264 if (entry.usage_count > 0) {
265 yaze::Json cmd;
266 cmd["usage_count"] = entry.usage_count;
267 cmd["last_used_ms"] = entry.last_used_ms;
268 j["commands"][name] = cmd;
269 }
270 }
271
272 std::ofstream file(filepath);
273 if (file.is_open()) {
274 file << j.dump(2);
275 LOG_INFO("CommandPalette", "Saved command history to %s",
276 filepath.c_str());
277 }
278 } catch (const std::exception& e) {
279 LOG_ERROR("CommandPalette", "Failed to save command history: %s", e.what());
280 }
281}
282
283void CommandPalette::LoadHistory(const std::string& filepath) {
284 if (!std::filesystem::exists(filepath)) {
285 return;
286 }
287
288 try {
289 std::ifstream file(filepath);
290 if (!file.is_open()) {
291 return;
292 }
293
294 std::string content((std::istreambuf_iterator<char>(file)),
295 std::istreambuf_iterator<char>());
296 yaze::Json j = yaze::Json::parse(content);
297
298 if (!j.contains("commands") || !j["commands"].is_object()) {
299 return;
300 }
301
302 int loaded = 0;
303 for (auto& [name, cmd_json] : j["commands"].items()) {
304 auto it = commands_.find(name);
305 if (it != commands_.end()) {
306 it->second.usage_count = cmd_json.value("usage_count", 0);
307 it->second.last_used_ms = cmd_json.value("last_used_ms", int64_t{0});
308 loaded++;
309 }
310 }
311
312 LOG_INFO("CommandPalette", "Loaded %d command history entries from %s",
313 loaded, filepath.c_str());
314 } catch (const std::exception& e) {
315 LOG_ERROR("CommandPalette", "Failed to load command history: %s", e.what());
316 }
317}
318
319std::vector<CommandEntry> CommandPalette::GetAllCommands() const {
320 std::vector<CommandEntry> result;
321 result.reserve(commands_.size());
322 for (const auto& [name, entry] : commands_) {
323 result.push_back(entry);
324 }
325 return result;
326}
327
329 WorkspaceWindowManager* window_manager, size_t session_id) {
330 if (!window_manager)
331 return;
332
333 for (const auto& base_id : window_manager->GetWindowsInSession(session_id)) {
334 const auto* descriptor =
335 window_manager->GetWindowDescriptor(session_id, base_id);
336 if (!descriptor) {
337 continue;
338 }
339
340 // Create show command
341 std::string show_name =
342 absl::StrFormat("Show: %s", descriptor->display_name);
343 std::string show_desc =
344 absl::StrFormat("Open the %s window", descriptor->display_name);
345
346 AddCommand(show_name, CommandCategory::kPanel, show_desc,
347 descriptor->shortcut_hint,
348 [window_manager, base_id, session_id]() {
349 window_manager->OpenWindow(session_id, base_id);
350 });
351
352 // Create hide command
353 std::string hide_name =
354 absl::StrFormat("Hide: %s", descriptor->display_name);
355 std::string hide_desc =
356 absl::StrFormat("Close the %s window", descriptor->display_name);
357
358 AddCommand(hide_name, CommandCategory::kPanel, hide_desc, "",
359 [window_manager, base_id, session_id]() {
360 window_manager->CloseWindow(session_id, base_id);
361 });
362
363 // Create toggle command
364 std::string toggle_name =
365 absl::StrFormat("Toggle: %s", descriptor->display_name);
366 std::string toggle_desc = absl::StrFormat("Toggle the %s window visibility",
367 descriptor->display_name);
368
369 AddCommand(toggle_name, CommandCategory::kPanel, toggle_desc, "",
370 [window_manager, base_id, session_id]() {
371 window_manager->ToggleWindow(session_id, base_id);
372 });
373
374 // Pin-to-global toggle. Mirrors the sidebar right-click pin + the panel
375 // tab pin UI, so users who live in the command palette never need to
376 // reach for the sidebar just to make a panel survive editor switches.
377 std::string pin_toggle_name =
378 absl::StrFormat("Toggle Pin: %s", descriptor->display_name);
379 std::string pin_toggle_desc =
380 absl::StrFormat("Keep the %s window visible across editor switches",
381 descriptor->display_name);
382
383 AddCommand(pin_toggle_name, CommandCategory::kPanel, pin_toggle_desc, "",
384 [window_manager, base_id, session_id]() {
385 const bool pinned =
386 window_manager->IsWindowPinned(session_id, base_id);
387 window_manager->SetWindowPinned(session_id, base_id, !pinned);
388 });
389 }
390}
391
393 std::function<void(const std::string&)> switch_callback) {
394 // Get all editor categories
395 auto categories = EditorRegistry::GetAllEditorCategories();
396
397 for (const auto& category : categories) {
398 std::string name = absl::StrFormat("Switch to: %s Editor", category);
399 std::string desc =
400 absl::StrFormat("Switch to the %s editor category", category);
401
402 AddCommand(name, CommandCategory::kEditor, desc, "",
403 [switch_callback, category]() { switch_callback(category); });
404 }
405}
406
408 std::function<void(const std::string&)> apply_callback) {
409 struct ProfileInfo {
410 const char* id;
411 const char* name;
412 const char* description;
413 };
414
415 static const ProfileInfo profiles[] = {
416 {"code", "Code", "Focused editing workspace with minimal panel noise"},
417 {"debug", "Debug",
418 "Debugger-first workspace for tracing and memory tools"},
419 {"mapping", "Mapping",
420 "Map-centric workspace for overworld/dungeon flows"},
421 {"chat", "Chat + Agent",
422 "Agent collaboration workspace with chat-centric layout"},
423 };
424
425 for (const auto& profile : profiles) {
426 std::string name = absl::StrFormat("Apply Profile: %s", profile.name);
427 AddCommand(name, CommandCategory::kLayout, profile.description, "",
428 [apply_callback, profile_id = std::string(profile.id)]() {
429 apply_callback("profile:" + profile_id);
430 });
431 }
432
433 AddCommand("Capture Layout Snapshot", CommandCategory::kLayout,
434 "Capture current layout as temporary session snapshot", "",
435 [apply_callback]() { apply_callback("session:capture"); });
436 AddCommand("Restore Layout Snapshot", CommandCategory::kLayout,
437 "Restore temporary session snapshot", "",
438 [apply_callback]() { apply_callback("session:restore"); });
439 AddCommand("Clear Layout Snapshot", CommandCategory::kLayout,
440 "Clear temporary session snapshot", "",
441 [apply_callback]() { apply_callback("session:clear"); });
442
443 // Legacy named workspace presets
444 struct PresetInfo {
445 const char* name;
446 const char* description;
447 };
448
449 static const PresetInfo presets[] = {
450 {"Minimal", "Minimal workspace with essential panels only"},
451 {"Developer", "Debug-focused layout with emulator and memory tools"},
452 {"Designer", "Visual-focused layout for graphics and palette editing"},
453 {"Modder", "Full-featured layout with all panels available"},
454 {"Overworld Expert", "Optimized layout for overworld editing"},
455 {"Dungeon Expert", "Optimized layout for dungeon editing"},
456 {"Testing", "QA-focused layout with testing tools"},
457 {"Audio", "Music and sound editing focused layout"},
458 };
459
460 for (const auto& preset : presets) {
461 std::string name = absl::StrFormat("Apply Layout: %s", preset.name);
462
463 AddCommand(name, CommandCategory::kLayout, preset.description, "",
464 [apply_callback, preset_name = std::string(preset.name)]() {
465 apply_callback(preset_name);
466 });
467 }
468
469 // Reset to default layout command
470 AddCommand("Reset Layout: Default", CommandCategory::kLayout,
471 "Reset to the default layout for current editor", "",
472 [apply_callback]() { apply_callback("Default"); });
473}
474
476 std::function<void(const std::string&)> open_callback) {
477 const auto& recent_files =
479
480 for (const auto& filepath : recent_files) {
481 // Skip files that no longer exist
482 if (!std::filesystem::exists(filepath)) {
483 continue;
484 }
485
486 // Extract just the filename for display
487 std::filesystem::path path(filepath);
488 std::string filename = path.filename().string();
489
490 std::string name = absl::StrFormat("Open Recent: %s", filename);
491 std::string desc = absl::StrFormat("Open file %s", filepath);
492
493 AddCommand(name, CommandCategory::kFile, desc, "",
494 [open_callback, filepath]() { open_callback(filepath); });
495 }
496}
497
499 constexpr int kTotalRooms = 0x128;
500 for (int room_id = 0; room_id < kTotalRooms; ++room_id) {
501 const std::string label = zelda3::GetRoomLabel(room_id);
502 const std::string room_name =
503 label.empty() ? absl::StrFormat("Room %03X", room_id) : label;
504
505 const std::string name =
506 absl::StrFormat("Dungeon: Open Room [%03X] %s", room_id, room_name);
507 const std::string desc =
508 absl::StrFormat("Jump to dungeon room %03X", room_id);
509
511 name, CommandCategory::kNavigation, desc, "", [room_id, session_id]() {
512 if (auto* bus = ContentRegistry::Context::event_bus()) {
513 bus->Publish(JumpToRoomRequestEvent::Create(room_id, session_id));
514 }
515 });
516 }
517}
518
520 WorkspaceWindowManager* window_manager, size_t session_id) {
521 if (window_manager) {
522 const auto categories = window_manager->GetAllCategories(session_id);
523 for (const auto& category : categories) {
524 for (const auto& descriptor :
525 window_manager->GetWindowsInCategory(session_id, category)) {
526 if (descriptor.workflow_group.empty()) {
527 continue;
528 }
529 if (descriptor.enabled_condition && !descriptor.enabled_condition()) {
530 continue;
531 }
532 const std::string label = descriptor.workflow_label.empty()
533 ? descriptor.display_name
534 : descriptor.workflow_label;
535 const std::string group = descriptor.workflow_group.empty()
536 ? std::string("General")
537 : descriptor.workflow_group;
538 const std::string description =
539 descriptor.workflow_description.empty()
540 ? absl::StrFormat("Open %s", descriptor.display_name)
541 : descriptor.workflow_description;
543 absl::StrFormat("%s: %s", group, label), CommandCategory::kWorkflow,
544 description, descriptor.shortcut_hint,
545 [window_manager, session_id, panel_id = descriptor.card_id]() {
546 window_manager->OpenWindow(session_id, panel_id);
547 });
548 }
549 }
550 }
551
552 for (const auto& action : ContentRegistry::WorkflowActions::GetAll()) {
553 if (action.enabled && !action.enabled()) {
554 continue;
555 }
556 const std::string group =
557 action.group.empty() ? std::string("General") : action.group;
558 AddCommand(absl::StrFormat("%s: %s", group, action.label),
559 CommandCategory::kWorkflow, action.description, action.shortcut,
560 action.callback);
561 }
562}
563
565 const RecentProjectsModel* model,
566 const std::vector<std::string>& template_names,
567 std::function<void(const std::string&)> remove_callback,
568 std::function<void(const std::string&)> toggle_pin_callback,
569 std::function<void()> undo_remove_callback,
570 std::function<void()> clear_recents_callback,
571 std::function<void(const std::string&)> create_from_template_callback,
572 std::function<void()> dismiss_welcome_callback,
573 std::function<void()> show_welcome_callback) {
574 // Per-entry remove/pin commands. Keeping the palette fully driven by the
575 // same model that powers the welcome screen means this list stays in sync
576 // with pins/renames automatically on the next RefreshCommands().
577 if (model) {
578 for (const auto& entry : model->entries()) {
579 if (entry.unavailable)
580 continue; // Platform-gated; skip silently.
581 const std::string label = entry.display_name_override.empty()
582 ? entry.name
583 : entry.display_name_override;
584 const std::string path = entry.filepath;
585
586 if (remove_callback) {
587 const std::string name =
588 absl::StrFormat("Welcome: Remove Recent \"%s\"", label);
589 const std::string desc =
590 absl::StrFormat("Remove %s from the welcome screen's recents list.",
591 entry.filepath);
592 AddCommand(name, CommandCategory::kFile, desc, "",
593 [remove_callback, path]() { remove_callback(path); });
594 }
595 if (toggle_pin_callback) {
596 const std::string name = absl::StrFormat(
597 "Welcome: %s Recent \"%s\"", entry.pinned ? "Unpin" : "Pin", label);
598 const std::string desc =
599 absl::StrFormat("%s %s on the welcome screen.",
600 entry.pinned ? "Unpin" : "Pin", entry.filepath);
602 name, CommandCategory::kFile, desc, "",
603 [toggle_pin_callback, path]() { toggle_pin_callback(path); });
604 }
605 }
606 }
607
608 if (clear_recents_callback) {
609 AddCommand("Welcome: Clear Recent Files", CommandCategory::kFile,
610 "Forget every entry in the welcome screen's recent list.", "",
611 clear_recents_callback);
612 }
613
614 if (undo_remove_callback) {
615 AddCommand("Welcome: Undo Last Recent Removal", CommandCategory::kFile,
616 "Restore the last recent-project entry removed via Forget.", "",
617 undo_remove_callback);
618 }
619
620 if (create_from_template_callback) {
621 for (const auto& template_name : template_names) {
622 if (template_name.empty())
623 continue;
624 const std::string name = absl::StrFormat(
625 "Welcome: Create Project from Template: %s", template_name);
626 const std::string desc = absl::StrFormat(
627 "Start a new project using the \"%s\" template.", template_name);
628 AddCommand(name, CommandCategory::kFile, desc, "",
629 [create_from_template_callback, template_name]() {
630 create_from_template_callback(template_name);
631 });
632 }
633 }
634
635 if (show_welcome_callback) {
636 AddCommand("Welcome: Show Welcome Screen", CommandCategory::kView,
637 "Bring back the welcome screen if it's been dismissed.", "",
638 show_welcome_callback);
639 }
640 if (dismiss_welcome_callback) {
641 AddCommand("Welcome: Dismiss Welcome Screen", CommandCategory::kView,
642 "Hide the welcome screen for the rest of this session.", "",
643 dismiss_welcome_callback);
644 }
645}
646
647} // namespace editor
648} // namespace yaze
bool is_object() const
Definition json.h:57
static Json parse(const std::string &)
Definition json.h:36
static Json object()
Definition json.h:34
items_view items()
Definition json.h:88
std::string dump(int=-1, char=' ', bool=false, int=0) const
Definition json.h:91
bool contains(const std::string &) const
Definition json.h:53
std::vector< CommandEntry > SearchCommands(const std::string &query)
void RegisterDungeonRoomCommands(size_t session_id)
Register dungeon room navigation commands.
void RegisterWelcomeCommands(const RecentProjectsModel *model, const std::vector< std::string > &template_names, std::function< void(const std::string &)> remove_callback, std::function< void(const std::string &)> toggle_pin_callback, std::function< void()> undo_remove_callback, std::function< void()> clear_recents_callback, std::function< void(const std::string &)> create_from_template_callback, std::function< void()> dismiss_welcome_callback, std::function< void()> show_welcome_callback)
Expose welcome-screen actions through the command palette.
void SaveHistory(const std::string &filepath)
Save command usage history to disk.
void Clear()
Clear all commands (and forget every registered provider).
void RegisterLayoutCommands(std::function< void(const std::string &)> apply_callback)
Register layout preset commands.
void AddCommand(const std::string &name, const std::string &category, const std::string &description, const std::string &shortcut, std::function< void()> callback)
void LoadHistory(const std::string &filepath)
Load command usage history from disk.
void UnregisterProvider(const std::string &provider_id)
void RegisterPanelCommands(WorkspaceWindowManager *window_manager, size_t session_id)
Register all window toggle commands from WorkspaceWindowManager.
void RemoveProviderCommands(const std::string &provider_id)
std::vector< CommandEntry > GetAllCommands() const
Get all registered commands.
void RecordUsage(const std::string &name)
std::unordered_map< std::string, CommandEntry > commands_
void RegisterProvider(std::unique_ptr< CommandProvider > provider)
void RegisterWorkflowCommands(WorkspaceWindowManager *window_manager, size_t session_id)
Register hack workflow commands from workflow-aware panels/actions.
void RegisterEditorCommands(std::function< void(const std::string &)> switch_callback)
Register all editor switch commands.
std::vector< CommandEntry > GetRecentCommands(int limit=10)
void RefreshProviders()
Remove-and-re-run every registered provider.
std::vector< std::unique_ptr< CommandProvider > > providers_
void RegisterRecentFilesCommands(std::function< void(const std::string &)> open_callback)
Register commands to open recent files.
std::vector< CommandEntry > GetFrequentCommands(int limit=10)
static int FuzzyScore(const std::string &text, const std::string &query)
void RefreshProvider(const std::string &provider_id)
Plug-in command source for the palette.
virtual void Provide(CommandPalette *palette)=0
Populate palette with this source's commands.
static std::vector< std::string > GetAllEditorCategories()
Get all editor categories in display order for sidebar.
const std::vector< RecentProject > & entries() const
Central registry for all editor cards with session awareness and dependency injection.
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 > GetAllCategories(size_t session_id) const
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)
static RecentFilesManager & GetInstance()
Definition project.h:425
const std::vector< std::string > & GetRecentFiles() const
Definition project.h:462
#define LOG_ERROR(category, format,...)
Definition log.h:109
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105
::yaze::EventBus * event_bus()
Get the current EventBus instance.
std::string GetRoomLabel(int id)
Convenience function to get a room label.
static constexpr const char * kLayout
static constexpr const char * kWorkflow
static constexpr const char * kFile
static constexpr const char * kView
static constexpr const char * kEditor
static constexpr const char * kPanel
static constexpr const char * kNavigation
std::function< void()> callback
static JumpToRoomRequestEvent Create(int room, size_t session=0)