feat(cpp/core): add text_editor_cpp_core component (PIMPL)

Wrapper en namespace fn:: sobre ImGuiColorTextEdit. La API publica solo expone
TextEditorState como tipo opaco; el TextEditor del vendor vive dentro del .cpp.

Soporta CodeLang::{Generic, GLSL, SQL, Cpp} (highlighting via las
LanguageDefinition del vendor). text_editor_render() devuelve true en el frame
en que el contenido cambia; flag dirty manejado independientemente para casos
"editado pero aun no guardado".

text_editor_get_text() cachea el resultado en un std::string del state para
mantener el const char* valido entre llamadas (el GetText() del vendor devuelve
std::string por valor).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 21:00:31 +02:00
parent f8a54942ee
commit 24905eebc7
3 changed files with 219 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
#include "text_editor.h"
#include "TextEditor.h"
#include <string>
namespace fn {
struct TextEditorState {
::TextEditor editor; // vendor type, oculto detras del PIMPL
std::string cached_text; // buffer estable para text_editor_get_text()
bool dirty = false;
};
static const ::TextEditor::LanguageDefinition& lang_def(CodeLang lang) {
switch (lang) {
case CodeLang::GLSL: return ::TextEditor::LanguageDefinition::GLSL();
case CodeLang::SQL: return ::TextEditor::LanguageDefinition::SQL();
case CodeLang::Cpp: return ::TextEditor::LanguageDefinition::CPlusPlus();
case CodeLang::Generic:
default:
return ::TextEditor::LanguageDefinition::CPlusPlus();
}
}
TextEditorState* text_editor_create(CodeLang lang) {
auto* s = new TextEditorState();
s->editor.SetLanguageDefinition(lang_def(lang));
s->editor.SetShowWhitespaces(false);
return s;
}
void text_editor_destroy(TextEditorState* state) {
delete state;
}
void text_editor_set_text(TextEditorState* state, const char* text) {
if (!state || !text) return;
state->editor.SetText(text);
state->cached_text = text;
state->dirty = false;
}
const char* text_editor_get_text(TextEditorState* state) {
if (!state) return "";
state->cached_text = state->editor.GetText();
// El editor anade siempre un '\n' final; lo dejamos para preservar
// la semantica del vendor (es lo que devuelve GetText()).
return state->cached_text.c_str();
}
bool text_editor_render(TextEditorState* state, const char* label, ImVec2 size) {
if (!state) return false;
state->editor.Render(label, size, true);
const bool changed = state->editor.IsTextChanged();
if (changed) state->dirty = true;
return changed;
}
bool text_editor_is_dirty(const TextEditorState* state) {
return state ? state->dirty : false;
}
void text_editor_clear_dirty(TextEditorState* state) {
if (state) state->dirty = false;
}
} // namespace fn
+50
View File
@@ -0,0 +1,50 @@
#pragma once
// text_editor — wrapper PIMPL sobre ImGuiColorTextEdit (vendor MIT).
// API en namespace fn:: que oculta el tipo concreto del vendor.
//
// Uso tipico:
// auto* ed = fn::text_editor_create(fn::CodeLang::GLSL);
// fn::text_editor_set_text(ed, "void main() { gl_FragColor = vec4(1); }");
// if (fn::text_editor_render(ed, "##ed", {600, 400})) { /* cambio */ }
// fn::text_editor_destroy(ed);
//
// El estado vive en TextEditorState (forward-declared), no se expone TextEditor del vendor.
#include "imgui.h"
namespace fn {
enum class CodeLang {
Generic,
GLSL,
SQL,
Cpp,
};
// Forward declaration — definicion completa en text_editor.cpp (PIMPL).
struct TextEditorState;
// Crea un editor con el lenguaje dado. El caller es dueno y debe llamar destroy.
TextEditorState* text_editor_create(CodeLang lang = CodeLang::Generic);
// Libera el editor. Acepta nullptr (no-op).
void text_editor_destroy(TextEditorState* state);
// Reemplaza el texto del editor.
void text_editor_set_text(TextEditorState* state, const char* text);
// Devuelve el texto actual. El puntero es valido solo hasta la siguiente llamada
// a text_editor_set_text/get_text/render sobre el mismo editor.
const char* text_editor_get_text(TextEditorState* state);
// Renderiza el editor. Devuelve true en el frame en que el contenido cambio.
bool text_editor_render(TextEditorState* state, const char* label, ImVec2 size);
// True si el editor esta marcado como "dirty" (modificado desde el ultimo clear).
bool text_editor_is_dirty(const TextEditorState* state);
// Limpia el flag dirty (tipicamente tras guardar a disco).
void text_editor_clear_dirty(TextEditorState* state);
} // namespace fn
+101
View File
@@ -0,0 +1,101 @@
---
name: text_editor
kind: component
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "fn::TextEditorState* fn::text_editor_create(fn::CodeLang); void fn::text_editor_destroy(fn::TextEditorState*); void fn::text_editor_set_text(fn::TextEditorState*, const char*); const char* fn::text_editor_get_text(fn::TextEditorState*); bool fn::text_editor_render(fn::TextEditorState*, const char* label, ImVec2 size); bool fn::text_editor_is_dirty(const fn::TextEditorState*); void fn::text_editor_clear_dirty(fn::TextEditorState*)"
description: "Editor de codigo embebido en ImGui con syntax highlighting (GLSL, SQL, C++, Generic). Wrapper PIMPL sobre ImGuiColorTextEdit (vendor MIT) — la API solo expone tipos opacos en namespace fn::."
tags: [imgui, editor, text, code, glsl, sql, syntax-highlighting, tokens]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui, TextEditor]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/text_editor.cpp"
framework: imgui
source_repo: "https://github.com/BalazsJako/ImGuiColorTextEdit"
source_license: "MIT"
source_file: "TextEditor.h, TextEditor.cpp"
params: []
output: "TextEditorState opaco — encapsula el editor del vendor + buffer de texto cacheado + flag dirty. Render devuelve true cuando el contenido cambio en el frame actual."
---
# text_editor
Editor de codigo in-app con syntax highlighting. Resuelve el ciclo de edicion sin alt-tab a un editor externo (ej: editar GLSL en `shaders_lab` y recompilar al guardar; futuro `sql_workbench` con CTAS sobre `registry.db`).
## API
```cpp
namespace fn {
enum class CodeLang { Generic, GLSL, SQL, Cpp };
struct TextEditorState; // PIMPL — tipo opaco
TextEditorState* text_editor_create(CodeLang lang = CodeLang::Generic);
void text_editor_destroy(TextEditorState*);
void text_editor_set_text(TextEditorState*, const char* text);
const char* text_editor_get_text(TextEditorState*); // valido hasta el siguiente call
bool text_editor_render(TextEditorState*, const char* label, ImVec2 size); // true si cambio
bool text_editor_is_dirty(const TextEditorState*);
void text_editor_clear_dirty(TextEditorState*);
}
```
## Ejemplo — editor GLSL con boton de guardado
```cpp
#include "core/text_editor.h"
#include "core/button.h"
static fn::TextEditorState* g_ed = nullptr;
void render() {
if (!g_ed) {
g_ed = fn::text_editor_create(fn::CodeLang::GLSL);
fn::text_editor_set_text(g_ed,
"#version 330\nout vec4 c;\nvoid main() { c = vec4(1); }\n");
}
fn::text_editor_render(g_ed, "##editor", ImVec2(600, 400));
if (fn::text_editor_is_dirty(g_ed)) ImGui::TextUnformatted("(modificado)");
if (fn_ui::button("Save")) {
FILE* f = std::fopen("/tmp/shader.glsl", "w");
std::fputs(fn::text_editor_get_text(g_ed), f);
std::fclose(f);
fn::text_editor_clear_dirty(g_ed);
}
}
```
## Decisiones de diseño
- **PIMPL** — el tipo `TextEditor` del vendor no aparece en `text_editor.h`. El header publico solo importa `imgui.h` (por `ImVec2`).
- **`get_text` cachea** el resultado en un `std::string` propio del state para que el `const char*` siga siendo valido entre llamadas (el `GetText()` del vendor devuelve un `std::string` por valor).
- **Sin tokens custom** — el editor hereda el theme global de ImGui (paleta interna del vendor). Para integrar `fn_tokens` haria falta crear un `Palette` propio; lo dejamos para una v2 si pesa visualmente.
- **`Generic` reusa C++** como default: el vendor no tiene un "plain text" sin highlighting; C++ es el menos intrusivo.
## Lenguajes soportados
| `CodeLang` | Highlighting | Origen |
|-----------|--------------|--------|
| `GLSL` | Si | `LanguageDefinition::GLSL()` |
| `SQL` | Si | `LanguageDefinition::SQL()` |
| `Cpp` | Si | `LanguageDefinition::CPlusPlus()` |
| `Generic` | C++ por defecto | `LanguageDefinition::CPlusPlus()` |
## Vendor
`cpp/vendor/imgui_text_edit/` (commit pinneado en su `README.md`). MIT.
## Combinacion sugerida
`text_editor` + `file_watcher_cpp_core` = ciclo de edicion completo. Ver `apps/primitives_gallery/demos_text_editor.cpp` para la demo combinada.