Files
GoakeFlax/Source/Game/Cabrito/Console/ConsoleTextBoxBase.cs
2021-05-18 21:39:05 +03:00

396 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using FlaxEditor;
using FlaxEngine;
using FlaxEngine.GUI;
namespace Cabrito
{
// Mostly based on TextBox
public class ConsoleTextBoxBase : TextBoxBase
{
protected TextLayoutOptions _layout;
/// <summary>
/// Gets or sets the text wrapping within the control bounds.
/// </summary>
[EditorDisplay("Style"), EditorOrder(2000), Tooltip("The text wrapping within the control bounds.")]
public TextWrapping Wrapping
{
get => _layout.TextWrapping;
set => _layout.TextWrapping = value;
}
/// <summary>
/// Gets or sets the font.
/// </summary>
[EditorDisplay("Style"), EditorOrder(2000)]
public FontReference Font { get; set; }
/// <summary>
/// Gets or sets the color of the text.
/// </summary>
[EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the text.")]
public Color TextColor { get; set; }
/// <summary>
/// Gets or sets the color of the selection (Transparent if not used).
/// </summary>
[EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the selection (Transparent if not used).")]
public Color SelectionColor { get; set; }
[HideInEditor]
public virtual string TextPrefix { get; set; } = "";
//[HideInEditor]
//public override string Text => _text;
public ConsoleTextBoxBase() : base()
{
}
public ConsoleTextBoxBase(float x, float y, float width, float height) : base(false, x, y, width)
{
Height = height;
IsReadOnly = false;
CaretColor = new Color(1f, 1f, 1f, 1f);
AutoFocus = true;
_layout = TextLayoutOptions.Default;
_layout.VerticalAlignment = IsMultiline ? TextAlignment.Near : TextAlignment.Center;
_layout.TextWrapping = TextWrapping.NoWrap;
_layout.Bounds = new Rectangle(0, 0, Width, Height);//new Rectangle(DefaultMargin, 1, Width - 2 * DefaultMargin, Height - 2);
#if FLAX_EDITOR
var style = Style.Current;
if (style.FontMedium != null)
Font = new FontReference(style.FontMedium);
TextColor = style.Foreground;
SelectionColor = style.BackgroundSelected;
#endif
}
/*protected override void SetText(string value)
{
// Prevent from null problems
if (value == null)
value = string.Empty;
// Filter text
if (value.IndexOf('\r') != -1)
value = value.Replace("\r", "");
// Clamp length
if (value.Length > MaxLength)
value = value.Substring(0, MaxLength);
// Ensure to use only single line
if (_isMultiline == false && value.Length > 0)
{
// Extract only the first line
value = value.GetLines()[0];
}
if (Text != value)
{
Deselect();
ResetViewOffset();
Text = value;
OnTextChanged();
}
}*/
public int GetFontHeight()
{
var font = Font.GetFont();
if (font == null)
return (int)Height;
return (int)Mathf.Round(font.Height * Scale.Y);
}
public override void Clear()
{
// Can't clear the text while user is editing it...
var oldEditing = _isEditing;
_isEditing = false;
base.Clear();
_isEditing = oldEditing;
}
public override void ResetViewOffset()
{
TargetViewOffset = new Vector2(0, 0);
}
/*public void ScrollToEnd()
{
float maxY = TextSize.Y - Height;
float spacing = GetRealLineSpacing();
maxY += spacing;
TargetViewOffset = new Vector2(0, Math.Max(0, maxY));
}*/
public override void ScrollToCaret()
{
if (Text.Length == 0)
return;
Rectangle caretBounds = CaretBounds;
float maxY = TextSize.Y - Height;
Vector2 newLocation = CaretBounds.Location;
TargetViewOffset = Vector2.Clamp(newLocation, new Vector2(0, 0), new Vector2(_targetViewOffset.X, maxY));
}
/*const bool smoothScrolling = false;
public override bool OnMouseWheel(Vector2 location, float delta)
{
if (!IsMultiline || Text.Length == 0)
return false;
if (!smoothScrolling)
delta = GetFontHeight() * Math.Sign(delta) * 3;
else
delta *= 30;
float maxY = TextSize.Y - Height;
float offset = GetRealLineSpacing();
maxY += offset;
TargetViewOffset = Vector2.Clamp(_targetViewOffset - new Vector2(0, delta), new Vector2(0, offset), new Vector2(_targetViewOffset.X, maxY));
return true;
}*/
public override Vector2 GetTextSize()
{
var font = Font.GetFont();
if (font == null)
return Vector2.Zero;
return font.MeasureText(Text, ref _layout);
}
public override Vector2 GetCharPosition(int index, out float height)
{
var font = Font.GetFont();
if (font == null)
{
height = Height;
return Vector2.Zero;
}
height = GetFontHeight();
return font.GetCharPosition(Text, index, ref _layout);
}
public override int HitTestText(Vector2 location)
{
var font = Font.GetFont();
if (font == null)
return 0;
if (TextPrefix != "")
{
var prefixSize = font.MeasureText(TextPrefix);
location.X -= prefixSize.X;
}
return font.HitTestText(Text, location, ref _layout);
}
protected override void OnIsMultilineChanged()
{
base.OnIsMultilineChanged();
_layout.VerticalAlignment = IsMultiline ? TextAlignment.Near : TextAlignment.Center;
}
public override bool OnKeyDown(KeyboardKeys key)
{
bool shiftDown = Root.GetKey(KeyboardKeys.Shift);
bool ctrlDown = Root.GetKey(KeyboardKeys.Control);
if (shiftDown && key == KeyboardKeys.Delete)
Cut();
else if (ctrlDown && key == KeyboardKeys.Insert)
Copy();
else if (shiftDown && key == KeyboardKeys.Insert)
Paste();
if (shiftDown && key == KeyboardKeys.Home)
{
if (!IsReadOnly)
SetSelection(_selectionStart, 0);
return true;
}
else if (shiftDown && key == KeyboardKeys.End)
{
if (!IsReadOnly)
SetSelection(_selectionStart, TextLength);
return true;
}
return base.OnKeyDown(key);
}
bool doubleClicked = false;
System.Diagnostics.Stopwatch lastDoubleClick = new System.Diagnostics.Stopwatch();
Vector2 lastDoubleClickLocation = new Vector2(0, 0);
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
if (doubleClicked && lastDoubleClick.Elapsed.TotalSeconds < 0.5 && location == lastDoubleClickLocation) // Windows defaults to 500ms window
{
doubleClicked = false;
if (OnMouseTripleClick(location, button))
return true;
}
return base.OnMouseDown(location, button);
}
public override bool OnMouseDoubleClick(Vector2 location, MouseButton button)
{
doubleClicked = true;
lastDoubleClick.Restart();
lastDoubleClickLocation = location;
return base.OnMouseDoubleClick(location, button);
}
public bool OnMouseTripleClick(Vector2 location, MouseButton button)
{
if (!IsMultiline)
SelectAll();
else
{
// TODO: select the line
SelectAll();
}
return true;
}
protected override void OnSizeChanged()
{
base.OnSizeChanged();
_layout.Bounds = TextRectangle;
}
public override void Draw()
{
// Cache data
var rect = new Rectangle(Vector2.Zero, Size);
var font = Font.GetFont();
if (!font)
return;
// Background
Color backColor = BackgroundColor;
if (IsMouseOver)
backColor = BackgroundSelectedColor;
if (backColor.A > 0.0f)
Render2D.FillRectangle(rect, backColor);
Color borderColor = IsFocused ? BorderSelectedColor : BorderColor;
if (borderColor.A > 0.0f)
Render2D.DrawRectangle(rect, borderColor);
//string text = TextPrefix + Text;
string text = TextPrefix + Text;
if (text.Length == 0)
return;
// Apply view offset and clip mask
Render2D.PushClip(TextClipRectangle);
bool useViewOffset = !_viewOffset.IsZero;
if (useViewOffset)
Render2D.PushTransform(Matrix3x3.Translation2D(-_viewOffset));
// Check if any text is selected to draw selection
if (HasSelection)
{
Vector2 leftEdge = font.GetCharPosition(text, SelectionLeft + TextPrefix.Length, ref _layout);
Vector2 rightEdge = font.GetCharPosition(text, SelectionRight + TextPrefix.Length, ref _layout);
float fontHeight = GetFontHeight();
// Draw selection background
float alpha = Mathf.Min(1.0f, Mathf.Cos(_animateTime * BackgroundSelectedFlashSpeed) * 0.5f + 1.3f);
alpha = alpha * alpha;
Color selectionColor = SelectionColor * alpha;
int selectedLinesCount = 1 + Mathf.FloorToInt((rightEdge.Y - leftEdge.Y) / fontHeight);
if (selectedLinesCount == 1) // Selected is part of single line
{
Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, rightEdge.X - leftEdge.X, fontHeight);
Render2D.FillRectangle(r1, selectionColor);
}
else // Selected is more than one line
{
float leftMargin = _layout.Bounds.Location.X;
Rectangle r1 = new Rectangle(leftEdge.X, leftEdge.Y, 1000000000, fontHeight);
Render2D.FillRectangle(r1, selectionColor);
for (int i = 3; i <= selectedLinesCount; i++)
{
leftEdge.Y += fontHeight;
Rectangle r = new Rectangle(leftMargin, leftEdge.Y, 1000000000, fontHeight);
Render2D.FillRectangle(r, selectionColor);
}
Rectangle r2 = new Rectangle(leftMargin, rightEdge.Y, rightEdge.X - leftMargin, fontHeight);
Render2D.FillRectangle(r2, selectionColor);
}
}
Render2D.DrawText(font, text, TextColor, ref _layout);
if (CaretPosition > -1)
{
var prefixSize = TextPrefix != "" ? font.MeasureText(TextPrefix) : new Vector2();
var caretBounds = CaretBounds;
caretBounds.X += prefixSize.X;
float alpha = Mathf.Saturate(Mathf.Cos(_animateTime * CaretFlashSpeed) * 0.5f + 0.7f);
alpha = alpha * alpha * alpha * alpha * alpha * alpha;
Render2D.FillRectangle(caretBounds, CaretColor * alpha);
}
// Restore rendering state
if (useViewOffset)
Render2D.PopTransform();
Render2D.PopClip();
}
public override void Paste()
{
if (IsReadOnly)
return;
var clipboardText = Clipboard.Text;
// Handle newlines in clipboard text
if (!string.IsNullOrEmpty(clipboardText))
{
// TODO: probably better to just split these lines and parse each line separately
clipboardText = clipboardText.Replace("\r\n", "");
clipboardText = clipboardText.Replace("\n", "");
}
if (!string.IsNullOrEmpty(clipboardText))
{
var right = SelectionRight;
Insert(clipboardText);
SetSelection(Mathf.Max(right, 0) + clipboardText.Length);
}
}
}
}