@@ -15,8 +15,8 @@ internal class TextSelectionHandleCanvas : Canvas
1515 private static bool s_isInTouchMode ;
1616
1717 private readonly TextSelectionHandle _caretHandle ;
18- private readonly TextSelectionHandle _startHandle ;
19- private readonly TextSelectionHandle _endHandle ;
18+ private readonly TextSelectionHandle _handle1 ;
19+ private readonly TextSelectionHandle _handle2 ;
2020 private TextPresenter ? _presenter ;
2121 private TextBox ? _textBox ;
2222 private bool _showHandle ;
@@ -32,8 +32,8 @@ internal bool ShowHandles
3232
3333 if ( ! value )
3434 {
35- _startHandle . IsVisible = false ;
36- _endHandle . IsVisible = false ;
35+ _handle1 . IsVisible = false ;
36+ _handle2 . IsVisible = false ;
3737 _caretHandle . IsVisible = false ;
3838 }
3939
@@ -44,37 +44,37 @@ internal bool ShowHandles
4444 public TextSelectionHandleCanvas ( )
4545 {
4646 _caretHandle = new TextSelectionHandle ( ) { SelectionHandleType = SelectionHandleType . Caret } ;
47- _startHandle = new TextSelectionHandle ( ) ;
48- _endHandle = new TextSelectionHandle ( ) ;
47+ _handle1 = new TextSelectionHandle ( ) ;
48+ _handle2 = new TextSelectionHandle ( ) ;
49+
50+ _caretHandle . SelectionHandleType = SelectionHandleType . Caret ;
51+ _handle1 . SelectionHandleType = SelectionHandleType . Start ;
52+ _handle2 . SelectionHandleType = SelectionHandleType . End ;
4953
5054 Children . Add ( _caretHandle ) ;
51- Children . Add ( _startHandle ) ;
52- Children . Add ( _endHandle ) ;
55+ Children . Add ( _handle1 ) ;
56+ Children . Add ( _handle2 ) ;
5357
5458 _caretHandle . DragStarted += Handle_DragStarted ;
5559 _caretHandle . DragDelta += CaretHandle_DragDelta ;
5660 _caretHandle . DragCompleted += Handle_DragCompleted ;
57- _startHandle . DragDelta += StartHandle_DragDelta ;
58- _startHandle . DragCompleted += Handle_DragCompleted ;
59- _startHandle . DragStarted += Handle_DragStarted ;
60- _endHandle . DragDelta += EndHandle_DragDelta ;
61- _endHandle . DragCompleted += Handle_DragCompleted ;
62- _endHandle . DragStarted += Handle_DragStarted ;
63-
64- _caretHandle . Classes . Add ( "caret" ) ;
65- _startHandle . Classes . Add ( "start" ) ;
66- _endHandle . Classes . Add ( "end" ) ;
67-
68- _startHandle . SetTopLeft ( default ) ;
61+ _handle1 . DragDelta += StartHandle_DragDelta ;
62+ _handle1 . DragCompleted += Handle_DragCompleted ;
63+ _handle1 . DragStarted += Handle_DragStarted ;
64+ _handle2 . DragDelta += EndHandle_DragDelta ;
65+ _handle2 . DragCompleted += Handle_DragCompleted ;
66+ _handle2 . DragStarted += Handle_DragStarted ;
67+
68+ _handle1 . SetTopLeft ( default ) ;
6969 _caretHandle . SetTopLeft ( default ) ;
70- _endHandle . SetTopLeft ( default ) ;
70+ _handle2 . SetTopLeft ( default ) ;
7171
72- _startHandle . ContextCanceled += Caret_ContextCanceled ;
72+ _handle1 . ContextCanceled += Caret_ContextCanceled ;
7373 _caretHandle . ContextCanceled += Caret_ContextCanceled ;
74- _endHandle . ContextCanceled += Caret_ContextCanceled ;
75- _startHandle . ContextRequested += Caret_ContextRequested ;
74+ _handle2 . ContextCanceled += Caret_ContextCanceled ;
75+ _handle1 . ContextRequested += Caret_ContextRequested ;
7676 _caretHandle . ContextRequested += Caret_ContextRequested ;
77- _endHandle . ContextRequested += Caret_ContextRequested ;
77+ _handle2 . ContextRequested += Caret_ContextRequested ;
7878
7979 IsVisible = ShowHandles ;
8080
@@ -176,21 +176,24 @@ private void EnsureVisible()
176176 if ( bounds == null )
177177 return ;
178178
179- var hasSelection = _presenter . SelectionStart != _presenter . SelectionEnd ;
179+ var clip = bounds . Value . Clip . Inflate ( _textBox ? . Padding ?? new Thickness ( 4 , 0 ) ) ;
180+
181+ var isSelectionDragging = _handle1 . IsDragging || _handle2 . IsDragging ;
182+
183+ var hasSelection = _presenter . SelectionStart != _presenter . SelectionEnd || isSelectionDragging ;
180184
181- _startHandle . IsVisible = ShowHandles && hasSelection &&
182- ! IsOccluded ( new Point ( GetLeft ( _startHandle ) , GetTop ( _startHandle ) ) ) ;
183- _endHandle . IsVisible = ShowHandles && hasSelection &&
184- ! IsOccluded ( new Point ( GetLeft ( _endHandle ) , GetTop ( _endHandle ) ) ) ;
185+ _handle1 . IsVisible = ShowHandles && hasSelection &&
186+ ! IsOccluded ( _handle1 . IndicatorPosition ) ;
187+ _handle2 . IsVisible = ShowHandles && hasSelection &&
188+ ! IsOccluded ( _handle2 . IndicatorPosition ) ;
185189 _caretHandle . IsVisible = ShowHandles && ! hasSelection &&
186- ! IsOccluded ( new Point ( GetLeft ( _caretHandle ) , GetTop ( _caretHandle ) ) ) ;
190+ ! IsOccluded ( _caretHandle . IndicatorPosition ) ;
187191
188192 bool IsOccluded ( Point point )
189193 {
190- return ! bounds . Value . Clip . Contains ( point ) ;
194+ return ! clip . Contains ( point ) ;
191195 }
192196
193-
194197 if ( ShowHandles && ! hasSelection )
195198 {
196199 _showDisposable = DispatcherTimer . RunOnce ( ( ) =>
@@ -219,54 +222,49 @@ private void DragSelectionHandle(TextSelectionHandle handle)
219222 {
220223 CloseFlyout ( ) ;
221224
222- var indicatorPosiiton = GetSearchPoint ( handle ) ;
223- var point = ToPresenter ( indicatorPosiiton ) ;
224- point = point . WithY ( point . Y - _presenter . FontSize / 2 ) ;
225- var hit = _presenter . TextLayout . HitTestPoint ( point ) ;
226- var position = hit . CharacterHit . FirstCharacterIndex + hit . CharacterHit . TrailingLength ;
225+ var position = GetTextPosition ( handle ) ;
226+
227+ var otherHandle = handle == _handle1 ? _handle2 : _handle1 ;
228+
229+ var otherPosition = GetTextPosition ( otherHandle ) ;
230+
231+ if ( position == otherPosition )
232+ {
233+ position = handle . SelectionHandleType == SelectionHandleType . Start ? position - 1 : position + 1 ;
234+
235+ position = Math . Clamp ( position , 0 , ( _textBox . Text ? . Length - 1 ) ?? 1 ) ;
236+ }
227237
228- var otherHandle = handle == _startHandle ? _endHandle : _startHandle ;
229238 using var _ = BeginChange ( ) ;
230239
231240 if ( handle . SelectionHandleType == SelectionHandleType . Start )
232241 {
233- if ( position >= _textBox . SelectionEnd )
234- position = _textBox . SelectionEnd - 1 ;
242+ position = position > _textBox . SelectionEnd ? _textBox . SelectionEnd - 1 : position ;
235243 _textBox . SetCurrentValue ( TextBox . SelectionStartProperty , position ) ;
236244 }
237245 else
238246 {
239- if ( position <= _textBox . SelectionStart )
240- position = _textBox . SelectionStart + 1 ;
247+ position = position < _textBox . SelectionStart ? _textBox . SelectionStart + 1 : position ;
241248 _textBox . SetCurrentValue ( TextBox . SelectionEndProperty , position ) ;
242249 }
243250
244- var selectionStart = _textBox . SelectionStart ;
245- var selectionEnd = _textBox . SelectionEnd ;
246- var start = Math . Min ( selectionStart , selectionEnd ) ;
247- var length = Math . Max ( selectionStart , selectionEnd ) - start ;
248- var rects = new List < Rect > ( _presenter . TextLayout . HitTestTextRange ( start , length ) ) ;
249-
250- if ( rects . Count > 0 )
251- {
252- var first = rects [ 0 ] ;
253- var last = rects [ rects . Count - 1 ] ;
254-
255- if ( handle . SelectionHandleType == SelectionHandleType . Start )
256- handle ? . SetTopLeft ( ToLayer ( first . BottomLeft ) ) ;
257- else
258- handle ? . SetTopLeft ( ToLayer ( last . BottomRight ) ) ;
251+ otherHandle . InvalidateVisual ( ) ;
259252
260- if ( otherHandle . SelectionHandleType == SelectionHandleType . Start )
261- otherHandle ? . SetTopLeft ( ToLayer ( first . BottomLeft ) ) ;
262- else
263- otherHandle ? . SetTopLeft ( ToLayer ( last . BottomRight ) ) ;
264- }
265-
266- _presenter ? . MoveCaretToTextPosition ( position ) ;
253+ _presenter . MoveCaretToTextPosition ( position ) ;
254+ var caretBound = _presenter . GetCursorRectangle ( ) ;
255+ handle . SetTopLeft ( ToLayer ( handle . SelectionHandleType == SelectionHandleType . Start ? caretBound . BottomLeft : caretBound . BottomLeft ) ) ;
267256
268257 EnsureVisible ( ) ;
269258 }
259+
260+ int GetTextPosition ( TextSelectionHandle handle )
261+ {
262+ var indicatorPosition = GetSearchPoint ( handle ) ;
263+ var point = ToPresenter ( indicatorPosition ) ;
264+ var hit = _presenter . TextLayout . HitTestPoint ( point ) ;
265+ var position = hit . CharacterHit . FirstCharacterIndex + hit . CharacterHit . TrailingLength ;
266+ return position ;
267+ }
270268 }
271269
272270 private Point ToLayer ( Point point )
@@ -282,9 +280,9 @@ private Point ToPresenter(Point point)
282280 public void MoveHandlesToSelection ( )
283281 {
284282 if ( _presenter == null
285- || _startHandle . IsDragging
283+ || _handle1 . IsDragging
286284 || _caretHandle . IsDragging
287- || _endHandle . IsDragging )
285+ || _handle2 . IsDragging )
288286 {
289287 return ;
290288 }
@@ -309,21 +307,11 @@ public void MoveHandlesToSelection()
309307 var first = rects [ 0 ] ;
310308 var last = rects [ rects . Count - 1 ] ;
311309
312- if ( ! _startHandle . IsDragging )
313- {
314- _startHandle . SetTopLeft ( ToLayer ( first . BottomLeft ) ) ;
315- _startHandle . SelectionHandleType = selectionStart < selectionEnd ?
316- SelectionHandleType . Start :
317- SelectionHandleType . End ;
318- }
310+ var position = _handle1 . SelectionHandleType == SelectionHandleType . Start ? first . BottomLeft : last . BottomRight ;
311+ _handle1 . SetTopLeft ( ToLayer ( position ) ) ;
319312
320- if ( ! _endHandle . IsDragging )
321- {
322- _endHandle . SetTopLeft ( ToLayer ( last . BottomRight ) ) ;
323- _endHandle . SelectionHandleType = selectionStart > selectionEnd ?
324- SelectionHandleType . Start :
325- SelectionHandleType . End ;
326- }
313+ position = _handle2 . SelectionHandleType == SelectionHandleType . Start ? first . BottomLeft : last . BottomRight ;
314+ _handle2 . SetTopLeft ( ToLayer ( position ) ) ;
327315 }
328316 }
329317
@@ -418,10 +406,10 @@ internal bool ShowFlyout(ContextRequestedEventArgs e)
418406
419407 if ( _textBox . SelectionStart != _textBox . SelectionEnd )
420408 {
421- if ( _startHandle . IsEffectivelyVisible )
422- handle = _startHandle ;
423- else if ( _endHandle . IsEffectivelyVisible )
424- handle = _endHandle ;
409+ if ( _handle1 . IsEffectivelyVisible )
410+ handle = _handle1 ;
411+ else if ( _handle2 . IsEffectivelyVisible )
412+ handle = _handle2 ;
425413 }
426414 else
427415 {
@@ -433,11 +421,17 @@ internal bool ShowFlyout(ContextRequestedEventArgs e)
433421
434422 if ( handle != null )
435423 {
424+ var oldVerticalOffset = flyout . VerticalOffset ;
425+ var oldHorizontalOffset = flyout . HorizontalOffset ;
426+ var oldPlacement = flyout . Placement ;
436427 var topLeft = ToPresenter ( handle . GetTopLeft ( ) ) ;
437428 flyout . VerticalOffset = topLeft . Y - verticalOffset ;
438429 flyout . HorizontalOffset = topLeft . X ;
439430 flyout . Placement = PlacementMode . TopEdgeAlignedLeft ;
440431 _textBox . RaiseEvent ( new ContextRequestedEventArgs ( ) ) ;
432+ flyout . VerticalOffset = oldVerticalOffset ;
433+ flyout . HorizontalOffset = oldHorizontalOffset ;
434+ flyout . Placement = oldPlacement ;
441435
442436 return true ;
443437 }
0 commit comments