- Es pot posar shader preset directament per nom desde la consola
- shader preset i palette ja autocompleten amb la llista de noms
This commit is contained in:
@@ -30,10 +30,10 @@ categories:
|
||||
- keyword: SHADER
|
||||
handler: cmd_shader
|
||||
description: "Toggle/select shader (F4)"
|
||||
usage: "SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI]"
|
||||
usage: "SHADER [ON|OFF|NEXT|POSTFX|CRTPI|PRESET [NEXT|PREV|<name>]]"
|
||||
completions:
|
||||
SHADER: [ON, OFF, NEXT, POSTFX, CRTPI]
|
||||
SHADER NEXT: [PRESET]
|
||||
SHADER: [ON, OFF, NEXT, POSTFX, CRTPI, PRESET]
|
||||
dynamic_completions: true
|
||||
|
||||
- keyword: BORDER
|
||||
handler: cmd_border
|
||||
|
||||
@@ -100,13 +100,13 @@ namespace GlobalInputs {
|
||||
if (!Options::crtpi_presets.empty()) {
|
||||
Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
|
||||
Screen::get()->reloadCrtPi();
|
||||
Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name}); // NOLINT(readability-static-accessed-through-instance)
|
||||
Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name)}); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
} else {
|
||||
if (!Options::postfx_presets.empty()) {
|
||||
Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
|
||||
Screen::get()->reloadPostFX();
|
||||
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name}); // NOLINT(readability-static-accessed-through-instance)
|
||||
Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name)}); // NOLINT(readability-static-accessed-through-instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "core/rendering/surface.hpp" // Para Surface
|
||||
#include "core/rendering/text.hpp" // Para Text
|
||||
#include "game/options.hpp" // Para Options
|
||||
#include "utils/utils.hpp" // Para prettyName
|
||||
#include "game/ui/console.hpp" // Para Console
|
||||
#include "game/ui/notifier.hpp" // Para Notifier
|
||||
|
||||
@@ -95,11 +96,11 @@ void RenderInfo::render() const {
|
||||
std::string preset_name = "-";
|
||||
if (IS_CRTPI) {
|
||||
if (!Options::crtpi_presets.empty()) {
|
||||
preset_name = Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name;
|
||||
preset_name = prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name);
|
||||
}
|
||||
} else {
|
||||
if (!Options::postfx_presets.empty()) {
|
||||
preset_name = Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name;
|
||||
preset_name = prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name);
|
||||
}
|
||||
}
|
||||
const bool SHOW_SS = Options::video.supersampling.enabled && !IS_CRTPI;
|
||||
|
||||
@@ -359,26 +359,10 @@ void Console::handleEvent(const SDL_Event& event) {
|
||||
} else {
|
||||
const std::string base_cmd = upper.substr(0, space_pos);
|
||||
const std::string sub_prefix = upper.substr(space_pos + 1);
|
||||
if (registry_.hasDynamicCompletions(base_cmd) && base_cmd == "PALETTE" && Screen::get() != nullptr) {
|
||||
// NEXT/PREV primero, luego todos los nombres de paleta disponibles
|
||||
for (const auto* sv : {"NEXT", "PREV"}) {
|
||||
if (sub_prefix.empty() || std::string_view{sv}.starts_with(sub_prefix)) {
|
||||
tab_matches_.emplace_back("PALETTE " + std::string(sv));
|
||||
}
|
||||
}
|
||||
for (const auto& name : Screen::get()->getPaletteNames()) {
|
||||
if (sub_prefix.empty() || std::string_view{name}.starts_with(sub_prefix)) {
|
||||
tab_matches_.emplace_back("PALETTE " + name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const auto* opts = registry_.getCompletions(base_cmd);
|
||||
if (opts != nullptr) {
|
||||
for (const auto& arg : *opts) {
|
||||
if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) {
|
||||
tab_matches_.emplace_back(base_cmd + " " + arg);
|
||||
}
|
||||
}
|
||||
const auto opts = registry_.getCompletions(base_cmd);
|
||||
for (const auto& arg : opts) {
|
||||
if (sub_prefix.empty() || std::string_view{arg}.starts_with(sub_prefix)) {
|
||||
tab_matches_.emplace_back(base_cmd + " " + arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "game/options.hpp" // Para Options
|
||||
#include "game/scene_manager.hpp" // Para SceneManager
|
||||
#include "game/ui/notifier.hpp" // Para Notifier
|
||||
#include "utils/utils.hpp" // Para toUpper, prettyName
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "core/system/debug.hpp" // Para Debug
|
||||
@@ -110,7 +111,59 @@ static auto cmd_ss(const std::vector<std::string>& args) -> std::string {
|
||||
return "usage: ss [on|off|size|upscale [nearest|linear]|downscale [bilinear|lanczos2|lanczos3]]";
|
||||
}
|
||||
|
||||
// SHADER [ON|OFF|NEXT [PRESET]|POSTFX|CRTPI]
|
||||
// Helper: aplica un preset por dirección (NEXT/PREV) o nombre; devuelve mensaje
|
||||
static auto applyPreset(const std::vector<std::string>& args) -> std::string {
|
||||
const bool IS_CRTPI = Options::video.shader.current_shader == Rendering::ShaderType::CRTPI;
|
||||
const auto& presets_postfx = Options::postfx_presets;
|
||||
const auto& presets_crtpi = Options::crtpi_presets;
|
||||
const std::string shader_label = IS_CRTPI ? "CrtPi" : "PostFX";
|
||||
|
||||
auto& current_idx = IS_CRTPI ? Options::video.shader.current_crtpi_preset
|
||||
: Options::video.shader.current_postfx_preset;
|
||||
const int count = static_cast<int>(IS_CRTPI ? presets_crtpi.size() : presets_postfx.size());
|
||||
|
||||
if (count == 0) { return "No " + shader_label + " presets available"; }
|
||||
|
||||
const auto presetName = [&]() -> std::string {
|
||||
const auto& name = IS_CRTPI ? presets_crtpi[static_cast<size_t>(current_idx)].name
|
||||
: presets_postfx[static_cast<size_t>(current_idx)].name;
|
||||
return prettyName(name);
|
||||
};
|
||||
|
||||
if (args.empty()) {
|
||||
return shader_label + " preset: " + presetName();
|
||||
}
|
||||
|
||||
if (args[0] == "NEXT") {
|
||||
current_idx = (current_idx + 1) % count;
|
||||
} else if (args[0] == "PREV") {
|
||||
current_idx = (current_idx - 1 + count) % count;
|
||||
} else {
|
||||
// Buscar por nombre (case-insensitive, con guiones)
|
||||
std::string search = args[0];
|
||||
std::ranges::transform(search, search.begin(), ::toupper);
|
||||
bool found = false;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto& name = IS_CRTPI ? presets_crtpi[static_cast<size_t>(i)].name
|
||||
: presets_postfx[static_cast<size_t>(i)].name;
|
||||
if (toUpper(name) == search) {
|
||||
current_idx = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
std::string name_lower = args[0];
|
||||
std::ranges::transform(name_lower, name_lower.begin(), ::tolower);
|
||||
return "Unknown preset: " + name_lower;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_CRTPI) { Screen::get()->reloadCrtPi(); } else { Screen::get()->reloadPostFX(); }
|
||||
return shader_label + " preset: " + presetName();
|
||||
}
|
||||
|
||||
// SHADER [ON|OFF|NEXT|POSTFX|CRTPI|PRESET [NEXT|PREV|<name>]]
|
||||
static auto cmd_shader(const std::vector<std::string>& args) -> std::string {
|
||||
if (!Screen::get()->isHardwareAccelerated()) { return "No GPU acceleration"; }
|
||||
if (args.empty()) {
|
||||
@@ -136,29 +189,15 @@ static auto cmd_shader(const std::vector<std::string>& args) -> std::string {
|
||||
return "Shader: CrtPi";
|
||||
}
|
||||
if (args[0] == "NEXT") {
|
||||
if (args.size() >= 2 && args[1] == "PRESET") {
|
||||
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
|
||||
if (Options::crtpi_presets.empty()) { return "No CrtPi presets available"; }
|
||||
Options::video.shader.current_crtpi_preset =
|
||||
(Options::video.shader.current_crtpi_preset + 1) %
|
||||
static_cast<int>(Options::crtpi_presets.size());
|
||||
Screen::get()->reloadCrtPi();
|
||||
return "CrtPi preset: " +
|
||||
Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name;
|
||||
}
|
||||
if (Options::postfx_presets.empty()) { return "No PostFX presets available"; }
|
||||
Options::video.shader.current_postfx_preset =
|
||||
(Options::video.shader.current_postfx_preset + 1) %
|
||||
static_cast<int>(Options::postfx_presets.size());
|
||||
Screen::get()->reloadPostFX();
|
||||
return "PostFX preset: " +
|
||||
Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name;
|
||||
}
|
||||
Screen::get()->nextShader();
|
||||
return std::string("Shader: ") +
|
||||
(Options::video.shader.current_shader == Rendering::ShaderType::CRTPI ? "CrtPi" : "PostFX");
|
||||
}
|
||||
return "usage: shader [on|off|next [preset]|postfx|crtpi]";
|
||||
if (args[0] == "PRESET") {
|
||||
const std::vector<std::string> rest(args.begin() + 1, args.end());
|
||||
return applyPreset(rest);
|
||||
}
|
||||
return "usage: shader [on|off|next|postfx|crtpi|preset [next|prev|<name>]]";
|
||||
}
|
||||
|
||||
// BORDER [ON|OFF]
|
||||
@@ -744,6 +783,30 @@ void CommandRegistry::registerHandlers() {
|
||||
handlers_["cmd_scene"] = cmd_scene;
|
||||
#endif
|
||||
// HELP se registra en load() como lambda que captura this
|
||||
|
||||
// Proveedores de completions dinámicas
|
||||
// PALETTE: NEXT, PREV + nombres de paletas disponibles (UPPERCASE)
|
||||
dynamic_providers_["PALETTE"] = []() -> std::vector<std::string> {
|
||||
std::vector<std::string> result = {"NEXT", "PREV"};
|
||||
if (Screen::get() != nullptr) {
|
||||
for (const auto& name : Screen::get()->getPaletteNames()) {
|
||||
result.push_back(toUpper(name));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// SHADER PRESET: NEXT, PREV + nombres de presets del shader activo (UPPERCASE)
|
||||
dynamic_providers_["SHADER PRESET"] = []() -> std::vector<std::string> {
|
||||
std::vector<std::string> result = {"NEXT", "PREV"};
|
||||
const bool IS_CRTPI = Options::video.shader.current_shader == Rendering::ShaderType::CRTPI;
|
||||
if (IS_CRTPI) {
|
||||
for (const auto& p : Options::crtpi_presets) { result.push_back(toUpper(p.name)); }
|
||||
} else {
|
||||
for (const auto& p : Options::postfx_presets) { result.push_back(toUpper(p.name)); }
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
void CommandRegistry::load(const std::string& yaml_path) {
|
||||
@@ -914,10 +977,16 @@ auto CommandRegistry::generateConsoleHelp() const -> std::string {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto CommandRegistry::getCompletions(const std::string& path) const -> const std::vector<std::string>* {
|
||||
auto CommandRegistry::getCompletions(const std::string& path) const -> std::vector<std::string> {
|
||||
// Primero: buscar proveedor dinámico (tiene prioridad si existe)
|
||||
const auto dyn_it = dynamic_providers_.find(path);
|
||||
if (dyn_it != dynamic_providers_.end()) {
|
||||
return dyn_it->second();
|
||||
}
|
||||
// Fallback: completions estáticas del YAML
|
||||
const auto it = completions_map_.find(path);
|
||||
if (it != completions_map_.end()) { return &it->second; }
|
||||
return nullptr;
|
||||
if (it != completions_map_.end()) { return it->second; }
|
||||
return {};
|
||||
}
|
||||
|
||||
auto CommandRegistry::getVisibleKeywords() const -> std::vector<std::string> {
|
||||
@@ -929,8 +998,3 @@ auto CommandRegistry::getVisibleKeywords() const -> std::vector<std::string> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto CommandRegistry::hasDynamicCompletions(const std::string& keyword) const -> bool {
|
||||
const auto* def = findCommand(keyword);
|
||||
return def != nullptr && def->dynamic_completions;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ struct CommandDef {
|
||||
// Tipo de función handler para comandos
|
||||
using CommandHandler = std::function<std::string(const std::vector<std::string>& args)>;
|
||||
|
||||
// Proveedor de completions dinámicas: devuelve las opciones para TAB en UPPERCASE
|
||||
using DynamicCompletionProvider = std::function<std::vector<std::string>()>;
|
||||
|
||||
// Registro de comandos: une metadatos YAML con handlers C++
|
||||
class CommandRegistry {
|
||||
public:
|
||||
@@ -38,14 +41,16 @@ class CommandRegistry {
|
||||
[[nodiscard]] auto generateConsoleHelp() const -> std::string;
|
||||
|
||||
// TAB completion
|
||||
[[nodiscard]] auto getCompletions(const std::string& path) const -> const std::vector<std::string>*;
|
||||
// Devuelve las opciones de completado para un path dado (ej: "SHADER", "SHADER PRESET")
|
||||
// Combina completions estáticas del YAML con dinámicas registradas en C++
|
||||
[[nodiscard]] auto getCompletions(const std::string& path) const -> std::vector<std::string>;
|
||||
[[nodiscard]] auto getVisibleKeywords() const -> std::vector<std::string>;
|
||||
[[nodiscard]] auto hasDynamicCompletions(const std::string& keyword) const -> bool;
|
||||
|
||||
private:
|
||||
std::vector<CommandDef> commands_;
|
||||
std::unordered_map<std::string, CommandHandler> handlers_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> completions_map_;
|
||||
std::unordered_map<std::string, DynamicCompletionProvider> dynamic_providers_;
|
||||
|
||||
void registerHandlers();
|
||||
};
|
||||
|
||||
@@ -414,6 +414,13 @@ auto toUpper(const std::string& str) -> std::string {
|
||||
return upper_str;
|
||||
}
|
||||
|
||||
// Convierte guiones a espacios ("crt-live" → "crt live")
|
||||
auto prettyName(const std::string& str) -> std::string {
|
||||
std::string result = str;
|
||||
std::ranges::replace(result, '-', ' ');
|
||||
return result;
|
||||
}
|
||||
|
||||
// Obtiene el nombre de un fichero a partir de una ruta completa
|
||||
auto getFileName(const std::string& path) -> std::string {
|
||||
return std::filesystem::path(path).filename().string();
|
||||
|
||||
@@ -98,6 +98,7 @@ auto stringToBool(const std::string& str) -> bool; // Strin
|
||||
auto boolToString(bool value) -> std::string; // Bool a string (1/0)
|
||||
auto toLower(const std::string& str) -> std::string; // String a minúsculas
|
||||
auto toUpper(const std::string& str) -> std::string; // String a mayúsculas
|
||||
auto prettyName(const std::string& str) -> std::string; // Guiones a espacios ("crt-live" → "crt live")
|
||||
|
||||
// OPERACIONES CON STRINGS
|
||||
auto stringInVector(const std::vector<std::string>& vec, const std::string& str) -> bool; // Busca string en vector
|
||||
|
||||
Reference in New Issue
Block a user