using Barotrauma.Extensions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System; namespace Barotrauma { public class GUIScrollBar : GUIComponent { public static GUIScrollBar draggingBar; private bool isHorizontal; public GUIFrame Frame { get; private set; } public GUIButton Bar { get; private set; } private float barSize; private float barScroll; private float step; public delegate bool OnMovedHandler(GUIScrollBar scrollBar, float barScroll); public OnMovedHandler OnMoved; public OnMovedHandler OnReleased; public bool IsBooleanSwitch; public override string ToolTip { get { return base.ToolTip; } set { base.ToolTip = value; Frame.ToolTip = value; Bar.ToolTip = value; } } private float minValue; public float MinValue { get { return minValue; } set { minValue = MathHelper.Clamp(value, 0.0f, 1.0f); BarScroll = Math.Max(minValue, barScroll); } } private float maxValue = 1.0f; public float MaxValue { get { return maxValue; } set { maxValue = MathHelper.Clamp(value, 0.0f, 1.0f); BarScroll = Math.Min(maxValue, barScroll); } } public bool IsHorizontal { get { return isHorizontal; } /*set { if (isHorizontal == value) return; isHorizontal = value; UpdateRect(); }*/ } public override bool Enabled { get { return enabled; } set { enabled = value; Bar.Enabled = value; if (!enabled) Bar.Selected = false; } } public Vector4 Padding { get { if (Frame?.Style == null) return Vector4.Zero; return Frame.Style.Padding; } } private Vector2 range; public Vector2 Range { get { return range; } set { float oldBarScrollValue = BarScrollValue; range = value; BarScrollValue = oldBarScrollValue; } } public delegate float ScrollConversion(GUIScrollBar scrollBar, float f); public ScrollConversion ScrollToValue = null; public ScrollConversion ValueToScroll = null; public float BarScrollValue { get { if (ScrollToValue==null) return (BarScroll * (Range.Y - Range.X)) + Range.X; return ScrollToValue(this, BarScroll); } set { if (ValueToScroll==null) BarScroll = (value - Range.X) / (Range.Y - Range.X); else BarScroll = ValueToScroll(this, value); } } public float BarScroll { get { return step == 0.0f ? barScroll : MathUtils.RoundTowardsClosest(barScroll, step); } set { if (float.IsNaN(value)) { return; } barScroll = MathHelper.Clamp(value, minValue, maxValue); int newX = Bar.RectTransform.AbsoluteOffset.X; int newY = Bar.RectTransform.AbsoluteOffset.Y; float newScroll = step == 0.0f ? barScroll : MathUtils.RoundTowardsClosest(barScroll, step); if (isHorizontal) { newX = (int)(Padding.X + newScroll * (Frame.Rect.Width - Bar.Rect.Width - Padding.X - Padding.Z)); newX = MathHelper.Clamp(newX, (int)Padding.X, Frame.Rect.Width - Bar.Rect.Width - (int)Padding.Z); } else { newY = (int)(Padding.Y + newScroll * (Frame.Rect.Height - Bar.Rect.Height - Padding.Y - Padding.W)); newY = MathHelper.Clamp(newY, (int)Padding.Y, Frame.Rect.Height - Bar.Rect.Height - (int)Padding.W); } Bar.RectTransform.AbsoluteOffset = new Point(newX, newY); } } public float Step { get { return step; } set { step = MathHelper.Clamp(value, 0.0f, 1.0f); } } public float BarSize { get { return barSize; } set { barSize = Math.Min(Math.Max(value, 0.0f), 1.0f); UpdateRect(); } } public GUIScrollBar(RectTransform rectT, float barSize = 1, Color? color = null, string style = "", bool? isHorizontal = null) : base(style, rectT) { this.isHorizontal = isHorizontal ?? (Rect.Width > Rect.Height); Frame = new GUIFrame(new RectTransform(Vector2.One, rectT)); GUI.Style.Apply(Frame, IsHorizontal ? "GUIFrameHorizontal" : "GUIFrameVertical", this); this.barSize = barSize; Bar = new GUIButton(new RectTransform(Vector2.One, rectT, IsHorizontal ? Anchor.CenterLeft : Anchor.TopCenter), color: color); GUI.Style.Apply(Bar, IsHorizontal ? "GUIButtonHorizontal" : "GUIButtonVertical", this); Bar.OnPressed = SelectBar; enabled = true; UpdateRect(); BarScroll = 0.0f; rectT.SizeChanged += UpdateRect; rectT.ScaleChanged += UpdateRect; Bar.RectTransform.SizeChanged += () => { BarScroll = barScroll; }; } private void UpdateRect() { Vector4 padding = Frame.Style.Padding; var newSize = new Point((int)(Rect.Size.X - padding.X - padding.Z), (int)(Rect.Size.Y - padding.Y - padding.W)); newSize = IsHorizontal ? newSize.Multiply(new Vector2(BarSize, 1)) : newSize.Multiply(new Vector2(1, BarSize)); Bar.RectTransform.Resize(newSize); BarScroll = barScroll; } protected override void Update(float deltaTime) { if (!Visible) return; base.Update(deltaTime); if (!enabled) return; if (IsBooleanSwitch && (!PlayerInput.LeftButtonHeld() || (GUI.MouseOn != this && !IsParentOf(GUI.MouseOn)))) { int dir = Math.Sign(barScroll - (minValue + maxValue) / 2.0f); if (dir == 0) dir = 1; if ((barScroll <= maxValue && dir > 0) || (barScroll > minValue && dir < 0)) { BarScroll += dir * 0.1f; } } if (draggingBar == this) { if (!PlayerInput.LeftButtonHeld()) { OnReleased?.Invoke(this, BarScroll); draggingBar = null; } if ((isHorizontal && PlayerInput.MousePosition.X > Rect.X && PlayerInput.MousePosition.X < Rect.Right) || (!isHorizontal && PlayerInput.MousePosition.Y > Rect.Y && PlayerInput.MousePosition.Y < Rect.Bottom)) { MoveButton(PlayerInput.MouseSpeed); } } else if (GUI.MouseOn == Frame) { if (PlayerInput.LeftButtonClicked()) { draggingBar?.OnReleased?.Invoke(draggingBar, draggingBar.BarScroll); MoveButton(new Vector2( Math.Sign(PlayerInput.MousePosition.X - Bar.Rect.Center.X) * Bar.Rect.Width, Math.Sign(PlayerInput.MousePosition.Y - Bar.Rect.Center.Y) * Bar.Rect.Height)); } } } private bool SelectBar() { if (!enabled) return false; // This doesn't work if (barSize == 1.0f) return false; draggingBar = this; return true; } public void MoveButton(Vector2 moveAmount) { float newScroll = barScroll; if (isHorizontal) { moveAmount.Y = 0.0f; newScroll += moveAmount.X / (Frame.Rect.Width - Bar.Rect.Width - Padding.X - Padding.Z); } else { moveAmount.X = 0.0f; newScroll += moveAmount.Y / (Frame.Rect.Height - Bar.Rect.Height - Padding.Y - Padding.W); } BarScroll = newScroll; if (moveAmount != Vector2.Zero && OnMoved != null) OnMoved(this, BarScroll); } } }