// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // Description: AutomationPeer associated with TextElements. // using System.Windows.Documents; // ITextContainer using System.Windows.Media; // Geometry using System.Windows.Interop; // HwndSource using MS.Internal; using MS.Internal.Documents; // TextContainerHelper namespace System.Windows.Automation.Peers { /// <summary> /// AutomationPeer associated with TextElements. /// </summary> public class TextElementAutomationPeer : ContentTextAutomationPeer { /// <summary> /// Constructor. /// </summary> /// <param name="owner">Owner of the AutomationPeer.</param> public TextElementAutomationPeer(TextElement owner) : base(owner) { } /// <summary> /// <see cref="AutomationPeer.GetChildrenCore"/> /// </summary> /// <remarks> /// Since DocumentAutomationPeer gives access to its content through /// TextPattern, this method always returns null. /// </remarks> protected override List<AutomationPeer> GetChildrenCore() { TextElement textElement = (TextElement)Owner; return TextContainerHelper.GetAutomationPeersFromRange(textElement.ContentStart, textElement.ContentEnd, null); } /// <summary> /// <see cref="AutomationPeer.GetBoundingRectangleCore"/> /// </summary> protected override Rect GetBoundingRectangleCore() { TextElement textElement = (TextElement)Owner; ITextView textView = textElement.TextContainer.TextView; if (textView == null || !textView.IsValid) { return Rect.Empty; } Geometry geometry = textView.GetTightBoundingGeometryFromTextPositions(textElement.ContentStart, textElement.ContentEnd); if (geometry != null) { PresentationSource presentationSource = PresentationSource.CriticalFromVisual(textView.RenderScope); if (presentationSource == null) { return Rect.Empty; } HwndSource hwndSource = presentationSource as HwndSource; // If the source isn't an HwnSource, there's not much we can do, return empty rect if (hwndSource == null) { return Rect.Empty; } Rect rectElement = geometry.Bounds; Rect rectRoot = PointUtil.ElementToRoot(rectElement, textView.RenderScope, presentationSource); Rect rectClient = PointUtil.RootToClient(rectRoot, presentationSource); Rect rectScreen = PointUtil.ClientToScreen(rectClient, hwndSource); return rectScreen; } else { return Rect.Empty; } } /// <summary> /// <see cref="AutomationPeer.GetClickablePointCore"/> /// </summary> protected override Point GetClickablePointCore() { Point pt = new Point(); TextElement textElement = (TextElement)Owner; ITextView textView = textElement.TextContainer.TextView; if (textView == null || !textView.IsValid || (!textView.Contains(textElement.ContentStart) && !textView.Contains(textElement.ContentEnd))) { return pt; } PresentationSource presentationSource = PresentationSource.CriticalFromVisual(textView.RenderScope); if (presentationSource == null) { return pt; } HwndSource hwndSource = presentationSource as HwndSource; // If the source isn't an HwnSource, there's not much we can do, return empty rect if (hwndSource == null) { return pt; } TextPointer endPosition = textElement.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward); if (endPosition == null || endPosition.CompareTo(textElement.ContentEnd) > 0) endPosition = textElement.ContentEnd; Rect rectElement = CalculateVisibleRect(textView, textElement, textElement.ContentStart, endPosition); Rect rectRoot = PointUtil.ElementToRoot(rectElement, textView.RenderScope, presentationSource); Rect rectClient = PointUtil.RootToClient(rectRoot, presentationSource); Rect rectScreen = PointUtil.ClientToScreen(rectClient, hwndSource); pt = new Point(rectScreen.Left + rectScreen.Width * 0.5, rectScreen.Top + rectScreen.Height * 0.5); return pt; } /// <summary> /// <see cref="AutomationPeer.IsOffscreenCore"/> /// </summary> /// <returns></returns> protected override bool IsOffscreenCore() { IsOffscreenBehavior behavior = AutomationProperties.GetIsOffscreenBehavior(Owner); switch (behavior) { case IsOffscreenBehavior.Onscreen : return false; case IsOffscreenBehavior.Offscreen : return true; default: { TextElement textElement = (TextElement)Owner; ITextView textView = textElement.TextContainer.TextView; if (textView == null || !textView.IsValid || (!textView.Contains(textElement.ContentStart) && !textView.Contains(textElement.ContentEnd))) { return true; } if (CalculateVisibleRect(textView, textElement, textElement.ContentStart, textElement.ContentEnd) == Rect.Empty) { return true; } return false; } } } /// <summary> /// Compute visible rectangle. /// </summary> private Rect CalculateVisibleRect(ITextView textView, TextElement textElement, TextPointer startPointer, TextPointer endPointer) { Geometry geometry = textView.GetTightBoundingGeometryFromTextPositions(startPointer, endPointer); Rect visibleRect = (geometry != null) ? geometry.Bounds : Rect.Empty; Visual visual = textView.RenderScope; while (visual != null && visibleRect != Rect.Empty) { if (VisualTreeHelper.GetClip(visual) != null) { GeneralTransform transform = textView.RenderScope.TransformToAncestor(visual).Inverse; // Safer version of transform to descendent (doing the inverse ourself), // we want the rect inside of our space. (Which is always rectangular and much nicer to work with) if (transform != null) { Rect rectBounds = VisualTreeHelper.GetClip(visual).Bounds; rectBounds = transform.TransformBounds(rectBounds); visibleRect.Intersect(rectBounds); } else { // No visibility if non-invertable transform exists. return Rect.Empty; } } visual = VisualTreeHelper.GetParent(visual) as Visual; } return visibleRect; } /// <summary> /// Gets collection of AutomationPeers for given text range. /// </summary> internal override List<AutomationPeer> GetAutomationPeersFromRange(ITextPointer start, ITextPointer end) { // Force children connection to automation tree. GetChildren(); TextElement textElement = (TextElement)Owner; return TextContainerHelper.GetAutomationPeersFromRange(start, end, textElement.ContentStart); } /// <summary> /// Gets the visibility of the TextElement owner based on its TextView /// </summary> internal bool? IsTextViewVisible { get { TextElement textElement = (TextElement)Owner; ITextView textView = textElement?.TextContainer?.TextView; UIElement uiElement = textView?.RenderScope; return uiElement?.IsVisible; } } } } |