using Content.Client.UserInterface.Systems.Actions;
using Content.Client.UserInterface.Systems.Alerts;
using Content.Client.UserInterface.Systems.Chat;
+using Content.Client.UserInterface.Systems.Chat.Widgets;
using Content.Client.UserInterface.Systems.Ghost;
using Content.Client.UserInterface.Systems.Hands;
using Content.Client.UserInterface.Systems.Hotbar;
{
case ScreenType.Default:
_uiManager.LoadScreen<DefaultGameScreen>();
+
break;
case ScreenType.Separated:
_uiManager.LoadScreen<SeparatedChatGameScreen>();
using Content.Client.Info;
using Content.Client.Preferences;
using Content.Client.Preferences.UI;
+using Content.Client.UserInterface.Screens;
+using Content.Client.UserInterface.Systems.Chat.Widgets;
using Content.Client.UserInterface.Systems.EscapeMenu;
using Robust.Client.AutoGenerated;
using Robust.Client.Console;
--- /dev/null
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Shared.Input;
+
+namespace Content.Client.UserInterface.Controls;
+
+/// <summary>
+/// A split container that performs an action when the split resizing is finished.
+/// </summary>
+public sealed class RecordedSplitContainer : SplitContainer
+{
+ public Action<Vector2, Vector2>? OnSplitResizeFinish;
+
+ public double? DesiredSplitCenter;
+
+ protected override Vector2 ArrangeOverride(Vector2 finalSize)
+ {
+ if (ResizeMode == SplitResizeMode.RespectChildrenMinSize
+ && DesiredSplitCenter != null)
+ {
+ var secondMin = GetChild(1).DesiredSize;
+ double minSize = Orientation == SplitOrientation.Vertical
+ ? secondMin.Y
+ : secondMin.X;
+ double finalSizeComponent = Orientation == SplitOrientation.Vertical
+ ? finalSize.Y
+ : finalSize.X;
+
+ var firstTotalFractional = (finalSizeComponent - minSize - SplitWidth - SplitEdgeSeparation) / finalSizeComponent;
+ DesiredSplitCenter = Math.Round(DesiredSplitCenter.Value, 2, MidpointRounding.ToZero);
+
+ // total space the split center takes up must fit the available space percentage given to the first child
+ var canFirstFit = DesiredSplitCenter <= firstTotalFractional;
+
+ if (DesiredSplitCenter > 1 || DesiredSplitCenter < 0 || !canFirstFit)
+ {
+ DesiredSplitCenter = Math.Round(firstTotalFractional, 2, MidpointRounding.ToZero);
+ }
+
+ // don't need anything more than two digits of precision for this
+ var currentSplitFraction = Math.Round(SplitFraction, 2, MidpointRounding.ToZero);
+
+ // brute force it
+ if (currentSplitFraction != DesiredSplitCenter.Value)
+ {
+ SplitFraction = (float) DesiredSplitCenter.Value;
+ }
+ else
+ {
+ DesiredSplitCenter = null;
+ }
+ }
+
+ return base.ArrangeOverride(finalSize);
+ }
+
+ protected override void KeyBindUp(GUIBoundKeyEventArgs args)
+ {
+ base.KeyBindUp(args);
+
+ if (args.Function != EngineKeyFunctions.UIClick)
+ {
+ return;
+ }
+
+ if (ChildCount != 2)
+ {
+ return;
+ }
+
+ var first = GetChild(0);
+ var second = GetChild(1);
+
+ OnSplitResizeFinish?.Invoke(first.Size, second.Size);
+ }
+}
-using Robust.Client.AutoGenerated;
+using Content.Client.UserInterface.Systems.Chat.Widgets;
+using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.UserInterface.Screens;
[GenerateTypedNameReferences]
-public sealed partial class DefaultGameScreen : UIScreen
+public sealed partial class DefaultGameScreen : InGameScreen
{
public DefaultGameScreen()
{
SetAnchorAndMarginPreset(Alerts, LayoutPreset.TopRight, margin: 10);
Chat.OnResized += ChatOnResized;
+ Chat.OnChatResizeFinish += ChatOnResizeFinish;
+ }
+
+ private void ChatOnResizeFinish(Vector2 _)
+ {
+ var marginBottom = Chat.GetValue<float>(MarginBottomProperty);
+ var marginLeft = Chat.GetValue<float>(MarginLeftProperty);
+ OnChatResized?.Invoke(new Vector2(marginBottom, marginLeft));
}
private void ChatOnResized()
var marginBottom = Chat.GetValue<float>(MarginBottomProperty);
SetMarginTop(Alerts, marginBottom);
}
+
+ public override ChatBox ChatBox => Chat;
+
+ //TODO: There's probably a better way to do this... but this is also the easiest way.
+ public override void SetChatSize(Vector2 size)
+ {
+ SetMarginBottom(Chat, size.X);
+ SetMarginLeft(Chat, size.Y);
+ SetMarginTop(Alerts, Size.X);
+ }
}
--- /dev/null
+using Content.Client.UserInterface.Systems.Chat.Widgets;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+
+namespace Content.Client.UserInterface.Screens;
+
+/// <summary>
+/// Screens that are considered to be 'in-game'.
+/// </summary>
+public abstract class InGameScreen : UIScreen
+{
+ public Action<Vector2>? OnChatResized;
+
+ public abstract ChatBox ChatBox { get; }
+
+ public abstract void SetChatSize(Vector2 size);
+}
VerticalExpand="False"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
- <SplitContainer Name="ScreenContainer"
- HorizontalExpand="True"
- VerticalExpand="True"
- SplitWidth="0">
+ <controls:RecordedSplitContainer Name="ScreenContainer" HorizontalExpand="True" VerticalExpand="True" SplitWidth="0">
<LayoutContainer Name="ViewportContainer" HorizontalExpand="True" VerticalExpand="True">
<controls:MainViewport Name="MainViewport"/>
<widgets:GhostGui Name="Ghost" Access="Protected" />
<actions:ActionsBar Name="Actions" Access="Protected" />
<alerts:AlertsUI Name="Alerts" Access="Protected" />
</LayoutContainer>
- <PanelContainer HorizontalExpand="True">
+ <PanelContainer HorizontalExpand="True" MinWidth="300">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#2B2C3B" />
</PanelContainer.PanelOverride>
- <BoxContainer Orientation="Vertical" HorizontalExpand="True" MinWidth="300" SeparationOverride="10" Margin="10">
+ <BoxContainer Orientation="Vertical" HorizontalExpand="True" SeparationOverride="10" Margin="10">
<menuBar:GameTopMenuBar Name="TopBar" HorizontalExpand="True" Access="Protected" />
<chat:ChatBox VerticalExpand="True" HorizontalExpand="True" Name="Chat" Access="Protected" />
</BoxContainer>
</PanelContainer>
- </SplitContainer>
+ </controls:RecordedSplitContainer>
</screens:SeparatedChatGameScreen>
+using Content.Client.UserInterface.Systems.Chat.Widgets;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.UserInterface.Screens;
[GenerateTypedNameReferences]
-public sealed partial class SeparatedChatGameScreen : UIScreen
+public sealed partial class SeparatedChatGameScreen : InGameScreen
{
public SeparatedChatGameScreen()
{
SetAnchorAndMarginPreset(Ghost, LayoutPreset.BottomWide, margin: 80);
SetAnchorAndMarginPreset(Hotbar, LayoutPreset.BottomWide, margin: 5);
SetAnchorAndMarginPreset(Alerts, LayoutPreset.CenterRight, margin: 10);
+
+ ScreenContainer.OnSplitResizeFinish += (first, second) =>
+ OnChatResized?.Invoke(new Vector2(ScreenContainer.SplitFraction, 0));
+ }
+
+ public override ChatBox ChatBox => GetWidget<ChatBox>()!;
+
+ public override void SetChatSize(Vector2 size)
+ {
+ ScreenContainer.DesiredSplitCenter = size.X;
+ ScreenContainer.ResizeMode = SplitContainer.SplitResizeMode.RespectChildrenMinSize;
}
}
using Content.Client.Examine;
using Content.Client.Gameplay;
using Content.Client.Ghost;
+using Content.Client.Lobby.UI;
+using Content.Client.UserInterface.Screens;
using Content.Client.UserInterface.Systems.Chat.Widgets;
using Content.Shared.Administration;
using Content.Shared.CCVar;
public void SetMainChat(bool setting)
{
- // This isn't very nice to look at.
- var widget = UIManager.ActiveScreen?.GetWidget<ChatBox>();
- if (widget == null)
+ if (UIManager.ActiveScreen == null)
{
- widget = UIManager.ActiveScreen?.GetWidget<ResizableChatBox>();
- if (widget == null)
- {
+ return;
+ }
+
+ ChatBox chatBox;
+ string? chatSizeRaw;
+
+ switch (UIManager.ActiveScreen)
+ {
+ case DefaultGameScreen defaultScreen:
+ chatBox = defaultScreen.ChatBox;
+ chatSizeRaw = _config.GetCVar(CCVars.DefaultScreenChatSize);
+ SetChatSizing(chatSizeRaw, defaultScreen, setting);
+ break;
+ case SeparatedChatGameScreen separatedScreen:
+ chatBox = separatedScreen.ChatBox;
+ chatSizeRaw = _config.GetCVar(CCVars.SeparatedScreenChatSize);
+ SetChatSizing(chatSizeRaw, separatedScreen, setting);
+ break;
+ default:
+ // this could be better?
+ var maybeChat = UIManager.ActiveScreen.GetWidget<ChatBox>();
+
+ chatBox = maybeChat ?? throw new Exception("Cannot get chat box in screen!");
+
+ break;
+ }
+
+ chatBox.Main = setting;
+ }
+
+ private void SetChatSizing(string sizing, InGameScreen screen, bool setting)
+ {
+ if (!setting)
+ {
+ screen.OnChatResized -= StoreChatSize;
+ return;
+ }
+
+ screen.OnChatResized += StoreChatSize;
+
+ if (string.IsNullOrEmpty(sizing))
+ {
+ return;
+ }
+
+ var split = sizing.Split(",");
+
+ var chatSize = new Vector2(
+ float.Parse(split[0]),
+ float.Parse(split[1]));
+
+
+ screen.SetChatSize(chatSize);
+ }
+
+ private void StoreChatSize(Vector2 size)
+ {
+ if (UIManager.ActiveScreen == null)
+ {
+ throw new Exception("Cannot get active screen!");
+ }
+
+ var stringSize = $"{size.X},{size.Y}";
+ switch (UIManager.ActiveScreen)
+ {
+ case DefaultGameScreen _:
+ _config.SetCVar(CCVars.DefaultScreenChatSize, stringSize);
+ break;
+ case SeparatedChatGameScreen _:
+ _config.SetCVar(CCVars.SeparatedScreenChatSize, stringSize);
+ break;
+ default:
+ // do nothing
return;
- }
}
- widget.Main = setting;
+ _config.SaveToFile();
}
private void FocusChat()
private byte _clampIn;
+ public Action<Vector2>? OnChatResizeFinish;
+
protected override void EnteredTree()
{
base.EnteredTree();
// If this is done in MouseDown, Godot won't fire MouseUp as you need focus to receive MouseUps.
UserInterfaceManager.KeyboardFocused?.ReleaseKeyboardFocus();
+
+ OnChatResizeFinish?.Invoke(Size);
}
base.KeyBindUp(args);
public static readonly CVarDef<string> UILayout =
CVarDef.Create("ui.layout", "Default", CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef<string> DefaultScreenChatSize =
+ CVarDef.Create("ui.default_chat_size", "", CVar.CLIENTONLY | CVar.ARCHIVE);
+
+ public static readonly CVarDef<string> SeparatedScreenChatSize =
+ CVarDef.Create("ui.separated_chat_size", "0.6,0", CVar.CLIENTONLY | CVar.ARCHIVE);
/*