Browse Source

Squashed 'AvalonEdit/' changes from 58e2044..307ad08

307ad08 Merge branch 'master' of f:\SD.git-bundle
05ebf8f Merge branch '4.0'
a0901d5 Improved inline object handling. Inline objects that change their size in response to user input are now supported.
be409d9 AvalonEdit: Change inline object implementation so that inline objects are direct children of the TextView (instead of being children of the TextLayer).
38e5797 move MouseHover logic from TextView to separate class and make it reusable

git-subtree-dir: AvalonEdit
git-subtree-split: 307ad0829ce305b08e3266f4165822eb1fc5288d
pull/1/head
Daniel Grunwald 14 years ago
parent
commit
0513c24aaf
  1. 1
      ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj
  2. 15
      ICSharpCode.AvalonEdit/Rendering/InlineObjectRun.cs
  3. 102
      ICSharpCode.AvalonEdit/Rendering/MouseHoverLogic.cs
  4. 89
      ICSharpCode.AvalonEdit/Rendering/TextLayer.cs
  5. 244
      ICSharpCode.AvalonEdit/Rendering/TextView.cs
  6. 1
      ICSharpCode.AvalonEdit/Rendering/VisualLine.cs
  7. 3
      ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs

1
ICSharpCode.AvalonEdit/ICSharpCode.AvalonEdit.csproj

@ -257,6 +257,7 @@ @@ -257,6 +257,7 @@
<DependentUpon>TextView.cs</DependentUpon>
</Compile>
<Compile Include="Rendering\LinkElementGenerator.cs" />
<Compile Include="Rendering\MouseHoverLogic.cs" />
<Compile Include="Rendering\NewLineElementGenerator.cs" />
<Compile Include="Rendering\SimpleTextSource.cs">
<DependentUpon>FormattedTextElement.cs</DependentUpon>

15
ICSharpCode.AvalonEdit/Rendering/InlineObjectRun.cs

@ -38,11 +38,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -38,11 +38,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (context == null)
throw new ArgumentNullException("context");
// remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
// TODO: certainly the text view should handle this internally? external code might want to use InlineObjectRun,
// but doesn't have access to textLayer.RemoveInlineObject
context.TextView.textLayer.RemoveInlineObject(this.Element);
return new InlineObjectRun(1, this.TextRunProperties, this.Element);
}
}
@ -55,6 +50,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -55,6 +50,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
UIElement element;
int length;
TextRunProperties properties;
internal Size desiredSize;
/// <summary>
/// Creates a new InlineObjectRun instance.
@ -122,11 +118,10 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -122,11 +118,10 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <inheritdoc/>
public override TextEmbeddedObjectMetrics Format(double remainingParagraphWidth)
{
Size size = element.DesiredSize;
double baseline = TextBlock.GetBaselineOffset(element);
if (double.IsNaN(baseline))
baseline = size.Height;
return new TextEmbeddedObjectMetrics(size.Width, size.Height, baseline);
baseline = desiredSize.Height;
return new TextEmbeddedObjectMetrics(desiredSize.Width, desiredSize.Height, baseline);
}
/// <inheritdoc/>
@ -135,8 +130,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -135,8 +130,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (this.element.IsArrangeValid) {
double baseline = TextBlock.GetBaselineOffset(element);
if (double.IsNaN(baseline))
baseline = element.DesiredSize.Height;
return new Rect(new Point(0, -baseline), element.DesiredSize);
baseline = desiredSize.Height;
return new Rect(new Point(0, -baseline), desiredSize);
} else {
return Rect.Empty;
}

102
ICSharpCode.AvalonEdit/Rendering/MouseHoverLogic.cs

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
namespace ICSharpCode.AvalonEdit.Rendering
{
public class MouseHoverLogic : IDisposable
{
UIElement target;
DispatcherTimer mouseHoverTimer;
Point mouseHoverStartPoint;
MouseEventArgs mouseHoverLastEventArgs;
bool mouseHovering;
public MouseHoverLogic(UIElement target)
{
if (target == null)
throw new ArgumentNullException("target");
this.target = target;
this.target.MouseLeave += MouseHoverLogicMouseLeave;
this.target.MouseMove += MouseHoverLogicMouseMove;
}
void MouseHoverLogicMouseMove(object sender, MouseEventArgs e)
{
Point newPosition = e.GetPosition(this.target);
Vector mouseMovement = mouseHoverStartPoint - newPosition;
if (Math.Abs(mouseMovement.X) > SystemParameters.MouseHoverWidth
|| Math.Abs(mouseMovement.Y) > SystemParameters.MouseHoverHeight)
{
StopHovering();
mouseHoverStartPoint = newPosition;
mouseHoverLastEventArgs = e;
mouseHoverTimer = new DispatcherTimer(SystemParameters.MouseHoverTime, DispatcherPriority.Background,
OnMouseHoverTimerElapsed, this.target.Dispatcher);
mouseHoverTimer.Start();
}
// do not set e.Handled - allow others to also handle MouseMove
}
void MouseHoverLogicMouseLeave(object sender, MouseEventArgs e)
{
StopHovering();
// do not set e.Handled - allow others to also handle MouseLeave
}
void StopHovering()
{
if (mouseHoverTimer != null) {
mouseHoverTimer.Stop();
mouseHoverTimer = null;
}
if (mouseHovering) {
mouseHovering = false;
OnMouseHoverStopped(mouseHoverLastEventArgs);
}
}
void OnMouseHoverTimerElapsed(object sender, EventArgs e)
{
mouseHoverTimer.Stop();
mouseHoverTimer = null;
mouseHovering = true;
OnMouseHover(mouseHoverLastEventArgs);
}
public event EventHandler<MouseEventArgs> MouseHover;
protected virtual void OnMouseHover(MouseEventArgs e)
{
if (MouseHover != null) {
MouseHover(this, e);
}
}
public event EventHandler<MouseEventArgs> MouseHoverStopped;
protected virtual void OnMouseHoverStopped(MouseEventArgs e)
{
if (MouseHoverStopped != null) {
MouseHoverStopped(this, e);
}
}
bool disposed;
public void Dispose()
{
if (!disposed) {
this.target.MouseLeave -= MouseHoverLogicMouseLeave;
this.target.MouseMove -= MouseHoverLogicMouseMove;
}
disposed = true;
}
}
}

89
ICSharpCode.AvalonEdit/Rendering/TextLayer.cs

@ -24,6 +24,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -24,6 +24,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// </remarks>
sealed class TextLayer : Layer
{
/// <summary>
/// the index of the text layer in the layers collection
/// </summary>
internal int index;
public TextLayer(TextView textView) : base(textView, KnownLayer.Text)
{
}
@ -33,89 +38,5 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -33,89 +38,5 @@ namespace ICSharpCode.AvalonEdit.Rendering
base.OnRender(drawingContext);
textView.RenderTextLayer(drawingContext);
}
#region Inline object handling
internal List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
/// <summary>
/// Adds a new inline object.
/// </summary>
internal void AddInlineObject(InlineObjectRun inlineObject)
{
inlineObjects.Add(inlineObject);
AddVisualChild(inlineObject.Element);
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
List<VisualLine> visualLinesWithOutstandingInlineObjects = new List<VisualLine>();
internal void RemoveInlineObjects(VisualLine visualLine)
{
// Delay removing inline objects:
// A document change immediately invalidates affected visual lines, but it does not
// cause an immediate redraw.
// To prevent inline objects from flickering when they are recreated, we delay removing
// inline objects until the next redraw.
visualLinesWithOutstandingInlineObjects.Add(visualLine);
}
internal void RemoveInlineObjectsNow()
{
inlineObjects.RemoveAll(
ior => {
if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
RemoveInlineObjectRun(ior);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
// Remove InlineObjectRun.Element from TextLayer.
// Caller of RemoveInlineObjectRun will remove it from inlineObjects collection.
void RemoveInlineObjectRun(InlineObjectRun ior)
{
if (ior.Element.IsKeyboardFocusWithin) {
// When the inline element that has the focus is removed, WPF will reset the
// focus to the main window without raising appropriate LostKeyboardFocus events.
// To work around this, we manually set focus to the next focusable parent.
UIElement element = textView;
while (element != null && !element.Focusable) {
element = VisualTreeHelper.GetParent(element) as UIElement;
}
if (element != null)
Keyboard.Focus(element);
}
ior.VisualLine = null;
RemoveVisualChild(ior.Element);
}
/// <summary>
/// Removes the inline object that displays the specified UIElement.
/// </summary>
internal void RemoveInlineObject(UIElement element)
{
inlineObjects.RemoveAll(
ior => {
if (ior.Element == element) {
RemoveInlineObjectRun(ior);
return true;
}
return false;
});
}
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return inlineObjects.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
return inlineObjects[index].Element;
}
#endregion
}
}

244
ICSharpCode.AvalonEdit/Rendering/TextView.cs

@ -53,8 +53,12 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -53,8 +53,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
this.Options = new TextEditorOptions();
Debug.Assert(singleCharacterElementGenerator != null); // assert that the option change created the builtin element generators
layers = new UIElementCollection(this, this);
layers = new LayerCollection(this);
InsertLayer(textLayer, KnownLayer.Text, LayerInsertionPosition.Replace);
this.hoverLogic = new MouseHoverLogic(this);
this.hoverLogic.MouseHover += (sender, e) => RaiseHoverEventPair(e, PreviewMouseHoverEvent, MouseHoverEvent);
this.hoverLogic.MouseHoverStopped += (sender, e) => RaiseHoverEventPair(e, PreviewMouseHoverStoppedEvent, MouseHoverStoppedEvent);
}
#endregion
@ -294,7 +298,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -294,7 +298,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
#region Layers
internal readonly TextLayer textLayer;
readonly UIElementCollection layers;
readonly LayerCollection layers;
/// <summary>
/// Gets the list of layers displayed in the text view.
@ -303,6 +307,47 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -303,6 +307,47 @@ namespace ICSharpCode.AvalonEdit.Rendering
get { return layers; }
}
sealed class LayerCollection : UIElementCollection
{
readonly TextView textView;
public LayerCollection(TextView textView)
: base(textView, textView)
{
this.textView = textView;
}
public override void Clear()
{
base.Clear();
textView.LayersChanged();
}
public override int Add(UIElement element)
{
int r = base.Add(element);
textView.LayersChanged();
return r;
}
public override void RemoveAt(int index)
{
base.RemoveAt(index);
textView.LayersChanged();
}
public override void RemoveRange(int index, int count)
{
base.RemoveRange(index, count);
textView.LayersChanged();
}
}
void LayersChanged()
{
textLayer.index = layers.IndexOf(textLayer);
}
/// <summary>
/// Inserts a new layer at a position specified relative to an existing layer.
/// </summary>
@ -352,18 +397,128 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -352,18 +397,128 @@ namespace ICSharpCode.AvalonEdit.Rendering
/// <inheritdoc/>
protected override int VisualChildrenCount {
get { return layers.Count; }
get { return layers.Count + inlineObjects.Count; }
}
/// <inheritdoc/>
protected override Visual GetVisualChild(int index)
{
return layers[index];
int cut = textLayer.index + 1;
if (index < cut)
return layers[index];
else if (index < cut + inlineObjects.Count)
return inlineObjects[index - cut].Element;
else
return layers[index - inlineObjects.Count];
}
/// <inheritdoc/>
protected override System.Collections.IEnumerator LogicalChildren {
get { return layers.GetEnumerator(); }
get {
return inlineObjects.Select(io => io.Element).Concat(layers.Cast<UIElement>()).GetEnumerator();
}
}
#endregion
#region Inline object handling
List<InlineObjectRun> inlineObjects = new List<InlineObjectRun>();
/// <summary>
/// Adds a new inline object.
/// </summary>
internal void AddInlineObject(InlineObjectRun inlineObject)
{
Debug.Assert(inlineObject.VisualLine != null);
// Remove inline object if its already added, can happen e.g. when recreating textrun for word-wrapping
bool alreadyAdded = false;
for (int i = 0; i < inlineObjects.Count; i++) {
if (inlineObjects[i].Element == inlineObject.Element) {
RemoveInlineObjectRun(inlineObjects[i], true);
inlineObjects.RemoveAt(i);
alreadyAdded = true;
break;
}
}
inlineObjects.Add(inlineObject);
if (!alreadyAdded) {
AddVisualChild(inlineObject.Element);
}
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
inlineObject.desiredSize = inlineObject.Element.DesiredSize;
}
void MeasureInlineObjects()
{
// As part of MeasureOverride(), re-measure the inline objects
foreach (InlineObjectRun inlineObject in inlineObjects) {
if (inlineObject.VisualLine.IsDisposed) {
// Don't re-measure inline objects that are going to be removed anyways.
// If the inline object will be reused in a different VisualLine, we'll measure it in the AddInlineObject() call.
continue;
}
inlineObject.Element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
if (!inlineObject.Element.DesiredSize.IsClose(inlineObject.desiredSize)) {
// the element changed size -> recreate its parent visual line
inlineObject.desiredSize = inlineObject.Element.DesiredSize;
if (allVisualLines.Remove(inlineObject.VisualLine)) {
DisposeVisualLine(inlineObject.VisualLine);
}
}
}
}
List<VisualLine> visualLinesWithOutstandingInlineObjects = new List<VisualLine>();
void RemoveInlineObjects(VisualLine visualLine)
{
// Delay removing inline objects:
// A document change immediately invalidates affected visual lines, but it does not
// cause an immediate redraw.
// To prevent inline objects from flickering when they are recreated, we delay removing
// inline objects until the next redraw.
if (visualLine.hasInlineObjects) {
visualLinesWithOutstandingInlineObjects.Add(visualLine);
}
}
/// <summary>
/// Remove the inline objects that were marked for removal.
/// </summary>
void RemoveInlineObjectsNow()
{
if (visualLinesWithOutstandingInlineObjects.Count == 0)
return;
inlineObjects.RemoveAll(
ior => {
if (visualLinesWithOutstandingInlineObjects.Contains(ior.VisualLine)) {
RemoveInlineObjectRun(ior, false);
return true;
}
return false;
});
visualLinesWithOutstandingInlineObjects.Clear();
}
// Remove InlineObjectRun.Element from TextLayer.
// Caller of RemoveInlineObjectRun will remove it from inlineObjects collection.
void RemoveInlineObjectRun(InlineObjectRun ior, bool keepElement)
{
if (!keepElement && ior.Element.IsKeyboardFocusWithin) {
// When the inline element that has the focus is removed, WPF will reset the
// focus to the main window without raising appropriate LostKeyboardFocus events.
// To work around this, we manually set focus to the next focusable parent.
UIElement element = this;
while (element != null && !element.Focusable) {
element = VisualTreeHelper.GetParent(element) as UIElement;
}
if (element != null)
Keyboard.Focus(element);
}
ior.VisualLine = null;
if (!keepElement)
RemoveVisualChild(ior.Element);
}
#endregion
@ -410,7 +565,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -410,7 +565,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
{
VerifyAccess();
if (allVisualLines.Remove(visualLine)) {
visibleVisualLines = null;
DisposeVisualLine(visualLine);
InvalidateMeasure(redrawPriority);
}
@ -422,7 +576,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -422,7 +576,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
public void Redraw(int offset, int length, DispatcherPriority redrawPriority = DispatcherPriority.Normal)
{
VerifyAccess();
bool removedLine = false;
bool changedSomethingBeforeOrInLine = false;
for (int i = 0; i < allVisualLines.Count; i++) {
VisualLine visualLine = allVisualLines[i];
@ -431,15 +584,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -431,15 +584,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (offset <= lineEnd) {
changedSomethingBeforeOrInLine = true;
if (offset + length >= lineStart) {
removedLine = true;
allVisualLines.RemoveAt(i--);
DisposeVisualLine(visualLine);
}
}
}
if (removedLine) {
visibleVisualLines = null;
}
if (changedSomethingBeforeOrInLine) {
// Repaint not only when something in visible area was changed, but also when anything in front of it
// was changed. We might have to redraw the line number margin. Or the highlighting changed.
@ -492,11 +641,12 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -492,11 +641,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
if (newVisualLines != null && newVisualLines.Contains(visualLine)) {
throw new ArgumentException("Cannot dispose visual line because it is in construction!");
}
visibleVisualLines = null;
visualLine.IsDisposed = true;
foreach (TextLine textLine in visualLine.TextLines) {
textLine.Dispose();
}
textLayer.RemoveInlineObjects(visualLine);
RemoveInlineObjects(visualLine);
}
#endregion
@ -677,11 +827,11 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -677,11 +827,11 @@ namespace ICSharpCode.AvalonEdit.Rendering
ClearVisualLines();
lastAvailableSize = availableSize;
textLayer.RemoveInlineObjectsNow();
foreach (UIElement layer in layers) {
layer.Measure(availableSize);
}
MeasureInlineObjects();
InvalidateVisual(); // = InvalidateArrange+InvalidateRender
textLayer.InvalidateVisual();
@ -700,7 +850,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -700,7 +850,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
}
}
textLayer.RemoveInlineObjectsNow();
// remove inline objects only at the end, so that inline objects that were re-used are not removed from the editor
RemoveInlineObjectsNow();
maxWidth += AdditionalHorizontalScrollAmount;
double heightTreeHeight = this.DocumentHeight;
@ -949,7 +1100,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -949,7 +1100,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
foreach (var span in textLine.GetTextRunSpans()) {
InlineObjectRun inline = span.Value as InlineObjectRun;
if (inline != null && inline.VisualLine != null) {
Debug.Assert(textLayer.inlineObjects.Contains(inline));
Debug.Assert(inlineObjects.Contains(inline));
double distance = textLine.GetDistanceFromCharacterHit(new CharacterHit(offset, 0));
inline.Element.Arrange(new Rect(new Point(pos.X + distance, pos.Y), inline.Element.DesiredSize));
}
@ -1566,63 +1717,12 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -1566,63 +1717,12 @@ namespace ICSharpCode.AvalonEdit.Rendering
remove { RemoveHandler(MouseHoverStoppedEvent, value); }
}
DispatcherTimer mouseHoverTimer;
Point mouseHoverStartPoint;
MouseEventArgs mouseHoverLastEventArgs;
bool mouseHovering;
MouseHoverLogic hoverLogic;
/// <inheritdoc/>
protected override void OnMouseMove(MouseEventArgs e)
void RaiseHoverEventPair(MouseEventArgs e, RoutedEvent tunnelingEvent, RoutedEvent bubblingEvent)
{
base.OnMouseMove(e);
Point newPosition = e.GetPosition(this);
Vector mouseMovement = mouseHoverStartPoint - newPosition;
if (Math.Abs(mouseMovement.X) > SystemParameters.MouseHoverWidth
|| Math.Abs(mouseMovement.Y) > SystemParameters.MouseHoverHeight)
{
StopHovering();
mouseHoverStartPoint = newPosition;
mouseHoverLastEventArgs = e;
mouseHoverTimer = new DispatcherTimer(SystemParameters.MouseHoverTime, DispatcherPriority.Background,
OnMouseHoverTimerElapsed, this.Dispatcher);
mouseHoverTimer.Start();
}
// do not set e.Handled - allow others to also handle MouseMove
}
/// <inheritdoc/>
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
StopHovering();
// do not set e.Handled - allow others to also handle MouseLeave
}
void StopHovering()
{
if (mouseHoverTimer != null) {
mouseHoverTimer.Stop();
mouseHoverTimer = null;
}
if (mouseHovering) {
mouseHovering = false;
RaiseHoverEventPair(PreviewMouseHoverStoppedEvent, MouseHoverStoppedEvent);
}
}
void OnMouseHoverTimerElapsed(object sender, EventArgs e)
{
mouseHoverTimer.Stop();
mouseHoverTimer = null;
mouseHovering = true;
RaiseHoverEventPair(PreviewMouseHoverEvent, MouseHoverEvent);
}
void RaiseHoverEventPair(RoutedEvent tunnelingEvent, RoutedEvent bubblingEvent)
{
var mouseDevice = mouseHoverLastEventArgs.MouseDevice;
var stylusDevice = mouseHoverLastEventArgs.StylusDevice;
var mouseDevice = e.MouseDevice;
var stylusDevice = e.StylusDevice;
int inputTime = Environment.TickCount;
var args1 = new MouseEventArgs(mouseDevice, inputTime, stylusDevice) {
RoutedEvent = tunnelingEvent,
@ -1636,8 +1736,6 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -1636,8 +1736,6 @@ namespace ICSharpCode.AvalonEdit.Rendering
};
RaiseEvent(args2);
}
#endregion
/// <summary>

1
ICSharpCode.AvalonEdit/Rendering/VisualLine.cs

@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -22,6 +22,7 @@ namespace ICSharpCode.AvalonEdit.Rendering
{
TextView textView;
List<VisualLineElement> elements;
internal bool hasInlineObjects;
/// <summary>
/// Gets the document to which this VisualLine belongs.

3
ICSharpCode.AvalonEdit/Rendering/VisualLineTextSource.cs

@ -42,7 +42,8 @@ namespace ICSharpCode.AvalonEdit.Rendering @@ -42,7 +42,8 @@ namespace ICSharpCode.AvalonEdit.Rendering
InlineObjectRun inlineRun = run as InlineObjectRun;
if (inlineRun != null) {
inlineRun.VisualLine = VisualLine;
TextView.textLayer.AddInlineObject(inlineRun);
VisualLine.hasInlineObjects = true;
TextView.AddInlineObject(inlineRun);
}
return run;
}

Loading…
Cancel
Save