diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index af37d1e4df7..e13fd8f3bec 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -172,8 +172,6 @@ private readonly Dictionary _queuedSpeechBubbl public ChatSelectChannel SelectableChannels { get; private set; } private ChatSelectChannel PreferredChannel { get; set; } = ChatSelectChannel.OOC; - public ChatMessage? LastMessage = null; - public event Action? CanSendChannelsChanged; public event Action? FilterableChannelsChanged; public event Action? SelectableChannelsChanged; @@ -882,21 +880,6 @@ public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true) } } - if (LastMessage != null && msg.Message == LastMessage.Message) - { - LastMessage.repeat++; - LastMessage.WrappedMessage = msg.WrappedMessage + $" x{LastMessage.repeat}"; - - foreach (var chat in _chats) - { - // chat._controller.History[index].Item2 - chat.UpdateMessage(chat.GetHistoryLength() - 1, LastMessage); - } - return; - } - - LastMessage = msg; - // Log all incoming chat to repopulate when filter is un-toggled if (!msg.HideChat) { diff --git a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml index 9b3a9840ddf..47286fbc26e 100644 --- a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml +++ b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml @@ -2,7 +2,6 @@ xmlns="https://spacestation14.io" xmlns:widgets="clr-namespace:Content.Client.UserInterface.Systems.Chat.Widgets" xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls" - xmlns:controls1="clr-namespace:Content.Client._Sunrise.UI.Controls" MouseFilter="Stop" HorizontalExpand="True" VerticalExpand="True" @@ -10,7 +9,7 @@ - + diff --git a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs index 54b372b546a..d55b3f01dfa 100644 --- a/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs +++ b/Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs @@ -104,29 +104,6 @@ private void OnChannelFilter(ChatChannel channel, bool active) } } - public int GetHistoryLength() - { - return _controller.History.Count; - } - - public void UpdateMessage(int index, ChatMessage message) - { - var updatedTuple = (_controller.History[index].Item1, message); - _controller.History.Pop(); - _controller.History.Add(updatedTuple); - - var color = message.MessageColorOverride != null - ? message.MessageColorOverride.Value - : message.Channel.TextColor(); - - var formatted = new FormattedMessage(3); - - formatted.AddMarkup(message.WrappedMessage); - formatted.PushColor(color); - - Contents.UpdateLastMessage(formatted); - } - public void AddLine(string message, Color color) { var formatted = new FormattedMessage(3); diff --git a/Content.Client/_Sunrise/UI/Controls/SunriseOutputPanel.cs b/Content.Client/_Sunrise/UI/Controls/SunriseOutputPanel.cs deleted file mode 100644 index 01ab0107360..00000000000 --- a/Content.Client/_Sunrise/UI/Controls/SunriseOutputPanel.cs +++ /dev/null @@ -1,289 +0,0 @@ -using System.Numerics; -using Robust.Client.Graphics; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.RichText; -using Robust.Shared.Utility; - -namespace Content.Client._Sunrise.UI.Controls; - -[Virtual] -public sealed class SunriseOutputPanel : Control -{ - [Dependency] private readonly MarkupTagManager _tagManager = default!; - - public const string StylePropertyStyleBox = "stylebox"; - - private readonly SunriseRingBufferList _entries = new(); - private bool _isAtBottom = true; - - private int _totalContentHeight; - private bool _firstLine = true; - private StyleBox? _styleBoxOverride; - private VScrollBar _scrollBar; - - public bool ScrollFollowing { get; set; } = true; - - private bool _invalidOnVisible; - - public SunriseOutputPanel() - { - IoCManager.InjectDependencies(this); - MouseFilter = Control.MouseFilterMode.Pass; - RectClipContent = true; - - _scrollBar = new VScrollBar - { - Name = "_v_scroll", - HorizontalAlignment = Control.HAlignment.Right - }; - AddChild(_scrollBar); - _scrollBar.OnValueChanged += _ => _isAtBottom = _scrollBar.IsAtEnd; - } - - public int EntryCount => _entries.Count; - - public void UpdateLastMessage(FormattedMessage message) - { - var newEnt = new SunriseRichTextEntry(message, this, _tagManager, null); - newEnt.Update(_tagManager, _getFont(), _getContentBox().Width, UIScale); - _entries[_entries.Count - 1] = newEnt; - } - - public StyleBox? StyleBoxOverride - { - get => _styleBoxOverride; - set - { - _styleBoxOverride = value; - InvalidateMeasure(); - _invalidateEntries(); - } - } - - public void Clear() - { - _firstLine = true; - _entries.Clear(); - _totalContentHeight = 0; - _scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight); - _scrollBar.Value = 0; - } - - public void RemoveEntry(Index index) - { - var entry = _entries[index]; - _entries.RemoveAt(index.GetOffset(_entries.Count)); - - var font = _getFont(); - _totalContentHeight -= entry.Height + font.GetLineSeparation(UIScale); - if (_entries.Count == 0) - { - Clear(); - } - - _scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight); - } - - public void AddText(string text) - { - var msg = new FormattedMessage(); - msg.AddText(text); - AddMessage(msg); - } - - public void AddMessage(FormattedMessage message) - { - var entry = new SunriseRichTextEntry(message, this, _tagManager, null); - - entry.Update(_tagManager, _getFont(), _getContentBox().Width, UIScale); - - _entries.Add(entry); - var font = _getFont(); - _totalContentHeight += entry.Height; - if (_firstLine) - { - _firstLine = false; - } - else - { - _totalContentHeight += font.GetLineSeparation(UIScale); - } - - _scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight); - if (_isAtBottom && ScrollFollowing) - { - _scrollBar.MoveToEnd(); - } - } - - public void ScrollToBottom() - { - _scrollBar.MoveToEnd(); - _isAtBottom = true; - } - - protected override void Draw(DrawingHandleScreen handle) - { - base.Draw(handle); - - var style = _getStyleBox(); - var font = _getFont(); - var lineSeparation = font.GetLineSeparation(UIScale); - style?.Draw(handle, PixelSizeBox, UIScale); - var contentBox = _getContentBox(); - - var entryOffset = -_scrollBar.Value; - - // A stack for format tags. - // This stack contains the format tag to RETURN TO when popped off. - // So when a new color tag gets hit this stack gets the previous color pushed on. - var context = new MarkupDrawingContext(2); - - foreach (ref var entry in _entries) - { - if (entryOffset + entry.Height < 0) - { - // Controls within the entry are the children of this control, which means they are drawn separately - // after this Draw call, so we have to mark them as invisible to prevent them from being drawn. - // - // An alternative option is to ensure that the control position updating logic in entry.Draw is always - // run, and then setting RectClipContent = true to use scissor box testing to handle the controls - // visibility - entry.HideControls(); - entryOffset += entry.Height + lineSeparation; - continue; - } - - if (entryOffset > contentBox.Height) - { - entry.HideControls(); - continue; - } - - entry.Draw(_tagManager, handle, font, contentBox, entryOffset, context, UIScale); - - entryOffset += entry.Height + lineSeparation; - } - } - - protected override void MouseWheel(GUIMouseWheelEventArgs args) - { - base.MouseWheel(args); - - if (MathHelper.CloseToPercent(0, args.Delta.Y)) - { - return; - } - - _scrollBar.ValueTarget -= _getScrollSpeed() * args.Delta.Y; - } - - protected override void Resized() - { - base.Resized(); - - var styleBoxSize = _getStyleBox()?.MinimumSize.Y ?? 0; - - _scrollBar.Page = UIScale * (Height - styleBoxSize); - _invalidateEntries(); - } - - protected override Vector2 MeasureOverride(Vector2 availableSize) - { - return _getStyleBox()?.MinimumSize ?? Vector2.Zero; - } - - private void _invalidateEntries() - { - _totalContentHeight = 0; - var font = _getFont(); - var sizeX = _getContentBox().Width; - foreach (ref var entry in _entries) - { - entry.Update(_tagManager, font, sizeX, UIScale); - _totalContentHeight += entry.Height + font.GetLineSeparation(UIScale); - } - - _scrollBar.MaxValue = Math.Max(_scrollBar.Page, _totalContentHeight); - if (_isAtBottom && ScrollFollowing) - { - _scrollBar.MoveToEnd(); - } - } - - [System.Diagnostics.Contracts.Pure] - private Font _getFont() - { - if (TryGetStyleProperty("font", out var font)) - { - return font; - } - - return UserInterfaceManager.ThemeDefaults.DefaultFont; - } - - [System.Diagnostics.Contracts.Pure] - private StyleBox? _getStyleBox() - { - if (StyleBoxOverride != null) - { - return StyleBoxOverride; - } - - TryGetStyleProperty(StylePropertyStyleBox, out var box); - return box; - } - - [System.Diagnostics.Contracts.Pure] - private float _getScrollSpeed() - { - // The scroll speed depends on the UI scale because the scroll bar is working with physical pixels. - return GetScrollSpeed(_getFont(), UIScale); - } - - [System.Diagnostics.Contracts.Pure] - private UIBox2 _getContentBox() - { - var style = _getStyleBox(); - var box = style?.GetContentBox(PixelSizeBox, UIScale) ?? PixelSizeBox; - box.Right = Math.Max(box.Left, box.Right - _scrollBar.DesiredPixelSize.X); - return box; - } - - protected override void UIScaleChanged() - { - // If this control isn't visible, don't invalidate entries immediately. - // This saves invalidating the debug console if it's hidden, - // which is a huge boon as auto-scaling changes UI scale a lot in that scenario. - if (!VisibleInTree) - _invalidOnVisible = true; - else - _invalidateEntries(); - - base.UIScaleChanged(); - } - - internal static float GetScrollSpeed(Font font, float scale) - { - return font.GetLineHeight(scale) * 2; - } - - protected override void EnteredTree() - { - base.EnteredTree(); - // Due to any number of reasons the entries may be invalidated if added when not visible in the tree. - // e.g. the control has not had its UI scale set and the messages were added, but the - // existing ones were valid when the UI scale was set. - _invalidateEntries(); - } - - protected override void VisibilityChanged(bool newVisible) - { - if (newVisible && _invalidOnVisible) - { - _invalidateEntries(); - _invalidOnVisible = false; - } - } -} diff --git a/Content.Client/_Sunrise/UI/Controls/SunriseRichTextEntry.cs b/Content.Client/_Sunrise/UI/Controls/SunriseRichTextEntry.cs deleted file mode 100644 index e84cfce9ed1..00000000000 --- a/Content.Client/_Sunrise/UI/Controls/SunriseRichTextEntry.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System.Numerics; -using System.Text; -using Robust.Client.Graphics; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.RichText; -using Robust.Shared.Collections; -using Robust.Shared.Utility; - -namespace Content.Client._Sunrise.UI.Controls; - -internal struct SunriseRichTextEntry -{ - private readonly Color _defaultColor; - private readonly Type[]? _tagsAllowed; - - public readonly FormattedMessage Message; - - /// - /// The vertical size of this entry, in pixels. - /// - public int Height; - - /// - /// The horizontal size of this entry, in pixels. - /// - public int Width; - - /// - /// The combined text indices in the message's text tags to put line breaks. - /// - public ValueList LineBreaks; - - private readonly Dictionary? _tagControls; - - public SunriseRichTextEntry(FormattedMessage message, Control parent, MarkupTagManager tagManager, Type[]? tagsAllowed = null, Color? defaultColor = null) - { - Message = message; - Height = 0; - Width = 0; - LineBreaks = default; - _defaultColor = defaultColor ?? new(200, 200, 200); - _tagsAllowed = tagsAllowed; - Dictionary? tagControls = null; - - var nodeIndex = -1; - foreach (var node in Message) - { - nodeIndex++; - - if (node.Name == null) - continue; - - if (!tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag) || !tag.TryGetControl(node, out var control)) - continue; - - parent.Children.Add(control); - tagControls ??= new Dictionary(); - tagControls.Add(nodeIndex, control); - } - - _tagControls = tagControls; - } - - /// - /// Recalculate line dimensions and where it has line breaks for word wrapping. - /// - /// The font being used for display. - /// The maximum horizontal size of the container of this entry. - /// - /// - public void Update(MarkupTagManager tagManager, Font defaultFont, float maxSizeX, float uiScale, float lineHeightScale = 1) - { - // This method is gonna suck due to complexity. - // Bear with me here. - // I am so deeply sorry for the person adding stuff to this in the future. - - Height = defaultFont.GetHeight(uiScale); - LineBreaks.Clear(); - - int? breakLine; - var wordWrap = new SunriseWordWrap(maxSizeX); - var context = new MarkupDrawingContext(); - context.Font.Push(defaultFont); - context.Color.Push(_defaultColor); - - // Go over every node. - // Nodes can change the markup drawing context and return additional text. - // It's also possible for nodes to return inline controls. They get treated as one large rune. - var nodeIndex = -1; - foreach (var node in Message) - { - nodeIndex++; - var text = ProcessNode(tagManager, node, context); - - if (!context.Font.TryPeek(out var font)) - font = defaultFont; - - // And go over every character. - foreach (var rune in text.EnumerateRunes()) - { - if (ProcessRune(ref this, rune, out breakLine)) - continue; - - // Uh just skip unknown characters I guess. - if (!font.TryGetCharMetrics(rune, uiScale, out var metrics)) - continue; - - if (ProcessMetric(ref this, metrics, out breakLine)) - return; - } - - if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control)) - continue; - - control.Measure(new Vector2(Width, Height)); - - var desiredSize = control.DesiredPixelSize; - var controlMetrics = new CharMetrics( - 0, 0, - desiredSize.X, - desiredSize.X, - desiredSize.Y); - - if (ProcessMetric(ref this, controlMetrics, out breakLine)) - return; - } - - Width = wordWrap.FinalizeText(out breakLine); - CheckLineBreak(ref this, breakLine); - - bool ProcessRune(ref SunriseRichTextEntry src, Rune rune, out int? outBreakLine) - { - wordWrap.NextRune(rune, out breakLine, out var breakNewLine, out var skip); - CheckLineBreak(ref src, breakLine); - CheckLineBreak(ref src, breakNewLine); - outBreakLine = breakLine; - return skip; - } - - bool ProcessMetric(ref SunriseRichTextEntry src, CharMetrics metrics, out int? outBreakLine) - { - wordWrap.NextMetrics(metrics, out breakLine, out var abort); - CheckLineBreak(ref src, breakLine); - outBreakLine = breakLine; - return abort; - } - - void CheckLineBreak(ref SunriseRichTextEntry src, int? line) - { - if (line is { } l) - { - src.LineBreaks.Add(l); - if (!context.Font.TryPeek(out var font)) - font = defaultFont; - - src.Height += GetLineHeight(font, uiScale, lineHeightScale); - } - } - } - - internal readonly void HideControls() - { - if (_tagControls == null) - return; - foreach (var control in _tagControls.Values) - { - control.Visible = false; - } - } - - public readonly void Draw( - MarkupTagManager tagManager, - DrawingHandleBase handle, - Font defaultFont, - UIBox2 drawBox, - float verticalOffset, - MarkupDrawingContext context, - float uiScale, - float lineHeightScale = 1) - { - context.Clear(); - context.Color.Push(_defaultColor); - context.Font.Push(defaultFont); - - var globalBreakCounter = 0; - var lineBreakIndex = 0; - var baseLine = drawBox.TopLeft + new Vector2(0, defaultFont.GetAscent(uiScale) + verticalOffset); - var controlYAdvance = 0f; - - var nodeIndex = -1; - foreach (var node in Message) - { - nodeIndex++; - var text = ProcessNode(tagManager, node, context); - if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font)) - { - color = _defaultColor; - font = defaultFont; - } - - foreach (var rune in text.EnumerateRunes()) - { - if (lineBreakIndex < LineBreaks.Count && - LineBreaks[lineBreakIndex] == globalBreakCounter) - { - baseLine = new Vector2(drawBox.Left, baseLine.Y + GetLineHeight(font, uiScale, lineHeightScale) + controlYAdvance); - controlYAdvance = 0; - lineBreakIndex += 1; - } - - var advance = font.DrawChar(handle, rune, baseLine, uiScale, color); - baseLine += new Vector2(advance, 0); - - globalBreakCounter += 1; - } - - if (_tagControls == null || !_tagControls.TryGetValue(nodeIndex, out var control)) - continue; - - // Controls may have been previously hidden via HideControls due to being "out-of frame". - // If this ever gets replaced with RectClipContents / scissor box testing, this can be removed. - control.Visible = true; - - var invertedScale = 1f / uiScale; - var pos = new Vector2(baseLine.X * invertedScale, (baseLine.Y - defaultFont.GetAscent(uiScale)) * invertedScale); - LayoutContainer.SetPosition(control, pos); - control.Measure(new Vector2(Width, Height)); - var advanceX = control.DesiredPixelSize.X; - controlYAdvance = Math.Max(0f, (control.DesiredPixelSize.Y - GetLineHeight(font, uiScale, lineHeightScale)) * invertedScale); - baseLine += new Vector2(advanceX, 0); - } - } - - private readonly string ProcessNode(MarkupTagManager tagManager, MarkupNode node, MarkupDrawingContext context) - { - // If a nodes name is null it's a text node. - if (node.Name == null) - return node.Value.StringValue ?? ""; - - //Skip the node if there is no markup tag for it. - if (!tagManager.TryGetMarkupTag(node.Name, _tagsAllowed, out var tag)) - return ""; - - if (!node.Closing) - { - tag.PushDrawContext(node, context); - return tag.TextBefore(node); - } - - tag.PopDrawContext(node, context); - return tag.TextAfter(node); - } - - private static int GetLineHeight(Font font, float uiScale, float lineHeightScale) - { - var height = font.GetLineHeight(uiScale); - return (int)(height * lineHeightScale); - } -} diff --git a/Content.Client/_Sunrise/UI/Controls/SunriseRingBufferList.cs b/Content.Client/_Sunrise/UI/Controls/SunriseRingBufferList.cs deleted file mode 100644 index 33d068f992d..00000000000 --- a/Content.Client/_Sunrise/UI/Controls/SunriseRingBufferList.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System.Collections; -using System.Runtime.CompilerServices; -using Robust.Shared.Utility; - -namespace Content.Client._Sunrise.UI.Controls; - -public sealed class SunriseRingBufferList : IList -{ - private T[] _items; - private int _read; - private int _write; - - public SunriseRingBufferList(int capacity) - { - _items = new T[capacity]; - } - - public SunriseRingBufferList() - { - _items = []; - } - - public int Capacity => _items.Length; - - private bool IsFull => _items.Length == 0 || NextIndex(_write) == _read; - - public void Add(T item) - { - if (IsFull) - Expand(); - - DebugTools.Assert(!IsFull); - - _items[_write] = item; - _write = NextIndex(_write); - } - - public void Clear() - { - _read = 0; - _write = 0; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - Array.Clear(_items); - } - - public bool Contains(T item) - { - return IndexOf(item) >= 0; - } - - public void CopyTo(T[] array, int arrayIndex) - { - ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); - - CopyTo(array.AsSpan(arrayIndex)); - } - - private void CopyTo(Span dest) - { - if (dest.Length < Count) - throw new ArgumentException("Not enough elements in destination!"); - - var i = 0; - foreach (var item in this) - { - dest[i++] = item; - } - } - - public bool Remove(T item) - { - var index = IndexOf(item); - if (index < 0) - return false; - - RemoveAt(index); - return true; - } - - public int Count - { - get - { - var length = _write - _read; - if (length >= 0) - return length; - - return length + _items.Length; - } - } - - public bool IsReadOnly => false; - - public int IndexOf(T item) - { - var i = 0; - foreach (var containedItem in this) - { - if (EqualityComparer.Default.Equals(item, containedItem)) - return i; - - i += 1; - } - - return -1; - } - - public void Insert(int index, T item) - { - throw new NotSupportedException(); - } - - public void RemoveAt(int index) - { - var length = Count; - ArgumentOutOfRangeException.ThrowIfNegative(index); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, length); - - if (index == 0) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - _items[_read] = default!; - - _read = NextIndex(_read); - } - else if (index == length - 1) - { - _write = WrapInv(_write - 1); - - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - _items[_write] = default!; - } - else - { - // If past me had better foresight I wouldn't be spending so much effort writing this right now. - - var realIdx = RealIndex(index); - var origValue = _items[realIdx]; - T result; - - if (realIdx < _read) - { - // Scenario one: to-remove index is after break. - // One shift is needed. - // v - // X X X O X X - // W R - DebugTools.Assert(_write < _read); - - result = ShiftDown(_items.AsSpan()[realIdx.._write], default!); - } - else if (_write < _read) - { - // Scenario two: to-remove index is before break, but write is after. - // Two shifts are needed. - // v - // X O X X X X - // W R - - var fromEnd = ShiftDown(_items.AsSpan(0, _write), default!); - result = ShiftDown(_items.AsSpan(realIdx), fromEnd); - } - else - { - // Scenario two: array is contiguous. - // One shift is needed. - // v - // X X X X O O - // R W - - result = ShiftDown(_items.AsSpan()[realIdx.._write], default!); - } - - // Just make sure we didn't bulldozer something. - DebugTools.Assert(EqualityComparer.Default.Equals(origValue, result)); - - _write = WrapInv(_write - 1); - } - } - - private static T ShiftDown(Span span, T substitution) - { - if (span.Length == 0) - return substitution; - - var first = span[0]; - span[1..].CopyTo(span[..^1]); - span[^1] = substitution!; - return first; - } - - private T GetSlot(int index) - { - ArgumentOutOfRangeException.ThrowIfNegative(index); - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Count); - - return _items[RealIndex(index)]; - } - - public T this[int index] - { - get => GetSlot(index); - set => _items[RealIndex(index)] = value; - } - - private int RealIndex(int index) - { - return Wrap(index + _read); - } - - private int NextIndex(int index) => Wrap(index + 1); - - private int Wrap(int index) - { - if (index >= _items.Length) - index -= _items.Length; - - return index; - } - - private int WrapInv(int index) - { - if (index < 0) - index = _items.Length - 1; - - return index; - } - - private void Expand() - { - var prevSize = _items.Length; - var newSize = Math.Max(4, prevSize * 2); - Array.Resize(ref _items, newSize); - - if (_write >= _read) - return; - - // Write is behind read pointer, so we need to copy the items to be after the read pointer. - var toCopy = _items.AsSpan(0, _write); - var copyDest = _items.AsSpan(prevSize); - toCopy.CopyTo(copyDest); - - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - toCopy.Clear(); - - _write += prevSize; - } - - public Enumerator GetEnumerator() - { - return new Enumerator(this); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public struct Enumerator : IEnumerator - { - private readonly SunriseRingBufferList _ringBufferList; - private int _readPos; - - internal Enumerator(SunriseRingBufferList ringBufferList) - { - _ringBufferList = ringBufferList; - _readPos = _ringBufferList._read - 1; - } - - public bool MoveNext() - { - _readPos = _ringBufferList.NextIndex(_readPos); - return _readPos != _ringBufferList._write; - } - - public void Reset() - { - this = new Enumerator(_ringBufferList); - } - - public ref T Current => ref _ringBufferList._items[_readPos]; - - T IEnumerator.Current => Current; - object? IEnumerator.Current => Current; - - void IDisposable.Dispose() - { - } - } -} diff --git a/Content.Client/_Sunrise/UI/Controls/SunriseWordWrap.cs b/Content.Client/_Sunrise/UI/Controls/SunriseWordWrap.cs deleted file mode 100644 index da14e701edd..00000000000 --- a/Content.Client/_Sunrise/UI/Controls/SunriseWordWrap.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Diagnostics.Contracts; -using System.Text; -using Robust.Client.Graphics; -using Robust.Shared.Utility; - -namespace Content.Client._Sunrise.UI.Controls; - -internal struct SunriseWordWrap -{ - private readonly float _maxSizeX; - - public float MaxUsedWidth; - // Index we put into the LineBreaks list when a line break should occur. - public int BreakIndexCounter; - public int NextBreakIndexCounter; - // If the CURRENT processing word ends up too long, this is the index to put a line break. - public (int index, float lineSize)? WordStartBreakIndex; - // Word size in pixels. - public int WordSizePixels; - // The horizontal position of the text cursor. - public int PosX; - public Rune LastRune; - // If a word is larger than maxSizeX, we split it. - // We need to keep track of some data to split it into two words. - public (int breakIndex, int wordSizePixels)? ForceSplitData = null; - - public SunriseWordWrap(float maxSizeX) - { - this = default; - _maxSizeX = maxSizeX; - LastRune = new Rune('A'); - } - - public void NextRune(Rune rune, out int? breakLine, out int? breakNewLine, out bool skip) - { - BreakIndexCounter = NextBreakIndexCounter; - NextBreakIndexCounter += rune.Utf16SequenceLength; - - breakLine = null; - breakNewLine = null; - skip = false; - - if (IsWordBoundary(LastRune, rune) || rune == new Rune('\n')) - { - // Word boundary means we know where the word ends. - if (PosX > _maxSizeX && LastRune != new Rune(' ')) - { - DebugTools.Assert(WordStartBreakIndex.HasValue, - "wordStartBreakIndex can only be null if the word begins at a new line, in which case this branch shouldn't be reached as the word would be split due to being longer than a single line."); - //Ensure the assert had a chance to run and then just return - if (!WordStartBreakIndex.HasValue) - return; - - // We ran into a word boundary and the word is too big to fit the previous line. - // So we insert the line break BEFORE the last word. - breakLine = WordStartBreakIndex!.Value.index; - MaxUsedWidth = Math.Max(MaxUsedWidth, WordStartBreakIndex.Value.lineSize); - PosX = WordSizePixels; - } - - // Start a new word since we hit a word boundary. - //wordSize = 0; - WordSizePixels = 0; - WordStartBreakIndex = (BreakIndexCounter, PosX); - ForceSplitData = null; - - // Just manually handle newlines. - if (rune == new Rune('\n')) - { - MaxUsedWidth = Math.Max(MaxUsedWidth, PosX); - PosX = 0; - WordStartBreakIndex = null; - skip = true; - breakNewLine = BreakIndexCounter; - } - } - - LastRune = rune; - } - - public void NextMetrics(in CharMetrics metrics, out int? breakLine, out bool abort) - { - abort = false; - breakLine = null; - - // Increase word size and such with the current character. - var oldWordSizePixels = WordSizePixels; - WordSizePixels += metrics.Advance; - // TODO: Theoretically, does it make sense to break after the glyph's width instead of its advance? - // It might result in some more tight packing but I doubt it'd be noticeable. - // Also definitely even more complex to implement. - PosX += metrics.Advance; - - if (PosX <= _maxSizeX) - return; - - if (!ForceSplitData.HasValue) - { - ForceSplitData = (BreakIndexCounter, oldWordSizePixels); - } - - // Oh hey we get to break a word that doesn't fit on a single line. - if (WordSizePixels > _maxSizeX) - { - var (breakIndex, splitWordSize) = ForceSplitData.Value; - if (splitWordSize == 0) - { - // Happens if there's literally not enough space for a single character so uh... - // Yeah just don't. - abort = true; - return; - } - - // Reset forceSplitData so that we can split again if necessary. - ForceSplitData = null; - breakLine = breakIndex; - WordSizePixels -= splitWordSize; - WordStartBreakIndex = null; - MaxUsedWidth = Math.Max(MaxUsedWidth, _maxSizeX); - PosX = WordSizePixels; - } - } - - public int FinalizeText(out int? breakLine) - { - // This needs to happen because word wrapping doesn't get checked for the last word. - if (PosX > _maxSizeX) - { - if (!WordStartBreakIndex.HasValue) - { - Logger.Error( - "Assert fail inside RichTextEntry.Update, " + - "wordStartBreakIndex is null on method end w/ word wrap required. " + - "Dumping relevant stuff. Send this to PJB."); - // Logger.Error($"Message: {Message}"); - Logger.Error($"maxSizeX: {_maxSizeX}"); - Logger.Error($"maxUsedWidth: {MaxUsedWidth}"); - Logger.Error($"breakIndexCounter: {BreakIndexCounter}"); - Logger.Error("wordStartBreakIndex: null (duh)"); - Logger.Error($"wordSizePixels: {WordSizePixels}"); - Logger.Error($"posX: {PosX}"); - Logger.Error($"lastChar: {LastRune}"); - Logger.Error($"forceSplitData: {ForceSplitData}"); - // Logger.Error($"LineBreaks: {string.Join(", ", LineBreaks)}"); - - throw new Exception( - "wordStartBreakIndex can only be null if the word begins at a new line," + - "in which case this branch shouldn't be reached as" + - "the word would be split due to being longer than a single line."); - } - - breakLine = WordStartBreakIndex.Value.index; - MaxUsedWidth = Math.Max(MaxUsedWidth, WordStartBreakIndex.Value.lineSize); - } - else - { - breakLine = null; - MaxUsedWidth = Math.Max(MaxUsedWidth, PosX); - } - - return (int)MaxUsedWidth; - } - - [Pure] - private static bool IsWordBoundary(Rune a, Rune b) - { - return a == new Rune(' ') || b == new Rune(' ') || a == new Rune('-') || b == new Rune('-'); - } - -} diff --git a/Content.Shared/Chat/MsgChatMessage.cs b/Content.Shared/Chat/MsgChatMessage.cs index 5bf6ff45718..85367ffb739 100644 --- a/Content.Shared/Chat/MsgChatMessage.cs +++ b/Content.Shared/Chat/MsgChatMessage.cs @@ -37,7 +37,6 @@ public sealed class ChatMessage public Color? MessageColorOverride; public string? AudioPath; public float AudioVolume; - public int repeat = 1; [NonSerialized] public bool Read;