Implement Entity List Display and rework StorageComponent window (#4140)
* Create EntityListDisplay * Rework ClientStorage window * Add styling * Remove unnecessary colors * Rename list * Make scrollbar push content * Change children update a bit * Add old index * Localize ClientStorageComponent * Add size return * Remove spaces * Fix usings
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Client.Animations;
|
using Content.Client.Animations;
|
||||||
|
using Content.Client.Items.Components;
|
||||||
using Content.Client.Hands;
|
using Content.Client.Hands;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.DragDrop;
|
using Content.Shared.DragDrop;
|
||||||
using Content.Shared.Storage;
|
using Content.Shared.Storage;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
@@ -13,9 +14,11 @@ using Robust.Client.UserInterface.Controls;
|
|||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
|
using static Robust.Client.UserInterface.Control;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
namespace Content.Client.Storage
|
namespace Content.Client.Storage
|
||||||
@@ -47,6 +50,8 @@ namespace Content.Client.Storage
|
|||||||
base.OnAdd();
|
base.OnAdd();
|
||||||
|
|
||||||
_window = new StorageWindow(this) {Title = Owner.Name};
|
_window = new StorageWindow(this) {Title = Owner.Name};
|
||||||
|
_window.EntityList.GenerateItem += GenerateButton;
|
||||||
|
_window.EntityList.ItemPressed += Interact;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRemove()
|
protected override void OnRemove()
|
||||||
@@ -104,7 +109,7 @@ namespace Content.Client.Storage
|
|||||||
_storedEntities = storageState.StoredEntities.Select(id => Owner.EntityManager.GetEntity(id)).ToList();
|
_storedEntities = storageState.StoredEntities.Select(id => Owner.EntityManager.GetEntity(id)).ToList();
|
||||||
StorageSizeUsed = storageState.StorageSizeUsed;
|
StorageSizeUsed = storageState.StorageSizeUsed;
|
||||||
StorageCapacityMax = storageState.StorageSizeMax;
|
StorageCapacityMax = storageState.StorageSizeMax;
|
||||||
_window?.BuildEntityList();
|
_window?.BuildEntityList(storageState.StoredEntities.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -172,14 +177,53 @@ namespace Content.Client.Storage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateButton(EntityUid entityUid, Control button)
|
||||||
|
{
|
||||||
|
if (!Owner.EntityManager.TryGetEntity(entityUid, out var entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.TryGetComponent(out ISpriteComponent? sprite);
|
||||||
|
entity.TryGetComponent(out ItemComponent? item);
|
||||||
|
|
||||||
|
button.AddChild(new HBoxContainer
|
||||||
|
{
|
||||||
|
SeparationOverride = 2,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new SpriteView
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Left,
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
MinSize = new Vector2(32.0f, 32.0f),
|
||||||
|
OverrideDirection = Direction.South,
|
||||||
|
Sprite = sprite
|
||||||
|
},
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
HorizontalExpand = true,
|
||||||
|
ClipText = true,
|
||||||
|
Text = entity.Name
|
||||||
|
},
|
||||||
|
new Label
|
||||||
|
{
|
||||||
|
Align = Label.AlignMode.Right,
|
||||||
|
Text = item?.Size.ToString() ?? Loc.GetString("no-item-size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GUI class for client storage component
|
/// GUI class for client storage component
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class StorageWindow : SS14Window
|
private class StorageWindow : SS14Window
|
||||||
{
|
{
|
||||||
private Control VSplitContainer;
|
private Control _vBox;
|
||||||
private readonly BoxContainer _entityList;
|
|
||||||
private readonly Label _information;
|
private readonly Label _information;
|
||||||
|
public readonly EntityListDisplay EntityList;
|
||||||
public ClientStorageComponent StorageEntity;
|
public ClientStorageComponent StorageEntity;
|
||||||
|
|
||||||
private readonly StyleBoxFlat _hoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.35f) };
|
private readonly StyleBoxFlat _hoveredBox = new() { BackgroundColor = Color.Black.WithAlpha(0.35f) };
|
||||||
@@ -189,20 +233,21 @@ namespace Content.Client.Storage
|
|||||||
{
|
{
|
||||||
StorageEntity = storageEntity;
|
StorageEntity = storageEntity;
|
||||||
SetSize = (200, 320);
|
SetSize = (200, 320);
|
||||||
Title = "Storage Item";
|
Title = Loc.GetString("comp-storage-window-title");
|
||||||
RectClipContent = true;
|
RectClipContent = true;
|
||||||
|
|
||||||
var containerButton = new ContainerButton
|
var containerButton = new ContainerButton
|
||||||
{
|
{
|
||||||
|
Name = "StorageContainerButton",
|
||||||
MouseFilter = MouseFilterMode.Pass,
|
MouseFilter = MouseFilterMode.Pass,
|
||||||
};
|
};
|
||||||
|
Contents.AddChild(containerButton);
|
||||||
|
|
||||||
var innerContainerButton = new PanelContainer
|
var innerContainerButton = new PanelContainer
|
||||||
{
|
{
|
||||||
PanelOverride = _unHoveredBox,
|
PanelOverride = _unHoveredBox,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
containerButton.AddChild(innerContainerButton);
|
containerButton.AddChild(innerContainerButton);
|
||||||
containerButton.OnPressed += args =>
|
containerButton.OnPressed += args =>
|
||||||
{
|
{
|
||||||
@@ -214,42 +259,30 @@ namespace Content.Client.Storage
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
VSplitContainer = new BoxContainer
|
_vBox = new BoxContainer()
|
||||||
{
|
{
|
||||||
Orientation = LayoutOrientation.Vertical,
|
Orientation = LayoutOrientation.Vertical,
|
||||||
MouseFilter = MouseFilterMode.Ignore,
|
MouseFilter = MouseFilterMode.Ignore,
|
||||||
};
|
};
|
||||||
containerButton.AddChild(VSplitContainer);
|
containerButton.AddChild(_vBox);
|
||||||
_information = new Label
|
_information = new Label
|
||||||
{
|
{
|
||||||
Text = "Items: 0 Volume: 0/0 Stuff",
|
Text = Loc.GetString("comp-storage-window-volume", ("itemCount", 0), ("usedVolume", 0), ("maxVolume", 0)),
|
||||||
VerticalAlignment = VAlignment.Center
|
VerticalAlignment = VAlignment.Center
|
||||||
};
|
};
|
||||||
VSplitContainer.AddChild(_information);
|
_vBox.AddChild(_information);
|
||||||
|
|
||||||
var listScrollContainer = new ScrollContainer
|
EntityList = new EntityListDisplay
|
||||||
{
|
{
|
||||||
VerticalExpand = true,
|
Name = "EntityListContainer",
|
||||||
HorizontalExpand = true,
|
|
||||||
HScrollEnabled = false,
|
|
||||||
VScrollEnabled = true,
|
|
||||||
};
|
};
|
||||||
_entityList = new BoxContainer
|
_vBox.AddChild(EntityList);
|
||||||
{
|
EntityList.OnMouseEntered += args =>
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
HorizontalExpand = true
|
|
||||||
};
|
|
||||||
listScrollContainer.AddChild(_entityList);
|
|
||||||
VSplitContainer.AddChild(listScrollContainer);
|
|
||||||
|
|
||||||
Contents.AddChild(containerButton);
|
|
||||||
|
|
||||||
listScrollContainer.OnMouseEntered += args =>
|
|
||||||
{
|
{
|
||||||
innerContainerButton.PanelOverride = _hoveredBox;
|
innerContainerButton.PanelOverride = _hoveredBox;
|
||||||
};
|
};
|
||||||
|
|
||||||
listScrollContainer.OnMouseExited += args =>
|
EntityList.OnMouseExited += args =>
|
||||||
{
|
{
|
||||||
innerContainerButton.PanelOverride = _unHoveredBox;
|
innerContainerButton.PanelOverride = _unHoveredBox;
|
||||||
};
|
};
|
||||||
@@ -264,121 +297,20 @@ namespace Content.Client.Storage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loops through stored entities creating buttons for each, updates information labels
|
/// Loops through stored entities creating buttons for each, updates information labels
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BuildEntityList()
|
public void BuildEntityList(List<EntityUid> entityUids)
|
||||||
{
|
{
|
||||||
_entityList.DisposeAllChildren();
|
EntityList.PopulateList(entityUids);
|
||||||
|
|
||||||
var storageList = StorageEntity.StoredEntities;
|
|
||||||
|
|
||||||
var storedGrouped = storageList.GroupBy(e => e).Select(e => new
|
|
||||||
{
|
|
||||||
Entity = e.Key,
|
|
||||||
Amount = e.Count()
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var group in storedGrouped)
|
|
||||||
{
|
|
||||||
var entity = group.Entity;
|
|
||||||
var button = new EntityButton()
|
|
||||||
{
|
|
||||||
EntityUid = entity.Uid,
|
|
||||||
MouseFilter = MouseFilterMode.Stop,
|
|
||||||
};
|
|
||||||
button.ActualButton.OnToggled += OnItemButtonToggled;
|
|
||||||
//Name and Size labels set
|
|
||||||
button.EntityName.Text = entity.Name;
|
|
||||||
|
|
||||||
button.EntitySize.Text = group.Amount.ToString();
|
|
||||||
|
|
||||||
//Gets entity sprite and assigns it to button texture
|
|
||||||
if (entity.TryGetComponent(out ISpriteComponent? sprite))
|
|
||||||
{
|
|
||||||
button.EntitySpriteView.Sprite = sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entityList.AddChild(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sets information about entire storage container current capacity
|
//Sets information about entire storage container current capacity
|
||||||
if (StorageEntity.StorageCapacityMax != 0)
|
if (StorageEntity.StorageCapacityMax != 0)
|
||||||
{
|
{
|
||||||
_information.Text = String.Format("Items: {0}, Stored: {1}/{2}", storageList.Count,
|
_information.Text = Loc.GetString("comp-storage-window-volume", ("itemCount", entityUids.Count),
|
||||||
StorageEntity.StorageSizeUsed, StorageEntity.StorageCapacityMax);
|
("usedVolume", StorageEntity.StorageSizeUsed), ("maxVolume", StorageEntity.StorageCapacityMax));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_information.Text = String.Format("Items: {0}", storageList.Count);
|
_information.Text = Loc.GetString("comp-storage-window-volume-unlimited", ("itemCount", entityUids.Count));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function assigned to button toggle which removes the entity from storage
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
private void OnItemButtonToggled(BaseButton.ButtonToggledEventArgs args)
|
|
||||||
{
|
|
||||||
if (args.Button.Parent is not EntityButton button)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Button.Pressed = false;
|
|
||||||
StorageEntity.Interact(button.EntityUid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label
|
|
||||||
/// </summary>
|
|
||||||
private class EntityButton : Control
|
|
||||||
{
|
|
||||||
public EntityUid EntityUid { get; set; }
|
|
||||||
public Button ActualButton { get; }
|
|
||||||
public SpriteView EntitySpriteView { get; }
|
|
||||||
public Label EntityName { get; }
|
|
||||||
public Label EntitySize { get; }
|
|
||||||
|
|
||||||
public EntityButton()
|
|
||||||
{
|
|
||||||
ActualButton = new Button
|
|
||||||
{
|
|
||||||
HorizontalExpand = true,
|
|
||||||
VerticalExpand = true,
|
|
||||||
ToggleMode = true,
|
|
||||||
MouseFilter = MouseFilterMode.Stop
|
|
||||||
};
|
|
||||||
AddChild(ActualButton);
|
|
||||||
|
|
||||||
var hBoxContainer = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal
|
|
||||||
};
|
|
||||||
EntitySpriteView = new SpriteView
|
|
||||||
{
|
|
||||||
MinSize = new Vector2(32.0f, 32.0f),
|
|
||||||
OverrideDirection = Direction.South
|
|
||||||
};
|
|
||||||
EntityName = new Label
|
|
||||||
{
|
|
||||||
VerticalAlignment = VAlignment.Center,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
Margin = new Thickness(0, 0, 6, 0),
|
|
||||||
Text = "Backpack",
|
|
||||||
ClipText = true
|
|
||||||
};
|
|
||||||
|
|
||||||
hBoxContainer.AddChild(EntitySpriteView);
|
|
||||||
hBoxContainer.AddChild(EntityName);
|
|
||||||
|
|
||||||
EntitySize = new Label
|
|
||||||
{
|
|
||||||
VerticalAlignment = VAlignment.Bottom,
|
|
||||||
Text = "Size 6",
|
|
||||||
Align = Label.AlignMode.Right,
|
|
||||||
};
|
|
||||||
|
|
||||||
hBoxContainer.AddChild(EntitySize);
|
|
||||||
AddChild(hBoxContainer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Content.Client.HUD;
|
|||||||
using Content.Client.HUD.UI;
|
using Content.Client.HUD.UI;
|
||||||
using Content.Client.Resources;
|
using Content.Client.Resources;
|
||||||
using Content.Client.Targeting;
|
using Content.Client.Targeting;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
@@ -38,6 +39,7 @@ namespace Content.Client.Stylesheets
|
|||||||
public const string StyleClassChatChannelSelectorButton = "chatSelectorOptionButton";
|
public const string StyleClassChatChannelSelectorButton = "chatSelectorOptionButton";
|
||||||
public const string StyleClassChatFilterOptionButton = "chatFilterOptionButton";
|
public const string StyleClassChatFilterOptionButton = "chatFilterOptionButton";
|
||||||
public const string StyleClassContextMenuCount = "contextMenuCount";
|
public const string StyleClassContextMenuCount = "contextMenuCount";
|
||||||
|
public const string StyleClassStorageButton = "storageButton";
|
||||||
|
|
||||||
public const string StyleClassSliderRed = "Red";
|
public const string StyleClassSliderRed = "Red";
|
||||||
public const string StyleClassSliderGreen = "Green";
|
public const string StyleClassSliderGreen = "Green";
|
||||||
@@ -139,6 +141,12 @@ namespace Content.Client.Stylesheets
|
|||||||
hotbarBackground.SetPatchMargin(StyleBox.Margin.All, 2);
|
hotbarBackground.SetPatchMargin(StyleBox.Margin.All, 2);
|
||||||
hotbarBackground.SetExpandMargin(StyleBox.Margin.All, 4);
|
hotbarBackground.SetExpandMargin(StyleBox.Margin.All, 4);
|
||||||
|
|
||||||
|
var buttonStorage = new StyleBoxTexture(BaseButton);
|
||||||
|
buttonStorage.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
buttonStorage.SetPadding(StyleBox.Margin.All, 0);
|
||||||
|
buttonStorage.SetContentMarginOverride(StyleBox.Margin.Vertical, 0);
|
||||||
|
buttonStorage.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
|
||||||
|
|
||||||
var buttonRectTex = resCache.GetTexture("/Textures/Interface/Nano/light_panel_background_bordered.png");
|
var buttonRectTex = resCache.GetTexture("/Textures/Interface/Nano/light_panel_background_bordered.png");
|
||||||
var buttonRect = new StyleBoxTexture(BaseButton)
|
var buttonRect = new StyleBoxTexture(BaseButton)
|
||||||
{
|
{
|
||||||
@@ -496,6 +504,26 @@ namespace Content.Client.Stylesheets
|
|||||||
new StyleProperty("font-color", Color.FromHex("#E5E5E581")),
|
new StyleProperty("font-color", Color.FromHex("#E5E5E581")),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Thin buttons (No padding nor vertical margin)
|
||||||
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
|
.Prop(ContainerButton.StylePropertyStyleBox, buttonStorage),
|
||||||
|
|
||||||
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassNormal)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorDefault),
|
||||||
|
|
||||||
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassHover)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorHovered),
|
||||||
|
|
||||||
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassPressed)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorPressed),
|
||||||
|
|
||||||
|
Element<EntityContainerButton>().Class(StyleClassStorageButton)
|
||||||
|
.Pseudo(ContainerButton.StylePseudoClassDisabled)
|
||||||
|
.Prop(Control.StylePropertyModulateSelf, ButtonColorDisabled),
|
||||||
|
|
||||||
// action slot hotbar buttons
|
// action slot hotbar buttons
|
||||||
new StyleRule(new SelectorElement(typeof(ActionSlot), null, null, new[] {ContainerButton.StylePseudoClassNormal}), new[]
|
new StyleRule(new SelectorElement(typeof(ActionSlot), null, null, new[] {ContainerButton.StylePseudoClassNormal}), new[]
|
||||||
{
|
{
|
||||||
|
|||||||
292
Content.Client/UserInterface/Controls/EntityListDisplay.cs
Normal file
292
Content.Client/UserInterface/Controls/EntityListDisplay.cs
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Controls
|
||||||
|
{
|
||||||
|
public class EntityListDisplay : Control
|
||||||
|
{
|
||||||
|
public const string StylePropertySeparation = "separation";
|
||||||
|
|
||||||
|
public int? SeparationOverride { get; set; }
|
||||||
|
public Action<EntityUid, Control>? GenerateItem;
|
||||||
|
public Action<EntityUid>? ItemPressed;
|
||||||
|
|
||||||
|
private const int DefaultSeparation = 3;
|
||||||
|
|
||||||
|
private readonly VScrollBar _vScrollBar;
|
||||||
|
|
||||||
|
private List<EntityUid>? _entityUids;
|
||||||
|
private int _count = 0;
|
||||||
|
private float _itemHeight = 0;
|
||||||
|
private float _totalHeight = 0;
|
||||||
|
private int _topIndex = 0;
|
||||||
|
private int _bottomIndex = 0;
|
||||||
|
private bool _updateChildren = false;
|
||||||
|
private bool _suppressScrollValueChanged;
|
||||||
|
|
||||||
|
public int ScrollSpeedY { get; set; } = 50;
|
||||||
|
|
||||||
|
private int ActualSeparation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TryGetStyleProperty(StylePropertySeparation, out int separation))
|
||||||
|
{
|
||||||
|
return separation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SeparationOverride ?? DefaultSeparation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityListDisplay()
|
||||||
|
{
|
||||||
|
HorizontalExpand = true;
|
||||||
|
VerticalExpand = true;
|
||||||
|
RectClipContent = true;
|
||||||
|
MouseFilter = MouseFilterMode.Pass;
|
||||||
|
|
||||||
|
_vScrollBar = new VScrollBar
|
||||||
|
{
|
||||||
|
HorizontalExpand = false,
|
||||||
|
HorizontalAlignment = HAlignment.Right
|
||||||
|
};
|
||||||
|
AddChild(_vScrollBar);
|
||||||
|
_vScrollBar.OnValueChanged += ScrollValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateList(List<EntityUid> entities)
|
||||||
|
{
|
||||||
|
if (_count == 0 && entities.Count > 0)
|
||||||
|
{
|
||||||
|
EntityContainerButton control = new(entities[0]);
|
||||||
|
GenerateItem?.Invoke(entities[0], control);
|
||||||
|
control.Measure(Vector2.Infinity);
|
||||||
|
_itemHeight = control.DesiredSize.Y;
|
||||||
|
control.Dispose();
|
||||||
|
}
|
||||||
|
_count = entities.Count;
|
||||||
|
_entityUids = entities;
|
||||||
|
_updateChildren = true;
|
||||||
|
InvalidateArrange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnItemPressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Button is not EntityContainerButton button)
|
||||||
|
return;
|
||||||
|
ItemPressed?.Invoke(button.EntityUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Pure]
|
||||||
|
private Vector2 GetScrollValue()
|
||||||
|
{
|
||||||
|
var v = _vScrollBar.Value;
|
||||||
|
if (!_vScrollBar.Visible)
|
||||||
|
{
|
||||||
|
v = 0;
|
||||||
|
}
|
||||||
|
return new Vector2(0, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||||
|
{
|
||||||
|
var separation = (int) (ActualSeparation * UIScale);
|
||||||
|
|
||||||
|
#region Scroll
|
||||||
|
var cHeight = _totalHeight;
|
||||||
|
var vBarSize = _vScrollBar.DesiredSize.X;
|
||||||
|
var (sWidth, sHeight) = finalSize;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Suppress events to avoid weird recursion.
|
||||||
|
_suppressScrollValueChanged = true;
|
||||||
|
|
||||||
|
if (sHeight < cHeight)
|
||||||
|
sWidth -= vBarSize;
|
||||||
|
|
||||||
|
if (sHeight < cHeight)
|
||||||
|
{
|
||||||
|
_vScrollBar.Visible = true;
|
||||||
|
_vScrollBar.Page = sHeight;
|
||||||
|
_vScrollBar.MaxValue = cHeight;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_vScrollBar.Visible = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_suppressScrollValueChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_vScrollBar.Visible)
|
||||||
|
{
|
||||||
|
_vScrollBar.Arrange(UIBox2.FromDimensions(Vector2.Zero, finalSize));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Rebuild Children
|
||||||
|
/*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* var _itemHeight = 32;
|
||||||
|
* var separation = 3;
|
||||||
|
* 32 | 32 | Control.Size.Y 0
|
||||||
|
* 35 | 3 | Padding
|
||||||
|
* 67 | 32 | Control.Size.Y 1
|
||||||
|
* 70 | 3 | Padding
|
||||||
|
* 102 | 32 | Control.Size.Y 2
|
||||||
|
* 105 | 3 | Padding
|
||||||
|
* 137 | 32 | Control.Size.Y 3
|
||||||
|
*
|
||||||
|
* If viewport height is 60
|
||||||
|
* visible should be 2 items (start = 0, end = 1)
|
||||||
|
*
|
||||||
|
* scroll.Y = 11
|
||||||
|
* visible should be 3 items (start = 0, end = 2)
|
||||||
|
*
|
||||||
|
* start expected: 11 (item: 0)
|
||||||
|
* var start = (int) (scroll.Y
|
||||||
|
*
|
||||||
|
* if (scroll == 32) then { start = 1 }
|
||||||
|
* var start = (int) (scroll.Y + separation / (_itemHeight + separation));
|
||||||
|
* var start = (int) (32 + 3 / (32 + 3));
|
||||||
|
* var start = (int) (35 / 35);
|
||||||
|
* var start = (int) (1);
|
||||||
|
*
|
||||||
|
* scroll = 0, height = 36
|
||||||
|
* if (scroll + height == 36) then { end = 2 }
|
||||||
|
* var end = (int) Math.Ceiling(scroll.Y + height / (_itemHeight + separation));
|
||||||
|
* var end = (int) Math.Ceiling(0 + 36 / (32 + 3));
|
||||||
|
* var end = (int) Math.Ceiling(36 / 35);
|
||||||
|
* var end = (int) Math.Ceiling(1.02857);
|
||||||
|
* var end = (int) 2;
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var scroll = GetScrollValue();
|
||||||
|
var oldTopIndex = _topIndex;
|
||||||
|
_topIndex = (int) ((scroll.Y + separation) / (_itemHeight + separation));
|
||||||
|
if (_topIndex != oldTopIndex)
|
||||||
|
_updateChildren = true;
|
||||||
|
|
||||||
|
var oldBottomIndex = _bottomIndex;
|
||||||
|
_bottomIndex = (int) Math.Ceiling((scroll.Y + Height) / (_itemHeight + separation));
|
||||||
|
_bottomIndex = Math.Min(_bottomIndex, _count);
|
||||||
|
if (_bottomIndex != oldBottomIndex)
|
||||||
|
_updateChildren = true;
|
||||||
|
|
||||||
|
// When scrolling only rebuild visible list when a new item should be visible
|
||||||
|
if (_updateChildren)
|
||||||
|
{
|
||||||
|
_updateChildren = false;
|
||||||
|
|
||||||
|
foreach (var child in Children.ToArray())
|
||||||
|
{
|
||||||
|
if (child == _vScrollBar)
|
||||||
|
continue;
|
||||||
|
RemoveChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entityUids != null)
|
||||||
|
{
|
||||||
|
for (var i = _topIndex; i < _bottomIndex; i++)
|
||||||
|
{
|
||||||
|
var entity = _entityUids[i];
|
||||||
|
|
||||||
|
var button = new EntityContainerButton(entity);
|
||||||
|
button.OnPressed += OnItemPressed;
|
||||||
|
|
||||||
|
GenerateItem?.Invoke(entity, button);
|
||||||
|
AddChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vScrollBar.SetPositionLast();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Layout Children
|
||||||
|
// Use pixel position
|
||||||
|
var pixelWidth = (int)(sWidth * UIScale);
|
||||||
|
|
||||||
|
var offset = (int) -((scroll.Y - _topIndex * (_itemHeight + separation)) * UIScale);
|
||||||
|
var first = true;
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
if (child == _vScrollBar)
|
||||||
|
continue;
|
||||||
|
if (!first)
|
||||||
|
offset += separation;
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
var size = child.DesiredPixelSize.Y;
|
||||||
|
var targetBox = new UIBox2i(0, offset, pixelWidth, offset + size);
|
||||||
|
child.ArrangePixel(targetBox);
|
||||||
|
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||||
|
{
|
||||||
|
_vScrollBar.Measure(availableSize);
|
||||||
|
availableSize.X -= _vScrollBar.DesiredSize.X;
|
||||||
|
|
||||||
|
var constraint = new Vector2(availableSize.X, float.PositiveInfinity);
|
||||||
|
|
||||||
|
var childSize = Vector2.Zero;
|
||||||
|
foreach (var child in Children)
|
||||||
|
{
|
||||||
|
child.Measure(constraint);
|
||||||
|
if (child == _vScrollBar)
|
||||||
|
continue;
|
||||||
|
childSize = Vector2.ComponentMax(childSize, child.DesiredSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_totalHeight = childSize.Y * _count + ActualSeparation * (_count - 1);
|
||||||
|
|
||||||
|
return new Vector2(childSize.X, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScrollValueChanged(Robust.Client.UserInterface.Controls.Range _)
|
||||||
|
{
|
||||||
|
if (_suppressScrollValueChanged)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidateArrange();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||||
|
{
|
||||||
|
base.MouseWheel(args);
|
||||||
|
|
||||||
|
_vScrollBar.ValueTarget -= args.Delta.Y * ScrollSpeedY;
|
||||||
|
|
||||||
|
args.Handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EntityContainerButton : ContainerButton
|
||||||
|
{
|
||||||
|
public EntityUid EntityUid;
|
||||||
|
|
||||||
|
public EntityContainerButton(EntityUid entityUid)
|
||||||
|
{
|
||||||
|
EntityUid = entityUid;
|
||||||
|
AddStyleClass(StyleNano.StyleClassStorageButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
Resources/Locale/en-US/components/storage-component.ftl
Normal file
4
Resources/Locale/en-US/components/storage-component.ftl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
comp-storage-no-item-size = None
|
||||||
|
comp-storage-window-title = Storage Item
|
||||||
|
comp-storage-window-volume = Items: { $itemCount }, Stored: { $usedVolume }/{ $maxVolume }
|
||||||
|
comp-storage-window-volume-unlimited = Items: { $itemCount }
|
||||||
Reference in New Issue
Block a user