4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
16 private Point screenResolution;
22 private bool _toggleOpen =
true;
25 get {
return _toggleOpen; }
29 if (value) { hideableElements.
Visible =
true; }
32 private float openState;
38 private float prevUIScale;
40 private readonly
GUIFrame channelSettingsFrame;
47 private bool channelMemPending;
50 const float PopupMessageDuration = 5.0f;
51 private readonly List<GUIComponent> popupMessages =
new List<GUIComponent>();
70 if (toggleButton !=
null)
78 private readonly
GUIButton showNewMessagesButton;
80 private readonly
GUIFrame hideableElements;
83 private int popupMessageOffset;
98 var chatBoxHolder =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.875f), hideableElements.RectTransform), style:
"ChatBox");
103 channelSettingsFrame =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.2f), chatBoxHolder.RectTransform,
Anchor.TopCenter,
Pivot.BottomCenter) { MinSize = new Point(0, 25) }, style:
"GUIFrameBottom");
104 var channelSettingsContent =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.9f), channelSettingsFrame.RectTransform,
Anchor.Center), isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
108 RelativeSpacing = 0.01f
111 radioJammedWarning =
new GUITextBlock(
new RectTransform(Vector2.One, channelSettingsFrame.RectTransform), TextManager.Get(
"radiojammedwarning"),
112 textColor: GUIStyle.Orange, color: Color.Black,
113 textAlignment: Alignment.Center, style:
"OuterGlow")
115 ToolTip = TextManager.Get(
"hint.radiojammed")
118 var buttonLeft =
new GUIButton(
new RectTransform(
new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style:
"DeviceButton")
120 PlaySoundOnSelect =
false,
121 OnClicked = (btn, userdata) =>
125 SetChannel(radio.Channel - 1, setText:
true);
131 var arrowIcon =
new GUIImage(
new RectTransform(
new Vector2(0.4f), buttonLeft.RectTransform,
Anchor.Center), style:
"GUIButtonHorizontalArrow", scaleToFit:
true)
133 Color =
new Color(51, 59, 46),
134 SpriteEffects = Microsoft.Xna.Framework.Graphics.
SpriteEffects.FlipHorizontally
136 arrowIcon.HoverColor = arrowIcon.PressedColor = arrowIcon.SelectedColor = arrowIcon.Color;
138 channelText =
new GUITextBox(
new RectTransform(
new Vector2(0.25f, 0.8f), channelSettingsContent.RectTransform), style:
"DigitalFrameLight", textAlignment: Alignment.Center, font: GUIStyle.DigitalFont)
140 textFilterFunction = text =>
142 var str =
new string(text.Where(c =>
char.IsNumber(c)).ToArray());
143 if (str.Length > 4) { str = str.Substring(0, 4); }
146 OnEnterPressed = (tb, text) =>
152 Vector2 textSize = channelText.Font.MeasureString(
"0000");
153 channelText.TextBlock.ToolTip = TextManager.Get(
"currentradiochannel");
154 channelText.TextBlock.TextScale = Math.Min(channelText.Rect.Height / textSize.Y * 0.9f, 1.0f);
155 channelText.OnDeselected += (sender, key) =>
157 int.TryParse(channelText.Text, out
int newChannel);
158 SetChannel(newChannel, setText:
true);
162 var buttonRight =
new GUIButton(
new RectTransform(
new Vector2(0.1f, 0.8f), channelSettingsContent.RectTransform), style:
"DeviceButton")
164 PlaySoundOnSelect =
false,
165 OnClicked = (btn, userdata) =>
169 SetChannel(radio.Channel + 1, setText:
true);
175 arrowIcon =
new GUIImage(
new RectTransform(
new Vector2(0.4f), buttonRight.RectTransform,
Anchor.Center), style:
"GUIButtonHorizontalArrow", scaleToFit:
true)
177 Color =
new Color(51, 59, 46)
179 arrowIcon.
HoverColor = arrowIcon.PressedColor = arrowIcon.PressedColor = arrowIcon.Color;
181 var channelPicker =
new GUIFrame(
new RectTransform(
new Vector2(0.4f, 0.6f), channelSettingsContent.RectTransform),
"InnerFrame");
186 for (
int i = 0; i < 10; i++)
188 new GUIButton(
new RectTransform(
new Vector2(0.1f, 1.0f), channelPickerContent.RectTransform), i.ToString(), style:
"GUITextBlock")
190 TextColor =
new Color(51, 59, 46),
191 SelectedTextColor = GUIStyle.Green,
193 PlaySoundOnSelect =
false,
194 OnClicked = (btn, userdata) =>
198 int index = (int)userdata;
199 if (channelMemPending)
201 int.TryParse(channelText.Text, out
int newChannel);
202 radio.SetChannelMemory(index, newChannel);
203 btn.
ToolTip = TextManager.GetWithVariables(
"radiochannelpreset",
204 (
"[index]", index.ToString()),
205 (
"[channel]", radio.GetChannelMemory(index).ToString()));
206 channelMemPending =
false;
207 channelPickerContent.Children.First().CanBeFocused =
true;
208 memButton.Enabled =
true;
209 channelPickerContent.Flash(GUIStyle.Green);
210 channelText.Flash(GUIStyle.Green);
212 SetChannel(radio.GetChannelMemory(index), setText:
true);
220 memButton =
new GUIButton(
new RectTransform(
new Vector2(0.2f, 0.9f), channelSettingsContent.RectTransform), style:
"DeviceButton", text: TextManager.Get(
"saveradiochannelbutton"))
222 ToolTip = TextManager.Get(
"saveradiochannelbuttontooltip"),
223 OnClicked = (btn, userdata) =>
225 channelMemPending =
true;
227 channelPickerContent.
Children.First().CanBeFocused =
false;
244 var dropdownRt =
new RectTransform(
new Vector2(0.1f, 1.0f), bottomContainer.RectTransform)
247 MaxSize =
new Point((
int)(0.45f * bottomContainer.RectTransform.NonScaledSize.X),
int.MaxValue)
250 ChatModeDropDown =
new GUIDropDown(dropdownRt, elementCount: chatModes.Length, dropAbove:
true)
252 OnSelected = (component, userdata) =>
263 float longestDropDownOption = 0.0f;
264 foreach (
ChatMode mode
in chatModes)
266 var text = TextManager.Get($
"chatmode.{mode}");
267 ChatModeDropDown.
AddItem(text, userData: mode);
270 if (textBlock.TextSize.X > longestDropDownOption)
272 longestDropDownOption = textBlock.TextSize.X;
278 float minDropDownWidth = longestDropDownOption + ChatModeDropDown.
Padding.X +
286 style:
"ChatTextBox")
289 Font = GUIStyle.SmallFont,
304 if (
string.IsNullOrEmpty(message))
315 chatSendButton.OnClicked += (
GUIButton btn,
object userdata) =>
320 chatSendButton.RectTransform.AbsoluteOffset =
new Point((
int)(
InputBox.
Rect.Height * 0.15f), 0);
325 showNewMessagesButton.OnClicked += (
GUIButton btn,
object userdata) =>
327 chatBox.ScrollBar.BarScrollValue = 1f;
328 showNewMessagesButton.Visible =
false;
332 showNewMessagesButton.Visible =
false;
348 if (command !=
"r" && command !=
"radio")
370 else if (command !=
"")
394 if (should !=
null && should.Value) {
return; }
397 while (chatBox.Content.CountChildren > 60)
399 chatBox.RemoveChild(chatBox.Content.Children.First());
402 float prevSize = chatBox.BarSize;
406 string senderName =
"";
407 Color senderColor = Color.White;
408 if (!
string.IsNullOrWhiteSpace(message.
SenderName))
417 var msgHolder =
new GUIFrame(
new RectTransform(
new Vector2(0.95f, 0.0f), chatBox.Content.RectTransform,
Anchor.TopCenter), style:
null,
418 color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f);
421 ChatMessage.
GetTimeStamp(), textColor: Color.LightGray, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft, style:
null)
425 if (!
string.IsNullOrEmpty(senderName))
427 var senderNameBlock =
new GUIButton(
new RectTransform(
new Vector2(0.8f, 1.0f), senderNameTimestamp.
RectTransform) { AbsoluteOffset = new Point((int)(senderNameTimestamp.TextSize.X), 0) },
428 senderName, textAlignment: Alignment.
TopLeft, style:
null, color: Color.Transparent)
432 Padding = Vector4.Zero
434 Font = GUIStyle.SmallFont,
438 PlaySoundOnSelect =
false,
439 OnClicked = (_, o) =>
441 if (!(o is
Client client)) {
return false; }
449 OnSecondaryClicked = (_, o) =>
451 if (!(o is
Client client)) {
return false; }
458 senderNameBlock.RectTransform.NonScaledSize = senderNameBlock.TextBlock.TextSize.ToPoint();
459 senderNameBlock.TextBlock.OverrideTextColor(senderColor);
460 if (senderNameBlock.UserData !=
null)
462 senderNameBlock.TextBlock.HoverTextColor = Color.White;
467 { AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameTimestamp == null ? 0 : senderNameTimestamp.Rect.Height) },
468 RichString.
Rich(displayedText), textColor: message.
Color, font: GUIStyle.SmallFont, textAlignment: Alignment.TopLeft, style:
null, wrap:
true,
469 color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
474 msgText.CalculateHeightFromText();
475 if (msgText.RichTextData !=
null)
477 foreach (var data
in msgText.RichTextData)
488 msgText.ClickableAreas.Add(clickableArea);
496 msgHolder.Flash(Color.OrangeRed * 0.6f, flashDuration: 5.0f);
500 msgHolder.Flash(Color.Yellow * 0.6f);
502 msgHolder.RectTransform.SizeChanged += Recalculate;
506 msgHolder.RectTransform.SizeChanged -= Recalculate;
508 msgText.RectTransform.MaxSize =
new Point(msgHolder.Rect.Width - msgText.RectTransform.AbsoluteOffset.X,
int.MaxValue);
510 msgHolder.Children.ForEach(c => (c as
GUITextBlock)?.CalculateHeightFromText());
511 msgHolder.RectTransform.Resize(
new Point(msgHolder.Rect.Width, msgHolder.Children.Sum(c => c.Rect.Height) + (
int)(10 * GUI.Scale)), resizeChildren:
false);
512 msgHolder.RectTransform.SizeChanged += Recalculate;
513 chatBox.RecalculateChildren();
514 chatBox.UpdateScrollBarSize();
517 CoroutineManager.StartCoroutine(UpdateMessageAnimation(msgHolder, 0.5f));
519 chatBox.UpdateScrollBarSize();
521 if (chatBox.ScrollBar.Visible && chatBox.ScrollBar.BarScroll < 1f)
523 showNewMessagesButton.Visible =
true;
539 Vector2 senderTextSize = Vector2.Zero;
540 if (!
string.IsNullOrEmpty(senderName))
543 senderName, textColor: senderColor, style:
null, font: GUIStyle.SmallFont)
548 senderText.RectTransform.MinSize =
new Point(0, senderText.Rect.Height);
551 RichString.
Rich(displayedText), textColor: message.
Color, font: GUIStyle.SmallFont, textAlignment: Alignment.BottomLeft, style:
null, wrap:
true)
556 Vector2 msgSize = msgPopupText.Font.MeasureString(msgPopupText.WrappedText);
557 int textWidth = (int)Math.Max(msgSize.X + msgPopupText.Padding.X + msgPopupText.Padding.Z, senderTextSize.X) + 10;
558 popupMsg.RectTransform.Resize(
new Point((
int)(textWidth / content.RectTransform.RelativeSize.X) , (
int)((senderTextSize.Y + msgSize.Y) / content.RectTransform.RelativeSize.Y)), resizeChildren:
true);
559 popupMsg.RectTransform.IsFixedSize =
true;
560 content.Recalculate();
561 popupMessages.Add(popupMsg);
564 if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) { chatBox.BarScroll = 1.0f; }
576 SoundPlayer.PlayUISound(soundType);
584 private IEnumerable<CoroutineStatus> UpdateMessageAnimation(
GUIComponent message,
float animDuration)
587 while (timer < animDuration)
589 timer += CoroutineManager.DeltaTime;
590 float wavePhase = timer / animDuration * MathHelper.TwoPi;
592 new Point((
int)(Math.Sin(wavePhase) * (1.0f - timer / animDuration) * 50.0f), 0);
596 yield
return CoroutineStatus.Success;
599 private void SetUILayout()
603 HUDLayoutSettings.ChatBoxArea.X / (
float)GameMain.GraphicsWidth,
604 HUDLayoutSettings.ChatBoxArea.Y / (
float)GameMain.GraphicsHeight);
611 popupMessageOffset = GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width +
GUIFrame.
Rect.Width + (int)(35 * GUI.Scale);
620 prevUIScale = GUI.Scale;
623 if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f)
625 showNewMessagesButton.Visible =
false;
638 _ =>
throw new NotImplementedException()
640 ChatModeDropDown.SelectItem(mode);
643 catch (NotImplementedException)
645 DebugConsole.ThrowError($
"Error toggling chat mode: not implemented for current mode \"{GameMain.ActiveChatMode}\"");
662 if (prevRadio != radio)
666 int index = (int)presetButton.
UserData;
667 presetButton.
ToolTip = TextManager.GetWithVariables(
"radiochannelpreset",
668 (
"[index]", index.ToString()),
669 (
"[channel]", radio.GetChannelMemory(index).ToString()));
671 SetChannel(radio.Channel, setText:
true);
674 if (channelMemPending)
676 if (channelPickerContent.FlashTimer <= 0)
678 channelPickerContent.Flash(GUIStyle.Green, flashRectInflate:
new Vector2(GUI.Scale * 5.0f));
682 channelPickerContent.Children.First().CanBeFocused =
true;
683 channelMemPending =
false;
684 memButton.Enabled =
true;
685 SetChannel(radio.Channel, setText:
true);
688 channelSettingsFrame.Visible =
true;
689 radioJammedWarning.Visible = radio is { JamTimer: > 0 };
693 radioJammedWarning.Visible = channelSettingsFrame.Visible =
false;
694 channelPickerContent.Children.First().CanBeFocused =
true;
695 channelMemPending =
false;
696 memButton.Enabled =
true;
702 openState += deltaTime * 5.0f;
704 foreach (var popupMsg
in popupMessages)
706 popupMsg.Parent.RemoveChild(popupMsg);
708 popupMessages.Clear();
713 openState -= deltaTime * 5.0f;
716 foreach (var popupMsg
in popupMessages)
718 float msgTimer = (float)popupMsg.UserData;
720 int targetYOffset = (
int)MathHelper.Lerp(popupMsg.RectTransform.ScreenSpaceOffset.Y, yOffset, deltaTime * 10.0f);
722 if (popupMsg == popupMessages.First())
724 popupMsg.UserData = msgTimer + deltaTime * Math.Max(popupMessages.Count / 2, 1);
725 if (msgTimer > PopupMessageDuration)
728 popupMsg.RectTransform.ScreenSpaceOffset =
729 new Point((
int)MathHelper.SmoothStep(popupMessageOffset, 10, (msgTimer - PopupMessageDuration) * 5.0f), targetYOffset);
730 if (msgTimer > PopupMessageDuration + 0.2f)
732 popupMsg.Parent?.RemoveChild(popupMsg);
736 if (msgTimer < PopupMessageDuration)
738 if (popupMsg != popupMessages.First()) { popupMsg.UserData = Math.Min(msgTimer + deltaTime, 1.0f); }
740 popupMsg.RectTransform.ScreenSpaceOffset =
new Point(
741 (
int)MathHelper.SmoothStep(0, popupMessageOffset, msgTimer * 5.0f), targetYOffset);
743 yOffset += popupMsg.Rect.Height + GUI.IntScale(10);
746 popupMessages.RemoveAll(p => p.Parent ==
null);
775 openState = MathHelper.Clamp(openState, 0.0f, 1.0f);
778 new Point((
int)MathHelper.SmoothStep(hiddenBoxOffset, 0, openState), 0);
779 hideableElements.Visible = openState > 0.0f;
782 private void SetChannel(
int channel,
bool setText)
786 radio.Channel = channel;
791 string text = radio.Channel.ToString().PadLeft(4,
'0');
792 if (channelText.Text != text) { channelText.Text = text; }
795 if (!channelMemPending)
797 foreach (GUIComponent channelButton
in channelPickerContent.Children)
799 int buttonIndex = (int)channelButton.UserData;
800 channelButton.Selected = radio.GetChannelMemory(buttonIndex) == channel;
838 var (activeChatKeyHit, localChatKeyHit, radioChatKeyHit) = chatKeyStates.
Deconstruct();
841 ChatModeDropDown.SelectItem(
ChatMode.Local);
856 ChatModeDropDown.SelectItem(
ChatMode.Radio);
static Character? Controlled
void ApplySelectionInputs()
void SetVisibility(bool visible)
GUITextBox.OnEnterHandler OnEnterMessage
static bool PreferChatBoxOpen
void Update(float deltaTime)
const int ToggleButtonWidthRaw
bool CloseAfterMessageSent
const string RadioChatString
readonly ChatManager ChatManager
void ApplySelectionInputs(GUITextBox inputBox, bool selectInputBox, ChatKeyStates chatKeyStates)
ChatBox(GUIComponent parent, bool isSinglePlayer)
void AddMessage(ChatMessage message)
bool TypingChatMessage(GUITextBox textBox, string text)
A class used for handling special key actions in chat boxes. For example tab completion or up/down ar...
static void RegisterKeys(GUITextBox element, ChatManager manager)
Registers special input actions to the selected input field
void Clear()
Call this function whenever we should stop doing special stuff and return normal behavior....
static CoroutineStatus Running
virtual void Flash(Color? color=null, float flashDuration=1.5f, bool useRectangleFlash=false, bool useCircularFlash=false, Vector2? flashRectInflate=null)
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
bool IsParentOf(GUIComponent component, bool recursive=true)
GUIComponent GetChildByUserData(object obj)
virtual RichString ToolTip
RectTransform RectTransform
SpriteEffects SpriteEffects
IEnumerable< GUIComponent > Children
GUIComponent AddItem(LocalizedString text, object userData=null, LocalizedString toolTip=null, Color? color=null, Color? textColor=null)
Vector2 MeasureString(LocalizedString str, bool removeExtraSpacing=false)
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
TextBoxEvent OnDeselected
delegate bool OnEnterHandler(GUITextBox textBox, string text)
void Select(int forcedCaretIndex=-1, bool ignoreSelectSound=false)
OnEnterHandler OnEnterPressed
static NetLobbyScreen NetLobbyScreen
static int GraphicsHeight
static ChatMode ActiveChatMode
static bool IsSingleplayer
static GameScreen GameScreen
static NetworkMember NetworkMember
object Call(string name, params object[] args)
static string GetChatMessageCommand(string message, out string messageWithoutCommand)
static Color[] MessageColor
readonly string SenderName
static string GetTimeStamp()
PlayerConnectionChangeType ChangeType
readonly Client SenderClient
Character SenderCharacter
static bool CanUseRadio(Character sender, bool ignoreJamming=false)
override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData=null)
static RichString Rich(LocalizedString str, Func< string, string >? postProcess=null)
PlayerConnectionChangeType
static ChatKeyStates GetChatKeyStates()
bool bool bool radio Deconstruct()
static void CreateModerationContextMenu(Client client)
void ShowPlayerContextMenu(GUITextBlock component, GUITextBlock.ClickableArea area)
void SelectPlayer(GUITextBlock component, GUITextBlock.ClickableArea area)