sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
C++ 72.3% C 26.2%git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gzsrc/text/handler.hpp raw
#pragma once
#include <cstdint>
#include <string>
#include "entities/camera.hpp"
#include "text/text.hpp"
#include "text/token.hpp"
#include "text/vim_buffer.hpp"
#include "utils/globals.hpp"
#include "utils/light.hpp"
#include "wall/wall.hpp"
namespace
Nimbin
{
class GlRenderer;
namespace Text
{
class Handler
{
public:
static constexpr
size_t HIT_BASE = size_t(1) << 32;
static bool is_NumberHit(size_t idx) { return idx >= HIT_BASE; }
static size_t line_Of (size_t idx) { return idx - HIT_BASE; }
struct NumberHit { size_t index; Vec3 aabb_Min; Vec3 aabb_Max; };
struct EditingMetrics {
double char_w;
double he_y;
double left_z;
double row0_y;
};
Handler() = default;
Handler(Wall::Role role, float world_scale, LayoutMode mode);
void set_Layout (LayoutMode m);
void set_FaceMode (FaceMode f) { _face = f; }
void set_Lexing (bool on) { _lex_enabled = on; }
void set_ShowNumbers (bool on, bool both_columns = false);
void set_Bordered (bool on) { _bordered = on; }
void set_Underline (bool on) { _underline = on; }
void set_DefaultStyle(float depth, float scale, double line_height, double word_spacing);
void set_Lines (const DynArray<std::string>& lines);
Wall::Role role () const { return _role; }
LayoutMode layout() const { return _layout; }
void set_AppendBaseline(double y) { _append_y = y; }
void set_AppendColor (SDL_FColor c) { _append_color = c; }
void clear();
void append_Line (const std::string& text);
const DynArray<Line>&
get_Lines () const { return _lines; }
DynArray<std::string>
as_Lines () const;
void append_Tokens (DynArray<TokenPtr> block);
void apply_Highlight(const DynArray<HiSpan>& spans, uint64_t gen);
Token::Data* token_At_Byte(size_t off);
TokenPtr detach (Token::Data* t);
void attach (DynArray<TokenPtr> tokens, const Vec3& impact);
// Spatial classifier for thrown words on the always-editing editor grid.
// 80/20 rule: a non-whitespace token's central 80% (of its column span) is a
// hit → DropToken; the outer 20% inserts beside it; whitespace cells always
// insert; vertical gaps between rows create a new line (row may be negative
// to prepend above the top, or >= line count to append below).
enum class HitKind : uint8_t { Insert, NewLine, DropToken };
struct EditorHit { HitKind kind; int row; int col; size_t line_idx; size_t token_idx; };
EditorHit classify_Impact(const Vec3& impact) const;
bool is_Editing() const { return _layout == LayoutMode::Editing; }
void clear_ToFloor(DynArray<TokenPtr>& out);
double scroll_DeltaToEye (size_t line_idx, double eye_y) const;
void bounce (size_t line_idx);
DynArray<NumberHit>
number_Hitboxes () const;
bool is_Dirty () const { return _lex_enabled && _dirty; }
uint64_t generation () const { return _gen; }
std::string render_Source();
void mark_Dirty ();
void update (double dt, const Camera& cam);
void render (GlRenderer& gl, const Camera& cam, const Light& light) const;
void scroll (double delta);
// ── vim cursor support (Editing) ──────────────────────────────────────────
EditingMetrics editing_Metrics() const;
double row_Y (int row) const;
// ── live editing (Editing) ───────────────────────────────────────────────
enum class EditReq : uint8_t { None, Write, WriteQuit, Exit, Discard };
void begin_Edit (double eye_y);
void end_Edit ();
bool edit_Active() const { return _capturing; }
EditReq feed_Keys (DynArray<int>& keys, double eye_y);
void mark_Saved ();
int cursor_Row () const;
int cursor_Col () const;
int cursor_VisualCol() const;
bool cursor_ToToken (Token::Data* t); // click-to-place: move cursor to a clicked wall token
bool cursor_AtImpact(const Vec3& impact); // click-to-place: exact char under the crosshair
struct CursorHit { int row = 0; int byte = 0; bool ok = false; };
CursorHit resolve_Impact (const Vec3& impact) const; // map crosshair → char, current layout
void place_CursorInsert(const CursorHit& hit); // apply after begin_Edit + scroll in
const std::string& cmdline_Text() const;
std::string modeline () const;
std::string vim_ShowCmd () const;
VimBuffer::Mode cursor_Mode () const;
const DynArray<std::string>&
status_Lines() const;
// Frustum half-height (world units) visible at the editor wall, so the Editing
// scrolloff knows how many rows actually fit and can keep the first/last line
// in view when zoomed in. Set each frame by the Connection (const view-state
// cache); 0 means "unknown" -> fall back to the default margin.
void set_ViewHalfHeight(double h) const { _view_half_h = h; }
double wall_X() const { return _transform.position.x; }
// ── facecam label ───────────────────────────────────────────────────
void set_Text (const std::string& text);
void set_Text (const std::string& text, SDL_FColor color, float depth, float scale);
void set_Centre (const Vec3& p);
void attach_To (const Vec3* anchor, const Vec3& offset = {});
void detach ();
void set_FaceCamera (bool on) { _facecam = on; }
void set_FaceYaw (float y) { _face_yaw = y; }
void set_Style (SDL_FColor color, float depth, float scale);
void set_Collidable(bool on);
void kick (size_t word_index, const Vec3& projectile_vel, const Vec3& normal);
bool view_Is (size_t tag) const { return tag != 0 && tag == _view_tag; }
void set_View (size_t tag) { _view_tag = tag; }
void set_Borrowed (const DynArray<TokenPtr>* borrowed) { _borrowed = borrowed; }
void clear_Borrowed() { _borrowed = nullptr; }
bool is_Empty () const { return _lines.empty() || _lines[0].tokens.empty(); }
const DynArray<TokenPtr>&
get_Words () const;
void draw (GlRenderer& gl, const Camera& cam, const Light& light) const { render(gl, cam, light); }
private:
DynArray<Line> _lines;
Wall::Role _role { Wall::Role::Editor };
Wall::Transform _transform{};
LayoutMode _layout { LayoutMode::Display };
FaceMode _face { FaceMode::Pitch };
float _depth { Token::depth_wall };
float _scale { 0.003f };
double _line_height { 0.0 };
double _word_spacing { 0.0 };
double _append_y { 0.0 };
SDL_FColor _append_color { 0.45f, 0.45f, 0.45f, 1.0f };
DynArray<std::string> _status;
const DynArray<TokenPtr>* _borrowed { nullptr };
size_t _view_tag { 0 }; // identity of currently-materialised info-wall content; 0 = none
bool _lex_enabled { true };
bool _show_numbers { false };
bool _both_columns { false };
bool _bordered { false };
bool _underline { false };
// lexing
bool _dirty { false };
uint64_t _gen { 0 };
std::string _last_src;
// Editing grid cache (vim layout + cursor)
double _char_w { 0.0 };
double _he_y { 0.0 };
double _code_left_z{ 0.0 };
double _row0_y { 0.0 };
double _eye_y { 0.0 };
mutable double _view_half_h { 0.0 }; // frustum half-height visible at the wall (set by Connection)
int _scroll_top { 0 }; // index of the row stuck to the top of the window
cpputils::unique_ptr<VimBuffer> _vim;
unsigned _vim_seen_version { 0 };
bool _capturing { false }; // keys are being captured (connected & editing)
enum class PositionMode : uint8_t { Absolute, Anchor };
const Vec3* _anchor_ptr { nullptr };
bool _facecam { true };
bool _collidable { true };
float _face_yaw { 0.0f };
Vec3 _attach_offset {};
Vec3 _centre {};
SDL_FColor _style_color { 1.0f, 1.0f, 0.0f, 1.0f };
std::string _label_text;
PositionMode _pos_mode { PositionMode::Absolute };
DynArray<double> _label_off;
void relayout_FaceCamera();
void facecam_Update (double dt, const Camera& cam);
Vec3 label_Centre () const;
// layout
void relayout ();
void relayout_Display();
void relayout_Editing ();
void sync_VimFromLines(); // editor: push _lines content back into the persistent buffer
// Display placement internals
struct Target {
enum Kind { Merge, Insert } kind;
size_t line_index;
double new_y;
double push_dist;
};
Target resolve_Target (double y) const;
Line& apply_Target (const Target& t);
void attach_Display (DynArray<TokenPtr>& block, const Vec3& impact);
void ensure_WordSeparators(Line& l);
void attach_Editing (DynArray<TokenPtr>& block, const Vec3& impact);
void maintain_Numbers(double dt, const Camera& cam);
void apply_Tilt (const Camera& cam);
bool impact_To_RowCol(const Vec3& impact, int& row, int& col) const;
};
} // namespace Text
} // namespace Nimbin