From f049d7cb677e899f9984611ae19410b01a18c978 Mon Sep 17 00:00:00 2001 From: Jai Sharma Date: Tue, 29 Mar 2022 21:36:08 +0100 Subject: [PATCH] Implement `ChatTextBox` for new chat design Reference design: https://www.figma.com/file/f8b2dHp9LJCMOqYP4mdrPZ/Client%2FChat?node-id=1%3A297 Adds new component `ChatTextBox`. Exposes `BindableBool` `ShowSearch` to change text input behaviour between normal and search behaviour. Adds new component `ChatTextBar`. Exposes `BindableBool` `ShowSearch` which toggles between showing current chat channel or search icon. Additionally binds to child `ChatTextBox` components. Requires a cached `Bindable` instance to be managed by a parent component. --- InspectCode.sh | 2 +- .../Visual/Online/TestSceneChatTextBox.cs | 96 ++++++++++ osu.Game/Overlays/Chat/ChatTextBar.cs | 174 ++++++++++++++++++ osu.Game/Overlays/Chat/ChatTextBox.cs | 36 ++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBar.cs create mode 100644 osu.Game/Overlays/Chat/ChatTextBox.cs diff --git a/InspectCode.sh b/InspectCode.sh index cf2bc18175..5a72324dd4 100755 --- a/InspectCode.sh +++ b/InspectCode.sh @@ -2,5 +2,5 @@ dotnet tool restore dotnet CodeFileSanity -dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN +dotnet jb inspectcode "osu.Game/osu.Game.csproj" --no-build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs new file mode 100644 index 0000000000..e72a1d6652 --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneChatTextBox.cs @@ -0,0 +1,96 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Chat; +using osu.Game.Overlays; +using osu.Game.Overlays.Chat; + +namespace osu.Game.Tests.Visual.Online +{ + [TestFixture] + public class TestSceneChatTextBox : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [Cached] + private readonly Bindable currentChannel = new Bindable(); + + private OsuSpriteText commitText; + private ChatTextBar bar; + + [SetUp] + public void SetUp() + { + Schedule(() => + { + Child = new GridContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 30), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + commitText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.Default.With(size: 20), + }, + }, + new Drawable[] + { + bar = new ChatTextBar + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.99f, + }, + }, + }, + }; + + bar.TextBox.OnCommit += (sender, newText) => + { + commitText.Text = $"Commit: {sender.Text}"; + commitText.FadeOutFromOne(1000, Easing.InQuint); + sender.Text = string.Empty; + }; + }); + } + + [Test] + public void TestVisual() + { + AddStep("Public Channel", () => currentChannel.Value = createPublicChannel("#osu")); + AddStep("Public Channel Long Name", () => currentChannel.Value = createPublicChannel("#public-channel-long-name")); + AddStep("Private Channel", () => currentChannel.Value = createPrivateChannel("peppy", 2)); + AddStep("Private Long Name", () => currentChannel.Value = createPrivateChannel("test user long name", 3)); + + AddStep("Chat Mode Channel", () => bar.ShowSearch.Value = false); + AddStep("Chat Mode Search", () => bar.ShowSearch.Value = true); + } + + private static Channel createPublicChannel(string name) + => new Channel { Name = name, Type = ChannelType.Public, Id = 1234 }; + + private static Channel createPrivateChannel(string username, int id) + => new Channel(new APIUser { Id = id, Username = username }); + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBar.cs b/osu.Game/Overlays/Chat/ChatTextBar.cs new file mode 100644 index 0000000000..00284fdd33 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBar.cs @@ -0,0 +1,174 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; +using osuTK; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBar : Container + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public ChatTextBox TextBox => chatTextBox; + + [Resolved] + private Bindable currentChannel { get; set; } = null!; + + private OsuTextFlowContainer chattingTextContainer = null!; + private Container searchIconContainer = null!; + private ChatTextBox chatTextBox = null!; + private Container enterContainer = null!; + + private const float chatting_text_width = 180; + private const float search_icon_width = 40; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + RelativeSizeAxes = Axes.X; + Height = 60; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background5, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new Drawable[] + { + chattingTextContainer = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 20)) + { + Masking = true, + Width = chatting_text_width, + Padding = new MarginPadding { Left = 10 }, + RelativeSizeAxes = Axes.Y, + TextAnchor = Anchor.CentreRight, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Colour = colourProvider.Background1, + }, + searchIconContainer = new Container + { + RelativeSizeAxes = Axes.Y, + Width = search_icon_width, + Child = new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Size = new Vector2(20), + Margin = new MarginPadding { Right = 2 }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 5 }, + Child = chatTextBox = new ChatTextBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + ShowSearch = { BindTarget = ShowSearch }, + HoldFocus = true, + ReleaseFocusOnCommit = false, + }, + }, + enterContainer = new Container + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Masking = true, + BorderColour = colourProvider.Background1, + BorderThickness = 2, + CornerRadius = 10, + Margin = new MarginPadding { Right = 10 }, + Size = new Vector2(60, 30), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Transparent, + }, + new OsuSpriteText + { + Text = "Enter", + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Colour = colourProvider.Background1, + Font = OsuFont.Torus.With(size: 20), + Margin = new MarginPadding { Bottom = 2 }, + }, + }, + }, + }, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + if (change.NewValue) + { + chattingTextContainer.Hide(); + enterContainer.Hide(); + searchIconContainer.Show(); + } + else + { + chattingTextContainer.Show(); + enterContainer.Show(); + searchIconContainer.Hide(); + } + }, true); + + currentChannel.BindValueChanged(change => + { + Channel newChannel = change.NewValue; + switch (newChannel?.Type) + { + case ChannelType.Public: + chattingTextContainer.Text = $"chatting in {newChannel.Name}"; + break; + case ChannelType.PM: + chattingTextContainer.Text = $"chatting with {newChannel.Name}"; + break; + default: + chattingTextContainer.Text = ""; + break; + } + }, true); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTextBox.cs b/osu.Game/Overlays/Chat/ChatTextBox.cs new file mode 100644 index 0000000000..35ed26cda3 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChatTextBox.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +using osu.Framework.Bindables; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Chat +{ + public class ChatTextBox : FocusedTextBox + { + public readonly BindableBool ShowSearch = new BindableBool(); + + public override bool HandleLeftRightArrows => !ShowSearch.Value; + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowSearch.BindValueChanged(change => + { + PlaceholderText = change.NewValue ? "type here to search" : "type here"; + Schedule(() => Text = string.Empty); + }, true); + } + + protected override void Commit() + { + if (ShowSearch.Value) + return; + + base.Commit(); + } + } +}