Skip to content

Commit

Permalink
Support move movement.
Browse files Browse the repository at this point in the history
- Record whether the mouse moved. Suppress the previous alternative
  implementation.
- Record mouse.control modifier.

Fixed:#791
  • Loading branch information
ArthurSonzogni committed Dec 16, 2023
1 parent bfadcb7 commit 86128fc
Show file tree
Hide file tree
Showing 17 changed files with 102 additions and 90 deletions.
28 changes: 9 additions & 19 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,16 @@ current (development)
### Component
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
option. Added by @mingsheng13.
- Feature/Bugfix/Breaking change: `Mouse transition`:
- Bugfix/Breaking change: `Mouse transition`:
- Detect when the mouse move, as opposed to being pressed.
The Mouse::Moved motion was added.
- Dragging the mouse with the left button pressed now avoids activating
multiple checkboxes.
- A couple of components are now activated when the mouse is pressed,
as opposed to being released.
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/773
Dragging the mouse with the left button pressed now avoids activating multiple
checkboxes.

Add support for detecting mouse press transition. Added:
```cpp
// The previous mouse event.
Mouse Mouse::previous;

// Return whether the mouse transitionned from:
// released to pressed => IsPressed()
// pressed to pressed => IsHeld()
// pressed to released => IsReleased()
bool Mouse::IsPressed(Button button) const;
bool Mouse::IsHeld(Button button) const;
bool Mouse::IsReleased(Button button) const;
```
A couple of components are now activated when the mouse is pressed,
as opposed to released.
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/792
- Bugfix: mouse.control is now reported correctly.
- Feature: Add `ScreenInteractive::FullscreenPrimaryScreen()`. This allows
displaying a fullscreen component on the primary screen, as opposed to the
alternate screen.
Expand Down
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ add_library(component
src/ftxui/component/maybe.cpp
src/ftxui/component/menu.cpp
src/ftxui/component/modal.cpp
src/ftxui/component/mouse.cpp
src/ftxui/component/radiobox.cpp
src/ftxui/component/radiobox.cpp
src/ftxui/component/renderer.cpp
Expand Down
3 changes: 3 additions & 0 deletions examples/component/print_key_press.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ std::string Stringify(Event event) {
case Mouse::Released:
out += "_released";
break;
case Mouse::Moved:
out += "_moved";
break;
}
if (event.mouse().control)
out += "_control";
Expand Down
9 changes: 1 addition & 8 deletions include/ftxui/component/mouse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,9 @@ struct Mouse {
enum Motion {
Released = 0,
Pressed = 1,
Moved = 2,
};

// Utility function to check the variations of the mouse state.
bool IsPressed(Button btn = Left) const; // Released => Pressed.
bool IsHeld(Button btn = Left) const; // Pressed => Pressed.
bool IsReleased(Button btn = Left) const; // Pressed => Released.

// Button
Button button = Button::None;

Expand All @@ -42,9 +38,6 @@ struct Mouse {
// Coordinates:
int x = 0;
int y = 0;

// Previous mouse event, if any.
Mouse* previous = nullptr;
};

} // namespace ftxui
Expand Down
1 change: 0 additions & 1 deletion include/ftxui/component/screen_interactive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ class ScreenInteractive : public Screen {

bool frame_valid_ = false;

Mouse latest_mouse_event_;
friend class Loop;

public:
Expand Down
3 changes: 2 additions & 1 deletion src/ftxui/component/button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ class ButtonBase : public ComponentBase, public ButtonOption {
return false;
}

if (event.mouse().IsPressed()) {
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
OnClick();
return true;
Expand Down
3 changes: 2 additions & 1 deletion src/ftxui/component/checkbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
return false;
}

if (event.mouse().IsPressed()) {
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
*checked = !*checked;
on_change();
return true;
Expand Down
5 changes: 4 additions & 1 deletion src/ftxui/component/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,10 @@ class InputBase : public ComponentBase, public InputOption {
return false;
}

if (!event.mouse().IsPressed()) {
if (event.mouse().button != Mouse::Left) {
return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false;
}

Expand Down
7 changes: 5 additions & 2 deletions src/ftxui/component/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ class MenuBase : public ComponentBase, public MenuOption {

TakeFocus();
focused_entry() = i;
if (event.mouse().IsPressed()) {

if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
if (selected() != i) {
selected() = i;
selected_previous_ = selected();
Expand Down Expand Up @@ -682,7 +684,8 @@ Component MenuEntry(MenuEntryOption option) {
return false;
}

if (event.mouse().IsPressed()) {
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
TakeFocus();
return true;
}
Expand Down
36 changes: 0 additions & 36 deletions src/ftxui/component/mouse.cpp

This file was deleted.

3 changes: 2 additions & 1 deletion src/ftxui/component/radiobox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {

TakeFocus();
focused_entry() = i;
if (event.mouse().IsPressed()) {
if (event.mouse().button == Mouse::Left &&
event.mouse().motion == Mouse::Pressed) {
if (selected() != i) {
selected() = i;
on_change();
Expand Down
3 changes: 2 additions & 1 deletion src/ftxui/component/resizable_split.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class ResizableSplitBase : public ComponentBase {
return true;
}

if (event.mouse().IsPressed() &&
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);
Expand Down
6 changes: 0 additions & 6 deletions src/ftxui/component/screen_interactive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,17 +715,11 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
if (arg.is_mouse()) {
arg.mouse().x -= cursor_x_;
arg.mouse().y -= cursor_y_;
arg.mouse().previous = &latest_mouse_event_;
}

arg.screen_ = this;
component->OnEvent(arg);
frame_valid_ = false;

if (arg.is_mouse()) {
latest_mouse_event_ = arg.mouse();
latest_mouse_event_.previous = nullptr;
}
return;
}

Expand Down
5 changes: 4 additions & 1 deletion src/ftxui/component/slider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ class SliderBase : public ComponentBase {
return true;
}

if (!event.mouse().IsPressed()) {
if (event.mouse().button != Mouse::Left) {
return false;
}
if (event.mouse().motion != Mouse::Pressed) {
return false;
}

Expand Down
43 changes: 36 additions & 7 deletions src/ftxui/component/terminal_input_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,42 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
(void)altered;

Output output(MOUSE);
output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
((arguments[0] & 64) >> 4)); // NOLINT
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
output.mouse.meta = bool(arguments[0] & 8); // NOLINT
output.mouse.x = arguments[1]; // NOLINT
output.mouse.y = arguments[2]; // NOLINT
output.mouse.motion = Mouse::Motion(pressed); // NOLINT

// Bits value Modifer Comment
// ---- ----- ------- ---------
// 0 1 1 2 button 0 = Left, 1 = Middle, 2 = Right, 3 = Release
// 2 4 Shift
// 3 8 Meta
// 4 16 Control
// 5 32 Move
// 6 64 Wheel

const bool is_move = arguments[0] & 32; // NOLINT
if (is_move) {
output.mouse.motion = Mouse::Motion::Moved; // NOLINT
output.mouse.button = Mouse::Button(arguments[0] & 3); // NOINT
} else {
output.mouse.motion = Mouse::Motion(pressed); // NOLINT
const bool is_wheel = arguments[0] & 64; // NOLINT

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (Linux GCC, ubuntu-latest, gcc, gcov)

unused variable ‘is_wheel’ [-Werror=unused-variable]

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (Linux Clang, ubuntu-latest, llvm, llvm-cov gcov)

unused variable 'is_wheel' [-Werror,-Wunused-variable]

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (MacOS clang, macos-latest, llvm, llvm-cov gcov)

unused variable 'is_wheel' [-Werror,-Wunused-variable]

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (Linux GCC, ubuntu-latest, gcc, gcov)

unused variable ‘is_wheel’ [-Werror=unused-variable]

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (Linux Clang, ubuntu-latest, llvm, llvm-cov gcov)

unused variable 'is_wheel' [-Werror,-Wunused-variable]

Check failure on line 414 in src/ftxui/component/terminal_input_parser.cpp

View workflow job for this annotation

GitHub Actions / Tests (MacOS clang, macos-latest, llvm, llvm-cov gcov)

unused variable 'is_wheel' [-Werror,-Wunused-variable]
output.mouse.button = Mouse::Button((arguments[0] & 3) + // NOLINT
((arguments[0] & 64) >> 4)); // NOLINT
}

// The next three bits encode the modifiers which were down when the
// button was pressed and are added together:
// 4=Shift,
// 8=Meta,
// 16=Control.
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
output.mouse.meta = bool(arguments[0] & 8); // NOLINT
output.mouse.control = bool(arguments[0] & 16); // NOLINT

// The next two arguments contains the coordinates directly:
output.mouse.x = arguments[1]; // NOLINT
output.mouse.y = arguments[2]; // NOLINT

// Motion event.
return output;
}

Expand Down
31 changes: 28 additions & 3 deletions src/ftxui/component/terminal_input_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ TEST(Event, MouseLeftClickPressed) {
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('[');
parser.Add('3');
parser.Add('2');
parser.Add('0');
parser.Add(';');
parser.Add('1');
parser.Add('2');
Expand All @@ -103,7 +102,7 @@ TEST(Event, MouseLeftClickPressed) {
EXPECT_FALSE(event_receiver->Receive(&received));
}

TEST(Event, MouseLeftClickReleased) {
TEST(Event, MouseLeftMoved) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
Expand All @@ -117,6 +116,32 @@ TEST(Event, MouseLeftClickReleased) {
parser.Add(';');
parser.Add('4');
parser.Add('2');
parser.Add('M');
}

Task received;
EXPECT_TRUE(event_receiver->Receive(&received));
EXPECT_TRUE(std::get<Event>(received).is_mouse());
EXPECT_EQ(Mouse::Left, std::get<Event>(received).mouse().button);
EXPECT_EQ(12, std::get<Event>(received).mouse().x);
EXPECT_EQ(42, std::get<Event>(received).mouse().y);
EXPECT_EQ(std::get<Event>(received).mouse().motion, Mouse::Moved);
EXPECT_FALSE(event_receiver->Receive(&received));
}

TEST(Event, MouseLeftClickReleased) {
auto event_receiver = MakeReceiver<Task>();
{
auto parser = TerminalInputParser(event_receiver->MakeSender());
parser.Add('\x1B');
parser.Add('[');
parser.Add('0');
parser.Add(';');
parser.Add('1');
parser.Add('2');
parser.Add(';');
parser.Add('4');
parser.Add('2');
parser.Add('m');
}

Expand Down
5 changes: 4 additions & 1 deletion src/ftxui/component/window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ class WindowImpl : public ComponentBase, public WindowOptions {
return true;
}

if (!event.mouse().IsPressed()) {
if (event.mouse().button != Mouse::Left) {
return true;
}
if (event.mouse().motion != Mouse::Pressed) {
return true;
}

Expand Down

0 comments on commit 86128fc

Please sign in to comment.