Skip to content

Commit

Permalink
TextArea.ScrollToStart/End fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
cwensley committed Dec 10, 2024
1 parent 94fcd16 commit a88b0c4
Show file tree
Hide file tree
Showing 17 changed files with 191 additions and 72 deletions.
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

0 comments on commit a88b0c4

Please sign in to comment.