From 45f970e0f20ee7230e8fd25873df1d9b4510a7d5 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Wed, 13 Oct 2021 17:13:56 +0200 Subject: [PATCH] added vertical movement to multiline text fields --- MLEM.Ui/Elements/TextField.cs | 57 +++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/MLEM.Ui/Elements/TextField.cs b/MLEM.Ui/Elements/TextField.cs index 677f7f3..b9a4128 100644 --- a/MLEM.Ui/Elements/TextField.cs +++ b/MLEM.Ui/Elements/TextField.cs @@ -178,9 +178,6 @@ namespace MLEM.Ui.Elements { /// If this is true, pressing will insert a new line into the if the allows it. /// Additionally, text will be rendered with horizontal soft wraps, and lines that are outside of the text field's bounds will be hidden. /// - /// - /// Moving up and down through the text field, and clicking on text to start editing at the mouse's position, are currently not supported. - /// public bool Multiline { get => this.multiline; set { @@ -263,6 +260,10 @@ namespace MLEM.Ui.Elements { this.CaretPos--; } else if (this.Input.IsKeyPressed(Keys.Right)) { this.CaretPos++; + } else if (this.Multiline && this.Input.IsKeyPressed(Keys.Up)) { + this.MoveCaretToLine(this.CaretLine - 1); + } else if (this.Multiline && this.Input.IsKeyPressed(Keys.Down)) { + this.MoveCaretToLine(this.CaretLine + 1); } else if (this.Input.IsKeyPressed(Keys.Home)) { this.CaretPos = 0; } else if (this.Input.IsKeyPressed(Keys.End)) { @@ -365,6 +366,31 @@ namespace MLEM.Ui.Elements { this.HandleTextChange(); } + /// + /// Moves the to the given line, if it exists. + /// Additionally maintains the roughly based on the visual distance that the caret has from the left border of the current . + /// + /// The line to move the caret to + /// True if the caret was moved, false if it was not (which indicates that the line with the given index does not exist) + public bool MoveCaretToLine(int line) { + var (destStart, destEnd) = this.GetLineBounds(line); + if (destEnd > 0) { + // find the position whose distance from the start is closest to the current distance from the start + var destAccum = ""; + while (destAccum.Length < destEnd - destStart) { + if (this.Font.Value.MeasureString(destAccum).X >= this.caretDrawOffset) { + this.CaretPos = destStart + destAccum.Length; + return true; + } + destAccum += this.text[destStart + destAccum.Length]; + } + // if we don't find a proper position, just move to the end of the destination line + this.CaretPos = destEnd; + return true; + } + return false; + } + /// protected override void InitStyle(UiStyle style) { base.InitStyle(style); @@ -498,6 +524,31 @@ namespace MLEM.Ui.Elements { } } + private (int, int) GetLineBounds(int boundLine) { + if (this.splitText != null) { + var line = 0; + var index = 0; + var startOfLineIndex = 0; + for (var d = 0; d < this.splitText.Length; d++) { + var split = this.splitText[d]; + for (var i = 0; i < split.Length; i++) { + index++; + if (split[i] == '\n') { + if (boundLine == line) + return (startOfLineIndex, index - 1); + line++; + startOfLineIndex = index; + } + } + if (boundLine == line) + return (startOfLineIndex, index - 1); + line++; + startOfLineIndex = index; + } + } + return default; + } + /// /// A delegate method used for ///