How can I ensure my Adornment scrolls with text?

SyntaxEditor for WPF Forum

Posted 7 years ago by Will Sullivan
Version: 12.1.0561
Avatar

I'm adding a simple adornment that essentially lives over a certain area of the SyntaxEditor.  I'm adding it using the Selection adornment layer.

var selectionLayer = SyntaxEditor.ActiveView.GetAdornmentLayer(AdornmentLayerDefinitions.Selection);

selectionLayer.AddAdornment(
    border,
    new Point(),
    model,
    null);

 No problem.  It shows great, and works correctly when the editor zooms in and out.

However, whenever the editor view scrolls, it remains in the same position relative to the viewport rather than to the top of the document when the text document is scrolled.

In other words, when the document is scrolled down one line, I wish my adornment to scroll up one line.  As if the adornment layer was relative to the top of the document rather than the top of the editor.

How do I get my adornment to scroll with the document?

Comments (4)

Posted 7 years ago by Will Sullivan
Avatar

Well, that's typical.

void ActiveView_TextAreaLayout(object sender, TextViewTextAreaLayoutEventArgs e)
{
    var first = e.TranslatedViewLines.FirstOrDefault();
    if(first == null)
        return;
    foreach (var adorner in _adornments)
        adorner.Translate(0, first.TranslationY);
}

 Its not a complete solution, but it does allow me to translate the adorners relative to scroll position.  I still do have to do a lot of work to get this running correctly.

 

Is this how its done?  Is there any other way to accomplish my goal?

Posted 7 years ago by Will Sullivan
Avatar

Noticing this algorithm is horribly simplistic, and will result in translations when lines are added or removed.  Really all I care about is when the view is scrolled, but I can't figure out how to do that.

Answer - Posted 7 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Will,

All the scrolling is virtualized so there is no real absolute scroll position that you can track.  What you need to do is peg your adornment to some text position if you want it to move with text.

If you have your adornment manager inherit our DecorationAdornmentManagerBase class, then you will get that sort of functionality for free.  But you need to have some an ITag driving where the decoration should appear, which is the best choice when your adornment relates to a range of text.  Assuming you have that, then our base class does all the work for you and you just have to override its AddAdornment method and in there add an adornment when called and pass the viewLine as its "tag" data.

Or if you aren't using ITags here, you'd need to watch the view's TextAreaLayout event as you did.  It tells you e.AddedOrUpdatedViewLines, e.TranslatedViewLines, and e.RemovedViewLines.  In the case of the e.TranslatedViewLines, they tell you the viewLine.TranslationY so that you know how to adjust your adornment.  But in cases where all the prior visible lines are scrolled off in one scroll change, that collection will be empty so watch for that.

Another option would probably be to still handle that event but use props/methods on the view (like FirstVisiblePosition) to sort of determine where your adornment should be positioned relative to.


Actipro Software Support

Answer - Posted 7 years ago by Will Sullivan
Avatar

Which is essentially what I did.  My adornment specifically is not associated with text.  In fact, it is the complete opposite--I don't care what text is within the editor.  I do want the adornment to scroll with the document, however.

What I did was listen to the text area layout event, then manually calculate my offsets by determining the difference between the first visual line and the first actual line, then multiplying this by the character bounds height in order to determine the translation amount.  I have to do this relative to the last translation, as adornment translation is relative rather than absolute.

double _lastOffset = 0;
private void OnViewTextAreaLayout(object sender, TextViewTextAreaLayoutEventArgs e)
{
    var first = e.View.VisibleViewLines.First();
    var line = first.StartPosition.Line;
    var lineHeight = e.View.VisibleViewLines.First().GetCharacterBounds(0).Height;
    var offset = line * lineHeight;
    var delta = _lastOffset - offset;
    foreach (var a in this.AdornmentLayer.Adornments)
        a.Translate(0, delta);
    _lastOffset = offset;
}

 I grab the first visible line, and determine what line number it is.  I then determine the size in pixels of each line.  Multiplying, I get the offset of the first visible line from the top of the document.  I then subtract this from the last offset I calculated, which gives me my delta.  I can then use this delta to translate all adornments.  Last step is to save this for the next scroll event.

The latest build of this product (v2019.1 build 0683) was released 1 month ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.