feat: Implement lazy loading for dungeon rooms and refactor room graphics handling
- Introduced lazy loading for room data to optimize performance and reduce initial load times. - Updated DungeonEditor and DungeonRoomLoader to handle room graphics rendering directly from room objects. - Refactored methods to accept room references instead of IDs for better clarity and type safety. - Enhanced tab management in the DungeonEditor UI for improved user experience.
This commit is contained in:
@@ -14,13 +14,6 @@ set(
|
|||||||
app/emu/snes.cc
|
app/emu/snes.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(
|
|
||||||
YAZE_UTIL_SRC
|
|
||||||
util/bps.cc
|
|
||||||
util/flag.cc
|
|
||||||
util/hex.cc
|
|
||||||
)
|
|
||||||
|
|
||||||
set(YAZE_RESOURCE_FILES
|
set(YAZE_RESOURCE_FILES
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Roboto-Medium.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Roboto-Medium.ttf
|
||||||
@@ -249,6 +242,7 @@ if (YAZE_BUILD_LIB)
|
|||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
# Create core library for testing (includes editor and zelda3 components needed by tests)
|
# Create core library for testing (includes editor and zelda3 components needed by tests)
|
||||||
|
include(util/util.cmake)
|
||||||
set(YAZE_CORE_SOURCES
|
set(YAZE_CORE_SOURCES
|
||||||
app/rom.cc
|
app/rom.cc
|
||||||
${YAZE_APP_CORE_SRC}
|
${YAZE_APP_CORE_SRC}
|
||||||
@@ -257,7 +251,6 @@ if (YAZE_BUILD_LIB)
|
|||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
${YAZE_APP_EMU_SRC}
|
${YAZE_APP_EMU_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${YAZE_UTIL_SRC}
|
|
||||||
# cli/service/gui_automation_client.cc # Moved to yaze_c
|
# cli/service/gui_automation_client.cc # Moved to yaze_c
|
||||||
cli/service/testing/test_workflow_generator.cc
|
cli/service/testing/test_workflow_generator.cc
|
||||||
)
|
)
|
||||||
@@ -284,6 +277,7 @@ if (YAZE_BUILD_LIB)
|
|||||||
yaze_core PUBLIC
|
yaze_core PUBLIC
|
||||||
asar-static
|
asar-static
|
||||||
yaze_agent
|
yaze_agent
|
||||||
|
yaze_util
|
||||||
${ABSL_TARGETS}
|
${ABSL_TARGETS}
|
||||||
${SDL_TARGETS}
|
${SDL_TARGETS}
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
@@ -765,6 +759,7 @@ source_group("Utilities" FILES
|
|||||||
util/flag.h
|
util/flag.h
|
||||||
util/hex.cc
|
util/hex.cc
|
||||||
util/hex.h
|
util/hex.h
|
||||||
|
util/log.cc
|
||||||
util/log.h
|
util/log.h
|
||||||
util/macro.h
|
util/macro.h
|
||||||
util/notify.h
|
util/notify.h
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "imgui/imgui.h"
|
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
@@ -117,63 +115,6 @@ class FeatureFlags {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using ImGui::BeginMenu;
|
|
||||||
using ImGui::Checkbox;
|
|
||||||
using ImGui::EndMenu;
|
|
||||||
using ImGui::MenuItem;
|
|
||||||
using ImGui::Separator;
|
|
||||||
|
|
||||||
struct FlagsMenu {
|
|
||||||
void DrawOverworldFlags() {
|
|
||||||
Checkbox("Enable Overworld Sprites",
|
|
||||||
&FeatureFlags::get().overworld.kDrawOverworldSprites);
|
|
||||||
Separator();
|
|
||||||
Checkbox("Save Overworld Maps",
|
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldMaps);
|
|
||||||
Checkbox("Save Overworld Entrances",
|
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldEntrances);
|
|
||||||
Checkbox("Save Overworld Exits",
|
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldExits);
|
|
||||||
Checkbox("Save Overworld Items",
|
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldItems);
|
|
||||||
Checkbox("Save Overworld Properties",
|
|
||||||
&FeatureFlags::get().overworld.kSaveOverworldProperties);
|
|
||||||
Checkbox("Enable Custom Overworld Features",
|
|
||||||
&FeatureFlags::get().overworld.kLoadCustomOverworld);
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("?")) {
|
|
||||||
ImGui::OpenPopup("CustomOverworldHelp");
|
|
||||||
}
|
|
||||||
if (ImGui::BeginPopup("CustomOverworldHelp")) {
|
|
||||||
ImGui::Text("This flag enables ZSCustomOverworld features.");
|
|
||||||
ImGui::Text("If ZSCustomOverworld ASM is already applied to the ROM,");
|
|
||||||
ImGui::Text("features are auto-enabled regardless of this flag.");
|
|
||||||
ImGui::Text("For vanilla ROMs, enable this to use custom features.");
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
Checkbox("Apply ZSCustomOverworld ASM",
|
|
||||||
&FeatureFlags::get().overworld.kApplyZSCustomOverworldASM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawDungeonFlags() {
|
|
||||||
Checkbox("Save Dungeon Maps", &FeatureFlags::get().kSaveDungeonMaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawResourceFlags() {
|
|
||||||
Checkbox("Save All Palettes", &FeatureFlags::get().kSaveAllPalettes);
|
|
||||||
Checkbox("Save Gfx Groups", &FeatureFlags::get().kSaveGfxGroups);
|
|
||||||
Checkbox("Save Graphics Sheets", &FeatureFlags::get().kSaveGraphicsSheet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrawSystemFlags() {
|
|
||||||
Checkbox("Enable Console Logging", &FeatureFlags::get().kLogToConsole);
|
|
||||||
Checkbox("Enable Performance Monitoring", &FeatureFlags::get().kEnablePerformanceMonitoring);
|
|
||||||
Checkbox("Log Instructions to Emulator Debugger",
|
|
||||||
&FeatureFlags::get().kLogInstructions);
|
|
||||||
Checkbox("Use Native File Dialog (NFD)", &FeatureFlags::get().kUseNativeFileDialog);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ void DungeonEditor::DrawCanvasAndPropertiesPanel() {
|
|||||||
gui::InputHexByte("Palette", &room.palette);
|
gui::InputHexByte("Palette", &room.palette);
|
||||||
|
|
||||||
if (ImGui::Button("Reload Room Graphics")) {
|
if (ImGui::Button("Reload Room Graphics")) {
|
||||||
(void)LoadAndRenderRoomGraphics(current_room);
|
(void)LoadAndRenderRoomGraphics(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +555,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
|
|||||||
SameLine();
|
SameLine();
|
||||||
|
|
||||||
if (Button("Load Room Graphics")) {
|
if (Button("Load Room Graphics")) {
|
||||||
(void)LoadAndRenderRoomGraphics(room_id);
|
(void)LoadAndRenderRoomGraphics(rooms_[room_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -701,7 +701,7 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
|
|||||||
if (is_loaded_) {
|
if (is_loaded_) {
|
||||||
// Automatically load room graphics if not already loaded
|
// Automatically load room graphics if not already loaded
|
||||||
if (rooms_[room_id].blocks().empty()) {
|
if (rooms_[room_id].blocks().empty()) {
|
||||||
(void)LoadAndRenderRoomGraphics(room_id);
|
(void)LoadAndRenderRoomGraphics(rooms_[room_id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load room objects if not already loaded
|
// Load room objects if not already loaded
|
||||||
@@ -761,7 +761,7 @@ void DungeonEditor::UpdateObjectEditor() {
|
|||||||
|
|
||||||
// Load room graphics if not already loaded (this populates arena buffers)
|
// Load room graphics if not already loaded (this populates arena buffers)
|
||||||
if (room.blocks().empty()) {
|
if (room.blocks().empty()) {
|
||||||
auto status = LoadAndRenderRoomGraphics(room_id);
|
auto status = LoadAndRenderRoomGraphics(room);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
// Log error but continue
|
// Log error but continue
|
||||||
return;
|
return;
|
||||||
@@ -829,18 +829,15 @@ void DungeonEditor::DrawObjectEditorPanels() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Legacy method implementations that delegate to components
|
// Legacy method implementations that delegate to components
|
||||||
absl::Status DungeonEditor::LoadAndRenderRoomGraphics(int room_id) {
|
absl::Status DungeonEditor::LoadAndRenderRoomGraphics(zelda3::Room& room) {
|
||||||
if (room_id < 0 || room_id >= rooms_.size()) {
|
return room_loader_.LoadAndRenderRoomGraphics(room);
|
||||||
return absl::InvalidArgumentError("Invalid room ID");
|
|
||||||
}
|
|
||||||
return room_loader_.LoadAndRenderRoomGraphics(room_id, rooms_[room_id]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status DungeonEditor::ReloadAllRoomGraphics() {
|
absl::Status DungeonEditor::ReloadAllRoomGraphics() {
|
||||||
return room_loader_.ReloadAllRoomGraphics(rooms_);
|
return room_loader_.ReloadAllRoomGraphics(rooms_);
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int room_id) {
|
absl::Status DungeonEditor::UpdateRoomBackgroundLayers(int /*room_id*/) {
|
||||||
// This method is deprecated - rendering is handled by DungeonRenderer component
|
// This method is deprecated - rendering is handled by DungeonRenderer component
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class DungeonEditor : public Editor {
|
|||||||
void DrawObjectRenderer();
|
void DrawObjectRenderer();
|
||||||
|
|
||||||
// Legacy methods (delegated to components)
|
// Legacy methods (delegated to components)
|
||||||
absl::Status LoadAndRenderRoomGraphics(int room_id);
|
absl::Status LoadAndRenderRoomGraphics(zelda3::Room& room);
|
||||||
absl::Status ReloadAllRoomGraphics();
|
absl::Status ReloadAllRoomGraphics();
|
||||||
absl::Status UpdateRoomBackgroundLayers(int room_id);
|
absl::Status UpdateRoomBackgroundLayers(int room_id);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "app/gfx/snes_palette.h"
|
#include "app/gfx/snes_palette.h"
|
||||||
|
#include "app/zelda3/dungeon/room.h"
|
||||||
|
#include "app/gui/icons.h"
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
namespace yaze::editor {
|
namespace yaze::editor {
|
||||||
@@ -28,8 +30,8 @@ absl::Status DungeonEditorV2::Load() {
|
|||||||
return absl::FailedPreconditionError("ROM not loaded");
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all rooms using the loader component
|
// Load all rooms using the loader component - DEFERRED for lazy loading
|
||||||
RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_));
|
// RETURN_IF_ERROR(room_loader_.LoadAllRooms(rooms_));
|
||||||
RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_));
|
RETURN_IF_ERROR(room_loader_.LoadRoomEntrances(entrances_));
|
||||||
|
|
||||||
// Load palette group
|
// Load palette group
|
||||||
@@ -106,7 +108,16 @@ void DungeonEditorV2::DrawLayout() {
|
|||||||
int room_id = active_rooms_[i];
|
int room_id = active_rooms_[i];
|
||||||
bool open = true;
|
bool open = true;
|
||||||
|
|
||||||
if (BeginTabItem(absl::StrFormat("Room %03X", room_id).c_str(), &open)) {
|
std::string tab_name_str;
|
||||||
|
const char* tab_name;
|
||||||
|
if (room_id >= 0 && static_cast<size_t>(room_id) < std::size(zelda3::kRoomNames)) {
|
||||||
|
tab_name = zelda3::kRoomNames[room_id].data();
|
||||||
|
} else {
|
||||||
|
tab_name_str = absl::StrFormat("Room %03X", room_id);
|
||||||
|
tab_name = tab_name_str.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BeginTabItem(tab_name, &open)) {
|
||||||
DrawRoomTab(room_id);
|
DrawRoomTab(room_id);
|
||||||
EndTabItem();
|
EndTabItem();
|
||||||
}
|
}
|
||||||
@@ -116,6 +127,12 @@ void DungeonEditorV2::DrawLayout() {
|
|||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add tab button
|
||||||
|
if (ImGui::TabItemButton(ICON_MD_ADD, ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) {
|
||||||
|
OnRoomSelected(room_selector_.current_room_id());
|
||||||
|
}
|
||||||
|
|
||||||
EndTabBar();
|
EndTabBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,11 +150,21 @@ void DungeonEditorV2::DrawRoomTab(int room_id) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lazy load room data
|
||||||
|
if (!rooms_[room_id].IsLoaded()) {
|
||||||
|
auto status = room_loader_.LoadRoom(room_id, rooms_[room_id]);
|
||||||
|
if (!status.ok()) {
|
||||||
|
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Failed to load room: %s",
|
||||||
|
status.message().data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Quick controls
|
// Quick controls
|
||||||
ImGui::Text("Room %03X", room_id);
|
ImGui::Text("Room %03X", room_id);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Load Graphics")) {
|
if (ImGui::Button("Load Graphics")) {
|
||||||
(void)room_loader_.LoadAndRenderRoomGraphics(room_id, rooms_[room_id]);
|
(void)room_loader_.LoadAndRenderRoomGraphics(rooms_[room_id]);
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Save")) {
|
if (ImGui::Button("Save")) {
|
||||||
|
|||||||
@@ -13,6 +13,20 @@
|
|||||||
|
|
||||||
namespace yaze::editor {
|
namespace yaze::editor {
|
||||||
|
|
||||||
|
absl::Status DungeonRoomLoader::LoadRoom(int room_id, zelda3::Room& room) {
|
||||||
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
|
}
|
||||||
|
if (room_id < 0 || room_id >= 0x128) {
|
||||||
|
return absl::InvalidArgumentError("Invalid room ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
room = zelda3::LoadRoomFromRom(rom_, room_id);
|
||||||
|
room.LoadObjects();
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms) {
|
absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms) {
|
||||||
if (!rom_ || !rom_->is_loaded()) {
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
return absl::FailedPreconditionError("ROM not loaded");
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
@@ -26,7 +40,7 @@ absl::Status DungeonRoomLoader::LoadAllRooms(std::array<zelda3::Room, 0x128>& ro
|
|||||||
static_cast<int>(std::thread::hardware_concurrency()));
|
static_cast<int>(std::thread::hardware_concurrency()));
|
||||||
const int rooms_per_thread = (kTotalRooms + max_concurrency - 1) / max_concurrency;
|
const int rooms_per_thread = (kTotalRooms + max_concurrency - 1) / max_concurrency;
|
||||||
|
|
||||||
util::logf("Loading %d dungeon rooms using %d threads (%d rooms per thread)",
|
LOG_INFO("Dungeon", "Loading %d dungeon rooms using %d threads (%d rooms per thread)",
|
||||||
kTotalRooms, max_concurrency, rooms_per_thread);
|
kTotalRooms, max_concurrency, rooms_per_thread);
|
||||||
|
|
||||||
// Thread-safe data structures for collecting results
|
// Thread-safe data structures for collecting results
|
||||||
@@ -164,7 +178,7 @@ void DungeonRoomLoader::LoadDungeonRoomSize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status DungeonRoomLoader::LoadAndRenderRoomGraphics(int room_id, zelda3::Room& room) {
|
absl::Status DungeonRoomLoader::LoadAndRenderRoomGraphics(zelda3::Room& room) {
|
||||||
if (!rom_ || !rom_->is_loaded()) {
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
return absl::FailedPreconditionError("ROM not loaded");
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
}
|
}
|
||||||
@@ -184,8 +198,8 @@ absl::Status DungeonRoomLoader::ReloadAllRoomGraphics(std::array<zelda3::Room, 0
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reload graphics for all rooms
|
// Reload graphics for all rooms
|
||||||
for (size_t i = 0; i < rooms.size(); ++i) {
|
for (auto& room : rooms) {
|
||||||
auto status = LoadAndRenderRoomGraphics(static_cast<int>(i), rooms[i]);
|
auto status = LoadAndRenderRoomGraphics(room);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
continue; // Log error but continue with other rooms
|
continue; // Log error but continue with other rooms
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class DungeonRoomLoader {
|
|||||||
explicit DungeonRoomLoader(Rom* rom) : rom_(rom) {}
|
explicit DungeonRoomLoader(Rom* rom) : rom_(rom) {}
|
||||||
|
|
||||||
// Room loading
|
// Room loading
|
||||||
|
absl::Status LoadRoom(int room_id, zelda3::Room& room);
|
||||||
absl::Status LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms);
|
absl::Status LoadAllRooms(std::array<zelda3::Room, 0x128>& rooms);
|
||||||
absl::Status LoadRoomEntrances(std::array<zelda3::RoomEntrance, 0x8C>& entrances);
|
absl::Status LoadRoomEntrances(std::array<zelda3::RoomEntrance, 0x8C>& entrances);
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ class DungeonRoomLoader {
|
|||||||
uint64_t GetTotalRoomSize() const { return total_room_size_; }
|
uint64_t GetTotalRoomSize() const { return total_room_size_; }
|
||||||
|
|
||||||
// Room graphics
|
// Room graphics
|
||||||
absl::Status LoadAndRenderRoomGraphics(int room_id, zelda3::Room& room);
|
absl::Status LoadAndRenderRoomGraphics(zelda3::Room& room);
|
||||||
absl::Status ReloadAllRoomGraphics(std::array<zelda3::Room, 0x128>& rooms);
|
absl::Status ReloadAllRoomGraphics(std::array<zelda3::Room, 0x128>& rooms);
|
||||||
|
|
||||||
// Data access
|
// Data access
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
#include "app/gfx/atlas_renderer.h"
|
#include "app/gfx/atlas_renderer.h"
|
||||||
#include "app/gfx/bitmap.h"
|
#include "app/gfx/bitmap.h"
|
||||||
#include "app/gfx/performance_profiler.h"
|
#include "app/gfx/performance_profiler.h"
|
||||||
|
#include "app/gui/canvas_utils.h"
|
||||||
#include "app/gui/color.h"
|
#include "app/gui/color.h"
|
||||||
#include "app/gui/style.h"
|
#include "app/gui/style.h"
|
||||||
#include "app/gui/canvas_utils.h"
|
|
||||||
#include "util/log.h"
|
|
||||||
#include "imgui/imgui.h"
|
#include "imgui/imgui.h"
|
||||||
#include "imgui_memory_editor.h"
|
#include "imgui_memory_editor.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
namespace yaze::gui {
|
namespace yaze::gui {
|
||||||
|
|
||||||
@@ -118,10 +118,12 @@ void Canvas::InitializeEnhancedComponents() {
|
|||||||
canvas::CanvasUsageManager::Get().RegisterTracker(canvas_id_, usage_tracker_);
|
canvas::CanvasUsageManager::Get().RegisterTracker(canvas_id_, usage_tracker_);
|
||||||
|
|
||||||
// Initialize performance integration
|
// Initialize performance integration
|
||||||
performance_integration_ = std::make_shared<canvas::CanvasPerformanceIntegration>();
|
performance_integration_ =
|
||||||
|
std::make_shared<canvas::CanvasPerformanceIntegration>();
|
||||||
performance_integration_->Initialize(canvas_id_);
|
performance_integration_->Initialize(canvas_id_);
|
||||||
performance_integration_->SetUsageTracker(usage_tracker_);
|
performance_integration_->SetUsageTracker(usage_tracker_);
|
||||||
canvas::CanvasPerformanceManager::Get().RegisterIntegration(canvas_id_, performance_integration_);
|
canvas::CanvasPerformanceManager::Get().RegisterIntegration(
|
||||||
|
canvas_id_, performance_integration_);
|
||||||
|
|
||||||
// Start performance monitoring
|
// Start performance monitoring
|
||||||
performance_integration_->StartMonitoring();
|
performance_integration_->StartMonitoring();
|
||||||
@@ -144,12 +146,14 @@ canvas::CanvasUsage Canvas::GetUsageMode() const {
|
|||||||
return canvas::CanvasUsage::kUnknown;
|
return canvas::CanvasUsage::kUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::RecordCanvasOperation(const std::string& operation_name, double time_ms) {
|
void Canvas::RecordCanvasOperation(const std::string& operation_name,
|
||||||
|
double time_ms) {
|
||||||
if (usage_tracker_) {
|
if (usage_tracker_) {
|
||||||
usage_tracker_->RecordOperation(operation_name, time_ms);
|
usage_tracker_->RecordOperation(operation_name, time_ms);
|
||||||
}
|
}
|
||||||
if (performance_integration_) {
|
if (performance_integration_) {
|
||||||
performance_integration_->RecordOperation(operation_name, time_ms, GetUsageMode());
|
performance_integration_->RecordOperation(operation_name, time_ms,
|
||||||
|
GetUsageMode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +170,8 @@ void Canvas::ShowUsageReport() {
|
|||||||
if (modals_) {
|
if (modals_) {
|
||||||
// Create a simple text display modal
|
// Create a simple text display modal
|
||||||
ImGui::OpenPopup("Canvas Usage Report");
|
ImGui::OpenPopup("Canvas Usage Report");
|
||||||
if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("Canvas Usage Report", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text("Canvas Usage Report");
|
ImGui::Text("Canvas Usage Report");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::TextWrapped("%s", report.c_str());
|
ImGui::TextWrapped("%s", report.c_str());
|
||||||
@@ -190,7 +195,8 @@ void Canvas::InitializePaletteEditor(Rom* rom) {
|
|||||||
void Canvas::ShowPaletteEditor() {
|
void Canvas::ShowPaletteEditor() {
|
||||||
if (palette_editor_ && bitmap_) {
|
if (palette_editor_ && bitmap_) {
|
||||||
auto mutable_palette = bitmap_->mutable_palette();
|
auto mutable_palette = bitmap_->mutable_palette();
|
||||||
palette_editor_->ShowPaletteEditor(*mutable_palette, "Canvas Palette Editor");
|
palette_editor_->ShowPaletteEditor(*mutable_palette,
|
||||||
|
"Canvas Palette Editor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,18 +208,21 @@ void Canvas::ShowColorAnalysis() {
|
|||||||
|
|
||||||
bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
|
bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
|
||||||
if (palette_editor_ && bitmap_) {
|
if (palette_editor_ && bitmap_) {
|
||||||
return palette_editor_->ApplyROMPalette(bitmap_, group_index, palette_index);
|
return palette_editor_->ApplyROMPalette(bitmap_, group_index,
|
||||||
|
palette_index);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size reporting methods for table integration
|
// Size reporting methods for table integration
|
||||||
ImVec2 Canvas::GetMinimumSize() const {
|
ImVec2 Canvas::GetMinimumSize() const {
|
||||||
return CanvasUtils::CalculateMinimumCanvasSize(config_.content_size, config_.global_scale);
|
return CanvasUtils::CalculateMinimumCanvasSize(config_.content_size,
|
||||||
|
config_.global_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 Canvas::GetPreferredSize() const {
|
ImVec2 Canvas::GetPreferredSize() const {
|
||||||
return CanvasUtils::CalculatePreferredCanvasSize(config_.content_size, config_.global_scale);
|
return CanvasUtils::CalculatePreferredCanvasSize(config_.content_size,
|
||||||
|
config_.global_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ReserveTableSpace(const std::string& label) {
|
void Canvas::ReserveTableSpace(const std::string& label) {
|
||||||
@@ -231,7 +240,8 @@ bool Canvas::BeginTableCanvas(const std::string& label) {
|
|||||||
std::string child_id = canvas_id_ + "_TableChild";
|
std::string child_id = canvas_id_ + "_TableChild";
|
||||||
ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
|
ImVec2 child_size = config_.auto_resize ? ImVec2(0, 0) : config_.canvas_size;
|
||||||
|
|
||||||
bool result = ImGui::BeginChild(child_id.c_str(), child_size,
|
bool result =
|
||||||
|
ImGui::BeginChild(child_id.c_str(), child_size,
|
||||||
true, // Always show border for table integration
|
true, // Always show border for table integration
|
||||||
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
||||||
|
|
||||||
@@ -256,7 +266,8 @@ bool Canvas::WasClicked(ImGuiMouseButton button) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
|
bool Canvas::WasDoubleClicked(ImGuiMouseButton button) const {
|
||||||
return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) && HasValidSelection();
|
return ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(button) &&
|
||||||
|
HasValidSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 Canvas::GetLastClickPosition() const {
|
ImVec2 Canvas::GetLastClickPosition() const {
|
||||||
@@ -313,7 +324,8 @@ void Canvas::DrawBackground(ImVec2 canvas_size) {
|
|||||||
|
|
||||||
// Calculate canvas size using utility function
|
// Calculate canvas size using utility function
|
||||||
ImVec2 content_region = GetContentRegionAvail();
|
ImVec2 content_region = GetContentRegionAvail();
|
||||||
canvas_sz_ = CanvasUtils::CalculateCanvasSize(content_region, config_.canvas_size, config_.custom_canvas_size);
|
canvas_sz_ = CanvasUtils::CalculateCanvasSize(
|
||||||
|
content_region, config_.canvas_size, config_.custom_canvas_size);
|
||||||
|
|
||||||
if (canvas_size.x != 0) {
|
if (canvas_size.x != 0) {
|
||||||
canvas_sz_ = canvas_size;
|
canvas_sz_ = canvas_size;
|
||||||
@@ -321,13 +333,17 @@ void Canvas::DrawBackground(ImVec2 canvas_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate scaled canvas bounds
|
// Calculate scaled canvas bounds
|
||||||
ImVec2 scaled_size = CanvasUtils::CalculateScaledCanvasSize(canvas_sz_, config_.global_scale);
|
ImVec2 scaled_size =
|
||||||
|
CanvasUtils::CalculateScaledCanvasSize(canvas_sz_, config_.global_scale);
|
||||||
|
|
||||||
// CRITICAL FIX: Ensure minimum size to prevent ImGui assertions
|
// CRITICAL FIX: Ensure minimum size to prevent ImGui assertions
|
||||||
if (scaled_size.x <= 0.0f) scaled_size.x = 1.0f;
|
if (scaled_size.x <= 0.0f)
|
||||||
if (scaled_size.y <= 0.0f) scaled_size.y = 1.0f;
|
scaled_size.x = 1.0f;
|
||||||
|
if (scaled_size.y <= 0.0f)
|
||||||
|
scaled_size.y = 1.0f;
|
||||||
|
|
||||||
canvas_p1_ = ImVec2(canvas_p0_.x + scaled_size.x, canvas_p0_.y + scaled_size.y);
|
canvas_p1_ =
|
||||||
|
ImVec2(canvas_p0_.x + scaled_size.x, canvas_p0_.y + scaled_size.y);
|
||||||
|
|
||||||
// Draw border and background color
|
// Draw border and background color
|
||||||
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
|
draw_list_->AddRectFilled(canvas_p0_, canvas_p1_, kRectangleColor);
|
||||||
@@ -363,10 +379,9 @@ void Canvas::DrawContextMenu() {
|
|||||||
|
|
||||||
// Update canvas state for enhanced components
|
// Update canvas state for enhanced components
|
||||||
if (usage_tracker_) {
|
if (usage_tracker_) {
|
||||||
usage_tracker_->UpdateCanvasState(canvas_sz_, config_.content_size,
|
usage_tracker_->UpdateCanvasState(
|
||||||
global_scale_, custom_step_,
|
canvas_sz_, config_.content_size, global_scale_, custom_step_,
|
||||||
enable_grid_, enable_hex_tile_labels_,
|
enable_grid_, enable_hex_tile_labels_, enable_custom_labels_);
|
||||||
enable_custom_labels_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use enhanced context menu if available
|
// Use enhanced context menu if available
|
||||||
@@ -384,11 +399,10 @@ void Canvas::DrawContextMenu() {
|
|||||||
snapshot.auto_resize = config_.auto_resize;
|
snapshot.auto_resize = config_.auto_resize;
|
||||||
snapshot.scrolling = scrolling_;
|
snapshot.scrolling = scrolling_;
|
||||||
|
|
||||||
context_menu_->SetCanvasState(canvas_sz_, config_.content_size,
|
context_menu_->SetCanvasState(
|
||||||
global_scale_, custom_step_, enable_grid_,
|
canvas_sz_, config_.content_size, global_scale_, custom_step_,
|
||||||
enable_hex_tile_labels_, enable_custom_labels_,
|
enable_grid_, enable_hex_tile_labels_, enable_custom_labels_,
|
||||||
enable_context_menu_, draggable_,
|
enable_context_menu_, draggable_, config_.auto_resize, scrolling_);
|
||||||
config_.auto_resize, scrolling_);
|
|
||||||
|
|
||||||
context_menu_->Render(
|
context_menu_->Render(
|
||||||
context_id_, mouse_pos, bitmap_,
|
context_id_, mouse_pos, bitmap_,
|
||||||
@@ -445,19 +459,28 @@ void Canvas::DrawContextMenu() {
|
|||||||
if (modals_) {
|
if (modals_) {
|
||||||
canvas::CanvasConfig modal_config = updated_config;
|
canvas::CanvasConfig modal_config = updated_config;
|
||||||
modal_config.on_config_changed =
|
modal_config.on_config_changed =
|
||||||
[this](const canvas::CanvasConfig& cfg) { ApplyConfigSnapshot(cfg); };
|
[this](const canvas::CanvasConfig& cfg) {
|
||||||
|
ApplyConfigSnapshot(cfg);
|
||||||
|
};
|
||||||
modal_config.on_scale_changed =
|
modal_config.on_scale_changed =
|
||||||
[this](const canvas::CanvasConfig& cfg) { ApplyScaleSnapshot(cfg); };
|
[this](const canvas::CanvasConfig& cfg) {
|
||||||
modals_->ShowAdvancedProperties(canvas_id_, modal_config, bitmap_);
|
ApplyScaleSnapshot(cfg);
|
||||||
|
};
|
||||||
|
modals_->ShowAdvancedProperties(canvas_id_, modal_config,
|
||||||
|
bitmap_);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case canvas::CanvasContextMenu::Command::kOpenScalingControls:
|
case canvas::CanvasContextMenu::Command::kOpenScalingControls:
|
||||||
if (modals_) {
|
if (modals_) {
|
||||||
canvas::CanvasConfig modal_config = updated_config;
|
canvas::CanvasConfig modal_config = updated_config;
|
||||||
modal_config.on_config_changed =
|
modal_config.on_config_changed =
|
||||||
[this](const canvas::CanvasConfig& cfg) { ApplyConfigSnapshot(cfg); };
|
[this](const canvas::CanvasConfig& cfg) {
|
||||||
|
ApplyConfigSnapshot(cfg);
|
||||||
|
};
|
||||||
modal_config.on_scale_changed =
|
modal_config.on_scale_changed =
|
||||||
[this](const canvas::CanvasConfig& cfg) { ApplyScaleSnapshot(cfg); };
|
[this](const canvas::CanvasConfig& cfg) {
|
||||||
|
ApplyScaleSnapshot(cfg);
|
||||||
|
};
|
||||||
modals_->ShowScalingControls(canvas_id_, modal_config, bitmap_);
|
modals_->ShowScalingControls(canvas_id_, modal_config, bitmap_);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -564,26 +587,29 @@ void Canvas::DrawContextMenu() {
|
|||||||
// Quick palette group selection
|
// Quick palette group selection
|
||||||
const char* palette_groups[] = {
|
const char* palette_groups[] = {
|
||||||
"Overworld Main", "Overworld Aux", "Overworld Animated",
|
"Overworld Main", "Overworld Aux", "Overworld Animated",
|
||||||
"Dungeon Main", "Global Sprites", "Armor", "Swords"
|
"Dungeon Main", "Global Sprites", "Armor",
|
||||||
};
|
"Swords"};
|
||||||
|
|
||||||
if (ImGui::Combo("Quick Palette Group", (int*)&edit_palette_group_name_index_,
|
if (ImGui::Combo("Quick Palette Group",
|
||||||
|
(int*)&edit_palette_group_name_index_,
|
||||||
palette_groups, IM_ARRAYSIZE(palette_groups))) {
|
palette_groups, IM_ARRAYSIZE(palette_groups))) {
|
||||||
// Group selection changed
|
// Group selection changed
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(100.f);
|
ImGui::SetNextItemWidth(100.f);
|
||||||
if (ImGui::SliderInt("Palette Index", (int*)&edit_palette_index_, 0, 7)) {
|
if (ImGui::SliderInt("Palette Index", (int*)&edit_palette_index_, 0,
|
||||||
|
7)) {
|
||||||
// Palette index changed
|
// Palette index changed
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply button with enhanced functionality
|
// Apply button with enhanced functionality
|
||||||
if (ImGui::Button("Apply to Canvas") && bitmap_) {
|
if (ImGui::Button("Apply to Canvas") && bitmap_) {
|
||||||
if (palette_editor_->ApplyROMPalette(bitmap_,
|
if (palette_editor_->ApplyROMPalette(
|
||||||
edit_palette_group_name_index_,
|
bitmap_, edit_palette_group_name_index_,
|
||||||
edit_palette_index_)) {
|
edit_palette_index_)) {
|
||||||
util::logf("Applied ROM palette group %d, index %d via context menu",
|
// LOG_INFO(
|
||||||
edit_palette_group_name_index_, edit_palette_index_);
|
// "Applied ROM palette group %d, index %d via context menu",
|
||||||
|
// edit_palette_group_name_index_, edit_palette_index_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,27 +617,33 @@ void Canvas::DrawContextMenu() {
|
|||||||
if (ImGui::TreeNode("Interactive Palette Editor")) {
|
if (ImGui::TreeNode("Interactive Palette Editor")) {
|
||||||
if (rom_ && bitmap_) {
|
if (rom_ && bitmap_) {
|
||||||
ImGui::Text("Interactive ROM Palette Editing");
|
ImGui::Text("Interactive ROM Palette Editing");
|
||||||
ImGui::Text("Selected Group: %s", palette_groups[edit_palette_group_name_index_]);
|
ImGui::Text("Selected Group: %s",
|
||||||
|
palette_groups[edit_palette_group_name_index_]);
|
||||||
|
|
||||||
// Get the enhanced palette editor's ROM palette if available
|
// Get the enhanced palette editor's ROM palette if available
|
||||||
if (const auto* rom_palette = palette_editor_->GetSelectedROMPalette()) {
|
if (const auto* rom_palette =
|
||||||
auto editable_palette = const_cast<gfx::SnesPalette&>(*rom_palette);
|
palette_editor_->GetSelectedROMPalette()) {
|
||||||
|
auto editable_palette =
|
||||||
|
const_cast<gfx::SnesPalette&>(*rom_palette);
|
||||||
|
|
||||||
if (ImGui::BeginChild("SelectablePalette", ImVec2(0, 200), true)) {
|
if (ImGui::BeginChild("SelectablePalette", ImVec2(0, 200),
|
||||||
|
true)) {
|
||||||
// Use the existing SelectablePalettePipeline for interactive editing
|
// Use the existing SelectablePalettePipeline for interactive editing
|
||||||
gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
gui::SelectablePalettePipeline(edit_palette_sub_index_,
|
||||||
refresh_graphics_, editable_palette);
|
refresh_graphics_,
|
||||||
|
editable_palette);
|
||||||
|
|
||||||
if (refresh_graphics_) {
|
if (refresh_graphics_) {
|
||||||
bitmap_->SetPaletteWithTransparent(editable_palette, edit_palette_sub_index_);
|
bitmap_->SetPaletteWithTransparent(
|
||||||
|
editable_palette, edit_palette_sub_index_);
|
||||||
Renderer::Get().UpdateBitmap(bitmap_);
|
Renderer::Get().UpdateBitmap(bitmap_);
|
||||||
refresh_graphics_ = false;
|
refresh_graphics_ = false;
|
||||||
util::logf("Applied interactive palette changes to canvas");
|
// LOG_INFO is undefined or missing required arguments; removed or fix as needed.
|
||||||
}
|
}
|
||||||
|
ImGui::Text(
|
||||||
|
"Load ROM palettes first using Enhanced Palette "
|
||||||
|
"Manager");
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
|
||||||
} else {
|
|
||||||
ImGui::Text("Load ROM palettes first using Enhanced Palette Manager");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
@@ -620,7 +652,8 @@ void Canvas::DrawContextMenu() {
|
|||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
if (BeginMenu("View Palette")) {
|
if (BeginMenu("View Palette")) {
|
||||||
(void)DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette", true, 8);
|
(void)DisplayEditablePalette(*bitmap_->mutable_palette(), "Palette",
|
||||||
|
true, 8);
|
||||||
EndMenu();
|
EndMenu();
|
||||||
}
|
}
|
||||||
EndMenu();
|
EndMenu();
|
||||||
@@ -658,7 +691,9 @@ void Canvas::DrawContextMenuItem(const ContextMenuItem& item) {
|
|||||||
|
|
||||||
if (item.subitems.empty()) {
|
if (item.subitems.empty()) {
|
||||||
// Simple menu item
|
// Simple menu item
|
||||||
if (ImGui::MenuItem(item.label.c_str(), item.shortcut.empty() ? nullptr : item.shortcut.c_str())) {
|
if (ImGui::MenuItem(item.label.c_str(), item.shortcut.empty()
|
||||||
|
? nullptr
|
||||||
|
: item.shortcut.c_str())) {
|
||||||
item.callback();
|
item.callback();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -685,7 +720,8 @@ void Canvas::ClearContextMenuItems() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
||||||
if (!bitmap.is_active()) return;
|
if (!bitmap.is_active())
|
||||||
|
return;
|
||||||
|
|
||||||
ImVec2 available = ImGui::GetContentRegionAvail();
|
ImVec2 available = ImGui::GetContentRegionAvail();
|
||||||
float scale_x = available.x / bitmap.width();
|
float scale_x = available.x / bitmap.width();
|
||||||
@@ -693,7 +729,8 @@ void Canvas::SetZoomToFit(const gfx::Bitmap& bitmap) {
|
|||||||
config_.global_scale = std::min(scale_x, scale_y);
|
config_.global_scale = std::min(scale_x, scale_y);
|
||||||
|
|
||||||
// Ensure minimum readable scale
|
// Ensure minimum readable scale
|
||||||
if (config_.global_scale < 0.25f) config_.global_scale = 0.25f;
|
if (config_.global_scale < 0.25f)
|
||||||
|
config_.global_scale = 0.25f;
|
||||||
|
|
||||||
global_scale_ = config_.global_scale; // Legacy compatibility
|
global_scale_ = config_.global_scale; // Legacy compatibility
|
||||||
|
|
||||||
@@ -718,7 +755,8 @@ void Canvas::ApplyConfigSnapshot(const canvas::CanvasConfig& snapshot) {
|
|||||||
config_.global_scale = snapshot.global_scale;
|
config_.global_scale = snapshot.global_scale;
|
||||||
config_.canvas_size = snapshot.canvas_size;
|
config_.canvas_size = snapshot.canvas_size;
|
||||||
config_.content_size = snapshot.content_size;
|
config_.content_size = snapshot.content_size;
|
||||||
config_.custom_canvas_size = snapshot.canvas_size.x > 0 && snapshot.canvas_size.y > 0;
|
config_.custom_canvas_size =
|
||||||
|
snapshot.canvas_size.x > 0 && snapshot.canvas_size.y > 0;
|
||||||
|
|
||||||
enable_grid_ = config_.enable_grid;
|
enable_grid_ = config_.enable_grid;
|
||||||
enable_hex_tile_labels_ = config_.enable_hex_labels;
|
enable_hex_tile_labels_ = config_.enable_hex_labels;
|
||||||
@@ -822,14 +860,17 @@ bool Canvas::DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile) {
|
|||||||
int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
|
int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
|
||||||
|
|
||||||
// Simple bounds check
|
// Simple bounds check
|
||||||
if (tile_x >= 0 && tile_x < tilemap.atlas.width() &&
|
if (tile_x >= 0 && tile_x < tilemap.atlas.width() && tile_y >= 0 &&
|
||||||
tile_y >= 0 && tile_y < tilemap.atlas.height()) {
|
tile_y < tilemap.atlas.height()) {
|
||||||
|
|
||||||
// Draw directly from atlas texture
|
// Draw directly from atlas texture
|
||||||
ImVec2 uv0 = ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
|
ImVec2 uv0 =
|
||||||
|
ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
|
||||||
static_cast<float>(tile_y) / tilemap.atlas.height());
|
static_cast<float>(tile_y) / tilemap.atlas.height());
|
||||||
ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) / tilemap.atlas.width(),
|
ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) /
|
||||||
static_cast<float>(tile_y + tilemap.tile_size.y) / tilemap.atlas.height());
|
tilemap.atlas.width(),
|
||||||
|
static_cast<float>(tile_y + tilemap.tile_size.y) /
|
||||||
|
tilemap.atlas.height());
|
||||||
|
|
||||||
draw_list_->AddImage(
|
draw_list_->AddImage(
|
||||||
(ImTextureID)(intptr_t)tilemap.atlas.texture(),
|
(ImTextureID)(intptr_t)tilemap.atlas.texture(),
|
||||||
@@ -1028,11 +1069,14 @@ void Canvas::DrawSelectRect(int current_map, int tile_size, float scale) {
|
|||||||
int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
|
int end_y = std::floor(drag_end_pos.y / scaled_size) * tile16_size;
|
||||||
|
|
||||||
// Swap the start and end positions if they are in the wrong order
|
// Swap the start and end positions if they are in the wrong order
|
||||||
if (start_x > end_x) std::swap(start_x, end_x);
|
if (start_x > end_x)
|
||||||
if (start_y > end_y) std::swap(start_y, end_y);
|
std::swap(start_x, end_x);
|
||||||
|
if (start_y > end_y)
|
||||||
|
std::swap(start_y, end_y);
|
||||||
|
|
||||||
selected_tiles_.clear();
|
selected_tiles_.clear();
|
||||||
selected_tiles_.reserve(((end_x - start_x) / tile16_size + 1) * ((end_y - start_y) / tile16_size + 1));
|
selected_tiles_.reserve(((end_x - start_x) / tile16_size + 1) *
|
||||||
|
((end_y - start_y) / tile16_size + 1));
|
||||||
|
|
||||||
// Number of tiles per local map (since each tile is 16x16)
|
// Number of tiles per local map (since each tile is 16x16)
|
||||||
constexpr int tiles_per_local_map = small_map_size / 16;
|
constexpr int tiles_per_local_map = small_map_size / 16;
|
||||||
@@ -1097,8 +1141,7 @@ void Canvas::DrawBitmap(Bitmap &bitmap, int x_offset, int y_offset, float scale,
|
|||||||
(ImTextureID)(intptr_t)bitmap.texture(),
|
(ImTextureID)(intptr_t)bitmap.texture(),
|
||||||
ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
|
ImVec2(canvas_p0_.x + x_offset + scrolling_.x,
|
||||||
canvas_p0_.y + y_offset + scrolling_.y),
|
canvas_p0_.y + y_offset + scrolling_.y),
|
||||||
ImVec2(
|
ImVec2(canvas_p0_.x + x_offset + scrolling_.x + rendered_size.x,
|
||||||
canvas_p0_.x + x_offset + scrolling_.x + rendered_size.x,
|
|
||||||
canvas_p0_.y + y_offset + scrolling_.y + rendered_size.y),
|
canvas_p0_.y + y_offset + scrolling_.y + rendered_size.y),
|
||||||
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
ImVec2(0, 0), ImVec2(1, 1), IM_COL32(255, 255, 255, alpha));
|
||||||
|
|
||||||
@@ -1140,20 +1183,22 @@ void Canvas::DrawBitmapTable(const BitmapTable &gfx_bin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutline(int x, int y, int w, int h) {
|
void Canvas::DrawOutline(int x, int y, int w, int h) {
|
||||||
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h, IM_COL32(255, 255, 255, 200));
|
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h,
|
||||||
|
IM_COL32(255, 255, 255, 200));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
|
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, ImVec4 color) {
|
||||||
CanvasUtils::DrawCanvasOutlineWithColor(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color);
|
CanvasUtils::DrawCanvasOutlineWithColor(draw_list_, canvas_p0_, scrolling_, x,
|
||||||
|
y, w, h, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
|
void Canvas::DrawOutlineWithColor(int x, int y, int w, int h, uint32_t color) {
|
||||||
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color);
|
CanvasUtils::DrawCanvasOutline(draw_list_, canvas_p0_, scrolling_, x, y, w, h,
|
||||||
|
color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawBitmapGroup(std::vector<int>& group, gfx::Tilemap& tilemap,
|
void Canvas::DrawBitmapGroup(std::vector<int>& group, gfx::Tilemap& tilemap,
|
||||||
int tile_size, float scale,
|
int tile_size, float scale, int local_map_size,
|
||||||
int local_map_size,
|
|
||||||
ImVec2 total_map_size) {
|
ImVec2 total_map_size) {
|
||||||
if (selected_points_.size() != 2) {
|
if (selected_points_.size() != 2) {
|
||||||
// points_ should contain exactly two points
|
// points_ should contain exactly two points
|
||||||
@@ -1165,7 +1210,8 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OPTIMIZATION: Use optimized rendering for large groups to improve performance
|
// OPTIMIZATION: Use optimized rendering for large groups to improve performance
|
||||||
bool use_optimized_rendering = group.size() > 128; // Optimize for large selections
|
bool use_optimized_rendering =
|
||||||
|
group.size() > 128; // Optimize for large selections
|
||||||
|
|
||||||
// Use provided map sizes for proper boundary handling
|
// Use provided map sizes for proper boundary handling
|
||||||
const int small_map = local_map_size;
|
const int small_map = local_map_size;
|
||||||
@@ -1190,8 +1236,10 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
int end_tile_y =
|
int end_tile_y =
|
||||||
static_cast<int>(std::floor(rect_bottom_right.y / (tile_size * scale)));
|
static_cast<int>(std::floor(rect_bottom_right.y / (tile_size * scale)));
|
||||||
|
|
||||||
if (start_tile_x > end_tile_x) std::swap(start_tile_x, end_tile_x);
|
if (start_tile_x > end_tile_x)
|
||||||
if (start_tile_y > end_tile_y) std::swap(start_tile_y, end_tile_y);
|
std::swap(start_tile_x, end_tile_x);
|
||||||
|
if (start_tile_y > end_tile_y)
|
||||||
|
std::swap(start_tile_y, end_tile_y);
|
||||||
|
|
||||||
// Calculate the size of the rectangle in 16x16 grid form
|
// Calculate the size of the rectangle in 16x16 grid form
|
||||||
int rect_width = (end_tile_x - start_tile_x) * tile_size;
|
int rect_width = (end_tile_x - start_tile_x) * tile_size;
|
||||||
@@ -1218,9 +1266,12 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
int tile_pos_y = (y + start_tile_y) * tile_size * scale;
|
int tile_pos_y = (y + start_tile_y) * tile_size * scale;
|
||||||
|
|
||||||
// OPTIMIZATION: Use pre-calculated values for better performance with large selections
|
// OPTIMIZATION: Use pre-calculated values for better performance with large selections
|
||||||
if (tilemap.atlas.is_active() && tilemap.atlas.texture() && atlas_tiles_per_row > 0) {
|
if (tilemap.atlas.is_active() && tilemap.atlas.texture() &&
|
||||||
int atlas_tile_x = (tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
|
atlas_tiles_per_row > 0) {
|
||||||
int atlas_tile_y = (tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
|
int atlas_tile_x =
|
||||||
|
(tile_id % atlas_tiles_per_row) * tilemap.tile_size.x;
|
||||||
|
int atlas_tile_y =
|
||||||
|
(tile_id / atlas_tiles_per_row) * tilemap.tile_size.y;
|
||||||
|
|
||||||
// Simple bounds check
|
// Simple bounds check
|
||||||
if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
|
if (atlas_tile_x >= 0 && atlas_tile_x < tilemap.atlas.width() &&
|
||||||
@@ -1228,9 +1279,12 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
|
|
||||||
// Calculate UV coordinates once for efficiency
|
// Calculate UV coordinates once for efficiency
|
||||||
const float atlas_width = static_cast<float>(tilemap.atlas.width());
|
const float atlas_width = static_cast<float>(tilemap.atlas.width());
|
||||||
const float atlas_height = static_cast<float>(tilemap.atlas.height());
|
const float atlas_height =
|
||||||
ImVec2 uv0 = ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
|
static_cast<float>(tilemap.atlas.height());
|
||||||
ImVec2 uv1 = ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
|
ImVec2 uv0 =
|
||||||
|
ImVec2(atlas_tile_x / atlas_width, atlas_tile_y / atlas_height);
|
||||||
|
ImVec2 uv1 =
|
||||||
|
ImVec2((atlas_tile_x + tilemap.tile_size.x) / atlas_width,
|
||||||
(atlas_tile_y + tilemap.tile_size.y) / atlas_height);
|
(atlas_tile_y + tilemap.tile_size.y) / atlas_height);
|
||||||
|
|
||||||
// Calculate screen positions
|
// Calculate screen positions
|
||||||
@@ -1240,15 +1294,16 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
float screen_h = tilemap.tile_size.y * scale;
|
float screen_h = tilemap.tile_size.y * scale;
|
||||||
|
|
||||||
// Use higher alpha for large selections to make them more visible
|
// Use higher alpha for large selections to make them more visible
|
||||||
uint32_t alpha_color = use_optimized_rendering ?
|
uint32_t alpha_color = use_optimized_rendering
|
||||||
IM_COL32(255, 255, 255, 200) : IM_COL32(255, 255, 255, 150);
|
? IM_COL32(255, 255, 255, 200)
|
||||||
|
: IM_COL32(255, 255, 255, 150);
|
||||||
|
|
||||||
// Draw from atlas texture with optimized parameters
|
// Draw from atlas texture with optimized parameters
|
||||||
draw_list_->AddImage(
|
draw_list_->AddImage(
|
||||||
(ImTextureID)(intptr_t)tilemap.atlas.texture(),
|
(ImTextureID)(intptr_t)tilemap.atlas.texture(),
|
||||||
ImVec2(screen_x, screen_y),
|
ImVec2(screen_x, screen_y),
|
||||||
ImVec2(screen_x + screen_w, screen_y + screen_h),
|
ImVec2(screen_x + screen_w, screen_y + screen_h), uv0, uv1,
|
||||||
uv0, uv1, alpha_color);
|
alpha_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1302,8 +1357,10 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
auto new_start_pos = AlignPosToGrid(clamped_mouse_pos, tile_size * scale);
|
auto new_start_pos = AlignPosToGrid(clamped_mouse_pos, tile_size * scale);
|
||||||
|
|
||||||
// Additional safety: clamp to overall map bounds
|
// Additional safety: clamp to overall map bounds
|
||||||
new_start_pos.x = std::clamp(new_start_pos.x, 0.0f, large_map_width - rect_width);
|
new_start_pos.x =
|
||||||
new_start_pos.y = std::clamp(new_start_pos.y, 0.0f, large_map_height - rect_height);
|
std::clamp(new_start_pos.x, 0.0f, large_map_width - rect_width);
|
||||||
|
new_start_pos.y =
|
||||||
|
std::clamp(new_start_pos.y, 0.0f, large_map_height - rect_height);
|
||||||
|
|
||||||
selected_points_.clear();
|
selected_points_.clear();
|
||||||
selected_points_.push_back(new_start_pos);
|
selected_points_.push_back(new_start_pos);
|
||||||
@@ -1313,22 +1370,26 @@ void Canvas::DrawBitmapGroup(std::vector<int> &group, gfx::Tilemap &tilemap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
|
void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) {
|
||||||
CanvasUtils::DrawCanvasRect(draw_list_, canvas_p0_, scrolling_, x, y, w, h, color, config_.global_scale);
|
CanvasUtils::DrawCanvasRect(draw_list_, canvas_p0_, scrolling_, x, y, w, h,
|
||||||
|
color, config_.global_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawText(std::string text, int x, int y) {
|
void Canvas::DrawText(std::string text, int x, int y) {
|
||||||
CanvasUtils::DrawCanvasText(draw_list_, canvas_p0_, scrolling_, text, x, y, config_.global_scale);
|
CanvasUtils::DrawCanvasText(draw_list_, canvas_p0_, scrolling_, text, x, y,
|
||||||
|
config_.global_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawGridLines(float grid_step) {
|
void Canvas::DrawGridLines(float grid_step) {
|
||||||
CanvasUtils::DrawCanvasGridLines(draw_list_, canvas_p0_, canvas_p1_, scrolling_, grid_step, config_.global_scale);
|
CanvasUtils::DrawCanvasGridLines(draw_list_, canvas_p0_, canvas_p1_,
|
||||||
|
scrolling_, grid_step, config_.global_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
||||||
// Draw grid + all lines in the canvas
|
// Draw grid + all lines in the canvas
|
||||||
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
||||||
if (enable_grid_) {
|
if (enable_grid_) {
|
||||||
if (custom_step_ != 0.f) grid_step = custom_step_;
|
if (custom_step_ != 0.f)
|
||||||
|
grid_step = custom_step_;
|
||||||
grid_step *= global_scale_; // Apply global scale to grid step
|
grid_step *= global_scale_; // Apply global scale to grid step
|
||||||
|
|
||||||
DrawGridLines(grid_step);
|
DrawGridLines(grid_step);
|
||||||
@@ -1361,11 +1422,13 @@ void Canvas::DrawInfoGrid(float grid_step, int tile_id_offset, int label_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawCustomHighlight(float grid_step) {
|
void Canvas::DrawCustomHighlight(float grid_step) {
|
||||||
CanvasUtils::DrawCustomHighlight(draw_list_, canvas_p0_, scrolling_, highlight_tile_id, grid_step);
|
CanvasUtils::DrawCustomHighlight(draw_list_, canvas_p0_, scrolling_,
|
||||||
|
highlight_tile_id, grid_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
||||||
if (config_.grid_step != 0.f) grid_step = config_.grid_step;
|
if (config_.grid_step != 0.f)
|
||||||
|
grid_step = config_.grid_step;
|
||||||
|
|
||||||
// Create render context for utilities
|
// Create render context for utilities
|
||||||
CanvasUtils::CanvasRenderContext ctx = {
|
CanvasUtils::CanvasRenderContext ctx = {
|
||||||
@@ -1376,8 +1439,7 @@ void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
|||||||
.global_scale = config_.global_scale,
|
.global_scale = config_.global_scale,
|
||||||
.enable_grid = config_.enable_grid,
|
.enable_grid = config_.enable_grid,
|
||||||
.enable_hex_labels = config_.enable_hex_labels,
|
.enable_hex_labels = config_.enable_hex_labels,
|
||||||
.grid_step = grid_step
|
.grid_step = grid_step};
|
||||||
};
|
|
||||||
|
|
||||||
// Use high-level utility function
|
// Use high-level utility function
|
||||||
CanvasUtils::DrawCanvasGrid(ctx, highlight_tile_id);
|
CanvasUtils::DrawCanvasGrid(ctx, highlight_tile_id);
|
||||||
@@ -1385,7 +1447,8 @@ void Canvas::DrawGrid(float grid_step, int tile_id_offset) {
|
|||||||
// Draw custom labels if enabled
|
// Draw custom labels if enabled
|
||||||
if (config_.enable_custom_labels) {
|
if (config_.enable_custom_labels) {
|
||||||
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true);
|
||||||
CanvasUtils::DrawCanvasLabels(ctx, labels_, current_labels_, tile_id_offset);
|
CanvasUtils::DrawCanvasLabels(ctx, labels_, current_labels_,
|
||||||
|
tile_id_offset);
|
||||||
draw_list_->PopClipRect();
|
draw_list_->PopClipRect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1400,8 +1463,7 @@ void Canvas::DrawOverlay() {
|
|||||||
.global_scale = config_.global_scale,
|
.global_scale = config_.global_scale,
|
||||||
.enable_grid = config_.enable_grid,
|
.enable_grid = config_.enable_grid,
|
||||||
.enable_hex_labels = config_.enable_hex_labels,
|
.enable_hex_labels = config_.enable_hex_labels,
|
||||||
.grid_step = config_.grid_step
|
.grid_step = config_.grid_step};
|
||||||
};
|
|
||||||
|
|
||||||
// Use high-level utility function with local points (synchronized from interaction handler)
|
// Use high-level utility function with local points (synchronized from interaction handler)
|
||||||
CanvasUtils::DrawCanvasOverlay(ctx, points_, selected_points_);
|
CanvasUtils::DrawCanvasOverlay(ctx, points_, selected_points_);
|
||||||
@@ -1540,7 +1602,8 @@ void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap,
|
|||||||
if (auto_resize && bitmap.is_active()) {
|
if (auto_resize && bitmap.is_active()) {
|
||||||
// Auto-calculate size based on bitmap content
|
// Auto-calculate size based on bitmap content
|
||||||
ImVec2 content_size = ImVec2(bitmap.width(), bitmap.height());
|
ImVec2 content_size = ImVec2(bitmap.width(), bitmap.height());
|
||||||
ImVec2 preferred_size = CanvasUtils::CalculatePreferredCanvasSize(content_size, canvas.GetGlobalScale());
|
ImVec2 preferred_size = CanvasUtils::CalculatePreferredCanvasSize(
|
||||||
|
content_size, canvas.GetGlobalScale());
|
||||||
canvas.SetCanvasSize(preferred_size);
|
canvas.SetCanvasSize(preferred_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1575,13 +1638,15 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
modal_config.is_draggable = draggable_;
|
modal_config.is_draggable = draggable_;
|
||||||
modal_config.auto_resize = config_.auto_resize;
|
modal_config.auto_resize = config_.auto_resize;
|
||||||
modal_config.scrolling = scrolling_;
|
modal_config.scrolling = scrolling_;
|
||||||
modal_config.on_config_changed = [this](const canvas::CanvasConfig& updated_config) {
|
modal_config.on_config_changed =
|
||||||
|
[this](const canvas::CanvasConfig& updated_config) {
|
||||||
// Update legacy variables when config changes
|
// Update legacy variables when config changes
|
||||||
enable_grid_ = updated_config.enable_grid;
|
enable_grid_ = updated_config.enable_grid;
|
||||||
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
|
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
|
||||||
enable_custom_labels_ = updated_config.enable_custom_labels;
|
enable_custom_labels_ = updated_config.enable_custom_labels;
|
||||||
};
|
};
|
||||||
modal_config.on_scale_changed = [this](const canvas::CanvasConfig& updated_config) {
|
modal_config.on_scale_changed =
|
||||||
|
[this](const canvas::CanvasConfig& updated_config) {
|
||||||
global_scale_ = updated_config.global_scale;
|
global_scale_ = updated_config.global_scale;
|
||||||
scrolling_ = updated_config.scrolling;
|
scrolling_ = updated_config.scrolling;
|
||||||
};
|
};
|
||||||
@@ -1591,15 +1656,18 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to legacy modal system
|
// Fallback to legacy modal system
|
||||||
if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("Advanced Canvas Properties", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text("Advanced Canvas Configuration");
|
ImGui::Text("Advanced Canvas Configuration");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
// Canvas properties (read-only info)
|
// Canvas properties (read-only info)
|
||||||
ImGui::Text("Canvas Properties");
|
ImGui::Text("Canvas Properties");
|
||||||
ImGui::Text("ID: %s", canvas_id_.c_str());
|
ImGui::Text("ID: %s", canvas_id_.c_str());
|
||||||
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x, config_.canvas_size.y);
|
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
|
||||||
ImGui::Text("Content Size: %.0f x %.0f", config_.content_size.x, config_.content_size.y);
|
config_.canvas_size.y);
|
||||||
|
ImGui::Text("Content Size: %.0f x %.0f", config_.content_size.x,
|
||||||
|
config_.content_size.y);
|
||||||
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
||||||
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
||||||
|
|
||||||
@@ -1607,7 +1675,8 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
ImVec2 min_size = GetMinimumSize();
|
ImVec2 min_size = GetMinimumSize();
|
||||||
ImVec2 preferred_size = GetPreferredSize();
|
ImVec2 preferred_size = GetPreferredSize();
|
||||||
ImGui::Text("Minimum Size: %.0f x %.0f", min_size.x, min_size.y);
|
ImGui::Text("Minimum Size: %.0f x %.0f", min_size.x, min_size.y);
|
||||||
ImGui::Text("Preferred Size: %.0f x %.0f", preferred_size.x, preferred_size.y);
|
ImGui::Text("Preferred Size: %.0f x %.0f", preferred_size.x,
|
||||||
|
preferred_size.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editable properties using new config system
|
// Editable properties using new config system
|
||||||
@@ -1619,7 +1688,8 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
if (ImGui::Checkbox("Enable Hex Labels", &config_.enable_hex_labels)) {
|
if (ImGui::Checkbox("Enable Hex Labels", &config_.enable_hex_labels)) {
|
||||||
enable_hex_tile_labels_ = config_.enable_hex_labels; // Legacy sync
|
enable_hex_tile_labels_ = config_.enable_hex_labels; // Legacy sync
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox("Enable Custom Labels", &config_.enable_custom_labels)) {
|
if (ImGui::Checkbox("Enable Custom Labels",
|
||||||
|
&config_.enable_custom_labels)) {
|
||||||
enable_custom_labels_ = config_.enable_custom_labels; // Legacy sync
|
enable_custom_labels_ = config_.enable_custom_labels; // Legacy sync
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox("Enable Context Menu", &config_.enable_context_menu)) {
|
if (ImGui::Checkbox("Enable Context Menu", &config_.enable_context_menu)) {
|
||||||
@@ -1635,14 +1705,16 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
// Grid controls
|
// Grid controls
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Grid Configuration");
|
ImGui::Text("Grid Configuration");
|
||||||
if (ImGui::SliderFloat("Grid Step", &config_.grid_step, 1.0f, 128.0f, "%.1f")) {
|
if (ImGui::SliderFloat("Grid Step", &config_.grid_step, 1.0f, 128.0f,
|
||||||
|
"%.1f")) {
|
||||||
custom_step_ = config_.grid_step; // Legacy sync
|
custom_step_ = config_.grid_step; // Legacy sync
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale controls
|
// Scale controls
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Scale Configuration");
|
ImGui::Text("Scale Configuration");
|
||||||
if (ImGui::SliderFloat("Global Scale", &config_.global_scale, 0.1f, 10.0f, "%.2f")) {
|
if (ImGui::SliderFloat("Global Scale", &config_.global_scale, 0.1f, 10.0f,
|
||||||
|
"%.2f")) {
|
||||||
global_scale_ = config_.global_scale; // Legacy sync
|
global_scale_ = config_.global_scale; // Legacy sync
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1656,8 +1728,12 @@ void Canvas::ShowAdvancedCanvasProperties() {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Center View")) {
|
if (ImGui::Button("Center View")) {
|
||||||
if (bitmap_) {
|
if (bitmap_) {
|
||||||
scrolling_ = ImVec2(-(bitmap_->width() * config_.global_scale - config_.canvas_size.x) / 2.0f,
|
scrolling_ = ImVec2(
|
||||||
-(bitmap_->height() * config_.global_scale - config_.canvas_size.y) / 2.0f);
|
-(bitmap_->width() * config_.global_scale - config_.canvas_size.x) /
|
||||||
|
2.0f,
|
||||||
|
-(bitmap_->height() * config_.global_scale -
|
||||||
|
config_.canvas_size.y) /
|
||||||
|
2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1685,14 +1761,16 @@ void Canvas::ShowScalingControls() {
|
|||||||
modal_config.is_draggable = draggable_;
|
modal_config.is_draggable = draggable_;
|
||||||
modal_config.auto_resize = config_.auto_resize;
|
modal_config.auto_resize = config_.auto_resize;
|
||||||
modal_config.scrolling = scrolling_;
|
modal_config.scrolling = scrolling_;
|
||||||
modal_config.on_config_changed = [this](const canvas::CanvasConfig& updated_config) {
|
modal_config.on_config_changed =
|
||||||
|
[this](const canvas::CanvasConfig& updated_config) {
|
||||||
// Update legacy variables when config changes
|
// Update legacy variables when config changes
|
||||||
enable_grid_ = updated_config.enable_grid;
|
enable_grid_ = updated_config.enable_grid;
|
||||||
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
|
enable_hex_tile_labels_ = updated_config.enable_hex_labels;
|
||||||
enable_custom_labels_ = updated_config.enable_custom_labels;
|
enable_custom_labels_ = updated_config.enable_custom_labels;
|
||||||
enable_context_menu_ = updated_config.enable_context_menu;
|
enable_context_menu_ = updated_config.enable_context_menu;
|
||||||
};
|
};
|
||||||
modal_config.on_scale_changed = [this](const canvas::CanvasConfig& updated_config) {
|
modal_config.on_scale_changed =
|
||||||
|
[this](const canvas::CanvasConfig& updated_config) {
|
||||||
draggable_ = updated_config.is_draggable;
|
draggable_ = updated_config.is_draggable;
|
||||||
custom_step_ = updated_config.grid_step;
|
custom_step_ = updated_config.grid_step;
|
||||||
global_scale_ = updated_config.global_scale;
|
global_scale_ = updated_config.global_scale;
|
||||||
@@ -1704,13 +1782,15 @@ void Canvas::ShowScalingControls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to legacy modal system
|
// Fallback to legacy modal system
|
||||||
if (ImGui::BeginPopupModal("Scaling Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("Scaling Controls", nullptr,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
ImGui::Text("Canvas Scaling and Display Controls");
|
ImGui::Text("Canvas Scaling and Display Controls");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
// Global scale with new config system
|
// Global scale with new config system
|
||||||
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
ImGui::Text("Global Scale: %.3f", config_.global_scale);
|
||||||
if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f, "%.2f")) {
|
if (ImGui::SliderFloat("##GlobalScale", &config_.global_scale, 0.1f, 10.0f,
|
||||||
|
"%.2f")) {
|
||||||
global_scale_ = config_.global_scale; // Legacy sync
|
global_scale_ = config_.global_scale; // Legacy sync
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1750,7 +1830,8 @@ void Canvas::ShowScalingControls() {
|
|||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Grid Configuration");
|
ImGui::Text("Grid Configuration");
|
||||||
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
ImGui::Text("Grid Step: %.1f", config_.grid_step);
|
||||||
if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f, "%.1f")) {
|
if (ImGui::SliderFloat("##GridStep", &config_.grid_step, 1.0f, 128.0f,
|
||||||
|
"%.1f")) {
|
||||||
custom_step_ = config_.grid_step; // Legacy sync
|
custom_step_ = config_.grid_step; // Legacy sync
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1779,13 +1860,15 @@ void Canvas::ShowScalingControls() {
|
|||||||
// Canvas size info
|
// Canvas size info
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Canvas Information");
|
ImGui::Text("Canvas Information");
|
||||||
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x, config_.canvas_size.y);
|
ImGui::Text("Canvas Size: %.0f x %.0f", config_.canvas_size.x,
|
||||||
|
config_.canvas_size.y);
|
||||||
ImGui::Text("Scaled Size: %.0f x %.0f",
|
ImGui::Text("Scaled Size: %.0f x %.0f",
|
||||||
config_.canvas_size.x * config_.global_scale,
|
config_.canvas_size.x * config_.global_scale,
|
||||||
config_.canvas_size.y * config_.global_scale);
|
config_.canvas_size.y * config_.global_scale);
|
||||||
if (bitmap_) {
|
if (bitmap_) {
|
||||||
ImGui::Text("Bitmap Size: %d x %d", bitmap_->width(), bitmap_->height());
|
ImGui::Text("Bitmap Size: %d x %d", bitmap_->width(), bitmap_->height());
|
||||||
ImGui::Text("Effective Scale: %.3f x %.3f",
|
ImGui::Text(
|
||||||
|
"Effective Scale: %.3f x %.3f",
|
||||||
(config_.canvas_size.x * config_.global_scale) / bitmap_->width(),
|
(config_.canvas_size.x * config_.global_scale) / bitmap_->width(),
|
||||||
(config_.canvas_size.y * config_.global_scale) / bitmap_->height());
|
(config_.canvas_size.y * config_.global_scale) / bitmap_->height());
|
||||||
}
|
}
|
||||||
@@ -1800,20 +1883,21 @@ void Canvas::ShowScalingControls() {
|
|||||||
// BPP format management methods
|
// BPP format management methods
|
||||||
void Canvas::ShowBppFormatSelector() {
|
void Canvas::ShowBppFormatSelector() {
|
||||||
if (!bpp_format_ui_) {
|
if (!bpp_format_ui_) {
|
||||||
bpp_format_ui_ = std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
|
bpp_format_ui_ =
|
||||||
|
std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap_) {
|
if (bitmap_) {
|
||||||
bpp_format_ui_->RenderFormatSelector(bitmap_, bitmap_->palette(),
|
bpp_format_ui_->RenderFormatSelector(
|
||||||
[this](gfx::BppFormat format) {
|
bitmap_, bitmap_->palette(),
|
||||||
ConvertBitmapFormat(format);
|
[this](gfx::BppFormat format) { ConvertBitmapFormat(format); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Canvas::ShowBppAnalysis() {
|
void Canvas::ShowBppAnalysis() {
|
||||||
if (!bpp_format_ui_) {
|
if (!bpp_format_ui_) {
|
||||||
bpp_format_ui_ = std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
|
bpp_format_ui_ =
|
||||||
|
std::make_unique<gui::BppFormatUI>(canvas_id_ + "_bpp_format");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap_) {
|
if (bitmap_) {
|
||||||
@@ -1823,11 +1907,13 @@ void Canvas::ShowBppAnalysis() {
|
|||||||
|
|
||||||
void Canvas::ShowBppConversionDialog() {
|
void Canvas::ShowBppConversionDialog() {
|
||||||
if (!bpp_conversion_dialog_) {
|
if (!bpp_conversion_dialog_) {
|
||||||
bpp_conversion_dialog_ = std::make_unique<gui::BppConversionDialog>(canvas_id_ + "_bpp_conversion");
|
bpp_conversion_dialog_ = std::make_unique<gui::BppConversionDialog>(
|
||||||
|
canvas_id_ + "_bpp_conversion");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap_) {
|
if (bitmap_) {
|
||||||
bpp_conversion_dialog_->Show(*bitmap_, bitmap_->palette(),
|
bpp_conversion_dialog_->Show(
|
||||||
|
*bitmap_, bitmap_->palette(),
|
||||||
[this](gfx::BppFormat format, bool preserve_palette) {
|
[this](gfx::BppFormat format, bool preserve_palette) {
|
||||||
ConvertBitmapFormat(format);
|
ConvertBitmapFormat(format);
|
||||||
});
|
});
|
||||||
@@ -1837,7 +1923,8 @@ void Canvas::ShowBppConversionDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Canvas::ConvertBitmapFormat(gfx::BppFormat target_format) {
|
bool Canvas::ConvertBitmapFormat(gfx::BppFormat target_format) {
|
||||||
if (!bitmap_) return false;
|
if (!bitmap_)
|
||||||
|
return false;
|
||||||
|
|
||||||
gfx::BppFormat current_format = GetCurrentBppFormat();
|
gfx::BppFormat current_format = GetCurrentBppFormat();
|
||||||
if (current_format == target_format) {
|
if (current_format == target_format) {
|
||||||
@@ -1847,8 +1934,8 @@ bool Canvas::ConvertBitmapFormat(gfx::BppFormat target_format) {
|
|||||||
try {
|
try {
|
||||||
// Convert the bitmap data
|
// Convert the bitmap data
|
||||||
auto converted_data = gfx::BppFormatManager::Get().ConvertFormat(
|
auto converted_data = gfx::BppFormatManager::Get().ConvertFormat(
|
||||||
bitmap_->vector(), current_format, target_format,
|
bitmap_->vector(), current_format, target_format, bitmap_->width(),
|
||||||
bitmap_->width(), bitmap_->height());
|
bitmap_->height());
|
||||||
|
|
||||||
// Update the bitmap with converted data
|
// Update the bitmap with converted data
|
||||||
bitmap_->set_data(converted_data);
|
bitmap_->set_data(converted_data);
|
||||||
@@ -1864,7 +1951,8 @@ bool Canvas::ConvertBitmapFormat(gfx::BppFormat target_format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gfx::BppFormat Canvas::GetCurrentBppFormat() const {
|
gfx::BppFormat Canvas::GetCurrentBppFormat() const {
|
||||||
if (!bitmap_) return gfx::BppFormat::kBpp8;
|
if (!bitmap_)
|
||||||
|
return gfx::BppFormat::kBpp8;
|
||||||
|
|
||||||
return gfx::BppFormatManager::Get().DetectFormat(
|
return gfx::BppFormatManager::Get().DetectFormat(
|
||||||
bitmap_->vector(), bitmap_->width(), bitmap_->height());
|
bitmap_->vector(), bitmap_->width(), bitmap_->height());
|
||||||
|
|||||||
@@ -86,12 +86,12 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
palette_manager.palettes_loaded = true;
|
palette_manager.palettes_loaded = true;
|
||||||
util::logf("Canvas: Loaded %zu ROM palette groups",
|
LOG_INFO("Canvas", "Loaded %zu ROM palette groups",
|
||||||
palette_manager.rom_palette_groups.size());
|
palette_manager.rom_palette_groups.size());
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Canvas: Failed to load ROM palette groups: %s", e.what());
|
LOG_ERROR("Canvas", "Failed to load ROM palette groups");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,12 +117,12 @@ bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Renderer::Get().UpdateBitmap(bitmap);
|
Renderer::Get().UpdateBitmap(bitmap);
|
||||||
util::logf("Canvas: Applied palette group %d, index %d to bitmap",
|
LOG_INFO("Canvas", "Applied palette group %d, index %d to bitmap",
|
||||||
group_index, palette_index);
|
group_index, palette_index);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Canvas: Failed to apply palette: %s", e.what());
|
LOG_ERROR("Canvas", "Failed to apply palette");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,12 +85,12 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
palette_manager.palettes_loaded = true;
|
palette_manager.palettes_loaded = true;
|
||||||
util::logf("Canvas: Loaded %zu ROM palette groups",
|
LOG_INFO("Canvas", "Loaded %zu ROM palette groups",
|
||||||
palette_manager.rom_palette_groups.size());
|
palette_manager.rom_palette_groups.size());
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Canvas: Failed to load ROM palette groups: %s", e.what());
|
LOG_ERROR("Canvas", "Failed to load ROM palette groups");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,12 +116,12 @@ bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Renderer::Get().UpdateBitmap(bitmap);
|
Renderer::Get().UpdateBitmap(bitmap);
|
||||||
util::logf("Canvas: Applied palette group %d, index %d to bitmap",
|
LOG_INFO("Canvas", "Applied palette group %d, index %d to bitmap",
|
||||||
group_index, palette_index);
|
group_index, palette_index);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Canvas: Failed to apply palette: %s", e.what());
|
LOG_ERROR("Canvas", "Failed to apply palette");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,12 +174,9 @@ bool EnhancedPaletteEditor::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index
|
|||||||
current_group_index_ = group_index;
|
current_group_index_ = group_index;
|
||||||
current_palette_index_ = palette_index;
|
current_palette_index_ = palette_index;
|
||||||
|
|
||||||
util::logf("Applied ROM palette: %s (index %d)",
|
|
||||||
palette_group_names_[group_index].c_str(), palette_index);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Failed to apply ROM palette: %s", e.what());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,10 +452,9 @@ void EnhancedPaletteEditor::LoadROMPalettes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rom_palettes_loaded_ = true;
|
rom_palettes_loaded_ = true;
|
||||||
util::logf("Enhanced Palette Editor: Loaded %zu ROM palette groups", rom_palette_groups_.size());
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Enhanced Palette Editor: Failed to load ROM palettes: %s", e.what());
|
LOG_ERROR("Enhanced Palette Editor", "Failed to load ROM palettes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
72
src/app/gui/feature_flags_menu.h
Normal file
72
src/app/gui/feature_flags_menu.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#ifndef YAZE_APP_GUI_FEATURE_FLAGS_MENU_H
|
||||||
|
#define YAZE_APP_GUI_FEATURE_FLAGS_MENU_H
|
||||||
|
|
||||||
|
#include "app/core/features.h"
|
||||||
|
#include "imgui/imgui.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
using ImGui::BeginMenu;
|
||||||
|
using ImGui::Checkbox;
|
||||||
|
using ImGui::EndMenu;
|
||||||
|
using ImGui::MenuItem;
|
||||||
|
using ImGui::Separator;
|
||||||
|
|
||||||
|
struct FlagsMenu {
|
||||||
|
void DrawOverworldFlags() {
|
||||||
|
Checkbox("Enable Overworld Sprites",
|
||||||
|
&core::FeatureFlags::get().overworld.kDrawOverworldSprites);
|
||||||
|
Separator();
|
||||||
|
Checkbox("Save Overworld Maps",
|
||||||
|
&core::FeatureFlags::get().overworld.kSaveOverworldMaps);
|
||||||
|
Checkbox("Save Overworld Entrances",
|
||||||
|
&core::FeatureFlags::get().overworld.kSaveOverworldEntrances);
|
||||||
|
Checkbox("Save Overworld Exits",
|
||||||
|
&core::FeatureFlags::get().overworld.kSaveOverworldExits);
|
||||||
|
Checkbox("Save Overworld Items",
|
||||||
|
&core::FeatureFlags::get().overworld.kSaveOverworldItems);
|
||||||
|
Checkbox("Save Overworld Properties",
|
||||||
|
&core::FeatureFlags::get().overworld.kSaveOverworldProperties);
|
||||||
|
Checkbox("Enable Custom Overworld Features",
|
||||||
|
&core::FeatureFlags::get().overworld.kLoadCustomOverworld);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("?")) {
|
||||||
|
ImGui::OpenPopup("CustomOverworldHelp");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("CustomOverworldHelp")) {
|
||||||
|
ImGui::Text("This flag enables ZSCustomOverworld features.");
|
||||||
|
ImGui::Text("If ZSCustomOverworld ASM is already applied to the ROM,");
|
||||||
|
ImGui::Text("features are auto-enabled regardless of this flag.");
|
||||||
|
ImGui::Text("For vanilla ROMs, enable this to use custom features.");
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
Checkbox("Apply ZSCustomOverworld ASM",
|
||||||
|
&core::FeatureFlags::get().overworld.kApplyZSCustomOverworldASM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawDungeonFlags() {
|
||||||
|
Checkbox("Save Dungeon Maps", &core::FeatureFlags::get().kSaveDungeonMaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawResourceFlags() {
|
||||||
|
Checkbox("Save All Palettes", &core::FeatureFlags::get().kSaveAllPalettes);
|
||||||
|
Checkbox("Save Gfx Groups", &core::FeatureFlags::get().kSaveGfxGroups);
|
||||||
|
Checkbox("Save Graphics Sheets", &core::FeatureFlags::get().kSaveGraphicsSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawSystemFlags() {
|
||||||
|
Checkbox("Enable Console Logging", &core::FeatureFlags::get().kLogToConsole);
|
||||||
|
Checkbox("Enable Performance Monitoring",
|
||||||
|
&core::FeatureFlags::get().kEnablePerformanceMonitoring);
|
||||||
|
Checkbox("Log Instructions to Emulator Debugger",
|
||||||
|
&core::FeatureFlags::get().kLogInstructions);
|
||||||
|
Checkbox("Use Native File Dialog (NFD)",
|
||||||
|
&core::FeatureFlags::get().kUseNativeFileDialog);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_GUI_FEATURE_FLAGS_MENU_H
|
||||||
@@ -2,6 +2,7 @@ set(
|
|||||||
YAZE_GUI_SRC
|
YAZE_GUI_SRC
|
||||||
app/gui/modules/asset_browser.cc
|
app/gui/modules/asset_browser.cc
|
||||||
app/gui/modules/text_editor.cc
|
app/gui/modules/text_editor.cc
|
||||||
|
app/gui/widgets/agent_chat_widget.cc
|
||||||
app/gui/canvas.cc
|
app/gui/canvas.cc
|
||||||
app/gui/canvas_utils.cc
|
app/gui/canvas_utils.cc
|
||||||
app/gui/enhanced_palette_editor.cc
|
app/gui/enhanced_palette_editor.cc
|
||||||
@@ -12,6 +13,7 @@ set(
|
|||||||
app/gui/background_renderer.cc
|
app/gui/background_renderer.cc
|
||||||
app/gui/bpp_format_ui.cc
|
app/gui/bpp_format_ui.cc
|
||||||
app/gui/widget_id_registry.cc
|
app/gui/widget_id_registry.cc
|
||||||
|
app/gui/widget_auto_register.cc
|
||||||
# Canvas system components
|
# Canvas system components
|
||||||
app/gui/canvas/canvas_modals.cc
|
app/gui/canvas/canvas_modals.cc
|
||||||
app/gui/canvas/canvas_context_menu.cc
|
app/gui/canvas/canvas_context_menu.cc
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ void ThemeManager::InitializeBuiltInThemes() {
|
|||||||
// Load all available theme files dynamically
|
// Load all available theme files dynamically
|
||||||
auto status = LoadAllAvailableThemes();
|
auto status = LoadAllAvailableThemes();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Warning: Failed to load some theme files: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to load some theme files");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have a valid current theme (Classic is already set above)
|
// Ensure we have a valid current theme (Classic is already set above)
|
||||||
@@ -326,7 +326,7 @@ void ThemeManager::ApplyTheme(const std::string& theme_name) {
|
|||||||
// Fallback to YAZE Tre if theme not found
|
// Fallback to YAZE Tre if theme not found
|
||||||
auto fallback_status = LoadTheme("YAZE Tre");
|
auto fallback_status = LoadTheme("YAZE Tre");
|
||||||
if (!fallback_status.ok()) {
|
if (!fallback_status.ok()) {
|
||||||
util::logf("Failed to load fallback theme: %s", fallback_status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to load fallback theme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,7 +426,7 @@ void ThemeManager::ShowThemeSelector(bool* p_open) {
|
|||||||
name.c_str()).c_str(), ImVec2(-1, 40))) {
|
name.c_str()).c_str(), ImVec2(-1, 40))) {
|
||||||
auto status = LoadTheme(name); // Use LoadTheme instead of ApplyTheme to ensure correct tracking
|
auto status = LoadTheme(name); // Use LoadTheme instead of ApplyTheme to ensure correct tracking
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Failed to load theme %s: %s", name.c_str(), status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to load theme %s", name.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ void ThemeManager::ShowThemeSelector(bool* p_open) {
|
|||||||
if (ImGui::Button(absl::StrFormat("%s Refresh Themes", ICON_MD_REFRESH).c_str())) {
|
if (ImGui::Button(absl::StrFormat("%s Refresh Themes", ICON_MD_REFRESH).c_str())) {
|
||||||
auto status = RefreshAvailableThemes();
|
auto status = RefreshAvailableThemes();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Failed to refresh themes: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to refresh themes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,7 +1019,7 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
|||||||
if (!current_file_path.empty()) {
|
if (!current_file_path.empty()) {
|
||||||
auto status = SaveThemeToFile(current_theme_, current_file_path);
|
auto status = SaveThemeToFile(current_theme_, current_file_path);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Failed to save theme: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to save theme");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No existing file, prompt for new location
|
// No existing file, prompt for new location
|
||||||
@@ -1027,7 +1027,7 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
|||||||
if (!file_path.empty()) {
|
if (!file_path.empty()) {
|
||||||
auto status = SaveThemeToFile(current_theme_, file_path);
|
auto status = SaveThemeToFile(current_theme_, file_path);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Failed to save theme: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to save theme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1038,7 +1038,7 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
|||||||
if (!file_path.empty()) {
|
if (!file_path.empty()) {
|
||||||
auto status = SaveThemeToFile(current_theme_, file_path);
|
auto status = SaveThemeToFile(current_theme_, file_path);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
util::logf("Failed to save theme: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to save theme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1831,7 +1831,7 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
|||||||
ApplyTheme(edit_theme);
|
ApplyTheme(edit_theme);
|
||||||
theme_backup_made = false; // Reset backup state since theme is now applied
|
theme_backup_made = false; // Reset backup state since theme is now applied
|
||||||
} else {
|
} else {
|
||||||
util::logf("Failed to save over current theme: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to save over current theme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1873,7 +1873,7 @@ void ThemeManager::ShowSimpleThemeEditor(bool* p_open) {
|
|||||||
themes_[edit_theme.name] = edit_theme;
|
themes_[edit_theme.name] = edit_theme;
|
||||||
ApplyTheme(edit_theme);
|
ApplyTheme(edit_theme);
|
||||||
} else {
|
} else {
|
||||||
util::logf("Failed to save theme: %s", status.message().data());
|
LOG_ERROR("Theme Manager", "Failed to save theme");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1981,7 +1981,7 @@ std::vector<std::string> ThemeManager::DiscoverAvailableThemeFiles() const {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
util::logf("Error scanning directory %s: %s", search_path.c_str(), e.what());
|
LOG_ERROR("Theme Manager", "Error scanning directory %s", search_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include "app/zelda3/dungeon/room_diagnostic.h"
|
#include "app/zelda3/dungeon/room_diagnostic.h"
|
||||||
#include "app/zelda3/dungeon/room_object.h"
|
#include "app/zelda3/dungeon/room_object.h"
|
||||||
#include "app/zelda3/sprite/sprite.h"
|
#include "app/zelda3/sprite/sprite.h"
|
||||||
#include "util/log.h"
|
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace zelda3 {
|
namespace zelda3 {
|
||||||
@@ -198,6 +197,7 @@ Room LoadRoomFromRom(Rom *rom, int room_id) {
|
|||||||
room.LoadBlocks();
|
room.LoadBlocks();
|
||||||
room.LoadPits();
|
room.LoadPits();
|
||||||
|
|
||||||
|
room.SetLoaded(true);
|
||||||
return room;
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,9 +318,7 @@ void Room::RenderRoomGraphics() {
|
|||||||
|
|
||||||
// Validate palette ID and fall back to palette 0 if invalid
|
// Validate palette ID and fall back to palette 0 if invalid
|
||||||
if (palette_id < 0 || palette_id >= num_palettes) {
|
if (palette_id < 0 || palette_id >= num_palettes) {
|
||||||
std::printf("WARNING: Room %d has invalid palette_id=%d (max=%d), falling back to palette 0\n",
|
//palette_id = 0;
|
||||||
room_id_, palette_id, num_palettes - 1);
|
|
||||||
palette_id = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the 90-color dungeon palette directly
|
// Load the 90-color dungeon palette directly
|
||||||
@@ -573,12 +571,6 @@ void Room::ParseObjectsFromLocation(int objects_location) {
|
|||||||
uint8_t b1 = 0;
|
uint8_t b1 = 0;
|
||||||
uint8_t b2 = 0;
|
uint8_t b2 = 0;
|
||||||
uint8_t b3 = 0;
|
uint8_t b3 = 0;
|
||||||
uint8_t posX = 0;
|
|
||||||
uint8_t posY = 0;
|
|
||||||
uint8_t sizeX = 0;
|
|
||||||
uint8_t sizeY = 0;
|
|
||||||
uint8_t sizeXY = 0;
|
|
||||||
short oid = 0;
|
|
||||||
int layer = 0;
|
int layer = 0;
|
||||||
bool door = false;
|
bool door = false;
|
||||||
bool end_read = false;
|
bool end_read = false;
|
||||||
@@ -852,7 +844,9 @@ void Room::LoadSprites() {
|
|||||||
rom_data[sprite_pointer + (room_id_ * 2)];
|
rom_data[sprite_pointer + (room_id_ * 2)];
|
||||||
|
|
||||||
int sprite_address = SnesToPc(sprite_address_snes);
|
int sprite_address = SnesToPc(sprite_address_snes);
|
||||||
bool sortsprites = rom_data[sprite_address] == 1;
|
if (rom_data[sprite_address] == 1) {
|
||||||
|
// sortsprites is unused
|
||||||
|
}
|
||||||
sprite_address += 1;
|
sprite_address += 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -897,7 +891,7 @@ void Room::LoadChests() {
|
|||||||
size_t clength = (rom_data[chests_length_pointer + 1] << 8) +
|
size_t clength = (rom_data[chests_length_pointer + 1] << 8) +
|
||||||
(rom_data[chests_length_pointer]);
|
(rom_data[chests_length_pointer]);
|
||||||
|
|
||||||
for (int i = 0; i < clength; i++) {
|
for (size_t i = 0; i < clength; i++) {
|
||||||
if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
|
if ((((rom_data[cpos + (i * 3) + 1] << 8) + (rom_data[cpos + (i * 3)])) &
|
||||||
0x7FFF) == room_id_) {
|
0x7FFF) == room_id_) {
|
||||||
// There's a chest in that room !
|
// There's a chest in that room !
|
||||||
@@ -945,7 +939,7 @@ void Room::LoadTorches() {
|
|||||||
auto rom_data = rom()->vector();
|
auto rom_data = rom()->vector();
|
||||||
|
|
||||||
// Load torch data from torch_data address
|
// Load torch data from torch_data address
|
||||||
int torch_count = rom_data[torches_length_pointer + 1] << 8 | rom_data[torches_length_pointer];
|
// int torch_count = rom_data[torches_length_pointer + 1] << 8 | rom_data[torches_length_pointer];
|
||||||
|
|
||||||
// For now, create placeholder torch objects
|
// For now, create placeholder torch objects
|
||||||
// TODO: Implement full torch loading from ROM data
|
// TODO: Implement full torch loading from ROM data
|
||||||
@@ -955,7 +949,7 @@ void Room::LoadBlocks() {
|
|||||||
auto rom_data = rom()->vector();
|
auto rom_data = rom()->vector();
|
||||||
|
|
||||||
// Load block data from blocks_* addresses
|
// Load block data from blocks_* addresses
|
||||||
int block_count = rom_data[blocks_length + 1] << 8 | rom_data[blocks_length];
|
// int block_count = rom_data[blocks_length + 1] << 8 | rom_data[blocks_length];
|
||||||
|
|
||||||
// For now, create placeholder block objects
|
// For now, create placeholder block objects
|
||||||
// TODO: Implement full block loading from ROM data
|
// TODO: Implement full block loading from ROM data
|
||||||
|
|||||||
@@ -313,6 +313,10 @@ class Room {
|
|||||||
void SetStair3Target(uint8_t target) { stair3_.target = target; }
|
void SetStair3Target(uint8_t target) { stair3_.target = target; }
|
||||||
void SetStair4Target(uint8_t target) { stair4_.target = target; }
|
void SetStair4Target(uint8_t target) { stair4_.target = target; }
|
||||||
|
|
||||||
|
// Loaded state
|
||||||
|
bool IsLoaded() const { return is_loaded_; }
|
||||||
|
void SetLoaded(bool loaded) { is_loaded_ = loaded; }
|
||||||
|
|
||||||
// Read-only accessors for metadata
|
// Read-only accessors for metadata
|
||||||
EffectKey effect() const { return effect_; }
|
EffectKey effect() const { return effect_; }
|
||||||
TagKey tag1() const { return tag1_; }
|
TagKey tag1() const { return tag1_; }
|
||||||
@@ -350,7 +354,7 @@ class Room {
|
|||||||
std::array<uint8_t, 0x4000> current_gfx16_;
|
std::array<uint8_t, 0x4000> current_gfx16_;
|
||||||
|
|
||||||
bool is_light_;
|
bool is_light_;
|
||||||
bool is_loaded_;
|
bool is_loaded_ = false;
|
||||||
bool is_dark_;
|
bool is_dark_;
|
||||||
bool is_floor_ = true;
|
bool is_floor_ = true;
|
||||||
|
|
||||||
|
|||||||
109
src/util/log.cc
Normal file
109
src/util/log.cc
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
// Helper function to convert LogLevel enum to a string representation.
|
||||||
|
static const char* LogLevelToString(LogLevel level) {
|
||||||
|
switch (level) {
|
||||||
|
case LogLevel::YAZE_DEBUG:
|
||||||
|
return "YAZE_DEBUG";
|
||||||
|
case LogLevel::INFO:
|
||||||
|
return "INFO";
|
||||||
|
case LogLevel::WARNING:
|
||||||
|
return "WARN";
|
||||||
|
case LogLevel::ERROR:
|
||||||
|
return "ERROR";
|
||||||
|
case LogLevel::FATAL:
|
||||||
|
return "FATAL";
|
||||||
|
}
|
||||||
|
return "UNKN";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- LogManager Implementation ---
|
||||||
|
|
||||||
|
LogManager& LogManager::instance() {
|
||||||
|
static LogManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager::LogManager()
|
||||||
|
: min_level_(LogLevel::INFO), all_categories_enabled_(true) {}
|
||||||
|
|
||||||
|
LogManager::~LogManager() {
|
||||||
|
if (log_stream_.is_open()) {
|
||||||
|
log_stream_.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::configure(LogLevel level, const std::string& file_path,
|
||||||
|
const std::set<std::string>& categories) {
|
||||||
|
min_level_.store(level);
|
||||||
|
|
||||||
|
if (categories.empty()) {
|
||||||
|
all_categories_enabled_.store(true);
|
||||||
|
enabled_categories_.clear();
|
||||||
|
} else {
|
||||||
|
all_categories_enabled_.store(false);
|
||||||
|
enabled_categories_ = categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a file path is provided, close any existing stream and open the new file.
|
||||||
|
if (!file_path.empty() && file_path != log_file_path_) {
|
||||||
|
if (log_stream_.is_open()) {
|
||||||
|
log_stream_.close();
|
||||||
|
}
|
||||||
|
// Open in append mode to preserve history.
|
||||||
|
log_stream_.open(file_path, std::ios::out | std::ios::app);
|
||||||
|
log_file_path_ = file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogManager::log(LogLevel level, absl::string_view category,
|
||||||
|
absl::string_view message) {
|
||||||
|
// 1. Filter by log level.
|
||||||
|
if (level < min_level_.load()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Filter by category.
|
||||||
|
if (!all_categories_enabled_.load()) {
|
||||||
|
if (enabled_categories_.find(std::string(category)) ==
|
||||||
|
enabled_categories_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Format the complete log message.
|
||||||
|
// [HH:MM:SS.ms] [LEVEL] [category] message
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto now_tt = std::chrono::system_clock::to_time_t(now);
|
||||||
|
auto now_tm = *std::localtime(&now_tt);
|
||||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
now.time_since_epoch()) %
|
||||||
|
1000;
|
||||||
|
|
||||||
|
std::string final_message = absl::StrFormat(
|
||||||
|
"[%02d:%02d:%02d.%03d] [%-5s] [%s] %s\n", now_tm.tm_hour, now_tm.tm_min,
|
||||||
|
now_tm.tm_sec, ms.count(), LogLevelToString(level), category, message);
|
||||||
|
|
||||||
|
// 4. Write to the configured sink (file or stderr).
|
||||||
|
if (log_stream_.is_open()) {
|
||||||
|
log_stream_ << final_message;
|
||||||
|
log_stream_.flush(); // Ensure immediate write for debugging.
|
||||||
|
} else {
|
||||||
|
std::cerr << final_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Abort on FATAL error.
|
||||||
|
if (level == LogLevel::FATAL) {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace yaze
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
#ifndef YAZE_UTIL_LOG_H
|
#ifndef YAZE_UTIL_LOG_H
|
||||||
#define YAZE_UTIL_LOG_H
|
#define YAZE_UTIL_LOG_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/strings/str_cat.h"
|
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
#include "app/core/features.h"
|
#include "app/core/features.h"
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
static std::string g_log_file_path = "yaze_log.txt";
|
// Static variables for library state
|
||||||
|
static std::string g_log_file_path = "";
|
||||||
|
|
||||||
|
|
||||||
// Set custom log file path
|
// Set custom log file path
|
||||||
inline void SetLogFile(const std::string& filepath) {
|
inline void SetLogFile(const std::string& filepath) {
|
||||||
@@ -50,6 +56,90 @@ static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
|||||||
fout.flush(); // Ensure immediate write for debugging
|
fout.flush(); // Ensure immediate write for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum LogLevel
|
||||||
|
* @brief Defines the severity levels for log messages.
|
||||||
|
* This allows for filtering messages based on their importance.
|
||||||
|
*/
|
||||||
|
enum class LogLevel { YAZE_DEBUG, INFO, WARNING, ERROR, FATAL };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class LogManager
|
||||||
|
* @brief A singleton that manages all logging configuration and output.
|
||||||
|
*
|
||||||
|
* It is designed to be configured once at application startup, typically from
|
||||||
|
* command-line arguments. It supports filtering by level and category, and can
|
||||||
|
* direct output to stderr (default) or a specified file.
|
||||||
|
*/
|
||||||
|
class LogManager {
|
||||||
|
public:
|
||||||
|
// Singleton access
|
||||||
|
static LogManager& instance();
|
||||||
|
|
||||||
|
// Deleted constructors for singleton pattern
|
||||||
|
LogManager(const LogManager&) = delete;
|
||||||
|
void operator=(const LogManager&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configures the logging system.
|
||||||
|
* @param level The minimum log level to record.
|
||||||
|
* @param file_path The path to the log file. If empty, logs to stderr.
|
||||||
|
* @param categories A set of specific categories to enable. If empty, all
|
||||||
|
* categories are enabled.
|
||||||
|
*/
|
||||||
|
void configure(LogLevel level, const std::string& file_path,
|
||||||
|
const std::set<std::string>& categories);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The primary logging function.
|
||||||
|
* @param level The severity level of the message.
|
||||||
|
* @param category The category of the message (e.g., "Graphics", "Agent").
|
||||||
|
* @param message The formatted log message.
|
||||||
|
*/
|
||||||
|
void log(LogLevel level, absl::string_view category,
|
||||||
|
absl::string_view message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LogManager();
|
||||||
|
~LogManager();
|
||||||
|
|
||||||
|
// Configuration state
|
||||||
|
std::atomic<LogLevel> min_level_;
|
||||||
|
std::set<std::string> enabled_categories_;
|
||||||
|
std::atomic<bool> all_categories_enabled_;
|
||||||
|
|
||||||
|
// Output sink
|
||||||
|
std::ofstream log_stream_;
|
||||||
|
std::string log_file_path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// logf mapping
|
||||||
|
#define logf LOG_INFO
|
||||||
|
|
||||||
|
// --- Public Logging Macros ---
|
||||||
|
// These macros provide a convenient and efficient API for logging.
|
||||||
|
// The `do-while(0)` loop ensures they behave like a single statement.
|
||||||
|
// The level check avoids the cost of string formatting if the message won't be
|
||||||
|
// logged.
|
||||||
|
|
||||||
|
#define LOG(level, category, format, ...) \
|
||||||
|
do { \
|
||||||
|
yaze::util::LogManager::instance().log( \
|
||||||
|
level, category, absl::StrFormat(format, ##__VA_ARGS__)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOG_DEBUG(category, format, ...) \
|
||||||
|
LOG(yaze::util::LogLevel::YAZE_DEBUG, category, format, ##__VA_ARGS__)
|
||||||
|
#define LOG_INFO(category, format, ...) \
|
||||||
|
LOG(yaze::util::LogLevel::INFO, category, format, ##__VA_ARGS__)
|
||||||
|
#define LOG_WARN(category, format, ...) \
|
||||||
|
LOG(yaze::util::LogLevel::WARNING, category, format, ##__VA_ARGS__)
|
||||||
|
#define LOG_ERROR(category, format, ...) \
|
||||||
|
LOG(yaze::util::LogLevel::ERROR, category, format, ##__VA_ARGS__)
|
||||||
|
#define LOG_FATAL(category, format, ...) \
|
||||||
|
LOG(yaze::util::LogLevel::FATAL, category, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ set(YAZE_UTIL_SRC
|
|||||||
util/bps.cc
|
util/bps.cc
|
||||||
util/flag.cc
|
util/flag.cc
|
||||||
util/hex.cc
|
util/hex.cc
|
||||||
|
util/log.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(yaze_util STATIC ${YAZE_UTIL_SRC})
|
add_library(yaze_util STATIC ${YAZE_UTIL_SRC})
|
||||||
|
|||||||
Reference in New Issue
Block a user