Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Content.Client._Funkystation.CateringDigiboard.UI;
using Content.Shared._Funkystation.CateringDigiboard;
using JetBrains.Annotations;
using Robust.Client.UserInterface;

namespace Content.Client._Funkystation.CateringDigiboard;

[UsedImplicitly]
public sealed class CateringDigiboardBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[ViewVariables]
private CateringDigiboardMenu? _menu;

protected override void Open()
{
base.Open();

_menu = this.CreateWindow<CateringDigiboardMenu>();
_menu.OnAddItem += () => SendMessage(new CateringDigiboardAddItemMessage());
_menu.OnAddCategory += () => SendMessage(new CateringDigiboardAddCategoryMessage());
_menu.OnEditItem += (id, name, description, cost) =>
SendMessage(new CateringDigiboardEditItemMessage(id, name, description, cost));
_menu.OnRemoveItem += id => SendMessage(new CateringDigiboardRemoveItemMessage(id));
_menu.OnReorderItem += (id, index) => SendMessage(new CateringDigiboardReorderMessage(id, index));
_menu.OnPrint += count => SendMessage(new CateringDigiboardPrintMessage(count));
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not CateringDigiboardBoundUserInterfaceState msg)
return;

_menu?.UpdateState(msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'catering-digiboard-title'}"
MinSize="480 720"
SetSize="480 720">
<BoxContainer Orientation="Vertical">
<PanelContainer Margin="0 0 0 8">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#444550" />
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="8 4">
<Label Text="{Loc 'catering-digiboard-menu-label'}" VerticalAlignment="Center" HorizontalExpand="True" />
<Button Name="AddCategoryButton" Text="{Loc 'catering-digiboard-add-category-button'}" Margin="0 0 4 0" />
<Button Name="AddItemButton" Text="{Loc 'catering-digiboard-add-item-button'}" />
</BoxContainer>
</PanelContainer>

<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Name="ItemList" Orientation="Vertical" />
</ScrollContainer>

<PanelContainer StyleClasses="LowDivider" Margin="0 8 0 8" />

<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="12 0 10 10">
<Label Text="{Loc 'catering-digiboard-print-label'}" VerticalAlignment="Center" HorizontalExpand="True" />
<Button Name="PrintOneButton" Text="{Loc 'catering-digiboard-print-one-button'}" Margin="0 0 4 0" />
<Button Name="PrintFiveButton" Text="{Loc 'catering-digiboard-print-five-button'}" Margin="0 0 4 0" />
<Button Name="PrintTenButton" Text="{Loc 'catering-digiboard-print-ten-button'}" />
</BoxContainer>

<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 4 10 4" VerticalAlignment="Bottom">
<Label Text="NT CATERING DIGI-BOARD" StyleClasses="WindowFooterText" />
<Label Text="SERVICE SYS" StyleClasses="WindowFooterText" HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered" VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="16 16"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
using System.Linq;
using Content.Shared._Funkystation.CateringDigiboard;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Timing;
using Content.Client.UserInterface.Controls;

namespace Content.Client._Funkystation.CateringDigiboard.UI;

[GenerateTypedNameReferences]
public sealed partial class CateringDigiboardMenu : FancyWindow
{
public event Action? OnAddItem;
public event Action? OnAddCategory;
public event Action<string, string, string, int>? OnEditItem;
public event Action<string>? OnRemoveItem;
public event Action<string, int>? OnReorderItem;
public event Action<int>? OnPrint;
private CateringDigiboardBoundUserInterfaceState? _lastState;
private readonly List<Control> _rowControls = new();
private string? _dragItemId;
private Control? _dragRowControl;
private int _dragTargetIndex;

public CateringDigiboardMenu()
{
RobustXamlLoader.Load(this);

AddItemButton.OnPressed += _ => OnAddItem?.Invoke();
AddCategoryButton.OnPressed += _ => OnAddCategory?.Invoke();
PrintOneButton.OnPressed += _ => OnPrint?.Invoke(1);
PrintFiveButton.OnPressed += _ => OnPrint?.Invoke(5);
PrintTenButton.OnPressed += _ => OnPrint?.Invoke(10);
}

public void UpdateState(CateringDigiboardBoundUserInterfaceState state)
{
var itemsChanged = _lastState is null || !ItemsEqual(_lastState.Items, state.Items);
_lastState = state;

if (itemsChanged)
RebuildList(state);
else
RefreshButtons(state);
}

private static bool ItemsEqual(List<CateringMenuItem> a, List<CateringMenuItem> b)
{
if (a.Count != b.Count)
return false;

for (var i = 0; i < a.Count; i++)
{
var x = a[i];
var y = b[i];

if (x.Id != y.Id || x.Name != y.Name || x.Description != y.Description ||
x.ScripCost != y.ScripCost || x.IsCategory != y.IsCategory)
return false;
}

return true;
}

private void RefreshButtons(CateringDigiboardBoundUserInterfaceState state)
{
var itemCount = state.Items.Count(i => !i.IsCategory);
AddItemButton.Disabled = itemCount >= state.MaxItems;

PrintOneButton.Disabled = state.IsPrinting;
PrintFiveButton.Disabled = state.IsPrinting;
PrintTenButton.Disabled = state.IsPrinting;
}

private void RebuildList(CateringDigiboardBoundUserInterfaceState state)
{
CancelDrag();

RefreshButtons(state);
RenderRows(state.Items);

if (state.Items.Count == 0)
{
ItemList.AddChild(new Label
{
Text = Loc.GetString("catering-digiboard-no-items"),
StyleClasses = { "LabelSubText" },
Margin = new Thickness(4)
});
}
}

private void RenderRows(List<CateringMenuItem> items)
{
ReleaseFocusRecursive(ItemList);
ItemList.RemoveAllChildren();
_rowControls.Clear();

foreach (var t in items)
{
var row = BuildRow(t);
_rowControls.Add(row);
ItemList.AddChild(row);
}
}

protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);

if (_dragItemId is null)
return;

var mouseY = UserInterfaceManager.MousePositionScaled.Position.Y;

var target = _rowControls.Where(row => row != _dragRowControl).Count(row => mouseY > row.GlobalPosition.Y + row.Size.Y / 2f);

_dragTargetIndex = target;
}

private void OnDragGrab(GUIBoundKeyEventArgs args, string itemId)
{
if (args.Function != EngineKeyFunctions.UIClick || _lastState is null)
return;

args.Handle();
_dragItemId = itemId;

var index = _lastState.Items.FindIndex(i => i.Id == itemId);
_dragTargetIndex = index;

if (index >= 0 && index < _rowControls.Count)
{
_dragRowControl = _rowControls[index];
_dragRowControl.Modulate = new Color(1f, 1f, 1f, 0.5f);
}
}

private void OnDragRelease(GUIBoundKeyEventArgs args, string itemId)
{
if (args.Function != EngineKeyFunctions.UIClick || _dragItemId != itemId)
return;

args.Handle();

var targetIndex = _dragTargetIndex;
CancelDrag();

OnReorderItem?.Invoke(itemId, targetIndex);

UserInterfaceManager.KeyboardFocused?.ReleaseKeyboardFocus();
}

private void CancelDrag()
{
if (_dragRowControl is not null)
_dragRowControl.Modulate = Color.White;

_dragItemId = null;
_dragRowControl = null;
_dragTargetIndex = 0;
}

private Control BuildRow(CateringMenuItem item)
{
return item.IsCategory
? BuildCategoryRow(item)
: BuildItemRow(item);
}

private Control BuildDragHandle(string itemId)
{
var handle = new Label
{
Text = Loc.GetString("catering-digiboard-drag-handle"),
MinWidth = 24,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
MouseFilter = MouseFilterMode.Stop,
DefaultCursorShape = CursorShape.VResize
};

handle.OnKeyBindDown += args => OnDragGrab(args, itemId);
handle.OnKeyBindUp += args => OnDragRelease(args, itemId);

return handle;
}

private Control BuildCategoryRow(CateringMenuItem item)
{
var row = new PanelContainer
{
HorizontalExpand = true,
Margin = new Thickness(0, 4, 0, 4)
};

row.PanelOverride = new StyleBoxFlat
{
BackgroundColor = new Color(0.27f, 0.27f, 0.31f)
};

var box = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
HorizontalExpand = true,
Margin = new Thickness(8, 4)
};

var nameEdit = new LineEdit
{
Text = item.Name,
HorizontalExpand = true,
PlaceHolder = Loc.GetString("catering-digiboard-category-name-placeholder")
};

nameEdit.OnFocusExit += _ => OnEditItem?.Invoke(item.Id, nameEdit.Text, string.Empty, 0);

var removeButton = new Button { Text = Loc.GetString("catering-digiboard-remove-item-button"), MinWidth = 32 };
removeButton.OnPressed += _ => OnRemoveItem?.Invoke(item.Id);

box.AddChild(BuildDragHandle(item.Id));
box.AddChild(new Label { Text = Loc.GetString("catering-digiboard-category-tag"), VerticalAlignment = VAlignment.Center, Margin = new Thickness(6, 0, 6, 0) });
box.AddChild(nameEdit);
box.AddChild(removeButton);

row.AddChild(box);
return row;
}

private Control BuildItemRow(CateringMenuItem item)
{
var row = new PanelContainer
{
HorizontalExpand = true,
Margin = new Thickness(0, 0, 0, 4)
};

var box = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
HorizontalExpand = true,
Margin = new Thickness(8, 6)
};

var topRow = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
HorizontalExpand = true
};

var nameEdit = new LineEdit
{
Text = item.Name,
HorizontalExpand = true,
PlaceHolder = Loc.GetString("catering-digiboard-item-name-placeholder")
};

var costEdit = new LineEdit
{
Text = item.ScripCost.ToString(),
MinWidth = 64,
PlaceHolder = Loc.GetString("catering-digiboard-item-cost-placeholder")
};

var removeButton = new Button { Text = Loc.GetString("catering-digiboard-remove-item-button"), MinWidth = 32 };

topRow.AddChild(BuildDragHandle(item.Id));
topRow.AddChild(nameEdit);
topRow.AddChild(costEdit);
topRow.AddChild(removeButton);

var descEdit = new LineEdit
{
Text = item.Description,
HorizontalExpand = true,
PlaceHolder = Loc.GetString("catering-digiboard-item-description-placeholder"),
Margin = new Thickness(0, 4, 0, 0)
};

void CommitEdit()
{
if (!int.TryParse(costEdit.Text, out var cost))
cost = 0;

OnEditItem?.Invoke(item.Id, nameEdit.Text, descEdit.Text, cost);
}

nameEdit.OnFocusExit += _ => CommitEdit();
costEdit.OnFocusExit += _ => CommitEdit();
descEdit.OnFocusExit += _ => CommitEdit();

removeButton.OnPressed += _ => OnRemoveItem?.Invoke(item.Id);

box.AddChild(topRow);
box.AddChild(descEdit);
row.AddChild(box);

return row;
}

private static void ReleaseFocusRecursive(Control control)
{
if (control.HasKeyboardFocus())
control.ReleaseKeyboardFocus();

foreach (var child in control.Children)
{
ReleaseFocusRecursive(child);
}
}
}
Loading
Loading