19 Commits
v0.9 ... v0.11

Author SHA1 Message Date
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
ArthurSonzogni
75cd2b0fca Fix workflow 2021-09-30 21:29:41 +02:00
ArthurSonzogni
0a5e9f2a2f Add workflow creating releases. 2021-09-30 21:25:47 +02:00
ArthurSonzogni
66cdf9b2a5 Fix: box drawing comparison.
There was a bug. Converting the bitfield to int using the union wasn't
working correctly.
2021-09-30 20:55:47 +02:00
ArthurSonzogni
84287eb217 Use vscroll_indicator in examples. 2021-09-30 20:55:47 +02:00
Arthur Sonzogni
535974d291 Update README.md 2021-09-30 20:55:47 +02:00
ArthurSonzogni
753502998c Add a C++ badge. 2021-09-30 20:55:47 +02:00
ArthurSonzogni
31b5fac9c5 Add fuzzer to Maybe & Dropdown. 2021-09-30 20:55:47 +02:00
ArthurSonzogni
76b2f17488 fix: vscroll_indicator. Reserve one cell. 2021-09-30 20:55:47 +02:00
Arthur Sonzogni
c5ef0c7fb5 feat: Dropdown select menu. (#214)
Dom
 - `vscroll_indicator`. Show a scrollback indicator on the right.

Component
 - `Maybe`: Display an component conditionnally based on a boolean.
 - `Dropdown`: A dropdown select list.

This address:
https://github.com/ArthurSonzogni/FTXUI/issues/204
2021-09-30 20:55:47 +02:00
39 changed files with 2191 additions and 348 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 . --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,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,6 +1,42 @@
# Changelog
Changelog
=========
## 0.9 (2021-09-26)
released (development)
------------------------
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
- `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.
0.9 (2021-09-26)
----------------
The initial release where changelog where written.

View File

@@ -4,7 +4,7 @@ include(cmake/ftxui_git_version.cmake)
project(ftxui
LANGUAGES CXX
VERSION 0.9.${git_version}
VERSION 0.11.${git_version}
)
option(FTXUI_BUILD_DOCS "Set to ON to build tests" ON)
@@ -24,7 +24,7 @@ else()
${FTXUI_MICROSOFT_TERMINAL_FALLBACK_HELP_TEXT} OFF)
endif()
add_library(screen STATIC
add_library(screen
src/ftxui/screen/box.cpp
src/ftxui/screen/color.cpp
src/ftxui/screen/color_info.cpp
@@ -38,7 +38,7 @@ add_library(screen STATIC
include/ftxui/screen/string.hpp
)
add_library(dom STATIC
add_library(dom
include/ftxui/dom/elements.hpp
include/ftxui/dom/node.hpp
include/ftxui/dom/requirement.hpp
@@ -57,24 +57,26 @@ add_library(dom STATIC
src/ftxui/dom/frame.cpp
src/ftxui/dom/gauge.cpp
src/ftxui/dom/graph.cpp
src/ftxui/dom/hbox.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
src/ftxui/dom/paragraph.cpp
src/ftxui/dom/reflect.cpp
src/ftxui/dom/scroll_indicator.cpp
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
@@ -87,8 +89,10 @@ add_library(component STATIC
src/ftxui/component/checkbox.cpp
src/ftxui/component/component.cpp
src/ftxui/component/container.cpp
src/ftxui/component/dropdown.cpp
src/ftxui/component/event.cpp
src/ftxui/component/input.cpp
src/ftxui/component/maybe.cpp
src/ftxui/component/menu.cpp
src/ftxui/component/radiobox.cpp
src/ftxui/component/radiobox.cpp
@@ -108,9 +112,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)
@@ -120,10 +128,6 @@ if (FTXUI_BUILD_TESTS AND ${CMAKE_VERSION} VERSION_GREATER "3.11.4")
include(cmake/ftxui_test.cmake)
endif()
if(FTXUI_ENABLE_INSTALL)
include(cmake/ftxui_install.cmake)
endif()
if(FTXUI_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
@@ -134,3 +138,8 @@ endif()
include(cmake/iwyu.cmake)
include(cmake/ftxui_export.cmake)
if(FTXUI_ENABLE_INSTALL)
include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake)
endif()

View File

@@ -1,24 +1,29 @@
# FTXUI
<p align="center">
<img src="./examples/component/homescreen.gif" alt="Demo image"></img>
<br/>
<a href="#"><img src="https://img.shields.io/badge/c++-%2300599C.svg?style=flat&logo=c%2B%2B&logoColor=white"></img></a>
<a href="http://opensource.org/licenses/MIT"><img src="https://img.shields.io/github/license/arthursonzogni/FTXUI?color=black"></img></a>
<a href="#"><img src="https://img.shields.io/github/stars/ArthurSonzogni/FTXUI"></img></a>
<a href="#"><img src="https://img.shields.io/github/forks/ArthurSonzogni/FTXUI"></img></a>
<a href="#"><img src="https://img.shields.io/github/repo-size/ArthurSonzogni/FTXUI"></img></a>
<a href="https://github.com/ArthurSonzogni/FTXUI/issues"><img src="https://img.shields.io/github/issues/ArthurSonzogni/FTXUI"></img></a>
<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://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> ·
<a href="https://github.com/ArthurSonzogni/FTXUI/pulls">Send a Pull Request</a>
[![issues][badge.issues]][issues]
[![license][badge.license]][license]
[![contributors][badge.contributors]][contributors]
</p>
[badge.issues]: https://img.shields.io/github/issues-raw/arthursonzogni/FTXUI
[badge.license]: https://img.shields.io/github/license/arthursonzogni/FTXUI?color=black
[badge.contributors]: https://img.shields.io/github/contributors/arthursonzogni/FTXUI?color=blue
## FTXUI
[issues]: https://github.com/ArthurSonzogni/FTXUI/issues
[license]: http://opensource.org/licenses/MIT
[contributors]: https://github.com/ArthurSonzogni/FTXUI/graphs/contributors
**Functional Terminal (X) User interface**
<i>Functional Terminal (X) User interface</i>
A simple C++ library for terminal based user interface.
## Demo:
![Demo image](./examples/component/homescreen.gif)
## Feature
* Functional style. Inspired by
[[1]](https://hackernoon.com/building-reactive-terminal-interfaces-in-c-d392ce34e649?gi=d9fb9ce35901)
@@ -30,25 +35,13 @@ 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
- Webassembly
- Linux
- MacOS
- Windows
[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
## Example:
## Example
~~~cpp
vbox({
hbox({
@@ -69,7 +62,7 @@ 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/)
@@ -94,11 +87,11 @@ Feel free to add your projects here:
- [todoman](https://github.com/aaleino/todoman)
- [TimeAccumulator](https://github.com/asari555/TimeAccumulator)
## Hosted on:
## Hosted on
* [github](https://github.com/ArthurSonzogni/ftxui)
* [gitlab](https://gitlab.com/ArthurSonzogni/ftxui)
## External package:
## External package
It is **highly** recommanded to use cmake FetchContent to depends on FTXUI. This
way you can specify which commit you would like to depends on.

24
cmake/ftxui_package.cmake Normal file
View File

@@ -0,0 +1,24 @@
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")
set(CPACK_DEBIAN_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A simple C++ Terminal UI library")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/ArthurSonzogni/FTXUI/")
set(CPACK_PACKAGE_NAME "ftxui")
set(CPACK_PACKAGE_VENDOR "Arthur Sonzogni")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
include(CPack)

View File

@@ -20,6 +20,7 @@ add_executable(tests
src/ftxui/component/terminal_input_parser_test.cpp
src/ftxui/component/toggle_test.cpp
src/ftxui/dom/gauge_test.cpp
src/ftxui/dom/table_test.cpp
src/ftxui/dom/gridbox_test.cpp
src/ftxui/dom/hbox_test.cpp
src/ftxui/dom/text_test.cpp

View File

@@ -2,19 +2,21 @@ set(DIRECTORY_LIB component)
example(button)
example(checkbox)
example(nested_screen)
example(checkbox_in_frame)
example(composition)
example(dropdown)
example(gallery)
example(homescreen)
example(input)
example(maybe)
example(menu)
example(menu_in_frame)
example(menu2)
example(menu_multiple)
example(menu_entries)
example(menu_in_frame)
example(menu_multiple)
example(menu_style)
example(modal_dialog)
example(nested_screen)
example(print_key_press)
example(radiobox)
example(radiobox_in_frame)

View File

@@ -24,7 +24,8 @@ int main(int argc, const char* argv[]) {
}
auto renderer = Renderer(container, [&] {
return container->Render() | frame | size(HEIGHT, LESS_THAN, 10) | border;
return container->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10) | border;
});
auto screen = ScreenInteractive::FitComponent();

View File

@@ -0,0 +1,45 @@
#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 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",
};
int selected_1 = 0;
int selected_2 = 0;
int selected_3 = 0;
int selected_4 = 0;
auto layout = Container::Vertical({
Container::Horizontal({
Dropdown(&entries, &selected_1),
Dropdown(&entries, &selected_2),
}),
Container::Horizontal({
Dropdown(&entries, &selected_3),
Dropdown(&entries, &selected_4),
}),
});
auto screen = ScreenInteractive::FitComponent();
screen.Loop(layout);
}
// 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

@@ -214,23 +214,25 @@ int main(int argc, const char* argv[]) {
};
auto compiler_renderer = Renderer(compiler_component, [&] {
auto compiler_win = window(text("Compiler"), compiler->Render() | frame);
auto flags_win = window(text("Flags"), flags->Render() | frame);
auto compiler_win = window(text("Compiler"),
compiler->Render() | vscroll_indicator | frame);
auto flags_win =
window(text("Flags"), flags->Render() | vscroll_indicator | frame);
auto executable_win = window(text("Executable:"), executable_->Render());
auto input_win =
window(text("Input"),
hbox({
vbox({
hbox({
text("Add: "),
input_add->Render(),
}) | size(WIDTH, EQUAL, 20) |
size(HEIGHT, EQUAL, 1),
filler(),
}),
separator(),
input->Render() | frame | size(HEIGHT, EQUAL, 3) | flex,
}));
window(text("Input"), hbox({
vbox({
hbox({
text("Add: "),
input_add->Render(),
}) | size(WIDTH, EQUAL, 20) |
size(HEIGHT, EQUAL, 1),
filler(),
}),
separator(),
input->Render() | vscroll_indicator | frame |
size(HEIGHT, EQUAL, 3) | flex,
}));
return vbox({
hbox({
compiler_win,
@@ -240,7 +242,7 @@ int main(int argc, const char* argv[]) {
input_win | size(WIDTH, EQUAL, 60),
}),
filler(),
}) | size(HEIGHT, LESS_THAN, 6),
}) | size(HEIGHT, LESS_THAN, 8),
hflow(render_command()) | flex_grow,
}) |
flex_grow | border;

View File

@@ -0,0 +1,46 @@
#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 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) {
return Renderer(child, [child] { return child->Render() | border; });
}
int main(int argc, const char* argv[]) {
std::vector<std::string> entries = {
"entry 1",
"entry 2",
"entry 3",
};
int menu_1_selected = 0;
int menu_2_selected = 0;
auto menu_1 = Radiobox(&entries, &menu_1_selected);
auto menu_2 = Radiobox(&entries, &menu_2_selected);
menu_1 = Border(menu_1);
menu_2 = Border(menu_2);
bool menu_1_show = false;
bool menu_2_show = false;
auto layout = Container::Vertical({
Checkbox("Show menu_1", &menu_1_show),
Maybe(menu_1, &menu_1_show),
Checkbox("Show menu_2", &menu_2_show),
Maybe(menu_2, &menu_2_show),
});
auto screen = ScreenInteractive::TerminalOutput();
screen.Loop(layout);
}
// 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

@@ -18,7 +18,8 @@ int main(int argc, const char* argv[]) {
entries.push_back("Entry " + std::to_string(i));
auto radiobox = Menu(&entries, &selected);
auto renderer = Renderer(radiobox, [&] {
return radiobox->Render() | frame | size(HEIGHT, LESS_THAN, 10) | border;
return radiobox->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10) | border;
});
auto screen = ScreenInteractive::FitComponent();

View File

@@ -18,7 +18,8 @@ int main(int argc, const char* argv[]) {
entries.push_back("RadioBox " + std::to_string(i));
auto radiobox = Radiobox(&entries, &selected);
auto renderer = Renderer(radiobox, [&] {
return radiobox->Render() | frame | size(HEIGHT, LESS_THAN, 10) | border;
return radiobox->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 10) | border;
});
auto screen = ScreenInteractive::FitComponent();

View File

@@ -3,9 +3,13 @@ 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(gauge)
example(graph)
example(gridbox)
example(hflow)
example(html_like)
example(package_manager)
@@ -17,13 +21,10 @@ 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(window)

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

@@ -0,0 +1,66 @@
#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/box.hpp" // for ftxui
#include "ftxui/screen/color.hpp" // for Color, Color::Blue, Color::Cyan, Color::White
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

@@ -39,6 +39,7 @@ Component Menu(ConstStringListRef entries,
int* selected_,
Ref<MenuOption> = {});
Component MenuEntry(ConstStringRef label, Ref<MenuEntryOption> = {});
Component Dropdown(ConstStringListRef entries, int* selected);
Component Radiobox(ConstStringListRef entries,
int* selected_,
Ref<RadioboxOption> option = {});
@@ -55,6 +56,7 @@ 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);

View File

@@ -74,6 +74,8 @@ struct Event {
bool operator!=(const Event& other) const { return !operator==(other); }
//--- State section ----------------------------------------------------------
ScreenInteractive* screen_ = nullptr;
private:
friend ComponentBase;
friend ScreenInteractive;
@@ -95,8 +97,6 @@ struct Event {
struct Cursor cursor_;
};
std::string input_;
ScreenInteractive* screen_ = nullptr;
};
} // namespace ftxui

View File

@@ -17,7 +17,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 +34,24 @@ 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 graph(GraphFunction);
Element emptyElement();
// -- Decorator ---
Element bold(Element);
@@ -104,6 +108,8 @@ Element yframe(Element);
Element focus(Element);
Element select(Element);
Element vscroll_indicator(Element);
// --- Util --------------------------------------------------------------------
Element hcenter(Element);
Element vcenter(Element);

View File

@@ -0,0 +1,93 @@
#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(std::vector<std::vector<std::string>>);
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:
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

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

View File

@@ -57,7 +57,7 @@ Component GeneratorComponent(const char*& data, size_t& size, int depth) {
if (depth <= 0)
return Button(GeneratorString(data, size), [] {});
switch (value % 16) {
switch (value % 18) {
case 1:
return Checkbox(GeneratorString(data, size), &g_bool);
case 2:
@@ -102,6 +102,10 @@ Component GeneratorComponent(const char*& data, size_t& size, int depth) {
&g_int);
case 15:
return Container::Tab(GeneratorComponents(data, size, depth - 1), &g_int);
case 16:
return Maybe(GeneratorComponent(data, size, depth - 1), &g_bool);
case 17:
return Dropdown(&g_list, &g_int);
default:
return Button(GeneratorString(data, size), [] {});
}

View File

@@ -0,0 +1,64 @@
#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 {
Component Dropdown(ConstStringListRef entries, int* selected) {
class Impl : public ComponentBase {
public:
Impl(ConstStringListRef entries, int* selected)
: entries_(std::move(entries)), selected_(selected) {
CheckboxOption option;
option.style_checked = "↓│";
option.style_unchecked = "→│";
checkbox_ = Checkbox(&title_, &show_, option),
radiobox_ = Radiobox(entries_, selected_);
Add(Container::Vertical({
checkbox_,
Maybe(radiobox_, &show_),
}));
}
Element Render() override {
title_ = entries_[*selected_];
if (show_) {
return vbox({
checkbox_->Render(),
separator(),
radiobox_->Render() | vscroll_indicator | frame |
size(HEIGHT, LESS_THAN, 12),
}) |
border;
}
return vbox({
checkbox_->Render() | border,
filler(),
});
}
private:
ConstStringListRef entries_;
bool show_ = false;
int* selected_;
std::string title_;
Component checkbox_;
Component radiobox_;
};
return Make<Impl>(std::move(entries), 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

@@ -0,0 +1,40 @@
#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) {
class Impl : public ComponentBase {
public:
Impl(bool* show) : show_(show) {}
private:
Element Render() override {
return *show_ ? ComponentBase::Render() : std::make_unique<Node>();
}
bool Focusable() const override {
return *show_ && ComponentBase::Focusable();
}
bool OnEvent(Event event) override {
return *show_ && ComponentBase::OnEvent(event);
}
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

@@ -0,0 +1,30 @@
#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);
}
// 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

@@ -14,10 +14,11 @@
namespace ftxui {
static std::string simple_border_charset[6][6] = {
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""},
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
};
// For reference, here is the charset for normal border:
@@ -124,6 +125,7 @@ class Border : public Node {
/// @see borderLight
/// @see borderDouble
/// @see borderHeavy
/// @see borderEmpty
/// @see borderRounded
///
/// Add a border around an element
@@ -174,6 +176,7 @@ Decorator borderStyled(BorderStyle style) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -192,9 +195,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 +210,7 @@ Element borderLight(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -225,9 +229,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 +244,7 @@ Element borderHeavy(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -258,9 +263,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 +278,7 @@ Element borderDouble(Element child) {
/// @see borderDouble
/// @see borderHeavy
/// @see borderRounded
/// @see borderEmpty
/// @see borderStyled
/// @see borderWith
///
@@ -291,14 +297,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

@@ -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

@@ -0,0 +1,69 @@
#include <algorithm> // for max
#include <memory> // for make_shared, __shared_ptr_access
#include <string> // for string
#include <utility> // for move
#include <vector> // for __alloc_traits<>::value_type
#include "ftxui/dom/elements.hpp" // for Element, vscroll_indicator
#include "ftxui/dom/node.hpp" // for Node, Elements
#include "ftxui/dom/node_decorator.hpp" // for NodeDecorator
#include "ftxui/dom/requirement.hpp" // for Requirement
#include "ftxui/screen/box.hpp" // for Box
#include "ftxui/screen/screen.hpp" // for Screen, Pixel
namespace ftxui {
/// @brief Add a filter that will invert the foreground and the background
/// colors.
/// @ingroup dom
Element vscroll_indicator(Element child) {
class Impl : public NodeDecorator {
using NodeDecorator::NodeDecorator;
void ComputeRequirement() override {
Node::ComputeRequirement();
requirement_ = children_[0]->requirement();
requirement_.min_x++;
}
void SetBox(Box box) override {
Node::SetBox(box);
if (box_.x_min > box_.x_max)
box_.x_max--;
children_[0]->SetBox(box);
}
void Render(Screen& screen) final {
Node::Render(screen);
const Box& stencil = screen.stencil;
int size_inner = box_.y_max - box_.y_min;
int size_outter = stencil.y_max - stencil.y_min;
if (size_outter >= size_inner)
return;
int start_y = 2 * stencil.y_min + 2 * float(stencil.y_min - box_.y_min) *
(size_outter - 1) / size_inner;
int size = 2 * float(size_outter) * (size_outter - 1) / size_inner + 2;
size = std::max(size, 1);
const int x = stencil.x_max;
for (int y = stencil.y_min; y <= stencil.y_max; ++y) {
bool up = (2 * y + -1 >= start_y) && (2 * y - 1 <= start_y + size);
bool down = (2 * y - 0 >= start_y) && (2 * y - 0 <= start_y + size);
const char* c = up ? (down ? "" : "") : (down ? "" : " ");
screen.PixelAt(x, y).character = c;
screen.PixelAt(x, y).inverted = true;
}
};
};
return std::make_shared<Impl>(std::move(child));
}
} // 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

@@ -12,15 +12,36 @@ namespace ftxui {
using ftxui::Screen;
const std::string charset[][2] = {
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""}, //
{"", ""}, //
{"", ""}, //
{"", ""}, //
{" ", " "}, //
};
class Separator : public Node {
public:
Separator(BorderStyle style) : style_(style) {}
Separator(std::string value) : value_(value) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
requirement_.min_y = 1;
}
void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
screen.PixelAt(x, y).character = value_;
}
}
}
std::string value_;
};
class SeparatorAuto : public Node {
public:
SeparatorAuto(BorderStyle style) : style_(style) {}
void ComputeRequirement() override {
requirement_.min_x = 1;
@@ -43,9 +64,9 @@ class Separator : public Node {
BorderStyle style_;
};
class SeparatorWithPixel : public Separator {
class SeparatorWithPixel : public SeparatorAuto {
public:
SeparatorWithPixel(Pixel pixel) : Separator(LIGHT), pixel_(pixel) {}
SeparatorWithPixel(Pixel pixel) : SeparatorAuto(LIGHT), pixel_(pixel) {}
void Render(Screen& screen) override {
for (int y = box_.y_min; y <= box_.y_max; ++y) {
for (int x = box_.x_min; x <= box_.x_max; ++x) {
@@ -58,22 +79,288 @@ class SeparatorWithPixel : public Separator {
Pixel pixel_;
};
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separator(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ────
/// down
/// ```
Element separator() {
return std::make_shared<Separator>(LIGHT);
return std::make_shared<SeparatorAuto>(LIGHT);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @param style the style of the separator.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separatorStyled(DOUBLE),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ════
/// down
/// ```
Element separatorStyled(BorderStyle style) {
return std::make_shared<Separator>(style);
return std::make_shared<SeparatorAuto>(style);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the LIGHT style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separatorLight(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ────
/// down
/// ```
Element separatorLight() {
return std::make_shared<Separator>(LIGHT);
return std::make_shared<SeparatorAuto>(LIGHT);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the HEAVY style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separatorHeavy(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ━━━━
/// down
/// ```
Element separatorHeavy() {
return std::make_shared<Separator>(HEAVY);
return std::make_shared<SeparatorAuto>(HEAVY);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the DOUBLE style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separatorDouble(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ════
/// down
/// ```
Element separatorDouble() {
return std::make_shared<Separator>(DOUBLE);
return std::make_shared<SeparatorAuto>(DOUBLE);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements, using the EMPTY style.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separator(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
///
/// down
/// ```
Element separatorEmpty() {
return std::make_shared<SeparatorAuto>(EMPTY);
}
/// @brief Draw a vertical or horizontal separation in between two other
/// elements.
/// @param value the character to fill the separator area.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorDouble
/// @see separatorHeavy
/// @see separatorEmpty
/// @see separatorRounded
/// @see separatorStyled
/// @see separatorCharacter
///
/// Add a visual separation in between two elements.
///
/// ### Example
///
/// ```cpp
/// // Use 'border' as a function...
/// Element document = vbox({
/// text("up"),
/// separator(),
/// text("down"),
/// });
/// ```
///
/// ### Output
///
/// ```bash
/// up
/// ────
/// down
/// ```
Element separatorCharacter(std::string value) {
return std::make_shared<Separator>(value);
}
/// @brief Draw a separator in between two element filled with a given pixel.
/// @ingroup dom
/// @see separator
/// @see separatorLight
/// @see separatorHeavy
/// @see separatorDouble
/// @see separatorStyled
///
/// ### Example
///
/// ```cpp
/// Pixel empty;
/// Element document = vbox({
/// text("Up"),
/// separator(empty),
/// text("Down"),
/// })
/// ```
///
/// ### Output
///
/// ```bash
/// Up
///
/// Down
/// ```
Element separator(Pixel pixel) {
return std::make_shared<SeparatorWithPixel>(pixel);
}
} // namespace ftxui

295
src/ftxui/dom/table.cpp Normal file
View File

@@ -0,0 +1,295 @@
#include "ftxui/dom/table.hpp"
#include <algorithm> // for max
#include <memory> // for allocator, shared_ptr, allocator_traits<>::value_type
#include <utility> // for move, swap
#include "ftxui/dom/elements.hpp" // for Element, operator|, text, separatorCharacter, Elements, BorderStyle, Decorator, emptyElement, size, gridbox, EQUAL, flex, flex_shrink, HEIGHT, WIDTH
namespace ftxui {
namespace {
bool IsCell(int x, int y) {
return x % 2 == 1 && y % 2 == 1;
}
static std::string charset[6][6] = {
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{"", "", "", "", "", ""}, //
{" ", " ", " ", " ", " ", " "}, //
};
int Wrap(int input, int modulo) {
input %= modulo;
input += modulo;
input %= modulo;
return input;
}
void Order(int& a, int& b) {
if (a >= b)
std::swap(a, b);
}
} // namespace
Table::Table(std::vector<std::vector<std::string>> input) {
input_dim_y_ = input.size();
input_dim_x_ = 0;
for (auto& row : input)
input_dim_x_ = std::max(input_dim_x_, (int)row.size());
dim_y_ = 2 * input_dim_y_ + 1;
dim_x_ = 2 * input_dim_x_ + 1;
// Reserve space.
elements_.resize(dim_y_);
for (int y = 0; y < dim_y_; ++y)
elements_[y].resize(dim_x_);
// Transfert elements_ from |input| toward |elements_|.
{
int y = 1;
for (auto& row : input) {
int x = 1;
for (auto& cell : row) {
elements_[y][x] = text(cell);
x += 2;
}
y += 2;
}
}
// Add empty element for the border.
for (int y = 0; y < dim_y_; ++y) {
for (int x = 0; x < dim_x_; ++x) {
auto& element = elements_[y][x];
if (IsCell(x, y)) {
if (!element)
element = emptyElement();
continue;
}
element = emptyElement();
}
}
}
TableSelection Table::SelectRow(int index) {
return SelectRectangle(0, -1, index, index);
}
TableSelection Table::SelectRows(int row_min, int row_max) {
return SelectRectangle(0, -1, row_min, row_max);
}
TableSelection Table::SelectColumn(int index) {
return SelectRectangle(index, index, 0, -1);
}
TableSelection Table::SelectColumns(int column_min, int column_max) {
return SelectRectangle(column_min, column_max, 0, -1);
}
TableSelection Table::SelectCell(int column, int row) {
return SelectRectangle(column, column, row, row);
}
TableSelection Table::SelectRectangle(int column_min,
int column_max,
int row_min,
int row_max) {
column_min = Wrap(column_min, input_dim_x_);
column_max = Wrap(column_max, input_dim_x_);
Order(column_min, column_max);
row_min = Wrap(row_min, input_dim_y_);
row_max = Wrap(row_max, input_dim_y_);
Order(row_min, row_max);
TableSelection output;
output.table_ = this;
output.x_min_ = 2 * column_min;
output.x_max_ = 2 * column_max + 2;
output.y_min_ = 2 * row_min;
output.y_max_ = 2 * row_max + 2;
return output;
}
TableSelection Table::SelectAll() {
TableSelection output;
output.table_ = this;
output.x_min_ = 0;
output.x_max_ = dim_x_ - 1;
output.y_min_ = 0;
output.y_max_ = dim_y_ - 1;
return output;
}
Element Table::Render() {
for (int y = 0; y < dim_y_; ++y) {
for (int x = 0; x < dim_x_; ++x) {
auto& it = elements_[y][x];
// Line
if ((x + y) % 2 == 1) {
it = std::move(it) | flex;
continue;
}
// Cells
if ((x % 2) == 1 && (y % 2) == 1) {
it = std::move(it) | flex_shrink;
continue;
}
// Corners
it = std::move(it) | size(WIDTH, EQUAL, 0) | size(HEIGHT, EQUAL, 0);
}
}
return gridbox(std::move(elements_));
}
void TableSelection::Decorate(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
void TableSelection::DecorateCells(Decorator decorator) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateAlternateColumn(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && (x / 2) % modulo == shift) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateAlternateRow(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && (y / 2) % modulo == shift) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateCellsAlternateColumn(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2 && ((x / 2) % modulo == shift)) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::DecorateCellsAlternateRow(Decorator decorator,
int modulo,
int shift) {
for (int y = y_min_; y <= y_max_; ++y) {
for (int x = x_min_; x <= x_max_; ++x) {
if (y % 2 && x % 2 && ((y / 2) % modulo == shift)) {
Element& e = table_->elements_[y][x];
e = std::move(e) | decorator;
}
}
}
}
void TableSelection::Border(BorderStyle style) {
BorderLeft(style);
BorderRight(style);
BorderTop(style);
BorderBottom(style);
table_->elements_[y_min_][x_min_] = text(charset[style][0]);
table_->elements_[y_min_][x_max_] = text(charset[style][1]);
table_->elements_[y_max_][x_min_] = text(charset[style][2]);
table_->elements_[y_max_][x_max_] = text(charset[style][3]);
}
void TableSelection::Separator(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0 || x % 2 == 0) {
Element& e = table_->elements_[y][x];
e = (y % 2) ? separatorCharacter(charset[style][5])
: separatorCharacter(charset[style][4]);
}
}
}
}
void TableSelection::SeparatorVertical(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (x % 2 == 0) {
table_->elements_[y][x] = text(charset[style][5]);
}
}
}
}
void TableSelection::SeparatorHorizontal(BorderStyle style) {
for (int y = y_min_ + 1; y <= y_max_ - 1; ++y) {
for (int x = x_min_ + 1; x <= x_max_ - 1; ++x) {
if (y % 2 == 0) {
table_->elements_[y][x] = text(charset[style][4]);
}
}
}
}
void TableSelection::BorderLeft(BorderStyle style) {
for (int y = y_min_; y <= y_max_; y++)
table_->elements_[y][x_min_] = separatorCharacter(charset[style][5]);
}
void TableSelection::BorderRight(BorderStyle style) {
for (int y = y_min_; y <= y_max_; y++)
table_->elements_[y][x_max_] = separatorCharacter(charset[style][5]);
}
void TableSelection::BorderTop(BorderStyle style) {
for (int x = x_min_; x <= x_max_; x++)
table_->elements_[y_min_][x] = separatorCharacter(charset[style][4]);
}
void TableSelection::BorderBottom(BorderStyle style) {
for (int x = x_min_; x <= x_max_; x++)
table_->elements_[y_max_][x] = separatorCharacter(charset[style][4]);
}
} // 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,716 @@
#include <gtest/gtest-message.h> // for Message
#include <gtest/gtest-test-part.h> // for SuiteApiResolver, TestFactoryImpl, TestPartResult
#include <memory> // for allocator
#include "ftxui/dom/elements.hpp" // for LIGHT, flex, center, EMPTY, DOUBLE
#include "ftxui/dom/node.hpp" // for Render
#include "ftxui/dom/table.hpp"
#include "ftxui/screen/box.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(TableTest, Empty) {
auto table = Table({});
Screen screen(5, 5);
Render(screen, table.Render());
EXPECT_EQ(
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, Basic) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
"efgh \r\n"
"ijkl \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalEmpty) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(EMPTY);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a b c d \r\n"
"e f g h \r\n"
"i j k l \r\n"
"m n o p \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalEmpty) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(EMPTY);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
" \r\n"
"efgh \r\n"
" \r\n"
"ijkl \r\n"
" \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"abcd \r\n"
"──── \r\n"
"efgh \r\n"
"──── \r\n"
"ijkl \r\n"
"──── \r\n"
"mnop \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"e│f│g│h \r\n"
"i│j│k│l \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Separator(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorVerticalHorizontalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorVertical(LIGHT);
table.SelectAll().SeparatorHorizontal(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SeparatorHorizontalVerticalLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().SeparatorHorizontal(LIGHT);
table.SelectAll().SeparatorVertical(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a│b│c│d \r\n"
"─┼─┼─┼─ \r\n"
"e│f│g│h \r\n"
"─┼─┼─┼─ \r\n"
"i│j│k│l \r\n"
"─┼─┼─┼─ \r\n"
"m│n│o│p \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, BorderLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌────┐ \r\n"
"│abcd│ \r\n"
"│efgh│ \r\n"
"│ijkl│ \r\n"
"│mnop│ \r\n"
"└────┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, BorderSeparatorLight) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRow) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
"┌────┐ \r\n"
"│efgh│ \r\n"
"└────┘ \r\n"
" ijkl \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRowNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(-2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
" efgh \r\n"
"┌────┐ \r\n"
"│ijkl│ \r\n"
"└────┘ \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumn) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumn(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
"a│b│cd \r\n"
"e│f│gh \r\n"
"i│j│kl \r\n"
"m│n│op \r\n"
" └─┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumnNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumn(-2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
"ab│c│d \r\n"
"ef│g│h \r\n"
"ij│k│l \r\n"
"mn│o│p \r\n"
" └─┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, CrossingBorders) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
table.SelectColumn(1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌─┐ \r\n"
" a│b│cd \r\n"
"┌─┼─┼──┐ \r\n"
"│e│f│gh│ \r\n"
"└─┼─┼──┘ \r\n"
" i│j│kl \r\n"
" m│n│op \r\n"
" └─┘ \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, CrossingBordersLightAndDouble) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRow(1).Border(LIGHT);
table.SelectColumn(1).Border(DOUBLE);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ╔═╗ \r\n"
" a║b║cd \r\n"
"┌─╫─╫──┐ \r\n"
"│e║f║gh│ \r\n"
"└─╫─╫──┘ \r\n"
" i║j║kl \r\n"
" m║n║op \r\n"
" ╚═╝ \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumns) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌──┐ \r\n"
"a│bc│d \r\n"
"e│fg│h \r\n"
"i│jk│l \r\n"
"m│no│p \r\n"
" └──┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRows) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRows(1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" abcd \r\n"
"┌────┐ \r\n"
"│efgh│ \r\n"
"│ijkl│ \r\n"
"└────┘ \r\n"
" mnop \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectRectangle) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectRectangle(1, 2, 1, 2).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
"a bc d \r\n"
" ┌──┐ \r\n"
"e│fg│h \r\n"
"i│jk│l \r\n"
" └──┘ \r\n"
"m no p \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectColumnsNegative) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(1, -1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌───┐ \r\n"
"a│bcd│ \r\n"
"e│fgh│ \r\n"
"i│jkl│ \r\n"
"m│nop│ \r\n"
" └───┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, SelectInverted) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectColumns(-1, 1).Border(LIGHT);
Screen screen(10, 10);
Render(screen, table.Render());
EXPECT_EQ(
" ┌───┐ \r\n"
"a│bcd│ \r\n"
"e│fgh│ \r\n"
"i│jkl│ \r\n"
"m│nop│ \r\n"
" └───┘ \r\n"
" \r\n"
" \r\n"
" \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlex) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬────────────┬─┬─┐\r\n"
"│a│b │c│d│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│e│f │g│h│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│i│j │k│l│\r\n"
"├─┼────────────┼─┼─┤\r\n"
"│m│n │o│p│\r\n"
"└─┴────────────┴─┴─┘\r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlexCenter) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
table.SelectColumn(1).DecorateCells(center);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnCenter) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).DecorateCells(center);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ \r\n"
" ",
screen.ToString());
}
TEST(TableTest, ColumnFlexTwo) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectColumn(1).Decorate(flex);
table.SelectColumn(3).Decorate(flex);
Screen screen(20, 10);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬──────┬─┬───────┐\r\n"
"│a│b │c│d │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│e│f │g│h │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│i│j │k│l │\r\n"
"├─┼──────┼─┼───────┤\r\n"
"│m│n │o│p │\r\n"
"└─┴──────┴─┴───────┘\r\n"
" ",
screen.ToString());
}
TEST(TableTest, RowFlex) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectRow(1).Decorate(flex);
Screen screen(10, 20);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"└─┴─┴─┴─┘ ",
screen.ToString());
}
TEST(TableTest, RowFlexTwo) {
auto table = Table({
{"a", "b", "c", "d"},
{"e", "f", "g", "h"},
{"i", "j", "k", "l"},
{"m", "n", "o", "p"},
});
table.SelectAll().Border(LIGHT);
table.SelectAll().Separator(LIGHT);
table.SelectRow(1).Decorate(flex);
table.SelectRow(3).Decorate(flex);
Screen screen(10, 20);
Render(screen, table.Render());
EXPECT_EQ(
"┌─┬─┬─┬─┐ \r\n"
"│a│b│c│d│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│e│f│g│h│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│i│j│k│l│ \r\n"
"├─┼─┼─┼─┤ \r\n"
"│m│n│o│p│ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"│ │ │ │ │ \r\n"
"└─┴─┴─┴─┘ ",
screen.ToString());
}
// 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

@@ -75,6 +75,18 @@ Dimensions Dimension::Fit(Element& e) {
std::min(e->requirement().min_y, size.dimy)};
}
/// An element of size 0x0 drawing nothing.
/// @ingroup dom
Element emptyElement() {
class Impl : public Node {
void ComputeRequirement() override {
requirement_.min_x = 0;
requirement_.min_x = 0;
}
};
return std::make_unique<Impl>();
}
} // namespace ftxui
// Copyright 2020 Arthur Sonzogni. All rights reserved.

View File

@@ -1,4 +1,3 @@
#include <stdint.h> // for uint16_t
#include <iostream> // for operator<<, stringstream, basic_ostream, flush, cout, ostream
#include <map> // for _Rb_tree_const_iterator, map, operator!=, operator==
#include <memory> // for allocator, allocator_traits<>::value_type
@@ -98,16 +97,21 @@ struct TileEncoding {
unsigned int down : 2;
unsigned int round : 1;
// clang-format off
bool operator<(const TileEncoding& other) const {
union Converter {
TileEncoding input;
uint16_t output = 0;
};
Converter a, b;
a.input = *this;
b.input = other;
return a.output < b.output;
if (left < other.left) return true;
if (left > other.left) return false;
if (top < other.top) return true;
if (top > other.top) return false;
if (right < other.right) return true;
if (right > other.right) return false;
if (down < other.down) return true;
if (down > other.down) return false;
if (round < other.round) return true;
if (round > other.round) return false;
return false;
}
// clang-format on
};
// clang-format off

View File

@@ -18,9 +18,18 @@
namespace ftxui {
#if defined(__EMSCRIPTEN__)
static Dimensions fallback_size{140, 43};
#elif defined(_WIN32)
// The Microsoft default "cmd" returns errors above.
static Dimensions fallback_size{80, 80};
#else
static Dimensions fallback_size{80, 25};
#endif
Dimensions Terminal::Size() {
#if defined(__EMSCRIPTEN__)
return Dimensions{140, 43};
return fallback_size;
#elif defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
@@ -29,16 +38,24 @@ Dimensions Terminal::Size() {
csbi.srWindow.Bottom - csbi.srWindow.Top + 1};
}
// The Microsoft default "cmd" returns errors above.
return Dimensions{80, 80};
return fallback_size;
#else
winsize w;
winsize w{};
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
if (w.ws_col == 0 || w.ws_row == 0) {
return fallback_size;
}
return Dimensions{w.ws_col, w.ws_row};
#endif
}
/// @brief Override terminal size in case auto-detection fails
/// @param fallbackSize Terminal dimensions to fallback to
void Terminal::SetFallbackSize(const Dimensions& fallbackSize) {
fallback_size = fallbackSize;
}
namespace {
const char* Safe(const char* c) {