Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextArea.ScrollToStart/End fixes #2712

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/monomac
Submodule monomac updated 1 files
+14 −8 src/generator.cs
30 changes: 21 additions & 9 deletions src/Eto.Gtk/Forms/Controls/TextAreaHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,20 +371,32 @@ public virtual void ScrollTo(Range<int> range)
Control.ScrollToMark(mark, 0, false, 0, 0);
}

double GetScrollX() => Control.Direction switch
{
Gtk.TextDirection.Rtl => Control.Justification switch
{
Gtk.Justification.Right => scroll.Hadjustment.Lower,
Gtk.Justification.Center => (scroll.Hadjustment.Upper - scroll.Hadjustment.Lower - scroll.Hadjustment.PageSize) / 2,
_ => scroll.Hadjustment.Upper,
},
_ => Control.Justification switch
{
Gtk.Justification.Right => scroll.Hadjustment.Upper,
Gtk.Justification.Center => (scroll.Hadjustment.Upper - scroll.Hadjustment.Lower - scroll.Hadjustment.PageSize) / 2,
_ => scroll.Hadjustment.Lower,
},
};

public virtual void ScrollToEnd()
{
var end = Control.Buffer.EndIter;
var mark = Control.Buffer.CreateMark(null, end, false);
Control.ScrollToMark(mark, 0, false, 0, 0);
scroll.Vadjustment.Value = scroll.Vadjustment.Upper - scroll.Vadjustment.PageSize;
scroll.Hadjustment.Value = GetScrollX();
}

public virtual void ScrollToStart()
{
var end = Control.Buffer.StartIter;
var mark = Control.Buffer.CreateMark(null, end, false);
Control.ScrollToMark(mark, 0, false, 0, 0);
}


scroll.Vadjustment.Value = scroll.Vadjustment.Lower;
scroll.Hadjustment.Value = GetScrollX();
}
}
}
37 changes: 33 additions & 4 deletions src/Eto.Mac/Forms/Controls/TextAreaHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,40 @@ public BorderType Border
}

public int TextLength => (int)Control.TextStorage.Length;

public void ScrollTo(Range<int> range) => Control.ScrollRangeToVisible(range.ToNS());

public void ScrollToStart() => ScrollTo(new Range<int>(0));
public void ScrollTo(Range<int> range)
{
var nsRange = range.ToNS();
Control.LayoutManager.EnsureLayoutForCharacterRange(nsRange);
Control.ScrollRangeToVisible(nsRange);
}

public void ScrollToStart()
{
Control.LayoutManager.EnsureLayoutForCharacterRange(new NSRange(0, Control.TextStorage.Length));
Control.ScrollRectToVisible(new CGRect(GetScrollX(), 0, 1, 1));
}

nfloat GetScrollX() => Control.UserInterfaceLayoutDirection switch
{
NSUserInterfaceLayoutDirection.RightToLeft => Control.Alignment switch
{
NSTextAlignment.Right => Control.Bounds.Left,
NSTextAlignment.Center => Control.Bounds.GetMidX() - Scroll.DocumentVisibleRect.Width / 2,
_ => Control.Bounds.Right,
},
_ => Control.Alignment switch
{
NSTextAlignment.Right => Control.Bounds.Right,
NSTextAlignment.Center => Control.Bounds.GetMidX() - Scroll.DocumentVisibleRect.Width / 2,
_ => Control.Bounds.Left,
},
};

public void ScrollToEnd() => ScrollTo(Range.FromLength(TextLength, 0));
public void ScrollToEnd()
{
Control.LayoutManager.EnsureLayoutForCharacterRange(new NSRange(0, Control.TextStorage.Length));
Control.ScrollPoint(new CGPoint(GetScrollX(), Control.Bounds.Bottom));
}
}
}
16 changes: 12 additions & 4 deletions src/Eto.WinForms/Forms/Controls/TextAreaHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,15 @@ public override bool ShouldBubbleEvent(swf.Message msg)
return !intrinsicEvents.Contains((Win32.WM)msg.Msg) && base.ShouldBubbleEvent(msg);
}

TextAlignment _textAlignment;

public TextAlignment TextAlignment
{
get { return Control.SelectionAlignment.ToEto(); }
get => _textAlignment;
set
{
if (value == TextAlignment) return;
_textAlignment = value;
var sel = Selection;
Control.SelectAll();
Control.SelectionAlignment = value.ToSWF();
Expand Down Expand Up @@ -277,18 +280,23 @@ public void ScrollTo(Range<int> range)
Win32.SendMessage(Control.Handle, Win32.WM.EM_SETSCROLLPOS, IntPtr.Zero, ref scrollPosition);
}

Win32.SB GetScrollX() => Control.RightToLeft switch
{
swf.RightToLeft.Yes => Win32.SB.RIGHT,
_ => Win32.SB.LEFT
};

public void ScrollToStart()
{
Win32.SendMessage(Control.Handle, Win32.WM.VSCROLL, (IntPtr)Win32.SB.TOP, IntPtr.Zero);
Win32.SendMessage(Control.Handle, Win32.WM.HSCROLL, (IntPtr)Win32.SB.LEFT, IntPtr.Zero);
Win32.SendMessage(Control.Handle, Win32.WM.HSCROLL, (IntPtr)GetScrollX(), IntPtr.Zero);
}

public void ScrollToEnd()
{
Win32.SendMessage(Control.Handle, Win32.WM.VSCROLL, (IntPtr)Win32.SB.BOTTOM, IntPtr.Zero);
Win32.SendMessage(Control.Handle, Win32.WM.HSCROLL, (IntPtr)Win32.SB.LEFT, IntPtr.Zero);
Win32.SendMessage(Control.Handle, Win32.WM.HSCROLL, (IntPtr)GetScrollX(), IntPtr.Zero);
}


}
}
28 changes: 26 additions & 2 deletions src/Eto.Wpf/Forms/Controls/TextAreaHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,33 @@ public virtual BorderType Border

public abstract void ScrollTo(Range<int> range);

public virtual void ScrollToEnd() => Control.ScrollToEnd();
double GetScrollX() => Control.FlowDirection switch
{
sw.FlowDirection.RightToLeft => Control.HorizontalContentAlignment switch
{
sw.HorizontalAlignment.Right => 0,
sw.HorizontalAlignment.Center => (Control.ExtentWidth - Control.ViewportWidth) / 2,
_ => Control.ExtentWidth,
},
_ => Control.HorizontalContentAlignment switch
{
sw.HorizontalAlignment.Right => Control.ExtentWidth,
sw.HorizontalAlignment.Center => (Control.ExtentWidth - Control.ViewportWidth) / 2,
_ => 0,
},
};

public virtual void ScrollToEnd()
{
Control.ScrollToVerticalOffset(Control.ExtentHeight);
Control.ScrollToHorizontalOffset(GetScrollX());
}

public virtual void ScrollToStart() => Control.ScrollToHome();
public virtual void ScrollToStart()
{
Control.ScrollToVerticalOffset(0);
Control.ScrollToHorizontalOffset(GetScrollX());
}

public abstract int TextLength { get; }

Expand Down
4 changes: 2 additions & 2 deletions src/Eto/Forms/Controls/TextArea.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,12 @@ public void Append(string text, bool scrollToCursor = false)
public void ScrollTo(Range<int> range) => Handler.ScrollTo(range);

/// <summary>
/// Scrolls to the start of the text in the text area.
/// Scrolls to the start of the text aligned on the same horizontal side as <see cref="TextAlignment"/>.
/// </summary>
public void ScrollToStart() => Handler.ScrollToStart();

/// <summary>
/// Scrolls to the end of the text in the text area.
/// Scrolls to the end of the text aligned on the same horizontal side as <see cref="TextAlignment"/>.
/// </summary>
public void ScrollToEnd() => Handler.ScrollToEnd();

Expand Down
2 changes: 1 addition & 1 deletion test/Eto.Test/Handlers/TabControlHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public TabControlHandler()
},
};

ContentPanel = new Panel { BackgroundColor = Colors.White };
ContentPanel = new Panel { BackgroundColor = SystemColors.ControlBackground };
var layout = new DynamicLayout { Padding = Padding.Empty, Spacing = Size.Empty };
layout.BeginHorizontal();
layout.Add(tabs);
Expand Down
53 changes: 53 additions & 0 deletions test/Eto.Test/LoremGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Eto.Test
{
public static class LoremGenerator
{
private static readonly string[] Words = new[]
{
"lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "sed", "do",
"eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua", "ut",
"enim", "ad", "minim", "veniam", "quis", "nostrud", "exercitation", "ullamco", "laboris",
"nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat", "duis", "aute", "irure", "dolor",
"in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat",
"nulla", "pariatur", "excepteur", "sint", "occaecat", "cupidatat", "non", "proident", "sunt",
"in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum"
};

public static string Generate(int wordCount, bool randomize = true)
{
var random = new Random();
var result = new List<string>();

for (int i = 0; i < wordCount; i++)
{
var word = Words[randomize ? random.Next(Words.Length) : i % Words.Length];
result.Add(word);
}

return string.Join(" ", result);
}

public static string GenerateLines(int lineCount, int maxWordsPerLine, bool randomize = true)
{
return GenerateLines(lineCount, 0, maxWordsPerLine, randomize);
}
public static string GenerateLines(int lineCount, int minWordsPerLine, int maxWordsPerLine, bool randomize = true)
{
var random = new Random();
var result = new List<string>();

for (int i = 0; i < lineCount; i++)
{
var line = Generate(random.Next(minWordsPerLine, maxWordsPerLine), randomize);
result.Add(line);
}

return string.Join("\n", result);
}
}
}
6 changes: 3 additions & 3 deletions test/Eto.Test/Sections/Controls/LabelSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Control MiddleLabel()
Text = "Middle Center Align",
TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
BackgroundColor = Colors.AliceBlue
BackgroundColor = Colors.Gray
};
}

Expand All @@ -73,7 +73,7 @@ Control BottomLabel()
Text = "Bottom Center Align",
TextAlignment = TextAlignment.Center,
VerticalAlignment = VerticalAlignment.Bottom,
BackgroundColor = Colors.AliceBlue
BackgroundColor = Colors.Gray
};
}

Expand Down Expand Up @@ -125,7 +125,7 @@ Control WrapLabel()
{
var label = new Label
{
Text = Utility.LoremTextWithTwoParagraphs
Text = LoremGenerator.GenerateLines(2, 50, 100)
};

var wrapDropDown = new EnumDropDown<WrapMode>();
Expand Down
2 changes: 1 addition & 1 deletion test/Eto.Test/Sections/Controls/RichTextAreaSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public class RichTextAreaSection : Panel
{
public static string RtfString = "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1343\\cocoasubrtf160\r\n{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;}\r\n{\\colortbl;\\red255\\green255\\blue255;}\r\n\\margl1440\\margr1440\\vieww10800\\viewh8400\\viewkind0\r\n\\pard\\tx566\\tx1133\\tx1700\\tx2267\\tx2834\\tx3401\\tx3968\\tx4535\\tx5102\\tx5669\\tx6236\\tx6803\\pardirnatural\r\n\r\n\\f0\\fs24 \\cf0 This is some \r\n\\b bold\r\n\\b0 , \r\n\\i italic\r\n\\i0 , and \\ul underline\\ulnone text! \\\r\n\\\r\n\\pard\\tx566\\tx1133\\tx1700\\tx2267\\tx2834\\tx3401\\tx3968\\tx4535\\tx5102\\tx5669\\tx6236\\tx6803\\pardirnatural\\qr\r\n\\cf0 Some other text}";

static string LastText = Utility.LoremTextWithTwoParagraphs;
static string LastText = LoremGenerator.GenerateLines(4, 50);

public RichTextAreaSection()
{
Expand Down
2 changes: 1 addition & 1 deletion test/Eto.Test/Sections/Controls/TextAreaSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public TextAreaSection()

Control Default()
{
var text = new TextArea { Text = Utility.LoremTextWithTwoParagraphs };
var text = new TextArea { Text = LoremGenerator.GenerateLines(3, 100) };
LogEvents(text);

return new TableLayout
Expand Down
4 changes: 2 additions & 2 deletions test/Eto.Test/Sections/Drawing/FormattedTextSection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public FormattedTextSection()

var formattedText = new FormattedText
{
Text = Utility.LoremTextWithTwoParagraphs,
Text = LoremGenerator.GenerateLines(2, 10, 30),
MaximumSize = new SizeF(500, 80),
Wrap = FormattedTextWrapMode.Word,
Trimming = FormattedTextTrimming.CharacterEllipsis,
Expand All @@ -34,7 +34,7 @@ public FormattedTextSection()

var formattedTextWithNewLines = new FormattedText
{
Text = Utility.LoremTextWithNewLines,
Text = LoremGenerator.GenerateLines(6, 10, 20),
Wrap = FormattedTextWrapMode.Word,
Trimming = FormattedTextTrimming.CharacterEllipsis,
ForegroundBrush = Brushes.White,
Expand Down
2 changes: 1 addition & 1 deletion test/Eto.Test/UnitTests/Forms/Controls/LabelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void WrapModeNoneShouldAllowNewLines()
{
var label = new Label
{
Text = Utility.LoremTextWithTwoParagraphs,
Text = LoremGenerator.GenerateLines(2, 50),
Wrap = WrapMode.None
};
var layout = new PixelLayout
Expand Down
22 changes: 22 additions & 0 deletions test/Eto.Test/UnitTests/Forms/Controls/TextAreaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,28 @@ public class TextAreaTests : TextAreaTests<TextArea>
public class TextAreaTests<T> : TestBase
where T : TextArea, new()
{
[Test]
[ManualTest]
public void ScrollToEndShouldHaveValidStateAfterTextIsInserted() => ManualForm(
"TextArea should scroll to the end after each time text is inserted,\nand should be fully visible",
(form, label) =>
{
var textArea = new T { Size = new Size(400, 300) };

form.Shown += async (sender, e) =>
{
for (int i = 0; i < 20; i++)
{
textArea.Text += LoremGenerator.GenerateLines(10, 100);
textArea.ScrollToEnd();
await Task.Delay(200);
}
};

return textArea;
});


[Test]
public void CheckSelectionTextCaretAfterSettingText()
{
Expand Down
16 changes: 8 additions & 8 deletions test/Eto.Test/UnitTests/Forms/MessageBoxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ public class MessageBoxTests : TestBase
{
public static IEnumerable<string> GetMessages()
{
yield return Utility.GenerateLoremText(2);
yield return Utility.GenerateLoremText(10);
yield return Utility.GenerateLoremText(20);
yield return Utility.GenerateLoremText(100, true);
yield return Utility.GenerateLoremText(1000, true);
yield return Utility.GenerateLoremText(1000, false);
yield return Utility.GenerateLoremText(3000, true);
yield return Utility.GenerateLoremText(3000, false);
yield return LoremGenerator.Generate(2);
yield return LoremGenerator.Generate(10);
yield return LoremGenerator.Generate(20);
yield return LoremGenerator.GenerateLines(2, 100);
yield return LoremGenerator.GenerateLines(4, 1000);
yield return LoremGenerator.Generate(1000);
yield return LoremGenerator.GenerateLines(20, 300);
yield return LoremGenerator.Generate(3000);
}

[TestCaseSource(nameof(GetMessages))]
Expand Down
Loading
Loading