2021-06-22 05:08:39 +08:00
|
|
|
#include <memory> // for __shared_ptr_access
|
|
|
|
#include <utility> // for move
|
|
|
|
|
2021-05-27 21:22:53 +08:00
|
|
|
#include "ftxui/component/captured_mouse.hpp" // for CapturedMouse
|
2021-06-22 05:08:39 +08:00
|
|
|
#include "ftxui/component/component.hpp" // for Component, Make, Horizontal, Vertical, ResizableSplitBottom, ResizableSplitLeft, ResizableSplitRight, ResizableSplitTop
|
|
|
|
#include "ftxui/component/component_base.hpp" // for ComponentBase
|
|
|
|
#include "ftxui/component/event.hpp" // for Event
|
|
|
|
#include "ftxui/component/mouse.hpp" // for Mouse, Mouse::Left, Mouse::Pressed, Mouse::Released
|
|
|
|
#include "ftxui/dom/elements.hpp" // for operator|, reflect, Element, separator, size, EQUAL, xflex, yflex, hbox, vbox, HEIGHT, WIDTH
|
|
|
|
#include "ftxui/screen/box.hpp" // for Box
|
2021-05-27 21:22:53 +08:00
|
|
|
|
|
|
|
namespace ftxui {
|
|
|
|
namespace {
|
|
|
|
|
2021-05-27 21:46:23 +08:00
|
|
|
class ResizableSplitLeftBase : public ComponentBase {
|
2021-05-27 21:22:53 +08:00
|
|
|
public:
|
2021-05-27 21:46:23 +08:00
|
|
|
ResizableSplitLeftBase(Component main, Component child, int* main_size)
|
2021-05-27 21:22:53 +08:00
|
|
|
: main_(main), child_(child), main_size_(main_size) {
|
|
|
|
Add(Container::Horizontal({
|
|
|
|
main,
|
|
|
|
child,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnEvent(Event event) final {
|
|
|
|
if (event.is_mouse())
|
|
|
|
return OnMouseEvent(std::move(event));
|
|
|
|
return ComponentBase::OnEvent(std::move(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnMouseEvent(Event event) {
|
|
|
|
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
|
|
|
captured_mouse_.reset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.mouse().button == Mouse::Left &&
|
|
|
|
event.mouse().motion == Mouse::Pressed &&
|
|
|
|
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
|
|
|
!captured_mouse_) {
|
|
|
|
captured_mouse_ = CaptureMouse(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (captured_mouse_) {
|
2021-09-08 15:36:37 +08:00
|
|
|
*main_size_ = event.mouse().x - box_.x_min;
|
2021-05-27 21:22:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ComponentBase::OnEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
Element Render() final {
|
|
|
|
return hbox({
|
|
|
|
main_->Render() | size(WIDTH, EQUAL, *main_size_),
|
|
|
|
separator() | reflect(separator_box_),
|
|
|
|
child_->Render() | xflex,
|
|
|
|
}) |
|
2021-09-08 15:36:37 +08:00
|
|
|
reflect(box_);
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Component main_;
|
|
|
|
Component child_;
|
|
|
|
int* const main_size_;
|
|
|
|
CapturedMouse captured_mouse_;
|
|
|
|
Box separator_box_;
|
2021-09-08 15:36:37 +08:00
|
|
|
Box box_;
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
2021-06-22 05:08:39 +08:00
|
|
|
class ResizableSplitRightBase : public ComponentBase {
|
2021-05-27 21:22:53 +08:00
|
|
|
public:
|
2021-05-27 21:46:23 +08:00
|
|
|
ResizableSplitRightBase(Component main, Component child, int* main_size)
|
2021-05-27 21:22:53 +08:00
|
|
|
: main_(main), child_(child), main_size_(main_size) {
|
|
|
|
Add(Container::Horizontal({
|
|
|
|
child,
|
|
|
|
main,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnEvent(Event event) final {
|
|
|
|
if (event.is_mouse())
|
|
|
|
return OnMouseEvent(std::move(event));
|
|
|
|
return ComponentBase::OnEvent(std::move(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnMouseEvent(Event event) {
|
|
|
|
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
|
|
|
captured_mouse_.reset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.mouse().button == Mouse::Left &&
|
|
|
|
event.mouse().motion == Mouse::Pressed &&
|
|
|
|
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
|
|
|
!captured_mouse_) {
|
|
|
|
captured_mouse_ = CaptureMouse(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (captured_mouse_) {
|
2021-09-08 15:36:37 +08:00
|
|
|
*main_size_ = box_.x_max - event.mouse().x;
|
2021-05-27 21:22:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ComponentBase::OnEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
Element Render() final {
|
|
|
|
return hbox({
|
|
|
|
child_->Render() | xflex,
|
|
|
|
separator() | reflect(separator_box_),
|
|
|
|
main_->Render() | size(WIDTH, EQUAL, *main_size_),
|
|
|
|
}) |
|
2021-09-08 15:36:37 +08:00
|
|
|
reflect(box_);
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Component main_;
|
|
|
|
Component child_;
|
|
|
|
int* const main_size_;
|
|
|
|
CapturedMouse captured_mouse_;
|
|
|
|
Box separator_box_;
|
2021-09-08 15:36:37 +08:00
|
|
|
Box box_;
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
2021-06-22 05:08:39 +08:00
|
|
|
class ResizableSplitTopBase : public ComponentBase {
|
2021-05-27 21:22:53 +08:00
|
|
|
public:
|
2021-05-27 21:46:23 +08:00
|
|
|
ResizableSplitTopBase(Component main, Component child, int* main_size)
|
2021-05-27 21:22:53 +08:00
|
|
|
: main_(main), child_(child), main_size_(main_size) {
|
|
|
|
Add(Container::Vertical({
|
|
|
|
main,
|
|
|
|
child,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnEvent(Event event) final {
|
|
|
|
if (event.is_mouse())
|
|
|
|
return OnMouseEvent(std::move(event));
|
|
|
|
return ComponentBase::OnEvent(std::move(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnMouseEvent(Event event) {
|
|
|
|
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
|
|
|
captured_mouse_.reset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.mouse().button == Mouse::Left &&
|
|
|
|
event.mouse().motion == Mouse::Pressed &&
|
|
|
|
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
|
|
|
!captured_mouse_) {
|
|
|
|
captured_mouse_ = CaptureMouse(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (captured_mouse_) {
|
2021-09-08 15:36:37 +08:00
|
|
|
*main_size_ = event.mouse().y - box_.y_min;
|
2021-05-27 21:22:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ComponentBase::OnEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
Element Render() final {
|
|
|
|
return vbox({
|
|
|
|
main_->Render() | size(HEIGHT, EQUAL, *main_size_),
|
|
|
|
separator() | reflect(separator_box_),
|
|
|
|
child_->Render() | yflex,
|
|
|
|
}) |
|
2021-09-08 15:36:37 +08:00
|
|
|
reflect(box_);
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Component main_;
|
|
|
|
Component child_;
|
|
|
|
int* const main_size_;
|
|
|
|
CapturedMouse captured_mouse_;
|
|
|
|
Box separator_box_;
|
2021-09-08 15:36:37 +08:00
|
|
|
Box box_;
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
2021-06-22 05:08:39 +08:00
|
|
|
class ResizableSplitBottomBase : public ComponentBase {
|
2021-05-27 21:22:53 +08:00
|
|
|
public:
|
2021-05-27 21:46:23 +08:00
|
|
|
ResizableSplitBottomBase(Component main, Component child, int* main_size)
|
2021-05-27 21:22:53 +08:00
|
|
|
: main_(main), child_(child), main_size_(main_size) {
|
|
|
|
Add(Container::Vertical({
|
|
|
|
child,
|
|
|
|
main,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnEvent(Event event) final {
|
|
|
|
if (event.is_mouse())
|
|
|
|
return OnMouseEvent(std::move(event));
|
|
|
|
return ComponentBase::OnEvent(std::move(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OnMouseEvent(Event event) {
|
|
|
|
if (captured_mouse_ && event.mouse().motion == Mouse::Released) {
|
|
|
|
captured_mouse_.reset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.mouse().button == Mouse::Left &&
|
|
|
|
event.mouse().motion == Mouse::Pressed &&
|
|
|
|
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
|
|
|
!captured_mouse_) {
|
|
|
|
captured_mouse_ = CaptureMouse(event);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (captured_mouse_) {
|
2021-09-08 15:36:37 +08:00
|
|
|
*main_size_ = box_.y_max - event.mouse().y;
|
2021-05-27 21:22:53 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ComponentBase::OnEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
Element Render() final {
|
|
|
|
return vbox({
|
|
|
|
child_->Render() | yflex,
|
|
|
|
separator() | reflect(separator_box_),
|
|
|
|
main_->Render() | size(HEIGHT, EQUAL, *main_size_),
|
|
|
|
}) |
|
2021-09-08 15:36:37 +08:00
|
|
|
reflect(box_);
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
Component main_;
|
|
|
|
Component child_;
|
|
|
|
int* const main_size_;
|
|
|
|
CapturedMouse captured_mouse_;
|
|
|
|
Box separator_box_;
|
2021-09-08 15:36:37 +08:00
|
|
|
Box box_;
|
2021-05-27 21:22:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2021-05-27 21:46:23 +08:00
|
|
|
/// @brief An horizontal split in between two components, configurable using the
|
|
|
|
/// mouse.
|
|
|
|
/// @param main The main component of size |main_size|, on the left.
|
|
|
|
/// @param back The back component taking the remaining size, on the right.
|
|
|
|
/// @param main_size The size of the |main| component.
|
|
|
|
/// @ingroup component
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```cpp
|
|
|
|
/// auto screen = ScreenInteractive::Fullscreen();
|
|
|
|
/// int left_size = 10;
|
2021-08-09 05:25:20 +08:00
|
|
|
/// auto left = Renderer([] { return text("Left") | center;});
|
|
|
|
/// auto right = Renderer([] { return text("right") | center;});
|
2021-05-27 21:46:23 +08:00
|
|
|
/// auto split = ResizableSplitLeft(left, right, &left_size);
|
|
|
|
/// screen.Loop(split);
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ### Output
|
|
|
|
///
|
|
|
|
/// ```bash
|
2021-06-22 05:08:39 +08:00
|
|
|
/// │
|
|
|
|
/// left │ right
|
|
|
|
/// │
|
2021-05-27 21:46:23 +08:00
|
|
|
/// ```
|
|
|
|
Component ResizableSplitLeft(Component main, Component back, int* main_size) {
|
2021-06-22 05:08:39 +08:00
|
|
|
return Make<ResizableSplitLeftBase>(std::move(main), std::move(back),
|
|
|
|
main_size);
|
2021-05-27 21:22:53 +08:00
|
|
|
}
|
2021-05-27 21:46:23 +08:00
|
|
|
|
|
|
|
/// @brief An horizontal split in between two components, configurable using the
|
|
|
|
/// mouse.
|
|
|
|
/// @param main The main component of size |main_size|, on the right.
|
|
|
|
/// @param back The back component taking the remaining size, on the left.
|
|
|
|
/// @param main_size The size of the |main| component.
|
|
|
|
/// @ingroup component
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```cpp
|
|
|
|
/// auto screen = ScreenInteractive::Fullscreen();
|
|
|
|
/// int right_size = 10;
|
2021-08-09 05:25:20 +08:00
|
|
|
/// auto left = Renderer([] { return text("Left") | center;});
|
|
|
|
/// auto right = Renderer([] { return text("right") | center;});
|
2021-05-27 21:46:23 +08:00
|
|
|
/// auto split = ResizableSplitRight(right, left, &right_size);
|
|
|
|
/// screen.Loop(split);
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ### Output
|
|
|
|
///
|
|
|
|
/// ```bash
|
|
|
|
/// │
|
|
|
|
/// left │ right
|
|
|
|
/// │
|
|
|
|
/// ```
|
|
|
|
Component ResizableSplitRight(Component main, Component back, int* main_size) {
|
|
|
|
return Make<ResizableSplitRightBase>(std::move(main), std::move(back),
|
|
|
|
main_size);
|
2021-05-27 21:22:53 +08:00
|
|
|
}
|
2021-05-27 21:46:23 +08:00
|
|
|
|
|
|
|
/// @brief An vertical split in between two components, configurable using the
|
|
|
|
/// mouse.
|
|
|
|
/// @param main The main component of size |main_size|, on the top.
|
|
|
|
/// @param back The back component taking the remaining size, on the bottom.
|
|
|
|
/// @param main_size The size of the |main| component.
|
|
|
|
/// @ingroup component
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```cpp
|
|
|
|
/// auto screen = ScreenInteractive::Fullscreen();
|
|
|
|
/// int top_size = 1;
|
2021-08-09 05:25:20 +08:00
|
|
|
/// auto top = Renderer([] { return text("Top") | center;});
|
|
|
|
/// auto bottom = Renderer([] { return text("Bottom") | center;});
|
2021-05-27 21:46:23 +08:00
|
|
|
/// auto split = ResizableSplitTop(top, bottom, &top_size);
|
|
|
|
/// screen.Loop(split);
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ### Output
|
|
|
|
///
|
|
|
|
/// ```bash
|
|
|
|
/// top
|
|
|
|
/// ────────────
|
|
|
|
/// bottom
|
|
|
|
/// ```
|
|
|
|
Component ResizableSplitTop(Component main, Component back, int* main_size) {
|
2021-06-22 05:08:39 +08:00
|
|
|
return Make<ResizableSplitTopBase>(std::move(main), std::move(back),
|
|
|
|
main_size);
|
2021-05-27 21:22:53 +08:00
|
|
|
}
|
2021-05-27 21:46:23 +08:00
|
|
|
|
|
|
|
/// @brief An vertical split in between two components, configurable using the
|
|
|
|
/// mouse.
|
|
|
|
/// @param main The main component of size |main_size|, on the bottom.
|
|
|
|
/// @param back The back component taking the remaining size, on the top.
|
|
|
|
/// @param main_size The size of the |main| component.
|
|
|
|
/// @ingroup component
|
|
|
|
///
|
|
|
|
/// ### Example
|
|
|
|
///
|
|
|
|
/// ```cpp
|
|
|
|
/// auto screen = ScreenInteractive::Fullscreen();
|
|
|
|
/// int bottom_size = 1;
|
2021-08-09 05:25:20 +08:00
|
|
|
/// auto top = Renderer([] { return text("Top") | center;});
|
|
|
|
/// auto bottom = Renderer([] { return text("Bottom") | center;});
|
2021-05-27 21:46:23 +08:00
|
|
|
/// auto split = ResizableSplit::Bottom(bottom, top, &bottom_size);
|
|
|
|
/// screen.Loop(split);
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// ### Output
|
|
|
|
///
|
|
|
|
/// ```bash
|
|
|
|
/// top
|
|
|
|
/// ────────────
|
|
|
|
/// bottom
|
|
|
|
/// ```
|
|
|
|
Component ResizableSplitBottom(Component main, Component back, int* main_size) {
|
2021-06-22 05:08:39 +08:00
|
|
|
return Make<ResizableSplitBottomBase>(std::move(main), std::move(back),
|
|
|
|
main_size);
|
2021-05-27 21:22:53 +08:00
|
|
|
}
|
|
|
|
} // namespace ftxui
|
2021-06-22 05:08:39 +08:00
|
|
|
|
|
|
|
// 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.
|