/// <summary> /// Get the number of lines of text that can fit in the textbox. /// </summary> /// <param name="currParagraph">The text of which paragraph we want to check.</param> /// <returns>Number of lines that can fit in the textbox.</returns> private int GetNumLinesFitInTheTextBox(Paragraph currParagraph) { if (!_multiLine) { return(1); } return(_destRectInternal.Height / (int)(System.Math.Max(currParagraph.GetCharacterActualSize().Y, 1))); }
/// <summary> /// Draw the entity. /// </summary> /// <param name="spriteBatch">Sprite batch to draw on.</param> override protected void DrawEntity(SpriteBatch spriteBatch) { // call base draw function to draw the panel part base.DrawEntity(spriteBatch); // get which paragraph we currently show - real or placeholder bool showPlaceholder = !(IsFocused || _value.Length > 0); Paragraph currParagraph = showPlaceholder ? PlaceholderParagraph : TextParagraph; // get actual processed string _actualDisplayText = PrepareInputTextForDisplay(showPlaceholder, IsFocused); // for multiline only - handle scrollbar visibility and max if (_multiLine && _actualDisplayText != null) { // get how many lines can fit in the textbox and how many lines display text actually have int linesFit = _destRectInternal.Height / (int)(System.Math.Max(currParagraph.GetCharacterActualSize().Y, 1)); int linesInText = _actualDisplayText.Split('\n').Length; // if there are more lines than can fit, show scrollbar and manage scrolling: if (linesInText > linesFit) { // fix paragraph width to leave room for the scrollbar float prevWidth = currParagraph.Size.X; currParagraph.Size = new Vector2(_destRectInternal.Width / UserInterface.Active.GlobalScale - 20, 0); if (currParagraph.Size.X != prevWidth) { // update size and re-calculate lines in text _actualDisplayText = PrepareInputTextForDisplay(showPlaceholder, IsFocused); linesInText = _actualDisplayText.Split('\n').Length; } // set scrollbar max and steps _scrollbar.Max = (uint)System.Math.Max(linesInText - linesFit, 2); _scrollbar.StepsCount = _scrollbar.Max; _scrollbar.Visible = true; // update text to fit scrollbar. first, rebuild the text with just the visible segment List <string> lines = new List <string>(_actualDisplayText.Split('\n')); int from = System.Math.Min(_scrollbar.Value, lines.Count - 1); int size = System.Math.Min(linesFit, lines.Count - from); lines = lines.GetRange(from, size); _actualDisplayText = string.Join("\n", lines); currParagraph.Text = _actualDisplayText; } // if no need for scrollbar make it invisible else { currParagraph.Size = Vector2.Zero; _scrollbar.Visible = false; } } // set placeholder and main text visibility based on current value TextParagraph.Visible = !showPlaceholder; PlaceholderParagraph.Visible = showPlaceholder; }
/// <summary> /// Handle mouse click event. /// TextInput override this function to handle picking caret position. /// </summary> /// <param name="input">Input helper instance.</param> override protected void DoOnClick(InputHelper input) { // first call base DoOnClick base.DoOnClick(input); // check if hit paragraph if (_value.Length > 0) { // get relative position Vector2 actualParagraphPos = new Vector2(_destRectInternal.Location.X, _destRectInternal.Location.Y); Vector2 relativeOffset = input.MousePosition - actualParagraphPos; // calc caret position Vector2 charSize = TextParagraph.GetCharacterActualSize(); int x = (int)(relativeOffset.X / charSize.X); _caret = x; // if multiline, take line into the formula if (_multiLine) { // get the whole processed text TextParagraph.Text = _value; TextParagraph.CalcTextActualRectWithWrap(); string processedValueText = TextParagraph.GetProcessedText(); // calc y position and add scrollbar value to it int y = (int)(relativeOffset.Y / charSize.Y) + _scrollbar.Value; // break actual text into lines List <string> lines = new List <string>(processedValueText.Split('\n')); for (int i = 0; i < y && i < lines.Count; ++i) { _caret += lines[i].Length + 1; } } // if override text length reset caret if (_caret >= _value.Length) { _caret = -1; } } // if don't click on the paragraph itself, reset caret position. else { _caret = -1; } }
/// <summary> /// Move scrollbar to show caret position. /// </summary> public void ScrollToCaret() { // skip if no scrollbar if (_scrollbar == null) { return; } // make sure caret position is legal if (_caret >= _value.Length) { _caret = -1; } // if caret is at end of text jump to it if (_caret == -1) { _scrollbar.Value = (int)_scrollbar.Max; } // if not try to find the right pos else { // get how many lines can fit in the textbox int linesFit = GetNumLinesFitInTheTextBox(TextParagraph); TextParagraph.Text = _value; TextParagraph.CalcTextActualRectWithWrap(); // get caret position string processedText = TextParagraph.GetProcessedText();; Point caretPosition = CalculateCaretPositionForMultiline(processedText, _caret); // find current line Vector2 charSize = TextParagraph.GetCharacterActualSize(); int currentLine = (int)(caretPosition.Y / charSize.Y); // reposition the scrollbar // if the carret is before the textInput dest rect if (currentLine - _scrollbar.Value < 0) { _scrollbar.Value = currentLine; } // if the carret is after the textInput dest rect else if (currentLine - _scrollbar.Value >= linesFit) { _scrollbar.Value = currentLine - linesFit + 1; } } }
/// <summary> /// When list is resized (also called on init), create the labels to show item values and init graphical stuff. /// </summary> protected virtual void OnResize() { // if not visible, skip if (!IsVisible()) { _hadResizeWhileNotVisible = true; return; } // clear the _hadResizeWhileNotVisible flag _hadResizeWhileNotVisible = false; // remove all children before re-creating them ClearChildren(); // remove previous paragraphs list _paragraphs.Clear(); // make sure destination rect is up-to-date UpdateDestinationRects(); // calculate paragraphs quantity int i = 0; while (true) { // create and add new paragraph Paragraph paragraph = UserInterface.DefaultParagraph(".", Anchor.Auto); paragraph.PromiscuousClicksMode = true; paragraph.WrapWords = false; paragraph.UpdateStyle(DefaultParagraphStyle); paragraph.Scale = paragraph.Scale * ItemsScale; paragraph.SpaceAfter = paragraph.SpaceAfter + new Vector2(0, ExtraSpaceBetweenLines - 2); paragraph.ExtraMargin.Y = ExtraSpaceBetweenLines / 2 + 3; paragraph.AttachedData = new ParagraphData(this, i++); paragraph.UseActualSizeForCollision = false; paragraph.Size = new Vector2(0, paragraph.GetCharacterActualSize().Y + ExtraSpaceBetweenLines); paragraph.BackgroundColorPadding = new Point((int)Padding.X, 5); paragraph.BackgroundColorUseBoxSize = true; paragraph._hiddenInternalEntity = true; paragraph.PropagateEventsTo(this); AddChild(paragraph); // call the callback whenever a new paragraph is created OnCreatedListParagraph(paragraph); // add to paragraphs list _paragraphs.Add(paragraph); // add callback to selection paragraph.OnClick += (Entity entity) => { ParagraphData data = (ParagraphData)entity.AttachedData; if (!data.list.LockSelection) { data.list.Select(data.relativeIndex, true); } }; // to calculate paragraph actual bottom paragraph.UpdateDestinationRects(); // if out of list bounderies remove this paragraph and stop if ((paragraph.GetActualDestRect().Bottom > _destRect.Bottom - _scaledPadding.Y) || i > _list.Count) { RemoveChild(paragraph); _paragraphs.Remove(paragraph); break; } } // add scrollbar last, but only if needed if (_paragraphs.Count > 0 && _paragraphs.Count < _list.Count) { // add scrollbar to list AddChild(_scrollbar, false); // calc max scroll value _scrollbar.Max = (uint)(_list.Count - _paragraphs.Count); if (_scrollbar.Max < 2) { _scrollbar.Max = 2; } _scrollbar.StepsCount = _scrollbar.Max; _scrollbar.Visible = true; } // if no scrollbar is needed, hide it else { _scrollbar.Visible = false; if (_scrollbar.Value > 0) { _scrollbar.Value = 0; } } }