You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.GUI.Dialogs;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Color value editor with picking support.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
[HideInEditor]
public class ColorValueBox : Control
{
/// <summary>
/// Delegate function used for the color picker events handling.
/// </summary>
/// <param name="color">The selected color.</param>
/// <param name="sliding">True if user is using a slider, otherwise false.</param>
public delegate void ColorPickerEvent(Color color, bool sliding);
/// <summary>
/// Delegate function used for the color picker close event handling.
/// </summary>
public delegate void ColorPickerClosedEvent();
/// <summary>
/// Delegate function used to handle showing color picking dialog.
/// </summary>
/// <param name="targetControl">The GUI control that invokes the picker.</param>
/// <param name="initialValue">The initial value.</param>
/// <param name="colorChanged">The color changed event.</param>
/// <param name="pickerClosed">The color editing end event.</param>
/// <param name="useDynamicEditing">True if allow dynamic value editing (slider-like usage), otherwise will fire color change event only on editing end.</param>
/// <returns>The created color picker dialog or null if failed.</returns>
public delegate IColorPickerDialog ShowPickColorDialogDelegate(Control targetControl, Color initialValue, ColorPickerEvent colorChanged, ColorPickerClosedEvent pickerClosed = null, bool useDynamicEditing = true);
/// <summary>
/// Shows picking color dialog (see <see cref="ShowPickColorDialogDelegate"/>).
/// </summary>
public static ShowPickColorDialogDelegate ShowPickColorDialog;
/// <summary>
/// The current opened dialog.
/// </summary>
protected IColorPickerDialog _currentDialog;
/// <summary>
/// True if slider is in use.
/// </summary>
protected bool _isSliding;
/// <summary>
/// The value.
/// </summary>
protected Color _value;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action ValueChanged;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action<ColorValueBox> ColorValueChanged;
/// <summary>
/// Gets or sets the color value.
/// </summary>
public Color Value
{
get => _value;
set
{
if (_value != value)
{
_value = value;
// Fire event
OnValueChanged();
}
}
}
/// <summary>
/// Gets a value indicating whether user is using a slider.
/// </summary>
public bool IsSliding => _isSliding;
/// <summary>
/// Initializes a new instance of the <see cref="ColorValueBox"/> class.
/// </summary>
public ColorValueBox()
: base(0, 0, 32, 18)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ColorValueBox"/> class.
/// </summary>
/// <param name="value">The initial value.</param>
/// <param name="x">The x location</param>
/// <param name="y">The y location</param>
public ColorValueBox(Color value, float x, float y)
: base(x, y, 32, 18)
{
_value = value;
}
/// <summary>
/// Called when value gets changed.
/// </summary>
protected virtual void OnValueChanged()
{
ValueChanged?.Invoke();
ColorValueChanged?.Invoke(this);
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
var style = Style.Current;
var r = new Rectangle(2, 2, Width - 4, Height - 4);
Render2D.FillRectangle(r, _value);
Render2D.DrawRectangle(r, IsMouseOver ? style.BackgroundSelected : Color.Black);
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
// Show color picker dialog
_currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed);
return true;
}
private void OnColorChanged(Color color, bool sliding)
{
// Force send ValueChanged event is sliding state gets modified by the color picker (e.g the color picker window closing event)
if (_isSliding != sliding)
{
_isSliding = sliding;
_value = color;
OnValueChanged();
}
else
{
Value = color;
}
}
private void OnPickerClosed()
{
_currentDialog = null;
}
/// <inheritdoc />
public override void OnDestroy()
{
if (_currentDialog != null)
{
_currentDialog.ClosePicker();
_currentDialog = null;
}
base.OnDestroy();
}
}
}

View File

@@ -0,0 +1,167 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using System.Globalization;
using FlaxEditor.Utilities;
using FlaxEngine;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Double precision floating point value editor.
/// </summary>
/// <seealso cref="double" />
[HideInEditor]
public class DoubleValueBox : ValueBox<double>
{
/// <inheritdoc />
public override double Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (Math.Abs(_value - value) > Mathf.Epsilon)
{
// Set value
_value = value;
// Update
UpdateText();
OnValueChanged();
}
}
}
/// <inheritdoc />
public override double MinValue
{
get => _min;
set
{
if (!Mathf.NearEqual(_min, value))
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <inheritdoc />
public override double MaxValue
{
get => _max;
set
{
if (!Mathf.NearEqual(_max, value))
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FloatValueBox"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x location.</param>
/// <param name="y">The y location.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="slideSpeed">The slide speed.</param>
public DoubleValueBox(double value, float x = 0, float y = 0, float width = 120, double min = double.MinValue, double max = double.MaxValue, float slideSpeed = 1)
: base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed)
{
UpdateText();
}
/// <summary>
/// Sets the value limits.
/// </summary>
/// <param name="min">The minimum value (bottom range).</param>
/// <param name="max">The maximum value (upper range).</param>
public void SetLimits(double min, double max)
{
_min = min;
_max = Math.Max(_min, max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(RangeAttribute limits)
{
_min = limits.Min;
_max = Math.Max(_min, limits.Max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(LimitAttribute limits)
{
_min = limits.Min;
_max = Math.Max(_min, (double)limits.Max);
_slideSpeed = limits.SliderSpeed;
Value = Value;
}
/// <summary>
/// Sets the limits from the other <see cref="DoubleValueBox"/>.
/// </summary>
/// <param name="other">The other.</param>
public void SetLimits(DoubleValueBox other)
{
_min = other._min;
_max = other._max;
_slideSpeed = other._slideSpeed;
Value = Value;
}
/// <inheritdoc />
protected sealed override void UpdateText()
{
string text;
if (double.IsPositiveInfinity(_value) || _value == double.MaxValue)
text = "Infinity";
else if (double.IsNegativeInfinity(_value) || _value == double.MinValue)
text = "-Infinity";
else
text = _value.ToString(CultureInfo.InvariantCulture);
SetText(text);
}
/// <inheritdoc />
protected override void TryGetValue()
{
try
{
Value = ShuntingYard.Parse(Text);
}
catch (Exception ex)
{
// Fall back to previous value
Editor.LogWarning(ex);
}
}
/// <inheritdoc />
protected override void ApplySliding(float delta)
{
Value = _startSlideValue + delta;
}
}
}

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using System.Globalization;
using FlaxEditor.Utilities;
using FlaxEngine;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Floating point value editor.
/// </summary>
/// <seealso cref="float" />
[HideInEditor]
public class FloatValueBox : ValueBox<float>
{
/// <inheritdoc />
public override float Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (Math.Abs(_value - value) > Mathf.Epsilon)
{
// Set value
_value = value;
// Update
UpdateText();
OnValueChanged();
}
}
}
/// <inheritdoc />
public override float MinValue
{
get => _min;
set
{
if (!Mathf.NearEqual(_min, value))
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <inheritdoc />
public override float MaxValue
{
get => _max;
set
{
if (!Mathf.NearEqual(_max, value))
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FloatValueBox"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x location.</param>
/// <param name="y">The y location.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="slideSpeed">The slide speed.</param>
public FloatValueBox(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue, float slideSpeed = 1)
: base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed)
{
UpdateText();
}
/// <summary>
/// Sets the value limits.
/// </summary>
/// <param name="min">The minimum value (bottom range).</param>
/// <param name="max">The maximum value (upper range).</param>
public void SetLimits(float min, float max)
{
_min = min;
_max = Mathf.Max(_min, max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(RangeAttribute limits)
{
_min = limits.Min;
_max = Mathf.Max(_min, limits.Max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(LimitAttribute limits)
{
_min = limits.Min;
_max = Mathf.Max(_min, limits.Max);
_slideSpeed = limits.SliderSpeed;
Value = Value;
}
/// <summary>
/// Sets the limits from the other <see cref="FloatValueBox"/>.
/// </summary>
/// <param name="other">The other.</param>
public void SetLimits(FloatValueBox other)
{
_min = other._min;
_max = other._max;
_slideSpeed = other._slideSpeed;
Value = Value;
}
/// <inheritdoc />
protected sealed override void UpdateText()
{
string text;
if (float.IsPositiveInfinity(_value) || _value == float.MaxValue)
text = "Infinity";
else if (float.IsNegativeInfinity(_value) || _value == float.MinValue)
text = "-Infinity";
else
text = _value.ToString(CultureInfo.InvariantCulture);
SetText(text);
}
/// <inheritdoc />
protected override void TryGetValue()
{
try
{
var value = ShuntingYard.Parse(Text);
Value = (float)Math.Round(value, 5);
}
catch (Exception ex)
{
// Fall back to previous value
Editor.LogWarning(ex);
}
}
/// <inheritdoc />
protected override void ApplySliding(float delta)
{
Value = _startSlideValue + delta;
}
}
}

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.Utilities;
using FlaxEngine;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Integer value editor.
/// </summary>
/// <seealso cref="int" />
[HideInEditor]
public class IntValueBox : ValueBox<int>
{
/// <inheritdoc />
public override int Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (_value != value)
{
// Set value
_value = value;
// Update
UpdateText();
OnValueChanged();
}
}
}
/// <inheritdoc />
public override int MinValue
{
get => _min;
set
{
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <inheritdoc />
public override int MaxValue
{
get => _max;
set
{
if (_max != value)
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="IntValueBox"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x location.</param>
/// <param name="y">The y location.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="slideSpeed">The slide speed.</param>
public IntValueBox(int value, float x = 0, float y = 0, float width = 120, int min = int.MinValue, int max = int.MaxValue, float slideSpeed = 1)
: base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed)
{
UpdateText();
}
/// <summary>
/// Sets the value limits.
/// </summary>
/// <param name="min">The minimum value (bottom range).</param>
/// <param name="max">The maximum value (upper range).</param>
public void SetLimits(int min, int max)
{
_min = min;
_max = Mathf.Max(_min, max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(RangeAttribute limits)
{
_min = limits.Min == float.MinValue ? int.MinValue : (int)limits.Min;
_max = Math.Max(_min, limits.Max == float.MaxValue ? int.MaxValue : (int)limits.Max);
Value = Value;
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(LimitAttribute limits)
{
_min = limits.Min == float.MinValue ? int.MinValue : (int)limits.Min;
_max = Math.Max(_min, limits.Max == float.MaxValue ? int.MaxValue : (int)limits.Max);
_slideSpeed = limits.SliderSpeed;
Value = Value;
}
/// <summary>
/// Sets the limits from the other <see cref="IntValueBox"/>.
/// </summary>
/// <param name="other">The other.</param>
public void SetLimits(IntValueBox other)
{
_min = other._min;
_max = other._max;
_slideSpeed = other._slideSpeed;
Value = Value;
}
/// <inheritdoc />
protected sealed override void UpdateText()
{
var text = _value.ToString();
SetText(text);
}
/// <inheritdoc />
protected override void TryGetValue()
{
try
{
var value = ShuntingYard.Parse(Text);
Value = (int)value;
}
catch (Exception ex)
{
// Fall back to previous value
Editor.LogWarning(ex);
}
}
/// <inheritdoc />
protected override void ApplySliding(float delta)
{
Value = _startSlideValue + (int)delta;
}
}
}

View File

@@ -0,0 +1,122 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Integer (long type) value editor.
/// </summary>
/// <seealso cref="long" />
[HideInEditor]
public class LongValueBox : ValueBox<long>
{
/// <inheritdoc />
public override long Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (_value != value)
{
// Set value
_value = value;
// Update
UpdateText();
OnValueChanged();
}
}
}
/// <inheritdoc />
public override long MinValue
{
get => _min;
set
{
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <inheritdoc />
public override long MaxValue
{
get => _max;
set
{
if (_max != value)
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="LongValueBox"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x location.</param>
/// <param name="y">The y location.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="slideSpeed">The slide speed.</param>
public LongValueBox(long value, float x = 0, float y = 0, float width = 120, long min = long.MinValue, long max = long.MaxValue, float slideSpeed = 1)
: base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed)
{
UpdateText();
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(LimitAttribute limits)
{
_min = limits.Min == int.MinValue ? long.MinValue : (long)limits.Min;
_max = Math.Max(_min, limits.Max == int.MaxValue ? long.MaxValue : (long)limits.Max);
_slideSpeed = limits.SliderSpeed;
Value = Value;
}
/// <inheritdoc />
protected sealed override void UpdateText()
{
var text = _value.ToString();
SetText(text);
}
/// <inheritdoc />
protected override void TryGetValue()
{
// Try to parse long
long value;
if (long.TryParse(Text, out value))
{
// Set value
Value = value;
}
}
/// <inheritdoc />
protected override void ApplySliding(float delta)
{
Value = _startSlideValue + (long)delta;
}
}
}

View File

@@ -0,0 +1,456 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using System.Globalization;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Float value editor with fixed size text box and slider.
/// </summary>
[HideInEditor]
public class SliderControl : ContainerControl
{
/// <summary>
/// The horizontal slider control.
/// </summary>
/// <seealso cref="FlaxEngine.GUI.Control" />
[HideInEditor]
protected class Slider : Control
{
/// <summary>
/// The default size.
/// </summary>
public const int DefaultSize = 16;
/// <summary>
/// The default thickness.
/// </summary>
public const int DefaultThickness = 6;
/// <summary>
/// The minimum value (constant)
/// </summary>
public const float Minimum = 0.0f;
/// <summary>
/// The maximum value (constant).
/// </summary>
public const float Maximum = 100.0f;
private float _value;
private Rectangle _thumbRect;
private float _thumbCenter, _thumbSize;
private bool _isSliding;
/// <summary>
/// Gets or sets the value (normalized to range 0-100).
/// </summary>
public float Value
{
get => _value;
set
{
value = Mathf.Clamp(value, Minimum, Maximum);
if (!Mathf.NearEqual(value, _value))
{
_value = value;
// Update
UpdateThumb();
ValueChanged?.Invoke();
}
}
}
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public Action ValueChanged;
/// <summary>
/// The color of the slider track line
/// </summary>
public Color TrackLineColor { get; set; }
/// <summary>
/// The color of the slider thumb when it's not selected
/// </summary>
public Color ThumbColor { get; set; }
/// <summary>
/// The color of the slider thumb when it's selected
/// </summary>
public Color ThumbColorSelected { get; set; }
/// <summary>
/// Gets a value indicating whether user is using a slider.
/// </summary>
public bool IsSliding => _isSliding;
/// <summary>
/// Occurs when sliding starts.
/// </summary>
public Action SlidingStart;
/// <summary>
/// Occurs when sliding ends.
/// </summary>
public Action SlidingEnd;
/// <summary>
/// Initializes a new instance of the <see cref="Slider"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public Slider(float width, float height)
: base(0, 0, width, height)
{
var style = Style.Current;
TrackLineColor = style.BackgroundHighlighted;
ThumbColor = style.BackgroundNormal;
ThumbColorSelected = style.BackgroundSelected;
}
private void UpdateThumb()
{
// Cache data
float trackSize = TrackSize;
float range = Maximum - Minimum;
_thumbSize = Mathf.Min(trackSize, Mathf.Max(trackSize / range * 10.0f, 30.0f));
float pixelRange = trackSize - _thumbSize;
float perc = (_value - Minimum) / range;
float thumbPosition = (int)(perc * pixelRange);
_thumbCenter = thumbPosition + _thumbSize / 2;
_thumbRect = new Rectangle(thumbPosition + 4, (Height - DefaultThickness) / 2, _thumbSize - 8, DefaultThickness);
}
private void EndSliding()
{
_isSliding = false;
EndMouseCapture();
SlidingEnd?.Invoke();
}
/// <summary>
/// Gets the size of the track.
/// </summary>
private float TrackSize => Width;
/// <inheritdoc />
public override void Draw()
{
base.Draw();
// Draw track line
var lineRect = new Rectangle(4, Height / 2, Width - 8, 1);
Render2D.FillRectangle(lineRect, TrackLineColor);
// Draw thumb
Render2D.FillRectangle(_thumbRect, _isSliding ? ThumbColorSelected : ThumbColor);
}
/// <inheritdoc />
public override void OnLostFocus()
{
if (_isSliding)
{
EndSliding();
}
base.OnLostFocus();
}
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
float mousePosition = location.X;
if (_thumbRect.Contains(ref location))
{
// Start sliding
_isSliding = true;
StartMouseCapture();
SlidingStart?.Invoke();
return true;
}
else
{
// Click change
Value += (mousePosition < _thumbCenter ? -1 : 1) * 10;
}
}
return base.OnMouseDown(location, button);
}
/// <inheritdoc />
public override void OnMouseMove(Vector2 location)
{
if (_isSliding)
{
// Update sliding
Vector2 slidePosition = location + Root.TrackingMouseOffset;
Value = Mathf.Map(slidePosition.X, 4, TrackSize - 4, Minimum, Maximum);
}
else
{
base.OnMouseMove(location);
}
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
if (button == MouseButton.Left && _isSliding)
{
// End sliding
EndSliding();
return true;
}
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override void OnEndMouseCapture()
{
// Check if was sliding
if (_isSliding)
{
EndSliding();
}
else
{
base.OnEndMouseCapture();
}
}
/// <inheritdoc />
protected override void OnSizeChanged()
{
base.OnSizeChanged();
UpdateThumb();
}
}
/// <summary>
/// The slider.
/// </summary>
protected Slider _slider;
/// <summary>
/// The text box.
/// </summary>
protected TextBox _textBox;
/// <summary>
/// The text box size (rest will be the slider area).
/// </summary>
protected const float TextBoxSize = 30.0f;
private float _value;
private float _min, _max;
private bool _valueIsChanging;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action ValueChanged;
/// <summary>
/// Gets or sets the value.
/// </summary>
public float Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (Math.Abs(_value - value) > Mathf.Epsilon)
{
// Set value
_value = value;
// Update
_valueIsChanging = true;
UpdateText();
UpdateSlider();
_valueIsChanging = false;
OnValueChanged();
}
}
}
/// <summary>
/// Gets or sets the minimum value.
/// </summary>
public float MinValue
{
get => _min;
set
{
if (!Mathf.NearEqual(_min, value))
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <summary>
/// Gets or sets the maximum value.
/// </summary>
public float MaxValue
{
get => _max;
set
{
if (!Mathf.NearEqual(_max, value))
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Gets a value indicating whether user is using a slider.
/// </summary>
public bool IsSliding => _slider.IsSliding;
/// <summary>
/// Occurs when sliding starts.
/// </summary>
public event Action SlidingStart;
/// <summary>
/// Occurs when sliding ends.
/// </summary>
public event Action SlidingEnd;
/// <summary>
/// Initializes a new instance of the <see cref="SliderControl"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The position x.</param>
/// <param name="y">The position y.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue)
: base(x, y, width, TextBox.DefaultHeight)
{
_min = min;
_max = max;
_value = Mathf.Clamp(value, min, max);
float split = Width - TextBoxSize;
_slider = new Slider(split, Height)
{
Parent = this,
};
_slider.ValueChanged += SliderOnValueChanged;
_slider.SlidingStart += SlidingStart;
_slider.SlidingEnd += SlidingEnd;
_textBox = new TextBox(false, split, 0)
{
Text = _value.ToString(CultureInfo.InvariantCulture),
Parent = this,
Location = new Vector2(split, 0),
Size = new Vector2(Height, TextBoxSize),
};
_textBox.EditEnd += OnTextBoxEditEnd;
}
private void SliderOnValueChanged()
{
if (_valueIsChanging)
return;
Value = Mathf.Map(_slider.Value, Slider.Minimum, Slider.Maximum, MinValue, MaxValue);
}
private void OnTextBoxEditEnd()
{
if (_valueIsChanging)
return;
var text = _textBox.Text.Replace(',', '.');
if (double.TryParse(text, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var value))
{
Value = (float)Math.Round(value, 5);
}
else
{
UpdateText();
}
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(RangeAttribute limits)
{
_min = limits.Min;
_max = Mathf.Max(_min, limits.Max);
Value = Value;
}
/// <summary>
/// Updates the text of the textbox.
/// </summary>
protected virtual void UpdateText()
{
_textBox.Text = _value.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Updates the slider value.
/// </summary>
protected virtual void UpdateSlider()
{
_slider.Value = Mathf.Map(_value, MinValue, MaxValue, Slider.Minimum, Slider.Maximum);
}
/// <summary>
/// Called when value gets changed.
/// </summary>
protected virtual void OnValueChanged()
{
ValueChanged?.Invoke();
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
base.PerformLayoutBeforeChildren();
float split = Width - TextBoxSize;
_slider.Bounds = new Rectangle(0, 0, split, Height);
_textBox.Bounds = new Rectangle(split, 0, TextBoxSize, Height);
}
/// <inheritdoc />
public override void OnDestroy()
{
_slider = null;
_textBox = null;
base.OnDestroy();
}
}
}

View File

@@ -0,0 +1,132 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using FlaxEditor.Utilities;
using FlaxEngine;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Unsigned integer (ulong type) value editor.
/// </summary>
/// <seealso cref="ulong" />
[HideInEditor]
public class ULongValueBox : ValueBox<ulong>
{
/// <inheritdoc />
public override ulong Value
{
get => _value;
set
{
value = Mathf.Clamp(value, _min, _max);
if (_value != value)
{
// Set value
_value = value;
// Update
UpdateText();
OnValueChanged();
}
}
}
/// <inheritdoc />
public override ulong MinValue
{
get => _min;
set
{
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
_min = value;
Value = Value;
}
}
}
/// <inheritdoc />
public override ulong MaxValue
{
get => _max;
set
{
if (_max != value)
{
if (value < _min)
throw new ArgumentException();
_max = value;
Value = Value;
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ULongValueBox"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x location.</param>
/// <param name="y">The y location.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="slideSpeed">The slide speed.</param>
public ULongValueBox(ulong value, float x = 0, float y = 0, float width = 120, ulong min = ulong.MinValue, ulong max = ulong.MaxValue, float slideSpeed = 1)
: base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed)
{
UpdateText();
}
/// <summary>
/// Sets the limits from the attribute.
/// </summary>
/// <param name="limits">The limits.</param>
public void SetLimits(LimitAttribute limits)
{
_min = limits.Min == int.MinValue ? ulong.MinValue : (ulong)limits.Min;
_max = Math.Max(_min, limits.Max == int.MaxValue ? ulong.MaxValue : (ulong)limits.Max);
_slideSpeed = limits.SliderSpeed;
Value = Value;
}
/// <inheritdoc />
protected sealed override void UpdateText()
{
var text = _value.ToString();
SetText(text);
}
/// <inheritdoc />
protected override void TryGetValue()
{
try
{
var value = ShuntingYard.Parse(Text);
Value = (ulong)value;
}
catch (Exception ex)
{
// Fall back to previous value
Editor.LogWarning(ex);
}
}
/// <inheritdoc />
protected override void ApplySliding(float delta)
{
// Check for negative overflow to positive numbers
if (delta < 0 && _startSlideValue > delta)
Value = (ulong)Mathf.Max(0, (long)_startSlideValue + (long)delta);
else if (delta < 0)
Value = _startSlideValue - (ulong)(-delta);
else
Value = _startSlideValue + (ulong)delta;
}
}
}

View File

@@ -0,0 +1,311 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Input
{
/// <summary>
/// Base class for text boxes for float/int value editing. Supports slider and range clamping.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <seealso cref="FlaxEngine.GUI.TextBox" />
[HideInEditor]
public abstract class ValueBox<T> : TextBox where T : struct, IComparable<T>
{
/// <summary>
/// The sliding box size.
/// </summary>
protected const float SlidingBoxSize = 12.0f;
/// <summary>
/// The current value.
/// </summary>
protected T _value;
/// <summary>
/// The minimum value.
/// </summary>
protected T _min;
/// <summary>
/// The maximum value.
/// </summary>
protected T _max;
/// <summary>
/// The slider speed.
/// </summary>
protected float _slideSpeed;
/// <summary>
/// True if slider is in use.
/// </summary>
protected bool _isSliding;
/// <summary>
/// The value cached on sliding start.
/// </summary>
protected T _startSlideValue;
private Vector2 _startSlideLocation;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action ValueChanged;
/// <summary>
/// Occurs when value gets changed.
/// </summary>
public event Action<ValueBox<T>> BoxValueChanged;
/// <summary>
/// Gets or sets the value.
/// </summary>
public abstract T Value { get; set; }
/// <summary>
/// Gets or sets the minimum value.
/// </summary>
public abstract T MinValue { get; set; }
/// <summary>
/// Gets or sets the maximum value.
/// </summary>
public abstract T MaxValue { get; set; }
/// <summary>
/// Gets a value indicating whether user is using a slider.
/// </summary>
public bool IsSliding => _isSliding;
/// <summary>
/// Occurs when sliding starts.
/// </summary>
public event Action SlidingStart;
/// <summary>
/// Occurs when sliding ends.
/// </summary>
public event Action SlidingEnd;
/// <summary>
/// Gets or sets the slider speed. Use value 0 to disable and hide slider UI.
/// </summary>
public float SlideSpeed
{
get => _slideSpeed;
set => _slideSpeed = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ValueBox{T}"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="width">The width.</param>
/// <param name="min">The minimum.</param>
/// <param name="max">The maximum.</param>
/// <param name="sliderSpeed">The slider speed.</param>
protected ValueBox(T value, float x, float y, float width, T min, T max, float sliderSpeed)
: base(false, x, y, width)
{
_value = value;
_min = min;
_max = max;
_slideSpeed = sliderSpeed;
}
/// <summary>
/// Updates the text of the textbox.
/// </summary>
protected abstract void UpdateText();
/// <summary>
/// Tries the get value from the textbox text.
/// </summary>
protected abstract void TryGetValue();
/// <summary>
/// Applies the sliding delta to the value.
/// </summary>
/// <param name="delta">The delta (scaled).</param>
protected abstract void ApplySliding(float delta);
/// <summary>
/// Called when value gets changed.
/// </summary>
protected virtual void OnValueChanged()
{
ValueChanged?.Invoke();
BoxValueChanged?.Invoke(this);
}
/// <summary>
/// Gets a value indicating whether this value box can use sliding.
/// </summary>
protected virtual bool CanUseSliding => _slideSpeed > Mathf.Epsilon;
/// <summary>
/// Gets the slide rectangle.
/// </summary>
protected virtual Rectangle SlideRect
{
get
{
float x = Width - SlidingBoxSize - 1.0f;
float y = (Height - SlidingBoxSize) * 0.5f;
return new Rectangle(x, y, SlidingBoxSize, SlidingBoxSize);
}
}
private void EndSliding()
{
_isSliding = false;
EndMouseCapture();
SlidingEnd?.Invoke();
}
/// <inheritdoc />
public override void Draw()
{
base.Draw();
if (CanUseSliding)
{
var style = Style.Current;
// Draw sliding UI
Render2D.DrawSprite(style.Scale, SlideRect, style.Foreground);
// Check if is sliding
if (_isSliding)
{
// Draw overlay
// TODO: render nicer overlay with some glow from the borders (inside)
Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), Color.Orange * 0.3f);
}
}
}
/// <inheritdoc />
public override void OnLostFocus()
{
// Check if was sliding
if (_isSliding)
{
EndSliding();
base.OnLostFocus();
}
else
{
base.OnLostFocus();
// Update
UpdateText();
}
ResetViewOffset();
}
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
if (button == MouseButton.Left && CanUseSliding && SlideRect.Contains(location))
{
// Start sliding
_isSliding = true;
_startSlideLocation = location;
_startSlideValue = _value;
StartMouseCapture(true);
SlidingStart?.Invoke();
return true;
}
return base.OnMouseDown(location, button);
}
/// <inheritdoc />
public override void OnMouseMove(Vector2 location)
{
if (_isSliding)
{
// Update sliding
Vector2 slideLocation = location + Root.TrackingMouseOffset;
ApplySliding(Mathf.RoundToInt(slideLocation.X - _startSlideLocation.X) * _slideSpeed);
}
else
{
base.OnMouseMove(location);
}
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
if (button == MouseButton.Left && _isSliding)
{
// End sliding
EndSliding();
return true;
}
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
protected override void OnEditEnd()
{
// Update value
TryGetValue();
base.OnEditEnd();
}
/// <inheritdoc />
public override void OnEndMouseCapture()
{
// Check if was sliding
if (_isSliding)
{
EndSliding();
}
else
{
base.OnEndMouseCapture();
}
}
/// <inheritdoc />
protected override Rectangle TextRectangle
{
get
{
var result = base.TextRectangle;
if (CanUseSliding)
{
result.Size.X -= SlidingBoxSize;
}
return result;
}
}
/// <inheritdoc />
protected override Rectangle TextClipRectangle
{
get
{
var result = base.TextRectangle;
if (CanUseSliding)
{
result.Size.X -= SlidingBoxSize;
}
return result;
}
}
}
}