feat: Introduce Agent Chat History Popup for Enhanced User Interaction
- Added AgentChatHistoryPopup class to provide a sidebar for displaying recent chat messages, improving user accessibility to chat history. - Integrated the new popup into the editor's source files, ensuring proper inclusion and functionality within the existing UI framework. - Refactored related files to accommodate the new popup, enhancing code organization and maintainability. - Updated CMake configuration to include the new source files, ensuring a seamless build process.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#include "app/editor/system/agent_chat_history_popup.h"
|
||||
#include "app/editor/agent/agent_chat_history_popup.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_EDITOR_SYSTEM_AGENT_CHAT_HISTORY_POPUP_H
|
||||
#define YAZE_APP_EDITOR_SYSTEM_AGENT_CHAT_HISTORY_POPUP_H
|
||||
#ifndef YAZE_APP_EDITOR_AGENT_AGENT_CHAT_HISTORY_POPUP_H
|
||||
#define YAZE_APP_EDITOR_AGENT_AGENT_CHAT_HISTORY_POPUP_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -122,4 +122,4 @@ class AgentChatHistoryPopup {
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_SYSTEM_AGENT_CHAT_HISTORY_POPUP_H
|
||||
#endif // YAZE_APP_EDITOR_AGENT_AGENT_CHAT_HISTORY_POPUP_H
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/agent/agent_chat_history_codec.h"
|
||||
#include "app/editor/agent/agent_ui_theme.h"
|
||||
#include "app/editor/system/agent_chat_history_popup.h"
|
||||
#include "app/editor/agent/agent_chat_history_popup.h"
|
||||
#include "app/editor/system/proposal_drawer.h"
|
||||
#include "app/editor/system/toast_manager.h"
|
||||
#include "app/gui/icons.h"
|
||||
@@ -1348,14 +1348,13 @@ void AgentChatWidget::RenderCollaborationPanel() {
|
||||
}
|
||||
|
||||
void AgentChatWidget::RenderMultimodalPanel() {
|
||||
const auto& theme = AgentUI::GetTheme();
|
||||
ImGui::PushID("MultimodalPanel");
|
||||
ImVec4 gemini_color = ImVec4(0.196f, 0.6f, 0.8f, 1.0f);
|
||||
|
||||
// Dense header (no collapsing for small panel)
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.16f, 0.20f, 0.95f));
|
||||
ImGui::BeginChild("Multimodal_Panel", ImVec2(0, 100), true);
|
||||
ImGui::TextColored(gemini_color, ICON_MD_CAMERA " Vision");
|
||||
ImGui::Separator();
|
||||
AgentUI::PushPanelStyle();
|
||||
ImGui::BeginChild("Multimodal_Panel", ImVec2(0, 120), true); // Slightly taller
|
||||
AgentUI::RenderSectionHeader(ICON_MD_CAMERA, "Vision", theme.provider_gemini);
|
||||
|
||||
bool can_capture = static_cast<bool>(multimodal_callbacks_.capture_snapshot);
|
||||
bool can_send = static_cast<bool>(multimodal_callbacks_.send_to_gemini);
|
||||
@@ -1405,7 +1404,7 @@ void AgentChatWidget::RenderMultimodalPanel() {
|
||||
|
||||
ImGui::SameLine();
|
||||
if (multimodal_state_.last_capture_path.has_value()) {
|
||||
ImGui::TextColored(gemini_color, ICON_MD_CHECK_CIRCLE);
|
||||
ImGui::TextColored(theme.status_success, ICON_MD_CHECK_CIRCLE);
|
||||
} else {
|
||||
ImGui::TextDisabled(ICON_MD_CAMERA_ALT);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ set(
|
||||
app/editor/system/shortcut_manager.cc
|
||||
app/editor/system/popup_manager.cc
|
||||
app/editor/system/proposal_drawer.cc
|
||||
app/editor/system/agent_chat_history_popup.cc
|
||||
app/editor/agent/agent_chat_history_codec.cc
|
||||
)
|
||||
|
||||
@@ -49,6 +48,7 @@ if(YAZE_WITH_GRPC)
|
||||
list(APPEND YAZE_APP_EDITOR_SRC
|
||||
app/editor/agent/agent_editor.cc
|
||||
app/editor/agent/agent_chat_widget.cc
|
||||
app/editor/agent/agent_chat_history_popup.cc
|
||||
app/editor/agent/agent_ui_theme.cc
|
||||
app/editor/agent/agent_collaboration_coordinator.cc
|
||||
app/editor/agent/network_collaboration_coordinator.cc
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "app/editor/sprite/sprite_editor.h"
|
||||
#include "app/editor/system/popup_manager.h"
|
||||
#include "app/editor/system/proposal_drawer.h"
|
||||
#include "app/editor/system/agent_chat_history_popup.h"
|
||||
#include "app/editor/agent/agent_chat_history_popup.h"
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
#include "app/editor/agent/agent_editor.h"
|
||||
#include "app/editor/agent/automation_bridge.h"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/common.h"
|
||||
@@ -453,16 +454,15 @@ void OverworldEditor::DrawOverworldMapSettings() {
|
||||
|
||||
// Header with ROM version indicator and upgrade option
|
||||
if (asm_version == 0xFF) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Vanilla ROM");
|
||||
if (ImGui::Button(ICON_MD_UPGRADE " Upgrade to v3")) {
|
||||
gui::RomVersionBadge("Vanilla ROM", true);
|
||||
if (gui::IconButton(ICON_MD_UPGRADE, "Upgrade to v3")) {
|
||||
// Show upgrade dialog
|
||||
ImGui::OpenPopup("UpgradeROMVersion");
|
||||
}
|
||||
HOVER_HINT("Upgrade ROM to support ZSCustomOverworld features");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
|
||||
"ZSCustomOverworld v%d", asm_version);
|
||||
if (asm_version < 3 && ImGui::Button(ICON_MD_UPGRADE " Upgrade to v3")) {
|
||||
gui::RomVersionBadge(absl::StrFormat("ZSCustomOverworld v%d", asm_version).c_str(), false);
|
||||
if (asm_version < 3 && gui::IconButton(ICON_MD_UPGRADE, "Upgrade to v3")) {
|
||||
ImGui::OpenPopup("UpgradeROMVersion");
|
||||
}
|
||||
}
|
||||
@@ -482,23 +482,20 @@ void OverworldEditor::DrawOverworldMapSettings() {
|
||||
|
||||
// Show ASM application option if feature flag is enabled
|
||||
if (core::FeatureFlags::get().overworld.kApplyZSCustomOverworldASM) {
|
||||
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
|
||||
ICON_MD_CODE " ASM Patch Application Enabled");
|
||||
ImGui::Text(
|
||||
"ZSCustomOverworld ASM will be automatically applied to ROM");
|
||||
gui::SectionHeader(ICON_MD_CODE, "ASM Patch Application Enabled", gui::GetSuccessColor());
|
||||
ImGui::Text("ZSCustomOverworld ASM will be automatically applied to ROM");
|
||||
ImGui::Separator();
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
|
||||
ICON_MD_INFO " ASM Patch Application Disabled");
|
||||
gui::SectionHeader(ICON_MD_INFO, "ASM Patch Application Disabled", gui::GetWarningColor());
|
||||
ImGui::Text("Only version marker will be set. Enable in Feature Flags");
|
||||
ImGui::Text("for full ASM functionality.");
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f),
|
||||
"Warning: This will modify your ROM!");
|
||||
ImGui::TextColored(gui::GetWarningColor(),
|
||||
ICON_MD_WARNING " Warning: This will modify your ROM!");
|
||||
|
||||
if (ImGui::Button(ICON_MD_CHECK " Upgrade", ImVec2(120, 0))) {
|
||||
if (gui::ColoredButton(ICON_MD_CHECK " Upgrade", gui::ButtonType::Success, ImVec2(120, 0))) {
|
||||
// Apply ASM if feature flag is enabled
|
||||
if (core::FeatureFlags::get().overworld.kApplyZSCustomOverworldASM) {
|
||||
auto asm_status = ApplyZSCustomOverworldASM(3);
|
||||
@@ -1611,7 +1608,7 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, ImVec2 scrolling,
|
||||
if (each.is_hole_) {
|
||||
color = ImVec4(255, 255, 0, 200);
|
||||
}
|
||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, color);
|
||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, gui::GetEntranceColor());
|
||||
std::string str = util::HexByte(each.entrance_id_);
|
||||
|
||||
if (current_mode == EditingMode::ENTRANCES) {
|
||||
@@ -1673,7 +1670,7 @@ void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) {
|
||||
if (each.map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||
each.map_id_ >= (current_world_ * 0x40) && !each.deleted_) {
|
||||
ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16,
|
||||
ImVec4(255, 255, 255, 150));
|
||||
gui::GetExitColor());
|
||||
if (current_mode == EditingMode::EXITS) {
|
||||
each.entity_id_ = i;
|
||||
HandleEntityDragging(&each, ow_map_canvas_.zero_point(),
|
||||
@@ -1724,7 +1721,7 @@ void OverworldEditor::DrawOverworldItems() {
|
||||
// Get the item's bitmap and real X and Y positions
|
||||
if (item.room_map_id_ < 0x40 + (current_world_ * 0x40) &&
|
||||
item.room_map_id_ >= (current_world_ * 0x40) && !item.deleted) {
|
||||
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, ImVec4(255, 0, 0, 150));
|
||||
ow_map_canvas_.DrawRect(item.x_, item.y_, 16, 16, gui::GetItemColor());
|
||||
|
||||
if (current_mode == EditingMode::ITEMS) {
|
||||
// Check if this item is being clicked and dragged
|
||||
@@ -1784,7 +1781,7 @@ void OverworldEditor::DrawOverworldSprites() {
|
||||
int original_y = sprite.y_;
|
||||
|
||||
ow_map_canvas_.DrawRect(sprite_x, sprite_y, kTile16Size, kTile16Size,
|
||||
/*magenta=*/ImVec4(255, 0, 255, 150));
|
||||
gui::GetSpriteColor());
|
||||
if (current_mode == EditingMode::SPRITES) {
|
||||
HandleEntityDragging(&sprite, ow_map_canvas_.zero_point(),
|
||||
ow_map_canvas_.scrolling(), is_dragging_entity_,
|
||||
@@ -2816,16 +2813,15 @@ void OverworldEditor::DrawOverlayPreview() {
|
||||
|
||||
void OverworldEditor::DrawMapLockControls() {
|
||||
if (current_map_lock_) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.0f, 1.0f));
|
||||
Text("Map Locked: %d (0x%02X)", current_map_, current_map_);
|
||||
PopStyleColor();
|
||||
|
||||
if (Button("Unlock Map")) {
|
||||
gui::LockIndicator(true, absl::StrFormat("Map %d (0x%02X)", current_map_, current_map_).c_str());
|
||||
|
||||
if (gui::IconButton(ICON_MD_LOCK_OPEN, "Unlock Map")) {
|
||||
current_map_lock_ = false;
|
||||
}
|
||||
} else {
|
||||
Text("Map: %d (0x%02X) - Click to lock", current_map_, current_map_);
|
||||
if (Button("Lock Map")) {
|
||||
gui::LockIndicator(false, absl::StrFormat("Map %d (0x%02X)", current_map_, current_map_).c_str());
|
||||
|
||||
if (gui::IconButton(ICON_MD_LOCK, "Lock Map")) {
|
||||
current_map_lock_ = true;
|
||||
}
|
||||
}
|
||||
@@ -2957,17 +2953,14 @@ void OverworldEditor::DrawMapPropertiesPanel() {
|
||||
|
||||
// Header with map info and lock status
|
||||
ImGui::BeginGroup();
|
||||
if (current_map_lock_) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.0f, 1.0f));
|
||||
Text("Map Locked: %d (0x%02X)", current_map_, current_map_);
|
||||
PopStyleColor();
|
||||
} else {
|
||||
Text("Current Map: %d (0x%02X)", current_map_, current_map_);
|
||||
}
|
||||
gui::LockIndicator(current_map_lock_,
|
||||
absl::StrFormat("Map %d (0x%02X)", current_map_, current_map_).c_str());
|
||||
|
||||
SameLine();
|
||||
if (Button(current_map_lock_ ? "Unlock" : "Lock")) {
|
||||
current_map_lock_ = !current_map_lock_;
|
||||
if (gui::ToggleIconButton(ICON_MD_LOCK_OPEN, ICON_MD_LOCK,
|
||||
¤t_map_lock_,
|
||||
current_map_lock_ ? "Unlock Map" : "Lock Map")) {
|
||||
// Toggle handled by helper
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -3538,9 +3531,7 @@ void OverworldEditor::DrawUsageGrid() {
|
||||
|
||||
// Set highlight color if needed
|
||||
if (highlight) {
|
||||
PushStyleColor(ImGuiCol_Button,
|
||||
ImVec4(1.0f, 0.5f, 0.0f,
|
||||
1.0f)); // Or any highlight color
|
||||
PushStyleColor(ImGuiCol_Button, gui::GetSelectedColor());
|
||||
}
|
||||
|
||||
// Create a button or selectable for each square
|
||||
|
||||
@@ -1,71 +1,414 @@
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// ============================================================================
|
||||
// Theme and Semantic Colors
|
||||
// ============================================================================
|
||||
|
||||
ImVec4 GetThemeColor(ImGuiCol idx) {
|
||||
return ImGui::GetStyle().Colors[idx];
|
||||
return ImGui::GetStyle().Colors[idx];
|
||||
}
|
||||
|
||||
ImVec4 GetSuccessColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.success);
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.success);
|
||||
}
|
||||
|
||||
ImVec4 GetWarningColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.warning);
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.warning);
|
||||
}
|
||||
|
||||
ImVec4 GetErrorColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.error);
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.error);
|
||||
}
|
||||
|
||||
ImVec4 GetInfoColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.info);
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.info);
|
||||
}
|
||||
|
||||
ImVec4 GetAccentColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
}
|
||||
|
||||
void BeginField(const char* label) {
|
||||
ImGui::BeginGroup();
|
||||
// Entity/Map marker colors
|
||||
ImVec4 GetEntranceColor() {
|
||||
return ImVec4(1.0f, 1.0f, 0.0f, 0.6f); // Yellow with transparency
|
||||
}
|
||||
|
||||
ImVec4 GetExitColor() {
|
||||
return ImVec4(1.0f, 1.0f, 1.0f, 0.6f); // White with transparency
|
||||
}
|
||||
|
||||
ImVec4 GetItemColor() {
|
||||
return ImVec4(1.0f, 0.0f, 0.0f, 0.6f); // Red with transparency
|
||||
}
|
||||
|
||||
ImVec4 GetSpriteColor() {
|
||||
return ImVec4(1.0f, 0.0f, 1.0f, 0.6f); // Magenta with transparency
|
||||
}
|
||||
|
||||
ImVec4 GetSelectedColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
}
|
||||
|
||||
ImVec4 GetLockedColor() {
|
||||
return ImVec4(1.0f, 0.5f, 0.0f, 1.0f); // Orange for locked items
|
||||
}
|
||||
|
||||
// Status colors
|
||||
ImVec4 GetVanillaRomColor() {
|
||||
return GetWarningColor(); // Yellow from theme
|
||||
}
|
||||
|
||||
ImVec4 GetCustomRomColor() {
|
||||
return GetSuccessColor(); // Green from theme
|
||||
}
|
||||
|
||||
ImVec4 GetModifiedColor() {
|
||||
const auto& theme = ThemeManager::Get().GetCurrentTheme();
|
||||
return ConvertColorToImVec4(theme.accent);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Layout Helpers
|
||||
// ============================================================================
|
||||
|
||||
void BeginField(const char* label, float label_width) {
|
||||
ImGui::BeginGroup();
|
||||
if (label_width > 0.0f) {
|
||||
ImGui::Text("%s:", label);
|
||||
ImGui::SameLine(label_width);
|
||||
} else {
|
||||
ImGui::TextUnformatted(label);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.6f);
|
||||
}
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.6f);
|
||||
}
|
||||
|
||||
void EndField() {
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
bool BeginPropertyTable(const char* id, int columns, ImGuiTableFlags extra_flags) {
|
||||
ImGuiTableFlags flags = ImGuiTableFlags_Borders |
|
||||
ImGuiTableFlags_SizingFixedFit |
|
||||
extra_flags;
|
||||
if (ImGui::BeginTable(id, columns, flags)) {
|
||||
ImGui::TableSetupColumn("Property", ImGuiTableColumnFlags_WidthFixed, 150);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EndPropertyTable() {
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
void PropertyRow(const char* label, const char* value) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", value);
|
||||
}
|
||||
|
||||
void PropertyRow(const char* label, int value) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", value);
|
||||
}
|
||||
|
||||
void PropertyRowHex(const char* label, uint8_t value) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%02X", value);
|
||||
}
|
||||
|
||||
void PropertyRowHex(const char* label, uint16_t value) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04X", value);
|
||||
}
|
||||
|
||||
void SectionHeader(const char* icon, const char* label, const ImVec4& color) {
|
||||
ImGui::TextColored(color, "%s %s", icon, label);
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Common Widget Patterns
|
||||
// ============================================================================
|
||||
|
||||
bool IconButton(const char* icon, const char* label, const ImVec2& size) {
|
||||
std::string button_text = std::string(icon) + " " + std::string(label);
|
||||
return ImGui::Button(button_text.c_str(), size);
|
||||
std::string button_text = std::string(icon) + " " + std::string(label);
|
||||
return ImGui::Button(button_text.c_str(), size);
|
||||
}
|
||||
|
||||
bool ColoredButton(const char* label, ButtonType type, const ImVec2& size) {
|
||||
ImVec4 color;
|
||||
switch (type) {
|
||||
case ButtonType::Success: color = GetSuccessColor(); break;
|
||||
case ButtonType::Warning: color = GetWarningColor(); break;
|
||||
case ButtonType::Error: color = GetErrorColor(); break;
|
||||
case ButtonType::Info: color = GetInfoColor(); break;
|
||||
default: color = GetThemeColor(ImGuiCol_Button); break;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(color.x * 1.2f, color.y * 1.2f, color.z * 1.2f, color.w));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(color.x * 0.8f, color.y * 0.8f, color.z * 0.8f, color.w));
|
||||
|
||||
bool result = ImGui::Button(label, size);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ToggleIconButton(const char* icon_on, const char* icon_off,
|
||||
bool* state, const char* tooltip) {
|
||||
const char* icon = *state ? icon_on : icon_off;
|
||||
ImVec4 color = *state ? GetSuccessColor() : GetThemeColor(ImGuiCol_Button);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
bool result = ImGui::SmallButton(icon);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (result) *state = !*state;
|
||||
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HelpMarker(const char* desc) {
|
||||
ImGui::TextDisabled(ICON_MD_HELP_OUTLINE);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::TextDisabled(ICON_MD_HELP_OUTLINE);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
||||
ImGui::TextUnformatted(desc);
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatorText(const char* label) {
|
||||
ImGui::SeparatorText(label);
|
||||
ImGui::SeparatorText(label);
|
||||
}
|
||||
|
||||
void StatusBadge(const char* text, ButtonType type) {
|
||||
ImVec4 color;
|
||||
switch (type) {
|
||||
case ButtonType::Success: color = GetSuccessColor(); break;
|
||||
case ButtonType::Warning: color = GetWarningColor(); break;
|
||||
case ButtonType::Error: color = GetErrorColor(); break;
|
||||
case ButtonType::Info: color = GetInfoColor(); break;
|
||||
default: color = GetThemeColor(ImGuiCol_Text); break;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, color);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 3));
|
||||
ImGui::SmallButton(text);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Editor-Specific Patterns
|
||||
// ============================================================================
|
||||
|
||||
void BeginToolset(const char* id) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 2));
|
||||
ImGui::BeginTable(id, 32, ImGuiTableFlags_SizingFixedFit);
|
||||
}
|
||||
|
||||
void EndToolset() {
|
||||
ImGui::EndTable();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void ToolsetButton(const char* icon, bool selected, const char* tooltip,
|
||||
std::function<void()> on_click) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (selected) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, GetAccentColor());
|
||||
}
|
||||
|
||||
if (ImGui::Button(icon)) {
|
||||
if (on_click) on_click();
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
void BeginCanvasContainer(const char* id, bool scrollable) {
|
||||
ImGuiWindowFlags flags = scrollable ?
|
||||
ImGuiWindowFlags_AlwaysVerticalScrollbar :
|
||||
ImGuiWindowFlags_None;
|
||||
ImGui::BeginChild(id, ImVec2(0, 0), true, flags);
|
||||
}
|
||||
|
||||
void EndCanvasContainer() {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
bool EditorTabItem(const char* icon, const char* label, bool* p_open) {
|
||||
char tab_label[256];
|
||||
snprintf(tab_label, sizeof(tab_label), "%s %s", icon, label);
|
||||
return ImGui::BeginTabItem(tab_label, p_open);
|
||||
}
|
||||
|
||||
bool ConfirmationDialog(const char* id, const char* title, const char* message,
|
||||
const char* confirm_text, const char* cancel_text) {
|
||||
bool confirmed = false;
|
||||
|
||||
if (ImGui::BeginPopupModal(id, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("%s", title);
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s", message);
|
||||
ImGui::Separator();
|
||||
|
||||
if (ColoredButton(confirm_text, ButtonType::Warning, ImVec2(120, 0))) {
|
||||
confirmed = true;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(cancel_text, ImVec2(120, 0))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Visual Indicators
|
||||
// ============================================================================
|
||||
|
||||
void StatusIndicator(const char* label, bool active, const char* tooltip) {
|
||||
ImVec4 color = active ? GetSuccessColor() : GetThemeColor(ImGuiCol_TextDisabled);
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float radius = 5.0f;
|
||||
|
||||
pos.x += radius + 3;
|
||||
pos.y += ImGui::GetTextLineHeight() * 0.5f;
|
||||
|
||||
draw_list->AddCircleFilled(pos, radius, ImGui::GetColorU32(color));
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + radius * 2 + 8);
|
||||
ImGui::Text("%s", label);
|
||||
|
||||
if (tooltip && ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
void RomVersionBadge(const char* version, bool is_vanilla) {
|
||||
ImVec4 color = is_vanilla ? GetWarningColor() : GetSuccessColor();
|
||||
const char* icon = is_vanilla ? ICON_MD_INFO : ICON_MD_CHECK_CIRCLE;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
ImGui::Text("%s %s", icon, version);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void LockIndicator(bool locked, const char* label) {
|
||||
if (locked) {
|
||||
ImGui::TextColored(GetLockedColor(), ICON_MD_LOCK " %s (Locked)", label);
|
||||
} else {
|
||||
ImGui::Text(ICON_MD_LOCK_OPEN " %s", label);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Spacing and Alignment
|
||||
// ============================================================================
|
||||
|
||||
void VerticalSpacing(float pixels) {
|
||||
ImGui::Dummy(ImVec2(0, pixels));
|
||||
}
|
||||
|
||||
void HorizontalSpacing(float pixels) {
|
||||
ImGui::Dummy(ImVec2(pixels, 0));
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
void CenterText(const char* text) {
|
||||
float text_width = ImGui::CalcTextSize(text).x;
|
||||
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - text_width) * 0.5f);
|
||||
ImGui::Text("%s", text);
|
||||
}
|
||||
|
||||
void RightAlign(float width) {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() +
|
||||
ImGui::GetContentRegionAvail().x - width);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Input Helpers
|
||||
// ============================================================================
|
||||
|
||||
bool LabeledInputHex(const char* label, uint8_t* value) {
|
||||
BeginField(label);
|
||||
ImGui::PushItemWidth(60);
|
||||
bool changed = ImGui::InputScalar("##hex", ImGuiDataType_U8, value, nullptr,
|
||||
nullptr, "%02X",
|
||||
ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::PopItemWidth();
|
||||
EndField();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool LabeledInputHex(const char* label, uint16_t* value) {
|
||||
BeginField(label);
|
||||
ImGui::PushItemWidth(80);
|
||||
bool changed = ImGui::InputScalar("##hex", ImGuiDataType_U16, value, nullptr,
|
||||
nullptr, "%04X",
|
||||
ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::PopItemWidth();
|
||||
EndField();
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool IconCombo(const char* icon, const char* label, int* current,
|
||||
const char* const items[], int count) {
|
||||
ImGui::Text("%s", icon);
|
||||
ImGui::SameLine();
|
||||
return ImGui::Combo(label, current, items, count);
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
|
||||
@@ -3,43 +3,147 @@
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
// A collection of helper functions and widgets to standardize UI development
|
||||
// and reduce boilerplate ImGui code.
|
||||
// and reduce boilerplate ImGui code across all editors.
|
||||
|
||||
// --- Theming and Colors ---
|
||||
// ============================================================================
|
||||
// Theme and Semantic Colors
|
||||
// ============================================================================
|
||||
|
||||
// Gets a color from the current theme.
|
||||
// Gets a color from the current theme
|
||||
ImVec4 GetThemeColor(ImGuiCol idx);
|
||||
|
||||
// Gets a semantic color from the current theme.
|
||||
// Semantic colors from current theme
|
||||
ImVec4 GetSuccessColor();
|
||||
ImVec4 GetWarningColor();
|
||||
ImVec4 GetErrorColor();
|
||||
ImVec4 GetInfoColor();
|
||||
ImVec4 GetAccentColor();
|
||||
|
||||
// --- Layout Helpers ---
|
||||
// Entity/Map marker colors (for overworld, dungeon)
|
||||
ImVec4 GetEntranceColor();
|
||||
ImVec4 GetExitColor();
|
||||
ImVec4 GetItemColor();
|
||||
ImVec4 GetSpriteColor();
|
||||
ImVec4 GetSelectedColor();
|
||||
ImVec4 GetLockedColor();
|
||||
|
||||
// Begins a standard row for a label and a widget.
|
||||
void BeginField(const char* label);
|
||||
// Ends a field row.
|
||||
// Status colors
|
||||
ImVec4 GetVanillaRomColor();
|
||||
ImVec4 GetCustomRomColor();
|
||||
ImVec4 GetModifiedColor();
|
||||
|
||||
// ============================================================================
|
||||
// Layout Helpers
|
||||
// ============================================================================
|
||||
|
||||
// Label + widget field pattern
|
||||
void BeginField(const char* label, float label_width = 0.0f);
|
||||
void EndField();
|
||||
|
||||
// --- Widget Wrappers ---
|
||||
// Property table pattern (common in editors)
|
||||
bool BeginPropertyTable(const char* id, int columns = 2,
|
||||
ImGuiTableFlags extra_flags = 0);
|
||||
void EndPropertyTable();
|
||||
|
||||
// A button with an icon from the Material Design icon font.
|
||||
bool IconButton(const char* icon, const char* label, const ImVec2& size = ImVec2(0, 0));
|
||||
// Property row helpers
|
||||
void PropertyRow(const char* label, const char* value);
|
||||
void PropertyRow(const char* label, int value);
|
||||
void PropertyRowHex(const char* label, uint8_t value);
|
||||
void PropertyRowHex(const char* label, uint16_t value);
|
||||
|
||||
// A help marker that shows a tooltip on hover.
|
||||
// Section headers with icons
|
||||
void SectionHeader(const char* icon, const char* label,
|
||||
const ImVec4& color = ImVec4(1, 1, 1, 1));
|
||||
|
||||
// ============================================================================
|
||||
// Common Widget Patterns
|
||||
// ============================================================================
|
||||
|
||||
// Button with icon
|
||||
bool IconButton(const char* icon, const char* label,
|
||||
const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
// Colored button for status actions
|
||||
enum class ButtonType { Default, Success, Warning, Error, Info };
|
||||
bool ColoredButton(const char* label, ButtonType type,
|
||||
const ImVec2& size = ImVec2(0, 0));
|
||||
|
||||
// Toggle button with visual state
|
||||
bool ToggleIconButton(const char* icon_on, const char* icon_off,
|
||||
bool* state, const char* tooltip = nullptr);
|
||||
|
||||
// Help marker with tooltip
|
||||
void HelpMarker(const char* desc);
|
||||
|
||||
// A separator with centered text.
|
||||
// Separator with text
|
||||
void SeparatorText(const char* label);
|
||||
|
||||
// Status badge (pill-shaped colored label)
|
||||
void StatusBadge(const char* text, ButtonType type = ButtonType::Default);
|
||||
|
||||
// ============================================================================
|
||||
// Editor-Specific Patterns
|
||||
// ============================================================================
|
||||
|
||||
// Toolset table (horizontal button bar)
|
||||
void BeginToolset(const char* id);
|
||||
void EndToolset();
|
||||
void ToolsetButton(const char* icon, bool selected, const char* tooltip,
|
||||
std::function<void()> on_click);
|
||||
|
||||
// Canvas container patterns
|
||||
void BeginCanvasContainer(const char* id, bool scrollable = true);
|
||||
void EndCanvasContainer();
|
||||
|
||||
// Tab pattern for editor modes
|
||||
bool EditorTabItem(const char* icon, const char* label, bool* p_open = nullptr);
|
||||
|
||||
// Modal confirmation dialog
|
||||
bool ConfirmationDialog(const char* id, const char* title, const char* message,
|
||||
const char* confirm_text = "OK",
|
||||
const char* cancel_text = "Cancel");
|
||||
|
||||
// ============================================================================
|
||||
// Visual Indicators
|
||||
// ============================================================================
|
||||
|
||||
// Status indicator dot + label
|
||||
void StatusIndicator(const char* label, bool active,
|
||||
const char* tooltip = nullptr);
|
||||
|
||||
// ROM version badge
|
||||
void RomVersionBadge(const char* version, bool is_vanilla);
|
||||
|
||||
// Locked/Unlocked indicator
|
||||
void LockIndicator(bool locked, const char* label);
|
||||
|
||||
// ============================================================================
|
||||
// Spacing and Alignment
|
||||
// ============================================================================
|
||||
|
||||
void VerticalSpacing(float pixels = 8.0f);
|
||||
void HorizontalSpacing(float pixels = 8.0f);
|
||||
void CenterText(const char* text);
|
||||
void RightAlign(float width);
|
||||
|
||||
// ============================================================================
|
||||
// Input Helpers (complement existing gui::InputHex functions)
|
||||
// ============================================================================
|
||||
|
||||
// Labeled hex input with automatic formatting
|
||||
bool LabeledInputHex(const char* label, uint8_t* value);
|
||||
bool LabeledInputHex(const char* label, uint16_t* value);
|
||||
|
||||
// Combo with icon
|
||||
bool IconCombo(const char* icon, const char* label, int* current,
|
||||
const char* const items[], int count);
|
||||
|
||||
} // namespace gui
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
Reference in New Issue
Block a user