feat(editor): spacing, border i centrat vertical del tilepicker via editor.yaml
This commit is contained in:
1 parent
bcbf990a5f
commit
a53cf4b797
7 files changed
+235
-29
No files matched your search
+2
-1
@@ -118,9 +118,10 @@ set(APP_SOURCES
|
|||||||
source/game/map/paint_layer.cpp
|
source/game/map/paint_layer.cpp
|
||||||
source/game/levels/level_manager.cpp
|
source/game/levels/level_manager.cpp
|
||||||
# Mòdul editor (només activa contingut en Debug via `#ifdef _DEBUG`).
|
# Mòdul editor (només activa contingut en Debug via `#ifdef _DEBUG`).
|
||||||
# En Release el fitxer compila a un object buit i l'enllaçador no
|
# En Release els fitxers compilen a object buit i l'enllaçador no
|
||||||
# arrossega cap símbol del joc final.
|
# arrossega cap símbol del joc final.
|
||||||
source/game/editor/editor.cpp
|
source/game/editor/editor.cpp
|
||||||
|
source/game/editor/editor_config.cpp
|
||||||
${ENTITY_SOURCES}
|
${ENTITY_SOURCES}
|
||||||
# Fitxer només del binari principal: `loadFromLevel` depèn de
|
# Fitxer només del binari principal: `loadFromLevel` depèn de
|
||||||
# Resource::Cache (→ Surface → GIF → àudio). Separat per no arrossegar
|
# Resource::Cache (→ Surface → GIF → àudio). Separat per no arrossegar
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Tema visual de l'editor de tilemap (només Debug). Carregat per
|
||||||
|
# EditorConfig a l'arrencada; les claus absents cauen als valors compilats
|
||||||
|
# a Defaults::Editor (veure source/utils/defaults.hpp).
|
||||||
|
|
||||||
|
picker:
|
||||||
|
margin: 8 # px entre canvas i caixa del picker
|
||||||
|
spacing: 1 # px de separació entre tiles dins del picker
|
||||||
|
|
||||||
|
# Colors ARGB en hex. Accepta enter (0xFF2244FF) o cadena ("#FF2244FF").
|
||||||
|
colors:
|
||||||
|
border: "0xFF2244FF" # vora 1 px que envolta canvas i picker
|
||||||
|
picker_bg: "0xFF101824" # fons del picker
|
||||||
|
picker_hover: "0xFFFFFFFF" # vora 1 px del tile sota el cursor
|
||||||
@@ -29,6 +29,9 @@
|
|||||||
#include "core/system/event_buffer.hpp"
|
#include "core/system/event_buffer.hpp"
|
||||||
#include "core/ui/ui_config.hpp"
|
#include "core/ui/ui_config.hpp"
|
||||||
#include "game/debug_config.hpp"
|
#include "game/debug_config.hpp"
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#include "game/editor/editor_config.hpp"
|
||||||
|
#endif
|
||||||
#include "game/levels/level_manager.hpp"
|
#include "game/levels/level_manager.hpp"
|
||||||
#include "game/options.hpp"
|
#include "game/options.hpp"
|
||||||
#include "game/scenes/game_scene.hpp"
|
#include "game/scenes/game_scene.hpp"
|
||||||
@@ -119,6 +122,12 @@ Director::Director() {
|
|||||||
// data/ui.yaml cau al valor compilat a Defaults::UI.
|
// data/ui.yaml cau al valor compilat a Defaults::UI.
|
||||||
UiConfig::get().load("data/ui.yaml");
|
UiConfig::get().load("data/ui.yaml");
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Tema visual de l'editor de tilemap (Debug-only). Claus absents
|
||||||
|
// cauen als valors compilats a Defaults::Editor.
|
||||||
|
EditorConfig::get().load("data/editor/editor.yaml");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Recursos compartits d'UI (paleta + fonts). Es carreguen abans que els
|
// Recursos compartits d'UI (paleta + fonts). Es carreguen abans que els
|
||||||
// overlays perquè tots els reaprofiten sense duplicar la lògica. Els
|
// overlays perquè tots els reaprofiten sense duplicar la lògica. Els
|
||||||
// noms de font viuen al fitxer data/ui.yaml via UiConfig.
|
// noms de font viuen al fitxer data/ui.yaml via UiConfig.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "core/resources/resource_cache.hpp"
|
#include "core/resources/resource_cache.hpp"
|
||||||
#include "core/resources/resource_helper.hpp"
|
#include "core/resources/resource_helper.hpp"
|
||||||
#include "core/system/event_buffer.hpp"
|
#include "core/system/event_buffer.hpp"
|
||||||
|
#include "game/editor/editor_config.hpp"
|
||||||
#include "game/levels/level_manager.hpp"
|
#include "game/levels/level_manager.hpp"
|
||||||
#include "game/map/tilemap.hpp"
|
#include "game/map/tilemap.hpp"
|
||||||
#include "utils/defaults.hpp"
|
#include "utils/defaults.hpp"
|
||||||
@@ -21,12 +22,20 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Gruix del border 1 px del picker: reservat dins del rectangle perquè
|
||||||
|
// no envaeixi els tiles ni se surti fora de la caixa. La cella efectiva
|
||||||
|
// de cada tile té `pitch = tile_size + spacing` i la graella s'inicia
|
||||||
|
// un píxel cap dins de cada vora.
|
||||||
|
constexpr int PICKER_BORDER = 1;
|
||||||
|
|
||||||
// Geometria del TilePicker en espai canvas. `cols × rows_visible` tiles
|
// Geometria del TilePicker en espai canvas. `cols × rows_visible` tiles
|
||||||
// encapsulats dins d'un rectangle amb `PICKER_MARGIN` per vora. Si el
|
// encapsulats dins d'un rectangle amb `picker.margin` per vora i un
|
||||||
// tileset cap en amplada (TCOLS·tile_size ≤ INNER_W), en respectem la
|
// border 1 px que el delimita. Si el tileset cap en amplada (la suma
|
||||||
// graella natural — els tiles surten ordenats igual que al GIF, només
|
// de tiles + spacing + border ≤ INNER_W), en respectem la graella
|
||||||
// amb scroll vertical si cal. Si no, linealitzem els índexs en una
|
// natural — els tiles surten ordenats igual que al GIF; si no,
|
||||||
// graella més ampla que sí cap, amb scroll vertical per veure la resta.
|
// linealitzem els índexs en una graella més ampla que sí cap. Quan el
|
||||||
|
// contingut cap sense scroll, la caixa s'arrossega a alçada mínima i
|
||||||
|
// es centra verticalment dins de l'àrea interna.
|
||||||
struct PickerLayout {
|
struct PickerLayout {
|
||||||
int x{0};
|
int x{0};
|
||||||
int y{0};
|
int y{0};
|
||||||
@@ -35,6 +44,8 @@ namespace {
|
|||||||
int cols{0};
|
int cols{0};
|
||||||
int rows_visible{0};
|
int rows_visible{0};
|
||||||
int tile_size{0};
|
int tile_size{0};
|
||||||
|
int spacing{0};
|
||||||
|
int pitch{0}; // tile_size + spacing — pas entre orígens de tiles veïns.
|
||||||
int total_tiles{0};
|
int total_tiles{0};
|
||||||
int max_scroll{0};
|
int max_scroll{0};
|
||||||
};
|
};
|
||||||
@@ -42,12 +53,20 @@ namespace {
|
|||||||
auto layoutPicker(const Tilemap& tm) -> PickerLayout {
|
auto layoutPicker(const Tilemap& tm) -> PickerLayout {
|
||||||
PickerLayout lay{};
|
PickerLayout lay{};
|
||||||
lay.tile_size = tm.getTileSize();
|
lay.tile_size = tm.getTileSize();
|
||||||
const int M = Defaults::Editor::PICKER_MARGIN;
|
lay.spacing = std::max(0, EditorConfig::get().pickerSpacing());
|
||||||
|
lay.pitch = lay.tile_size + lay.spacing;
|
||||||
|
const int M = EditorConfig::get().pickerMargin();
|
||||||
const int INNER_W = GameCanvas::WIDTH - (2 * M);
|
const int INNER_W = GameCanvas::WIDTH - (2 * M);
|
||||||
const int INNER_H = GameCanvas::HEIGHT - (2 * M);
|
const int INNER_H = GameCanvas::HEIGHT - (2 * M);
|
||||||
if (lay.tile_size <= 0) { return lay; }
|
if (lay.tile_size <= 0 || lay.pitch <= 0) { return lay; }
|
||||||
const int MAX_COLS = INNER_W / lay.tile_size;
|
|
||||||
lay.rows_visible = INNER_H / lay.tile_size;
|
// Espai útil per a tiles, descomptat el border 1 px de cada vora.
|
||||||
|
// Inversió de N·ts + (N-1)·spacing ≤ AVAIL → N ≤ (AVAIL+spacing)/pitch.
|
||||||
|
const int AVAIL_W = INNER_W - (2 * PICKER_BORDER);
|
||||||
|
const int AVAIL_H = INNER_H - (2 * PICKER_BORDER);
|
||||||
|
const int MAX_COLS = std::max(0, (AVAIL_W + lay.spacing) / lay.pitch);
|
||||||
|
const int MAX_ROWS = std::max(0, (AVAIL_H + lay.spacing) / lay.pitch);
|
||||||
|
|
||||||
int tcols = 0;
|
int tcols = 0;
|
||||||
const auto TILESET = Resource::Cache::get().getSurface(tm.getTilesetPath());
|
const auto TILESET = Resource::Cache::get().getSurface(tm.getTilesetPath());
|
||||||
if (TILESET != nullptr) {
|
if (TILESET != nullptr) {
|
||||||
@@ -59,17 +78,34 @@ namespace {
|
|||||||
// fer servir cols = TCOLS reprodueix exactament la disposició del GIF.
|
// fer servir cols = TCOLS reprodueix exactament la disposició del GIF.
|
||||||
const bool FITS_W = (tcols > 0 && tcols <= MAX_COLS);
|
const bool FITS_W = (tcols > 0 && tcols <= MAX_COLS);
|
||||||
lay.cols = FITS_W ? tcols : MAX_COLS;
|
lay.cols = FITS_W ? tcols : MAX_COLS;
|
||||||
lay.w = lay.cols * lay.tile_size;
|
|
||||||
lay.h = lay.rows_visible * lay.tile_size;
|
|
||||||
// Centrat horitzontalment quan la graella natural és més estreta;
|
|
||||||
// verticalment mantenim el marge superior i deixem scroll si cal.
|
|
||||||
lay.x = M + ((INNER_W - lay.w) / 2);
|
|
||||||
lay.y = M;
|
|
||||||
const int TOTAL_ROWS = (lay.cols > 0) ? ((lay.total_tiles + lay.cols - 1) / lay.cols) : 0;
|
const int TOTAL_ROWS = (lay.cols > 0) ? ((lay.total_tiles + lay.cols - 1) / lay.cols) : 0;
|
||||||
|
// Si tot el contingut cap sense scroll, encongim la caixa a les
|
||||||
|
// files reals; així podem centrar-la verticalment.
|
||||||
|
lay.rows_visible = std::min(MAX_ROWS, TOTAL_ROWS);
|
||||||
lay.max_scroll = std::max(0, TOTAL_ROWS - lay.rows_visible);
|
lay.max_scroll = std::max(0, TOTAL_ROWS - lay.rows_visible);
|
||||||
|
|
||||||
|
// Mida real de la caixa: tiles + spacing intern + 1 px de border per cara.
|
||||||
|
const int TILES_W = (lay.cols > 0) ? ((lay.cols * lay.tile_size) + ((lay.cols - 1) * lay.spacing)) : 0;
|
||||||
|
const int TILES_H = (lay.rows_visible > 0) ? ((lay.rows_visible * lay.tile_size) + ((lay.rows_visible - 1) * lay.spacing)) : 0;
|
||||||
|
lay.w = TILES_W + (2 * PICKER_BORDER);
|
||||||
|
lay.h = TILES_H + (2 * PICKER_BORDER);
|
||||||
|
// Centrat horitzontal i vertical dins de l'àrea interna; quan el
|
||||||
|
// picker arriba a l'alçada màxima, el centrat vertical degenera a
|
||||||
|
// origen=M (no hi ha espai extra que repartir).
|
||||||
|
lay.x = M + ((INNER_W - lay.w) / 2);
|
||||||
|
lay.y = M + ((INNER_H - lay.h) / 2);
|
||||||
return lay;
|
return lay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Origen (cantonada superior-esquerra) del tile a la cel·la (vc, vr)
|
||||||
|
// dins del picker. Centralitzat perquè render i hit-test no divergeixin.
|
||||||
|
auto pickerTileOrigin(const PickerLayout& lay, int vc, int vr) -> std::pair<int, int> {
|
||||||
|
const int PX = lay.x + PICKER_BORDER + (vc * lay.pitch);
|
||||||
|
const int PY = lay.y + PICKER_BORDER + (vr * lay.pitch);
|
||||||
|
return {PX, PY};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void Editor::enter() {
|
void Editor::enter() {
|
||||||
@@ -179,11 +215,21 @@ void Editor::updateMap(Tilemap& tilemap, int canvas_x, int canvas_y) {
|
|||||||
void Editor::updatePicker(const Tilemap& tilemap, int canvas_x, int canvas_y) {
|
void Editor::updatePicker(const Tilemap& tilemap, int canvas_x, int canvas_y) {
|
||||||
const auto LAY = layoutPicker(tilemap);
|
const auto LAY = layoutPicker(tilemap);
|
||||||
picker_hover_ = -1;
|
picker_hover_ = -1;
|
||||||
if (canvas_x >= LAY.x && canvas_x < LAY.x + LAY.w && canvas_y >= LAY.y && canvas_y < LAY.y + LAY.h && LAY.tile_size > 0) {
|
// Espai útil per als tiles dins de la caixa (descomptat el border 1 px).
|
||||||
const int VC = (canvas_x - LAY.x) / LAY.tile_size;
|
const int INNER_X = LAY.x + PICKER_BORDER;
|
||||||
const int VR = (canvas_y - LAY.y) / LAY.tile_size;
|
const int INNER_Y = LAY.y + PICKER_BORDER;
|
||||||
const int IDX = ((picker_scroll_ + VR) * LAY.cols) + VC;
|
const int RX = canvas_x - INNER_X;
|
||||||
if (IDX < LAY.total_tiles) { picker_hover_ = IDX; }
|
const int RY = canvas_y - INNER_Y;
|
||||||
|
if (RX >= 0 && RY >= 0 && LAY.tile_size > 0 && LAY.pitch > 0 && LAY.cols > 0) {
|
||||||
|
const int VC = RX / LAY.pitch;
|
||||||
|
const int VR = RY / LAY.pitch;
|
||||||
|
// Píxels de spacing entre cel·les: el clic cau a la "vora morta".
|
||||||
|
const bool IN_SPACING_X = (RX % LAY.pitch) >= LAY.tile_size;
|
||||||
|
const bool IN_SPACING_Y = (RY % LAY.pitch) >= LAY.tile_size;
|
||||||
|
if (!IN_SPACING_X && !IN_SPACING_Y && VC < LAY.cols && VR < LAY.rows_visible) {
|
||||||
|
const int IDX = ((picker_scroll_ + VR) * LAY.cols) + VC;
|
||||||
|
if (IDX < LAY.total_tiles) { picker_hover_ = IDX; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& ev : EventBuffer::events) {
|
for (const auto& ev : EventBuffer::events) {
|
||||||
@@ -214,7 +260,7 @@ void Editor::render(const Tilemap& tilemap) const {
|
|||||||
tilemap.renderTilePreview(PX, PY, brush_);
|
tilemap.renderTilePreview(PX, PY, brush_);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto BORDER = Defaults::Editor::BORDER_ARGB;
|
const auto BORDER = EditorConfig::get().borderArgb();
|
||||||
screen.fillRect(0, 0, W, 1, BORDER);
|
screen.fillRect(0, 0, W, 1, BORDER);
|
||||||
screen.fillRect(0, H - 1, W, 1, BORDER);
|
screen.fillRect(0, H - 1, W, 1, BORDER);
|
||||||
screen.fillRect(0, 0, 1, H, BORDER);
|
screen.fillRect(0, 0, 1, H, BORDER);
|
||||||
@@ -223,34 +269,43 @@ void Editor::render(const Tilemap& tilemap) const {
|
|||||||
|
|
||||||
void Editor::renderPicker(const Tilemap& tilemap) const {
|
void Editor::renderPicker(const Tilemap& tilemap) const {
|
||||||
const auto LAY = layoutPicker(tilemap);
|
const auto LAY = layoutPicker(tilemap);
|
||||||
|
if (LAY.cols <= 0 || LAY.rows_visible <= 0) { return; }
|
||||||
auto& screen = Screen::get();
|
auto& screen = Screen::get();
|
||||||
screen.fillRect(LAY.x, LAY.y, LAY.w, LAY.h, Defaults::Editor::PICKER_BG_ARGB);
|
const auto& cfg = EditorConfig::get();
|
||||||
|
screen.fillRect(LAY.x, LAY.y, LAY.w, LAY.h, cfg.pickerBgArgb());
|
||||||
|
|
||||||
for (int vr = 0; vr < LAY.rows_visible; ++vr) {
|
for (int vr = 0; vr < LAY.rows_visible; ++vr) {
|
||||||
const int LOGICAL_ROW = picker_scroll_ + vr;
|
const int LOGICAL_ROW = picker_scroll_ + vr;
|
||||||
for (int vc = 0; vc < LAY.cols; ++vc) {
|
for (int vc = 0; vc < LAY.cols; ++vc) {
|
||||||
const int IDX = (LOGICAL_ROW * LAY.cols) + vc;
|
const int IDX = (LOGICAL_ROW * LAY.cols) + vc;
|
||||||
if (IDX >= LAY.total_tiles) { break; }
|
if (IDX >= LAY.total_tiles) { break; }
|
||||||
const int PX = LAY.x + (vc * LAY.tile_size);
|
const auto [PX, PY] = pickerTileOrigin(LAY, vc, vr);
|
||||||
const int PY = LAY.y + (vr * LAY.tile_size);
|
|
||||||
tilemap.renderTilePreview(PX, PY, IDX);
|
tilemap.renderTilePreview(PX, PY, IDX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picker_hover_ >= 0 && LAY.cols > 0) {
|
if (picker_hover_ >= 0) {
|
||||||
const int VR = (picker_hover_ / LAY.cols) - picker_scroll_;
|
const int VR = (picker_hover_ / LAY.cols) - picker_scroll_;
|
||||||
const int VC = picker_hover_ % LAY.cols;
|
const int VC = picker_hover_ % LAY.cols;
|
||||||
if (VR >= 0 && VR < LAY.rows_visible) {
|
if (VR >= 0 && VR < LAY.rows_visible) {
|
||||||
const int PX = LAY.x + (VC * LAY.tile_size);
|
const auto [PX, PY] = pickerTileOrigin(LAY, VC, VR);
|
||||||
const int PY = LAY.y + (VR * LAY.tile_size);
|
|
||||||
const int TS = LAY.tile_size;
|
const int TS = LAY.tile_size;
|
||||||
constexpr auto C = Defaults::Editor::PICKER_HOVER_ARGB;
|
const auto C = cfg.pickerHoverArgb();
|
||||||
screen.fillRect(PX, PY, TS, 1, C);
|
screen.fillRect(PX, PY, TS, 1, C);
|
||||||
screen.fillRect(PX, PY + TS - 1, TS, 1, C);
|
screen.fillRect(PX, PY + TS - 1, TS, 1, C);
|
||||||
screen.fillRect(PX, PY, 1, TS, C);
|
screen.fillRect(PX, PY, 1, TS, C);
|
||||||
screen.fillRect(PX + TS - 1, PY, 1, TS, C);
|
screen.fillRect(PX + TS - 1, PY, 1, TS, C);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Border 1 px del picker, dibuixat al damunt del fons i dels tiles
|
||||||
|
// perquè delimite clarament la caixa també quan hi ha tiles que
|
||||||
|
// arriben a la vora.
|
||||||
|
const auto BORDER = cfg.borderArgb();
|
||||||
|
screen.fillRect(LAY.x, LAY.y, LAY.w, 1, BORDER);
|
||||||
|
screen.fillRect(LAY.x, LAY.y + LAY.h - 1, LAY.w, 1, BORDER);
|
||||||
|
screen.fillRect(LAY.x, LAY.y, 1, LAY.h, BORDER);
|
||||||
|
screen.fillRect(LAY.x + LAY.w - 1, LAY.y, 1, LAY.h, BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _DEBUG
|
#endif // _DEBUG
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#include "game/editor/editor_config.hpp"
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/resources/resource_helper.hpp"
|
||||||
|
#include "external/fkyaml_node.hpp"
|
||||||
|
#include "utils/defaults.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void readField(const fkyaml::node& node, const char* key, T& dst) {
|
||||||
|
if (node.contains(key)) { dst = node[key].get_value<T>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Llegeix un color ARGB. Accepta enter (0xFF2244FF al YAML) o cadena
|
||||||
|
// ("0xFF2244FF" / "#FF2244FF"). std::stoul amb base=0 detecta el prefix
|
||||||
|
// "0x" automàticament; si el valor és invàlid, deixem `dst` intacte i
|
||||||
|
// avisem perquè l'error no passi en silenci.
|
||||||
|
void readArgb(const fkyaml::node& node, const char* key, std::uint32_t& dst) {
|
||||||
|
if (!node.contains(key)) { return; }
|
||||||
|
try {
|
||||||
|
const auto& v = node[key];
|
||||||
|
if (v.is_integer()) {
|
||||||
|
dst = static_cast<std::uint32_t>(v.get_value<std::uint64_t>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto s = v.get_value<std::string>();
|
||||||
|
if (!s.empty() && s.front() == '#') { s.erase(0, 1); }
|
||||||
|
dst = static_cast<std::uint32_t>(std::stoul(s, nullptr, 0));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "EditorConfig: color '" << key << "' invàlid (" << e.what() << ") — ignorat\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EditorConfig::EditorConfig()
|
||||||
|
: picker_margin_(Defaults::Editor::PICKER_MARGIN),
|
||||||
|
picker_spacing_(Defaults::Editor::PICKER_SPACING),
|
||||||
|
border_argb_(Defaults::Editor::BORDER_ARGB),
|
||||||
|
picker_bg_argb_(Defaults::Editor::PICKER_BG_ARGB),
|
||||||
|
picker_hover_argb_(Defaults::Editor::PICKER_HOVER_ARGB) {
|
||||||
|
}
|
||||||
|
|
||||||
|
auto EditorConfig::get() -> EditorConfig& {
|
||||||
|
static EditorConfig instance_;
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorConfig::load(const std::string& file_path) {
|
||||||
|
auto bytes = Resource::Helper::loadFile(file_path);
|
||||||
|
if (bytes.empty()) {
|
||||||
|
std::cerr << "EditorConfig: no s'ha pogut obrir " << file_path << " — s'usaran els defaults compilats\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const auto* begin = reinterpret_cast<const char*>(bytes.data());
|
||||||
|
const auto* end = begin + bytes.size();
|
||||||
|
auto yaml = fkyaml::node::deserialize(begin, end);
|
||||||
|
|
||||||
|
if (yaml.contains("picker")) {
|
||||||
|
const auto& p = yaml["picker"];
|
||||||
|
readField(p, "margin", picker_margin_);
|
||||||
|
readField(p, "spacing", picker_spacing_);
|
||||||
|
}
|
||||||
|
if (yaml.contains("colors")) {
|
||||||
|
const auto& c = yaml["colors"];
|
||||||
|
readArgb(c, "border", border_argb_);
|
||||||
|
readArgb(c, "picker_bg", picker_bg_argb_);
|
||||||
|
readArgb(c, "picker_hover", picker_hover_argb_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "EditorConfig: carregat des de " << file_path << '\n';
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "EditorConfig: error parsejant " << file_path << ": " << e.what() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _DEBUG
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Configuració visual de l'editor de tilemap (Debug-only). Carregada des
|
||||||
|
// de data/editor/editor.yaml; les claus absents cauen als valors compilats
|
||||||
|
// a Defaults::Editor. Mateix patró que UiConfig (Meyers singleton + load).
|
||||||
|
class EditorConfig {
|
||||||
|
public:
|
||||||
|
static auto get() -> EditorConfig&;
|
||||||
|
|
||||||
|
EditorConfig(const EditorConfig&) = delete;
|
||||||
|
EditorConfig(EditorConfig&&) = delete;
|
||||||
|
auto operator=(const EditorConfig&) -> EditorConfig& = delete;
|
||||||
|
auto operator=(EditorConfig&&) -> EditorConfig& = delete;
|
||||||
|
|
||||||
|
void load(const std::string& file_path);
|
||||||
|
|
||||||
|
[[nodiscard]] auto pickerMargin() const -> int { return picker_margin_; }
|
||||||
|
[[nodiscard]] auto pickerSpacing() const -> int { return picker_spacing_; }
|
||||||
|
[[nodiscard]] auto borderArgb() const -> std::uint32_t { return border_argb_; }
|
||||||
|
[[nodiscard]] auto pickerBgArgb() const -> std::uint32_t { return picker_bg_argb_; }
|
||||||
|
[[nodiscard]] auto pickerHoverArgb() const -> std::uint32_t { return picker_hover_argb_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
EditorConfig();
|
||||||
|
~EditorConfig() = default;
|
||||||
|
|
||||||
|
int picker_margin_;
|
||||||
|
int picker_spacing_;
|
||||||
|
std::uint32_t border_argb_;
|
||||||
|
std::uint32_t picker_bg_argb_;
|
||||||
|
std::uint32_t picker_hover_argb_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _DEBUG
|
||||||
@@ -228,6 +228,10 @@ namespace Defaults {
|
|||||||
// les vores. Prou perquè el border blau de l'editor no es
|
// les vores. Prou perquè el border blau de l'editor no es
|
||||||
// superpose amb la graella del picker.
|
// superpose amb la graella del picker.
|
||||||
constexpr int PICKER_MARGIN = 8;
|
constexpr int PICKER_MARGIN = 8;
|
||||||
|
// Píxels de separació entre tiles dins de la graella del picker.
|
||||||
|
// 1 px deixa veure clarament on acaba un tile i on comença l'altre,
|
||||||
|
// útil sobretot quan dos tiles veïns tenen colors molt similars.
|
||||||
|
constexpr int PICKER_SPACING = 1;
|
||||||
// Fons del TilePicker (blau fosc, prou contrastat amb els tiles
|
// Fons del TilePicker (blau fosc, prou contrastat amb els tiles
|
||||||
// de color clar i clarament diferent del canvas).
|
// de color clar i clarament diferent del canvas).
|
||||||
constexpr std::uint32_t PICKER_BG_ARGB = 0xFF101824U;
|
constexpr std::uint32_t PICKER_BG_ARGB = 0xFF101824U;
|
||||||
|
|||||||
Reference in new issue
Block a user