diff --git a/src/cascadia/TerminalControl/TSFInputControl.cpp b/src/cascadia/TerminalControl/TSFInputControl.cpp index 6f4abc6371b..8cee7ffe154 100644 --- a/src/cascadia/TerminalControl/TSFInputControl.cpp +++ b/src/cascadia/TerminalControl/TSFInputControl.cpp @@ -5,8 +5,6 @@ #include "TSFInputControl.h" #include "TSFInputControl.g.cpp" -#include - using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Graphics::Display; using namespace winrt::Windows::UI::Core; @@ -16,17 +14,7 @@ using namespace winrt::Windows::UI::Xaml; namespace winrt::Microsoft::Terminal::Control::implementation { - TSFInputControl::TSFInputControl() : - _editContext{ nullptr }, - _inComposition{ false }, - _activeTextStart{ 0 }, - _focused{ false }, - _currentTerminalCursorPos{ 0, 0 }, - _currentCanvasWidth{ 0.0 }, - _currentTextBlockHeight{ 0.0 }, - _currentTextBounds{ 0, 0, 0, 0 }, - _currentControlBounds{ 0, 0, 0, 0 }, - _currentWindowBounds{ 0, 0, 0, 0 } + TSFInputControl::TSFInputControl() { InitializeComponent(); @@ -83,11 +71,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void TSFInputControl::NotifyFocusEnter() { - if (_editContext != nullptr) - { - _editContext.NotifyFocusEnter(); - _focused = true; - } + _editContext.NotifyFocusEnter(); + _focused = true; } // Method Description: @@ -99,11 +84,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void TSFInputControl::NotifyFocusLeave() { - if (_editContext != nullptr) - { - _editContext.NotifyFocusLeave(); - _focused = false; - } + _editContext.NotifyFocusLeave(); + _focused = false; } // Method Description: @@ -117,14 +99,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (!_inputBuffer.empty()) { - TextBlock().Text(L""); - const auto bufLen = ::base::ClampedNumeric(_inputBuffer.length()); _inputBuffer.clear(); + _selection = {}; + _activeTextStart = 0; _editContext.NotifyFocusLeave(); - _editContext.NotifyTextChanged({ 0, bufLen }, 0, { 0, 0 }); + _editContext.NotifyTextChanged({ 0, INT32_MAX }, 0, _selection); _editContext.NotifyFocusEnter(); - _activeTextStart = 0; - _inComposition = false; + TextBlock().Text({}); } } @@ -303,12 +284,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TSFInputControl::_compositionCompletedHandler(CoreTextEditContext sender, const CoreTextCompositionCompletedEventArgs& /*args*/) { _inComposition = false; - - // only need to do work if the current buffer has text - if (!_inputBuffer.empty()) - { - _SendAndClearText(); - } + _SendAndClearText(); } // Method Description: @@ -336,16 +312,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void TSFInputControl::_textRequestedHandler(CoreTextEditContext sender, const CoreTextTextRequestedEventArgs& args) { - // the range the TSF wants to know about - const auto range = args.Request().Range(); - try { - const auto textEnd = ::base::ClampMin(range.EndCaretPosition, _inputBuffer.length()); - const auto length = ::base::ClampSub(textEnd, range.StartCaretPosition); - const auto textRequested = _inputBuffer.substr(range.StartCaretPosition, length); - - args.Request().Text(textRequested); + const auto range = args.Request().Range(); + const auto text = _inputBuffer.substr( + range.StartCaretPosition, + range.EndCaretPosition - range.StartCaretPosition); + args.Request().Text(text); } CATCH_LOG(); } @@ -360,8 +333,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - args: CoreTextSelectionRequestedEventArgs for providing data for the SelectionRequested event. Not used in method. // Return Value: // - - void TSFInputControl::_selectionRequestedHandler(CoreTextEditContext sender, const CoreTextSelectionRequestedEventArgs& /*args*/) + void TSFInputControl::_selectionRequestedHandler(CoreTextEditContext sender, const CoreTextSelectionRequestedEventArgs& args) { + args.Request().Selection(_selection); } // Method Description: @@ -374,8 +348,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - args: CoreTextSelectionUpdatingEventArgs for providing data for the SelectionUpdating event. Not used in method. // Return Value: // - - void TSFInputControl::_selectionUpdatingHandler(CoreTextEditContext sender, const CoreTextSelectionUpdatingEventArgs& /*args*/) + void TSFInputControl::_selectionUpdatingHandler(CoreTextEditContext sender, const CoreTextSelectionUpdatingEventArgs& args) { + _selection = args.Selection(); + args.Result(CoreTextSelectionUpdatingResult::Succeeded); } // Method Description: @@ -388,24 +364,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - void TSFInputControl::_textUpdatingHandler(CoreTextEditContext sender, const CoreTextTextUpdatingEventArgs& args) { - const auto incomingText = args.Text(); - const auto range = args.Range(); - try { - // When a user deletes the last character in their current composition, some machines - // will fire a CompositionCompleted before firing a TextUpdating event that deletes the last character. - // The TextUpdating will have a lower StartCaretPosition, so in this scenario, _activeTextStart - // needs to update to be the StartCaretPosition. - // A known issue related to this behavior is that the last character that's deleted from a composition - // will get sent to the terminal before we receive the TextUpdate to delete the character. - // See GH #5054. - _activeTextStart = ::base::ClampMin(_activeTextStart, ::base::ClampedNumeric(range.StartCaretPosition)); + const auto incomingText = args.Text(); + const auto range = args.Range(); _inputBuffer = _inputBuffer.replace( range.StartCaretPosition, - ::base::ClampSub(range.EndCaretPosition, range.StartCaretPosition), + range.EndCaretPosition - range.StartCaretPosition, incomingText); + _selection = args.NewSelection(); + // GH#5054: Pressing backspace might move the caret before the _activeTextStart. + _activeTextStart = ::base::ClampMin(_activeTextStart, ::base::ClampedNumeric(range.StartCaretPosition)); // Emojis/Kaomojis/Symbols chosen through the IME without starting composition // will be sent straight through to the terminal. @@ -432,22 +402,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - // Method Description: - // - Send the portion of the textBuffer starting at _activeTextStart to the end of the buffer. - // Then clear the TextBlock and hide it until the next time text is received. - // Arguments: - // - - // Return Value: - // - void TSFInputControl::_SendAndClearText() { const auto text = _inputBuffer.substr(_activeTextStart); + if (text.empty()) + { + return; + } _CompositionCompletedHandlers(text); - _activeTextStart = _inputBuffer.length(); + _activeTextStart = _inputBuffer.size(); - TextBlock().Text(L""); + TextBlock().Text({}); // After we reset the TextBlock to empty string, we want to make sure // ActualHeight reflects the respective height. It seems that ActualHeight diff --git a/src/cascadia/TerminalControl/TSFInputControl.h b/src/cascadia/TerminalControl/TSFInputControl.h index d70eec8301c..2285e5bcce8 100644 --- a/src/cascadia/TerminalControl/TSFInputControl.h +++ b/src/cascadia/TerminalControl/TSFInputControl.h @@ -58,6 +58,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _textUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextTextUpdatingEventArgs& args); void _formatUpdatingHandler(winrt::Windows::UI::Text::Core::CoreTextEditContext sender, const winrt::Windows::UI::Text::Core::CoreTextFormatUpdatingEventArgs& args); + void _SendAndClearText(); + void _RedrawCanvas(); + winrt::Windows::UI::Text::Core::CoreTextEditContext::TextRequested_revoker _textRequestedRevoker; winrt::Windows::UI::Text::Core::CoreTextEditContext::SelectionRequested_revoker _selectionRequestedRevoker; winrt::Windows::UI::Text::Core::CoreTextEditContext::FocusRemoved_revoker _focusRemovedRevoker; @@ -68,22 +71,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionStarted_revoker _compositionStartedRevoker; winrt::Windows::UI::Text::Core::CoreTextEditContext::CompositionCompleted_revoker _compositionCompletedRevoker; - Windows::UI::Text::Core::CoreTextEditContext _editContext; - + Windows::UI::Text::Core::CoreTextEditContext _editContext{ nullptr }; std::wstring _inputBuffer; - - bool _inComposition; - size_t _activeTextStart; - void _SendAndClearText(); - void _RedrawCanvas(); - bool _focused; - - til::point _currentTerminalCursorPos; - double _currentCanvasWidth; - double _currentTextBlockHeight; - winrt::Windows::Foundation::Rect _currentControlBounds; - winrt::Windows::Foundation::Rect _currentTextBounds; - winrt::Windows::Foundation::Rect _currentWindowBounds; + winrt::Windows::UI::Text::Core::CoreTextRange _selection{}; + size_t _activeTextStart = 0; + bool _inComposition = false; + bool _focused = false; + + til::point _currentTerminalCursorPos{}; + double _currentCanvasWidth = 0.0; + double _currentTextBlockHeight = 0.0; + winrt::Windows::Foundation::Rect _currentControlBounds{}; + winrt::Windows::Foundation::Rect _currentTextBounds{}; + winrt::Windows::Foundation::Rect _currentWindowBounds{}; }; } namespace winrt::Microsoft::Terminal::Control::factory_implementation