From 36820c22929f4fb11892c4fbd52f321cc63a55ad Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 7 Oct 2018 00:49:38 +0800 Subject: Add shift selection, and fix the bug that caret is at wrong position when move with selection. --- src/ui/controls/text_box.cpp | 70 +++++++++++++++++++++++++++++++++++++--- src/ui/controls/text_box.h | 8 ++++- src/ui/controls/text_control.cpp | 13 +------- src/ui/ui_base.cpp | 15 ++++++++- src/ui/ui_base.h | 23 ++++++++++++- 5 files changed, 109 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp index 30b9069a..54b2f7ab 100644 --- a/src/ui/controls/text_box.cpp +++ b/src/ui/controls/text_box.cpp @@ -67,16 +67,47 @@ namespace cru::ui::controls Control::OnKeyDownCore(args); if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0) { - ClearSelection(); - caret_position_--; + if (IsKeyDown(VK_SHIFT)) + { + if (GetCaretSelectionSide()) + ShiftLeftSelectionRange(-1); + else + ShiftRightSelectionRange(-1); + } + else + { + const auto selection = GetSelectedRange(); + if (selection.has_value()) + { + ClearSelection(); + caret_position_ = selection.value().position; + } + else + caret_position_--; + } Repaint(); } if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size()) { - ClearSelection(); - caret_position_++; - Repaint(); + if (IsKeyDown(VK_SHIFT)) + { + if (GetCaretSelectionSide()) + ShiftLeftSelectionRange(1); + else + ShiftRightSelectionRange(1); + } + else + { + const auto selection = GetSelectedRange(); + if (selection.has_value()) + { + ClearSelection(); + caret_position_ = selection.value().position + selection.value().count; + } + else + caret_position_++; + } } } @@ -138,4 +169,33 @@ namespace cru::ui::controls caret_position_ = position; Repaint(); } + + bool TextBox::GetCaretSelectionSide() const + { + const auto selection = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); + if (selection.first == caret_position_) + return true; + if (selection.second == caret_position_) + return false; + assert(false); + return true; + } + + void TextBox::ShiftLeftSelectionRange(const int count) + { + const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); + int new_left = selection_range_side.first + count; + new_left = new_left < 0 ? 0 : new_left; // at least 0 + caret_position_ = new_left; + SetSelectedRange(TextRange::FromTwoSides(static_cast(new_left), selection_range_side.second)); + } + + void TextBox::ShiftRightSelectionRange(const int count) + { + const auto selection_range_side = TextRange::ToTwoSides(GetSelectedRange(), caret_position_); + int new_right = selection_range_side.second + count; + new_right = new_right < 0 ? 0 : new_right; // at least 0 + caret_position_ = new_right; + SetSelectedRange(TextRange::FromTwoSides(selection_range_side.first, static_cast(new_right))); + } } diff --git a/src/ui/controls/text_box.h b/src/ui/controls/text_box.h index 540ac758..37e04835 100644 --- a/src/ui/controls/text_box.h +++ b/src/ui/controls/text_box.h @@ -36,7 +36,13 @@ namespace cru::ui::controls void OnKeyDownCore(events::KeyEventArgs& args) override final; void OnCharCore(events::CharEventArgs& args) override final; - void RequestChangeCaretPosition(unsigned position) override; + void RequestChangeCaretPosition(unsigned position) override final; + + private: + // return true if left + bool GetCaretSelectionSide() const; + void ShiftLeftSelectionRange(int count); + void ShiftRightSelectionRange(int count); private: unsigned caret_position_ = 0; diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp index ee5b253d..3c5d7c33 100644 --- a/src/ui/controls/text_control.cpp +++ b/src/ui/controls/text_control.cpp @@ -162,18 +162,7 @@ namespace cru::ui::controls { const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this), false).value(); RequestChangeCaretPosition(hit_test_result); - if (hit_test_result > mouse_down_position_) - { - selected_range_ = TextRange(mouse_down_position_, hit_test_result - mouse_down_position_); - } - else if (hit_test_result < mouse_down_position_) - { - selected_range_ = TextRange(hit_test_result, mouse_down_position_ - hit_test_result); - } - else - { - selected_range_ = std::nullopt; - } + selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_); Repaint(); } } diff --git a/src/ui/ui_base.cpp b/src/ui/ui_base.cpp index 550432e4..3bee2269 100644 --- a/src/ui/ui_base.cpp +++ b/src/ui/ui_base.cpp @@ -1,6 +1,19 @@ #include "ui_base.h" +#include "system_headers.h" + namespace cru { namespace ui { - } + bool IsKeyDown(const int virtual_code) + { + const auto result = ::GetKeyState(virtual_code); + return (static_cast(result) & 0x8000) != 0; + } + + bool IsKeyToggled(const int virtual_code) + { + const auto result = ::GetKeyState(virtual_code); + return (static_cast(result) & 1) != 0; + } + } } diff --git a/src/ui/ui_base.h b/src/ui/ui_base.h index 51ae4084..bff30bfd 100644 --- a/src/ui/ui_base.h +++ b/src/ui/ui_base.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace cru { @@ -125,8 +127,24 @@ namespace cru struct TextRange { + constexpr static std::optional FromTwoSides(unsigned first, unsigned second) + { + if (first > second) + return std::make_optional(second, first - second); + if (first < second) + return std::make_optional(first, second - first); + return std::nullopt; + } + + constexpr static std::pair ToTwoSides(std::optional text_range, unsigned default_position = 0) + { + if (text_range.has_value()) + return std::make_pair(text_range.value().position, text_range.value().position + text_range.value().count); + return std::make_pair(default_position, default_position); + } + constexpr TextRange() = default; - constexpr TextRange(const int position, const int count) + constexpr TextRange(const unsigned position, const unsigned count) : position(position), count(count) { @@ -135,5 +153,8 @@ namespace cru unsigned position = 0; unsigned count = 0; }; + + bool IsKeyDown(int virtual_code); + bool IsKeyToggled(int virtual_code); } } -- cgit v1.2.3