3 Commits

Author SHA1 Message Date
Harri Pehkonen
63c39010b5 Merge 143b24c6a5 into f3448f49f1 2025-08-17 12:16:30 +00:00
Harri Pehkonen
143b24c6a5 Add opt-in piped input support for POSIX systems
Enables applications to read piped data while maintaining interactive
keyboard input by redirecting stdin to /dev/tty when explicitly enabled.
2025-08-17 14:08:51 +02:00
Arthur Sonzogni
f3448f49f1 Improve example style (#1097)
Some checks are pending
Build / Bazel, ${{ matrix.cxx }}, ${{ matrix.os }} (cl, cl, windows-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.cxx }}, ${{ matrix.os }} (clang, clang++, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.cxx }}, ${{ matrix.os }} (clang, clang++, ubuntu-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.cxx }}, ${{ matrix.os }} (gcc, g++, macos-latest) (push) Waiting to run
Build / Bazel, ${{ matrix.cxx }}, ${{ matrix.os }} (gcc, g++, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (cl, Windows MSVC, windows-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (gcc, Linux GCC, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, Linux Clang, ubuntu-latest) (push) Waiting to run
Build / CMake, ${{ matrix.compiler }}, ${{ matrix.os }} (llvm, llvm-cov gcov, MacOS clang, macos-latest) (push) Waiting to run
Build / Test modules (llvm, ubuntu-latest) (push) Waiting to run
Documentation / documentation (push) Waiting to run
Based uppon @yurenchen000 suggestion.

Fixed:https://github.com/ArthurSonzogni/FTXUI/issues/1090
2025-08-17 13:53:19 +02:00
19 changed files with 370 additions and 224 deletions

View File

@@ -169,15 +169,13 @@ ftxui_cc_library(
"src/ftxui/component/util.cpp",
"src/ftxui/component/window.cpp",
# Private header from ftxui:dom.
"src/ftxui/dom/node_decorator.hpp",
# Private header from ftxui:screen.
"src/ftxui/screen/string_internal.hpp",
"src/ftxui/screen/util.hpp",
# Private header.
"include/ftxui/util/warn_windows_macro.hpp",
],
hdrs = [
"include/ftxui/component/animation.hpp",

View File

@@ -29,8 +29,6 @@ Next
### Component
- Fix ScreenInteractive::FixedSize screen stomps on the preceding terminal
output. Thanks @zozowell in #1064.
- Fix vertical `ftxui::Slider`. The "up" key was previously decreasing the
value. Thanks @its-pablo in #1093 for reporting the issue.
6.1.9 (2025-05-07)

View File

@@ -178,8 +178,8 @@ include(cmake/ftxui_install.cmake)
include(cmake/ftxui_package.cmake)
include(cmake/ftxui_modules.cmake)
add_subdirectory(examples)
add_subdirectory(doc)
add_subdirectory(examples)
# You can generate ./examples_modules/ by running
# ./tools/generate_examples_modules.sh

View File

@@ -1,4 +1,3 @@
<p align="center">
<img src="https://github.com/ArthurSonzogni/FTXUI/assets/4759106/6925b6da-0a7e-49d9-883c-c890e1f36007" alt="Demo image"></img>
<br/>
@@ -363,8 +362,6 @@ Feel free to add your projects here:
- [FTB - tertminal file browser](https://github.com/Cyxuan0311/FTB)
- [openJuice](https://github.com/mikomikotaishi/openJuice)
- [SHOOT!](https://github.com/ShingZhanho/ENGG1340-Project-25Spring)
- [VerifySN (Fast Hash Tool)](https://github.com/d06i/verifySN)
- [tic-tac-toe](https://github.com/birland/tic-tac-toe)
### [cpp-best-practices/game_jam](https://github.com/cpp-best-practices/game_jam)
@@ -381,6 +378,22 @@ Several games using the FTXUI have been made during the Game Jam:
- [smoothlife](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/smoothlife.md)
- [Consu](https://github.com/cpp-best-practices/game_jam/blob/main/Jam1_April_2022/consu.md)
## Advanced Usage
### Piped Input Support
If your application reads from stdin (piped data) and also needs interactive keyboard input:
```cpp
auto screen = ScreenInteractive::Fullscreen();
screen.HandlePipedInput(true); // Enable before Loop()
screen.Loop(component);
```
This allows commands like `cat data.txt | your_app` to work with full keyboard interaction.
**Note:** This feature is only available on POSIX systems (Linux/macOS). On Windows, the method call is a no-op.
## Build using CMake
It is **highly** recommended to use CMake FetchContent to depend on FTXUI so you may specify which commit you would like to depend on.

Submodule build/_deps/googlebenchmark-src deleted from 015d1a091a

View File

@@ -1,42 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
cmake_minimum_required(VERSION 3.31.6)
# Reject any attempt to use a toolchain file. We must not use one because
# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment
# variable is set, the cache variable will have been initialized from it.
unset(CMAKE_TOOLCHAIN_FILE CACHE)
unset(ENV{CMAKE_TOOLCHAIN_FILE})
# We name the project and the target for the ExternalProject_Add() call
# to something that will highlight to the user what we are working on if
# something goes wrong and an error message is produced.
project(googlebenchmark-populate NONE)
# Pass through things we've already detected in the main project to avoid
# paying the cost of redetecting them again in ExternalProject_Add()
set(GIT_EXECUTABLE [==[/usr/bin/git]==])
set(GIT_VERSION_STRING [==[2.51.0]==])
set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
[==[/usr/bin/git;2.51.0]==]
)
include(ExternalProject)
ExternalProject_Add(googlebenchmark-populate
"UPDATE_DISCONNECTED" "False" "GIT_REPOSITORY" "https://github.com/google/benchmark" "EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR" "GIT_TAG" "015d1a091af6937488242b70121858bce8fd40e9" "GIT_PROGRESS" "TRUE"
SOURCE_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googlebenchmark-src"
BINARY_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googlebenchmark-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
USES_TERMINAL_DOWNLOAD YES
USES_TERMINAL_UPDATE YES
USES_TERMINAL_PATCH YES
)

Submodule build/_deps/googletest-src deleted from 23ef29555e

View File

@@ -1,42 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
cmake_minimum_required(VERSION 3.31.6)
# Reject any attempt to use a toolchain file. We must not use one because
# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment
# variable is set, the cache variable will have been initialized from it.
unset(CMAKE_TOOLCHAIN_FILE CACHE)
unset(ENV{CMAKE_TOOLCHAIN_FILE})
# We name the project and the target for the ExternalProject_Add() call
# to something that will highlight to the user what we are working on if
# something goes wrong and an error message is produced.
project(googletest-populate NONE)
# Pass through things we've already detected in the main project to avoid
# paying the cost of redetecting them again in ExternalProject_Add()
set(GIT_EXECUTABLE [==[/usr/bin/git]==])
set(GIT_VERSION_STRING [==[2.51.0]==])
set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
[==[/usr/bin/git;2.51.0]==]
)
include(ExternalProject)
ExternalProject_Add(googletest-populate
"UPDATE_DISCONNECTED" "False" "GIT_REPOSITORY" "https://github.com/google/googletest" "EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR" "GIT_TAG" "23ef29555ef4789f555f1ba8c51b4c52975f0907" "GIT_PROGRESS" "TRUE"
SOURCE_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googletest-src"
BINARY_DIR "/home/runner/work/FTXUI/FTXUI/build/_deps/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
USES_TERMINAL_DOWNLOAD YES
USES_TERMINAL_UPDATE YES
USES_TERMINAL_PATCH YES
)

View File

@@ -11,9 +11,6 @@ include(CMakePackageConfigHelpers)
install(
TARGETS screen dom component
EXPORT ftxui-targets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
)
install(

View File

@@ -1,104 +1,24 @@
@page installation_conan Conan
@tableofcontents
FTXUI can be easily obtained and integrated into your project using the Conan package manager.
## Prerequisites
First, ensure that Conan is installed on your system. If not, you can install it via pip:
```powershell
pip install conan
```
Conan often works in tandem with CMake, so you will need to have CMake installed as well. Once you have confirmed both Conan and CMake are installed, create a project directory, for example, `ftxui-demo`:
```powershell
mkdir C:\ftxui-demo
cd C:\ftxui-demo
```
## Configuration
After ensuring your environment is set up correctly, create a Conan configuration file `conanfile.txt`. This file is used to declare your project's dependencies. The community-maintained package for FTXUI can be found on [Conan Center](https://conan.io/center/recipes/ftxui).
Unofficial recipe for FTXUI exists on Conan Center:
<https://conan.io/center/recipes/ftxui>
> [!note]
> This is an unofficial build script. This means it is not maintained by the FTXUI
> team but by the community. The package maintainer appears to actively update it
> to the latest releases. Many thanks to the maintainer for their work!
> This is an unofficial recipe. That means it is not maintained by the FTXUI
> team, but by the community. The package maintainers seems to actively update
> the package to the latest version. Thanks to the maintainers for their work!
@todo If you are familiar with the process, please consider adding an "official" build script to Conan Center.
This could be a GitHub Action that automatically updates Conan Center upon new releases.
```ini
[requires]
ftxui/6.0.2
@todo Add instructions on how to use the conan recipe.
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout
```
## Install Dependencies and Build
Once configured, run the following command to install FTXUI and its dependencies:
```powershell
conan install . --output-folder=build --build=missing
```
This will download and install `ftxui/6.0.2` along with all its dependencies from Conan's remote repositories.
After the installation completes, you can test it by creating a `demo.cpp` file in your project directory:
```cpp
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/elements.hpp>
#include <iostream>
int main() {
using namespace ftxui;
auto document = hbox({
text(" Hello "),
text("FTXUI ") | bold | color(Color::Red),
text(" world! ")
});
auto screen = Screen::Create(Dimension::Full(), Dimension::Fit(document));
Render(screen, document);
std::cout << screen.ToString() << std::endl;
return 0;
}
```
If the test is successful, you can then create a `CMakeLists.txt` file in the project directory:
```cmake
cmake_minimum_required(VERSION 3.20)
project(ftxui-demo)
# Set the C++ standard
set(CMAKE_CXX_STANDARD 20)
# Find the FTXUI package installed via Conan
find_package(ftxui CONFIG REQUIRED)
# Create the executable
add_executable(demo demo.cpp)
# Link the executable to the FTXUI library
target_link_libraries(demo PRIVATE ftxui::component)
```
@todo 考虑到中国多数地区使用Conan很有可能遇到各种网络问题我想做一个定制的版本说明但是我对conan的了解有限再加上没有找到合适的资料因此这个计划短暂的被搁置了如果您知道方法欢迎在[中文版本](xiaoditx.girhub.io/public/docs/ftxui%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91/installation/conan/)的下方留下评论以提醒我
---
@todo Please consider adding an "official" recipe to Conan Center if know how.
It could be a github action that will automatically update the conan center
when a new release is made.
<div class="section_buttons">
| Previous |
|:------------------|
| [Getting Started](getting-started.html) |
</div>
</div>

View File

@@ -17,12 +17,10 @@ add_subdirectory(dom)
if (EMSCRIPTEN)
get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES)
foreach(file
"index.css"
"index.html"
"index.mjs"
"run_webassembly.py"
"sw.js"
)
"index.css"
"run_webassembly.py")
configure_file(${file} ${file})
endforeach(file)
endif()

View File

@@ -34,8 +34,8 @@ int main() {
int value = 50;
// clang-format off
auto btn_dec_01 = Button("-1", [&] { value -= 1; }, Style());
auto btn_inc_01 = Button("+1", [&] { value += 1; }, Style());
auto btn_dec_01 = Button("-1", [&] { value += 1; }, Style());
auto btn_inc_01 = Button("+1", [&] { value -= 1; }, Style());
auto btn_dec_10 = Button("-10", [&] { value -= 10; }, Style());
auto btn_inc_10 = Button("+10", [&] { value += 10; }, Style());
// clang-format on

View File

@@ -4,7 +4,7 @@
#ifndef FTXUI_COMPONENT_RECEIVER_HPP_
#define FTXUI_COMPONENT_RECEIVER_HPP_
#include <ftxui/util/warn_windows_macro.hpp>
#include <ftxui/util/warn_windows_macro.h>
#include <algorithm> // for copy, max
#include <atomic> // for atomic, __atomic_base
#include <condition_variable> // for condition_variable

View File

@@ -47,6 +47,7 @@ class ScreenInteractive : public Screen {
// Options. Must be called before Loop().
void TrackMouse(bool enable = true);
void HandlePipedInput(bool enable = true);
// Return the currently active screen, nullptr if none.
static ScreenInteractive* Active();
@@ -141,6 +142,13 @@ class ScreenInteractive : public Screen {
bool force_handle_ctrl_c_ = true;
bool force_handle_ctrl_z_ = true;
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
// Piped input handling state (POSIX only)
bool handle_piped_input_ = false;
bool stdin_was_redirected_ = false;
int original_stdin_fd_ = -1;
#endif
// The style of the cursor to restore on exit.
int cursor_reset_shape_ = 1;

View File

@@ -60,7 +60,7 @@ class Screen : public Image {
BarBlinking = 5,
Bar = 6,
};
Shape shape = Hidden;
Shape shape;
};
Cursor cursor() const { return cursor_; }

View File

@@ -372,6 +372,24 @@ void ScreenInteractive::TrackMouse(bool enable) {
track_mouse_ = enable;
}
/// @brief Enable or disable automatic piped input handling.
/// When enabled, FTXUI will detect piped input and redirect stdin to /dev/tty
/// for keyboard input, allowing applications to read piped data while still
/// receiving interactive keyboard events.
/// @param enable Whether to enable piped input handling
/// @note This must be called before Loop().
/// @note This feature is disabled by default for backward compatibility.
/// @note This feature is only available on POSIX systems (Linux/macOS).
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
void ScreenInteractive::HandlePipedInput(bool enable) {
handle_piped_input_ = enable;
}
#else
void ScreenInteractive::HandlePipedInput(bool /*enable*/) {
// This feature is not supported on this platform.
}
#endif
/// @brief Add a task to the main loop.
/// It will be executed later, after every other scheduled tasks.
void ScreenInteractive::Post(Task task) {
@@ -658,6 +676,28 @@ void ScreenInteractive::Install() {
// ensure it is fully applied:
Flush();
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
// Handle piped input redirection if explicitly enabled by the application.
// This allows applications to read data from stdin while still receiving
// keyboard input from the terminal for interactive use.
if (handle_piped_input_ && !stdin_was_redirected_ && !isatty(STDIN_FILENO)) {
// Save the current stdin so we can restore it later
original_stdin_fd_ = dup(STDIN_FILENO);
if (original_stdin_fd_ >= 0) {
// Redirect stdin to the controlling terminal for keyboard input
if (freopen("/dev/tty", "r", stdin) != nullptr) {
stdin_was_redirected_ = true;
} else {
// Failed to open /dev/tty (containers, headless systems, etc.)
// Clean up and continue without redirection
close(original_stdin_fd_);
original_stdin_fd_ = -1;
}
}
// If dup() failed, we silently continue without redirection
}
#endif
quit_ = false;
PostAnimationTask();
@@ -666,6 +706,17 @@ void ScreenInteractive::Install() {
// private
void ScreenInteractive::Uninstall() {
ExitNow();
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
// Restore stdin to its original state if we redirected it
if (stdin_was_redirected_ && original_stdin_fd_ >= 0) {
dup2(original_stdin_fd_, STDIN_FILENO);
close(original_stdin_fd_);
original_stdin_fd_ = -1;
stdin_was_redirected_ = false;
}
#endif
OnExit();
}

View File

@@ -0,0 +1,219 @@
// Copyright 2025 Arthur Sonzogni. All rights reserved.
// Use of this source code is governed by the MIT license that can be found in
// the LICENSE file.
#include <gtest/gtest.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <sys/stat.h>
#include "ftxui/component/component.hpp"
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/elements.hpp"
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
namespace ftxui {
namespace {
// Test fixture for piped input functionality
class PipedInputTest : public ::testing::Test {
protected:
void SetUp() override {
// Save original stdin for restoration
original_stdin_ = dup(STDIN_FILENO);
}
void TearDown() override {
// Restore original stdin
if (original_stdin_ >= 0) {
dup2(original_stdin_, STDIN_FILENO);
close(original_stdin_);
}
}
// Create a pipe and redirect stdin to read from it
void SetupPipedStdin() {
if (pipe(pipe_fds_) == 0) {
dup2(pipe_fds_[0], STDIN_FILENO);
close(pipe_fds_[0]);
// Keep write end open for writing test data
piped_stdin_setup_ = true;
}
}
// Write test data to the piped stdin
void WriteToPipedStdin(const std::string& data) {
if (piped_stdin_setup_) {
write(pipe_fds_[1], data.c_str(), data.length());
close(pipe_fds_[1]); // Close write end to signal EOF
}
}
// Check if /dev/tty is available (not available in some CI environments)
bool IsTtyAvailable() {
struct stat st;
return stat("/dev/tty", &st) == 0;
}
private:
int original_stdin_ = -1;
int pipe_fds_[2] = {-1, -1};
bool piped_stdin_setup_ = false;
};
TEST_F(PipedInputTest, DefaultBehaviorNoChange) {
// Test that HandlePipedInput is disabled by default
auto screen = ScreenInteractive::TerminalOutput();
auto component = Renderer([] { return text("test"); });
SetupPipedStdin();
WriteToPipedStdin("test data\n");
// Install should not redirect stdin since HandlePipedInput not called
screen.Install();
// Stdin should still be the pipe (isatty should return false)
EXPECT_FALSE(isatty(STDIN_FILENO));
screen.Uninstall();
}
TEST_F(PipedInputTest, ExplicitlyDisabled) {
// Test that explicitly disabling works
auto screen = ScreenInteractive::TerminalOutput();
screen.HandlePipedInput(false);
auto component = Renderer([] { return text("test"); });
SetupPipedStdin();
WriteToPipedStdin("test data\n");
screen.Install();
// Stdin should still be the pipe since feature is disabled
EXPECT_FALSE(isatty(STDIN_FILENO));
screen.Uninstall();
}
TEST_F(PipedInputTest, PipedInputDetectionAndRedirection) {
if (!IsTtyAvailable()) {
GTEST_SKIP() << "/dev/tty not available in this environment";
}
auto screen = ScreenInteractive::TerminalOutput();
screen.HandlePipedInput(true); // Explicitly enable
auto component = Renderer([] { return text("test"); });
SetupPipedStdin();
WriteToPipedStdin("test data\n");
// Before install: stdin should be piped
EXPECT_FALSE(isatty(STDIN_FILENO));
screen.Install();
// After install with piped input handling: stdin should be redirected to tty
EXPECT_TRUE(isatty(STDIN_FILENO));
screen.Uninstall();
// After uninstall: stdin should be restored to original state
// Note: This will be the pipe we set up, so it should be non-tty
EXPECT_FALSE(isatty(STDIN_FILENO));
}
TEST_F(PipedInputTest, NormalStdinUnchanged) {
// Test that normal stdin (not piped) is not affected
auto screen = ScreenInteractive::TerminalOutput();
screen.HandlePipedInput(true);
auto component = Renderer([] { return text("test"); });
// Don't setup piped stdin - use normal stdin
bool original_isatty = isatty(STDIN_FILENO);
screen.Install();
// Stdin should remain unchanged
EXPECT_EQ(original_isatty, isatty(STDIN_FILENO));
screen.Uninstall();
// Stdin should still be unchanged
EXPECT_EQ(original_isatty, isatty(STDIN_FILENO));
}
TEST_F(PipedInputTest, MultipleInstallUninstallCycles) {
if (!IsTtyAvailable()) {
GTEST_SKIP() << "/dev/tty not available in this environment";
}
auto screen = ScreenInteractive::TerminalOutput();
screen.HandlePipedInput(true);
auto component = Renderer([] { return text("test"); });
SetupPipedStdin();
WriteToPipedStdin("test data\n");
// First cycle
screen.Install();
EXPECT_TRUE(isatty(STDIN_FILENO));
screen.Uninstall();
EXPECT_FALSE(isatty(STDIN_FILENO));
// Second cycle should work the same
screen.Install();
EXPECT_TRUE(isatty(STDIN_FILENO));
screen.Uninstall();
EXPECT_FALSE(isatty(STDIN_FILENO));
}
TEST_F(PipedInputTest, HandlePipedInputMethodBehavior) {
auto screen = ScreenInteractive::TerminalOutput();
// Test method can be called multiple times
screen.HandlePipedInput(true);
screen.HandlePipedInput(false);
screen.HandlePipedInput(true);
// Should be enabled after last call
SetupPipedStdin();
WriteToPipedStdin("test data\n");
if (IsTtyAvailable()) {
screen.Install();
EXPECT_TRUE(isatty(STDIN_FILENO));
screen.Uninstall();
}
}
// Test the graceful fallback when /dev/tty is not available
// This test simulates environments like containers where /dev/tty might not exist
TEST_F(PipedInputTest, GracefulFallbackWhenTtyUnavailable) {
auto screen = ScreenInteractive::TerminalOutput();
screen.HandlePipedInput(true);
auto component = Renderer([] { return text("test"); });
SetupPipedStdin();
WriteToPipedStdin("test data\n");
// This test doesn't directly mock /dev/tty unavailability since that's hard to do
// in a unit test environment, but the code path handles freopen() failure gracefully
screen.Install();
// The behavior depends on whether /dev/tty is available
// If available, stdin gets redirected; if not, it remains piped
// Both behaviors are correct
screen.Uninstall();
// After uninstall, stdin should be restored
EXPECT_FALSE(isatty(STDIN_FILENO)); // Should still be our test pipe
}
} // namespace
} // namespace ftxui
#endif // !defined(_WIN32) && !defined(__EMSCRIPTEN__)

View File

@@ -33,20 +33,6 @@ Decorator flexDirection(Direction direction) {
return xflex; // NOT_REACHED()
}
Direction Opposite(Direction d) {
switch (d) {
case Direction::Up:
return Direction::Down;
case Direction::Down:
return Direction::Up;
case Direction::Left:
return Direction::Right;
case Direction::Right:
return Direction::Left;
}
return d; // NOT_REACHED()
}
template <class T>
class SliderBase : public SliderOption<T>, public ComponentBase {
public:
@@ -61,15 +47,59 @@ class SliderBase : public SliderOption<T>, public ComponentBase {
flexDirection(this->direction) | reflect(gauge_box_) | gauge_color;
}
void OnDirection(Direction pressed) {
if (pressed == this->direction) {
this->value() += this->increment();
return;
void OnLeft() {
switch (this->direction) {
case Direction::Right:
this->value() -= this->increment();
break;
case Direction::Left:
this->value() += this->increment();
break;
case Direction::Up:
case Direction::Down:
break;
}
}
if (pressed == Opposite(this->direction)) {
this->value() -= this->increment();
return;
void OnRight() {
switch (this->direction) {
case Direction::Right:
this->value() += this->increment();
break;
case Direction::Left:
this->value() -= this->increment();
break;
case Direction::Up:
case Direction::Down:
break;
}
}
void OnUp() {
switch (this->direction) {
case Direction::Up:
this->value() -= this->increment();
break;
case Direction::Down:
this->value() += this->increment();
break;
case Direction::Left:
case Direction::Right:
break;
}
}
void OnDown() {
switch (this->direction) {
case Direction::Down:
this->value() += this->increment();
break;
case Direction::Up:
this->value() -= this->increment();
break;
case Direction::Left:
case Direction::Right:
break;
}
}
@@ -80,16 +110,16 @@ class SliderBase : public SliderOption<T>, public ComponentBase {
T old_value = this->value();
if (event == Event::ArrowLeft || event == Event::Character('h')) {
OnDirection(Direction::Left);
OnLeft();
}
if (event == Event::ArrowRight || event == Event::Character('l')) {
OnDirection(Direction::Right);
OnRight();
}
if (event == Event::ArrowUp || event == Event::Character('k')) {
OnDirection(Direction::Up);
OnDown();
}
if (event == Event::ArrowDown || event == Event::Character('j')) {
OnDirection(Direction::Down);
OnUp();
}
this->value() = std::max(this->min(), std::min(this->max(), this->value()));

View File

@@ -81,7 +81,7 @@ class Size : public Node {
} // namespace
/// @brief Apply a constraint on the size of an element.
/// @param direction Whether the WIDTH or the HEIGHT of the element must be
/// @param direction Whether the WIDTH of the HEIGHT of the element must be
/// constrained.
/// @param constraint The type of constaint.
/// @param value The value.