2019-08-12 19:44:16 +02:00
|
|
|
using System;
|
2019-08-21 17:00:22 +02:00
|
|
|
using System.Linq;
|
2019-08-12 19:44:16 +02:00
|
|
|
using Microsoft.Xna.Framework;
|
|
|
|
using Microsoft.Xna.Framework.Graphics;
|
2019-08-31 19:32:22 +02:00
|
|
|
using Microsoft.Xna.Framework.Input.Touch;
|
2019-08-12 19:44:16 +02:00
|
|
|
using MLEM.Extensions;
|
2019-08-13 16:02:29 +02:00
|
|
|
using MLEM.Input;
|
2019-11-02 14:53:59 +01:00
|
|
|
using MLEM.Misc;
|
2019-08-12 19:44:16 +02:00
|
|
|
using MLEM.Textures;
|
|
|
|
using MLEM.Ui.Style;
|
|
|
|
|
|
|
|
namespace MLEM.Ui.Elements {
|
|
|
|
public class ScrollBar : Element {
|
|
|
|
|
2019-10-14 21:28:12 +02:00
|
|
|
public StyleProp<NinePatch> Background;
|
|
|
|
public StyleProp<NinePatch> ScrollerTexture;
|
2019-11-02 14:53:59 +01:00
|
|
|
public Vector2 ScrollerOffset;
|
|
|
|
public Vector2 ScrollerSize;
|
2019-08-12 19:44:16 +02:00
|
|
|
private float maxValue;
|
|
|
|
public float MaxValue {
|
|
|
|
get => this.maxValue;
|
|
|
|
set {
|
|
|
|
this.maxValue = Math.Max(0, value);
|
|
|
|
// force current value to be clamped
|
|
|
|
this.CurrentValue = this.currValue;
|
2019-12-14 14:00:12 +01:00
|
|
|
if (this.AutoHideWhenEmpty && this.IsHidden != this.maxValue <= 0) {
|
2019-09-01 19:53:52 +02:00
|
|
|
this.IsHidden = this.maxValue <= 0;
|
2019-12-14 14:00:12 +01:00
|
|
|
this.OnAutoHide?.Invoke(this);
|
|
|
|
}
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
private float currValue;
|
|
|
|
public float CurrentValue {
|
|
|
|
get => this.currValue;
|
|
|
|
set {
|
|
|
|
var val = MathHelper.Clamp(value, 0, this.maxValue);
|
|
|
|
if (this.currValue != val) {
|
|
|
|
this.currValue = val;
|
|
|
|
this.OnValueChanged?.Invoke(this, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-16 19:08:36 +02:00
|
|
|
public readonly bool Horizontal;
|
2019-08-12 19:44:16 +02:00
|
|
|
public float StepPerScroll = 1;
|
|
|
|
public ValueChanged OnValueChanged;
|
2019-12-14 14:00:12 +01:00
|
|
|
public GenericCallback OnAutoHide;
|
2019-08-13 16:02:29 +02:00
|
|
|
private bool isMouseHeld;
|
2019-08-31 19:32:22 +02:00
|
|
|
private bool isDragging;
|
|
|
|
private bool isTouchHeld;
|
2019-09-01 19:53:52 +02:00
|
|
|
public bool AutoHideWhenEmpty;
|
2019-08-31 19:32:22 +02:00
|
|
|
|
|
|
|
static ScrollBar() {
|
|
|
|
InputHandler.EnableGestures(GestureType.HorizontalDrag, GestureType.VerticalDrag);
|
|
|
|
}
|
2019-08-12 19:44:16 +02:00
|
|
|
|
2019-08-16 19:08:36 +02:00
|
|
|
public ScrollBar(Anchor anchor, Vector2 size, int scrollerSize, float maxValue, bool horizontal = false) : base(anchor, size) {
|
2019-08-12 19:44:16 +02:00
|
|
|
this.maxValue = maxValue;
|
2019-08-16 19:08:36 +02:00
|
|
|
this.Horizontal = horizontal;
|
2019-11-02 14:53:59 +01:00
|
|
|
this.ScrollerSize = new Vector2(horizontal ? scrollerSize : size.X, !horizontal ? scrollerSize : size.Y);
|
2019-08-28 18:27:17 +02:00
|
|
|
this.CanBeSelected = false;
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void Update(GameTime time) {
|
|
|
|
base.Update(time);
|
2019-08-31 19:32:22 +02:00
|
|
|
|
|
|
|
// MOUSE INPUT
|
2019-08-28 18:27:17 +02:00
|
|
|
var moused = this.Controls.MousedElement;
|
2019-09-02 21:11:05 +02:00
|
|
|
if (moused == this && this.Controls.Input.IsMouseButtonPressed(MouseButton.Left)) {
|
2019-08-13 16:02:29 +02:00
|
|
|
this.isMouseHeld = true;
|
2019-08-28 18:27:17 +02:00
|
|
|
} else if (this.isMouseHeld && !this.Controls.Input.IsMouseButtonDown(MouseButton.Left)) {
|
2019-08-13 16:02:29 +02:00
|
|
|
this.isMouseHeld = false;
|
|
|
|
}
|
2019-08-31 19:32:22 +02:00
|
|
|
if (this.isMouseHeld)
|
2019-09-02 19:55:26 +02:00
|
|
|
this.ScrollToPos(this.Input.MousePosition.Transform(this.Root.InvTransform));
|
2019-08-31 19:32:22 +02:00
|
|
|
if (!this.Horizontal && moused != null && (moused == this.Parent || moused.GetParentTree().Contains(this.Parent))) {
|
2020-02-06 01:51:41 +01:00
|
|
|
var scroll = this.Input.LastScrollWheel - this.Input.ScrollWheel;
|
2019-08-31 19:32:22 +02:00
|
|
|
if (scroll != 0)
|
|
|
|
this.CurrentValue += this.StepPerScroll * Math.Sign(scroll);
|
|
|
|
}
|
2019-08-16 19:08:36 +02:00
|
|
|
|
2019-08-31 19:32:22 +02:00
|
|
|
// TOUCH INPUT
|
|
|
|
if (!this.Horizontal) {
|
|
|
|
// are we dragging on top of the panel?
|
|
|
|
if (this.Input.GetGesture(GestureType.VerticalDrag, out var drag)) {
|
|
|
|
// if the element under the drag's start position is on top of the panel, start dragging
|
2019-11-02 14:53:59 +01:00
|
|
|
var touched = this.Parent.GetElementUnderPos(Vector2.Transform(drag.Position, this.Root.InvTransform));
|
2019-08-31 19:32:22 +02:00
|
|
|
if (touched != null && touched != this)
|
|
|
|
this.isDragging = true;
|
|
|
|
|
|
|
|
// if we're dragging at all, then move the scroller
|
|
|
|
if (this.isDragging)
|
|
|
|
this.CurrentValue -= drag.Delta.Y / this.Scale;
|
2019-08-16 19:08:36 +02:00
|
|
|
} else {
|
2019-08-31 19:32:22 +02:00
|
|
|
this.isDragging = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this.Input.TouchState.Count <= 0) {
|
|
|
|
// if no touch has occured this tick, then reset the variable
|
|
|
|
this.isTouchHeld = false;
|
|
|
|
} else {
|
|
|
|
foreach (var loc in this.Input.TouchState) {
|
2019-09-02 19:55:26 +02:00
|
|
|
var pos = Vector2.Transform(loc.Position, this.Root.InvTransform);
|
2019-08-31 19:32:22 +02:00
|
|
|
// if we just started touching and are on top of the scroller, then we should start scrolling
|
2019-09-02 19:55:26 +02:00
|
|
|
if (this.DisplayArea.Contains(pos) && !loc.TryGetPreviousLocation(out _)) {
|
2019-08-31 19:32:22 +02:00
|
|
|
this.isTouchHeld = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// scroll no matter if we're on the scroller right now
|
|
|
|
if (this.isTouchHeld)
|
2019-09-02 19:55:26 +02:00
|
|
|
this.ScrollToPos(pos.ToPoint());
|
2019-08-16 19:08:36 +02:00
|
|
|
}
|
2019-08-13 16:02:29 +02:00
|
|
|
}
|
2019-08-31 19:32:22 +02:00
|
|
|
}
|
2019-08-16 19:08:36 +02:00
|
|
|
|
2019-08-31 19:32:22 +02:00
|
|
|
private void ScrollToPos(Point position) {
|
|
|
|
if (this.Horizontal) {
|
|
|
|
var internalX = position.X - this.Area.X - this.ScrollerSize.X * this.Scale / 2;
|
|
|
|
this.CurrentValue = internalX / (this.Area.Width - this.ScrollerSize.X * this.Scale) * this.MaxValue;
|
|
|
|
} else {
|
|
|
|
var internalY = position.Y - this.Area.Y - this.ScrollerSize.Y * this.Scale / 2;
|
|
|
|
this.CurrentValue = internalY / (this.Area.Height - this.ScrollerSize.Y * this.Scale) * this.MaxValue;
|
2019-08-12 19:46:43 +02:00
|
|
|
}
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
|
|
|
|
2019-09-20 13:22:05 +02:00
|
|
|
public override void Draw(GameTime time, SpriteBatch batch, float alpha, BlendState blendState, SamplerState samplerState, Matrix matrix) {
|
2019-09-04 17:19:31 +02:00
|
|
|
batch.Draw(this.Background, this.DisplayArea, Color.White * alpha, this.Scale);
|
2019-08-12 19:44:16 +02:00
|
|
|
|
2019-12-26 12:35:47 +01:00
|
|
|
if (this.MaxValue > 0) {
|
|
|
|
var scrollerPos = new Vector2(this.DisplayArea.X + this.ScrollerOffset.X, this.DisplayArea.Y + this.ScrollerOffset.Y);
|
|
|
|
var scrollerOffset = new Vector2(
|
|
|
|
!this.Horizontal ? 0 : this.currValue / this.maxValue * (this.DisplayArea.Width - this.ScrollerSize.X * this.Scale),
|
|
|
|
this.Horizontal ? 0 : this.currValue / this.maxValue * (this.DisplayArea.Height - this.ScrollerSize.Y * this.Scale));
|
|
|
|
var scrollerRect = new RectangleF(scrollerPos + scrollerOffset, this.ScrollerSize * this.Scale);
|
|
|
|
batch.Draw(this.ScrollerTexture, scrollerRect, Color.White * alpha, this.Scale);
|
|
|
|
}
|
2019-09-20 13:22:05 +02:00
|
|
|
base.Draw(time, batch, alpha, blendState, samplerState, matrix);
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override void InitStyle(UiStyle style) {
|
|
|
|
base.InitStyle(style);
|
2019-10-14 21:28:12 +02:00
|
|
|
this.Background.SetFromStyle(style.ScrollBarBackground);
|
|
|
|
this.ScrollerTexture.SetFromStyle(style.ScrollBarScrollerTexture);
|
2019-08-12 19:44:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public delegate void ValueChanged(Element element, float value);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|