using Barotrauma.Extensions; using Barotrauma.Networking; using Microsoft.Xna.Framework; using System.Linq; using System.Xml.Linq; namespace Barotrauma.Items.Components { partial class Terminal : ItemComponent, IClientSerializable, IServerSerializable { private readonly struct ClientEventData : IEventData { public readonly string Text; public ClientEventData(string text) { Text = text; } } private GUIListBox historyBox; private GUITextBlock fillerBlock; private GUITextBox inputBox; private bool shouldSelectInputBox; partial void InitProjSpecific(XElement element) { float marginMultiplier = element.GetAttributeFloat("marginmultiplier", 1.0f); var layoutGroup = new GUILayoutGroup(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin.Multiply(marginMultiplier), GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset.Multiply(marginMultiplier) }) { ChildAnchor = Anchor.TopCenter, RelativeSpacing = 0.02f, Stretch = true }; historyBox = new GUIListBox(new RectTransform(new Vector2(1, .9f), layoutGroup.RectTransform), style: null) { AutoHideScrollBar = this.AutoHideScrollbar }; if (!Readonly) { CreateFillerBlock(); new GUIFrame(new RectTransform(new Vector2(0.9f, 0.01f), layoutGroup.RectTransform), style: "HorizontalLine"); inputBox = new GUITextBox(new RectTransform(new Vector2(1, .1f), layoutGroup.RectTransform), textColor: TextColor) { MaxTextLength = MaxMessageLength, OverflowClip = true, OnEnterPressed = (GUITextBox textBox, string text) => { if (GameMain.NetworkMember == null) { SendOutput(text); } else { item.CreateClientEvent(this, new ClientEventData(text)); } textBox.Text = string.Empty; return true; } }; } layoutGroup.Recalculate(); } // Create fillerBlock to cover historyBox so new values appear at the bottom of historyBox // This could be removed if GUIListBox supported aligning its children public void CreateFillerBlock() { fillerBlock = new GUITextBlock(new RectTransform(new Vector2(1, 1), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), string.Empty) { CanBeFocused = false }; } private void SendOutput(string input) { if (input.Length > MaxMessageLength) { input = input.Substring(0, MaxMessageLength); } OutputValue = input; ShowOnDisplay(input, addToHistory: true, TextColor, isWelcomeMessage: false); item.SendSignal(input, "signal_out"); } partial void ShowOnDisplay(string input, bool addToHistory, Color color, bool isWelcomeMessage) { if (addToHistory) { messageHistory.Add(new TerminalMessage(input, color, isWelcomeMessage)); while (messageHistory.Count > MaxMessages) { messageHistory.RemoveAt(0); } while (historyBox.Content.CountChildren > MaxMessages) { historyBox.RemoveChild(historyBox.Content.Children.First()); } } GUITextBlock newBlock = new GUITextBlock( new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter), LineStartSymbol + TextManager.Get(input).Fallback(input), textColor: color, wrap: true, font: UseMonospaceFont ? GUIStyle.MonospacedFont : GUIStyle.Font) { CanBeFocused = false }; if (fillerBlock != null) { float y = fillerBlock.RectTransform.RelativeSize.Y - newBlock.RectTransform.RelativeSize.Y; if (y > 0) { fillerBlock.RectTransform.RelativeSize = new Vector2(1, y); } else { historyBox.RemoveChild(fillerBlock); fillerBlock = null; } } historyBox.RecalculateChildren(); historyBox.UpdateScrollBarSize(); if (AutoScrollToBottom) { historyBox.ScrollBar.BarScrollValue = 1; } } public override bool Select(Character character) { shouldSelectInputBox = true; return base.Select(character); } // This method is overrided instead of the UpdateHUD method because this ensures the input box is selected // even when the terminal component is selected for the very first time. Doing the input box selection in the // UpdateHUD method only selects the input box on every terminal selection except for the very first time. public override void AddToGUIUpdateList(int order = 0) { base.AddToGUIUpdateList(order: order); if (shouldSelectInputBox && !Readonly) { inputBox.Select(); shouldSelectInputBox = false; } } public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null) { if (TryExtractEventData(extraData, out ClientEventData eventData)) { msg.WriteString(eventData.Text); } } public void ClientEventRead(IReadMessage msg, float sendingTime) { SendOutput(msg.ReadString()); } } }