TUI shell + ftxui via FetchContent.

- ftxui v6.1.9 fetched at configure time; vendored .a + headers dropped.
- TUI: visualisation area on top, input prompt at the bottom; ↑/↓ history,
  Tab completion (commands + file paths), Esc cancels prompts. History is
  persisted (XDG on Linux, %LOCALAPPDATA% on Windows when ported).
- Command registry: `new`, `load`, `search`, `clear`, `help`, `quit`/`exit`.
  Inline params or interactive prompts; either way the canonical inline
  form is what gets stored in history.
- `search`: second full-screen mode with module + parts/signals menus and
  a live-filtered list; Tab cycles focus, Esc returns to the main shell.
- Domain: `System::modules()` accessor + `SystemElementContainer::size()`
  to support load summary + search.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 10:17:03 +02:00
parent 8b1de63849
commit 3395469810
40 changed files with 673 additions and 2674 deletions

86
CLAUDE.md Normal file
View File

@@ -0,0 +1,86 @@
# essim — notes for Claude
System digital twin: simulator for the interconnections between cards/boards. C++17, FTXUI for the TUI, importers for external netlist formats.
## Build
```sh
cmake -S . -B build
cmake --build build -j
./build/essim
```
- CMake **3.14+** required (uses `FetchContent_MakeAvailable`).
- FTXUI is fetched at configure time from GitHub (`v6.1.9`, shallow clone). First configure pays ~20 s for the clone; subsequent ones are cached in `build/_deps/`.
- Sources are collected with `file(GLOB_RECURSE ALL_SOURCES "src/*.cpp")`. **After adding a new `.cpp`, re-run `cmake -S . -B build`** — CMake does not re-glob automatically and link will fail with "undefined reference".
## Layout
```
src/
main.cpp -- launches Tui
system/ -- domain model
syselmts.hpp SystemElement + SystemElementContainer<T> (templated, get/merge/iterate)
modules.{hpp,cpp} Module, Modules
parts.{hpp,cpp} Part, Parts
pins.{hpp,cpp} Pin, Pins
signals.{hpp,cpp} Signal, Signals
connect.{hpp,cpp} Connection, Connections
transform.{hpp,cpp} transforms applied to the model
system.{hpp,cpp} System: owns Modules + Connections, exposes Load()
imports/ -- adapters that populate the domain
import_base.hpp ImportBase interface
import_mentor.{hpp,cpp} Mentor Graphics netlist parser (done)
tui/ -- FTXUI shell
tui.{hpp,cpp} Tui: visualisation area on top, input + history at the bottom
doc/classes.puml -- PlantUML class diagram
```
`include/` and `lib/` are kept empty by design — FTXUI used to live there as precompiled `.a` + headers, now it comes through FetchContent.
## Domain conventions
- Everything in `system/` is **pointer-based** (commit `d8122d1`: "everything is pointer"). Containers store `T*`, ownership lives with the container.
- `SystemElementContainer<T>::merge(name)` is the get-or-create primitive — call it instead of `add` when you don't know whether the element already exists. `add` throws on duplicate names or empty names.
- `using namespace std;` is present in `syselmts.hpp` — pre-existing, don't add more `using namespace` in headers.
- Include guards `_NAME_HPP_` *and* `#pragma once` are both used. Match the existing style.
## TUI
`Tui::Run()` enters an FTXUI fullscreen loop:
- Top: scrollable visualisation area (`window` + `vscroll_indicator | yframe`, last line gets `focus` so it auto-scrolls).
- Bottom: single-line `Input` inside a border. Label switches between `> ` (normal) and `<question>? ` (during a multi-step prompt).
- `CatchEvent` is wrapped **outside** the `Renderer` (not between `Renderer` and `Input`). Pattern: `Renderer(input_component, lambda)` then `CatchEvent(renderer, handler)`. Wrapping `CatchEvent(input, …)` inside a `Container::Vertical({…})` and then `Renderer(container, …)` was found to break the `Input`'s content rendering on at least one terminal — typed characters didn't show even though `on_enter` fired correctly.
- `InputOption::transform` is overridden to avoid hard-coded colors (default sets `White on Black` when focused, which is unreadable on light terminal themes). The custom transform only applies `dim` to the placeholder; everything else inherits terminal default fg/bg, so the UI adapts to any theme.
- `InputOption::multiline` **must** be set to `false` for the command prompt. Default is `true`, and FTXUI's `HandleReturn()` then both inserts `\n` into the content **and** fires `on_enter` — so `Submit()` would receive `"help\n"` instead of `"help"` and command lookups would all fall through to "unknown command".
- Commands live in a `std::map<std::string, CommandSpec>` registry built in `RegisterCommands()`. Each `CommandSpec` declares a list of `Param{name, path_completion}` and an `action(args)` lambda. `Dispatch(raw)` tokenises the input (whitespace split with `"…"` quoting), takes inline args first, and pushes a `Prompt` onto the queue for each missing param. The last prompt's callback calls `Finalize()`.
- `Finalize()` rebuilds the **canonical inline form** (`name arg1 "arg with spaces" arg3`) and writes that to history — so a `load` command answered via interactive prompts still shows up in `history` (and on disk) as a single inline line, ready to be replayed via ↑.
- Multi-step prompts work via a `std::deque<Prompt>` queue. `Submit()` pops them one by one before falling back to dispatch. Adding a new command = one entry in `RegisterCommands()`; the prompt-flow and inline-flow are both handled automatically.
- Tab completion: at the top-level prompt (no `pending`), completes built-in command names. Inside a prompt with `path_completion = true` (e.g. the `filename` step of `load`), completes file paths via `std::filesystem::directory_iterator` (handles `~/`, dirs get a trailing `/`). Logic: 1 match → replace; multiple with progress on the longest common prefix → extend; multiple stuck at LCP → list candidates in the visualisation area.
Built-in commands: `new`, `load`, `search`, `clear`, `help`, `quit`/`exit`. `Esc` cancels an in-progress multi-step prompt.
`search` switches to a second full-screen layout (handled by `Container::Tab({main, search}, &screen_idx)`). Layout:
- Left column: `Menu` for the module list and `Menu` for the type (`parts` / `signals`).
- Right column: `Input` for the live filter query, plus a results panel rebuilt every frame.
- `Tab` cycles focus between the query input and the menus. **Implemented manually** in the outer `CatchEvent`: `Menu::OnEvent` consumes `Event::Tab` to cycle its own entries and returns `true`, which prevents `Container::Vertical` from ever seeing the event (Container only cycles between children when the active child returns `false`). So we short-circuit Tab/TabReverse upstream and mutate `search_focus_idx` directly.
- `Esc` exits the search mode (flips `screen_idx` back to 0). The search state (selected module/type, query) is preserved across re-entries until `search` is run again.
Adding a new screen mode = add a child to `Container::Tab` and a `screen_idx` value; key handling already lives in the outer `CatchEvent`.
Command history is persisted on disk and loaded on startup. Path resolution is platform-aware:
- Linux/macOS: `$XDG_DATA_HOME/essim/history`, falling back to `~/.local/share/essim/history`.
- Windows: `%LOCALAPPDATA%\essim\history`, falling back to `%APPDATA%\…` then `%USERPROFILE%\AppData\Local\…`.
Each successful submission appends a single line to the file (so a crash doesn't lose history). Multi-step prompt answers are NOT persisted — only top-level commands.
## Gotchas
- `System::Load` for `IMPORT_ALTIUM` / `IMPORT_ODS`: the corresponding constructor lines are commented out, so `imp` stays uninitialised → UB on `imp->parse(...)`. Only `IMPORT_MENTOR` is safe today. Wrap calls in `try/catch` (the TUI does).
- `System::Load` throws `std::runtime_error("Unknown import type")` for any value outside the three enum cases.
- `Modules`/`Parts`/etc. have no const-correct iteration on the `*` accessor; iterators on `SystemElementContainer<T>` are available but `begin()`/`end()` are non-const-safe in some places.
## Memory layout for sessions
Persistent notes live in `~/.claude/projects/-home-francois-Projets-essim/memory/`. Index: `MEMORY.md`. Add project/feedback/user/reference entries there when relevant — see top-level Claude Code memory rules.

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -9,20 +9,29 @@ project(essim
DESCRIPTION "System digital twin."
)
set(DIR_LIBS ${CMAKE_SOURCE_DIR}/libs)
include(FetchContent)
set(FTXUI_BUILD_DOCS OFF CACHE INTERNAL "")
set(FTXUI_BUILD_EXAMPLES OFF CACHE INTERNAL "")
set(FTXUI_BUILD_TESTS OFF CACHE INTERNAL "")
set(FTXUI_ENABLE_INSTALL OFF CACHE INTERNAL "")
FetchContent_Declare(ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/FTXUI.git
GIT_TAG v6.1.9
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(ftxui)
file(GLOB_RECURSE ALL_SOURCES "src/*.cpp")
file(GLOB_RECURSE LIB_FTXUI "lib/ftxui/*.a")
add_executable(essim ${ALL_SOURCES})
target_include_directories(essim PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_executable(
essim
${ALL_SOURCES}
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(
essim PRIVATE ${LIB_FTXUI}
target_link_libraries(essim
PRIVATE
ftxui::screen
ftxui::dom
ftxui::component
)

View File

@@ -1,118 +0,0 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_ANIMATION_HPP
#define FTXUI_ANIMATION_HPP
#include <chrono> // for milliseconds, duration, steady_clock, time_point
#include <functional> // for function
#include "ftxui/component/event.hpp"
namespace ftxui {
namespace animation {
// Components who haven't completed their animation can call this function to
// request a new frame to be drawn later.
//
// When there is no new events and no animations to complete, no new frame is
// drawn.
void RequestAnimationFrame();
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Duration = std::chrono::duration<float>;
// Parameter of Component::OnAnimation(param).
class Params {
public:
Params(Duration duration) : duration_(duration) {}
/// The duration this animation step represents.
Duration duration() const { return duration_; }
private:
Duration duration_;
};
namespace easing {
using Function = std::function<float(float)>;
// Linear interpolation (no easing)
float Linear(float p);
// Quadratic easing; p^2
float QuadraticIn(float p);
float QuadraticOut(float p);
float QuadraticInOut(float p);
// Cubic easing; p^3
float CubicIn(float p);
float CubicOut(float p);
float CubicInOut(float p);
// Quartic easing; p^4
float QuarticIn(float p);
float QuarticOut(float p);
float QuarticInOut(float p);
// Quintic easing; p^5
float QuinticIn(float p);
float QuinticOut(float p);
float QuinticInOut(float p);
// Sine wave easing; sin(p * PI/2)
float SineIn(float p);
float SineOut(float p);
float SineInOut(float p);
// Circular easing; sqrt(1 - p^2)
float CircularIn(float p);
float CircularOut(float p);
float CircularInOut(float p);
// Exponential easing, base 2
float ExponentialIn(float p);
float ExponentialOut(float p);
float ExponentialInOut(float p);
// Exponentially-damped sine wave easing
float ElasticIn(float p);
float ElasticOut(float p);
float ElasticInOut(float p);
// Overshooting cubic easing;
float BackIn(float p);
float BackOut(float p);
float BackInOut(float p);
// Exponentially-decaying bounce easing
float BounceIn(float p);
float BounceOut(float p);
float BounceInOut(float p);
} // namespace easing
class Animator {
public:
Animator(float* from,
float to = 0.f,
Duration duration = std::chrono::milliseconds(250),
easing::Function easing_function = easing::Linear,
Duration delay = std::chrono::milliseconds(0));
void OnAnimation(Params&);
float to() const { return to_; }
private:
float* value_;
float from_;
float to_;
Duration duration_;
easing::Function easing_function_;
Duration current_;
};
} // namespace animation
} // namespace ftxui
#endif /* end of include guard: FTXUI_ANIMATION_HPP */

View File

@@ -1,17 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_CAPTURED_MOUSE_HPP
#define FTXUI_CAPTURED_MOUSE_HPP
#include <memory>
namespace ftxui {
class CapturedMouseInterface {
public:
virtual ~CapturedMouseInterface() = default;
};
using CapturedMouse = std::unique_ptr<CapturedMouseInterface>;
} // namespace ftxui
#endif /* end of include guard: FTXUI_CAPTURED_MOUSE_HPP */

View File

@@ -1,142 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_HPP
#define FTXUI_COMPONENT_HPP
#include <functional> // for function
#include <memory> // for make_shared, shared_ptr
#include <string> // for wstring
#include <utility> // for forward
#include <vector> // for vector
#include "ftxui/component/component_base.hpp" // for Component, Components
#include "ftxui/component/component_options.hpp" // for ButtonOption, CheckboxOption, MenuOption
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/util/ref.hpp" // for ConstRef, Ref, ConstStringRef, ConstStringListRef, StringRef
namespace ftxui {
struct ButtonOption;
struct CheckboxOption;
struct Event;
struct InputOption;
struct MenuOption;
struct RadioboxOption;
struct MenuEntryOption;
template <class T, class... Args>
std::shared_ptr<T> Make(Args&&... args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
// Pipe operator to decorate components.
using ComponentDecorator = std::function<Component(Component)>;
using ElementDecorator = std::function<Element(Element)>;
Component operator|(Component component, ComponentDecorator decorator);
Component operator|(Component component, ElementDecorator decorator);
Component& operator|=(Component& component, ComponentDecorator decorator);
Component& operator|=(Component& component, ElementDecorator decorator);
namespace Container {
Component Vertical(Components children);
Component Vertical(Components children, int* selector);
Component Horizontal(Components children);
Component Horizontal(Components children, int* selector);
Component Tab(Components children, int* selector);
Component Stacked(Components children);
} // namespace Container
Component Button(ButtonOption options);
Component Button(ConstStringRef label,
std::function<void()> on_click,
ButtonOption options = ButtonOption::Simple());
Component Checkbox(CheckboxOption options);
Component Checkbox(ConstStringRef label,
bool* checked,
CheckboxOption options = CheckboxOption::Simple());
Component Input(InputOption options = {});
Component Input(StringRef content, InputOption options = {});
Component Input(StringRef content,
StringRef placeholder,
InputOption options = {});
Component Menu(MenuOption options);
Component Menu(ConstStringListRef entries,
int* selected_,
MenuOption options = MenuOption::Vertical());
Component MenuEntry(MenuEntryOption options);
Component MenuEntry(ConstStringRef label, MenuEntryOption options = {});
Component Radiobox(RadioboxOption options);
Component Radiobox(ConstStringListRef entries,
int* selected_,
RadioboxOption options = {});
Component Dropdown(ConstStringListRef entries, int* selected);
Component Toggle(ConstStringListRef entries, int* selected);
// General slider constructor:
template <typename T>
Component Slider(SliderOption<T> options);
// Shorthand without the `SliderOption` constructor:
Component Slider(ConstStringRef label,
Ref<int> value,
ConstRef<int> min = 0,
ConstRef<int> max = 100,
ConstRef<int> increment = 5);
Component Slider(ConstStringRef label,
Ref<float> value,
ConstRef<float> min = 0.f,
ConstRef<float> max = 100.f,
ConstRef<float> increment = 5.f);
Component Slider(ConstStringRef label,
Ref<long> value,
ConstRef<long> min = 0l,
ConstRef<long> max = 100l,
ConstRef<long> increment = 5l);
Component ResizableSplit(ResizableSplitOption options);
Component ResizableSplitLeft(Component main, Component back, int* main_size);
Component ResizableSplitRight(Component main, Component back, int* main_size);
Component ResizableSplitTop(Component main, Component back, int* main_size);
Component ResizableSplitBottom(Component main, Component back, int* main_size);
Component Renderer(Component child, std::function<Element()>);
Component Renderer(std::function<Element()>);
Component Renderer(std::function<Element(bool /* focused */)>);
ComponentDecorator Renderer(ElementDecorator);
Component CatchEvent(Component child, std::function<bool(Event)>);
ComponentDecorator CatchEvent(std::function<bool(Event)> on_event);
Component Maybe(Component, const bool* show);
Component Maybe(Component, std::function<bool()>);
ComponentDecorator Maybe(const bool* show);
ComponentDecorator Maybe(std::function<bool()>);
Component Modal(Component main, Component modal, const bool* show_modal);
ComponentDecorator Modal(Component modal, const bool* show_modal);
Component Collapsible(ConstStringRef label,
Component child,
Ref<bool> show = false);
Component Hoverable(Component component, bool* hover);
Component Hoverable(Component component,
std::function<void()> on_enter,
std::function<void()> on_leave);
Component Hoverable(Component component, //
std::function<void(bool)> on_change);
ComponentDecorator Hoverable(bool* hover);
ComponentDecorator Hoverable(std::function<void()> on_enter,
std::function<void()> on_leave);
ComponentDecorator Hoverable(std::function<void(bool)> on_change);
Component Window(WindowOptions option);
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_HPP */

View File

@@ -1,98 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_BASE_HPP
#define FTXUI_COMPONENT_BASE_HPP
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CaptureMouse
#include "ftxui/dom/elements.hpp" // for Element
namespace ftxui {
class Delegate;
class Focus;
struct Event;
namespace animation {
class Params;
} // namespace animation
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
using Components = std::vector<Component>;
/// @brief It implement rendering itself as ftxui::Element. It implement
/// keyboard navigation by responding to ftxui::Event.
/// @ingroup component
class ComponentBase {
public:
// virtual Destructor.
virtual ~ComponentBase();
ComponentBase() = default;
// A component is not copiable.
ComponentBase(const ComponentBase&) = delete;
void operator=(const ComponentBase&) = delete;
// Component hierarchy:
ComponentBase* Parent() const;
Component& ChildAt(size_t i);
size_t ChildCount() const;
void Add(Component children);
void Detach();
void DetachAllChildren();
// Renders the component.
virtual Element Render();
// Handles an event.
// By default, reduce on children with a lazy OR.
//
// Returns whether the event was handled or not.
virtual bool OnEvent(Event);
// Handle an animation step.
virtual void OnAnimation(animation::Params& params);
// Focus management ----------------------------------------------------------
//
// If this component contains children, this indicates which one is active,
// nullptr if none is active.
//
// We say an element has the focus if the chain of ActiveChild() from the
// root component contains this object.
virtual Component ActiveChild();
// Return true when the component contains focusable elements.
// The non focusable Component will be skipped when navigating using the
// keyboard.
virtual bool Focusable() const;
// Whether this is the active child of its parent.
bool Active() const;
// Whether all the ancestors are active.
bool Focused() const;
// Make the |child| to be the "active" one.
virtual void SetActiveChild(ComponentBase* child);
void SetActiveChild(Component child);
// Configure all the ancestors to give focus to this component.
void TakeFocus();
protected:
CapturedMouse CaptureMouse(const Event& event);
Components children_;
private:
ComponentBase* parent_ = nullptr;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_BASE_HPP */

View File

@@ -1,267 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#define FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP
#include <chrono> // for milliseconds
#include <ftxui/component/animation.hpp> // for Duration, QuadraticInOut, Function
#include <ftxui/dom/direction.hpp> // for Direction, Direction::Left, Direction::Right, Direction::Down
#include <ftxui/dom/elements.hpp> // for Element, separator
#include <ftxui/util/ref.hpp> // for Ref, ConstRef, StringRef
#include <functional> // for function
#include <optional> // for optional
#include <string> // for string
#include "ftxui/component/component_base.hpp" // for Component
#include "ftxui/screen/color.hpp" // for Color, Color::GrayDark, Color::White
namespace ftxui {
/// @brief arguments for |ButtonOption::transform|, |CheckboxOption::transform|,
/// |Radiobox::transform|, |MenuEntryOption::transform|,
/// |MenuOption::transform|.
struct EntryState {
std::string label; /// < The label to display.
bool state; /// < The state of the button/checkbox/radiobox
bool active; /// < Whether the entry is the active one.
bool focused; /// < Whether the entry is one focused by the user.
};
struct UnderlineOption {
bool enabled = false;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
animation::easing::Function leader_function =
animation::easing::QuadraticInOut;
animation::easing::Function follower_function =
animation::easing::QuadraticInOut;
animation::Duration leader_duration = std::chrono::milliseconds(250);
animation::Duration leader_delay = std::chrono::milliseconds(0);
animation::Duration follower_duration = std::chrono::milliseconds(250);
animation::Duration follower_delay = std::chrono::milliseconds(0);
void SetAnimation(animation::Duration d, animation::easing::Function f);
void SetAnimationDuration(animation::Duration d);
void SetAnimationFunction(animation::easing::Function f);
void SetAnimationFunction(animation::easing::Function f_leader,
animation::easing::Function f_follower);
};
/// @brief Option about a potentially animated color.
/// @ingroup component
struct AnimatedColorOption {
void Set(
Color inactive,
Color active,
animation::Duration duration = std::chrono::milliseconds(250),
animation::easing::Function function = animation::easing::QuadraticInOut);
bool enabled = false;
Color inactive;
Color active;
animation::Duration duration = std::chrono::milliseconds(250);
animation::easing::Function function = animation::easing::QuadraticInOut;
};
struct AnimatedColorsOption {
AnimatedColorOption background;
AnimatedColorOption foreground;
};
/// @brief Option for the MenuEntry component.
/// @ingroup component
struct MenuEntryOption {
ConstStringRef label = "MenuEntry";
std::function<Element(const EntryState& state)> transform;
AnimatedColorsOption animated_colors;
};
/// @brief Option for the Menu component.
/// @ingroup component
struct MenuOption {
// Standard constructors:
static MenuOption Horizontal();
static MenuOption HorizontalAnimated();
static MenuOption Vertical();
static MenuOption VerticalAnimated();
static MenuOption Toggle();
ConstStringListRef entries; ///> The list of entries.
Ref<int> selected = 0; ///> The index of the selected entry.
// Style:
UnderlineOption underline;
MenuEntryOption entries_option;
Direction direction = Direction::Down;
std::function<Element()> elements_prefix;
std::function<Element()> elements_infix;
std::function<Element()> elements_postfix;
// Observers:
std::function<void()> on_change; ///> Called when the selected entry changes.
std::function<void()> on_enter; ///> Called when the user presses enter.
Ref<int> focused_entry = 0;
};
/// @brief Option for the AnimatedButton component.
/// @ingroup component
struct ButtonOption {
// Standard constructors:
static ButtonOption Ascii();
static ButtonOption Simple();
static ButtonOption Border();
static ButtonOption Animated();
static ButtonOption Animated(Color color);
static ButtonOption Animated(Color background, Color foreground);
static ButtonOption Animated(Color background,
Color foreground,
Color background_active,
Color foreground_active);
ConstStringRef label = "Button";
std::function<void()> on_click = [] {};
// Style:
std::function<Element(const EntryState&)> transform;
AnimatedColorsOption animated_colors;
};
/// @brief Option for the Checkbox component.
/// @ingroup component
struct CheckboxOption {
// Standard constructors:
static CheckboxOption Simple();
ConstStringRef label = "Checkbox";
Ref<bool> checked = false;
// Style:
std::function<Element(const EntryState&)> transform;
// Observer:
/// Called when the user change the state.
std::function<void()> on_change = [] {};
};
/// @brief Used to define style for the Input component.
struct InputState {
Element element;
bool hovered; /// < Whether the input is hovered by the mouse.
bool focused; /// < Whether the input is focused by the user.
bool is_placeholder; /// < Whether the input is empty and displaying the
/// < placeholder.
};
/// @brief Option for the Input component.
/// @ingroup component
struct InputOption {
// A set of predefined styles:
/// @brief Create the default input style:
static InputOption Default();
/// @brief A white on black style with high margins:
static InputOption Spacious();
/// The content of the input.
StringRef content = "";
/// The content of the input when it's empty.
StringRef placeholder = "";
// Style:
std::function<Element(InputState)> transform;
Ref<bool> password = false; /// < Obscure the input content using '*'.
Ref<bool> multiline = true; /// < Whether the input can be multiline.
/// Called when the content changes.
std::function<void()> on_change = [] {};
/// Called when the user presses enter.
std::function<void()> on_enter = [] {};
// The char position of the cursor:
Ref<int> cursor_position = 0;
};
/// @brief Option for the Radiobox component.
/// @ingroup component
struct RadioboxOption {
// Standard constructors:
static RadioboxOption Simple();
// Content:
ConstStringListRef entries;
Ref<int> selected = 0;
// Style:
std::function<Element(const EntryState&)> transform;
// Observers:
/// Called when the selected entry changes.
std::function<void()> on_change = [] {};
Ref<int> focused_entry = 0;
};
struct ResizableSplitOption {
Component main;
Component back;
Ref<Direction> direction = Direction::Left;
Ref<int> main_size =
(direction() == Direction::Left || direction() == Direction::Right) ? 20
: 10;
std::function<Element()> separator_func = [] { return ::ftxui::separator(); };
};
// @brief Option for the `Slider` component.
// @ingroup component
template <typename T>
struct SliderOption {
Ref<T> value;
ConstRef<T> min = T(0);
ConstRef<T> max = T(100);
ConstRef<T> increment = (max() - min()) / 20;
Direction direction = Direction::Right;
Color color_active = Color::White;
Color color_inactive = Color::GrayDark;
};
// Parameter pack used by `WindowOptions::render`.
struct WindowRenderState {
Element inner; /// < The element wrapped inside this window.
const std::string& title; /// < The title of the window.
bool active = false; /// < Whether the window is the active one.
bool drag = false; /// < Whether the window is being dragged.
bool resize = false; /// < Whether the window is being resized.
bool hover_left = false; /// < Whether the resizeable left side is hovered.
bool hover_right = false; /// < Whether the resizeable right side is hovered.
bool hover_top = false; /// < Whether the resizeable top side is hovered.
bool hover_down = false; /// < Whether the resizeable down side is hovered.
};
// @brief Option for the `Window` component.
// @ingroup component
struct WindowOptions {
Component inner; /// < The component wrapped by this window.
ConstStringRef title = ""; /// < The title displayed by this window.
Ref<int> left = 0; /// < The left side position of the window.
Ref<int> top = 0; /// < The top side position of the window.
Ref<int> width = 20; /// < The width of the window.
Ref<int> height = 10; /// < The height of the window.
Ref<bool> resize_left = true; /// < Can the left side be resized?
Ref<bool> resize_right = true; /// < Can the right side be resized?
Ref<bool> resize_top = true; /// < Can the top side be resized?
Ref<bool> resize_down = true; /// < Can the down side be resized?
/// An optional function to customize how the window looks like:
std::function<Element(const WindowRenderState&)> render;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_COMPONENT_OPTIONS_HPP */

View File

@@ -1,112 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_EVENT_HPP
#define FTXUI_COMPONENT_EVENT_HPP
#include <ftxui/component/mouse.hpp> // for Mouse
#include <functional>
#include <string> // for string, operator==
#include <vector>
namespace ftxui {
class ScreenInteractive;
class ComponentBase;
/// @brief Represent an event. It can be key press event, a terminal resize, or
/// more ...
///
/// For example:
/// - Printable character can be created using Event::Character('a').
/// - Some special are predefined, like Event::ArrowLeft.
/// - One can find arbitrary code for special Events using:
/// ./example/util/print_key_press
/// For instance, CTLR+A maps to Event::Special({1});
///
/// Useful documentation about xterm specification:
/// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
struct Event {
// --- Constructor section ---------------------------------------------------
static Event Character(std::string);
static Event Character(char);
static Event Character(wchar_t);
static Event Special(std::string);
static Event Mouse(std::string, Mouse mouse);
static Event CursorReporting(std::string, int x, int y);
// --- Arrow ---
static const Event ArrowLeft;
static const Event ArrowRight;
static const Event ArrowUp;
static const Event ArrowDown;
static const Event ArrowLeftCtrl;
static const Event ArrowRightCtrl;
static const Event ArrowUpCtrl;
static const Event ArrowDownCtrl;
// --- Other ---
static const Event Backspace;
static const Event Delete;
static const Event Return;
static const Event Escape;
static const Event Tab;
static const Event TabReverse;
static const Event F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12;
static const Event Home;
static const Event End;
static const Event PageUp;
static const Event PageDown;
// --- Custom ---
static const Event Custom;
//--- Method section ---------------------------------------------------------
bool is_character() const { return type_ == Type::Character; }
std::string character() const { return input_; }
bool is_mouse() const { return type_ == Type::Mouse; }
struct Mouse& mouse() { return data_.mouse; }
bool is_cursor_reporting() const { return type_ == Type::CursorReporting; }
int cursor_x() const { return data_.cursor.x; }
int cursor_y() const { return data_.cursor.y; }
const std::string& input() const { return input_; }
bool operator==(const Event& other) const { return input_ == other.input_; }
bool operator!=(const Event& other) const { return !operator==(other); }
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
private:
friend ComponentBase;
friend ScreenInteractive;
enum class Type {
Unknown,
Character,
Mouse,
CursorReporting,
};
Type type_ = Type::Unknown;
struct Cursor {
int x = 0;
int y = 0;
};
union {
struct Mouse mouse;
struct Cursor cursor;
} data_ = {};
std::string input_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_EVENT_HPP */

View File

@@ -1,38 +0,0 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_LOOP_HPP
#define FTXUI_COMPONENT_LOOP_HPP
#include <memory> // for shared_ptr
#include "ftxui/component/component_base.hpp" // for ComponentBase
namespace ftxui {
class ComponentBase;
using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractive;
class Loop {
public:
Loop(ScreenInteractive* screen, Component component);
~Loop();
bool HasQuitted();
void RunOnce();
void RunOnceBlocking();
void Run();
private:
// This class is non copyable.
Loop(const ScreenInteractive&) = delete;
Loop& operator=(const Loop&) = delete;
ScreenInteractive* screen_;
Component component_;
};
} // namespace ftxui
#endif // FTXUI_COMPONENT_LOOP_HPP

View File

@@ -1,44 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_MOUSE_HPP
#define FTXUI_COMPONENT_MOUSE_HPP
namespace ftxui {
/// @brief A mouse event. It contains the coordinate of the mouse, the button
/// pressed and the modifier (shift, ctrl, meta).
/// @ingroup component
struct Mouse {
enum Button {
Left = 0,
Middle = 1,
Right = 2,
None = 3,
WheelUp = 4,
WheelDown = 5,
};
enum Motion {
Released = 0,
Pressed = 1,
};
// Button
Button button = Button::None;
// Motion
Motion motion = Motion::Pressed;
// Modifiers:
bool shift = false;
bool meta = false;
bool control = false;
// Coordinates:
int x = 0;
int y = 0;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_MOUSE_HPP */

View File

@@ -1,140 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_RECEIVER_HPP_
#define FTXUI_COMPONENT_RECEIVER_HPP_
#include <algorithm> // for copy, max
#include <atomic> // for atomic, __atomic_base
#include <condition_variable> // for condition_variable
#include <functional>
#include <iostream>
#include <memory> // for unique_ptr, make_unique
#include <mutex> // for mutex, unique_lock
#include <queue> // for queue
#include <utility> // for move
namespace ftxui {
// Usage:
//
// Initialization:
// ---------------
//
// auto receiver = MakeReceiver<std:string>();
// auto sender_1= receiver->MakeSender();
// auto sender_2 = receiver->MakeSender();
//
// Then move the senders elsewhere, potentially in a different thread.
//
// On the producer side:
// ----------------------
// [thread 1] sender_1->Send("hello");
// [thread 2] sender_2->Send("world");
//
// On the consumer side:
// ---------------------
// char c;
// while(receiver->Receive(&c)) // Return true as long as there is a producer.
// print(c)
//
// Receiver::Receive() returns true when there are no more senders.
// clang-format off
template<class T> class SenderImpl;
template<class T> class ReceiverImpl;
template<class T> using Sender = std::unique_ptr<SenderImpl<T>>;
template<class T> using Receiver = std::unique_ptr<ReceiverImpl<T>>;
template<class T> Receiver<T> MakeReceiver();
// clang-format on
// ---- Implementation part ----
template <class T>
class SenderImpl {
public:
void Send(T t) { receiver_->Receive(std::move(t)); }
~SenderImpl() { receiver_->ReleaseSender(); }
Sender<T> Clone() { return receiver_->MakeSender(); }
private:
friend class ReceiverImpl<T>;
SenderImpl(ReceiverImpl<T>* consumer) : receiver_(consumer) {}
ReceiverImpl<T>* receiver_;
};
template <class T>
class ReceiverImpl {
public:
Sender<T> MakeSender() {
std::unique_lock<std::mutex> lock(mutex_);
senders_++;
return std::unique_ptr<SenderImpl<T>>(new SenderImpl<T>(this));
}
ReceiverImpl() { senders_ = 0; }
bool Receive(T* t) {
while (senders_ || !queue_.empty()) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
notifier_.wait(lock);
if (queue_.empty())
continue;
*t = std::move(queue_.front());
queue_.pop();
return true;
}
return false;
}
bool ReceiveNonBlocking(T* t) {
std::unique_lock<std::mutex> lock(mutex_);
if (queue_.empty())
return false;
*t = queue_.front();
queue_.pop();
return true;
}
bool HasPending() {
std::unique_lock<std::mutex> lock(mutex_);
return !queue_.empty();
}
bool HasQuitted() {
std::unique_lock<std::mutex> lock(mutex_);
return queue_.empty() && !senders_;
}
private:
friend class SenderImpl<T>;
void Receive(T t) {
{
std::unique_lock<std::mutex> lock(mutex_);
queue_.push(std::move(t));
}
notifier_.notify_one();
}
void ReleaseSender() {
senders_--;
notifier_.notify_one();
}
std::mutex mutex_;
std::queue<T> queue_;
std::condition_variable notifier_;
std::atomic<int> senders_;
};
template <class T>
Receiver<T> MakeReceiver() {
return std::make_unique<ReceiverImpl<T>>();
}
} // namespace ftxui
#endif // FTXUI_COMPONENT_RECEIVER_HPP_

View File

@@ -1,127 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#define FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP
#include <atomic> // for atomic
#include <ftxui/component/receiver.hpp> // for Receiver, Sender
#include <functional> // for function
#include <memory> // for shared_ptr
#include <string> // for string
#include <thread> // for thread
#include <variant> // for variant
#include "ftxui/component/animation.hpp" // for TimePoint
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/task.hpp" // for Task, Closure
#include "ftxui/screen/screen.hpp" // for Screen
namespace ftxui {
class ComponentBase;
class Loop;
struct Event;
using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractivePrivate;
class ScreenInteractive : public Screen {
public:
// Constructors:
static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen();
static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput();
// Options. Must be called before Loop().
void TrackMouse(bool enable = true);
// Return the currently active screen, nullptr if none.
static ScreenInteractive* Active();
// Start/Stop the main loop.
void Loop(Component);
void Exit();
Closure ExitLoopClosure();
// Post tasks to be executed by the loop.
void Post(Task task);
void PostEvent(Event event);
void RequestAnimationFrame();
CapturedMouse CaptureMouse();
// Decorate a function. The outputted one will execute similarly to the
// inputted one, but with the currently active screen terminal hooks
// temporarily uninstalled.
Closure WithRestoredIO(Closure);
private:
void ExitNow();
void Install();
void Uninstall();
void PreMain();
void PostMain();
bool HasQuitted();
void RunOnce(Component component);
void RunOnceBlocking(Component component);
void HandleTask(Component component, Task& task);
void Draw(Component component);
void ResetCursorPosition();
void Signal(int signal);
ScreenInteractive* suspended_screen_ = nullptr;
enum class Dimension {
FitComponent,
Fixed,
Fullscreen,
TerminalOutput,
};
Dimension dimension_ = Dimension::Fixed;
bool use_alternative_screen_ = false;
ScreenInteractive(int dimx,
int dimy,
Dimension dimension,
bool use_alternative_screen);
bool track_mouse_ = true;
Sender<Task> task_sender_;
Receiver<Task> task_receiver_;
std::string set_cursor_position;
std::string reset_cursor_position;
std::atomic<bool> quit_ = false;
std::thread event_listener_;
std::thread animation_listener_;
bool animation_requested_ = false;
animation::TimePoint previous_animation_time_;
int cursor_x_ = 1;
int cursor_y_ = 1;
bool mouse_captured = false;
bool previous_frame_resized_ = false;
bool frame_valid_ = false;
friend class Loop;
public:
class Private {
public:
static void Signal(ScreenInteractive& s, int signal) { s.Signal(signal); }
};
friend Private;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_COMPONENT_SCREEN_INTERACTIVE_HPP */

View File

@@ -1,17 +0,0 @@
// Copyright 2022 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_COMPONENT_ANIMATION_HPP
#define FTXUI_COMPONENT_ANIMATION_HPP
#include <functional>
#include <variant>
#include "ftxui/component/event.hpp"
namespace ftxui {
class AnimationTask {};
using Closure = std::function<void()>;
using Task = std::variant<Event, Closure, AnimationTask>;
} // namespace ftxui
#endif // FTXUI_COMPONENT_ANIMATION_HPP

View File

@@ -1,138 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_CANVAS_HPP
#define FTXUI_DOM_CANVAS_HPP
#include <cstddef> // for size_t
#include <functional> // for function
#include <string> // for string
#include <unordered_map> // for unordered_map
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/screen.hpp" // for Pixel
#ifdef DrawText
// Workaround for WinUsr.h (via Windows.h) defining macros that break things.
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-drawtext
#undef DrawText
#endif
namespace ftxui {
struct Canvas {
public:
Canvas() = default;
Canvas(int width, int height);
// Getters:
int width() const { return width_; }
int height() const { return height_; }
Pixel GetPixel(int x, int y) const;
using Stylizer = std::function<void(Pixel&)>;
// Draws using braille characters --------------------------------------------
void DrawPointOn(int x, int y);
void DrawPointOff(int x, int y);
void DrawPointToggle(int x, int y);
void DrawPoint(int x, int y, bool value);
void DrawPoint(int x, int y, bool value, const Stylizer& s);
void DrawPoint(int x, int y, bool value, const Color& color);
void DrawPointLine(int x1, int y1, int x2, int y2);
void DrawPointLine(int x1, int y1, int x2, int y2, const Stylizer& s);
void DrawPointLine(int x1, int y1, int x2, int y2, const Color& color);
void DrawPointCircle(int x, int y, int radius);
void DrawPointCircle(int x, int y, int radius, const Stylizer& s);
void DrawPointCircle(int x, int y, int radius, const Color& color);
void DrawPointCircleFilled(int x, int y, int radius);
void DrawPointCircleFilled(int x, int y, int radius, const Stylizer& s);
void DrawPointCircleFilled(int x, int y, int radius, const Color& color);
void DrawPointEllipse(int x, int y, int r1, int r2);
void DrawPointEllipse(int x, int y, int r1, int r2, const Color& color);
void DrawPointEllipse(int x, int y, int r1, int r2, const Stylizer& s);
void DrawPointEllipseFilled(int x, int y, int r1, int r2);
void DrawPointEllipseFilled(int x, int y, int r1, int r2, const Color& color);
void DrawPointEllipseFilled(int x, int y, int r1, int r2, const Stylizer& s);
// Draw using box characters -------------------------------------------------
// Block are of size 1x2. y is considered to be a multiple of 2.
void DrawBlockOn(int x, int y);
void DrawBlockOff(int x, int y);
void DrawBlockToggle(int x, int y);
void DrawBlock(int x, int y, bool value);
void DrawBlock(int x, int y, bool value, const Stylizer& s);
void DrawBlock(int x, int y, bool value, const Color& color);
void DrawBlockLine(int x1, int y1, int x2, int y2);
void DrawBlockLine(int x1, int y1, int x2, int y2, const Stylizer& s);
void DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color);
void DrawBlockCircle(int x1, int y1, int radius);
void DrawBlockCircle(int x1, int y1, int radius, const Stylizer& s);
void DrawBlockCircle(int x1, int y1, int radius, const Color& color);
void DrawBlockCircleFilled(int x1, int y1, int radius);
void DrawBlockCircleFilled(int x1, int y1, int radius, const Stylizer& s);
void DrawBlockCircleFilled(int x1, int y1, int radius, const Color& color);
void DrawBlockEllipse(int x1, int y1, int r1, int r2);
void DrawBlockEllipse(int x1, int y1, int r1, int r2, const Stylizer& s);
void DrawBlockEllipse(int x1, int y1, int r1, int r2, const Color& color);
void DrawBlockEllipseFilled(int x1, int y1, int r1, int r2);
void DrawBlockEllipseFilled(int x1,
int y1,
int r1,
int r2,
const Stylizer& s);
void DrawBlockEllipseFilled(int x1,
int y1,
int r1,
int r2,
const Color& color);
// Draw using normal characters ----------------------------------------------
// Draw using character of size 2x4 at position (x,y)
// x is considered to be a multiple of 2.
// y is considered to be a multiple of 4.
void DrawText(int x, int y, const std::string& value);
void DrawText(int x, int y, const std::string& value, const Color& color);
void DrawText(int x, int y, const std::string& value, const Stylizer& style);
// Decorator:
// x is considered to be a multiple of 2.
// y is considered to be a multiple of 4.
void Style(int x, int y, const Stylizer& style);
private:
bool IsIn(int x, int y) const {
return x >= 0 && x < width_ && y >= 0 && y < height_;
}
enum CellType {
kBraille,
kBlock,
kText,
};
struct Cell {
CellType type = kText;
Pixel content;
};
struct XY {
int x;
int y;
bool operator==(const XY& other) const {
return x == other.x && y == other.y;
}
};
struct XYHash {
size_t operator()(const XY& xy) const {
constexpr size_t shift = 1024;
return size_t(xy.x) * shift + size_t(xy.y);
}
};
int width_ = 0;
int height_ = 0;
std::unordered_map<XY, Cell, XYHash> storage_;
};
} // namespace ftxui
#endif // FTXUI_DOM_CANVAS_HPP

View File

@@ -1,15 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_DEPRECATED_HPP
#define FTXUI_DOM_DEPRECATED_HPP
#include "ftxui/dom/elements.hpp"
namespace ftxui {
Element text(std::wstring text);
Element vtext(std::wstring text);
Elements paragraph(std::wstring text);
} // namespace ftxui
#endif // FTXUI_DOM_DEPRECATED_HPP

View File

@@ -1,17 +0,0 @@
// Copyright 2023 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_DIRECTION_HPP
#define FTXUI_DOM_DIRECTION_HPP
namespace ftxui {
enum class Direction {
Up = 0,
Down = 1,
Left = 2,
Right = 3,
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_DIRECTION_HPP */

View File

@@ -1,196 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_ELEMENTS_HPP
#define FTXUI_DOM_ELEMENTS_HPP
#include <functional>
#include <memory>
#include "ftxui/dom/canvas.hpp"
#include "ftxui/dom/direction.hpp"
#include "ftxui/dom/flexbox_config.hpp"
#include "ftxui/dom/linear_gradient.hpp"
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/color.hpp"
#include "ftxui/screen/screen.hpp"
#include "ftxui/screen/terminal.hpp"
#include "ftxui/util/ref.hpp"
namespace ftxui {
class Node;
using Element = std::shared_ptr<Node>;
using Elements = std::vector<Element>;
using Decorator = std::function<Element(Element)>;
using GraphFunction = std::function<std::vector<int>(int, int)>;
enum BorderStyle {
LIGHT,
DASHED,
HEAVY,
DOUBLE,
ROUNDED,
EMPTY,
};
// Pipe elements into decorator togethers.
// For instance the next lines are equivalents:
// -> text("ftxui") | bold | underlined
// -> underlined(bold(text("FTXUI")))
Element operator|(Element, Decorator);
Element& operator|=(Element&, Decorator);
Elements operator|(Elements, Decorator);
Decorator operator|(Decorator, Decorator);
// --- Widget ---
Element text(std::string text);
Element vtext(std::string text);
Element separator();
Element separatorLight();
Element separatorDashed();
Element separatorHeavy();
Element separatorDouble();
Element separatorEmpty();
Element separatorStyled(BorderStyle);
Element separator(Pixel);
Element separatorCharacter(std::string);
Element separatorHSelector(float left,
float right,
Color unselected_color,
Color selected_color);
Element separatorVSelector(float up,
float down,
Color unselected_color,
Color selected_color);
Element gauge(float progress);
Element gaugeLeft(float progress);
Element gaugeRight(float progress);
Element gaugeUp(float progress);
Element gaugeDown(float progress);
Element gaugeDirection(float progress, Direction direction);
Element border(Element);
Element borderLight(Element);
Element borderDashed(Element);
Element borderHeavy(Element);
Element borderDouble(Element);
Element borderRounded(Element);
Element borderEmpty(Element);
Decorator borderStyled(BorderStyle);
Decorator borderStyled(BorderStyle, Color);
Decorator borderStyled(Color);
Decorator borderWith(const Pixel&);
Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index);
Element paragraph(const std::string& text);
Element paragraphAlignLeft(const std::string& text);
Element paragraphAlignRight(const std::string& text);
Element paragraphAlignCenter(const std::string& text);
Element paragraphAlignJustify(const std::string& text);
Element graph(GraphFunction);
Element emptyElement();
Element canvas(ConstRef<Canvas>);
Element canvas(int width, int height, std::function<void(Canvas&)>);
Element canvas(std::function<void(Canvas&)>);
// -- Decorator ---
Element bold(Element);
Element dim(Element);
Element inverted(Element);
Element underlined(Element);
Element underlinedDouble(Element);
Element blink(Element);
Element strikethrough(Element);
Decorator color(Color);
Decorator bgcolor(Color);
Decorator color(const LinearGradient&);
Decorator bgcolor(const LinearGradient&);
Element color(Color, Element);
Element bgcolor(Color, Element);
Element color(const LinearGradient&, Element);
Element bgcolor(const LinearGradient&, Element);
Decorator focusPosition(int x, int y);
Decorator focusPositionRelative(float x, float y);
Element automerge(Element child);
Decorator hyperlink(std::string link);
Element hyperlink(std::string link, Element child);
// --- Layout is
// Horizontal, Vertical or stacked set of elements.
Element hbox(Elements);
Element vbox(Elements);
Element dbox(Elements);
Element flexbox(Elements, FlexboxConfig config = FlexboxConfig());
Element gridbox(std::vector<Elements> lines);
Element hflow(Elements); // Helper: default flexbox with row direction.
Element vflow(Elements); // Helper: default flexbox with column direction.
// -- Flexibility ---
// Define how to share the remaining space when not all of it is used inside a
// container.
Element flex(Element); // Expand/Minimize if possible/needed.
Element flex_grow(Element); // Expand element if possible.
Element flex_shrink(Element); // Minimize element if needed.
Element xflex(Element); // Expand/Minimize if possible/needed on X axis.
Element xflex_grow(Element); // Expand element if possible on X axis.
Element xflex_shrink(Element); // Minimize element if needed on X axis.
Element yflex(Element); // Expand/Minimize if possible/needed on Y axis.
Element yflex_grow(Element); // Expand element if possible on Y axis.
Element yflex_shrink(Element); // Minimize element if needed on Y axis.
Element notflex(Element); // Reset the flex attribute.
Element filler(); // A blank expandable element.
// -- Size override;
enum WidthOrHeight { WIDTH, HEIGHT };
enum Constraint { LESS_THAN, EQUAL, GREATER_THAN };
Decorator size(WidthOrHeight, Constraint, int value);
// --- Frame ---
// A frame is a scrollable area. The internal area is potentially larger than
// the external one. The internal area is scrolled in order to make visible the
// focused element.
Element frame(Element);
Element xframe(Element);
Element yframe(Element);
Element focus(Element);
Element select(Element);
// --- Cursor ---
// Those are similar to `focus`, but also change the shape of the cursor.
Element focusCursorBlock(Element);
Element focusCursorBlockBlinking(Element);
Element focusCursorBar(Element);
Element focusCursorBarBlinking(Element);
Element focusCursorUnderline(Element);
Element focusCursorUnderlineBlinking(Element);
// --- Misc ---
Element vscroll_indicator(Element);
Decorator reflect(Box& box);
// Before drawing the |element| clear the pixel below. This is useful in
// combinaison with dbox.
Element clear_under(Element element);
// --- Util --------------------------------------------------------------------
Element hcenter(Element);
Element vcenter(Element);
Element center(Element);
Element align_right(Element);
Element nothing(Element element);
namespace Dimension {
Dimensions Fit(Element&);
} // namespace Dimension
} // namespace ftxui
// Make container able to take any number of children as input.
#include "ftxui/dom/take_any_args.hpp"
// Include old definitions using wstring.
#include "ftxui/dom/deprecated.hpp"
#endif // FTXUI_DOM_ELEMENTS_HPP

View File

@@ -1,114 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_FLEXBOX_CONFIG_HPP
#define FTXUI_DOM_FLEXBOX_CONFIG_HPP
/*
This replicate the CSS flexbox model.
See guide for documentation:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
*/
namespace ftxui {
struct FlexboxConfig {
/// This establishes the main-axis, thus defining the direction flex items are
/// placed in the flex container. Flexbox is (aside wrapping) single-direction
/// layout concept. Think of flex items as primarily laying out either in
/// horizontal rows or vertical columns.
enum class Direction {
Row, ///< Flex items are laid out in a row.
RowInversed, ///< Flex items are laid out in a row, but in reverse order.
Column, ///< Flex items are laid out in a column.
ColumnInversed ///< Flex items are laid out in a column, but in reverse
///< order.
};
Direction direction = Direction::Row;
/// By default, flex items will all try to fit onto one line. You can change
/// that and allow the items to wrap as needed with this property.
enum class Wrap {
NoWrap, ///< Flex items will all try to fit onto one line.
Wrap, ///< Flex items will wrap onto multiple lines.
WrapInversed, ///< Flex items will wrap onto multiple lines, but in reverse
///< order.
};
Wrap wrap = Wrap::Wrap;
/// This defines the alignment along the main axis. It helps distribute extra
/// free space leftover when either all the flex items on a line are
/// inflexible, or are flexible but have reached their maximum size. It also
/// exerts some control over the alignment of items when they overflow the
/// line.
enum class JustifyContent {
/// Items are aligned to the start of flexbox's direction.
FlexStart,
/// Items are aligned to the end of flexbox's direction.
FlexEnd,
/// Items are centered along the line.
Center,
/// Items are stretched to fill the line.
Stretch,
/// Items are evenly distributed in the line; first item is on the start
// line, last item on the end line
SpaceBetween,
/// Items are evenly distributed in the line with equal space around them.
/// Note that visually the spaces arent equal, since all the items have
/// equal space on both sides. The first item will have one unit of space
/// against the container edge, but two units of space between the next item
/// because that next item has its own spacing that applies.
SpaceAround,
/// Items are distributed so that the spacing between any two items (and the
/// space to the edges) is equal.
SpaceEvenly,
};
JustifyContent justify_content = JustifyContent::FlexStart;
/// This defines the default behavior for how flex items are laid out along
/// the cross axis on the current line. Think of it as the justify-content
/// version for the cross-axis (perpendicular to the main-axis).
enum class AlignItems {
FlexStart, ///< items are placed at the start of the cross axis.
FlexEnd, ///< items are placed at the end of the cross axis.
Center, ///< items are centered along the cross axis.
Stretch, ///< items are stretched to fill the cross axis.
};
AlignItems align_items = AlignItems::FlexStart;
// This aligns a flex containers lines within when there is extra space in
// the cross-axis, similar to how justify-content aligns individual items
// within the main-axis.
enum class AlignContent {
FlexStart, ///< items are placed at the start of the cross axis.
FlexEnd, ///< items are placed at the end of the cross axis.
Center, ///< items are centered along the cross axis.
Stretch, ///< items are stretched to fill the cross axis.
SpaceBetween, ///< items are evenly distributed in the cross axis.
SpaceAround, ///< tems evenly distributed with equal space around each
///< line.
SpaceEvenly, ///< items are evenly distributed in the cross axis with equal
///< space around them.
};
AlignContent align_content = AlignContent::FlexStart;
int gap_x = 0;
int gap_y = 0;
// Constructor pattern. For chained use like:
// ```
// FlexboxConfig()
// .Set(FlexboxConfig::Direction::Row)
// .Set(FlexboxConfig::Wrap::Wrap);
// ```
FlexboxConfig& Set(FlexboxConfig::Direction);
FlexboxConfig& Set(FlexboxConfig::Wrap);
FlexboxConfig& Set(FlexboxConfig::JustifyContent);
FlexboxConfig& Set(FlexboxConfig::AlignItems);
FlexboxConfig& Set(FlexboxConfig::AlignContent);
FlexboxConfig& SetGap(int gap_x, int gap_y);
};
} // namespace ftxui
#endif // FTXUI_DOM_FLEXBOX_CONFIG_HPP

View File

@@ -1,51 +0,0 @@
// Copyright 2023 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_LINEAR_GRADIENT_HPP
#define FTXUI_DOM_LINEAR_GRADIENT_HPP
#include <optional>
#include <vector>
#include "ftxui/screen/color.hpp" // for Colors
namespace ftxui {
/// @brief A class representing the settings for linear-gradient color effect.
///
/// Example:
/// ```cpp
/// LinearGradient()
/// .Angle(45)
/// .Stop(Color::Red, 0.0)
/// .Stop(Color::Green, 0.5)
/// .Stop(Color::Blue, 1.0);
/// ```
///
/// There are also shorthand constructors:
/// ```cpp
/// LinearGradient(Color::Red, Color::Blue);
/// LinearGradient(45, Color::Red, Color::Blue);
/// ```
struct LinearGradient {
float angle = 0.f;
struct Stop {
Color color = Color::Default;
std::optional<float> position;
};
std::vector<Stop> stops;
// Simple constructor
LinearGradient();
LinearGradient(Color begin, Color end);
LinearGradient(float angle, Color begin, Color end);
// Modifier using the builder pattern.
LinearGradient& Angle(float angle);
LinearGradient& Stop(Color color, float position);
LinearGradient& Stop(Color color);
};
} // namespace ftxui
#endif // FTXUI_DOM_LINEAR_GRADIENT_HPP

View File

@@ -1,66 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_NODE_HPP
#define FTXUI_DOM_NODE_HPP
#include <memory> // for shared_ptr
#include <vector> // for vector
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp"
namespace ftxui {
class Node;
class Screen;
using Element = std::shared_ptr<Node>;
using Elements = std::vector<Element>;
class Node {
public:
Node();
Node(Elements children);
Node(const Node&) = delete;
Node(const Node&&) = delete;
Node& operator=(const Node&) = delete;
Node& operator=(const Node&&) = delete;
virtual ~Node();
// Step 1: Compute layout requirement. Tell parent what dimensions this
// element wants to be.
// Propagated from Children to Parents.
virtual void ComputeRequirement();
Requirement requirement() { return requirement_; }
// Step 2: Assign this element its final dimensions.
// Propagated from Parents to Children.
virtual void SetBox(Box box);
// Step 3: Draw this element.
virtual void Render(Screen& screen);
// Layout may not resolve within a single iteration for some elements. This
// allows them to request additionnal iterations. This signal must be
// forwarded to children at least once.
struct Status {
int iteration = 0;
bool need_iteration = false;
};
virtual void Check(Status* status);
protected:
Elements children_;
Requirement requirement_;
Box box_;
};
void Render(Screen& screen, const Element& element);
void Render(Screen& screen, Node* node);
} // namespace ftxui
#endif // FTXUI_DOM_NODE_HPP

View File

@@ -1,34 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_REQUIREMENT_HPP
#define FTXUI_DOM_REQUIREMENT_HPP
#include "ftxui/screen/box.hpp"
namespace ftxui {
struct Requirement {
// The required size to fully draw the element.
int min_x = 0;
int min_y = 0;
// How much flexibility is given to the component.
int flex_grow_x = 0;
int flex_grow_y = 0;
int flex_shrink_x = 0;
int flex_shrink_y = 0;
// Focus management to support the frame/focus/select element.
enum Selection {
NORMAL = 0,
SELECTED = 1,
FOCUSED = 2,
};
Selection selection = NORMAL;
Box selected_box;
};
} // namespace ftxui
#endif // FTXUI_DOM_REQUIREMENT_HPP

View File

@@ -1,95 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_TABLE
#define FTXUI_DOM_TABLE
#include <memory>
#include <string> // for string
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, BorderStyle, LIGHT, Decorator
namespace ftxui {
// Usage:
//
// Initialization:
// ---------------
//
// auto table = Table({
// {"X", "Y"},
// {"-1", "1"},
// {"+0", "0"},
// {"+1", "1"},
// });
//
// table.SelectAll().Border(LIGHT);
//
// table.SelectRow(1).Border(DOUBLE);
// table.SelectRow(1).SeparatorInternal(Light);
//
// std::move(table).Element();
class Table;
class TableSelection;
class Table {
public:
Table();
Table(std::vector<std::vector<std::string>>);
Table(std::vector<std::vector<Element>>);
TableSelection SelectAll();
TableSelection SelectCell(int column, int row);
TableSelection SelectRow(int row_index);
TableSelection SelectRows(int row_min, int row_max);
TableSelection SelectColumn(int column_index);
TableSelection SelectColumns(int column_min, int column_max);
TableSelection SelectRectangle(int column_min,
int column_max,
int row_min,
int row_max);
Element Render();
private:
void Initialize(std::vector<std::vector<Element>>);
friend TableSelection;
std::vector<std::vector<Element>> elements_;
int input_dim_x_ = 0;
int input_dim_y_ = 0;
int dim_x_ = 0;
int dim_y_ = 0;
};
class TableSelection {
public:
void Decorate(Decorator);
void DecorateAlternateRow(Decorator, int modulo = 2, int shift = 0);
void DecorateAlternateColumn(Decorator, int modulo = 2, int shift = 0);
void DecorateCells(Decorator);
void DecorateCellsAlternateColumn(Decorator, int modulo = 2, int shift = 0);
void DecorateCellsAlternateRow(Decorator, int modulo = 2, int shift = 0);
void Border(BorderStyle border = LIGHT);
void BorderLeft(BorderStyle border = LIGHT);
void BorderRight(BorderStyle border = LIGHT);
void BorderTop(BorderStyle border = LIGHT);
void BorderBottom(BorderStyle border = LIGHT);
void Separator(BorderStyle border = LIGHT);
void SeparatorVertical(BorderStyle border = LIGHT);
void SeparatorHorizontal(BorderStyle border = LIGHT);
private:
friend Table;
Table* table_;
int x_min_;
int x_max_;
int y_min_;
int y_max_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_TABLE */

View File

@@ -1,47 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_DOM_TAKE_ANY_ARGS_HPP
#define FTXUI_DOM_TAKE_ANY_ARGS_HPP
// IWYU pragma: private, include "ftxui/dom/elements.hpp"
#include <type_traits>
namespace ftxui {
template <class T>
void Merge(Elements& /*container*/, T /*element*/) {}
template <>
inline void Merge(Elements& container, Element element) {
container.push_back(std::move(element));
}
template <>
inline void Merge(Elements& container, Elements elements) {
for (auto& element : elements)
container.push_back(std::move(element));
}
// Turn a set of arguments into a vector.
template <class... Args>
Elements unpack(Args... args) {
std::vector<Element> vec;
(Merge(vec, std::move(args)), ...);
return vec;
}
// Make |container| able to take any number of argments.
#define TAKE_ANY_ARGS(container) \
template <class... Args> \
Element container(Args... children) { \
return container(unpack(std::forward<Args>(children)...)); \
}
TAKE_ANY_ARGS(vbox)
TAKE_ANY_ARGS(hbox)
TAKE_ANY_ARGS(dbox)
TAKE_ANY_ARGS(hflow)
} // namespace ftxui
#endif // FTXUI_DOM_TAKE_ANY_ARGS_HPP

View File

@@ -1,24 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_BOX_HPP
#define FTXUI_SCREEN_BOX_HPP
namespace ftxui {
struct Box {
int x_min = 0;
int x_max = 0;
int y_min = 0;
int y_max = 0;
static auto Intersection(Box a, Box b) -> Box;
static auto Union(Box a, Box b) -> Box;
bool Contain(int x, int y) const;
bool operator==(const Box& other) const;
bool operator!=(const Box& other) const;
};
} // namespace ftxui
#endif // FTXUI_SCREEN_BOX_HPP

View File

@@ -1,336 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_COLOR_HPP
#define FTXUI_SCREEN_COLOR_HPP
#include <cstdint> // for uint8_t
#include <string> // for string
#include <vector> // for vector
#ifdef RGB
// Workaround for wingdi.h (via Windows.h) defining macros that break things.
// https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-rgb
#undef RGB
#endif
namespace ftxui {
/// @brief A class representing terminal colors.
/// @ingroup screen
class Color {
public:
enum Palette1 : uint8_t;
enum Palette16 : uint8_t;
enum Palette256 : uint8_t;
Color(); // Transparent.
Color(Palette1 index); // Transparent.
Color(Palette16 index); // Implicit conversion from index to Color.
Color(Palette256 index); // Implicit conversion from index to Color.
Color(uint8_t red, uint8_t green, uint8_t blue);
static Color RGB(uint8_t red, uint8_t green, uint8_t blue);
static Color HSV(uint8_t hue, uint8_t saturation, uint8_t value);
static Color Interpolate(float t, const Color& a, const Color& b);
//---------------------------
// List of colors:
//---------------------------
// clang-format off
enum Palette1 : uint8_t{
Default, // Transparent
};
enum Palette16 : uint8_t {
Black = 0,
Red = 1,
Green = 2,
Yellow = 3,
Blue = 4,
Magenta = 5,
Cyan = 6,
GrayLight = 7,
GrayDark = 8,
RedLight = 9,
GreenLight = 10,
YellowLight = 11,
BlueLight = 12,
MagentaLight = 13,
CyanLight = 14,
White = 15,
};
enum Palette256 : uint8_t {
Aquamarine1 = 122,
Aquamarine1Bis = 86,
Aquamarine3 = 79,
Blue1 = 21,
Blue3 = 19,
Blue3Bis = 20,
BlueViolet = 57,
CadetBlue = 72,
CadetBlueBis = 73,
Chartreuse1 = 118,
Chartreuse2 = 112,
Chartreuse2Bis = 82,
Chartreuse3 = 70,
Chartreuse3Bis = 76,
Chartreuse4 = 64,
CornflowerBlue = 69,
Cornsilk1 = 230,
Cyan1 = 51,
Cyan2 = 50,
Cyan3 = 43,
DarkBlue = 18,
DarkCyan = 36,
DarkGoldenrod = 136,
DarkGreen = 22,
DarkKhaki = 143,
DarkMagenta = 90,
DarkMagentaBis = 91,
DarkOliveGreen1 = 191,
DarkOliveGreen1Bis = 192,
DarkOliveGreen2 = 155,
DarkOliveGreen3 = 107,
DarkOliveGreen3Bis = 113,
DarkOliveGreen3Ter = 149,
DarkOrange = 208,
DarkOrange3 = 130,
DarkOrange3Bis = 166,
DarkRed = 52,
DarkRedBis = 88,
DarkSeaGreen = 108,
DarkSeaGreen1 = 158,
DarkSeaGreen1Bis = 193,
DarkSeaGreen2 = 151,
DarkSeaGreen2Bis = 157,
DarkSeaGreen3 = 115,
DarkSeaGreen3Bis = 150,
DarkSeaGreen4 = 65,
DarkSeaGreen4Bis = 71,
DarkSlateGray1 = 123,
DarkSlateGray2 = 87,
DarkSlateGray3 = 116,
DarkTurquoise = 44,
DarkViolet = 128,
DarkVioletBis = 92,
DeepPink1 = 198,
DeepPink1Bis = 199,
DeepPink2 = 197,
DeepPink3 = 161,
DeepPink3Bis = 162,
DeepPink4 = 125,
DeepPink4Bis = 89,
DeepPink4Ter = 53,
DeepSkyBlue1 = 39,
DeepSkyBlue2 = 38,
DeepSkyBlue3 = 31,
DeepSkyBlue3Bis = 32,
DeepSkyBlue4 = 23,
DeepSkyBlue4Bis = 24,
DeepSkyBlue4Ter = 25,
DodgerBlue1 = 33,
DodgerBlue2 = 27,
DodgerBlue3 = 26,
Gold1 = 220,
Gold3 = 142,
Gold3Bis = 178,
Green1 = 46,
Green3 = 34,
Green3Bis = 40,
Green4 = 28,
GreenYellow = 154,
Grey0 = 16,
Grey100 = 231,
Grey11 = 234,
Grey15 = 235,
Grey19 = 236,
Grey23 = 237,
Grey27 = 238,
Grey3 = 232,
Grey30 = 239,
Grey35 = 240,
Grey37 = 59,
Grey39 = 241,
Grey42 = 242,
Grey46 = 243,
Grey50 = 244,
Grey53 = 102,
Grey54 = 245,
Grey58 = 246,
Grey62 = 247,
Grey63 = 139,
Grey66 = 248,
Grey69 = 145,
Grey7 = 233,
Grey70 = 249,
Grey74 = 250,
Grey78 = 251,
Grey82 = 252,
Grey84 = 188,
Grey85 = 253,
Grey89 = 254,
Grey93 = 255,
Honeydew2 = 194,
HotPink = 205,
HotPink2 = 169,
HotPink3 = 132,
HotPink3Bis = 168,
HotPinkBis = 206,
IndianRed = 131,
IndianRed1 = 203,
IndianRed1Bis = 204,
IndianRedBis = 167,
Khaki1 = 228,
Khaki3 = 185,
LightCoral = 210,
LightCyan1Bis = 195,
LightCyan3 = 152,
LightGoldenrod1 = 227,
LightGoldenrod2 = 186,
LightGoldenrod2Bis = 221,
LightGoldenrod2Ter = 222,
LightGoldenrod3 = 179,
LightGreen = 119,
LightGreenBis = 120,
LightPink1 = 217,
LightPink3 = 174,
LightPink4 = 95,
LightSalmon1 = 216,
LightSalmon3 = 137,
LightSalmon3Bis = 173,
LightSeaGreen = 37,
LightSkyBlue1 = 153,
LightSkyBlue3 = 109,
LightSkyBlue3Bis = 110,
LightSlateBlue = 105,
LightSlateGrey = 103,
LightSteelBlue = 147,
LightSteelBlue1 = 189,
LightSteelBlue3 = 146,
LightYellow3 = 187,
Magenta1 = 201,
Magenta2 = 165,
Magenta2Bis = 200,
Magenta3 = 127,
Magenta3Bis = 163,
Magenta3Ter = 164,
MediumOrchid = 134,
MediumOrchid1 = 171,
MediumOrchid1Bis = 207,
MediumOrchid3 = 133,
MediumPurple = 104,
MediumPurple1 = 141,
MediumPurple2 = 135,
MediumPurple2Bis = 140,
MediumPurple3 = 97,
MediumPurple3Bis = 98,
MediumPurple4 = 60,
MediumSpringGreen = 49,
MediumTurquoise = 80,
MediumVioletRed = 126,
MistyRose1 = 224,
MistyRose3 = 181,
NavajoWhite1 = 223,
NavajoWhite3 = 144,
NavyBlue = 17,
Orange1 = 214,
Orange3 = 172,
Orange4 = 58,
Orange4Bis = 94,
OrangeRed1 = 202,
Orchid = 170,
Orchid1 = 213,
Orchid2 = 212,
PaleGreen1 = 121,
PaleGreen1Bis = 156,
PaleGreen3 = 114,
PaleGreen3Bis = 77,
PaleTurquoise1 = 159,
PaleTurquoise4 = 66,
PaleVioletRed1 = 211,
Pink1 = 218,
Pink3 = 175,
Plum1 = 219,
Plum2 = 183,
Plum3 = 176,
Plum4 = 96,
Purple = 129,
Purple3 = 56,
Purple4 = 54,
Purple4Bis = 55,
PurpleBis = 93,
Red1 = 196,
Red3 = 124,
Red3Bis = 160,
RosyBrown = 138,
RoyalBlue1 = 63,
Salmon1 = 209,
SandyBrown = 215,
SeaGreen1 = 84,
SeaGreen1Bis = 85,
SeaGreen2 = 83,
SeaGreen3 = 78,
SkyBlue1 = 117,
SkyBlue2 = 111,
SkyBlue3 = 74,
SlateBlue1 = 99,
SlateBlue3 = 61,
SlateBlue3Bis = 62,
SpringGreen1 = 48,
SpringGreen2 = 42,
SpringGreen2Bis = 47,
SpringGreen3 = 35,
SpringGreen3Bis = 41,
SpringGreen4 = 29,
SteelBlue = 67,
SteelBlue1 = 75,
SteelBlue1Bis = 81,
SteelBlue3 = 68,
Tan = 180,
Thistle1 = 225,
Thistle3 = 182,
Turquoise2 = 45,
Turquoise4 = 30,
Violet = 177,
Wheat1 = 229,
Wheat4 = 101,
Yellow1 = 226,
Yellow2 = 190,
Yellow3 = 148,
Yellow3Bis = 184,
Yellow4 = 100,
Yellow4Bis = 106,
};
// clang-format on
// --- Operators ------
bool operator==(const Color& rhs) const;
bool operator!=(const Color& rhs) const;
std::string Print(bool is_background_color) const;
private:
enum class ColorType : uint8_t {
Palette1,
Palette16,
Palette256,
TrueColor,
};
ColorType type_ = ColorType::Palette1;
uint8_t red_ = 0;
uint8_t green_ = 0;
uint8_t blue_ = 0;
};
inline namespace literals {
/// @brief Creates a color from a combined hex RGB representation,
/// e.g. 0x808000_rgb
Color operator""_rgb(unsigned long long int combined);
} // namespace literals
} // namespace ftxui
#endif // FTXUI_SCREEN_COLOR_HPP

View File

@@ -1,29 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_COLOR_INFO_HPP
#define FTXUI_SCREEN_COLOR_INFO_HPP
#include <cstdint>
#include <ftxui/screen/color.hpp>
namespace ftxui {
struct ColorInfo {
const char* name;
uint8_t index_256;
uint8_t index_16;
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t hue;
uint8_t saturation;
uint8_t value;
};
ColorInfo GetColorInfo(Color::Palette256 index);
ColorInfo GetColorInfo(Color::Palette16 index);
} // namespace ftxui
#endif // FTXUI_SCREEN_COLOR_INFO_HPP

View File

@@ -1,14 +0,0 @@
// Copyright 2021 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_DEPRECATED_HPP
#define FTXUI_SCREEN_DEPRECATED_HPP
#include <string>
namespace ftxui {
int wchar_width(wchar_t);
int wstring_width(const std::wstring&);
} // namespace ftxui
#endif // FTXUI_SCREEN_DEPRECATED_HPP

View File

@@ -1,130 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_SCREEN_HPP
#define FTXUI_SCREEN_SCREEN_HPP
#include <cstdint> // for uint8_t
#include <memory>
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/color.hpp" // for Color, Color::Default
#include "ftxui/screen/terminal.hpp" // for Dimensions
namespace ftxui {
/// @brief A unicode character and its associated style.
/// @ingroup screen
struct Pixel {
Pixel()
: blink(false),
bold(false),
dim(false),
inverted(false),
underlined(false),
underlined_double(false),
strikethrough(false),
automerge(false) {}
// A bit field representing the style:
bool blink : 1;
bool bold : 1;
bool dim : 1;
bool inverted : 1;
bool underlined : 1;
bool underlined_double : 1;
bool strikethrough : 1;
bool automerge : 1;
// The hyperlink associated with the pixel.
// 0 is the default value, meaning no hyperlink.
uint8_t hyperlink = 0;
// The graphemes stored into the pixel. To support combining characters,
// like: a⃦, this can potentially contain multiple codepoints.
std::string character = " ";
// Colors:
Color background_color = Color::Default;
Color foreground_color = Color::Default;
};
/// @brief Define how the Screen's dimensions should look like.
/// @ingroup screen
namespace Dimension {
Dimensions Fixed(int);
Dimensions Full();
} // namespace Dimension
/// @brief A rectangular grid of Pixel.
/// @ingroup screen
class Screen {
public:
// Constructors:
Screen(int dimx, int dimy);
static Screen Create(Dimensions dimension);
static Screen Create(Dimensions width, Dimensions height);
// Access a character in the grid at a given position.
std::string& at(int x, int y);
const std::string& at(int x, int y) const;
// Access a cell (Pixel) in the grid at a given position.
Pixel& PixelAt(int x, int y);
const Pixel& PixelAt(int x, int y) const;
std::string ToString() const;
// Print the Screen on to the terminal.
void Print() const;
// Get screen dimensions.
int dimx() const { return dimx_; }
int dimy() const { return dimy_; }
// Move the terminal cursor n-lines up with n = dimy().
std::string ResetPosition(bool clear = false) const;
// Fill the screen with space.
void Clear();
void ApplyShader();
struct Cursor {
int x = 0;
int y = 0;
enum Shape {
Hidden = 0,
BlockBlinking = 1,
Block = 2,
UnderlineBlinking = 3,
Underline = 4,
BarBlinking = 5,
Bar = 6,
};
Shape shape;
};
Cursor cursor() const { return cursor_; }
void SetCursor(Cursor cursor) { cursor_ = cursor; }
// Store an hyperlink in the screen. Return the id of the hyperlink. The id is
// used to identify the hyperlink when the user click on it.
uint8_t RegisterHyperlink(const std::string& link);
const std::string& Hyperlink(uint8_t id) const;
Box stencil;
protected:
int dimx_;
int dimy_;
std::vector<std::vector<Pixel>> pixels_;
Cursor cursor_;
std::vector<std::string> hyperlinks_ = {""};
};
} // namespace ftxui
#endif // FTXUI_SCREEN_SCREEN_HPP

View File

@@ -1,35 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_STRING_HPP
#define FTXUI_SCREEN_STRING_HPP
#include <stddef.h> // for size_t
#include <cstdint> // for uint8_t
#include <string> // for string, wstring, to_string
#include <vector> // for vector
namespace ftxui {
std::string to_string(const std::wstring& s);
std::wstring to_wstring(const std::string& s);
template <typename T>
std::wstring to_wstring(T s) {
return to_wstring(std::to_string(s));
}
int string_width(const std::string&);
// Split the string into a its glyphs. An empty one is inserted ater fullwidth
// ones.
std::vector<std::string> Utf8ToGlyphs(const std::string& input);
// Map every cells drawn by |input| to their corresponding Glyphs. Half-size
// Glyphs takes one cell, full-size Glyphs take two cells.
std::vector<int> CellToGlyphIndex(const std::string& input);
} // namespace ftxui
#include "ftxui/screen/deprecated.hpp"
#endif /* end of include guard: FTXUI_SCREEN_STRING_HPP */

View File

@@ -1,30 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_SCREEN_TERMINAL_HPP
#define FTXUI_SCREEN_TERMINAL_HPP
namespace ftxui {
struct Dimensions {
int dimx;
int dimy;
};
namespace Terminal {
Dimensions Size();
void SetFallbackSize(const Dimensions& fallbackSize);
enum Color {
Palette1,
Palette16,
Palette256,
TrueColor,
};
Color ColorSupport();
void SetColorSupport(Color color);
} // namespace Terminal
} // namespace ftxui
#endif // FTXUI_SCREEN_TERMINAL_HPP

View File

@@ -1,28 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_UTIL_AUTORESET_HPP
#define FTXUI_UTIL_AUTORESET_HPP
#include <utility>
namespace ftxui {
/// Assign a value to a variable, reset its old value when going out of scope.
template <typename T>
class AutoReset {
public:
AutoReset(T* variable, T new_value)
: variable_(variable), previous_value_(std::move(*variable)) {
*variable_ = std::move(new_value);
}
~AutoReset() { *variable_ = std::move(previous_value_); }
private:
T* variable_;
T previous_value_;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_UTIL_AUTORESET_HPP */

View File

@@ -1,133 +0,0 @@
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#ifndef FTXUI_UTIL_REF_HPP
#define FTXUI_UTIL_REF_HPP
#include <ftxui/screen/string.hpp>
#include <string>
#include <variant>
namespace ftxui {
/// @brief An adapter. Own or reference an immutable object.
template <typename T>
class ConstRef {
public:
ConstRef() = default;
ConstRef(const ConstRef<T>&) = default;
ConstRef(ConstRef<T>&&) = default;
ConstRef(T t) : variant_(std::move(t)) {}
ConstRef(const T* t) : variant_(t) {}
// Make a "reseatable" reference
ConstRef<T>& operator=(const ConstRef<T>&) = default;
// Accessors:
const T& operator()() const { return *Address(); }
const T& operator*() const { return *Address(); }
const T* operator->() const { return Address(); }
private:
std::variant<T, const T*> variant_ = T{};
const T* Address() const {
return std::holds_alternative<T>(variant_) ? &std::get<T>(variant_)
: std::get<const T*>(variant_);
}
};
/// @brief An adapter. Own or reference an mutable object.
template <typename T>
class Ref {
public:
Ref() = default;
Ref(const Ref<T>&) = default;
Ref(Ref<T>&&) = default;
Ref(T t) : variant_(std::move(t)) {}
Ref(T* t) : variant_(t) {}
// Make a "reseatable" reference.
Ref<T>& operator=(const Ref<T>&) = default;
// Accessors:
T& operator()() { return *Address(); }
T& operator*() { return *Address(); }
T* operator->() { return Address(); }
const T& operator()() const { return *Address(); }
const T& operator*() const { return *Address(); }
const T* operator->() const { return Address(); }
private:
std::variant<T, T*> variant_ = T{};
const T* Address() const {
return std::holds_alternative<T>(variant_) ? &std::get<T>(variant_)
: std::get<T*>(variant_);
}
T* Address() {
return std::holds_alternative<T>(variant_) ? &std::get<T>(variant_)
: std::get<T*>(variant_);
}
};
/// @brief An adapter. Own or reference a constant string. For convenience, this
/// class convert multiple mutable string toward a shared representation.
class StringRef : public Ref<std::string> {
public:
using Ref<std::string>::Ref;
StringRef(const wchar_t* ref) : StringRef(to_string(std::wstring(ref))) {}
StringRef(const char* ref) : StringRef(std::string(ref)) {}
};
/// @brief An adapter. Own or reference a constant string. For convenience, this
/// class convert multiple immutable string toward a shared representation.
class ConstStringRef : public ConstRef<std::string> {
public:
using ConstRef<std::string>::ConstRef;
ConstStringRef(const std::wstring* ref) : ConstStringRef(to_string(*ref)) {}
ConstStringRef(const std::wstring ref) : ConstStringRef(to_string(ref)) {}
ConstStringRef(const wchar_t* ref)
: ConstStringRef(to_string(std::wstring(ref))) {}
ConstStringRef(const char* ref) : ConstStringRef(std::string(ref)) {}
};
/// @brief An adapter. Reference a list of strings.
class ConstStringListRef {
public:
ConstStringListRef() = default;
ConstStringListRef(const std::vector<std::string>* ref) : ref_(ref) {}
ConstStringListRef(const std::vector<std::wstring>* ref) : ref_wide_(ref) {}
ConstStringListRef(const ConstStringListRef& other) = default;
ConstStringListRef& operator=(const ConstStringListRef& other) = default;
size_t size() const {
if (ref_) {
return ref_->size();
}
if (ref_wide_) {
return ref_wide_->size();
}
return 0;
}
std::string operator[](size_t i) const {
if (ref_) {
return (*ref_)[i];
}
if (ref_wide_) {
return to_string((*ref_wide_)[i]);
}
return "";
}
private:
const std::vector<std::string>* ref_ = nullptr;
const std::vector<std::wstring>* ref_wide_ = nullptr;
};
} // namespace ftxui
#endif /* end of include guard: FTXUI_UTIL_REF_HPP */

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,11 +1,7 @@
#include "system/system.hpp"
using namespace std;
#include "tui/tui.hpp"
int main() {
System *sys = new System();
string ment_filename = "/home/francois/Projets/twinsys/test/BPB-2177_Netlist_2_3.qcv";
sys->Load("bpb", ment_filename, ImportType::IMPORT_MENTOR);
delete sys;
};
Tui tui;
tui.Run();
return 0;
}

View File

@@ -175,6 +175,8 @@ public:
}
}
size_t size() const { return content.size(); }
/**
* @brief Returns an iterator to the beginning of the container.
* @return Iterator to the beginning.

View File

@@ -22,6 +22,7 @@ class System
public:
System();
void Load(std::string module_name, std::string filename, ImportType type);
Modules *modules() const { return mods; }
~System();
};

482
src/tui/tui.cpp Normal file
View File

@@ -0,0 +1,482 @@
#include "tui/tui.hpp"
#include "system/modules.hpp"
#include "system/parts.hpp"
#include "system/signals.hpp"
#include "system/system.hpp"
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <exception>
#include <filesystem>
#include <fstream>
#include <system_error>
using namespace ftxui;
Tui::Tui()
: cursor_pos(0), history_idx(-1), quit(false),
screen_idx(0),
search_types{"parts", "signals"},
search_module_idx(0), search_type_idx(0), search_focus_idx(0)
{
LoadHistory();
RegisterCommands();
Print("essim — type 'help' for commands, 'quit' to exit.");
}
Tui::~Tui() = default;
void Tui::Print(const std::string &line) {
output.push_back(line);
}
void Tui::HistoryUp() {
if (history.empty()) return;
if (history_idx == -1) history_idx = (int)history.size() - 1;
else if (history_idx > 0) history_idx--;
input = history[history_idx];
cursor_pos = (int)input.size();
}
void Tui::HistoryDown() {
if (history_idx == -1) return;
history_idx++;
if (history_idx >= (int)history.size()) {
history_idx = -1;
input.clear();
} else {
input = history[history_idx];
}
cursor_pos = (int)input.size();
}
void Tui::CancelPending() {
if (pending.empty()) return;
pending.clear();
input.clear();
cursor_pos = 0;
history_idx = -1;
Print("(cancelled)");
}
static std::string ToLower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(),
[](unsigned char c) { return std::tolower(c); });
return s;
}
std::vector<std::string> Tui::Tokenize(const std::string &s) {
std::vector<std::string> out;
std::string cur;
bool in_q = false;
for (char c : s) {
if (c == '"') { in_q = !in_q; continue; }
if (!in_q && std::isspace((unsigned char)c)) {
if (!cur.empty()) { out.push_back(std::move(cur)); cur.clear(); }
} else {
cur.push_back(c);
}
}
if (!cur.empty()) out.push_back(std::move(cur));
return out;
}
void Tui::RegisterCommands() {
commands["help"] = { {}, [this](auto &) {
Print("Commands (give params inline or fill them in via prompt):");
Print(" new create a new (empty) system");
Print(" load <module> <file> <mentor|altium|ods> load a module into the system");
Print(" search interactive search (parts/signals, live filter)");
Print(" clear clear the visualization area");
Print(" help show this message");
Print(" quit / exit leave essim");
Print(" Esc cancel a multi-step prompt");
Print(" Tab complete command name / file path");
}};
commands["clear"] = { {}, [this](auto &) { output.clear(); }};
commands["quit"] = { {}, [this](auto &) { quit = true; }};
commands["exit"] = { {}, [this](auto &) { quit = true; }};
commands["new"] = { {}, [this](auto &) {
sys = std::make_unique<System>();
Print("system created.");
}};
commands["load"] = {
{{"module name", false},
{"filename", true},
{"import type [mentor|altium|ods]", false}},
[this](const std::vector<std::string> &args) {
if (!sys) { Print("no system: run 'new' first."); return; }
std::string ls = args[2];
std::transform(ls.begin(), ls.end(), ls.begin(),
[](unsigned char c) { return std::tolower(c); });
ImportType t;
if (ls == "mentor") t = ImportType::IMPORT_MENTOR;
else if (ls == "altium") t = ImportType::IMPORT_ALTIUM;
else if (ls == "ods") t = ImportType::IMPORT_ODS;
else { Print("unknown import type: " + args[2]); return; }
try {
sys->Load(args[0], args[1], t);
Module *mod = sys->modules()->get(args[0]);
Print("loaded '" + args[0] + "' from " + args[1]);
Print(" parts: " + std::to_string(mod->size()));
Print(" signals: " + std::to_string(mod->signals->size()));
} catch (const std::exception &e) {
Print(std::string("load failed: ") + e.what());
}
}
};
commands["search"] = { {}, [this](auto &) {
if (!sys) { Print("no system: run 'new' first."); return; }
search_modules.clear();
for (auto &m : *sys->modules()) search_modules.push_back(m.first);
if (search_modules.empty()) { Print("no modules loaded."); return; }
search_module_idx = 0;
search_type_idx = 0;
search_query.clear();
search_focus_idx = 0; // start with the query input focused
screen_idx = 1;
}};
}
void Tui::Submit() {
if (!pending.empty()) {
if (input.empty()) { Print("(empty — Esc to cancel)"); return; }
Prompt p = std::move(pending.front());
pending.pop_front();
Print(" " + p.question + ": " + input);
std::string answer = std::move(input);
input.clear();
cursor_pos = 0;
history_idx = -1;
p.on_answer(answer);
return;
}
if (input.empty()) return;
history_idx = -1;
Print("> " + input);
std::string raw = std::move(input);
input.clear();
cursor_pos = 0;
Dispatch(raw);
}
void Tui::Dispatch(const std::string &raw) {
auto tokens = Tokenize(raw);
if (tokens.empty()) return;
auto it = commands.find(tokens[0]);
if (it == commands.end()) {
Print("unknown command: " + tokens[0]);
history.push_back(raw);
AppendHistory(raw);
return;
}
const std::string name = it->first;
const CommandSpec &spec = it->second;
if (tokens.size() - 1 > spec.params.size()) {
Print("too many arguments for '" + name + "'");
history.push_back(raw);
AppendHistory(raw);
return;
}
auto args = std::make_shared<std::vector<std::string>>(
tokens.begin() + 1, tokens.end());
if (args->size() == spec.params.size()) {
Finalize(name, spec, *args);
return;
}
for (size_t i = args->size(); i < spec.params.size(); ++i) {
bool last = (i + 1 == spec.params.size());
const auto &param = spec.params[i];
pending.push_back({
param.name,
[this, name, &spec, args, last](const std::string &s) {
args->push_back(s);
if (last) Finalize(name, spec, *args);
},
param.path_completion,
});
}
}
void Tui::Finalize(const std::string &name,
const CommandSpec &spec,
const std::vector<std::string> &args) {
std::string canonical = name;
for (const auto &a : args) {
if (a.find_first_of(" \t\"") != std::string::npos)
canonical += " \"" + a + "\"";
else
canonical += " " + a;
}
history.push_back(canonical);
AppendHistory(canonical);
spec.action(args);
}
static std::filesystem::path HistoryPath() {
namespace fs = std::filesystem;
#ifdef _WIN32
if (const char *p = std::getenv("LOCALAPPDATA"); p && *p)
return fs::path(p) / "essim" / "history";
if (const char *p = std::getenv("APPDATA"); p && *p)
return fs::path(p) / "essim" / "history";
if (const char *p = std::getenv("USERPROFILE"); p && *p)
return fs::path(p) / "AppData" / "Local" / "essim" / "history";
#else
if (const char *p = std::getenv("XDG_DATA_HOME"); p && *p)
return fs::path(p) / "essim" / "history";
if (const char *p = std::getenv("HOME"); p && *p)
return fs::path(p) / ".local" / "share" / "essim" / "history";
#endif
return {};
}
void Tui::LoadHistory() {
auto p = HistoryPath();
if (p.empty()) return;
std::ifstream f(p);
std::string line;
while (std::getline(f, line))
if (!line.empty()) history.push_back(line);
}
void Tui::AppendHistory(const std::string &cmd) {
auto p = HistoryPath();
if (p.empty()) return;
std::string trimmed = cmd;
while (!trimmed.empty() && std::isspace((unsigned char)trimmed.back()))
trimmed.pop_back();
if (trimmed.empty()) return;
std::error_code ec;
std::filesystem::create_directories(p.parent_path(), ec);
if (ec) return;
std::ofstream f(p, std::ios::app);
if (f) f << trimmed << '\n';
}
static std::string LongestCommonPrefix(const std::vector<std::string> &v) {
if (v.empty()) return "";
std::string lcp = v[0];
for (size_t i = 1; i < v.size(); ++i) {
size_t k = 0;
while (k < lcp.size() && k < v[i].size() && lcp[k] == v[i][k]) ++k;
lcp.resize(k);
}
return lcp;
}
void Tui::CompleteCommand() {
std::vector<std::string> matches;
for (const auto &kv : commands)
if (kv.first.rfind(input, 0) == 0) matches.push_back(kv.first);
if (matches.empty()) return;
if (matches.size() == 1) { input = matches[0]; cursor_pos = (int)input.size(); return; }
std::string lcp = LongestCommonPrefix(matches);
if (lcp.size() > input.size()) { input = lcp; cursor_pos = (int)input.size(); return; }
std::string line = " ";
for (const auto &m : matches) line += " " + m;
Print(line);
}
void Tui::CompletePath() {
namespace fs = std::filesystem;
auto pos = input.rfind('/');
std::string disp, prefix;
if (pos == std::string::npos) { disp = ""; prefix = input; }
else { disp = input.substr(0, pos + 1); prefix = input.substr(pos + 1); }
std::string resolved = disp.empty() ? "." : disp;
if (!resolved.empty() && resolved[0] == '~') {
if (const char *home = std::getenv("HOME"))
resolved = std::string(home) + resolved.substr(1);
}
std::vector<std::string> names;
std::vector<bool> is_dir;
try {
for (const auto &e : fs::directory_iterator(resolved)) {
std::string n = e.path().filename().string();
if (n.rfind(prefix, 0) == 0) {
names.push_back(n);
is_dir.push_back(e.is_directory());
}
}
} catch (const std::exception &) {
return;
}
if (names.empty()) return;
if (names.size() == 1) {
input = disp + names[0] + (is_dir[0] ? "/" : "");
cursor_pos = (int)input.size();
return;
}
std::string lcp = LongestCommonPrefix(names);
if (lcp.size() > prefix.size()) { input = disp + lcp; cursor_pos = (int)input.size(); return; }
std::string line = " ";
for (size_t i = 0; i < names.size(); ++i)
line += " " + names[i] + (is_dir[i] ? "/" : "");
Print(line);
}
void Tui::Run() {
auto screen = ScreenInteractive::Fullscreen();
// ---- Main TUI ----
InputOption opt;
opt.multiline = false;
opt.cursor_position = &cursor_pos;
opt.on_enter = [this] { Submit(); };
opt.transform = [](InputState s) {
auto el = s.element;
if (s.is_placeholder) el |= dim;
return el;
};
auto input_component = Input(&input, "type a command…", opt);
auto main_renderer = Renderer(input_component, [this, &screen, input_component] {
if (quit) screen.Exit();
Elements lines;
for (const auto &l : output) lines.push_back(text(l));
auto view = vbox(std::move(lines))
| focusPositionRelative(0, 1)
| yframe
| flex;
std::string label = pending.empty()
? "> "
: pending.front().question + "? ";
return vbox({
view,
separator(),
hbox({text(label), input_component->Render()}),
}) | border;
});
// ---- Search screen ----
InputOption query_opt;
query_opt.multiline = false;
query_opt.transform = opt.transform;
auto query_input = Input(&search_query, "filter…", query_opt);
auto module_menu = Menu(&search_modules, &search_module_idx);
auto type_menu = Menu(&search_types, &search_type_idx);
auto search_components = Container::Vertical(
{query_input, module_menu, type_menu}, &search_focus_idx);
auto search_renderer = Renderer(search_components,
[this, query_input, module_menu, type_menu] {
// Compute filtered list.
Elements result_lines;
int total = 0;
if (!search_modules.empty() && sys) {
const std::string &mname = search_modules[search_module_idx];
try {
Module *mod = sys->modules()->get(mname);
std::string needle = ToLower(search_query);
if (search_type_idx == 0) { // parts
for (auto &pkv : *mod) {
if (needle.empty()
|| ToLower(pkv.first).find(needle) != std::string::npos) {
result_lines.push_back(
text(" " + pkv.first
+ " (" + std::to_string(pkv.second->size()) + " pins)"));
++total;
}
}
} else { // signals
for (auto &skv : *mod->signals) {
if (needle.empty()
|| ToLower(skv.first).find(needle) != std::string::npos) {
result_lines.push_back(
text(" " + skv.first
+ " (" + std::to_string(skv.second->size()) + " pins)"));
++total;
}
}
}
} catch (const std::exception &) {}
}
auto left = vbox({
text("module") | bold,
module_menu->Render() | yframe | flex,
separator(),
text("type") | bold,
type_menu->Render(),
}) | size(WIDTH, EQUAL, 28);
auto right = vbox({
hbox({text(" search: "), query_input->Render() | flex}) | border,
text(std::to_string(total) + " match(es)") | dim,
vbox(std::move(result_lines)) | yframe | flex,
}) | flex;
return vbox({
hbox({left, separator(), right}) | flex,
text(" Tab: cycle focus | Esc: leave search ") | dim,
}) | border;
});
// ---- Screen tab + global key handling ----
auto tab = Container::Tab({main_renderer, search_renderer}, &screen_idx);
auto root = CatchEvent(tab, [this](Event e) {
if (screen_idx == 1) {
// Search mode
if (e == Event::Escape) { screen_idx = 0; return true; }
// Cycle focus query → modules → type → query (Menu eats Tab otherwise).
if (e == Event::Tab) {
search_focus_idx = (search_focus_idx + 1) % 3;
return true;
}
if (e == Event::TabReverse) {
search_focus_idx = (search_focus_idx + 2) % 3;
return true;
}
return false; // let menus / input handle the rest
}
// Main mode
if (e == Event::Escape && !pending.empty()) { CancelPending(); return true; }
if (e == Event::ArrowUp || e == Event::ArrowDown) {
if (pending.empty()) {
if (e == Event::ArrowUp) HistoryUp();
else HistoryDown();
}
return true;
}
if (e == Event::Tab) {
if (pending.empty()) {
if (input.find(' ') == std::string::npos) CompleteCommand();
} else if (pending.front().path_completion) {
CompletePath();
}
return true;
}
return false;
});
screen.Loop(root);
}

75
src/tui/tui.hpp Normal file
View File

@@ -0,0 +1,75 @@
#ifndef _TUI_HPP_
#define _TUI_HPP_
#include <deque>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
class System;
class Tui {
struct Prompt {
std::string question;
std::function<void(const std::string &)> on_answer;
bool path_completion = false;
};
struct CommandSpec {
struct Param {
std::string name;
bool path_completion = false;
};
std::vector<Param> params;
std::function<void(const std::vector<std::string> &)> action;
};
std::vector<std::string> history;
std::vector<std::string> output;
std::string input;
int cursor_pos;
int history_idx;
bool quit;
std::unique_ptr<System> sys;
std::deque<Prompt> pending;
std::map<std::string, CommandSpec> commands;
int screen_idx;
std::vector<std::string> search_modules;
std::vector<std::string> search_types;
int search_module_idx;
int search_type_idx;
int search_focus_idx;
std::string search_query;
public:
Tui();
~Tui();
void Run();
private:
void RegisterCommands();
void Submit();
void Dispatch(const std::string &raw);
void Finalize(const std::string &name,
const CommandSpec &spec,
const std::vector<std::string> &args);
void HistoryUp();
void HistoryDown();
void CancelPending();
void Print(const std::string &line);
void CompleteCommand();
void CompletePath();
void LoadHistory();
void AppendHistory(const std::string &cmd);
static std::vector<std::string> Tokenize(const std::string &s);
};
#endif // _TUI_HPP_