45 Commits

Author SHA1 Message Date
Arthur Sonzogni
81b7207121 Move to v2.0.0 (#315) 2022-01-22 17:58:33 +01:00
Arthur Sonzogni
6039474a26 Automerge feature. (#313)
Add the `automerge` attribute to the Pixel bit field. It controls
whether two pixels must be automerged. Defining this allows two
mergeable characters not to be merged.

This was requested by:
https://github.com/ArthurSonzogni/FTXUI/issues/285
2022-01-22 15:38:01 +01:00
Arthur Sonzogni
4267b40a68 Add collapsible into fuzzer. (#312) 2022-01-22 11:44:52 +01:00
Arthur Sonzogni
3829734fa9 Fix table separator (#311) 2022-01-21 23:02:29 +01:00
Vladislav Nepogodin
b4a655ec65 Introduce WithRestoredIO (#307)
This function allow running a callback with the terminal hooks
temporarily uninstalled.

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-01-19 13:38:39 +01:00
Vladislav Nepogodin
cd82fccde7 Add missing const. (#297)
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-01-18 19:48:58 +01:00
Arthur Sonzogni
382205c057 Add Table constructor from Elements. (#310) 2022-01-16 16:46:32 +01:00
pezy
feb24b9498 Fix Clamp crash when entries_ size is zero (#306)
Run ftxui_example_homescreen on Windows, then select compiler tab, crash on origin code.

Co-authored-by: chenpeizhe <peizhe.chen@horizon.ai>
2022-01-13 01:46:09 +01:00
pezy
3dc215e6c0 Fix README link (#308)
- [Build using nxxm] is same with [Build using CMake], just remove
- fix [Build using CMake] link

Co-authored-by: chenpeizhe <peizhe.chen@horizon.ai>
2022-01-12 15:31:44 +01:00
Arthur Sonzogni
1888631bec Main (#303)
* Refresh cursor reporting on resize.

* Fix invalid size write.

This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/302
2022-01-11 23:06:36 +01:00
Arthur Sonzogni
84299de2e1 Add json-tui as using FTXUI. 2022-01-09 22:00:45 +01:00
Arthur Sonzogni
cdd6339849 Execute IWYU (#299) 2022-01-07 11:03:54 +01:00
Andrey Zimin
358f886fab Clamp selected_ on list resize for Radiobox/Menu/Toggle (#298)
fix: https://github.com/ArthurSonzogni/FTXUI/issues/296#issue-1092343846

When the list in Radiobox/Menu/Toggle is resized, clamp the |selected_| values so that it stays within bounds.
Clamping is executed in Render() and in OnEvent()

Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2022-01-06 22:38:32 +01:00
Arthur Sonzogni
728976bdeb Fix vscroll indicator (#295)
* Clear vscroll_indicator content before drawing it.
2022-01-05 12:04:03 +01:00
Arthur Sonzogni
fc92f52b4c The collapsible element. (#294) 2022-01-02 15:48:56 +01:00
Man, Jianting (Meco)
071d2bc92b improve the window size handle method (#292)
1. the default window size should be 80x24 rather than 80x25 in VT100.
2. the ioctl return value result should be checked. Some operating systems don't support TIOCGWINSZ this command.
2022-01-01 19:24:37 +01:00
Vladislav Nepogodin
d549cdabb0 Reduce amount of warnings with pedantic compiler (#291) 2022-01-01 11:40:18 +01:00
Arthur Sonzogni
aea67743d4 Canvas bug fix and documentation. (#289) 2021-12-29 16:31:27 +01:00
Arthur Sonzogni
7614bf04a6 Address felixjulianheitmann suggestions. (#288)
- Remove unused examples/dom/flexbox.cpp
- Add canvas(width, height, void(Canvas&)) method
2021-12-24 17:29:39 +01:00
Arthur Sonzogni
188cffc5f6 Add example in the README.md 2021-12-23 15:12:22 +01:00
Arthur Sonzogni
0d47dd19ab Feature: Canvas (#287)
Draw using braille and block characters on a grid.
2021-12-23 14:17:33 +01:00
Arthur Sonzogni
7e5cd23b4c Add focusPosition[relative](x,y) (#280)
It allows when using inside a frame, to scroll the view toward a
particular position.

This resolves:
https://github.com/ArthurSonzogni/FTXUI/issues/125
2021-12-13 11:38:31 +01:00
Arthur Sonzogni
52276c8a2b Bugfix Input use std::string (#279)
Use std::string by default for the implementation of FTXUI's input
component.

Along the way:
- Give a correct implementation for fullwidth characters.
- Add tests
- Modify the way the cursor is drawn.
2021-12-12 21:31:54 +01:00
Arthur Sonzogni
602392c43d Implement flexbox (#277)
This implement the flexbox elements, following the HTML one.

Built from them, there is also the following elements:
- `paragraph`
- `paragraphAlignLeft`
- `paragraphAlignRight`
- `paragraphAlignCenter`
- `paragraphAlignJustify`

This is a breaking change.
2021-12-11 17:58:25 +01:00
Hassan Farooq
f7c6bf91a7 adding to projects using FTXUI list (#274)
Co-authored-by: hfarooq <hfarooq@tenstorrent.com>
2021-12-06 20:18:09 +01:00
Nikola Dućak
cecd54df42 Fix gridbox segfault (#260)
The problem was about
2021-11-17 10:16:09 +01:00
Gokulakrishnan D P
0186f8a463 added vantage in project section (#258) 2021-11-10 13:06:15 +01:00
Arthur Sonzogni
ebfb5ef3a1 Input shouldn't take focus. (#257)
This fixes:
https://github.com/ArthurSonzogni/FTXUI/issues/252
2021-11-09 20:30:27 +01:00
bogdasar1985
8f405f5054 Fix a typo (#256) 2021-11-09 12:11:45 +01:00
Arthur Sonzogni
8652280c85 Input shouldn't take focus. (#253)
This fixes:
https://github.com/ArthurSonzogni/FTXUI/issues/252
2021-11-07 12:01:17 +01:00
Arthur Sonzogni
aa6b78b8ad Upgrade version to 0.11.1 (#246)
A minor new version. Mostly for packaging the library in Release mode.
2021-10-23 21:13:23 +02:00
Arthur Sonzogni
c0e47aecb2 Remove unused file. (#244)
It was added mistakenly in:
https://github.com/ArthurSonzogni/FTXUI/pull/214#pullrequestreview-786773967
but @robinlinden found it.
2021-10-22 18:10:11 +02:00
Kevin Brennan
cba11151b5 Fix Windows builds not being in Release mode (#245) 2021-10-22 18:06:19 +02:00
Arthur Sonzogni
f80d9b5cfd Fix dropdown fuzzer. (#243) 2021-10-22 14:04:07 +02:00
Arthur Sonzogni
313ce9c35f Add support for PageUp/PageDown/Home/End buttons. (#242)
See:
https://github.com/ArthurSonzogni/FTXUI/issues/241
2021-10-20 21:15:40 +02:00
Arthur Sonzogni
4188ee2c04 Merge workflows. (#234)
- Merge all the workflows into one file.
- Produce Windows and MacOS artifact.
2021-10-17 20:10:07 +02:00
Arthur Sonzogni
026a005753 Table (#229)
This class allows rendering stylized table.
2021-10-15 23:04:11 +02:00
Jarosław Pelczar
7298636e7c Add API to set terminal fallback size (#230)
In case of embedded systems, the terminal size may not
always be detectable (e.g. in case of serial output).
Allow application to set up the default size in case
autodetection fails. On platform such as Emscripten,
there is only "fallback" size.

Signed-off-by: Jarosław Pelczar <jarek@jpelczar.com>
2021-10-13 13:44:30 +02:00
tomvg
5e199fcd85 Include <algorithm> for std::max for msvc 2017, (#226)
Msvc defined its own (lowercase) macros for min/max. Because of this, the std::min and std::max functions need to be explicitly included or otherwise are not availible.
In the msvc 2019 compiler this issue seems fixed. However, on msvc 2017 not including <algorithm> causes compilation errors. Adding the include is a simple fix that does not hurt the other platforms and enables compilation on msvc 2017.
2021-10-12 11:36:19 +02:00
Arthur Sonzogni
57a5512a22 Support dynamic library. (#225)
- Let the global `BUILD_SHARED_LIBS` dictates whether the library should
  be built statically or dynamically. The cmake's default is statically.
- Add library version and symlink.

This lead to the following install tree.
.
├── include
│   └── ftxui
│       ├── component [...]
│       ├── dom [...]
│       ├── screen [...]
│       └── util [...]
└── lib
    ├── cmake
    │   └── ftxui
    │       ├── ftxui-config.cmake
    │       ├── ftxui-config-version.cmake
    │       └── ftxui-config-version-noconfig.cmake
    ├── ftxui-component.so -> ftxui-component.so.0.10.369
    ├── ftxui-component.so.0.10.369
    ├── ftxui-dom.so -> ftxui-dom.so.0.10.369
    ├── ftxui-dom.so.0.10.369
    ├── ftxui-screen.so -> ftxui-screen.so.0.10.369
    └── ftxui-screen.so.0.10.369

Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/223
2021-10-10 12:52:34 +02:00
Jarosław Pelczar
75482d82d4 Use sensible defaults in case TIOCGWINSZ fails (#224)
This can happen for example in embedded linux, in case
the application is started via serial terminal.

Signed-off-by: Jarosław Pelczar <jarek@jpelczar.com>
Co-authored-by: ArthurSonzogni <sonzogniarthur@gmail.com>
2021-10-09 11:51:00 +02:00
Arthur Sonzogni
31c26c6956 Update README.md 2021-10-08 14:12:27 +02:00
ArthurSonzogni
6dd626a79a Fix: Add separator(Pixel) back.
It was removed by mistacke previously.
Take the opportunity to create new documentation.
2021-10-03 10:45:47 +02:00
ArthurSonzogni
05fc866d74 Do not trigger the CQ twice on pull request. 2021-09-30 21:44:29 +02:00
ArthurSonzogni
46f481ded7 Submit release as draft.
This will let me time to add a description.
2021-09-30 21:42:52 +02:00
124 changed files with 6834 additions and 927 deletions

161
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,161 @@
name: Build
on:
create:
tags:
-v*
push:
branches:
- master
pull_request:
branches:
- master
jobs:
test:
name: "Tests"
strategy:
matrix:
include:
- name: Linux GCC
os: ubuntu-latest
compiler: g++-9
test: true
- name: Linux Clang
os: ubuntu-latest
compiler: clang++
test: true
- name: MacOS clang
os: macos-latest
compiler: clang++
test: true
- name: Windows MSVC
os: windows-latest
compiler: cl
test: false
runs-on: ${{ matrix.os }}
steps:
- name: "Checkout repository"
uses: actions/checkout@v2
- name: "Enable MSVC command prompt"
if: matrix.os == 'windows-latest'
uses: ilammy/msvc-dev-cmd@v1
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Build debug mode"
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER=${{ matrix.compiler }}
-DFTXUI_BUILD_DOCS=OFF
-DFTXUI_BUILD_EXAMPLES=ON
-DFTXUI_BUILD_TESTS=ON
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON ;
cmake --build . --config Debug;
- name: "Run tests"
if: matrix.test
run: >
cd build;
./tests
# Create a release on new v* tags
release:
needs: test
if: ${{ github.event_name == 'create' && startsWith(github.ref, 'refs/tags/v') }}
name: "Create release"
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: "Create release"
uses: softprops/action-gh-release@v1
id: create_release
with:
draft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Build artifact for the release
package:
name: "Build packages"
needs: release
strategy:
matrix:
include:
- os: ubuntu-latest
asset_path: build/ftxui*Linux*
- os: macos-latest
asset_path: build/ftxui*Darwin*
- os: windows-latest
asset_path: build/ftxui*Win64*
runs-on: ${{ matrix.os }}
steps:
- name: "Checkout repository"
uses: actions/checkout@v2
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Build packages"
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_BUILD_TYPE=Release
-DFTXUI_BUILD_DOCS=OFF
-DFTXUI_BUILD_EXAMPLES=OFF
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON;
cmake --build . --config Release --target package;
- uses: shogo82148/actions-upload-release-asset@v1
with:
upload_url: ${{ needs.release.outputs.upload_url }}
asset_path: ${{ matrix.asset_path }}
overwrite: true
documentation:
needs: package
runs-on: ubuntu-latest
steps:
- name: "Checkout repository"
uses: actions/checkout@v2
- name: "Install cmake"
uses: lukka/get-cmake@latest
- name: "Install emsdk"
uses: mymindstorm/setup-emsdk@v7
- name: "Install Doxygen/Graphviz"
run: >
sudo apt-get update;
sudo apt-get install doxygen graphviz;
- name: "Build documentation"
run: >
mkdir build;
cd build;
emcmake cmake ..;
cmake --build . --target doc;
rsync -amv --include='*/' --include='*.html' --include='*.js' --include='*.wasm' --exclude='*' examples doc/doxygen/html;
- name: "Deploy"
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/doc/doxygen/html/
enable_jekyll: false
allow_empty_commit: false
force_orphan: true
publish_branch: gh-pages

View File

@@ -1,71 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '38 7 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -1,27 +0,0 @@
name: Linux Clang
on:
- pull_request
- push
jobs:
build:
name: Linux Clang
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Build
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER=clang++
-DFTXUI_BUILD_TESTS=ON;
cmake --build . --config Release;
- name: Tests
if: ${{ matrix.config.test }}
run: >
cd build;
./tests

View File

@@ -1,47 +0,0 @@
name: Linux Emscripten
on:
- pull_request
- push
jobs:
build:
name: Linux Emscripten
runs-on: ubuntu-latest
env:
DOCUMENTATION: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: mymindstorm/setup-emsdk@v7
- name: Install Doxygen/Graphviz
if: fromJSON(env.DOCUMENTATION)
run: |
sudo apt-get update
sudo apt-get install doxygen graphviz
- name: Build
run: |
emcmake cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S . -B build
cmake --build build
- name: Build documentation
if: fromJSON(env.DOCUMENTATION)
run: |
cd build
cmake --build . --target doc
# Copy emscripten built examples to the doxygen output directory for deployment
rsync -amv --include='*/' --include='*.html' --include='*.js' --include='*.wasm' --exclude='*' examples doc/doxygen/html
# Deploy the HTML documentation to GitHub Pages
- name: GH Pages Deployment
if: fromJSON(env.DOCUMENTATION)
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: build/doc/doxygen/html/
enable_jekyll: false
allow_empty_commit: false
force_orphan: true
publish_branch: gh-pages

View File

@@ -1,26 +0,0 @@
name: Linux GCC
on:
- pull_request
- push
jobs:
build:
name: Linux GCC
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Build
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER=g++-9
-DFTXUI_BUILD_TESTS=ON;
cmake --build . --config Release;
- name: Tests
run: >
cd build;
./tests

View File

@@ -1,27 +0,0 @@
name: MacOS Clang
on:
- pull_request
- push
jobs:
build:
name: MacOS Clang
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Build
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER=clang++
-DFTXUI_BUILD_TESTS=ON;
cmake --build . --config Release;
- name: Tests
if: ${{ matrix.config.test }}
run: >
cd build;
./tests

View File

@@ -1,39 +0,0 @@
name: Release
on:
create:
tags:
-v*
jobs:
build:
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: seanmiddleditch/gha-setup-ninja@master
- name: Build
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER=clang++
-DFTXUI_BUILD_DOCS=OFF
-DFTXUI_BUILD_EXAMPLES=OFF
-DFTXUI_BUILD_TESTS=OFF
-DFTXUI_BUILD_TESTS_FUZZER=OFF
-DFTXUI_ENABLE_INSTALL=ON;
cmake --build . --config Release;
make package;
- name: Upload
uses: softprops/action-gh-release@v1
with:
files: build/ftxui-*
draft: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,28 +0,0 @@
name: Windows MSVC
on:
- pull_request
- push
jobs:
build:
name: Windows MSVC
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: seanmiddleditch/gha-setup-ninja@master
- uses: ilammy/msvc-dev-cmd@v1
- name: Build
run: >
mkdir build;
cd build;
cmake ..
-DCMAKE_CXX_COMPILER="cl"
-DFTXUI_BUILD_TESTS=ON;
cmake --build . --config Release;
- name: Tests
if: ${{ matrix.config.test }}
run: >
cd build;
./tests.exe

View File

@@ -1,20 +1,96 @@
# Changelog
Changelog
=========
## Current
current (development)
---------------------
## 0.10 (2021-09-30)
2.0.0
-----
### Features:
#### Screen
- Add the `automerge` to the Pixel bit field. This now controls which pixels are
automatically merged.
#### DOM:
- Add the `Canvas` class and `ElementFrom('canvas')` function. Together users of
the library can draw using braille and block characters.
- Support `flexbox` dom elements. This is build symmetrically to the HTML one.
All the following attributes are supported: direction, wrap, justify-content,
align-items, align-content, gap
- Add the dom elements helper based on `flexbox`:
- `paragraph`
- `paragraphAlignLeft`
- `paragraphAlignCenter`
- `paragraphAlignRight`
- `paragraphAlignJustify`
- Add the helper elements based on `flexbox`: `hflow()`, `vflow()`.
- Add: `focusPositionRelative` and `focusPosition`
- Add `Table` constructor from 2D vector of Element, instead of string.
#### Component
- Add the `collapsible` component.
- Add the `ScreenInteractive::WithRestoredIO`. This decorates a callback. This
runs it with the terminal hooks temporarilly uninstalled. This is useful if
you want to execute command using directly stdin/stdout/sterr.
### Bug
#### Table
- The `table` horizontal and vertical separator are now correctly expanded.
#### Component
- `Input` shouldn't take focus when hovered by the mouse.
- Modifying `Input`'s during on_enter/on_change event is now working correctly.
### Breaking changes:
- The behavior of `paragraph` has been modified. It now returns en Element,
instead of a list of elements.
0.11.1
------
# Component
- Feature: Support for PageUp/PageDown/Home/End buttons.
- Bugfix: Check the selected element are within bounds for Dropdown.
# Build
- Bugfix: Package library using the "Release config". Not debug.
0.11
----
## github workflow
- Add Windows ad MacOS artefacts.
- Merge all the workflows.
## Bug
- On Unix system, fallback to {80,25} screen dimension on failure.
## CMake
- Support for shared library, via `BUILD_SHARED_LIBS` option.
- Add library version and symlinks.
0.10 (2021-09-30)
--------------------
## Bug
- Fix the automated merge of borders.
### Dom
- `vscroll_indicator`. Show a scrollbar indicator on the right.
- `Table()` class to build stylised table.
See https://github.com/ArthurSonzogni/FTXUI/discussions/228
- `vscroll_indicator`. Show a scrollbar indicator on the right.
- `separatorEmpty`. A separator drawing nothing.
- `separatorFixed`. A separator drawing the provided character.
### Component
- `Maybe`: Display an component conditionnally based on a boolean.
- `Dropdown`: A dropdown select list.
- `Maybe`: Display an component conditionnally based on a boolean.
- `Dropdown`: A dropdown select list.
## 0.9 (2021-09-26)
0.9 (2021-09-26)
----------------
The initial release where changelog where written.

View File

@@ -1,13 +1,11 @@
cmake_minimum_required(VERSION 3.11)
include(cmake/ftxui_git_version.cmake)
project(ftxui
LANGUAGES CXX
VERSION 0.10.${git_version}
VERSION 2.0.0
)
option(FTXUI_BUILD_DOCS "Set to ON to build tests" ON)
option(FTXUI_BUILD_DOCS "Set to ON to build docs" ON)
option(FTXUI_BUILD_EXAMPLES "Set to ON to build examples" ON)
option(FTXUI_BUILD_TESTS "Set to ON to build tests" OFF)
option(FTXUI_BUILD_TESTS_FUZZER "Set to ON to enable fuzzing" OFF)
@@ -24,22 +22,25 @@ else()
${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
endif()
add_library(screen STATIC
add_library(screen
include/ftxui/screen/box.hpp
include/ftxui/screen/color.hpp
include/ftxui/screen/color_info.hpp
include/ftxui/screen/screen.hpp
include/ftxui/screen/string.hpp
src/ftxui/screen/box.cpp
src/ftxui/screen/color.cpp
src/ftxui/screen/color_info.cpp
src/ftxui/screen/screen.cpp
src/ftxui/screen/string.cpp
src/ftxui/screen/terminal.cpp
include/ftxui/screen/box.hpp
include/ftxui/screen/color.hpp
include/ftxui/screen/color_info.hpp
include/ftxui/screen/screen.hpp
include/ftxui/screen/string.hpp
src/ftxui/screen/util.hpp
)
add_library(dom STATIC
add_library(dom
include/ftxui/dom/canvas.hpp
include/ftxui/dom/elements.hpp
include/ftxui/dom/flexbox_config.hpp
include/ftxui/dom/node.hpp
include/ftxui/dom/requirement.hpp
include/ftxui/dom/take_any_args.hpp
@@ -48,18 +49,23 @@ add_library(dom STATIC
src/ftxui/dom/border.cpp
src/ftxui/dom/box_helper.cpp
src/ftxui/dom/box_helper.hpp
src/ftxui/dom/canvas.cpp
src/ftxui/dom/clear_under.cpp
src/ftxui/dom/color.cpp
src/ftxui/dom/composite_decorator.cpp
src/ftxui/dom/dbox.cpp
src/ftxui/dom/dim.cpp
src/ftxui/dom/flex.cpp
src/ftxui/dom/flexbox.cpp
src/ftxui/dom/flexbox_config.cpp
src/ftxui/dom/flexbox_helper.cpp
src/ftxui/dom/flexbox_helper.hpp
src/ftxui/dom/focus.cpp
src/ftxui/dom/frame.cpp
src/ftxui/dom/gauge.cpp
src/ftxui/dom/graph.cpp
src/ftxui/dom/gridbox.cpp
src/ftxui/dom/hbox.cpp
src/ftxui/dom/hflow.cpp
src/ftxui/dom/inverted.cpp
src/ftxui/dom/node.cpp
src/ftxui/dom/node_decorator.cpp
@@ -69,13 +75,14 @@ add_library(dom STATIC
src/ftxui/dom/separator.cpp
src/ftxui/dom/size.cpp
src/ftxui/dom/spinner.cpp
src/ftxui/dom/table.cpp
src/ftxui/dom/text.cpp
src/ftxui/dom/underlined.cpp
src/ftxui/dom/util.cpp
src/ftxui/dom/vbox.cpp
)
add_library(component STATIC
add_library(component
include/ftxui/component/captured_mouse.hpp
include/ftxui/component/component.hpp
include/ftxui/component/component_base.hpp
@@ -86,6 +93,7 @@ add_library(component STATIC
src/ftxui/component/button.cpp
src/ftxui/component/catch_event.cpp
src/ftxui/component/checkbox.cpp
src/ftxui/component/collapsible.cpp
src/ftxui/component/component.cpp
src/ftxui/component/container.cpp
src/ftxui/component/dropdown.cpp
@@ -111,9 +119,13 @@ target_link_libraries(dom
find_package(Threads)
target_link_libraries(component
PUBLIC dom
PRIVATE Threads::Threads
PUBLIC Threads::Threads
)
set_target_properties(screen PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(dom PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(component PROPERTIES VERSION ${PROJECT_VERSION})
include(cmake/ftxui_set_options.cmake)
ftxui_set_options(screen)
ftxui_set_options(dom)
@@ -138,4 +150,3 @@ if(FTXUI_ENABLE_INSTALL)
include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake)
endif()

254
README.md
View File

@@ -10,7 +10,7 @@
<a href="https://github.com/ArthurSonzogni/FTXUI/graphs/contributors"><img src="https://img.shields.io/github/contributors/arthursonzogni/FTXUI?color=blue"></img></a>
<br/>
<a href="https://github.com/ArthurSonzogni/FTXUI/wiki">Documentation</a> ·
<a href="https://arthursonzogni.github.io/FTXUI/">Documentation</a> ·
<a href="https://github.com/ArthurSonzogni/FTXUI/issues">Report Bug</a> ·
<a href="https://arthursonzogni.github.io/FTXUI/examples.html">Examples</a> .
<a href="https://github.com/ArthurSonzogni/FTXUI/issues">Request Feature</a> ·
@@ -35,23 +35,11 @@ A simple C++ library for terminal based user interface.
* Keyboard & mouse navigation.
## Operating systems
- [![linux-emscripten][badge.linux-emscripten]][link.linux-emscripten]
- [![linux-gcc][badge.linux-gcc]][link.linux-gcc]
[![linux-clang][badge.linux-clang]][link.linux-clang]
- [![windows-msvc][badge.windows-msvc]][link.windows-msvc]
- [![mac-clang][badge.mac-clang]][link.mac-clang]
[badge.linux-gcc]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-gcc.yaml/badge.svg?branch=master
[badge.linux-clang]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-clang.yaml/badge.svg?branch=master
[badge.linux-emscripten]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-emscripten.yaml/badge.svg?branch=master
[badge.windows-msvc]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/windows-msvc.yaml/badge.svg?branch=master
[badge.mac-clang]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/mac-clang.yaml/badge.svg?branch=master
[link.linux-gcc]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-gcc.yaml
[link.linux-clang]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-clang.yaml
[link.linux-emscripten]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/linux-emscripten.yaml
[link.windows-msvc]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/windows-msvc.yaml
[link.mac-clang]: https://github.com/ArthurSonzogni/FTXUI/actions/workflows/mac-clang.yaml
- Webassembly
- Linux
- MacOS
- Windows
## Example
~~~cpp
@@ -74,17 +62,241 @@ A simple C++ library for terminal based user interface.
└────────────────────────────────────────────────────────────────────────────┘
~~~
# Documentation
## Documentation
- [Starter example project](https://github.com/ArthurSonzogni/ftxui-starter)
- [Documentation](https://arthursonzogni.github.io/FTXUI/)
- [Examples (WebAssembly)](https://arthursonzogni.com/FTXUI/examples/)
- [Build using CMake](https://arthursonzogni.com/FTXUI/doc/#build-using-cmake)
- [Build using nxxm](https://arthursonzogni.com/FTXUI/doc/#build-using-cmake)
- [Build using CMake](https://github.com/ArthurSonzogni/FTXUI/blob/master/doc/mainpage.md#using-cmake)
## Short gallery
#### DOM
This module defines a hierarchical set of Element. An element manages layout and can be responsive to the terminal dimensions.
They are declared in [<ftxui/dom/elements.hpp>](https://arthursonzogni.github.io/FTXUI/elements_8hpp_source.html
)
<details><summary>Layout</summary>
Element can be arranged together:
- horizontally with `hbox`
- vertically with `vbox`
- inside a grid with `gridbox`
- wrap along one direction using the `flexbox`.
Element can become flexible using the the `flex` decorator.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2vbox_hbox_8cpp-example.html) using `hbox`, `vbox` and `filler`.
![image](https://user-images.githubusercontent.com/4759106/147242524-7103b5d9-1a92-4e2d-ac70-b3d6740061e3.png)
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2gridbox_8cpp-example.htmlp) using gridbox:
![image](https://user-images.githubusercontent.com/4759106/147242972-0db1f2e9-0790-496f-86e6-ed2c604f7a73.png)
[Example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/hflow.cpp) using flexbox:
![image](https://user-images.githubusercontent.com/4759106/147243064-780ac7cc-605b-475f-94b8-cf7c4aed03a5.png)
[See](https://arthursonzogni.github.io/FTXUI/examples_2dom_2hflow_8cpp-example.html) also this [demo](https://arthursonzogni.com/FTXUI/examples/?file=component/flexbox).
</details>
<details><summary>Style</summary>
An element can be decorated using the functions:
- `bold`
- `dim`
- `inverted`
- `underlined`
- `blink`
- `color`
- `bgcolor`
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2style_gallery_8cpp-example.html)
![image](https://user-images.githubusercontent.com/4759106/147244118-380bf834-9e33-40df-9ff0-07c10f2598ef.png)
FTXUI support the pipe operator. It means: `decorator1(decorator2(element))` and `element | decorator1 | decorator2` can be used.
</details>
<details><summary>Colors</summary>
FTXUI support every color palettes:
Color [gallery](https://arthursonzogni.github.io/FTXUI/examples_2dom_2color_gallery_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147248595-04c7245a-5b85-4544-809d-a5984fc6f9e7.png)
</details>
<details><summary>Border and separator</summary>
Use decorator border and element separator() to subdivide your UI:
```cpp
auto document = vbox({
text("top"),
separator(),
text("bottom"),
}) | border;
```
[Demo](https://arthursonzogni.github.io/FTXUI/examples_2dom_2separator_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147244514-4135f24b-fb8e-4067-8896-bc53545583f7.png)
</details>
<details><summary>Text and paragraph</summary>
A simple piece of text is represented using `text("content")`.
To support text wrapping following spaces the following function are provided:
```cpp
Element paragraph(std::string text);
Element paragraphAlignLeft(std::string text);
Element paragraphAlignRight(std::string text);
Element paragraphAlignCenter(std::string text);
Element paragraphAlignJustify(std::string text);
```
[Paragraph example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2table_8cpp-example.html):
![ezgif com-gif-maker (4)](https://user-images.githubusercontent.com/4759106/147251370-983a06e7-6f41-4113-92b8-942f43d34d06.gif)
</details>
<details><summary>Table</summary>
A class to easily style a table of data.
[Example](https://arthursonzogni.github.io/FTXUI/examples_2dom_2table_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147250766-77d8ec9e-cf2b-486d-9866-1fd9f1bd2e6b.png)
</details>
<details><summary>Canvas</summary>
Drawing can be made on a Canvas, using braille, block, or simple characters:
Simple [example](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/dom/canvas.cpp):
![image](https://user-images.githubusercontent.com/4759106/147245843-76cc62fb-ccb4-421b-aacf-939f9afb42fe.png)
Complex [examples](https://github.com/ArthurSonzogni/FTXUI/blob/master/examples/component/canvas_animated.cpp):
![ezgif com-gif-maker (3)](https://user-images.githubusercontent.com/4759106/147250538-783a8246-98e0-4a25-b032-3bd3710549d1.gif)
</details>
#### Component
The ftxui/component is needed when you want to produce dynamic UI, reactive to the user's input. It defines a set of ftxui::Component. A component reacts to Events (keyboard, mouse, resize, ...) and Render Element (see previous section).
Prebuilt components are declared in [<ftxui/component/component.hpp>](https://arthursonzogni.github.io/FTXUI/component_8hpp_source.html)
<details><summary>Gallery</summary>
[Gallery](https://arthursonzogni.github.io/FTXUI/examples_2component_2gallery_8cpp-example.html) of multiple components. ([demo](https://arthursonzogni.com/FTXUI/examples/?file=component/gallery))
![image](https://user-images.githubusercontent.com/4759106/147247330-b60beb9f-e665-48b4-81c0-4b01ee95bc66.png)
</details>
<details><summary>Radiobox</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2radiobox_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147246401-809d14a5-6621-4e36-8dd9-a2d75ef2a94e.png)
</details>
<details><summary>Checkbox</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2checkbox_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147246646-b86926a9-1ef9-4efb-af98-48a9b62acd81.png)
</details>
<details><summary>Input</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2input_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147247671-f1d6f606-1845-4e94-a4a0-d4273e9ae6bd.png)
</details>
<details><summary>Toggle</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2toggle_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147249383-e2201cf1-b7b8-4a5a-916f-d761e3e7ae40.png)
</details>
<details><summary>Slider</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2slider_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147249265-7e2cad75-082c-436e-affe-44a550c480ab.png)
</details>
<details><summary>Menu</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2menu_8cpp-example.html):
![image](https://user-images.githubusercontent.com/4759106/147247822-0035fd6f-bb13-4b3a-b057-77eb9291582f.png)
</details>
<details><summary>ResizableSplit</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2resizable_split_8cpp-example.html):
![ezgif com-gif-maker](https://user-images.githubusercontent.com/4759106/147248372-c55512fe-9b96-4b08-a1df-d05cf2cae431.gif)
</details>
<details><summary>Dropdown</summary>
[Example](https://arthursonzogni.github.io/FTXUI/examples_2component_2dropdown_8cpp-example.html):
![youtube-video-gif (3)](https://user-images.githubusercontent.com/4759106/147246982-1e821751-531c-4e1f-bc37-2fa290e143cd.gif)
</details>
<details><summary>Tab</summary>
[Vertical](https://arthursonzogni.github.io/FTXUI/examples_2component_2tab_vertical_8cpp-example.html):
![ezgif com-gif-maker (1)](https://user-images.githubusercontent.com/4759106/147250144-22ff044a-4773-4ff7-a49c-12ba4034acb4.gif)
[Horizontal](https://arthursonzogni.github.io/FTXUI/examples_2component_2tab_horizontal_8cpp-example.html):
![ezgif com-gif-maker (2)](https://user-images.githubusercontent.com/4759106/147250217-fe447e0f-7a99-4e08-948a-995087d9b40e.gif)
</details>
## Project using FTXUI
Feel free to add your projects here:
- [json-tui](https://github.com/ArthurSonzogni/json-tui)
- [git-tui](https://github.com/ArthurSonzogni/git-tui)
- [rgb-tui](https://github.com/ArthurSonzogni/rgb-tui)
- [chrome-log-beautifier](https://github.com/ArthurSonzogni/chrome-log-beautifier)
@@ -98,6 +310,8 @@ Feel free to add your projects here:
- [CryptoCalculator](https://github.com/brevis/CryptoCalculator)
- [todoman](https://github.com/aaleino/todoman)
- [TimeAccumulator](https://github.com/asari555/TimeAccumulator)
- [vantage](https://github.com/gokulmaxi/vantage)
- [tabdeeli](https://github.com/typon/tabdeeli)
## Hosted on
* [github](https://github.com/ArthurSonzogni/ftxui)

View File

@@ -3,7 +3,12 @@ if (NOT WIN32)
GIT_REPOSITORY "https://github.com/google/benchmark"
GIT_TAG 62937f91b5c763a8e119d0c20c67b87bde8eff1c
)
FetchContent_MakeAvailable(googlebenchmark)
FetchContent_GetProperties(googlebenchmark)
if(NOT googlebenchmark_POPULATED)
FetchContent_Populate(googlebenchmark)
add_subdirectory(${googlebenchmark_SOURCE_DIR} ${googlebenchmark_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
add_executable(ftxui_benchmark
src/ftxui/dom/benchmark_test.cpp

View File

@@ -1,13 +0,0 @@
find_package(Git QUIET)
if (Git_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
message("git found")
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE git_version
OUTPUT_STRIP_TRAILING_WHITESPACE
)
else()
set(git_version 0)
endif()

View File

@@ -1,4 +1,12 @@
set(CPACK_GENERATOR "DEB;External;RPM;STGZ;TBZ2;TGZ;TXZ;TZ;TZST;ZIP")
if (UNIX AND NOT APPLE)
set(CPACK_GENERATOR "DEB;External;RPM;STGZ;TBZ2;TGZ;TXZ;TZ;TZST;ZIP")
elseif (UNIX AND APPLE)
set(CPACK_GENERATOR "DragNDrop;NuGet;TGZ;ZIP")
elseif (WIN32)
set(CPACK_GENERATOR "DEB;NuGet;TGZ;ZIP")
else()
set(CPACK_GENERATOR "ZIP")
endif()
set(CPACK_DEBIAN_PACKAGE_DEPENDS " ")
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE_URL "https://github.com/ArthurSonzogni/FTXUI/")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Arthur Sonzogni")

View File

@@ -4,24 +4,32 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
include(FetchContent)
FetchContent_Declare( googletest
FetchContent_Declare(googletest
GIT_REPOSITORY "https://github.com/google/googletest"
GIT_TAG 23ef29555ef4789f555f1ba8c51b4c52975f0907
)
FetchContent_MakeAvailable(googletest)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
add_executable(tests
src/ftxui/component/component_test.cpp
src/ftxui/component/container_test.cpp
src/ftxui/component/input_test.cpp
src/ftxui/component/menu_test.cpp
src/ftxui/component/radiobox_test.cpp
src/ftxui/component/receiver_test.cpp
src/ftxui/component/screen_interactive_test.cpp
src/ftxui/component/terminal_input_parser_test.cpp
src/ftxui/component/toggle_test.cpp
src/ftxui/dom/flexbox_helper_test.cpp
src/ftxui/dom/flexbox_test.cpp
src/ftxui/dom/gauge_test.cpp
src/ftxui/dom/gridbox_test.cpp
src/ftxui/dom/hbox_test.cpp
src/ftxui/dom/table_test.cpp
src/ftxui/dom/text_test.cpp
src/ftxui/dom/vbox_test.cpp
src/ftxui/screen/string_test.cpp

View File

@@ -1,7 +1,7 @@
set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR})
function(example name)
add_executable(${name} ${name}.cpp)
target_link_libraries(${name} PUBLIC ${DIRECTORY_LIB})
add_executable(ftxui_example_${name} ${name}.cpp)
target_link_libraries(ftxui_example_${name} PUBLIC ${DIRECTORY_LIB})
file(RELATIVE_PATH dir ${EXAMPLES_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_property(GLOBAL APPEND PROPERTY FTXUI::EXAMPLES ${dir}/${name})
endfunction(example)

View File

@@ -1,10 +1,14 @@
set(DIRECTORY_LIB component)
example(button)
example(canvas_animated)
example(checkbox)
example(checkbox_in_frame)
example(collapsible)
example(composition)
example(dropdown)
example(flexbox_gallery)
example(focus)
example(gallery)
example(homescreen)
example(input)
@@ -27,3 +31,4 @@ example(slider_rgb)
example(tab_horizontal)
example(tab_vertical)
example(toggle)
example(with_restored_io)

View File

@@ -0,0 +1,263 @@
#include <cmath> // for sin, cos
#include <ftxui/dom/elements.hpp> // for canvas, Element, separator, hbox, operator|, border
#include <ftxui/screen/screen.hpp> // for Pixel
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/component/component.hpp" // for Renderer, CatchEvent, Horizontal, Menu, Tab
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/mouse.hpp" // for Mouse
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/canvas.hpp" // for Canvas
#include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::Blue, Color::Green, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
int mouse_x = 0;
int mouse_y = 0;
// A triangle following the mouse, using braille characters.
auto renderer_line_braille = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "Several lines (braille)");
c.DrawPointLine(mouse_x, mouse_y, 80, 10, Color::Red);
c.DrawPointLine(80, 10, 80, 40, Color::Blue);
c.DrawPointLine(80, 40, mouse_x, mouse_y, Color::Green);
return canvas(std::move(c));
});
// A triangle following the mouse, using block characters.
auto renderer_line_block = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "Several lines (block)");
c.DrawBlockLine(mouse_x, mouse_y, 80, 10, Color::Red);
c.DrawBlockLine(80, 10, 80, 40, Color::Blue);
c.DrawBlockLine(80, 40, mouse_x, mouse_y, Color::Green);
return canvas(std::move(c));
});
// A circle following the mouse, using braille characters.
auto renderer_circle_braille = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A circle (braille)");
c.DrawPointCircle(mouse_x, mouse_y, 30);
return canvas(std::move(c));
});
// A circle following the mouse, using block characters.
auto renderer_circle_block = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A circle (block)");
c.DrawBlockCircle(mouse_x, mouse_y, 30);
return canvas(std::move(c));
});
// A filled circle following the mouse, using braille characters.
auto renderer_circle_filled_braille = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A circle filled (braille)");
c.DrawPointCircleFilled(mouse_x, mouse_y, 30);
return canvas(std::move(c));
});
// A filled circle following the mouse, using block characters.
auto renderer_circle_filled_block = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A circle filled (block)");
c.DrawBlockCircleFilled(mouse_x, mouse_y, 30);
return canvas(std::move(c));
});
// An ellipse following the mouse, using braille characters.
auto renderer_ellipse_braille = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "An ellipse (braille)");
c.DrawPointEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2);
return canvas(std::move(c));
});
// An ellipse following the mouse, using block characters.
auto renderer_ellipse_block = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "An ellipse (block)");
c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2);
return canvas(std::move(c));
});
// An ellipse following the mouse filled, using braille characters.
auto renderer_ellipse_filled_braille = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A filled ellipse (braille)");
c.DrawPointEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2,
mouse_y / 2);
return canvas(std::move(c));
});
// An ellipse following the mouse filled, using block characters.
auto renderer_ellipse_filled_block = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A filled ellipse (block)");
c.DrawBlockEllipseFilled(mouse_x / 2, mouse_y / 2, mouse_x / 2,
mouse_y / 2);
c.DrawBlockEllipse(mouse_x / 2, mouse_y / 2, mouse_x / 2, mouse_y / 2);
return canvas(std::move(c));
});
// A text following the mouse
auto renderer_text = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A piece of text");
c.DrawText(mouse_x, mouse_y, "This is a piece of text with effects",
[](Pixel& p) {
p.foreground_color = Color::Red;
p.underlined = true;
p.bold = true;
});
return canvas(std::move(c));
});
auto renderer_plot_1 = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A graph");
std::vector<int> ys(100);
for (int x = 0; x < 100; x++) {
float dx = x - mouse_x;
float dy = 50;
ys[x] = dy + 20 * cos(dx * 0.14) + 10 * sin(dx * 0.42);
}
for (int x = 1; x < 99; x++)
c.DrawPointLine(x, ys[x], x + 1, ys[x + 1]);
return canvas(std::move(c));
});
auto renderer_plot_2 = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A symmetrical graph filled");
std::vector<int> ys(100);
for (int x = 0; x < 100; x++) {
ys[x] = 30 + //
10 * cos(x * 0.2 - mouse_x * 0.05) + //
5 * sin(x * 0.4) + //
5 * sin(x * 0.3 - mouse_y * 0.05); //
}
for (int x = 0; x < 100; x++) {
c.DrawPointLine(x, 50 + ys[x], x, 50 - ys[x], Color::Red);
}
return canvas(std::move(c));
});
auto renderer_plot_3 = Renderer([&] {
auto c = Canvas(100, 100);
c.DrawText(0, 0, "A 2D gaussian plot");
int size = 15;
// mouse_x = 5mx + 3*my
// mouse_y = 0mx + -5my + 90
float my = (mouse_y - 90) / -5.f;
float mx = (mouse_x - 3 * my) / 5.f;
std::vector<std::vector<float>> ys(size, std::vector<float>(size));
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
float dx = x - mx;
float dy = y - my;
ys[y][x] = -1.5 + 3.0 * std::exp(-0.2f * (dx * dx + dy * dy));
}
}
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
if (x != 0) {
c.DrawPointLine(
5 * (x - 1) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x - 1],
5 * (x - 0) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x]);
}
if (y != 0) {
c.DrawPointLine(
5 * (x - 0) + 3 * (y - 1), 90 - 5 * (y - 1) - 5 * ys[y - 1][x],
5 * (x - 0) + 3 * (y - 0), 90 - 5 * (y - 0) - 5 * ys[y][x]);
}
}
}
return canvas(std::move(c));
});
int selected_tab = 12;
auto tab = Container::Tab(
{
renderer_line_braille,
renderer_line_block,
renderer_circle_braille,
renderer_circle_block,
renderer_circle_filled_braille,
renderer_circle_filled_block,
renderer_ellipse_braille,
renderer_ellipse_block,
renderer_ellipse_filled_braille,
renderer_ellipse_filled_block,
renderer_plot_1,
renderer_plot_2,
renderer_plot_3,
renderer_text,
},
&selected_tab);
// This capture the last mouse position.
auto tab_with_mouse = CatchEvent(tab, [&](Event e) {
if (e.is_mouse()) {
mouse_x = (e.mouse().x - 1) * 2;
mouse_y = (e.mouse().y - 1) * 4;
}
return false;
});
std::vector<std::string> tab_titles = {
"line (braille)",
"line (block)",
"circle (braille)",
"circle (block)",
"circle filled (braille)",
"circle filled (block)",
"ellipse (braille)",
"ellipse (block)",
"ellipse filled (braille)",
"ellipse filled (block)",
"plot_1 simple",
"plot_2 filled",
"plot_3 3D",
"text",
};
auto tab_toggle = Menu(&tab_titles, &selected_tab);
auto component = Container::Horizontal({
tab_with_mouse,
tab_toggle,
});
// Add some separator to decorate the whole component:
auto component_renderer = Renderer(component, [&] {
return hbox({
tab_with_mouse->Render(),
separator(),
tab_toggle->Render(),
}) |
border;
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(component_renderer);
return 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 LICENSED file.

View File

@@ -1,25 +1,31 @@
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Checkbox, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include <memory> // for allocator, __shared_ptr_access
#include <string> // for string, basic_string, operator+, to_string
#include <vector> // for vector
using namespace ftxui;
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Input, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, Element, size, border, frame, vscroll_indicator, HEIGHT, LESS_THAN
int main(int argc, const char* argv[]) {
bool build_examples_state = false;
bool build_tests_state = false;
bool use_webassembly_state = true;
using namespace ftxui;
auto component = Container::Vertical({
Checkbox("Build examples", &build_examples_state),
Checkbox("Build tests", &build_tests_state),
Checkbox("Use WebAssembly", &use_webassembly_state),
Component input_list = Container::Vertical({});
std::vector<std::string> items(100, "");
for (int i = 0; i < items.size(); ++i) {
input_list->Add(Input(&(items[i]), "placeholder " + std::to_string(i)));
}
auto renderer = Renderer(input_list, [&] {
return input_list->Render() | vscroll_indicator | frame | border |
size(HEIGHT, LESS_THAN, 10);
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(component);
return 0;
screen.Loop(renderer);
}
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// 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.

View File

@@ -0,0 +1,58 @@
#include <memory> // for allocator, make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Collapsible, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, hbox, Element
using namespace ftxui;
// Take a list of component, display them vertically, one column shifted to the
// right.
Component Inner(std::vector<Component> children) {
Component vlist = Container::Vertical(std::move(children));
return Renderer(vlist, [vlist] {
return hbox({
text(" "),
vlist->Render(),
});
});
}
Component Empty() {
return std::make_shared<ComponentBase>();
}
int main(int argc, const char* argv[]) {
auto component =
Collapsible("Collapsible 1",
Inner({
Collapsible("Collapsible 1.1",
Inner({
Collapsible("Collapsible 1.1.1", Empty()),
Collapsible("Collapsible 1.1.2", Empty()),
Collapsible("Collapsible 1.1.3", Empty()),
})),
Collapsible("Collapsible 1.2",
Inner({
Collapsible("Collapsible 1.2.1", Empty()),
Collapsible("Collapsible 1.2.2", Empty()),
Collapsible("Collapsible 1.2.3", Empty()),
})),
Collapsible("Collapsible 1.3",
Inner({
Collapsible("Collapsible 1.3.1", Empty()),
Collapsible("Collapsible 1.3.2", Empty()),
Collapsible("Collapsible 1.3.3", Empty()),
})),
}));
ScreenInteractive::FitComponent().Loop(component);
}
// 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.

View File

@@ -1,26 +1,23 @@
#include <functional> // for function
#include <iostream> // for basic_ostream::operator<<, operator<<, endl, basic_ostream, basic_ostream<>::__ostream_type, cout, ostream
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include <string> // for basic_string, string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Dropdown, Horizontal, Vertical
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main(int argc, const char* argv[]) {
using namespace ftxui;
std::vector<std::string> entries = {
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east",
"tribute", "clearance", "ally", "bend", "electronics",
"module", "era", "cultural", "sniff", "nationalism",
"negotiation", "deliver", "figure", "east", "tribute",
"clearance", "ally", "bend", "electronics", "module",
"era", "cultural", "sniff", "nationalism", "negotiation",
"deliver", "figure", "east", "tribute", "clearance",
"ally", "bend", "electronics", "module", "era",
"cultural", "sniff", "nationalism", "negotiation", "deliver",
"figure", "east",
};
int selected_1 = 0;

View File

@@ -0,0 +1,192 @@
#include <stddef.h> // for size_t
#include <memory> // for shared_ptr, __shared_ptr_access, allocator
#include <string> // for string, basic_string, to_string, operator+, char_traits
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Radiobox, Vertical, Checkbox, Horizontal, Renderer, ResizableSplitBottom, ResizableSplitRight
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, window, operator|, vbox, hbox, Element, flexbox, bgcolor, filler, flex, size, border, hcenter, color, EQUAL, bold, dim, notflex, xflex_grow, yflex_grow, HEIGHT, WIDTH
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignItems, FlexboxConfig::Direction, FlexboxConfig::JustifyContent::Center, FlexboxConfig::Wrap
#include "ftxui/screen/color.hpp" // for Color, Color::Black
using namespace ftxui;
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::Fullscreen();
int direction_index = 0;
int wrap_index = 0;
int justify_content_index = 0;
int align_items_index = 0;
int align_content_index = 0;
std::vector<std::string> directions = {
"Row",
"RowInversed",
"Column",
"ColumnInversed",
};
std::vector<std::string> wraps = {
"NoWrap",
"Wrap",
"WrapInversed",
};
std::vector<std::string> justify_content = {
"FlexStart", "FlexEnd", "Center", "Stretch",
"SpaceBetween", "SpaceAround", "SpaceEvenly",
};
std::vector<std::string> align_items = {
"FlexStart",
"FlexEnd",
"Center",
"Stretch",
};
std::vector<std::string> align_content = {
"FlexStart", "FlexEnd", "Center", "Stretch",
"SpaceBetween", "SpaceAround", "SpaceEvenly",
};
auto radiobox_direction = Radiobox(&directions, &direction_index);
auto radiobox_wrap = Radiobox(&wraps, &wrap_index);
auto radiobox_justify_content =
Radiobox(&justify_content, &justify_content_index);
auto radiobox_align_items = Radiobox(&align_items, &align_items_index);
auto radiobox_align_content = Radiobox(&align_content, &align_content_index);
bool element_xflex_grow = false;
bool element_yflex_grow = false;
bool group_xflex_grow = true;
bool group_yflex_grow = true;
auto checkbox_element_xflex_grow =
Checkbox("element |= xflex_grow", &element_xflex_grow);
auto checkbox_element_yflex_grow =
Checkbox("element |= yflex_grow", &element_yflex_grow);
auto checkbox_group_xflex_grow =
Checkbox("group |= xflex_grow", &group_xflex_grow);
auto checkbox_group_yflex_grow =
Checkbox("group |= yflex_grow", &group_yflex_grow);
auto make_box = [&](size_t dimx, size_t dimy, size_t index) {
std::string title = std::to_string(dimx) + "x" + std::to_string(dimy);
auto element = window(text(title) | hcenter | bold,
text(std::to_string(index)) | hcenter | dim) |
size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy) |
bgcolor(Color::HSV(index * 25, 255, 255)) |
color(Color::Black);
if (element_xflex_grow)
element = element | xflex_grow;
if (element_yflex_grow)
element = element | yflex_grow;
return element;
};
auto content_renderer = Renderer([&] {
FlexboxConfig config;
config.direction = static_cast<FlexboxConfig::Direction>(direction_index);
config.wrap = static_cast<FlexboxConfig::Wrap>(wrap_index);
config.justify_content =
static_cast<FlexboxConfig::JustifyContent>(justify_content_index);
config.align_items =
static_cast<FlexboxConfig::AlignItems>(align_items_index);
config.align_content =
static_cast<FlexboxConfig::AlignContent>(align_content_index);
auto group = flexbox(
{
make_box(8, 4, 0),
make_box(9, 6, 1),
make_box(11, 6, 2),
make_box(10, 4, 3),
make_box(13, 7, 4),
make_box(12, 4, 5),
make_box(12, 5, 6),
make_box(10, 4, 7),
make_box(12, 4, 8),
make_box(10, 5, 9),
},
config);
group = group | bgcolor(Color::Black);
group = group | notflex;
if (!group_xflex_grow)
group = hbox(group, filler());
if (!group_yflex_grow)
group = vbox(group, filler());
group = group | flex;
return group;
});
auto center = FlexboxConfig()
.Set(FlexboxConfig::JustifyContent::Center)
.Set(FlexboxConfig::AlignContent::Center);
int space_right = 10;
int space_bottom = 1;
content_renderer = ResizableSplitRight(
Renderer([&] { return flexbox({text("resizable")}, center); }),
content_renderer, &space_right);
content_renderer = ResizableSplitBottom(
Renderer([&] { return flexbox({text("resizable")}, center); }),
content_renderer, &space_bottom);
auto main_container = Container::Vertical({
Container::Horizontal({
radiobox_direction,
radiobox_wrap,
Container::Vertical({
checkbox_element_xflex_grow,
checkbox_element_yflex_grow,
checkbox_group_xflex_grow,
checkbox_group_yflex_grow,
}),
}),
Container::Horizontal({
radiobox_justify_content,
radiobox_align_items,
radiobox_align_content,
}),
content_renderer,
});
auto main_renderer = Renderer(main_container, [&] {
return vbox({
vbox({hbox({
window(text("FlexboxConfig::Direction"),
radiobox_direction->Render()),
window(text("FlexboxConfig::Wrap"), radiobox_wrap->Render()),
window(text("Misc:"),
vbox({
checkbox_element_xflex_grow->Render(),
checkbox_element_yflex_grow->Render(),
checkbox_group_xflex_grow->Render(),
checkbox_group_yflex_grow->Render(),
})),
}),
hbox({
window(text("FlexboxConfig::JustifyContent"),
radiobox_justify_content->Render()),
window(text("FlexboxConfig::AlignItems"),
radiobox_align_items->Render()),
window(text("FlexboxConfig::AlignContent"),
radiobox_align_content->Render()),
})}),
content_renderer->Render() | flex | border,
});
});
screen.Loop(main_renderer);
return 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.

View File

@@ -0,0 +1,70 @@
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for operator+, char_traits, to_string, string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Slider, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Elements, Element, operator|, separator, text, focusPositionRelative, size, border, flex, frame, bgcolor, gridbox, vbox, EQUAL, center, HEIGHT, WIDTH
#include "ftxui/screen/color.hpp" // for Color
using namespace ftxui;
Element make_box(int x, int y) {
std::string title = "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
return text(title) | center | size(WIDTH, EQUAL, 18) |
size(HEIGHT, EQUAL, 9) | border |
bgcolor(Color::HSV(x * 255 / 15, 255, y * 255 / 15));
};
Element make_grid() {
std::vector<Elements> rows;
for (int i = 0; i < 15; i++) {
std::vector<Element> cols;
for (int j = 0; j < 15; j++) {
cols.push_back(make_box(i, j));
}
rows.push_back(cols);
}
return gridbox(rows);
};
int main(int argc, const char* argv[]) {
float focus_x = 0.0f;
float focus_y = 0.0f;
auto slider_x = Slider("x", &focus_x, 0.f, 1.f, 0.01f);
auto slider_y = Slider("y", &focus_y, 0.f, 1.f, 0.01f);
auto renderer = Renderer(
Container::Vertical({
slider_x,
slider_y,
}),
[&] {
auto title = "focusPositionRelative(" + //
std::to_string(focus_x) + ", " + //
std::to_string(focus_y) + ")"; //
return vbox({
text(title),
separator(),
slider_x->Render(),
slider_y->Render(),
separator(),
make_grid() | focusPositionRelative(focus_x, focus_y) |
frame | flex,
}) |
border;
});
auto screen = ScreenInteractive::Fullscreen();
screen.Loop(renderer);
return 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.

View File

@@ -1,27 +1,33 @@
#include <stddef.h> // for size_t
#include <array> // for array
#include <chrono> // for operator""s, chrono_literals
#include <cmath> // for sin
#include <functional> // for ref, reference_wrapper, function
#include <memory> // for allocator, shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, operator+, char_traits, to_string
#include <string> // for string, basic_string, operator+, to_string, char_traits
#include <thread> // for sleep_for, thread
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Menu, Radiobox, Tab, Toggle
#include "ftxui/component/component.hpp" // for Checkbox, Renderer, Horizontal, Vertical, Input, Menu, Radiobox, ResizableSplitLeft, Tab, Toggle
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/event.hpp" // for Event, Event::Custom
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for operator|, color, bgcolor, filler, Element, size, vbox, flex, hbox, graph, separator, EQUAL, WIDTH, hcenter, bold, border, window, HEIGHT, Elements, hflow, flex_grow, frame, gauge, LESS_THAN, spinner, dim, GREATER_THAN
#include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::Black, Color::Blue, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default
#include "ftxui/dom/elements.hpp" // for text, operator|, color, bgcolor, filler, Element, size, vbox, flex, hbox, separator, graph, EQUAL, paragraph, hcenter, WIDTH, bold, window, border, vscroll_indicator, Elements, HEIGHT, hflow, frame, flex_grow, flexbox, gauge, paragraphAlignCenter, paragraphAlignJustify, paragraphAlignLeft, paragraphAlignRight, dim, spinner, Decorator, LESS_THAN, center, yflex, GREATER_THAN
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::BlueLight, Color::RedLight, Color::Black, Color::Cyan, Color::CyanLight, Color::GrayDark, Color::GrayLight, Color::Green, Color::GreenLight, Color::Magenta, Color::MagentaLight, Color::Red, Color::White, Color::Yellow, Color::YellowLight, Color::Default
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
using namespace ftxui;
int main(int argc, const char* argv[]) {
auto screen = ScreenInteractive::Fullscreen();
// ---------------------------------------------------------------------------
// HTOP
// ---------------------------------------------------------------------------
int shift = 0;
auto my_graph = [&shift](int width, int height) {
@@ -92,6 +98,10 @@ int main(int argc, const char* argv[]) {
flex | border;
});
// ---------------------------------------------------------------------------
// Compiler
// ---------------------------------------------------------------------------
const std::vector<std::string> compiler_entries = {
"gcc",
"clang",
@@ -248,6 +258,9 @@ int main(int argc, const char* argv[]) {
flex_grow | border;
});
// ---------------------------------------------------------------------------
// Spiner
// ---------------------------------------------------------------------------
auto spinner_tab_renderer = Renderer([&] {
Elements entries;
for (int i = 0; i < 22; ++i) {
@@ -257,6 +270,9 @@ int main(int argc, const char* argv[]) {
return hflow(std::move(entries)) | border;
});
// ---------------------------------------------------------------------------
// Colors
// ---------------------------------------------------------------------------
auto color_tab_renderer = Renderer([] {
return hbox({
vbox({
@@ -301,6 +317,9 @@ int main(int argc, const char* argv[]) {
hcenter | border;
});
// ---------------------------------------------------------------------------
// Gauges
// ---------------------------------------------------------------------------
auto render_gauge = [&shift](int delta) {
float progress = (shift + delta) % 1000 / 1000.f;
return hbox({
@@ -333,9 +352,83 @@ int main(int argc, const char* argv[]) {
border;
});
// ---------------------------------------------------------------------------
// Paragraph
// ---------------------------------------------------------------------------
auto make_box = [](size_t dimx, size_t dimy) {
std::string title = std::to_string(dimx) + "x" + std::to_string(dimy);
return window(text(title) | hcenter | bold,
text("content") | hcenter | dim) |
size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy);
};
auto paragraph_renderer_left = Renderer([&] {
auto title_style = bold | bgcolor(Color::Blue) | color(Color::Black);
std::string str =
"Lorem Ipsum is simply dummy text of the printing and typesetting "
"industry. Lorem Ipsum has been the industry's standard dummy text "
"ever since the 1500s, when an unknown printer took a galley of type "
"and scrambled it to make a type specimen book.";
return vbox({
// [ Left ]
text("Align left:") | title_style,
paragraphAlignLeft(str),
// [ Center ]
text("Align center:") | title_style,
paragraphAlignCenter(str),
// [ Right ]
text("Align right:") | title_style,
paragraphAlignRight(str),
// [ Justify]
text("Align justify:") | title_style,
paragraphAlignJustify(str),
// [ Side by side ]
text("Side by side:") | title_style,
hbox({
paragraph(str),
separator() | color(Color::Blue),
paragraph(str),
}),
// [ Misc ]
text("Elements with different size:") | title_style,
flexbox({
make_box(10, 5),
make_box(9, 4),
make_box(8, 4),
make_box(6, 3),
make_box(10, 5),
make_box(9, 4),
make_box(8, 4),
make_box(6, 3),
make_box(10, 5),
make_box(9, 4),
make_box(8, 4),
make_box(6, 3),
}),
}) |
vscroll_indicator | yframe | flex;
});
auto paragraph_renderer_right = Renderer([] {
return paragraph("<--- This vertical bar is resizable using the mouse") |
center;
});
int paragraph_renderer_split_position = Terminal::Size().dimx / 2;
auto paragraph_renderer_group =
ResizableSplitLeft(paragraph_renderer_left, paragraph_renderer_right,
&paragraph_renderer_split_position);
auto paragraph_renderer_group_renderer =
Renderer(paragraph_renderer_group,
[&] { return paragraph_renderer_group->Render() | border; });
// ---------------------------------------------------------------------------
// Tabs
// ---------------------------------------------------------------------------
int tab_index = 0;
std::vector<std::string> tab_entries = {
"htop", "color", "spinner", "gauge", "compiler",
"htop", "color", "spinner", "gauge", "compiler", "paragraph",
};
auto tab_selection = Toggle(&tab_entries, &tab_index);
auto tab_content = Container::Tab(
@@ -345,6 +438,7 @@ int main(int argc, const char* argv[]) {
spinner_tab_renderer,
gauge_component,
compiler_renderer,
paragraph_renderer_group_renderer,
},
&tab_index);

View File

@@ -1,12 +1,12 @@
#include <functional> // for function
#include <iostream> // for basic_ostream::operator<<, operator<<, endl, basic_ostream, basic_ostream<>::__ostream_type, cout, ostream
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include <memory> // for shared_ptr, __shared_ptr_access
#include <string> // for string, basic_string, allocator
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Radiobox, Renderer, Vertical
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for Element, operator|, border
using namespace ftxui;
Component Border(Component child) {
@@ -14,7 +14,6 @@ Component Border(Component child) {
}
int main(int argc, const char* argv[]) {
std::vector<std::string> entries = {
"entry 1",
"entry 2",

View File

@@ -0,0 +1,55 @@
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
int main() {
using namespace ftxui;
auto screen = ScreenInteractive::Fullscreen();
// When pressing this button, "screen.WithRestoredIO" will execute the
// temporarily uninstall the terminal hook and execute the provided callback
// function. This allow running the application in a non-interactive mode.
auto btn_run = Button("Execute with restored IO", screen.WithRestoredIO([] {
std::system("bash");
std::cout << "This is a child program using stdin/stdout." << std::endl;
for (int i = 0; i < 10; ++i) {
std::cout << "Please enter 10 strings (" << i << "/10)" << std::flush;
std::string input;
std::getline(std::cin, input);
}
std::system("bash");
}));
auto btn_quit = Button("Quit", screen.ExitLoopClosure());
auto layout = Container::Horizontal({
btn_run,
btn_quit,
});
auto renderer = Renderer(layout, [&] {
auto explanation = paragraph(
"After clicking this button, the ScreenInteractive will be "
"suspended and access to stdin/stdout will temporarilly be "
"restore for running a function.");
auto element = vbox({
explanation | borderEmpty,
hbox({
btn_run->Render(),
filler(),
btn_quit->Render(),
}),
});
element = element | borderEmpty | border | size(WIDTH, LESS_THAN, 80) |
size(HEIGHT, LESS_THAN, 20) | center;
return element;
});
screen.Loop(renderer);
return EXIT_SUCCESS;
}
// 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.

View File

@@ -3,9 +3,14 @@ set(DIRECTORY_LIB dom)
example(border)
example(border_style)
example(color_gallery)
example(color_info_palette256)
example(color_truecolor_HSV)
example(color_truecolor_RGB)
example(dbox)
example(canvas)
example(gauge)
example(graph)
example(gridbox)
example(hflow)
example(html_like)
example(package_manager)
@@ -17,13 +22,11 @@ example(spinner)
example(style_blink)
example(style_bold)
example(style_color)
example(color_truecolor_RGB)
example(color_truecolor_HSV)
example(color_info_palette256)
example(style_dim)
example(gridbox)
example(style_gallery)
example(style_inverted)
example(style_underlined)
example(table)
example(vbox_hbox)
example(vflow)
example(window)

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -1,10 +1,10 @@
#include <ftxui/dom/elements.hpp> // for text, operator|, vbox, border, Element, Fit, hbox
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <iostream>
#include <memory> // for allocator
#include <ftxui/dom/elements.hpp> // for operator|, text, Element, Fit, borderDouble, borderHeavy, borderLight, borderRounded, vbox
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for endl, cout, ostream
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

49
examples/dom/canvas.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include <stdio.h> // for getchar
#include <cmath> // for cos
#include <ftxui/dom/elements.hpp> // for Fit, canvas, operator|, border, Element
#include <ftxui/screen/screen.hpp> // for Pixel, Screen
#include <vector> // for vector, allocator
#include "ftxui/dom/canvas.hpp" // for Canvas
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Red, Color::Blue, Color::Green, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto c = Canvas(100, 100);
c.DrawText(0, 0, "This is a canvas", [](Pixel& p) {
p.foreground_color = Color::Red;
p.underlined = true;
});
// Triangle:
c.DrawPointLine(10, 10, 80, 10, Color::Red);
c.DrawPointLine(80, 10, 80, 40, Color::Blue);
c.DrawPointLine(80, 40, 10, 10, Color::Green);
// Circle, not filled and filled:
c.DrawPointCircle(30, 50, 20);
c.DrawPointCircleFilled(40, 40, 10);
// Plot a function:
std::vector<int> ys(100);
for (int x = 0; x < 100; x++)
ys[x] = 80 + 20 * cos(x * 0.2);
for (int x = 0; x < 99; x++)
c.DrawPointLine(x, ys[x], x + 1, ys[x + 1], Color::Red);
auto document = canvas(&c) | border;
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
screen.Print();
getchar();
return 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.

View File

@@ -5,8 +5,7 @@
#include <vector> // for vector, allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Palette256
#include "ftxui/screen/color.hpp" // for Color, Color::Palette256, ftxui
using namespace ftxui;
#include "./color_info_sorted_2d.ipp" // for ColorInfoSorted2D

View File

@@ -4,8 +4,7 @@
#include <utility> // for move
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/color.hpp" // for Color, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -4,8 +4,7 @@
#include <utility> // for move
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color
#include "ftxui/screen/color.hpp" // for Color, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -5,8 +5,8 @@
#include <string> // for allocator, operator+, char_traits, operator<<, string, to_string, basic_string
#include <thread> // for sleep_for
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -1,16 +1,15 @@
#include <chrono>
#include <cmath>
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <functional>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <chrono> // for operator""s, chrono_literals
#include <cmath> // for sin
#include <ftxui/dom/elements.hpp> // for operator|, graph, separator, color, Element, vbox, flex, inverted, Fit, hbox, size, border, GREATER_THAN, HEIGHT
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <functional> // for ref, reference_wrapper
#include <iostream> // for cout, ostream
#include <string> // for operator<<, string
#include <thread> // for sleep_for
#include <vector> // for vector
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/color.hpp"
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::BlueLight, Color::RedLight, Color::YellowLight, ftxui
class Graph {
public:

View File

@@ -1,10 +1,10 @@
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for filler, text, hbox, vbox
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for Elements, gridbox, Fit, operator|, text, border, Element
#include <ftxui/screen/screen.hpp> // for Screen
#include <memory> // for allocator, shared_ptr
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -1,11 +1,12 @@
#include <stddef.h> // for size_t
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for operator|, size, Element, text, hcenter, Decorator, Fit, WIDTH, hflow, window, EQUAL, GREATER_THAN, HEIGHT, bold, border, dim, LESS_THAN
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator, shared_ptr
#include <string> // for operator+, to_string, char_traits, string
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
@@ -44,6 +45,7 @@ int main(int argc, const char* argv[]) {
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
getchar();
return 0;
}

View File

@@ -44,7 +44,7 @@ int main(int argc, const char* argv[]) {
paragraph(" A spinner "), spinner(6, i / 10)) |
border;
auto screen = Screen::Create(Dimension::Full());
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
std::cout << reset_position;
screen.Print();

View File

@@ -9,9 +9,8 @@
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Green, Color::Red, Color::RedLight
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Green, Color::Red, Color::RedLight, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -1,35 +1,50 @@
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for operator|, hflow, paragraph, border, Element, hbox, flex, vbox
#include <chrono> // for operator""s, chrono_literals
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <string> // for allocator, string
#include <iostream> // for cout, ostream
#include <memory> // for allocator, shared_ptr
#include <string> // for string, operator<<
#include <thread> // for sleep_for
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/elements.hpp" // for hflow, paragraph, separator, hbox, vbox, filler, operator|, border, Element
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
using namespace std::chrono_literals;
int main(int argc, const char* argv[]) {
using namespace ftxui;
std::string p =
R"(In probability theory and statistics, Bayes' theorem (alternatively Bayes' law or Bayes' rule) describes the probability of an event, based on prior knowledge of conditions that might be related to the event. For example, if cancer is related to age, then, using Bayes' theorem, a person's age can be used to more accurately assess the probability that they have cancer, compared to the assessment of the probability of cancer made without knowledge of the person's age. One of the many applications of Bayes' theorem is Bayesian inference, a particular approach to statistical inference. When applied, the probabilities involved in Bayes' theorem may have different probability interpretations. With the Bayesian probability interpretation the theorem expresses how a subjective degree of belief should rationally change to account for availability of related evidence. Bayesian inference is fundamental to Bayesian statistics.)";
auto document = vbox({
hbox({
hflow(paragraph(p)) | border,
hflow(paragraph(p)) | border,
hflow(paragraph(p)) | border,
}) | flex,
hbox({
hflow(paragraph(p)) | border,
hflow(paragraph(p)) | border,
}) | flex,
hbox({
hflow(paragraph(p)) | border,
}) | flex,
});
std::string reset_position;
for (int i = 0;; ++i) {
auto document = vbox({
hflow(paragraph(p)),
separator(),
hflow(paragraph(p)),
separator(),
hbox({
hflow(paragraph(p)),
separator(),
hflow(paragraph(p)),
}),
}) |
border;
auto screen = Screen::Create(Dimension::Full(), Dimension::Full());
Render(screen, document);
screen.Print();
getchar();
document = vbox(filler(), document);
// auto screen = Screen::Create(Dimension::Fit(document));
// Render(screen, document);
// screen.Print();
// getchar();
auto screen = Screen::Create(Dimension::Full());
Render(screen, document);
std::cout << reset_position;
screen.Print();
reset_position = screen.ResetPosition();
std::this_thread::sleep_for(0.01s);
}
return 0;
}

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -4,8 +4,8 @@
#include <string> // for string, to_string
#include <utility> // for move
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -7,8 +7,8 @@
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -3,8 +3,7 @@
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Blue
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

View File

@@ -2,8 +2,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

65
examples/dom/table.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include <ftxui/dom/elements.hpp> // for color, Fit, LIGHT, align_right, bold, DOUBLE
#include <ftxui/dom/table.hpp> // for Table, TableSelection
#include <ftxui/screen/screen.hpp> // for Screen
#include <iostream> // for endl, cout, ostream
#include <string> // for basic_string, allocator, string
#include <vector> // for vector
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::Cyan, Color::White, ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto table = Table({
{"Version", "Marketing name", "Release date", "API level", "Runtime"},
{"2.3", "Gingerbread", "February 9 2011", "10", "Dalvik 1.4.0"},
{"4.0", "Ice Cream Sandwich", "October 19 2011", "15", "Dalvik"},
{"4.1", "Jelly Bean", "July 9 2012", "16", "Dalvik"},
{"4.2", "Jelly Bean", "November 13 2012", "17", "Dalvik"},
{"4.3", "Jelly Bean", "July 24 2013", "18", "Dalvik"},
{"4.4", "KitKat", "October 31 2013", "19", "Dalvik and ART"},
{"5.0", "Lollipop", "November 3 2014", "21", "ART"},
{"5.1", "Lollipop", "March 9 2015", "22", "ART"},
{"6.0", "Marshmallow", "October 5 2015", "23", "ART"},
{"7.0", "Nougat", "August 22 2016", "24", "ART"},
{"7.1", "Nougat", "October 4 2016", "25", "ART"},
{"8.0", "Oreo", "August 21 2017", "26", "ART"},
{"8.1", "Oreo", "December 5 2017", "27", "ART"},
{"9", "Pie", "August 6 2018", "28", "ART"},
{"10", "10", "September 3 2019", "29", "ART"},
{"11", "11", "September 8 2020", "30", "ART"},
});
table.SelectAll().Border(LIGHT);
// Add border around the first column.
table.SelectColumn(0).Border(LIGHT);
// Make first row bold with a double border.
table.SelectRow(0).Decorate(bold);
table.SelectRow(0).SeparatorVertical(LIGHT);
table.SelectRow(0).Border(DOUBLE);
// Align right the "Release date" column.
table.SelectColumn(2).DecorateCells(align_right);
// Select row from the second to the last.
auto content = table.SelectRows(1, -1);
// Alternate in between 3 colors.
content.DecorateCellsAlternateRow(color(Color::Blue), 3, 0);
content.DecorateCellsAlternateRow(color(Color::Cyan), 3, 1);
content.DecorateCellsAlternateRow(color(Color::White), 3, 2);
auto document = table.Render();
auto screen = Screen::Create(Dimension::Fit(document));
Render(screen, document);
screen.Print();
std::cout << std::endl;
return 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.

View File

@@ -3,8 +3,8 @@
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;

52
examples/dom/vflow.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include <stddef.h> // for size_t
#include <stdio.h> // for getchar
#include <ftxui/dom/elements.hpp> // for operator|, Element, size, text, hcenter, Fit, vflow, window, EQUAL, bold, border, dim, HEIGHT, WIDTH
#include <ftxui/screen/screen.hpp> // for Full, Screen
#include <memory> // for allocator, shared_ptr
#include <string> // for operator+, to_string, char_traits, string
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
int main(int argc, const char* argv[]) {
using namespace ftxui;
auto make_box = [](size_t dimx, size_t dimy) {
std::string title = std::to_string(dimx) + "x" + std::to_string(dimy);
return window(text(title) | hcenter | bold,
text("content") | hcenter | dim) |
size(WIDTH, EQUAL, dimx) | size(HEIGHT, EQUAL, dimy);
};
auto document = vflow({
make_box(7, 7),
make_box(7, 5),
make_box(5, 7),
make_box(10, 4),
make_box(10, 4),
make_box(10, 4),
make_box(10, 4),
make_box(11, 4),
make_box(11, 4),
make_box(11, 4),
make_box(11, 4),
make_box(12, 4),
make_box(12, 5),
make_box(12, 4),
make_box(13, 4),
make_box(13, 3),
make_box(13, 3),
make_box(10, 3),
}) |
border;
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
screen.Print();
getchar();
return 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.

View File

@@ -1,10 +1,9 @@
#include <ftxui/dom/elements.hpp>
#include <ftxui/screen/screen.hpp>
#include <vector>
#include <ftxui/dom/elements.hpp> // for operator|, color, Element, bgcolor, graph, border
#include <ftxui/screen/screen.hpp> // for Fixed, Screen
#include <vector> // for vector
#include "ftxui/dom/node.hpp"
#include "ftxui/screen/box.hpp"
#include "ftxui/screen/color.hpp"
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for Color, Color::DarkBlue, Color::Green, Color::Red, ftxui
int main(void) {
using namespace ftxui;

View File

@@ -71,7 +71,10 @@
postRun: [],
onRuntimeInitialized: () => {},
};
document.querySelector("#example_script").src = example + '.js';
const words = example.split('/')
words[1] = "ftxui_example_" + words[1] + ".js"
document.querySelector("#example_script").src = words.join('/');
</script>
<style>

View File

@@ -26,6 +26,15 @@ std::shared_ptr<T> Make(Args&&... args) {
return std::make_shared<T>(args...);
}
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);
} // namespace Container
Component Button(ConstStringRef label,
std::function<void()> on_click,
Ref<ButtonOption> = {});
@@ -56,16 +65,10 @@ Component Renderer(Component child, std::function<Element()>);
Component Renderer(std::function<Element()>);
Component Renderer(std::function<Element(bool /* focused */)>);
Component CatchEvent(Component child, std::function<bool(Event)>);
Component Maybe(Component, bool* show);
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);
} // namespace Container
Component Maybe(Component, const bool* show);
Component Collapsible(ConstStringRef label,
Component child,
Ref<bool> show = false);
} // namespace ftxui

View File

@@ -75,6 +75,7 @@ struct Event {
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
private:
friend ComponentBase;
friend ScreenInteractive;

View File

@@ -20,20 +20,28 @@ using Component = std::shared_ptr<ComponentBase>;
class ScreenInteractive : public Screen {
public:
using Callback = std::function<void()>;
static ScreenInteractive FixedSize(int dimx, int dimy);
static ScreenInteractive Fullscreen();
static ScreenInteractive FitComponent();
static ScreenInteractive TerminalOutput();
void Loop(Component);
std::function<void()> ExitLoopClosure();
Callback ExitLoopClosure();
void PostEvent(Event event);
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.
Callback WithRestoredIO(Callback);
private:
void Install();
void Uninstall();
void Main(Component component);
ScreenInteractive* suspended_screen_ = nullptr;
@@ -66,6 +74,7 @@ class ScreenInteractive : public Screen {
int cursor_y_ = 1;
bool mouse_captured = false;
bool previous_frame_resized_ = false;
};
} // namespace ftxui

View File

@@ -0,0 +1,132 @@
#ifndef FTXUI_DOM_CANVAS_HPP
#define FTXUI_DOM_CANVAS_HPP
#include <stddef.h> // 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
namespace ftxui {
struct Canvas {
public:
Canvas() {}
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 {
return static_cast<size_t>(xy.x * 1024 + xy.y);
}
};
int width_ = 0;
int height_ = 0;
std::unordered_map<XY, Cell, XYHash> storage_;
};
} // namespace ftxui
#endif // FTXUI_DOM_CANVAS_HPP
// 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.

View File

@@ -4,11 +4,14 @@
#include <functional>
#include <memory>
#include "ftxui/dom/canvas.hpp"
#include "ftxui/dom/flexbox_config.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;
@@ -17,7 +20,7 @@ using Elements = std::vector<Element>;
using Decorator = std::function<Element(Element)>;
using GraphFunction = std::function<std::vector<int>(int, int)>;
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED };
enum BorderStyle { LIGHT, HEAVY, DOUBLE, ROUNDED, EMPTY };
// Pipe elements into decorator togethers.
// For instance the next lines are equivalents:
@@ -34,20 +37,31 @@ Element separator(void);
Element separatorLight();
Element separatorHeavy();
Element separatorDouble();
Element separatorEmpty();
Element separatorStyled(BorderStyle);
Element separator(Pixel);
Element separatorCharacter(std::string);
Element gauge(float ratio);
Element border(Element);
Element borderLight(Element);
Element borderHeavy(Element);
Element borderDouble(Element);
Element borderRounded(Element);
Element borderEmpty(Element);
Decorator borderStyled(BorderStyle);
Decorator borderWith(Pixel);
Element window(Element title, Element content);
Element spinner(int charset_index, size_t image_index);
Elements paragraph(std::string text); // Use inside hflow(). Split by space.
Element paragraph(std::string text);
Element paragraphAlignLeft(std::string text);
Element paragraphAlignRight(std::string text);
Element paragraphAlignCenter(std::string text);
Element paragraphAlignJustify(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);
@@ -59,14 +73,19 @@ Decorator color(Color);
Decorator bgcolor(Color);
Element color(Color, Element);
Element bgcolor(Color, Element);
Decorator focusPosition(int x, int y);
Decorator focusPositionRelative(float x, float y);
// --- 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);
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

View File

@@ -0,0 +1,115 @@
#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 /* end of include guard: FTXUI_DOM_FLEXBOX_CONFIG_HPP */
// 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.

View File

@@ -35,6 +35,15 @@ class Node {
// 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_;

View File

@@ -28,7 +28,7 @@ struct Requirement {
} // namespace ftxui
#endif /* end of include guard: FTXUI_REQUIREMENT_HPP */
#endif /* end of include guard: FTXUI_DOM_REQUIREMENT_HPP */
// Copyright 2020 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in

View File

@@ -0,0 +1,96 @@
#ifndef FTXUI_DOM_TABLE
#define FTXUI_DOM_TABLE
#include <memory>
#include <string> // for string
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for BorderStyle, LIGHT, Element, 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_;
int input_dim_y_;
int dim_x_;
int dim_y_;
};
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 */
// 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.

View File

@@ -11,6 +11,8 @@ struct Box {
static Box Intersection(Box a, Box b);
bool Contain(int x, int y);
bool operator==(const Box& other) const;
bool operator!=(const Box& other) const;
};
} // namespace ftxui

View File

@@ -28,13 +28,15 @@ struct Pixel {
bool dim : 1;
bool inverted : 1;
bool underlined : 1;
bool automerge : 1;
Pixel()
: blink(false),
bold(false),
dim(false),
inverted(false),
underlined(false) {}
underlined(false),
automerge(false) {}
};
/// @brief Define how the Screen's dimensions should look like.
@@ -62,8 +64,8 @@ class Screen {
void Print();
// Get screen dimensions.
int dimx() { return dimx_; }
int dimy() { return dimy_; }
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);

View File

@@ -1,8 +1,9 @@
#ifndef FTXUI_SCREEN_STRING_HPP
#define FTXUI_SCREEN_STRING_HPP
#include <string> // for string, wstring, to_string
#include <vector> // for vector
#include <stddef.h> // for size_t
#include <string> // for string, wstring, to_string
#include <vector> // for vector
namespace ftxui {
std::string to_string(const std::wstring& s);
@@ -14,7 +15,20 @@ std::wstring to_wstring(T 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);
// If |input| was an array of glyphs, this returns the number of char to eat
// before reaching the glyph at index |glyph_index|.
int GlyphPosition(const std::string& input,
size_t glyph_index,
size_t start = 0);
// Returns the number of glyphs in |input|.
int GlyphCount(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

View File

@@ -9,6 +9,7 @@ struct Dimensions {
namespace Terminal {
Dimensions Size();
void SetFallbackSize(const Dimensions& fallbackSize);
enum Color {
Palette1,

View File

@@ -13,9 +13,9 @@ class ConstRef {
ConstRef() {}
ConstRef(T t) : owned_(t) {}
ConstRef(const T* t) : address_(t) {}
const T& operator*() { return address_ ? *address_ : owned_; }
const T& operator()() { return address_ ? *address_ : owned_; }
const T* operator->() { return address_ ? address_ : &owned_; }
const T& operator*() const { return address_ ? *address_ : owned_; }
const T& operator()() const { return address_ ? *address_ : owned_; }
const T* operator->() const { return address_ ? address_ : &owned_; }
private:
T owned_;
@@ -82,8 +82,10 @@ class ConstStringRef {
ConstStringRef(const wchar_t* ref) : ConstStringRef(std::wstring(ref)) {}
ConstStringRef(const char* ref)
: ConstStringRef(to_wstring(std::string(ref))) {}
const std::string& operator*() { return address_ ? *address_ : owned_; }
const std::string* operator->() { return address_ ? address_ : &owned_; }
const std::string& operator*() const { return address_ ? *address_ : owned_; }
const std::string* operator->() const {
return address_ ? address_ : &owned_;
}
private:
const std::string owned_;

View File

@@ -1,12 +1,17 @@
[
{ symbol: [ "char_traits", private, "<string>", public ] },
{ symbol: [ "ECHO", private, "<termios.h>", public ] },
{ symbol: [ "ICANON", private, "<termios.h>", public ] },
{ symbol: [ "TCSANOW", private, "<termios.h>", public ] },
{ symbol: [ "VMIN", private, "<termios.h>", public ] },
{ symbol: [ "VTIME", private, "<termios.h>", public ] },
{ symbol: [ "__shared_ptr_access", private, "<memory>", public ] },
{ symbol: [ "termios", private, "<termios.h>", public ] },
{ symbol: ["__alloc_traits<>:value_type", private, "<vector>", public ] },
{ include: ["<ext/alloc_traits.h>", private, "<vector>", public] },
{ include: ["<bits/termios-c_cc.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-c_lflag.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-struct.h>", "private", "<termios.h>", "public"]},
{ include: ["<bits/termios-tcflow.h>", "private", "<termios.h>", "public"]},
{ include: ["<ext/alloc_traits.h>", "private", "<vector>", "public"] },
{ symbol: [ "ftxui", "private", "", "public" ] },
{ symbol: [ "char_traits", "private", "<string>", "public" ] },
{ symbol: [ "ECHO", "private", "<termios.h>", "public" ] },
{ symbol: [ "ICANON", "private", "<termios.h>", "public" ] },
{ symbol: [ "TCSANOW", "private", "<termios.h>", "public" ] },
{ symbol: [ "VMIN", "private", "<termios.h>", "public" ] },
{ symbol: [ "VTIME", "private", "<termios.h>", "public" ] },
{ symbol: [ "__shared_ptr_access", "private", "<memory>", "public" ] },
{ symbol: [ "termios", "private", "<termios.h>", "public" ] },
{ symbol: ["__alloc_traits<>:value_type", "private", "<vector>", "public" ] },
]

View File

@@ -0,0 +1,52 @@
#include <string> // for string
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Checkbox, Maybe, Make, Vertical, Collapsible
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption
#include "ftxui/util/ref.hpp" // for Ref, ConstStringRef
namespace ftxui {
/// @brief A collapsible component. It display a checkbox with an arrow. Once
/// activated, the children is displayed.
/// @params label The label of the checkbox.
/// @params child The children to display.
/// @params show Hold the state about whether the children is displayed or not.
///
/// ### Example
/// ```cpp
/// auto component = Collapsible("Show details", details);
/// ```
///
/// ### Output
/// ```
///
/// ▼ Show details
/// <details component>
/// ```
Component Collapsible(ConstStringRef label, Component child, Ref<bool> show) {
class Impl : public ComponentBase {
public:
Impl(ConstStringRef label, Component child, Ref<bool> show)
: label_(label), show_(std::move(show)) {
CheckboxOption opt;
opt.style_checked = "";
opt.style_unchecked = "";
Add(Container::Vertical({
Checkbox(label_, show_.operator->(), opt),
Maybe(std::move(child), show_.operator->()),
}));
}
ConstStringRef label_;
Ref<bool> show_;
};
return Make<Impl>(label, std::move(child), std::move(show));
}
} // namespace ftxui
// 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.

View File

@@ -1,5 +1,5 @@
#include <stddef.h> // for size_t
#include <algorithm> // for find_if, max
#include <algorithm> // for find_if
#include <cassert> // for assert
#include <iterator> // for begin, end
#include <utility> // for move
@@ -7,7 +7,7 @@
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_base.hpp" // for ComponentBase, Components
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/component/screen_interactive.hpp" // for Component, ScreenInteractive
#include "ftxui/dom/elements.hpp" // for text, Element
@@ -64,8 +64,9 @@ void ComponentBase::Detach() {
[this](const Component& that) { //
return this == that.get();
});
parent_->children_.erase(it);
ComponentBase* parent = parent_;
parent_ = nullptr;
parent->children_.erase(it); // Might delete |this|.
}
/// @brief Remove all children.

View File

@@ -1,6 +1,5 @@
#include <iostream>
//#include "ftxui/component/event.hpp"
//#include "ftxui/component/receiver.hpp"
#include <cassert>
#include <vector>
#include "ftxui/component/component.hpp"
#include "ftxui/component/terminal_input_parser.hpp"
@@ -57,7 +56,11 @@ Component GeneratorComponent(const char*& data, size_t& size, int depth) {
if (depth <= 0)
return Button(GeneratorString(data, size), [] {});
switch (value % 18) {
constexpr int value_max = 19;
value = (value % value_max + value_max) % value_max;
switch (value) {
case 0:
return Button(GeneratorString(data, size), [] {});
case 1:
return Checkbox(GeneratorString(data, size), &g_bool);
case 2:
@@ -106,8 +109,12 @@ Component GeneratorComponent(const char*& data, size_t& size, int depth) {
return Maybe(GeneratorComponent(data, size, depth - 1), &g_bool);
case 17:
return Dropdown(&g_list, &g_int);
case 18:
return Collapsible(GeneratorString(data, size),
GeneratorComponent(data, size, depth - 1),
GeneratorBool(data, size));
default:
return Button(GeneratorString(data, size), [] {});
assert(false);
}
}

View File

@@ -6,7 +6,7 @@
#include "ftxui/component/component.hpp" // for Horizontal, Vertical, Tab
#include "ftxui/component/component_base.hpp" // for Components, Component, ComponentBase
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp
#include "ftxui/component/event.hpp" // for Event, Event::Tab, Event::TabReverse, Event::ArrowDown, Event::ArrowLeft, Event::ArrowRight, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp
#include "ftxui/dom/elements.hpp" // for text, Elements, operator|, reflect, Element, hbox, vbox
#include "ftxui/screen/box.hpp" // for Box
@@ -101,6 +101,22 @@ class VerticalContainer : public ContainerBase {
MoveSelector(-1);
if (event == Event::ArrowDown || event == Event::Character('j'))
MoveSelector(+1);
if (event == Event::PageUp) {
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
MoveSelector(-1);
}
if (event == Event::PageDown) {
for (int i = 0; i < box_.y_max - box_.y_min; ++i)
MoveSelector(1);
}
if (event == Event::Home) {
for (size_t i = 0; i < children_.size(); ++i)
MoveSelector(-1);
}
if (event == Event::End) {
for (size_t i = 0; i < children_.size(); ++i)
MoveSelector(1);
}
if (event == Event::Tab && children_.size())
MoveSelectorWrap(+1);
if (event == Event::TabReverse && children_.size())

View File

@@ -1,6 +1,13 @@
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/event.hpp"
#include <algorithm> // for max, min
#include <memory> // for __shared_ptr_access
#include <string> // for string
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Maybe, Checkbox, Make, Radiobox, Vertical, Dropdown
#include "ftxui/component/component_base.hpp" // for Component, ComponentBase
#include "ftxui/component/component_options.hpp" // for CheckboxOption
#include "ftxui/dom/elements.hpp" // for operator|, Element, border, filler, separator, size, vbox, frame, vscroll_indicator, HEIGHT, LESS_THAN
#include "ftxui/util/ref.hpp" // for ConstStringListRef
namespace ftxui {
@@ -22,6 +29,7 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
}
Element Render() override {
*selected_ = std::min((int)entries_.size() - 1, std::max(0, *selected_));
title_ = entries_[*selected_];
if (show_) {
return vbox({
@@ -52,3 +60,7 @@ Component Dropdown(ConstStringListRef entries, int* selected) {
}
} // namespace ftxui
// 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.

View File

@@ -1,8 +1,10 @@
#include <algorithm> // for max, min
#include <stddef.h> // for size_t
#include <algorithm> // for clamp, max, min
#include <functional> // for function
#include <memory> // for shared_ptr, allocator
#include <string> // for wstring, basic_string
#include <string> // for string, wstring
#include <utility> // for move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Input
@@ -12,20 +14,29 @@
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Custom, Event::Delete, Event::End, Event::Home, Event::Return
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/deprecated.hpp" // for text
#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, text, dim, flex, focus, inverted, hbox, size, frame, select, underlined, Decorator, EQUAL, HEIGHT
#include "ftxui/dom/elements.hpp" // for operator|, text, Element, reflect, inverted, Decorator, flex, focus, hbox, size, bold, dim, frame, select, EQUAL, HEIGHT
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/string.hpp" // for to_wstring, to_string
#include "ftxui/util/ref.hpp" // for WideStringRef, Ref, ConstStringRef, StringRef
#include "ftxui/screen/string.hpp" // for GlyphPosition, GlyphCount, to_string, CellToGlyphIndex, to_wstring
#include "ftxui/util/ref.hpp" // for StringRef, Ref, WideStringRef, ConstStringRef
namespace ftxui {
namespace {
std::string PasswordField(int size) {
std::string out;
out.reserve(2 * size);
while (size--)
out += "";
return out;
}
// An input box. The user can type text into it.
class WideInputBase : public ComponentBase {
class InputBase : public ComponentBase {
public:
WideInputBase(WideStringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
InputBase(StringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
: content_(content), placeholder_(placeholder), option_(option) {}
int cursor_position_internal_ = 0;
@@ -38,46 +49,52 @@ class WideInputBase : public ComponentBase {
// Component implementation:
Element Render() override {
std::wstring password_content;
std::string password_content;
if (option_->password())
password_content = std::wstring(content_->size(), U'');
std::wstring& content = option_->password() ? password_content : *content_;
password_content = PasswordField(content_->size());
std::string& content = option_->password() ? password_content : *content_;
cursor_position() =
std::max(0, std::min<int>(content.size(), cursor_position()));
auto main_decorator = flex | size(HEIGHT, EQUAL, 1);
int size = GlyphCount(content);
cursor_position() = std::max(0, std::min<int>(size, cursor_position()));
auto main_decorator = flex | ftxui::size(HEIGHT, EQUAL, 1);
bool is_focused = Focused();
// placeholder.
if (content.size() == 0) {
if (size == 0) {
bool hovered = hovered_;
Decorator decorator = dim | main_decorator;
if (is_focused)
return text(*placeholder_) | focus | dim | inverted | main_decorator |
reflect(box_);
else
return text(*placeholder_) | dim | main_decorator | reflect(box_);
decorator = decorator | focus | inverted;
if (hovered || is_focused)
decorator = decorator | inverted;
return text(*placeholder_) | decorator | reflect(box_);
}
// Not focused.
if (!is_focused)
return text(content) | main_decorator | reflect(box_);
if (!is_focused) {
if (hovered_)
return text(content) | main_decorator | inverted | reflect(box_);
else
return text(content) | main_decorator | reflect(box_);
}
std::wstring part_before_cursor = content.substr(0, cursor_position());
std::wstring part_at_cursor = cursor_position() < (int)content.size()
? content.substr(cursor_position(), 1)
: L" ";
std::wstring part_after_cursor = cursor_position() < (int)content.size() - 1
? content.substr(cursor_position() + 1)
: L"";
auto focused = is_focused ? focus : select;
// clang-format off
return
hbox(
text(part_before_cursor),
text(part_at_cursor) | underlined | focused | reflect(cursor_box_),
text(part_after_cursor)
) | flex | inverted | frame | main_decorator | reflect(box_);
// clang-format on
int index_before_cursor = GlyphPosition(content, cursor_position());
int index_after_cursor = GlyphPosition(content, 1, index_before_cursor);
std::string part_before_cursor = content.substr(0, index_before_cursor);
std::string part_at_cursor = " ";
if (cursor_position() < size) {
part_at_cursor = content.substr(index_before_cursor,
index_after_cursor - index_before_cursor);
}
std::string part_after_cursor = content.substr(index_after_cursor);
auto focused = (is_focused || hovered_) ? focus : select;
return hbox({
text(part_before_cursor),
text(part_at_cursor) | focused | inverted | reflect(cursor_box_),
text(part_after_cursor),
}) |
flex | frame | bold | main_decorator | reflect(box_);
}
bool OnEvent(Event event) override {
@@ -87,13 +104,15 @@ class WideInputBase : public ComponentBase {
if (event.is_mouse())
return OnMouseEvent(event);
std::wstring c;
std::string c;
// Backspace.
if (event == Event::Backspace) {
if (cursor_position() == 0)
return false;
content_->erase(cursor_position() - 1, 1);
size_t start = GlyphPosition(*content_, cursor_position() - 1);
size_t end = GlyphPosition(*content_, cursor_position());
content_->erase(start, end - start);
cursor_position()--;
option_->on_change();
return true;
@@ -103,7 +122,9 @@ class WideInputBase : public ComponentBase {
if (event == Event::Delete) {
if (cursor_position() == int(content_->size()))
return false;
content_->erase(cursor_position(), 1);
size_t start = GlyphPosition(*content_, cursor_position());
size_t end = GlyphPosition(*content_, cursor_position() + 1);
content_->erase(start, end - start);
option_->on_change();
return true;
}
@@ -135,13 +156,14 @@ class WideInputBase : public ComponentBase {
}
if (event == Event::End) {
cursor_position() = (int)content_->size();
cursor_position() = GlyphCount(*content_);
return true;
}
// Content
if (event.is_character()) {
content_->insert(cursor_position(), 1, to_wstring(event.character())[0]);
size_t start = GlyphPosition(*content_, cursor_position());
content_->insert(start, event.character());
cursor_position()++;
option_->on_change();
return true;
@@ -151,30 +173,47 @@ class WideInputBase : public ComponentBase {
private:
bool OnMouseEvent(Event event) {
if (!CaptureMouse(event))
hovered_ =
box_.Contain(event.mouse().x, event.mouse().y) && CaptureMouse(event);
if (!hovered_)
return false;
if (!box_.Contain(event.mouse().x, event.mouse().y))
if (event.mouse().button != Mouse::Left ||
event.mouse().motion != Mouse::Pressed) {
return false;
}
TakeFocus();
if (content_->size() == 0)
return true;
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
int new_cursor_position =
cursor_position() + event.mouse().x - cursor_box_.x_min;
new_cursor_position =
std::max(0, std::min<int>(content_->size(), new_cursor_position));
if (cursor_position() != new_cursor_position) {
cursor_position() = new_cursor_position;
option_->on_change();
auto mapping = CellToGlyphIndex(*content_);
int original_glyph = cursor_position();
original_glyph = std::clamp(original_glyph, 0, int(mapping.size()));
int original_cell = 0;
for (size_t i = 0; i < mapping.size(); i++) {
if (mapping[i] == original_glyph) {
original_cell = i;
break;
}
}
if (mapping[original_cell] != original_glyph)
original_cell = mapping.size();
int target_cell = original_cell + event.mouse().x - cursor_box_.x_min;
int target_glyph = target_cell < (int)mapping.size() ? mapping[target_cell]
: (int)mapping.size();
target_glyph = std::clamp(target_glyph, 0, GlyphCount(*content_));
if (cursor_position() != target_glyph) {
cursor_position() = target_glyph;
option_->on_change();
}
return true;
}
bool Focusable() const final { return true; }
WideStringRef content_;
bool hovered_ = false;
StringRef content_;
ConstStringRef placeholder_;
Box box_;
@@ -183,39 +222,37 @@ class WideInputBase : public ComponentBase {
};
// An input box. The user can type text into it.
// For convenience, the std::string version of Input simply wrap a
// WideInputBase.
// TODO(arthursonzogni): Provide an implementation handling std::string natively
// and adds better support for combining characters.
class InputBase : public WideInputBase {
// For convenience, the std::wstring version of Input simply wrap a
// InputBase.
class WideInputBase : public InputBase {
public:
InputBase(StringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
: WideInputBase(&wrapped_content_,
std::move(placeholder),
std::move(option)),
WideInputBase(WideStringRef content,
ConstStringRef placeholder,
Ref<InputOption> option)
: InputBase(&wrapped_content_, std::move(placeholder), std::move(option)),
content_(std::move(content)),
wrapped_content_(to_wstring(*content_)) {}
wrapped_content_(to_string(*content_)) {}
Element Render() override {
wrapped_content_ = to_wstring(*content_);
return WideInputBase::Render();
wrapped_content_ = to_string(*content_);
return InputBase::Render();
}
bool OnEvent(Event event) override {
wrapped_content_ = to_wstring(*content_);
if (WideInputBase::OnEvent(event)) {
*content_ = to_string(wrapped_content_);
wrapped_content_ = to_string(*content_);
if (InputBase::OnEvent(event)) {
*content_ = to_wstring(wrapped_content_);
return true;
}
return false;
}
StringRef content_;
std::wstring wrapped_content_;
WideStringRef content_;
std::string wrapped_content_;
};
} // namespace
/// @brief An input box for editing text.
/// @param content The editable content.
/// @param placeholder The text displayed when content is still empty.

View File

@@ -8,11 +8,12 @@
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/component_options.hpp" // for InputOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Backspace, Event::Delete, Event::End, Event::Home
#include "ftxui/dom/elements.hpp" // for Fit
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Button, Mouse::Left, Mouse::Motion, Mouse::Pressed
#include "ftxui/dom/elements.hpp" // for Fit
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/screen.hpp" // for Fixed, Screen, Pixel
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
@@ -226,6 +227,151 @@ TEST(InputTest, Backspace) {
EXPECT_EQ(option.cursor_position(), 0u);
}
TEST(InputTest, MouseClick) {
std::string content;
std::string placeholder;
auto option = InputOption();
option.cursor_position = 0;
auto input = Input(&content, &placeholder, &option);
input->OnEvent(Event::Character("a"));
input->OnEvent(Event::Character("b"));
input->OnEvent(Event::Character("c"));
input->OnEvent(Event::Character("d"));
EXPECT_EQ(option.cursor_position(), 4u);
auto render = [&] {
auto document = input->Render();
auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(1));
Render(screen, document);
};
render();
Mouse mouse;
mouse.button = Mouse::Button::Left;
mouse.motion = Mouse::Motion::Pressed;
mouse.y = 0;
mouse.shift = false;
mouse.meta = false;
mouse.control = false;
mouse.x = 0;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 2;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 2u);
mouse.x = 2;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 2u);
mouse.x = 1;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 1u);
mouse.x = 3;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 3u);
mouse.x = 4;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 4u);
mouse.x = 5;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 4u);
}
TEST(InputTest, MouseClickComplex) {
std::string content;
std::string placeholder;
auto option = InputOption();
option.cursor_position = 0;
auto input = Input(&content, &placeholder, &option);
input->OnEvent(Event::Character(""));
input->OnEvent(Event::Character(""));
input->OnEvent(Event::Character("a⃒"));
input->OnEvent(Event::Character(""));
EXPECT_EQ(option.cursor_position(), 4u);
auto render = [&] {
auto document = input->Render();
auto screen = Screen::Create(Dimension::Fixed(10), Dimension::Fixed(1));
Render(screen, document);
};
render();
Mouse mouse;
mouse.button = Mouse::Button::Left;
mouse.motion = Mouse::Motion::Pressed;
mouse.y = 0;
mouse.shift = false;
mouse.meta = false;
mouse.control = false;
mouse.x = 0;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 0;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 1;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 1;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 2;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 1u);
mouse.x = 2;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 1u);
mouse.x = 1;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 0u);
mouse.x = 4;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 2u);
mouse.x = 5;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 3u);
mouse.x = 6;
input->OnEvent(Event::Mouse("", mouse));
render();
EXPECT_EQ(option.cursor_position(), 4u);
}
// 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.

View File

@@ -1,13 +1,18 @@
#include "ftxui/component/component.hpp"
#include "ftxui/component/component_base.hpp"
#include "ftxui/component/event.hpp"
#include <memory> // for make_unique, __shared_ptr_access, __shared_ptr_access<>::element_type, shared_ptr
#include <utility> // for move
#include "ftxui/component/component.hpp" // for Make, Maybe
#include "ftxui/component/component_base.hpp" // for ComponentBase, Component
#include "ftxui/component/event.hpp" // for Event
#include "ftxui/dom/elements.hpp" // for Element
#include "ftxui/dom/node.hpp" // for Node
namespace ftxui {
Component Maybe(Component child, bool* show) {
Component Maybe(Component child, const bool* show) {
class Impl : public ComponentBase {
public:
Impl(bool* show): show_(show) {}
Impl(const bool* show) : show_(show) {}
private:
Element Render() override {
@@ -20,12 +25,16 @@ Component Maybe(Component child, bool* show) {
return *show_ && ComponentBase::OnEvent(event);
}
bool* show_;
const bool* show_;
};
auto maybe = Make<Impl>(show);
maybe->Add(std::move(child));
return maybe;
}
} // namespace ftxui
// 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.

View File

@@ -1,21 +1,21 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <algorithm> // for clamp, max
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for operator+, string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Menu
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
#include "ftxui/component/component.hpp" // for Make, Menu, MenuEntry
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption, MenuEntryOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Released, Mouse::WheelDown, Mouse::WheelUp, Mouse::None
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, text, vbox, Elements, focus, nothing, select
#include "ftxui/dom/elements.hpp" // for operator|, Element, reflect, text, nothing, select, vbox, Elements, focus
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for Ref
#include "ftxui/screen/util.hpp"
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef, ConstStringRef
namespace ftxui {
@@ -27,12 +27,12 @@ class MenuBase : public ComponentBase {
: entries_(entries), selected_(selected), option_(option) {}
Element Render() override {
Clamp();
Elements elements;
bool is_menu_focused = Focused();
boxes_.resize(entries_.size());
for (size_t i = 0; i < entries_.size(); ++i) {
bool is_focused = (focused_entry() == int(i)) && is_menu_focused;
bool is_selected = (*selected_ == int(i));
for (int i = 0; i < size(); ++i) {
bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (*selected_ == i);
auto style = is_selected ? (is_focused ? option_->style_selected_focused
: option_->style_selected)
@@ -49,6 +49,7 @@ class MenuBase : public ComponentBase {
}
bool OnEvent(Event event) override {
Clamp();
if (!CaptureMouse(event))
return false;
@@ -61,12 +62,20 @@ class MenuBase : public ComponentBase {
(*selected_)--;
if (event == Event::ArrowDown || event == Event::Character('j'))
(*selected_)++;
if (event == Event::Tab && entries_.size())
*selected_ = (*selected_ + 1) % entries_.size();
if (event == Event::TabReverse && entries_.size())
*selected_ = (*selected_ + entries_.size() - 1) % entries_.size();
if (event == Event::PageUp)
(*selected_) -= box_.y_max - box_.y_min;
if (event == Event::PageDown)
(*selected_) += box_.y_max - box_.y_min;
if (event == Event::Home)
(*selected_) = 0;
if (event == Event::End)
(*selected_) = size() - 1;
if (event == Event::Tab && size())
*selected_ = (*selected_ + 1) % size();
if (event == Event::TabReverse && size())
*selected_ = (*selected_ + size() - 1) % size();
*selected_ = std::max(0, std::min(int(entries_.size()) - 1, *selected_));
*selected_ = util::clamp(*selected_, 0, size() - 1);
if (*selected_ != old_selected) {
focused_entry() = *selected_;
@@ -95,7 +104,7 @@ class MenuBase : public ComponentBase {
}
if (!CaptureMouse(event))
return false;
for (int i = 0; i < int(boxes_.size()); ++i) {
for (int i = 0; i < size(); ++i) {
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
continue;
@@ -123,15 +132,22 @@ class MenuBase : public ComponentBase {
if (event.mouse().button == Mouse::WheelDown)
(*selected_)++;
*selected_ = std::max(0, std::min(int(entries_.size()) - 1, *selected_));
*selected_ = util::clamp(*selected_, 0, size() - 1);
if (*selected_ != old_selected)
option_->on_change();
return true;
}
void Clamp() {
boxes_.resize(size());
*selected_ = util::clamp(*selected_, 0, size() - 1);
focused_entry() = util::clamp(focused_entry(), 0, size() - 1);
}
bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); }
int size() const { return entries_.size(); }
protected:
ConstStringListRef entries_;

View File

@@ -0,0 +1,48 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for allocator, __shared_ptr_access, shared_ptr
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Menu
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for MenuOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::Return
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
TEST(MenuTest, RemoveEntries) {
int focused_entry = 0;
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
MenuOption option;
option.focused_entry = &focused_entry;
auto menu = Menu(&entries, &selected, option);
EXPECT_EQ(selected, 0);
EXPECT_EQ(focused_entry, 0);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::ArrowDown);
menu->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
entries.resize(2);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
(void)menu->Render();
EXPECT_EQ(selected, 1);
EXPECT_EQ(focused_entry, 1);
}
// 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.

View File

@@ -1,5 +1,4 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <algorithm> // for clamp, max
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <string> // for string
@@ -10,12 +9,13 @@
#include "ftxui/component/component.hpp" // for Make, Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for RadioboxOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/component/event.hpp" // for Event, Event::ArrowDown, Event::ArrowUp, Event::End, Event::Home, Event::PageDown, Event::PageUp, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::WheelDown, Mouse::WheelUp, Mouse::Left, Mouse::Released
#include "ftxui/component/screen_interactive.hpp" // for Component
#include "ftxui/dom/elements.hpp" // for Element, operator|, text, hbox, reflect, vbox, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for Ref
#include "ftxui/dom/elements.hpp" // for operator|, reflect, text, Element, hbox, vbox, Elements, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/util.hpp"
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef
namespace ftxui {
@@ -42,12 +42,12 @@ class RadioboxBase : public ComponentBase {
private:
Element Render() override {
Clamp();
Elements elements;
bool is_menu_focused = Focused();
boxes_.resize(entries_.size());
for (size_t i = 0; i < entries_.size(); ++i) {
bool is_focused = (focused_entry() == int(i)) && is_menu_focused;
bool is_selected = (hovered_ == int(i));
for (int i = 0; i < size(); ++i) {
bool is_focused = (focused_entry() == i) && is_menu_focused;
bool is_selected = (hovered_ == i);
auto style = is_selected ? (is_focused ? option_->style_selected_focused
: option_->style_selected)
@@ -57,9 +57,8 @@ class RadioboxBase : public ComponentBase {
: is_menu_focused ? focus
: select;
const std::string& symbol = *selected_ == int(i)
? option_->style_checked
: option_->style_unchecked;
const std::string& symbol =
*selected_ == i ? option_->style_checked : option_->style_unchecked;
elements.push_back(hbox(text(symbol), text(entries_[i]) | style) |
focus_management | reflect(boxes_[i]));
}
@@ -67,6 +66,7 @@ class RadioboxBase : public ComponentBase {
}
bool OnEvent(Event event) override {
Clamp();
if (!CaptureMouse(event))
return false;
@@ -79,12 +79,20 @@ class RadioboxBase : public ComponentBase {
(hovered_)--;
if (event == Event::ArrowDown || event == Event::Character('j'))
(hovered_)++;
if (event == Event::Tab && entries_.size())
hovered_ = (hovered_ + 1) % entries_.size();
if (event == Event::TabReverse && entries_.size())
hovered_ = (hovered_ + entries_.size() - 1) % entries_.size();
if (event == Event::PageUp)
(hovered_) -= box_.y_max - box_.y_min;
if (event == Event::PageDown)
(hovered_) += box_.y_max - box_.y_min;
if (event == Event::Home)
(hovered_) = 0;
if (event == Event::End)
(hovered_) = size() - 1;
if (event == Event::Tab && size())
hovered_ = (hovered_ + 1) % size();
if (event == Event::TabReverse && size())
hovered_ = (hovered_ + size() - 1) % size();
hovered_ = std::max(0, std::min(int(entries_.size()) - 1, hovered_));
hovered_ = util::clamp(hovered_, 0, size() - 1);
if (hovered_ != old_hovered) {
focused_entry() = hovered_;
@@ -108,7 +116,7 @@ class RadioboxBase : public ComponentBase {
return OnMouseWheel(event);
}
for (int i = 0; i < int(boxes_.size()); ++i) {
for (int i = 0; i < size(); ++i) {
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
continue;
@@ -138,7 +146,7 @@ class RadioboxBase : public ComponentBase {
if (event.mouse().button == Mouse::WheelDown)
(hovered_)++;
hovered_ = std::max(0, std::min(int(entries_.size()) - 1, hovered_));
hovered_ = util::clamp(hovered_, 0, size() - 1);
if (hovered_ != old_hovered)
option_->on_change();
@@ -146,8 +154,16 @@ class RadioboxBase : public ComponentBase {
return true;
}
void Clamp() {
boxes_.resize(size());
*selected_ = util::clamp(*selected_, 0, size() - 1);
focused_entry() = util::clamp(focused_entry(), 0, size() - 1);
hovered_ = util::clamp(hovered_, 0, size() - 1);
}
bool Focusable() const final { return entries_.size(); }
int& focused_entry() { return option_->focused_entry(); }
int size() const { return entries_.size(); }
ConstStringListRef entries_;
int* selected_;

View File

@@ -4,10 +4,12 @@
#include <string> // for string, basic_string
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/captured_mouse.hpp" // for ftxui
#include "ftxui/component/component.hpp" // for Radiobox
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for RadioboxOption
#include "ftxui/component/event.hpp" // for Event, Event::Return, Event::ArrowDown, Event::ArrowUp, Event::Tab, Event::TabReverse
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
using namespace ftxui;
@@ -114,6 +116,35 @@ TEST(RadioboxTest, Navigation) {
radiobox->OnEvent(Event::Return);
}
TEST(RadioboxTest, RemoveEntries) {
int focused_entry = 0;
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
RadioboxOption option;
option.focused_entry = &focused_entry;
auto radiobox = Radiobox(&entries, &selected, option);
EXPECT_EQ(selected, 0);
EXPECT_EQ(focused_entry, 0);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::ArrowDown);
radiobox->OnEvent(Event::Return);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
entries.resize(2);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
(void)radiobox->Render();
EXPECT_EQ(selected, 1);
EXPECT_EQ(focused_entry, 1);
}
// 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.

View File

@@ -6,7 +6,7 @@
#include <iostream> // for cout, ostream, basic_ostream, operator<<, endl, flush
#include <stack> // for stack
#include <thread> // for thread
#include <utility> // for move
#include <utility> // for swap, move
#include <vector> // for vector
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse, CapturedMouseInterface
@@ -18,7 +18,7 @@
#include "ftxui/component/terminal_input_parser.hpp" // for TerminalInputParser
#include "ftxui/dom/node.hpp" // for Node, Render
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/terminal.hpp" // for Terminal::Dimensions, Terminal
#include "ftxui/screen/terminal.hpp" // for Size, Dimensions
#if defined(_WIN32)
#define DEFINE_CONSOLEV2_PROPERTIES
@@ -32,8 +32,8 @@
#endif
#else
#include <sys/select.h> // for select, FD_ISSET, FD_SET, FD_ZERO, fd_set
#include <termios.h> // for tcsetattr, termios, tcgetattr, TCSANOW, cc_t, ECHO, ICANON, VMIN, VTIME
#include <unistd.h> // for STDIN_FILENO, read
#include <termios.h> // for tcsetattr, tcgetattr, cc_t
#include <unistd.h> // for STDIN_FILENO, read
#endif
// Quick exit is missing in standard CLang headers
@@ -200,7 +200,7 @@ const std::string DeviceStatusReport(DSRMode ps) {
}
using SignalHandler = void(int);
std::stack<std::function<void()>> on_exit_functions;
std::stack<ScreenInteractive::Callback> on_exit_functions;
void OnExit(int signal) {
(void)signal;
while (!on_exit_functions.empty()) {
@@ -211,10 +211,10 @@ void OnExit(int signal) {
auto install_signal_handler = [](int sig, SignalHandler handler) {
auto old_signal_handler = std::signal(sig, handler);
on_exit_functions.push([&]() { std::signal(sig, old_signal_handler); });
on_exit_functions.push([&] { std::signal(sig, old_signal_handler); });
};
std::function<void()> on_resize = [] {};
ScreenInteractive::Callback on_resize = [] {};
void OnResize(int /* signal */) {
on_resize();
}
@@ -230,6 +230,8 @@ class CapturedMouseImpl : public CapturedMouseInterface {
} // namespace
ScreenInteractive* g_active_screen = nullptr;
ScreenInteractive::ScreenInteractive(int dimx,
int dimy,
Dimension dimension,
@@ -275,7 +277,6 @@ CapturedMouse ScreenInteractive::CaptureMouse() {
}
void ScreenInteractive::Loop(Component component) {
static ScreenInteractive* g_active_screen = nullptr;
// Suspend previously active screen:
if (g_active_screen) {
@@ -311,7 +312,22 @@ void ScreenInteractive::Loop(Component component) {
}
}
/// @brief Decorate a function. It executes the same way, but with the currently
/// active screen terminal hooks temporarilly uninstalled during its execution.
/// @param fn The function to decorate.
ScreenInteractive::Callback ScreenInteractive::WithRestoredIO(Callback fn) {
return [this, fn] {
Uninstall();
fn();
Install();
};
}
void ScreenInteractive::Install() {
// After uninstalling the new configuration, flush it to the terminal to
// ensure it is fully applied:
on_exit_functions.push([] { Flush(); });
on_exit_functions.push([this] { ExitLoopClosure()(); });
// Install signal handlers to restore the terminal state on exit. The default
@@ -370,12 +386,6 @@ void ScreenInteractive::Install() {
install_signal_handler(SIGWINCH, OnResize);
#endif
// Commit state:
auto flush = [&] {
Flush();
on_exit_functions.push([] { Flush(); });
};
auto enable = [&](std::vector<DECMode> parameters) {
std::cout << Set(parameters);
on_exit_functions.push([=] { std::cout << Reset(parameters); });
@@ -404,7 +414,9 @@ void ScreenInteractive::Install() {
DECMode::kMouseSgrExtMode,
});
flush();
// After installing the new configuration, flush it to the terminal to ensure
// it is fully applied:
Flush();
quit_ = false;
event_listener_ =
@@ -488,22 +500,24 @@ void ScreenInteractive::Draw(Component component) {
// Periodically request the terminal emulator the frame position relative to
// the screen. This is useful for converting mouse position reported in
// screen's coordinates to frame's coordinates.
static constexpr int cursor_refresh_rate =
#if defined(FTXUI_MICROSOFT_TERMINAL_FALLBACK)
// Microsoft's terminal suffers from a [bug]. When reporting the cursor
// position, several output sequences are mixed together into garbage.
// This causes FTXUI user to see some "1;1;R" sequences into the Input
// component. See [issue]. Solution is to request cursor position less
// often. [bug]: https://github.com/microsoft/terminal/pull/7583 [issue]:
// https://github.com/ArthurSonzogni/FTXUI/issues/136
150;
#else
20;
#endif
// Microsoft's terminal suffers from a [bug]. When reporting the cursor
// position, several output sequences are mixed together into garbage.
// This causes FTXUI user to see some "1;1;R" sequences into the Input
// component. See [issue]. Solution is to request cursor position less
// often. [bug]: https://github.com/microsoft/terminal/pull/7583 [issue]:
// https://github.com/ArthurSonzogni/FTXUI/issues/136
static int i = -3;
++i;
if (!use_alternative_screen_ && (i % cursor_refresh_rate == 0))
if (!use_alternative_screen_ && (i % 150 == 0))
std::cout << DeviceStatusReport(DSRMode::kCursor);
#else
static int i = -3;
++i;
if (!use_alternative_screen_ && (previous_frame_resized_ || i % 40 == 0))
std::cout << DeviceStatusReport(DSRMode::kCursor);
#endif
previous_frame_resized_ = resized;
Render(*this, document);
@@ -524,8 +538,8 @@ void ScreenInteractive::Draw(Component component) {
}
}
std::function<void()> ScreenInteractive::ExitLoopClosure() {
return [this]() {
ScreenInteractive::Callback ScreenInteractive::ExitLoopClosure() {
return [this] {
quit_ = true;
event_sender_.reset();
};

View File

@@ -1,26 +0,0 @@
#include "ftxui/component/component_base.hpp"
Component Maybe(Component child, bool* show) {
class Impl : public ComponentBase {
public:
Impl(Component child, bool* show) : ComponentBase(child), show_(show) {}
private:
Element Render() override {
if (*show_)
return ComponentBase::Render();
else
return text("");
}
bool Focusable() const override {
return *show_ && ComponentBase::Focusable();
}
bool OnEvent(Event event) override {
if (*show_)
return false return ComponentBase::OnEvent(event);
}
bool* show_;
};
return Make<Impl>(std::move(child), show);
}

View File

@@ -1,5 +1,4 @@
#include <stddef.h> // for size_t
#include <algorithm> // for max, min
#include <algorithm> // for clamp, max
#include <functional> // for function
#include <memory> // for shared_ptr, allocator_traits<>::value_type
#include <utility> // for move
@@ -13,7 +12,8 @@
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed
#include "ftxui/dom/elements.hpp" // for operator|, Element, Elements, hbox, reflect, separator, text, focus, nothing, select
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/util/ref.hpp" // for ConstStringListRef, Ref
#include "ftxui/screen/util.hpp"
#include "ftxui/util/ref.hpp" // for Ref, ConstStringListRef
namespace ftxui {
@@ -30,16 +30,16 @@ class ToggleBase : public ComponentBase {
private:
Element Render() override {
Clamp();
Elements children;
bool is_toggle_focused = Focused();
boxes_.resize(entries_.size());
for (size_t i = 0; i < entries_.size(); ++i) {
for (int i = 0; i < size(); ++i) {
// Separator.
if (i != 0)
children.push_back(separator());
bool is_focused = (focused_entry() == int(i)) && is_toggle_focused;
bool is_selected = (*selected_ == int(i));
bool is_focused = (focused_entry() == i) && is_toggle_focused;
bool is_selected = (*selected_ == i);
auto style = is_selected ? (is_focused ? option_->style_selected_focused
: option_->style_selected)
@@ -55,6 +55,7 @@ class ToggleBase : public ComponentBase {
}
bool OnEvent(Event event) override {
Clamp();
if (event.is_mouse())
return OnMouseEvent(event);
@@ -63,12 +64,12 @@ class ToggleBase : public ComponentBase {
(*selected_)--;
if (event == Event::ArrowRight || event == Event::Character('l'))
(*selected_)++;
if (event == Event::Tab && entries_.size())
*selected_ = (*selected_ + 1) % entries_.size();
if (event == Event::TabReverse && entries_.size())
*selected_ = (*selected_ + entries_.size() - 1) % entries_.size();
if (event == Event::Tab && size())
*selected_ = (*selected_ + 1) % size();
if (event == Event::TabReverse && size())
*selected_ = (*selected_ + size() - 1) % size();
*selected_ = std::max(0, std::min(int(entries_.size()) - 1, *selected_));
*selected_ = util::clamp(*selected_, 0, size() - 1);
if (old_selected != *selected_) {
focused_entry() = *selected_;
@@ -87,7 +88,7 @@ class ToggleBase : public ComponentBase {
bool OnMouseEvent(Event event) {
if (!CaptureMouse(event))
return false;
for (int i = 0; i < int(boxes_.size()); ++i) {
for (int i = 0; i < size(); ++i) {
if (!boxes_[i].Contain(event.mouse().x, event.mouse().y))
continue;
@@ -106,8 +107,15 @@ class ToggleBase : public ComponentBase {
return false;
}
bool Focusable() const final { return entries_.size(); }
void Clamp() {
boxes_.resize(size());
*selected_ = util::clamp(*selected_, 0, size() - 1);
focused_entry() = util::clamp(focused_entry(), 0, size() - 1);
}
bool Focusable() const final { return size(); }
int& focused_entry() { return option_->focused_entry(); }
int size() const { return entries_.size(); }
ConstStringListRef entries_;
int* selected_ = 0;

View File

@@ -10,6 +10,7 @@
#include "ftxui/component/component_base.hpp" // for ComponentBase
#include "ftxui/component/component_options.hpp" // for ToggleOption
#include "ftxui/component/event.hpp" // for Event, Event::ArrowLeft, Event::ArrowRight, Event::Return, Event::Tab, Event::TabReverse
#include "ftxui/util/ref.hpp" // for Ref
#include "gtest/gtest_pred_impl.h" // for AssertionResult, EXPECT_EQ, Test, EXPECT_TRUE, EXPECT_FALSE, TEST
using namespace ftxui;
@@ -150,6 +151,34 @@ TEST(ToggleTest, OnEnter) {
EXPECT_EQ(counter, 7);
}
TEST(ToggleTest, RemoveEntries) {
int focused_entry = 0;
int selected = 0;
std::vector<std::string> entries = {"1", "2", "3"};
ToggleOption option;
option.focused_entry = &focused_entry;
auto toggle = Toggle(&entries, &selected, option);
EXPECT_EQ(selected, 0);
EXPECT_EQ(focused_entry, 0);
toggle->OnEvent(Event::ArrowRight);
toggle->OnEvent(Event::ArrowRight);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
entries.resize(2);
EXPECT_EQ(selected, 2);
EXPECT_EQ(focused_entry, 2);
(void)toggle->Render();
EXPECT_EQ(selected, 1);
EXPECT_EQ(focused_entry, 1);
}
// 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.

View File

@@ -2,7 +2,7 @@
#include "ftxui/dom/elements.hpp" // for separator, gauge, operator|, text, Element, blink, inverted, hbox, vbox, border
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen
using namespace ftxui;

View File

@@ -1,12 +1,12 @@
#include <algorithm> // for max
#include <iterator> // for begin, end
#include <memory> // for allocator, make_shared, __shared_ptr_access
#include <string> // for basic_string, string
#include <string> // for string, basic_string
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, Elements, border, borderWith, window
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/elements.hpp" // for unpack, Element, Decorator, BorderStyle, ROUNDED, Elements, DOUBLE, EMPTY, HEAVY, LIGHT, border, borderDouble, borderEmpty, borderHeavy, borderLight, borderRounded, borderStyled, borderWith, window
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
@@ -14,10 +14,11 @@
namespace ftxui {
static std::string simple_border_charset[6][6] = {
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
};
// For reference, here is the charset for normal border:
@@ -88,13 +89,22 @@ class Border : public Node {
screen.at(box_.x_max, box_.y_min) = charset[1];
screen.at(box_.x_min, box_.y_max) = charset[2];
screen.at(box_.x_max, box_.y_max) = charset[3];
for (float x = box_.x_min + 1; x < box_.x_max; ++x) {
screen.at(x, box_.y_min) = charset[4];
screen.at(x, box_.y_max) = charset[4];
Pixel& p1 = screen.PixelAt(x, box_.y_min);
Pixel& p2 = screen.PixelAt(x, box_.y_max);
p1.character = charset[4];
p2.character = charset[4];
p1.automerge = true;
p2.automerge = true;
}
for (float y = box_.y_min + 1; y < box_.y_max; ++y) {
screen.at(box_.x_min, y) = charset[5];
screen.at(box_.x_max, y) = charset[5];
Pixel& p3 = screen.PixelAt(box_.x_min, y);
Pixel& p4 = screen.PixelAt(box_.x_max, y);
p3.character = charset[5];
p4.character = charset[5];
p3.automerge = true;
p4.automerge = true;
}
// Draw title.
@@ -108,12 +118,20 @@ class Border : public Node {
screen.PixelAt(box_.x_min, box_.y_max) = charset_pixel[2];
screen.PixelAt(box_.x_max, box_.y_max) = charset_pixel[3];
for (float x = box_.x_min + 1; x < box_.x_max; ++x) {
screen.PixelAt(x, box_.y_min) = charset_pixel[4];
screen.PixelAt(x, box_.y_max) = charset_pixel[4];
Pixel& p1 = screen.PixelAt(x, box_.y_min);
Pixel& p2 = screen.PixelAt(x, box_.y_max);
p1 = charset_pixel[5];
p2 = charset_pixel[5];
p1.automerge = true;
p2.automerge = true;
}
for (float y = box_.y_min + 1; y < box_.y_max; ++y) {
screen.PixelAt(box_.x_min, y) = charset_pixel[5];
screen.PixelAt(box_.x_max, y) = charset_pixel[5];
Pixel& p3 = screen.PixelAt(box_.x_min, y);
Pixel& p4 = screen.PixelAt(box_.x_max, y);
p3 = charset_pixel[5];
p4 = charset_pixel[5];
p3.automerge = true;
p4.automerge = true;
}
}
};
@@ -124,6 +142,7 @@ class Border : public Node {
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderEmpty
/// @see borderRounded
///
/// Add a border around an element
@@ -174,6 +193,7 @@ Decorator borderStyled(BorderStyle style) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -192,9 +212,9 @@ Decorator borderStyled(BorderStyle style) {
/// ### Output
///
/// ```bash
/// ┌──────────────┐
/// │The element │
/// └──────────────┘
/// ┌──────────────┐
/// │The element │
/// └──────────────┘
/// ```
Element borderLight(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), LIGHT);
@@ -207,6 +227,7 @@ Element borderLight(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -225,9 +246,9 @@ Element borderLight(Element child) {
/// ### Output
///
/// ```bash
/// ┏━━━━━━━━━━━━━━┓
/// ┃The element ┃
/// ┗━━━━━━━━━━━━━━┛
/// ┏━━━━━━━━━━━━━━┓
/// ┃The element ┃
/// ┗━━━━━━━━━━━━━━┛
/// ```
Element borderHeavy(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), HEAVY);
@@ -240,6 +261,7 @@ Element borderHeavy(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -258,9 +280,9 @@ Element borderHeavy(Element child) {
/// ### Output
///
/// ```bash
/// ╔══════════════╗
/// ║The element ║
/// ╚══════════════╝
/// ╔══════════════╗
/// ║The element ║
/// ╚══════════════╝
/// ```
Element borderDouble(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), DOUBLE);
@@ -273,6 +295,7 @@ Element borderDouble(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -291,14 +314,48 @@ Element borderDouble(Element child) {
/// ### Output
///
/// ```bash
/// ╭──────────────╮
/// │The element │
/// ╰──────────────╯
/// ╭──────────────╮
/// │The element │
/// ╰──────────────╯
/// ```
Element borderRounded(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), ROUNDED);
}
/// @brief Draw an empty border around the element.
/// @ingroup dom
/// @see border
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
/// Add a border around an element
///
/// ### Example
///
/// ```cpp
/// // Use 'borderRounded' as a function...
/// Element document = borderRounded(text("The element"));
///
/// // ...Or as a 'pipe'.
/// Element document = text("The element") | borderRounded;
/// ```
///
/// ### Output
///
/// ```bash
///
/// The element
///
/// ```
Element borderEmpty(Element child) {
return std::make_shared<Border>(unpack(std::move(child)), EMPTY);
}
/// @brief Draw window with a title and a border around the element.
/// @param title The title of the window.
/// @param content The element to be wrapped.

View File

@@ -25,4 +25,4 @@ void Compute(std::vector<Element>* elements, int target_size);
// 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.
// the LICENSE file.line.

879
src/ftxui/dom/canvas.cpp Normal file
View File

@@ -0,0 +1,879 @@
#include "ftxui/dom/canvas.hpp"
#include <stdlib.h> // for abs
#include <algorithm> // for max, min
#include <cstdint> // for uint8_t
#include <map> // for allocator, map
#include <memory> // for make_shared
#include <utility> // for move, pair
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, canvas
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Pixel, Screen
#include "ftxui/screen/string.hpp" // for Utf8ToGlyphs
#include "ftxui/util/ref.hpp" // for ConstRef
namespace ftxui {
namespace {
// Base UTF8 pattern:
// 11100010 10100000 10000000 // empty
// Pattern for the individuel dots:
// ┌──────┬───────┐
// │dot1 │ dot4 │
// ├──────┼───────┤
// │dot2 │ dot5 │
// ├──────┼───────┤
// │dot3 │ dot6 │
// ├──────┼───────┤
// │dot0-1│ dot0-2│
// └──────┴───────┘
// 11100010 10100000 10000001 // dot1
// 11100010 10100000 10000010 // dot2
// 11100010 10100000 10000100 // dot3
// 11100010 10100001 10000000 // dot0-1
// 11100010 10100000 10001000 // dot4
// 11100010 10100000 10010000 // dot5
// 11100010 10100000 10100000 // dot6
// 11100010 10100010 10000000 // dot0-2
uint8_t g_map_braille[2][4][2] = {
{
{0b00000000, 0b00000001}, // dot1
{0b00000000, 0b00000010}, // dot2
{0b00000000, 0b00000100}, // dot3
{0b00000001, 0b00000000}, // dot0-1
},
{
{0b00000000, 0b00001000}, // dot4
{0b00000000, 0b00010000}, // dot5
{0b00000000, 0b00100000}, // dot6
{0b00000010, 0b00000000}, // dot0-2
},
};
std::vector<std::string> g_map_block = {
" ", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
};
const std::map<std::string, uint8_t> g_map_block_inversed = {
{" ", 0b0000}, {"", 0b0001}, {"", 0b0010}, {"", 0b0011},
{"", 0b0100}, {"", 0b0101}, {"", 0b0110}, {"", 0b0111},
{"", 0b1000}, {"", 0b1001}, {"", 0b1010}, {"", 0b1011},
{"", 0b1100}, {"", 0b1101}, {"", 0b1110}, {"", 0b1111},
};
} // namespace
/// @brief Constructor.
/// @param width the width of the canvas. A cell is a 2x8 braille dot.
/// @param height the height of the canvas. A cell is a 2x8 braille dot.
Canvas::Canvas(int width, int height)
: width_(width), height_(height), storage_(width_ * height_ / 8) {}
/// @brief Get the content of a cell.
/// @param x the x coordinate of the cell.
/// @param y the y coordinate of the cell.
Pixel Canvas::GetPixel(int x, int y) const {
auto it = storage_.find(XY{x, y});
return (it == storage_.end()) ? Pixel{} : it->second.content;
}
/// @brief Draw a braille dot.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
/// @param value whether the dot is filled or not.
void Canvas::DrawPoint(int x, int y, bool value) {
DrawPoint(x, y, value, [](Pixel&) {});
}
/// @brief Draw a braille dot.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
/// @param value whether the dot is filled or not.
/// @param color the color of the dot.
void Canvas::DrawPoint(int x, int y, bool value, const Color& color) {
DrawPoint(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a braille dot.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
/// @param value whether the dot is filled or not.
/// @param style the style of the cell.
void Canvas::DrawPoint(int x, int y, bool value, const Stylizer& style) {
Style(x, y, style);
if (value)
DrawPointOn(x, y);
else
DrawPointOff(x, y);
}
/// @brief Draw a braille dot.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
void Canvas::DrawPointOn(int x, int y) {
if (!IsIn(x, y))
return;
Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 bytes.
cell.type = CellType::kBraille;
}
cell.content.character[1] |= g_map_braille[x % 2][y % 4][0];
cell.content.character[2] |= g_map_braille[x % 2][y % 4][1];
}
/// @brief Erase a braille dot.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
void Canvas::DrawPointOff(int x, int y) {
if (!IsIn(x, y))
return;
Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 byt
cell.type = CellType::kBraille;
}
cell.content.character[1] &= ~(g_map_braille[x % 2][y % 4][0]);
cell.content.character[2] &= ~(g_map_braille[x % 2][y % 4][1]);
}
/// @brief Toggle a braille dot. A filled one will be erased, and the other will
/// be drawn.
/// @param x the x coordinate of the dot.
/// @param y the y coordinate of the dot.
void Canvas::DrawPointToggle(int x, int y) {
if (!IsIn(x, y))
return;
Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBraille) {
cell.content.character = ""; // 3 byt
cell.type = CellType::kBraille;
}
cell.content.character[1] ^= g_map_braille[x % 2][y % 4][0];
cell.content.character[2] ^= g_map_braille[x % 2][y % 4][1];
}
/// @brief Draw a line made of braille dots.
/// @param x1 the x coordinate of the first dot.
/// @param y1 the y coordinate of the first dot.
/// @param x2 the x coordinate of the second dot.
/// @param y2 the y coordinate of the second dot.
void Canvas::DrawPointLine(int x1, int y1, int x2, int y2) {
DrawPointLine(x1, y1, x2, y2, [](Pixel&) {});
}
/// @brief Draw a line made of braille dots.
/// @param x1 the x coordinate of the first dot.
/// @param y1 the y coordinate of the first dot.
/// @param x2 the x coordinate of the second dot.
/// @param y2 the y coordinate of the second dot.
/// @param color the color of the line.
void Canvas::DrawPointLine(int x1, int y1, int x2, int y2, const Color& color) {
DrawPointLine(x1, y1, x2, y2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a line made of braille dots.
/// @param x1 the x coordinate of the first dot.
/// @param y1 the y coordinate of the first dot.o
/// @param x2 the x coordinate of the second dot.
/// @param y2 the y coordinate of the second dot.
/// @param style the style of the line.
void Canvas::DrawPointLine(int x1,
int y1,
int x2,
int y2,
const Stylizer& style) {
const int dx = std::abs(x2 - x1);
const int dy = std::abs(y2 - y1);
const int sx = x1 < x2 ? 1 : -1;
const int sy = y1 < y2 ? 1 : -1;
const int length = std::max(dx, dy);
if (!IsIn(x1, y1) && !IsIn(x2, y2))
return;
if (dx + dx > width_ * height_)
return;
int error = dx - dy;
for (int i = 0; i < length; ++i) {
DrawPoint(x1, y1, true, style);
if (2 * error >= -dy) {
error -= dy;
x1 += sx;
}
if (2 * error <= dx) {
error += dx;
y1 += sy;
}
}
DrawPoint(x2, y2, true, style);
}
/// @brief Draw a circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
void Canvas::DrawPointCircle(int x, int y, int radius) {
DrawPointCircle(x, y, radius, [](Pixel&) {});
}
/// @brief Draw a circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param color the color of the circle.
void Canvas::DrawPointCircle(int x, int y, int radius, const Color& color) {
DrawPointCircle(x, y, radius,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param style the style of the circle.
void Canvas::DrawPointCircle(int x, int y, int radius, const Stylizer& style) {
DrawPointEllipse(x, y, radius, radius, style);
}
/// @brief Draw a filled circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
void Canvas::DrawPointCircleFilled(int x, int y, int radius) {
DrawPointCircleFilled(x, y, radius, [](Pixel&) {});
}
/// @brief Draw a filled circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param color the color of the circle.
void Canvas::DrawPointCircleFilled(int x,
int y,
int radius,
const Color& color) {
DrawPointCircleFilled(x, y, radius,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a filled circle made of braille dots.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param style the style of the circle.
void Canvas::DrawPointCircleFilled(int x,
int y,
int radius,
const Stylizer& style) {
DrawPointEllipseFilled(x, y, radius, radius, style);
}
/// @brief Draw an ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawPointEllipse(int x, int y, int r1, int r2) {
DrawPointEllipse(x, y, r1, r2, [](Pixel&) {});
}
/// @brief Draw an ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param color the color of the ellipse.
void Canvas::DrawPointEllipse(int x,
int y,
int r1,
int r2,
const Color& color) {
DrawPointEllipse(x, y, r1, r2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw an ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param style the style of the ellipse.
void Canvas::DrawPointEllipse(int x1,
int y1,
int r1,
int r2,
const Stylizer& s) {
int x = -r1;
int y = 0;
int e2 = r2;
int dx = (1 + 2 * x) * e2 * e2;
int dy = x * x;
int err = dx + dy;
do {
DrawPoint(x1 - x, y1 + y, true, s);
DrawPoint(x1 + x, y1 + y, true, s);
DrawPoint(x1 + x, y1 - y, true, s);
DrawPoint(x1 - x, y1 - y, true, s);
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * r2 * r2;
}
if (e2 <= dy) {
y++;
err += dy += 2 * r1 * r1;
}
} while (x <= 0);
while (y++ < r2) {
DrawPoint(x1, y1 + y, true, s);
DrawPoint(x1, y1 - y, true, s);
}
}
/// @brief Draw a filled ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawPointEllipseFilled(int x1, int y1, int r1, int r2) {
DrawPointEllipseFilled(x1, y1, r1, r2, [](Pixel&) {});
}
/// @brief Draw a filled ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param color the color of the ellipse.
void Canvas::DrawPointEllipseFilled(int x1,
int y1,
int r1,
int r2,
const Color& color) {
DrawPointEllipseFilled(x1, y1, r1, r2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a filled ellipse made of braille dots.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param style the style of the ellipse.
void Canvas::DrawPointEllipseFilled(int x1,
int y1,
int r1,
int r2,
const Stylizer& s) {
int x = -r1;
int y = 0;
int e2 = r2;
int dx = (1 + 2 * x) * e2 * e2;
int dy = x * x;
int err = dx + dy;
do {
for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawPoint(xx, y1 + y, true, s);
DrawPoint(xx, y1 - y, true, s);
}
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * (long)r2 * r2;
}
if (e2 <= dy) {
y++;
err += dy += 2 * (long)r1 * r1;
}
} while (x <= 0);
while (y++ < r2) {
for (int yy = y1 - y; yy <= y1 + y; ++yy) {
DrawPoint(x1, yy, true, s);
}
}
}
/// @brief Draw a block.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
/// @param value whether the block is filled or not.
void Canvas::DrawBlock(int x, int y, bool value) {
DrawBlock(x, y, value, [](Pixel&) {});
}
/// @brief Draw a block.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
/// @param value whether the block is filled or not.
/// @param color the color of the block.
void Canvas::DrawBlock(int x, int y, bool value, const Color& color) {
DrawBlock(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a block.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
/// @param value whether the block is filled or not.
/// @param style the style of the block.
void Canvas::DrawBlock(int x, int y, bool value, const Stylizer& style) {
Style(x, y, style);
if (value)
DrawBlockOn(x, y);
else
DrawBlockOff(x, y);
}
/// @brief Draw a block.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
void Canvas::DrawBlockOn(int x, int y) {
if (!IsIn(x, y))
return;
y /= 2;
Cell& cell = storage_[XY{x / 2, y / 2}];
if (cell.type != CellType::kBlock) {
cell.content.character = " ";
cell.type = CellType::kBlock;
}
int bit = (x % 2) * 2 + y % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value |= 1 << bit;
cell.content.character = g_map_block[value];
}
/// @brief Erase a block.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
void Canvas::DrawBlockOff(int x, int y) {
if (!IsIn(x, y))
return;
Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBlock) {
cell.content.character = " ";
cell.type = CellType::kBlock;
}
y /= 2;
int bit = (y % 2) * 2 + x % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value &= ~(1 << bit);
cell.content.character = g_map_block[value];
}
/// @brief Toggle a block. If it is filled, it will be erased. If it is empty,
/// it will be filled.
/// @param x the x coordinate of the block.
/// @param y the y coordinate of the block.
void Canvas::DrawBlockToggle(int x, int y) {
if (!IsIn(x, y))
return;
Cell& cell = storage_[XY{x / 2, y / 4}];
if (cell.type != CellType::kBlock) {
cell.content.character = " ";
cell.type = CellType::kBlock;
}
y /= 2;
int bit = (y % 2) * 2 + x % 2;
uint8_t value = g_map_block_inversed.at(cell.content.character);
value ^= 1 << bit;
cell.content.character = g_map_block[value];
}
/// @brief Draw a line made of block characters.
/// @param x1 the x coordinate of the first point of the line.
/// @param y1 the y coordinate of the first point of the line.
/// @param x2 the x coordinate of the second point of the line.
/// @param y2 the y coordinate of the second point of the line.
void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2) {
DrawBlockLine(x1, y1, x2, y2, [](Pixel&) {});
}
/// @brief Draw a line made of block characters.
/// @param x1 the x coordinate of the first point of the line.
/// @param y1 the y coordinate of the first point of the line.
/// @param x2 the x coordinate of the second point of the line.
/// @param y2 the y coordinate of the second point of the line.
/// @param color the color of the line.
void Canvas::DrawBlockLine(int x1, int y1, int x2, int y2, const Color& color) {
DrawBlockLine(x1, y1, x2, y2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a line made of block characters.
/// @param x1 the x coordinate of the first point of the line.
/// @param y1 the y coordinate of the first point of the line.
/// @param x2 the x coordinate of the second point of the line.
/// @param y2 the y coordinate of the second point of the line.
/// @param style the style of the line.
void Canvas::DrawBlockLine(int x1,
int y1,
int x2,
int y2,
const Stylizer& style) {
y1 /= 2;
y2 /= 2;
const int dx = std::abs(x2 - x1);
const int dy = std::abs(y2 - y1);
const int sx = x1 < x2 ? 1 : -1;
const int sy = y1 < y2 ? 1 : -1;
const int length = std::max(dx, dy);
if (!IsIn(x1, y1) && !IsIn(x2, y2))
return;
if (dx + dx > width_ * height_)
return;
int error = dx - dy;
for (int i = 0; i < length; ++i) {
DrawBlock(x1, y1 * 2, true, style);
if (2 * error >= -dy) {
error -= dy;
x1 += sx;
}
if (2 * error <= dx) {
error += dx;
y1 += sy;
}
}
DrawBlock(x2, y2 * 2, true, style);
}
/// @brief Draw a circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
void Canvas::DrawBlockCircle(int x, int y, int radius) {
DrawBlockCircle(x, y, radius, [](Pixel&) {});
}
/// @brief Draw a circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param color the color of the circle.
void Canvas::DrawBlockCircle(int x, int y, int radius, const Color& color) {
DrawBlockCircle(x, y, radius,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param style the style of the circle.
void Canvas::DrawBlockCircle(int x, int y, int radius, const Stylizer& style) {
DrawBlockEllipse(x, y, radius, radius, style);
}
/// @brief Draw a filled circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
void Canvas::DrawBlockCircleFilled(int x, int y, int radius) {
DrawBlockCircleFilled(x, y, radius, [](Pixel&) {});
}
/// @brief Draw a filled circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param color the color of the circle.
void Canvas::DrawBlockCircleFilled(int x,
int y,
int radius,
const Color& color) {
DrawBlockCircleFilled(x, y, radius,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a filled circle made of block characters.
/// @param x the x coordinate of the center of the circle.
/// @param y the y coordinate of the center of the circle.
/// @param radius the radius of the circle.
/// @param style the style of the circle.
void Canvas::DrawBlockCircleFilled(int x,
int y,
int radius,
const Stylizer& s) {
DrawBlockEllipseFilled(x, y, radius, radius, s);
}
/// @brief Draw an ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawBlockEllipse(int x, int y, int r1, int r2) {
DrawBlockEllipse(x, y, r1, r2, [](Pixel&) {});
}
/// @brief Draw an ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param color the color of the ellipse.
void Canvas::DrawBlockEllipse(int x,
int y,
int r1,
int r2,
const Color& color) {
DrawBlockEllipse(x, y, r1, r2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw an ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param style the style of the ellipse.
void Canvas::DrawBlockEllipse(int x1,
int y1,
int r1,
int r2,
const Stylizer& s) {
y1 /= 2;
r2 /= 2;
int x = -r1;
int y = 0;
int e2 = r2;
int dx = (1 + 2 * x) * e2 * e2;
int dy = x * x;
int err = dx + dy;
do {
DrawBlock(x1 - x, 2 * (y1 + y), true, s);
DrawBlock(x1 + x, 2 * (y1 + y), true, s);
DrawBlock(x1 + x, 2 * (y1 - y), true, s);
DrawBlock(x1 - x, 2 * (y1 - y), true, s);
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * r2 * r2;
}
if (e2 <= dy) {
y++;
err += dy += 2 * r1 * r1;
}
} while (x <= 0);
while (y++ < r2) {
DrawBlock(x1, 2 * (y1 + y), true, s);
DrawBlock(x1, 2 * (y1 - y), true, s);
}
}
/// @brief Draw a filled ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
void Canvas::DrawBlockEllipseFilled(int x, int y, int r1, int r2) {
DrawBlockEllipseFilled(x, y, r1, r2, [](Pixel&) {});
}
/// @brief Draw a filled ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param color the color of the ellipse.
void Canvas::DrawBlockEllipseFilled(int x,
int y,
int r1,
int r2,
const Color& color) {
DrawBlockEllipseFilled(x, y, r1, r2,
[color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a filled ellipse made of block characters.
/// @param x the x coordinate of the center of the ellipse.
/// @param y the y coordinate of the center of the ellipse.
/// @param r1 the radius of the ellipse along the x axis.
/// @param r2 the radius of the ellipse along the y axis.
/// @param style the style of the ellipse.
void Canvas::DrawBlockEllipseFilled(int x1,
int y1,
int r1,
int r2,
const Stylizer& s) {
y1 /= 2;
r2 /= 2;
int x = -r1;
int y = 0;
int e2 = r2;
int dx = (1 + 2 * x) * e2 * e2;
int dy = x * x;
int err = dx + dy;
do {
for (int xx = x1 + x; xx <= x1 - x; ++xx) {
DrawBlock(xx, 2 * (y1 + y), true, s);
DrawBlock(xx, 2 * (y1 - y), true, s);
}
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * r2 * r2;
}
if (e2 <= dy) {
y++;
err += dy += 2 * r1 * r1;
}
} while (x <= 0);
while (y++ < r2) {
for (int yy = y1 + y; yy <= y1 - y; ++yy) {
DrawBlock(x1, 2 * yy, true, s);
}
}
}
/// @brief Draw a piece of text.
/// @param x the x coordinate of the text.
/// @param y the y coordinate of the text.
/// @param value the text to draw.
void Canvas::DrawText(int x, int y, const std::string& value) {
DrawText(x, y, value, [](Pixel&) {});
}
/// @brief Draw a piece of text.
/// @param x the x coordinate of the text.
/// @param y the y coordinate of the text.
/// @param value the text to draw.
/// @param color the color of the text.
void Canvas::DrawText(int x,
int y,
const std::string& value,
const Color& color) {
DrawText(x, y, value, [color](Pixel& p) { p.foreground_color = color; });
}
/// @brief Draw a piece of text.
/// @param x the x coordinate of the text.
/// @param y the y coordinate of the text.
/// @param value the text to draw.
/// @param style the style of the text.
void Canvas::DrawText(int x,
int y,
const std::string& value,
const Stylizer& style) {
for (const auto& it : Utf8ToGlyphs(value)) {
if (!IsIn(x, y))
continue;
Cell& cell = storage_[XY{x / 2, y / 4}];
cell.type = CellType::kText;
cell.content.character = it;
style(cell.content);
x += 2;
}
}
/// @brief Modify a pixel at a given location.
/// @param style a function that modifies the pixel.
void Canvas::Style(int x, int y, const Stylizer& style) {
if (IsIn(x, y))
style(storage_[XY{x / 2, y / 4}].content);
}
namespace {
class CanvasNodeBase : public Node {
public:
CanvasNodeBase() {}
void Render(Screen& screen) override {
const Canvas& c = canvas();
int y_max = std::min(c.height() / 4, box_.y_max - box_.y_min + 1);
int x_max = std::min(c.width() / 2, box_.x_max - box_.x_min + 1);
for (int y = 0; y < y_max; ++y) {
for (int x = 0; x < x_max; ++x) {
screen.PixelAt(box_.x_min + x, box_.y_min + y) = c.GetPixel(x, y);
}
}
}
virtual const Canvas& canvas() = 0;
};
} // namespace
/// @brief Produce an element from a Canvas, or a reference to a Canvas.
Element canvas(ConstRef<Canvas> canvas) {
class Impl : public CanvasNodeBase {
public:
Impl(ConstRef<Canvas> canvas) : canvas_(canvas) {
requirement_.min_x = (canvas_->width() + 1) / 2;
requirement_.min_y = (canvas_->height() + 3) / 4;
}
const Canvas& canvas() final { return *canvas_; }
ConstRef<Canvas> canvas_;
};
return std::make_shared<Impl>(std::move(canvas));
}
/// @brief Produce an element drawing a canvas of requested size.
/// @param width the width of the canvas.
/// @param height the height of the canvas.
/// @param fn a function drawing the canvas.
Element canvas(int width, int height, std::function<void(Canvas&)> fn) {
class Impl : public CanvasNodeBase {
public:
Impl(int width, int height, std::function<void(Canvas&)> fn)
: width_(width), height_(height), fn_(std::move(fn)) {}
void ComputeRequirement() final {
requirement_.min_x = (width_ + 1) / 2;
requirement_.min_y = (height_ + 3) / 4;
}
void Render(Screen& screen) final {
int width = (box_.y_max - box_.y_min + 1) * 2;
int height = (box_.x_max - box_.x_min + 1) * 4;
canvas_ = Canvas(width, height);
fn_(canvas_);
CanvasNodeBase::Render(screen);
}
const Canvas& canvas() final { return canvas_; }
Canvas canvas_;
int width_;
int height_;
std::function<void(Canvas&)> fn_;
};
return std::make_shared<Impl>(width, height, std::move(fn));
}
/// @brief Produce an element drawing a canvas.
/// @param fn a function drawing the canvas.
Element canvas(std::function<void(Canvas&)> fn) {
return canvas(12, 12, std::move(fn));
}
} // namespace ftxui
// 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.

View File

@@ -10,7 +10,7 @@ namespace ftxui {
/// @return The centered element.
/// @ingroup dom
Element hcenter(Element child) {
return hbox(filler(), std::move(child), filler()) | xflex_grow;
return hbox(filler(), std::move(child), filler());
}
/// @brief Center an element vertically.
@@ -18,7 +18,7 @@ Element hcenter(Element child) {
/// @return The centered element.
/// @ingroup dom
Element vcenter(Element child) {
return vbox(filler(), std::move(child), filler()) | yflex_grow;
return vbox(filler(), std::move(child), filler());
}
/// @brief Center an element horizontally and vertically.
@@ -26,7 +26,7 @@ Element vcenter(Element child) {
/// @return The centered element.
/// @ingroup dom
Element center(Element child) {
return hcenter(vcenter(std::move(child))) | flex_grow;
return hcenter(vcenter(std::move(child)));
}
/// @brief Align an element on the right side.
@@ -34,7 +34,7 @@ Element center(Element child) {
/// @return The right aligned element.
/// @ingroup dom
Element align_right(Element child) {
return hbox(filler(), std::move(child)) | flex_grow;
return hbox(filler(), std::move(child));
}
} // namespace ftxui

View File

@@ -1,9 +1,9 @@
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, filler, flex, flex_grow, flex_shrink, notflex, xflex, xflex_grow, xflex_shrink, yflex, yflex_grow, yflex_shrink
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/node.hpp" // for Elements, Node
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box

239
src/ftxui/dom/flexbox.cpp Normal file
View File

@@ -0,0 +1,239 @@
#include <stddef.h> // for size_t
#include <algorithm> // for min, max
#include <memory> // for __shared_ptr_access, shared_ptr, allocator_traits<>::value_type, make_shared
#include <utility> // for move, swap
#include <vector> // for vector
#include "ftxui/dom/elements.hpp" // for Element, Elements, flexbox, hflow, vflow
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::Direction::Column, FlexboxConfig::AlignContent, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::JustifyContent, FlexboxConfig::Wrap, FlexboxConfig::AlignContent::FlexStart, FlexboxConfig::Direction::RowInversed, FlexboxConfig::JustifyContent::FlexStart, FlexboxConfig::Wrap::Wrap
#include "ftxui/dom/flexbox_helper.hpp" // for Block, Global, Compute
#include "ftxui/dom/node.hpp" // for Node, Elements, Node::Status
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui {
namespace {
void Normalize(FlexboxConfig::Direction& direction) {
switch (direction) {
case FlexboxConfig::Direction::Row:
case FlexboxConfig::Direction::RowInversed: {
direction = FlexboxConfig::Direction::Row;
} break;
case FlexboxConfig::Direction::Column:
case FlexboxConfig::Direction::ColumnInversed: {
direction = FlexboxConfig::Direction::Column;
} break;
}
}
void Normalize(FlexboxConfig::AlignContent& align_content) {
align_content = FlexboxConfig::AlignContent::FlexStart;
}
void Normalize(FlexboxConfig::JustifyContent& justify_content) {
justify_content = FlexboxConfig::JustifyContent::FlexStart;
}
void Normalize(FlexboxConfig::Wrap& wrap) {
wrap = FlexboxConfig::Wrap::Wrap;
}
FlexboxConfig Normalize(FlexboxConfig config) {
Normalize(config.direction);
Normalize(config.wrap);
Normalize(config.justify_content);
Normalize(config.align_content);
return config;
}
class Flexbox : public Node {
public:
Flexbox(Elements children, FlexboxConfig config)
: Node(std::move(children)),
config_(config),
config_normalized_(Normalize(config)) {
requirement_.flex_grow_x = 1;
requirement_.flex_grow_y = 0;
if (IsColumnOriented())
std::swap(requirement_.flex_grow_x, requirement_.flex_grow_y);
}
bool IsColumnOriented() {
return config_.direction == FlexboxConfig::Direction::Column ||
config_.direction == FlexboxConfig::Direction::ColumnInversed;
}
void Layout(flexbox_helper::Global& global,
bool compute_requirement = false) {
for (auto& child : children_) {
flexbox_helper::Block block;
block.min_size_x = child->requirement().min_x;
block.min_size_y = child->requirement().min_y;
if (!compute_requirement) {
block.flex_grow_x = child->requirement().flex_grow_x;
block.flex_grow_y = child->requirement().flex_grow_y;
block.flex_shrink_x = child->requirement().flex_shrink_x;
block.flex_shrink_y = child->requirement().flex_shrink_y;
}
global.blocks.push_back(block);
}
flexbox_helper::Compute(global);
}
void ComputeRequirement() override {
for (auto& child : children_)
child->ComputeRequirement();
flexbox_helper::Global global;
global.config = config_normalized_;
if (IsColumnOriented()) {
global.size_x = 100000;
global.size_y = asked_;
} else {
global.size_x = asked_;
global.size_y = 100000;
}
Layout(global, true);
if (global.blocks.size() == 0) {
requirement_.min_x = 0;
requirement_.min_y = 0;
return;
}
Box box;
box.x_min = global.blocks[0].x;
box.y_min = global.blocks[0].y;
box.x_max = global.blocks[0].x + global.blocks[0].dim_x;
box.y_max = global.blocks[0].y + global.blocks[0].dim_y;
for (auto& b : global.blocks) {
box.x_min = std::min(box.x_min, b.x);
box.y_min = std::min(box.y_min, b.y);
box.x_max = std::max(box.x_max, b.x + b.dim_x);
box.y_max = std::max(box.y_max, b.y + b.dim_y);
}
requirement_.min_x = box.x_max - box.x_min;
requirement_.min_y = box.y_max - box.y_min;
}
void SetBox(Box box) override {
Node::SetBox(box);
asked_ = std::min(asked_, IsColumnOriented() ? box.y_max - box.y_min + 1
: box.x_max - box.x_min + 1);
flexbox_helper::Global global;
global.config = config_;
global.size_x = box.x_max - box.x_min + 1;
global.size_y = box.y_max - box.y_min + 1;
Layout(global);
need_iteration_ = false;
for (size_t i = 0; i < children_.size(); ++i) {
auto& child = children_[i];
auto& b = global.blocks[i];
Box children_box;
children_box.x_min = box.x_min + b.x;
children_box.y_min = box.y_min + b.y;
children_box.x_max = box.x_min + b.x + b.dim_x - 1;
children_box.y_max = box.y_min + b.y + b.dim_y - 1;
Box intersection = Box::Intersection(children_box, box);
child->SetBox(intersection);
need_iteration_ |= (intersection != children_box);
}
}
void Check(Status* status) override {
for (auto& child : children_)
child->Check(status);
if (status->iteration == 0) {
asked_ = 6000;
need_iteration_ = true;
}
status->need_iteration |= need_iteration_;
}
int asked_ = 6000;
bool need_iteration_ = true;
const FlexboxConfig config_;
const FlexboxConfig config_normalized_;
};
} // namespace
/// @brief A container displaying elements on row/columns and capable of
/// wrapping on the next column/row when full.
/// @param children The elements in the container
/// @param config The option
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// flexbox({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// }, FlexboxConfig()
// .Set(FlexboxConfig::Direction::Column)
// .Set(FlexboxConfig::Wrap::WrapInversed)
// .SetGapMainAxis(1)
// .SetGapCrossAxis(1)
// )
/// ```
Element flexbox(Elements children, FlexboxConfig config) {
return std::make_shared<Flexbox>(std::move(children), std::move(config));
}
/// @brief A container displaying elements in rows from left to right. When
/// filled, it starts on a new row below.
/// @param children The elements in the container
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// hflow({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// });
/// ```
Element hflow(Elements children) {
return flexbox(std::move(children), FlexboxConfig());
}
/// @brief A container displaying elements in rows from top to bottom. When
/// filled, it starts on a new columns on the right.
/// filled, it starts on a new row.
/// is full, it starts a new row.
/// @param children The elements in the container
/// @return The container.
///
/// #### Example
///
/// ```cpp
/// vflow({
/// text("element 1"),
/// text("element 2"),
/// text("element 3"),
/// });
/// ```
Element vflow(Elements children) {
return flexbox(std::move(children),
FlexboxConfig().Set(FlexboxConfig::Direction::Column));
}
} // namespace ftxui
// 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.

View File

@@ -0,0 +1,40 @@
#include "ftxui/dom/flexbox_config.hpp"
namespace ftxui {
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::Direction d) {
this->direction = d;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::Wrap w) {
this->wrap = w;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::JustifyContent j) {
this->justify_content = j;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::AlignItems a) {
this->align_items = a;
return *this;
}
FlexboxConfig& FlexboxConfig::Set(FlexboxConfig::AlignContent a) {
this->align_content = a;
return *this;
}
FlexboxConfig& FlexboxConfig::SetGap(int x, int y) {
this->gap_x = x;
this->gap_y = y;
return *this;
}
} // namespace ftxui
// 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.

View File

@@ -0,0 +1,325 @@
#include "ftxui/dom/flexbox_helper.hpp"
#include <stddef.h> // for size_t
#include <algorithm> // for min, max
#include <memory> // for allocator_traits<>::value_type
#include <utility> // for swap, move
#include "ftxui/dom/box_helper.hpp" // for Element, Compute
namespace ftxui {
namespace flexbox_helper {
namespace {
void SymmetryXY(FlexboxConfig& c) {
std::swap(c.gap_x, c.gap_y);
switch (c.direction) {
case FlexboxConfig::Direction::Row:
c.direction = FlexboxConfig::Direction::Column;
break;
case FlexboxConfig::Direction::RowInversed:
c.direction = FlexboxConfig::Direction::ColumnInversed;
break;
case FlexboxConfig::Direction::Column:
c.direction = FlexboxConfig::Direction::Row;
break;
case FlexboxConfig::Direction::ColumnInversed:
c.direction = FlexboxConfig::Direction::RowInversed;
break;
}
}
void SymmetryX(FlexboxConfig& c) {
switch (c.direction) {
case FlexboxConfig::Direction::Row:
c.direction = FlexboxConfig::Direction::RowInversed;
break;
case FlexboxConfig::Direction::RowInversed:
c.direction = FlexboxConfig::Direction::Row;
break;
default:
break;
}
}
void SymmetryY(FlexboxConfig& c) {
switch (c.wrap) {
case FlexboxConfig::Wrap::NoWrap:
break;
case FlexboxConfig::Wrap::Wrap:
c.wrap = FlexboxConfig::Wrap::WrapInversed;
break;
case FlexboxConfig::Wrap::WrapInversed:
c.wrap = FlexboxConfig::Wrap::Wrap;
break;
}
}
void SymmetryXY(Global& g) {
SymmetryXY(g.config);
std::swap(g.size_x, g.size_y);
for (auto& b : g.blocks) {
std::swap(b.min_size_x, b.min_size_y);
std::swap(b.flex_grow_x, b.flex_grow_y);
std::swap(b.flex_shrink_x, b.flex_shrink_y);
std::swap(b.x, b.y);
std::swap(b.dim_x, b.dim_y);
}
}
void SymmetryX(Global& g) {
SymmetryX(g.config);
for (auto& b : g.blocks) {
b.x = g.size_x - b.x - b.dim_x;
}
}
void SymmetryY(Global& g) {
SymmetryY(g.config);
for (auto& b : g.blocks) {
b.y = g.size_y - b.y - b.dim_y;
}
}
struct Line {
std::vector<Block*> blocks;
};
void SetX(Global& global, std::vector<Line> lines) {
for (auto& line : lines) {
std::vector<box_helper::Element> elements;
for (auto* block : line.blocks) {
box_helper::Element element;
element.min_size = block->min_size_x;
element.flex_grow =
block->flex_grow_x || global.config.justify_content ==
FlexboxConfig::JustifyContent::Stretch;
element.flex_shrink = block->flex_shrink_x;
elements.push_back(element);
}
box_helper::Compute(
&elements,
global.size_x - global.config.gap_x * (line.blocks.size() - 1));
int x = 0;
for (size_t i = 0; i < line.blocks.size(); ++i) {
line.blocks[i]->dim_x = elements[i].size;
line.blocks[i]->x = x;
x += elements[i].size;
x += global.config.gap_x;
}
}
}
void SetY(Global& g, std::vector<Line> lines) {
std::vector<box_helper::Element> elements;
for (auto& line : lines) {
box_helper::Element element;
element.flex_shrink = line.blocks.front()->flex_shrink_y;
element.flex_grow = line.blocks.front()->flex_grow_y;
for (auto* block : line.blocks) {
element.min_size = std::max(element.min_size, block->min_size_y);
element.flex_shrink = std::min(element.flex_shrink, block->flex_shrink_y);
element.flex_grow = std::min(element.flex_grow, block->flex_grow_y);
}
elements.push_back(element);
}
// box_helper::Compute(&elements, g.size_y);
box_helper::Compute(&elements, 10000);
// [Align-content]
std::vector<int> ys(elements.size());
int y = 0;
for (size_t i = 0; i < elements.size(); ++i) {
ys[i] = y;
y += elements[i].size;
y += g.config.gap_y;
}
int remaining_space = std::max(0, g.size_y - y);
switch (g.config.align_content) {
case FlexboxConfig::AlignContent::FlexStart: {
} break;
case FlexboxConfig::AlignContent::FlexEnd: {
for (size_t i = 0; i < ys.size(); ++i)
ys[i] += remaining_space;
} break;
case FlexboxConfig::AlignContent::Center: {
for (size_t i = 0; i < ys.size(); ++i)
ys[i] += remaining_space / 2;
} break;
case FlexboxConfig::AlignContent::Stretch: {
for (int i = ys.size() - 1; i >= 0; --i) {
int shifted = remaining_space * (i + 0) / (i + 1);
ys[i] += shifted;
int consumed = remaining_space - shifted;
elements[i].size += consumed;
remaining_space -= consumed;
}
} break;
case FlexboxConfig::AlignContent::SpaceBetween: {
for (int i = ys.size() - 1; i >= 1; --i) {
ys[i] += remaining_space;
remaining_space = remaining_space * (i - 1) / i;
}
} break;
case FlexboxConfig::AlignContent::SpaceAround: {
for (int i = ys.size() - 1; i >= 0; --i) {
ys[i] += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2);
}
} break;
case FlexboxConfig::AlignContent::SpaceEvenly: {
for (int i = ys.size() - 1; i >= 0; --i) {
ys[i] += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2);
}
} break;
}
// [Align items]
for (size_t i = 0; i < lines.size(); ++i) {
auto& element = elements[i];
for (auto* block : lines[i].blocks) {
bool stretch =
block->flex_grow_y ||
g.config.align_content == FlexboxConfig::AlignContent::Stretch;
int size =
stretch ? element.size : std::min(element.size, block->min_size_y);
switch (g.config.align_items) {
case FlexboxConfig::AlignItems::FlexStart: {
block->y = ys[i];
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::Center: {
block->y = ys[i] + (element.size - size) / 2;
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::FlexEnd: {
block->y = ys[i] + element.size - size;
block->dim_y = size;
} break;
case FlexboxConfig::AlignItems::Stretch: {
block->y = ys[i];
block->dim_y = element.size;
} break;
}
}
}
}
void JustifyContent(Global& g, std::vector<Line> lines) {
for (auto& line : lines) {
Block* last = line.blocks.back();
int remaining_space = g.size_x - last->x - last->dim_x;
switch (g.config.justify_content) {
case FlexboxConfig::JustifyContent::FlexStart:
case FlexboxConfig::JustifyContent::Stretch:
break;
case FlexboxConfig::JustifyContent::FlexEnd: {
for (auto* block : line.blocks)
block->x += remaining_space;
} break;
case FlexboxConfig::JustifyContent::Center: {
for (auto* block : line.blocks)
block->x += remaining_space / 2;
} break;
case FlexboxConfig::JustifyContent::SpaceBetween: {
for (int i = line.blocks.size() - 1; i >= 1; --i) {
line.blocks[i]->x += remaining_space;
remaining_space = remaining_space * (i - 1) / i;
}
} break;
case FlexboxConfig::JustifyContent::SpaceAround: {
for (int i = line.blocks.size() - 1; i >= 0; --i) {
line.blocks[i]->x += remaining_space * (2 * i + 1) / (2 * i + 2);
remaining_space = remaining_space * (2 * i) / (2 * i + 2);
}
} break;
case FlexboxConfig::JustifyContent::SpaceEvenly: {
for (int i = line.blocks.size() - 1; i >= 0; --i) {
line.blocks[i]->x += remaining_space * (i + 1) / (i + 2);
remaining_space = remaining_space * (i + 1) / (i + 2);
}
} break;
}
}
}
} // namespace
void Compute(Global& global) {
if (global.config.direction == FlexboxConfig::Direction::Column ||
global.config.direction == FlexboxConfig::Direction::ColumnInversed) {
SymmetryXY(global);
Compute(global);
SymmetryXY(global);
return;
}
if (global.config.direction == FlexboxConfig::Direction::RowInversed) {
SymmetryX(global);
Compute(global);
SymmetryX(global);
return;
}
if (global.config.wrap == FlexboxConfig::Wrap::WrapInversed) {
SymmetryY(global);
Compute(global);
SymmetryY(global);
return;
}
// Step 1: Lay out every elements into rows:
std::vector<Line> lines;
{
Line line;
int x = 0;
for (auto& block : global.blocks) {
// Does it fit the end of the row?
// No? Then we need to start a new one:
if (x + block.min_size_x > global.size_x) {
x = 0;
if (!line.blocks.empty())
lines.push_back(std::move(line));
line = Line();
}
block.line = lines.size();
block.line_position = line.blocks.size();
line.blocks.push_back(&block);
x += block.min_size_x + global.config.gap_x;
}
if (!line.blocks.empty())
lines.push_back(std::move(line));
}
// Step 2: Set positions on the X axis.
SetX(global, lines);
JustifyContent(global, lines); // Distribute remaining space.
// Step 3: Set positions on the Y axis.
SetY(global, lines);
}
} // namespace flexbox_helper
} // namespace ftxui
// 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.

View File

@@ -0,0 +1,45 @@
#ifndef FTXUI_DOM_FLEXBOX_HELPER_HPP
#define FTXUI_DOM_FLEXBOX_HELPER_HPP
#include <vector>
#include "ftxui/dom/flexbox_config.hpp"
namespace ftxui {
namespace flexbox_helper {
struct Block {
// Input:
int min_size_x = 0;
int min_size_y = 0;
int flex_grow_x = 0;
int flex_grow_y = 0;
int flex_shrink_x = 0;
int flex_shrink_y = 0;
// Output:
int line;
int line_position;
int x = 0;
int y = 0;
int dim_x = 0;
int dim_y = 0;
bool overflow = false;
};
struct Global {
std::vector<Block> blocks;
FlexboxConfig config;
int size_x;
int size_y;
};
void Compute(Global& global);
} // namespace flexbox_helper
} // namespace ftxui
#endif /* end of include guard: FTXUI_DOM_FLEXBOX_HELPER_HPP*/
// 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.

View File

@@ -0,0 +1,233 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApiResolver, TestFactoryImpl
#include <memory> // for allocator_traits<>::value_type
#include "ftxui/dom/flexbox_helper.hpp"
#include "gtest/gtest_pred_impl.h" // for EXPECT_EQ, Test, TEST
using namespace ftxui;
using namespace ftxui;
TEST(FlexboxHelperTest, BasicRow) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::Row);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 10);
EXPECT_EQ(g.blocks[1].y, 0);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 20);
EXPECT_EQ(g.blocks[2].y, 0);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 0);
EXPECT_EQ(g.blocks[3].y, 5);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicColumn) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::Column);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 0);
EXPECT_EQ(g.blocks[1].y, 5);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 0);
EXPECT_EQ(g.blocks[2].y, 10);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 10);
EXPECT_EQ(g.blocks[3].y, 0);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicRowInversed) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::RowInversed);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 22);
EXPECT_EQ(g.blocks[0].y, 0);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 12);
EXPECT_EQ(g.blocks[1].y, 0);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 2);
EXPECT_EQ(g.blocks[2].y, 0);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 22);
EXPECT_EQ(g.blocks[3].y, 5);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 12);
EXPECT_EQ(g.blocks[4].y, 5);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
TEST(FlexboxHelperTest, BasicColumnInversed) {
flexbox_helper::Block block_10_5;
block_10_5.min_size_x = 10;
block_10_5.min_size_y = 5;
flexbox_helper::Global g;
g.blocks = {
block_10_5, block_10_5, block_10_5, block_10_5, block_10_5,
};
g.size_x = 32;
g.size_y = 16;
g.config = FlexboxConfig().Set(FlexboxConfig::Direction::ColumnInversed);
flexbox_helper::Compute(g);
EXPECT_EQ(g.blocks.size(), 5u);
EXPECT_EQ(g.blocks[0].line, 0);
EXPECT_EQ(g.blocks[1].line, 0);
EXPECT_EQ(g.blocks[2].line, 0);
EXPECT_EQ(g.blocks[3].line, 1);
EXPECT_EQ(g.blocks[4].line, 1);
EXPECT_EQ(g.blocks[0].line_position, 0);
EXPECT_EQ(g.blocks[1].line_position, 1);
EXPECT_EQ(g.blocks[2].line_position, 2);
EXPECT_EQ(g.blocks[3].line_position, 0);
EXPECT_EQ(g.blocks[4].line_position, 1);
EXPECT_EQ(g.blocks[0].x, 0);
EXPECT_EQ(g.blocks[0].y, 11);
EXPECT_EQ(g.blocks[0].dim_x, 10);
EXPECT_EQ(g.blocks[0].dim_y, 5);
EXPECT_EQ(g.blocks[1].x, 0);
EXPECT_EQ(g.blocks[1].y, 6);
EXPECT_EQ(g.blocks[1].dim_x, 10);
EXPECT_EQ(g.blocks[1].dim_y, 5);
EXPECT_EQ(g.blocks[2].x, 0);
EXPECT_EQ(g.blocks[2].y, 1);
EXPECT_EQ(g.blocks[2].dim_x, 10);
EXPECT_EQ(g.blocks[2].dim_y, 5);
EXPECT_EQ(g.blocks[3].x, 10);
EXPECT_EQ(g.blocks[3].y, 11);
EXPECT_EQ(g.blocks[3].dim_x, 10);
EXPECT_EQ(g.blocks[3].dim_y, 5);
EXPECT_EQ(g.blocks[4].x, 10);
EXPECT_EQ(g.blocks[4].y, 6);
EXPECT_EQ(g.blocks[4].dim_x, 10);
EXPECT_EQ(g.blocks[4].dim_y, 5);
}
// 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.

View File

@@ -0,0 +1,438 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <string> // for allocator
#include "ftxui/dom/elements.hpp" // for text, flexbox
#include "ftxui/dom/flexbox_config.hpp" // for FlexboxConfig, FlexboxConfig::Direction, FlexboxConfig::AlignContent, FlexboxConfig::JustifyContent, FlexboxConfig::Direction::Column, FlexboxConfig::AlignItems, FlexboxConfig::JustifyContent::SpaceAround, FlexboxConfig::AlignContent::Center, FlexboxConfig::AlignContent::FlexEnd, FlexboxConfig::AlignContent::SpaceAround, FlexboxConfig::AlignContent::SpaceBetween, FlexboxConfig::AlignContent::SpaceEvenly, FlexboxConfig::AlignItems::Center, FlexboxConfig::AlignItems::FlexEnd, FlexboxConfig::Direction::ColumnInversed, FlexboxConfig::Direction::Row, FlexboxConfig::Direction::RowInversed, FlexboxConfig::JustifyContent::Center, FlexboxConfig::JustifyContent::SpaceBetween
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/screen/color.hpp" // for ftxui
#include "ftxui/screen/screen.hpp" // for Screen
#include "gtest/gtest_pred_impl.h" // for Test, EXPECT_EQ, TEST
using namespace ftxui;
TEST(FlexboxTest, BasicRow) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::Row));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaabbb \r\n"
"cccc \r\n"
"dddd \r\n"
" ");
}
TEST(FlexboxTest, BasicRowInversed) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::RowInversed));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" bbbaaa\r\n"
" cccc\r\n"
" dddd\r\n"
" ");
}
TEST(FlexboxTest, BasicColumn) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::Column));
Screen screen(8, 3);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaa dddd\r\n"
"bbb e \r\n"
"cccc ");
}
TEST(FlexboxTest, BasicColumnInversed) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::Direction::ColumnInversed));
Screen screen(8, 3);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"cccc \r\n"
"bbb e \r\n"
"aaa dddd");
}
TEST(FlexboxTest, JustifyContentCenter) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::Center));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaabbb \r\n"
" cccc \r\n"
" dddde \r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceBetween) {
auto root = flexbox(
{
text("aaa"),
text("bbb"),
text("cccc"),
text("dddd"),
text("e"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceBetween));
Screen screen(7, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aaa bbb\r\n"
"cccc \r\n"
"dddd e\r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceAround) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("dddddddddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceAround));
Screen screen(15, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa bb ccc \r\n"
"ddddddddddddee \r\n"
" ff ggg \r\n"
" ");
}
TEST(FlexboxTest, JustifyContentSpaceEvenly) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("dddddddddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::JustifyContent::SpaceAround));
Screen screen(15, 4);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa bb ccc \r\n"
"ddddddddddddee \r\n"
" ff ggg \r\n"
" ");
}
TEST(FlexboxTest, AlignItemsFlexEnd) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig()
.Set(FlexboxConfig::Direction::Column)
.Set(FlexboxConfig::AlignItems::FlexEnd));
Screen screen(15, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa ff \r\n"
" bbggg \r\n"
" ccc \r\n"
"ddddd \r\n"
" ee ");
}
TEST(FlexboxTest, AlignItemsCenter) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig()
.Set(FlexboxConfig::Direction::Column)
.Set(FlexboxConfig::AlignItems::Center));
Screen screen(15, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" aa ff \r\n"
" bb ggg \r\n"
" ccc \r\n"
"ddddd \r\n"
" ee ");
}
TEST(FlexboxTest, AlignContentFlexEnd) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::FlexEnd));
Screen screen(10, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
" \r\n"
"aabbccc \r\n"
"dddddeeff \r\n"
"ggg ");
}
TEST(FlexboxTest, AlignContentCenter) {
auto root = flexbox(
{
text("aa"),
text("bb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::Center));
Screen screen(10, 5);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
"aabbccc \r\n"
"dddddeeff \r\n"
"ggg \r\n"
" ");
}
TEST(FlexboxTest, AlignContentSpaceBetween) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceBetween));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
" \r\n"
"ffggg ");
}
TEST(FlexboxTest, AlignContentSpaceAround) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceAround));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
" \r\n"
"ffggg \r\n"
" ");
}
TEST(FlexboxTest, AlignContentSpaceEvenly) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().Set(FlexboxConfig::AlignContent::SpaceEvenly));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
" \r\n"
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
"ffggg \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapX) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(1, 0));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aa bbb \r\n"
"ccc \r\n"
"ddddd \r\n"
"ee ff \r\n"
"ggg \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapX2) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(2, 0));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aa bbb\r\n"
"ccc \r\n"
"ddddd \r\n"
"ee ff \r\n"
"ggg \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ");
}
TEST(FlexboxTest, GapY) {
auto root = flexbox(
{
text("aa"),
text("bbb"),
text("ccc"),
text("ddddd"),
text("ee"),
text("ff"),
text("ggg"),
},
FlexboxConfig().SetGap(0, 1));
Screen screen(7, 10);
Render(screen, root);
EXPECT_EQ(screen.ToString(),
"aabbb \r\n"
" \r\n"
"ccc \r\n"
" \r\n"
"dddddee\r\n"
" \r\n"
"ffggg \r\n"
" \r\n"
" \r\n"
" ");
}
// 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.

98
src/ftxui/dom/focus.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include <memory> // for make_shared
#include <utility> // for move
#include "ftxui/dom/elements.hpp" // for Decorator, Element, focusPosition, focusPositionRelative
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/dom/requirement.hpp" // for Requirement, Requirement::NORMAL, Requirement::Selection
#include "ftxui/screen/box.hpp" // for Box
namespace ftxui {
/// @brief Used inside a `frame`, this force the view to be scrolled toward a
/// a given position. The position is expressed in proportion of the requested
/// size.
///
/// For instance:
/// - (0, 0) means that the view is scrolled toward the upper left.
/// - (1, 0) means that the view is scrolled toward the upper right.
/// - (0, 1) means that the view is scrolled toward the bottom left.
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// Element document = huge_document()
/// | focusPositionRelative(0.f, 1.f)
/// | frame;
/// ```
Decorator focusPositionRelative(float x, float y) {
class Impl : public NodeDecorator {
public:
Impl(Element child, float x, float y)
: NodeDecorator(child), x_(x), y_(y) {}
void ComputeRequirement() override {
NodeDecorator::ComputeRequirement();
requirement_.selection = Requirement::Selection::NORMAL;
Box& box = requirement_.selected_box;
box.x_min = requirement_.min_x * x_;
box.y_min = requirement_.min_y * y_;
box.x_max = requirement_.min_x * x_;
box.y_max = requirement_.min_y * y_;
}
private:
const float x_;
const float y_;
};
return [x, y](Element child) {
return std::make_shared<Impl>(std::move(child), x, y);
};
}
/// @brief Used inside a `frame`, this force the view to be scrolled toward a
/// a given position. The position is expressed in the numbers of cells.
///
/// @ingroup dom
///
/// ### Example
///
/// ```cpp
/// Element document = huge_document()
/// | focusPosition(10, 10)
/// | frame;
/// ```
Decorator focusPosition(int x, int y) {
class Impl : public NodeDecorator {
public:
Impl(Element child, float x, float y)
: NodeDecorator(child), x_(x), y_(y) {}
void ComputeRequirement() override {
NodeDecorator::ComputeRequirement();
requirement_.selection = Requirement::Selection::NORMAL;
Box& box = requirement_.selected_box;
box.x_min = x_;
box.y_min = y_;
box.x_max = x_;
box.y_max = y_;
}
private:
const int x_;
const int y_;
};
return [x, y](Element child) {
return std::make_shared<Impl>(std::move(child), x, y);
};
}
} // namespace ftxui
// 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.

View File

@@ -1,10 +1,10 @@
#include <algorithm> // for max, min
#include <memory> // for make_shared, shared_ptr, __shared_ptr_access
#include <memory> // for make_shared, __shared_ptr_access
#include <utility> // for move
#include <vector> // for vector, __alloc_traits<>::value_type
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, unpack, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node
#include "ftxui/dom/elements.hpp" // for Element, unpack, Elements, focus, frame, select, xframe, yframe
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/requirement.hpp" // for Requirement, Requirement::FOCUSED, Requirement::SELECTED
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen, Screen::Cursor

Some files were not shown because too many files have changed in this diff Show More