jacobslusser / ScintillaNET

A Windows Forms control, wrapper, and bindings for the Scintilla text editor.
MIT License
966 stars 246 forks source link

Scrolling and Wrapping Issue #158

Closed MikeDev96 closed 8 years ago

MikeDev96 commented 8 years ago

Currently I'm using Scintilla with WrapMode on Word and it works great. I'm also making it scroll to the bottom to keep up, which is where the problem occurs. It only scrolls the first line into visibility regardless of whether it's visually 2 or 3. Obviously this is only viewed as 2 or 3 lines but still only counts as 1 actual line. If I add a button to my form to manually scroll to the bottom it does, so it's like it takes some time to figure out that it needs to go multiple lines rather than just 1. This is the code I'm using:

Scintilla.AppendText("text here");
Scintilla.GotoPosition(Scintilla.TextLength);
Scintilla.ScrollCaret();

Here's the bottom of my Scintilla: image As you can see the scrollbar isn't all the way at the bottom.

Here's how it should be: image

Also, I was trying to detect if the Scintilla was scrolled all the way to the bottom like this:

Scintilla.Lines[Scintilla.Lines.Count - 1].Visible

but it didn't work at all, it kept returning true. Am I using it incorrectly or is it bugged?

MikeDev96 commented 8 years ago

Is there a specific reason you've replied to other people's issues and not mine?

jacobslusser commented 8 years ago

Apologies, it is nothing personal. Many of the other issues I've replied to are responses that I know off the top of my head. This question was something that I thought I would need to investigate more.

As you've found, a line in the Scintilla.Lines collection is a document line... which is not the same as a display line.

Have you tried setting the Scintilla.FirstVisibleLine property to the last line of the document to force it to scroll all the way to the bottom? Or Scintilla.ExecuteCmd(Comamnd.DocumentEnd)?

MikeDev96 commented 8 years ago

I tried Scintilla.ExecuteCmd(Command.DocumentEnd) and it seems to be working perfectly now. Although the documentation says it moves the caret to the end of the document, it does but also scrolls the caret into view. https://github.com/jacobslusser/ScintillaNET/blob/master/src/ScintillaNET/Command.cs#L319

MikeDev96 commented 8 years ago

While we're on the topic of Scintilla (what else? lol), how would I go about detecting if the Scintilla is scrolled to the bottom? Would I check if the scrollbar is as far down as it can go or would I check if the last line is visible? And then how would I go about checking that.

jacobslusser commented 8 years ago

Cool, I'm glad you got it working. Since virtually all of the Command enumerations that move the caret will also scroll the caret into view, it's not documented for each option.

How to determine if the last line is in view? I'm not sure I have a great answer for that. A combination of FirstVisibleLine and LinesOnScreen is generally enough for most people, however, you are using wrapping and those may not give you accurate results. I suppose you could check that the scroll bar is at maximum... I can't think of any use cases where that would not be true so it's worth a shot.

MikeDev96 commented 8 years ago

How would I go about checking if the scrollbar is max? The only property I can see related is VScrollBar which doesn't offer any further properties.

jacobslusser commented 8 years ago

That information is not exposed through Scintilla(NET), or even through Windows Forms. You have to drop down to Win32 calls:

[DllImport("user32")]
private static extern int GetScrollInfo(IntPtr hwnd, int nBar, ref SCROLLINFO scrollInfo);

public struct SCROLLINFO
{
    public int cbSize;
    public int fMask;
    public int min;
    public int max;
    public int nPage;
    public int nPos;
    public int nTrackPos;
}

public static bool ReachedBottom(Scintilla scintilla)
{
    SCROLLINFO scrollInfo = new SCROLLINFO();
    scrollInfo.cbSize = Marshal.SizeOf(scrollInfo);
    //SIF_RANGE = 0x1, SIF_TRACKPOS = 0x10,  SIF_PAGE= 0x2
    scrollInfo.fMask = 0x10 | 0x1 | 0x2;
    GetScrollInfo(scintilla.Handle, 1, ref scrollInfo);//nBar = 1 -> VScrollbar
    return scrollInfo.max <= scrollInfo.nTrackPos + scrollInfo.nPage;
}

Call the ReachedBottom method to determine if the vertical scroll bar is at maxium. The original source code for this is from http://stackoverflow.com/questions/19343794/how-to-know-if-richtextbox-vertical-scrollbar-reached-the-max-value. I only change it to use <= in return scrollInfo.max <= scrollInfo.nTrackPos + scrollInfo.nPage.

Cheers.