diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs index 429472525d..e5dc94416d 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs @@ -338,6 +338,7 @@ namespace ICSharpCode.AvalonEdit.Editing } bool showScheduled; + bool hasWin32Caret; void ShowInternal() { @@ -350,7 +351,16 @@ namespace ICSharpCode.AvalonEdit.Editing if (caretAdorner != null && textView != null) { VisualLine visualLine = textView.GetVisualLine(position.Line); if (visualLine != null) { - caretAdorner.Show(CalcCaretRectangle(visualLine)); + Rect caretRect = CalcCaretRectangle(visualLine); + // Create Win32 caret so that Windows knows where our managed caret is. This is necessary for + // features like 'Follow text editing' in the Windows Magnifier. + if (!hasWin32Caret) { + hasWin32Caret = Win32.CreateCaret(textView, caretRect.Size); + } + if (hasWin32Caret) { + Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset); + } + caretAdorner.Show(caretRect); } else { caretAdorner.Hide(); } @@ -364,6 +374,10 @@ namespace ICSharpCode.AvalonEdit.Editing { Debug.WriteLine("Caret.Hide()"); visible = false; + if (hasWin32Caret) { + Win32.DestroyCaret(); + hasWin32Caret = false; + } if (caretAdorner != null) { caretAdorner.Hide(); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs index bfb1390205..95ea661fe9 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/IReadOnlySectionProvider.cs @@ -27,6 +27,9 @@ namespace ICSharpCode.AvalonEdit.Editing /// /// All segments in the result must be within the given segment, and they must be returned in order /// (e.g. if two segments are returned, EndOffset of first segment must be less than StartOffset of second segment). + /// + /// For replacements, the last segment being returned will be replaced with the new text. If an empty list is returned, + /// no replacement will be done. /// IEnumerable GetDeletableSegments(ISegment segment); } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/Win32.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/Win32.cs index ab22a81608..72553a6fc2 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/Win32.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Utils/Win32.cs @@ -8,6 +8,9 @@ using System; using System.Runtime.InteropServices; using System.Security; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Media; namespace ICSharpCode.AvalonEdit.Utils { @@ -23,11 +26,64 @@ namespace ICSharpCode.AvalonEdit.Utils get { return TimeSpan.FromMilliseconds(SafeNativeMethods.GetCaretBlinkTime()); } } + /// + /// Creates an invisible Win32 caret for the specified Visual with the specified size (coordinates local to the owner visual). + /// + public static bool CreateCaret(Visual owner, Size size) + { + if (owner == null) + throw new ArgumentNullException("owner"); + HwndSource source = PresentationSource.FromVisual(owner) as HwndSource; + if (source != null) { + Vector r = owner.PointToScreen(new Point(size.Width, size.Height)) - owner.PointToScreen(new Point(0, 0)); + return SafeNativeMethods.CreateCaret(source.Handle, IntPtr.Zero, (int)Math.Ceiling(r.X), (int)Math.Ceiling(r.Y)); + } else { + return false; + } + } + + /// + /// Sets the position of the caret previously created using . position is relative to the owner visual. + /// + public static bool SetCaretPosition(Visual owner, Point position) + { + if (owner == null) + throw new ArgumentNullException("owner"); + HwndSource source = PresentationSource.FromVisual(owner) as HwndSource; + if (source != null) { + Point pointOnRootVisual = owner.TransformToAncestor(source.RootVisual).Transform(position); + Point pointOnHwnd = pointOnRootVisual.TransformToDevice(source.RootVisual); + return SafeNativeMethods.SetCaretPos((int)pointOnHwnd.X, (int)pointOnHwnd.Y); + } else { + return false; + } + } + + /// + /// Destroys the caret previously created using . + /// + public static bool DestroyCaret() + { + return SafeNativeMethods.DestroyCaret(); + } + [SuppressUnmanagedCodeSecurity] static class SafeNativeMethods { [DllImport("user32.dll")] public static extern int GetCaretBlinkTime(); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetCaretPos(int x, int y); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyCaret(); } } }