mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-06-09 10:06:34 +02:00
Basic ItemsControl
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Robust.Client.Console.Commands;
|
||||
|
||||
internal sealed partial class UITestControl
|
||||
{
|
||||
private sealed class TabItemsControl : Control
|
||||
{
|
||||
private readonly Random _random = new();
|
||||
private readonly ObservableCollection<int> _collection = [];
|
||||
|
||||
private int _counter = 0;
|
||||
|
||||
public TabItemsControl()
|
||||
{
|
||||
var addButton = new Button
|
||||
{
|
||||
Text = "Add Item",
|
||||
};
|
||||
addButton.OnPressed += AddButtonOnOnPressed;
|
||||
|
||||
var removeButton = new Button
|
||||
{
|
||||
Text = "Remove Item"
|
||||
};
|
||||
removeButton.OnPressed += RemoveButtonOnOnChildAdded;
|
||||
|
||||
var replaceButton = new Button
|
||||
{
|
||||
Text = "Replace Item"
|
||||
};
|
||||
replaceButton.OnPressed += ReplaceButtonOnOnPressed;
|
||||
|
||||
var moveButton = new Button
|
||||
{
|
||||
Text = "Move Item"
|
||||
};
|
||||
moveButton.OnPressed += MoveButtonOnOnPressed;
|
||||
|
||||
var resetButton = new Button
|
||||
{
|
||||
Text = "Reset Item"
|
||||
};
|
||||
resetButton.OnPressed += ResetButtonOnOnPressed;
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
addButton,
|
||||
removeButton,
|
||||
replaceButton,
|
||||
moveButton,
|
||||
resetButton
|
||||
}
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
Children =
|
||||
{
|
||||
new ItemsControl
|
||||
{
|
||||
ItemsSource = _collection,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void AddButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var value = _counter++;
|
||||
var index = _random.Next(_collection.Count + 1);
|
||||
Log.Info($"Inserted {value} at {index}");
|
||||
_collection.Insert(index, value);
|
||||
}
|
||||
|
||||
private void RemoveButtonOnOnChildAdded(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_collection.Count == 0)
|
||||
return;
|
||||
|
||||
var index = _random.Next(_collection.Count);
|
||||
Log.Info($"Removed {index} ({_collection[index]})");
|
||||
_collection.RemoveAt(index);
|
||||
}
|
||||
|
||||
private void ReplaceButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_collection.Count == 0)
|
||||
return;
|
||||
|
||||
var index = _random.Next(_collection.Count);
|
||||
var value = _counter++;
|
||||
|
||||
Log.Info($"Replaced {index} ({_collection[index]}) with {value}");
|
||||
_collection[index] = value;
|
||||
}
|
||||
|
||||
private void MoveButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
if (_collection.Count == 0)
|
||||
return;
|
||||
|
||||
var oldIndex = _random.Next(_collection.Count);
|
||||
var newIndex = _random.Next(_collection.Count);
|
||||
|
||||
Log.Info($"Moved {oldIndex} ({_collection[oldIndex]}) -> {newIndex}");
|
||||
_collection.Move(oldIndex, newIndex);
|
||||
}
|
||||
|
||||
private void ResetButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
Log.Info("Reset list");
|
||||
_collection.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,7 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
_tabContainer.AddChild(TabCursorShapes());
|
||||
_tabContainer.AddChild(new TabWrapContainer { Name = nameof(Tab.WrapContainer) });
|
||||
_tabContainer.AddChild(new TabOkLab());
|
||||
_tabContainer.AddChild(new TabItemsControl());
|
||||
}
|
||||
|
||||
public void OnClosed()
|
||||
@@ -283,6 +284,8 @@ Suspendisse hendrerit blandit urna ut laoreet. Suspendisse ac elit at erat males
|
||||
SpriteView = 8,
|
||||
TabCursorShapes = 9,
|
||||
WrapContainer = 10,
|
||||
OkLab = 11,
|
||||
ItemsControl = 12,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
public interface IControlTemplate
|
||||
{
|
||||
Control Instantiate(object? data);
|
||||
}
|
||||
|
||||
public sealed class ControlTemplateDelegate(Func<object?, Control> @delegate) : IControlTemplate
|
||||
{
|
||||
public Control Instantiate(object? data)
|
||||
{
|
||||
return @delegate(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
public interface IVirtualizingContainer
|
||||
{
|
||||
void SetParent(IVirtualizingContainerParent parent);
|
||||
void ClearParent();
|
||||
}
|
||||
|
||||
public interface IVirtualizingContainerParent
|
||||
{
|
||||
int ItemCount { get; }
|
||||
Control CreateControl(object? item);
|
||||
event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
[Virtual]
|
||||
public class ItemsControl : Control, IVirtualizingContainerParent
|
||||
{
|
||||
private static readonly NotifyCollectionChangedEventArgs NotifyReset = new(NotifyCollectionChangedAction.Reset);
|
||||
|
||||
private IList _items = Array.Empty<object>();
|
||||
private Control _panelControl = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||
private NotifyCollectionChangedEventHandler? _collectionChanged;
|
||||
private IControlTemplate? _itemTemplate;
|
||||
|
||||
public Control PanelControl
|
||||
{
|
||||
get => _panelControl;
|
||||
set
|
||||
{
|
||||
if (_panelControl == value)
|
||||
return;
|
||||
|
||||
if (value is { Parent: not null })
|
||||
throw new ArgumentException("Assigned panel must not be attached to a control.");
|
||||
|
||||
{
|
||||
_panelControl.Orphan();
|
||||
if (_panelControl is IVirtualizingContainer virt)
|
||||
virt.ClearParent();
|
||||
}
|
||||
|
||||
_panelControl = value;
|
||||
|
||||
{
|
||||
AddChild(_panelControl);
|
||||
if (_panelControl is IVirtualizingContainer virt)
|
||||
{
|
||||
virt.SetParent(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Trigger rebuild logic on new control.
|
||||
ItemsOnCollectionChanged(this, NotifyReset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IControlTemplate? ItemTemplate
|
||||
{
|
||||
get => _itemTemplate;
|
||||
set => _itemTemplate = value;
|
||||
}
|
||||
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
set
|
||||
{
|
||||
if (_items is INotifyCollectionChanged notifyOld)
|
||||
notifyOld.CollectionChanged -= ItemsOnCollectionChanged;
|
||||
|
||||
_items = value switch
|
||||
{
|
||||
IList list => list,
|
||||
IEnumerable<object> enumerable => new List<object>(enumerable),
|
||||
_ => new List<object>(value.Cast<object>())
|
||||
};
|
||||
|
||||
if (_items is INotifyCollectionChanged notifyNew)
|
||||
notifyNew.CollectionChanged += ItemsOnCollectionChanged;
|
||||
|
||||
ItemsOnCollectionChanged(this, NotifyReset);
|
||||
}
|
||||
}
|
||||
|
||||
public ItemsControl()
|
||||
{
|
||||
AddChild(_panelControl);
|
||||
}
|
||||
|
||||
private void ItemsOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
_collectionChanged?.Invoke(sender, e);
|
||||
|
||||
if (_panelControl is null or IVirtualizingContainer)
|
||||
return;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
AddItems(e.NewStartingIndex, e.NewItems!);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
RemoveItems(e.OldStartingIndex, e.OldItems!.Count);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
// TODO: More intelligent move handling is possible here.
|
||||
RemoveItems(e.OldStartingIndex, e.OldItems!.Count);
|
||||
AddItems(e.NewStartingIndex, e.NewItems!);
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
_panelControl.RemoveAllChildren();
|
||||
AddItems(0, _items);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void AddItems(int startIndex, IList items)
|
||||
{
|
||||
var insertIndex = startIndex;
|
||||
foreach (var item in items)
|
||||
{
|
||||
var control = CreateControl(item);
|
||||
_panelControl.AddChild(control);
|
||||
if (insertIndex != _panelControl.ChildCount)
|
||||
control.SetPositionInParent(insertIndex);
|
||||
insertIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveItems(int startIndex, int itemCount)
|
||||
{
|
||||
for (var i = 0; i < itemCount; i++)
|
||||
{
|
||||
_panelControl.RemoveChild(startIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IVirtualizingContainerParent.ItemCount => _items.Count;
|
||||
|
||||
private Control CreateControl(object? item)
|
||||
{
|
||||
if (_itemTemplate == null)
|
||||
{
|
||||
// Fallback if no template is provided.
|
||||
var str = item?.ToString() ?? "";
|
||||
return new Label { Text = str };
|
||||
}
|
||||
|
||||
return _itemTemplate.Instantiate(item);
|
||||
}
|
||||
|
||||
Control IVirtualizingContainerParent.CreateControl(object? item)
|
||||
{
|
||||
return CreateControl(item);
|
||||
}
|
||||
|
||||
event NotifyCollectionChangedEventHandler IVirtualizingContainerParent.CollectionChanged
|
||||
{
|
||||
add => _collectionChanged += value;
|
||||
remove => _collectionChanged -= value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using Robust.Shared.Utility;
|
||||
using LayoutOrientation = Robust.Client.UserInterface.Controls.BoxContainer.LayoutOrientation;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls;
|
||||
|
||||
public sealed class VirtualizingBoxContainer : Control, IVirtualizingContainer
|
||||
{
|
||||
private LayoutOrientation _orientation;
|
||||
private IVirtualizingContainerParent? _parent;
|
||||
|
||||
public LayoutOrientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
InvalidateMeasure();
|
||||
}
|
||||
}
|
||||
|
||||
void IVirtualizingContainer.SetParent(IVirtualizingContainerParent parent)
|
||||
{
|
||||
DebugTools.Assert(_parent == null);
|
||||
|
||||
_parent = parent;
|
||||
_parent.CollectionChanged += ParentOnCollectionChanged;
|
||||
}
|
||||
|
||||
void IVirtualizingContainer.ClearParent()
|
||||
{
|
||||
DebugTools.Assert(_parent != null);
|
||||
|
||||
_parent.CollectionChanged -= ParentOnCollectionChanged;
|
||||
_parent = null;
|
||||
}
|
||||
|
||||
private void ParentOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user