Skip to content

Commit

Permalink
Use our own custom Splitter control
Browse files Browse the repository at this point in the history
  • Loading branch information
wixoaGit committed Jun 8, 2024
1 parent 33cb723 commit d1e6dec
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 66 deletions.
1 change: 1 addition & 0 deletions OpenDream.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestGame", "TestGame", "{72
TestGame\map_z1.dmm = TestGame\map_z1.dmm
TestGame\map_z2.dmm = TestGame\map_z2.dmm
TestGame\map_z3.dmm = TestGame\map_z3.dmm
TestGame\TestInterface.dmf = TestGame\TestInterface.dmf
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "icons", "icons", "{4F1F4C68-4117-4ECC-8174-D399C1D6C409}"
Expand Down
1 change: 1 addition & 0 deletions OpenDreamClient/Input/MouseInputSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using OpenDreamClient.Input.ContextMenu;
using OpenDreamClient.Interface;
using OpenDreamClient.Interface.Controls;
using OpenDreamClient.Interface.Controls.UI;
using OpenDreamClient.Rendering;
using OpenDreamShared.Dream;
using OpenDreamShared.Input;
Expand Down
70 changes: 10 additions & 60 deletions OpenDreamClient/Interface/Controls/ControlChild.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
using System.Diagnostics.CodeAnalysis;
using OpenDreamClient.Interface.Controls.UI;
using OpenDreamClient.Interface.Descriptors;
using OpenDreamClient.Interface.DMF;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;

namespace OpenDreamClient.Interface.Controls;

// todo: robust needs GridSplitter.
// and a non-shit grid control.
internal sealed class ControlChild : InterfaceControl {
internal sealed class ControlChild(ControlDescriptor controlDescriptor, ControlWindow window) : InterfaceControl(controlDescriptor, window) {
private ControlDescriptorChild ChildDescriptor => (ControlDescriptorChild)ElementDescriptor;

private SplitContainer _grid;
private Control? _leftElement, _rightElement;

public ControlChild(ControlDescriptor controlDescriptor, ControlWindow window) : base(controlDescriptor, window) { }
private Splitter _splitter;

Check warning

Code scanning / InspectCode

Non-nullable member is uninitialized. Warning

Non-nullable field '_splitter' is uninitialized. Consider declaring the field as nullable.

protected override Control CreateUIElement() {
_grid = new SplitContainer();
_splitter = new Splitter();

return _grid;
return _splitter;
}

protected override void UpdateElementDescriptor() {
Expand All @@ -32,44 +27,10 @@ protected override void UpdateElementDescriptor() {
? rightWindow.UIElement
: null;

if (newLeftElement != _leftElement || _grid.ChildCount == 1) {
if (_leftElement != null)
_grid.Children.Remove(_leftElement);

if (newLeftElement != null) {
_leftElement = newLeftElement;
_leftElement.HorizontalExpand = true;
_leftElement.VerticalExpand = true;
} else {
// SplitContainer will have a size of 0x0 if there aren't 2 controls
_leftElement = new Control();
}

_grid.Children.Add(_leftElement);
}

if (newRightElement != _rightElement || _grid.ChildCount == 2) {
if (_rightElement != null)
_grid.Children.Remove(_rightElement);

if (newRightElement != null) {
_rightElement = newRightElement;
_rightElement.HorizontalExpand = true;
_rightElement.VerticalExpand = true;
} else {
// SplitContainer will have a size of 0x0 if there aren't 2 controls
_rightElement = new Control();
}

_grid.Children.Add(_rightElement);
}

if(_leftElement is not null)
_leftElement.SetPositionInParent(0);
if (_rightElement is not null)
_rightElement.SetPositionInParent(1);

UpdateGrid();
_splitter.Left = newLeftElement;
_splitter.Right = newRightElement;
_splitter.Vertical = ChildDescriptor.IsVert.Value;
_splitter.SplitterPercentage = ChildDescriptor.Splitter.Value / 100f;
}

public override void Shutdown() {
Expand All @@ -79,21 +40,10 @@ public override void Shutdown() {
right.Shutdown();
}

private void UpdateGrid() {
_grid.Orientation = ChildDescriptor.IsVert.Value
? SplitContainer.SplitOrientation.Horizontal
: SplitContainer.SplitOrientation.Vertical;

if (_grid.Size == Vector2.Zero)
return;

_grid.SplitFraction = ChildDescriptor.Splitter.Value / 100f;
}

public override bool TryGetProperty(string property, [NotNullWhen(true)] out IDMFProperty? value) {
switch (property) {
case "splitter":
value = new DMFPropertyNum(_grid.SplitFraction * 100);
value = new DMFPropertyNum(_splitter.SplitterPercentage * 100);
return true;
default:
return base.TryGetProperty(property, out value);
Expand Down
1 change: 1 addition & 0 deletions OpenDreamClient/Interface/Controls/ControlInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Linq;
using OpenDreamShared.Network.Messages;
using OpenDreamClient.Input;
using OpenDreamClient.Interface.Controls.UI;
using OpenDreamClient.Interface.Descriptors;
using OpenDreamClient.Interface.Html;
using OpenDreamShared.Dream;
Expand Down
1 change: 1 addition & 0 deletions OpenDreamClient/Interface/Controls/ControlMap.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OpenDreamClient.Input;
using OpenDreamClient.Interface.Controls.UI;
using OpenDreamClient.Interface.Descriptors;
using OpenDreamShared.Dream;
using Robust.Client.Graphics;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
using Robust.Shared.Utility;
using SixLabors.ImageSharp.PixelFormats;

#nullable enable

namespace OpenDreamClient.Interface.Controls;
namespace OpenDreamClient.Interface.Controls.UI;

/// <summary>
/// Viewport control that has a fixed viewport size and scales it appropriately.
Expand Down
195 changes: 195 additions & 0 deletions OpenDreamClient/Interface/Controls/UI/Splitter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;

namespace OpenDreamClient.Interface.Controls.UI;

/// <summary>
/// A splitter control that gives 2 children a resizable amount of space.
/// Equivalent to BYOND's CHILD control.
/// </summary>
/// <remarks>Do not add children directly! Use <see cref="Left"/> and <see cref="Right"/>.</remarks>
public sealed class Splitter : Container {
public float SplitterWidth {
get => _splitterWidth;
set {
_splitterWidth = value;
InvalidateMeasure();
}
}

public Control? Left {
get => _left;
set {
if (_left == value)
return;

if (_left != null)
RemoveChild(_left);

_left = value;
if (_left != null)
AddChild(_left);
}
}

public Control? Right {
get => _right;
set {
if (_right == value)
return;

if (_right != null)
RemoveChild(_right);

_right = value;
if (_right != null)
AddChild(_right);
}
}

public float SplitterPercentage {
get => _splitterPercentage;
set {
_splitterPercentage = Math.Clamp(value, 0.1f, 0.9f);
InvalidateMeasure();
}
}

public bool Vertical {
get => _vertical;
set {
_vertical = value;
_drag.DefaultCursorShape = value ? CursorShape.HResize : CursorShape.VResize;
InvalidateMeasure();
}
}

private readonly DragControl _drag = new();

private float _splitterWidth = 5f;
private float _splitterPercentage = 0.5f;
private bool _vertical;
private bool _dragging;
private Control? _left, _right;

public Splitter() {
MouseFilter = MouseFilterMode.Stop;

_drag.OnMouseMove += MouseMove;
_drag.OnMouseDown += StartDragging;
_drag.OnMouseUp += StopDragging;
AddChild(_drag);
}

protected override Vector2 MeasureOverride(Vector2 availableSize) {
var space = CalculateSpace(availableSize);

_left?.Measure(space.LeftBox.Size);
_drag.Measure(space.DragBox.Size);
_right?.Measure(space.RightBox.Size);

// Always take the full space
return availableSize;
}

protected override Vector2 ArrangeOverride(Vector2 finalSize) {
var space = CalculateSpace(finalSize);

_left?.Arrange(space.LeftBox);
_drag.Arrange(space.DragBox);
_right?.Arrange(space.RightBox);

// Always take the full space
return finalSize;
}

protected override void MouseMove(GUIMouseMoveEventArgs args) {
if (!_dragging)
return;

var relative = args.GlobalPosition - GlobalPosition;

if (Vertical) {
SplitterPercentage = relative.X / Size.X;
} else {
SplitterPercentage = relative.Y / Size.Y;
}
}

private void StartDragging(GUIBoundKeyEventArgs args) {
if (_dragging)
return;

_dragging = true;
DefaultCursorShape = Vertical ? CursorShape.HResize : CursorShape.VResize;
}

private void StopDragging(GUIBoundKeyEventArgs args) {
_dragging = false;
DefaultCursorShape = CursorShape.Arrow;
}

private (UIBox2 LeftBox, UIBox2 DragBox, UIBox2 RightBox) CalculateSpace(Vector2 available) {
if (_left != null && _right == null)
return (UIBox2.FromDimensions(Vector2.Zero, available), default, default);
if (_left == null && _right != null)
return (default, default, UIBox2.FromDimensions(Vector2.Zero, available));
if (_left == null && _right == null)
return (default, default, default);

var leftSize = Vertical
? available with { X = available.X * SplitterPercentage - SplitterWidth/2 }
: available with { Y = available.Y * SplitterPercentage - SplitterWidth/2 };
var rightSize = Vertical
? available with { X = available.X * (1f - SplitterPercentage) - SplitterWidth/2 }
: available with { Y = available.Y * (1f - SplitterPercentage) - SplitterWidth/2 };
var dragSize = Vertical
? available with { X = SplitterWidth }
: available with { Y = SplitterWidth };
var dragPos = Vertical
? leftSize with { Y = 0f }
: leftSize with { X = 0f };
var rightPos = Vertical
? new Vector2(leftSize.X + SplitterWidth, 0f)
: new Vector2(0f, leftSize.Y + SplitterWidth);

return (
UIBox2.FromDimensions(Vector2.Zero, leftSize),
UIBox2.FromDimensions(dragPos, dragSize),
UIBox2.FromDimensions(rightPos, rightSize)
);
}

private sealed class DragControl : Control {
public event Action<GUIBoundKeyEventArgs>? OnMouseDown;
public event Action<GUIBoundKeyEventArgs>? OnMouseUp;
public event Action<GUIMouseMoveEventArgs>? OnMouseMove;

public DragControl() {
MouseFilter = MouseFilterMode.Stop;
}

protected override void MouseMove(GUIMouseMoveEventArgs args) {
base.MouseMove(args);
OnMouseMove?.Invoke(args);
}

protected override void KeyBindDown(GUIBoundKeyEventArgs args) {
base.KeyBindDown(args);
if (args.Function == EngineKeyFunctions.UIClick)
OnMouseDown?.Invoke(args);
}

protected override void KeyBindUp(GUIBoundKeyEventArgs args) {
base.KeyBindUp(args);
if (args.Function == EngineKeyFunctions.UIClick)
OnMouseUp?.Invoke(args);
}

protected override void Draw(DrawingHandleScreen handle) {
handle.DrawRect(UIBox2.FromDimensions(Vector2.Zero, PixelSize), Color.Gray, filled: false);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Robust.Client.UserInterface.Controls;

namespace OpenDreamClient.Interface.Controls;
namespace OpenDreamClient.Interface.Controls.UI;

/// <summary>
/// The control responsible for sizing & layout of verb panels in INFO controls
Expand Down
2 changes: 1 addition & 1 deletion Resources/OpenDream/DefaultInterface.dmf
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ window "mainwindow"
icon = "icons/mob.dmi"
elem "split"
type = CHILD
pos = 3,0
pos = 0,0
size = 0x0
anchor1 = 0,0
anchor2 = 100,100
Expand Down
2 changes: 1 addition & 1 deletion TestGame/TestInterface.dmf
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ window "mainwindow"
icon = "icons/mob.dmi"
elem "split"
type = CHILD
pos = 3,0
pos = 0,0
size = 0x0
anchor1 = 0,0
anchor2 = 100,100
Expand Down

0 comments on commit d1e6dec

Please sign in to comment.