mirror of
https://github.com/ArthurSonzogni/FTXUI.git
synced 2024-11-25 12:11:33 +08:00
Fixed: https://github.com/ArthurSonzogni/FTXUI/issues/792
This commit is contained in:
parent
348c3853d4
commit
a7b6785420
28
CHANGELOG.md
28
CHANGELOG.md
@ -7,26 +7,16 @@ current (development)
|
|||||||
### Component
|
### Component
|
||||||
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
|
- Feature: Add support for `Input`'s insert mode. Add `InputOption::insert`
|
||||||
option. Added by @mingsheng13.
|
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
|
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/773
|
||||||
Dragging the mouse with the left button pressed now avoids activating multiple
|
This fixes: https://github.com/ArthurSonzogni/FTXUI/issues/792
|
||||||
checkboxes.
|
- Bugfix: mouse.control is now reported correctly.
|
||||||
|
|
||||||
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.
|
|
||||||
- Feature: Add `ScreenInteractive::FullscreenPrimaryScreen()`. This allows
|
- Feature: Add `ScreenInteractive::FullscreenPrimaryScreen()`. This allows
|
||||||
displaying a fullscreen component on the primary screen, as opposed to the
|
displaying a fullscreen component on the primary screen, as opposed to the
|
||||||
alternate screen.
|
alternate screen.
|
||||||
|
@ -123,7 +123,6 @@ add_library(component
|
|||||||
src/ftxui/component/maybe.cpp
|
src/ftxui/component/maybe.cpp
|
||||||
src/ftxui/component/menu.cpp
|
src/ftxui/component/menu.cpp
|
||||||
src/ftxui/component/modal.cpp
|
src/ftxui/component/modal.cpp
|
||||||
src/ftxui/component/mouse.cpp
|
|
||||||
src/ftxui/component/radiobox.cpp
|
src/ftxui/component/radiobox.cpp
|
||||||
src/ftxui/component/radiobox.cpp
|
src/ftxui/component/radiobox.cpp
|
||||||
src/ftxui/component/renderer.cpp
|
src/ftxui/component/renderer.cpp
|
||||||
|
@ -55,6 +55,9 @@ std::string Stringify(Event event) {
|
|||||||
case Mouse::Released:
|
case Mouse::Released:
|
||||||
out += "_released";
|
out += "_released";
|
||||||
break;
|
break;
|
||||||
|
case Mouse::Moved:
|
||||||
|
out += "_moved";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (event.mouse().control)
|
if (event.mouse().control)
|
||||||
out += "_control";
|
out += "_control";
|
||||||
|
@ -21,13 +21,9 @@ struct Mouse {
|
|||||||
enum Motion {
|
enum Motion {
|
||||||
Released = 0,
|
Released = 0,
|
||||||
Pressed = 1,
|
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 = Button::None;
|
Button button = Button::None;
|
||||||
|
|
||||||
@ -42,9 +38,6 @@ struct Mouse {
|
|||||||
// Coordinates:
|
// Coordinates:
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
// Previous mouse event, if any.
|
|
||||||
Mouse* previous = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ftxui
|
} // namespace ftxui
|
||||||
|
@ -117,7 +117,6 @@ class ScreenInteractive : public Screen {
|
|||||||
// The style of the cursor to restore on exit.
|
// The style of the cursor to restore on exit.
|
||||||
int cursor_reset_shape_ = 1;
|
int cursor_reset_shape_ = 1;
|
||||||
|
|
||||||
Mouse latest_mouse_event_;
|
|
||||||
friend class Loop;
|
friend class Loop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -124,7 +124,8 @@ class ButtonBase : public ComponentBase, public ButtonOption {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.mouse().IsPressed()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
OnClick();
|
OnClick();
|
||||||
return true;
|
return true;
|
||||||
|
@ -69,7 +69,8 @@ class CheckboxBase : public ComponentBase, public CheckboxOption {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.mouse().IsPressed()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
*checked = !*checked;
|
*checked = !*checked;
|
||||||
on_change();
|
on_change();
|
||||||
return true;
|
return true;
|
||||||
|
@ -466,7 +466,10 @@ class InputBase : public ComponentBase, public InputOption {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.mouse().IsPressed()) {
|
if (event.mouse().button != Mouse::Left) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (event.mouse().motion != Mouse::Pressed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +318,9 @@ class MenuBase : public ComponentBase, public MenuOption {
|
|||||||
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
focused_entry() = i;
|
focused_entry() = i;
|
||||||
if (event.mouse().IsPressed()) {
|
|
||||||
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
if (selected() != i) {
|
if (selected() != i) {
|
||||||
selected() = i;
|
selected() = i;
|
||||||
selected_previous_ = selected();
|
selected_previous_ = selected();
|
||||||
@ -682,7 +684,8 @@ Component MenuEntry(MenuEntryOption option) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.mouse().IsPressed()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright 2023 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 "ftxui/component/mouse.hpp"
|
|
||||||
|
|
||||||
namespace ftxui {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
bool IsDown(const Mouse* mouse, Mouse::Button btn) {
|
|
||||||
return mouse->button == btn && mouse->motion == Mouse::Pressed;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/// Return whether the mouse transitionned from released to pressed.
|
|
||||||
/// This is useful to detect a click.
|
|
||||||
/// @arg btn The button to check.
|
|
||||||
bool Mouse::IsPressed(Button btn) const {
|
|
||||||
return IsDown(this, btn) && (!previous || !IsDown(previous, btn));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether the mouse is currently held.
|
|
||||||
/// This is useful to detect a drag.
|
|
||||||
/// @arg btn The button to check.
|
|
||||||
bool Mouse::IsHeld(Button btn) const {
|
|
||||||
return IsDown(this, btn) && previous && IsDown(previous, btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return whether the mouse transitionned from pressed to released.
|
|
||||||
/// This is useful to detect a click.
|
|
||||||
/// @arg btn The button to check.
|
|
||||||
bool Mouse::IsReleased(Button btn) const {
|
|
||||||
return !IsDown(this, btn) && (previous && IsDown(previous, btn));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ftxui
|
|
@ -123,7 +123,8 @@ class RadioboxBase : public ComponentBase, public RadioboxOption {
|
|||||||
|
|
||||||
TakeFocus();
|
TakeFocus();
|
||||||
focused_entry() = i;
|
focused_entry() = i;
|
||||||
if (event.mouse().IsPressed()) {
|
if (event.mouse().button == Mouse::Left &&
|
||||||
|
event.mouse().motion == Mouse::Pressed) {
|
||||||
if (selected() != i) {
|
if (selected() != i) {
|
||||||
selected() = i;
|
selected() = i;
|
||||||
on_change();
|
on_change();
|
||||||
|
@ -42,7 +42,8 @@ class ResizableSplitBase : public ComponentBase {
|
|||||||
return true;
|
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) &&
|
separator_box_.Contain(event.mouse().x, event.mouse().y) &&
|
||||||
!captured_mouse_) {
|
!captured_mouse_) {
|
||||||
captured_mouse_ = CaptureMouse(event);
|
captured_mouse_ = CaptureMouse(event);
|
||||||
|
@ -734,17 +734,11 @@ void ScreenInteractive::HandleTask(Component component, Task& task) {
|
|||||||
if (arg.is_mouse()) {
|
if (arg.is_mouse()) {
|
||||||
arg.mouse().x -= cursor_x_;
|
arg.mouse().x -= cursor_x_;
|
||||||
arg.mouse().y -= cursor_y_;
|
arg.mouse().y -= cursor_y_;
|
||||||
arg.mouse().previous = &latest_mouse_event_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
arg.screen_ = this;
|
arg.screen_ = this;
|
||||||
component->OnEvent(arg);
|
component->OnEvent(arg);
|
||||||
frame_valid_ = false;
|
frame_valid_ = false;
|
||||||
|
|
||||||
if (arg.is_mouse()) {
|
|
||||||
latest_mouse_event_ = arg.mouse();
|
|
||||||
latest_mouse_event_.previous = nullptr;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,10 @@ class SliderBase : public ComponentBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.mouse().IsPressed()) {
|
if (event.mouse().button != Mouse::Left) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (event.mouse().motion != Mouse::Pressed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,13 +410,36 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse( // NOLINT
|
|||||||
(void)altered;
|
(void)altered;
|
||||||
|
|
||||||
Output output(MOUSE);
|
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.motion = Mouse::Motion(pressed); // NOLINT
|
||||||
output.mouse.shift = bool(arguments[0] & 4); // NOLINT
|
|
||||||
output.mouse.meta = bool(arguments[0] & 8); // 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
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
const int button = arguments[0] & (1 + 2); // NOLINT
|
||||||
|
const bool is_shift = arguments[0] & 4; // NOLINT
|
||||||
|
const bool is_meta = arguments[0] & 8; // NOLINT
|
||||||
|
const bool is_control = arguments[0] & 16; // NOLINT
|
||||||
|
const bool is_move = arguments[0] & 32; // NOLINT
|
||||||
|
const bool is_wheel = arguments[0] & 64; // NOLINT
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
output.mouse.motion = is_move ? Mouse::Moved : Mouse::Motion(pressed);
|
||||||
|
output.mouse.button = is_wheel ? Mouse::Button(Mouse::WheelUp + button) //
|
||||||
|
: Mouse::Button(button);
|
||||||
|
output.mouse.shift = is_shift;
|
||||||
|
output.mouse.meta = is_meta;
|
||||||
|
output.mouse.control = is_control;
|
||||||
output.mouse.x = arguments[1]; // NOLINT
|
output.mouse.x = arguments[1]; // NOLINT
|
||||||
output.mouse.y = arguments[2]; // NOLINT
|
output.mouse.y = arguments[2]; // NOLINT
|
||||||
|
|
||||||
|
// Motion event.
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +82,7 @@ TEST(Event, MouseLeftClickPressed) {
|
|||||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||||
parser.Add('\x1B');
|
parser.Add('\x1B');
|
||||||
parser.Add('[');
|
parser.Add('[');
|
||||||
parser.Add('3');
|
parser.Add('0');
|
||||||
parser.Add('2');
|
|
||||||
parser.Add(';');
|
parser.Add(';');
|
||||||
parser.Add('1');
|
parser.Add('1');
|
||||||
parser.Add('2');
|
parser.Add('2');
|
||||||
@ -103,7 +102,7 @@ TEST(Event, MouseLeftClickPressed) {
|
|||||||
EXPECT_FALSE(event_receiver->Receive(&received));
|
EXPECT_FALSE(event_receiver->Receive(&received));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Event, MouseLeftClickReleased) {
|
TEST(Event, MouseLeftMoved) {
|
||||||
auto event_receiver = MakeReceiver<Task>();
|
auto event_receiver = MakeReceiver<Task>();
|
||||||
{
|
{
|
||||||
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
auto parser = TerminalInputParser(event_receiver->MakeSender());
|
||||||
@ -117,6 +116,32 @@ TEST(Event, MouseLeftClickReleased) {
|
|||||||
parser.Add(';');
|
parser.Add(';');
|
||||||
parser.Add('4');
|
parser.Add('4');
|
||||||
parser.Add('2');
|
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');
|
parser.Add('m');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,10 @@ class WindowImpl : public ComponentBase, public WindowOptions {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.mouse().IsPressed()) {
|
if (event.mouse().button != Mouse::Left) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event.mouse().motion != Mouse::Pressed) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user