diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index c75348112f..4d3992ce13 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -3,19 +3,23 @@ using System; using System.Collections.Generic; -using System.ComponentModel; +using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Overlays; using osu.Game.Overlays.Chat; +using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Tabs; +using osu.Game.Users; +using osuTK.Input; namespace osu.Game.Tests.Visual.Online { - [Description("Testing chat api and overlay")] - public class TestSceneChatOverlay : OsuTestScene + public class TestSceneChatOverlay : ManualInputManagerTestScene { public override IReadOnlyList RequiredTypes => new[] { @@ -28,17 +32,126 @@ namespace osu.Game.Tests.Visual.Online typeof(TabCloseButton) }; - [Cached] - private readonly ChannelManager channelManager = new ChannelManager(); + private TestChatOverlay chatOverlay; + private ChannelManager channelManager; - [BackgroundDependencyLoader] - private void load() + private readonly Channel channel1 = new Channel(new User()) { Name = "test1" }; + private readonly Channel channel2 = new Channel(new User()) { Name = "test2" }; + + [SetUp] + public void Setup() { - Children = new Drawable[] + Schedule(() => { - channelManager, - new ChatOverlay { State = { Value = Visibility.Visible } } - }; + ChannelManagerContainer container; + + Child = container = new ChannelManagerContainer(new List { channel1, channel2 }) + { + RelativeSizeAxes = Axes.Both, + }; + + chatOverlay = container.ChatOverlay; + channelManager = container.ChannelManager; + }); + } + + [Test] + public void TestHideOverlay() + { + AddAssert("Chat overlay is visible", () => chatOverlay.State.Value == Visibility.Visible); + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + + AddStep("Close chat overlay", () => chatOverlay.Hide()); + + AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden); + AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + } + + [Test] + public void TestSelectingChannelClosesSelector() + { + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1])); + + AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + } + + [Test] + public void TestCloseChannelWhileSelectorClosed() + { + AddStep("Join channel 1", () => channelManager.JoinChannel(channel1)); + AddStep("Join channel 2", () => channelManager.JoinChannel(channel2)); + + AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2])); + AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child)); + + AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden); + AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1); + + AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child)); + + AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible); + } + + private void clickDrawable(Drawable d) + { + InputManager.MoveMouseTo(d); + InputManager.Click(MouseButton.Left); + } + + private class ChannelManagerContainer : Container + { + public TestChatOverlay ChatOverlay { get; private set; } + + [Cached] + public ChannelManager ChannelManager { get; } = new ChannelManager(); + + private readonly List channels; + + public ChannelManagerContainer(List channels) + { + this.channels = channels; + } + + [BackgroundDependencyLoader] + private void load() + { + ((BindableList)ChannelManager.AvailableChannels).AddRange(channels); + + Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, }; + ChatOverlay.Show(); + } + } + + private class TestChatOverlay : ChatOverlay + { + public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value; + + public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay; + + protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl(); + + public IReadOnlyDictionary> TabMap => ((TestTabControl)ChannelTabControl).TabMap; + } + + private class TestTabControl : ChannelTabControl + { + protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value); + + public new IReadOnlyDictionary> TabMap => base.TabMap; + } + + private class TestChannelTabItem : PrivateChannelTabItem + { + public TestChannelTabItem(Channel channel) + : base(channel) + { + } + + public new ClickableContainer CloseButton => base.CloseButton; } } } diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 71e9e4bdf3..e0ded11ec9 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Chat.Selection private readonly SearchTextBox search; private readonly SearchContainer sectionsFlow; + protected override bool DimMainContent => false; + public Action OnRequestJoin; public Action OnRequestLeave; diff --git a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs index fafcb0a72d..612379d339 100644 --- a/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs +++ b/osu.Game/Overlays/Chat/Tabs/ChannelTabControl.cs @@ -49,6 +49,8 @@ namespace osu.Game.Overlays.Chat.Tabs // performTabSort might've made selectorTab's position wonky, fix it TabContainer.SetLayoutPosition(selectorTab, float.MaxValue); + ((ChannelTabItem)item).OnRequestClose += tabCloseRequested; + base.AddTabItem(item, addToDropdown); } @@ -57,10 +59,10 @@ namespace osu.Game.Overlays.Chat.Tabs switch (value.Type) { default: - return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + return new ChannelTabItem(value); case ChannelType.PM: - return new PrivateChannelTabItem(value) { OnRequestClose = tabCloseRequested }; + return new PrivateChannelTabItem(value); } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index fce9862e8e..e223856b27 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -45,7 +45,9 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private ChannelTabControl channelTabControl; + protected ChannelTabControl ChannelTabControl; + + protected virtual ChannelTabControl CreateChannelTabControl() => new ChannelTabControl(); private Container chatContainer; private TabsArea tabsArea; @@ -55,9 +57,10 @@ namespace osu.Game.Overlays public Bindable ChatHeight { get; set; } private Container channelSelectionContainer; - private ChannelSelectionOverlay channelSelectionOverlay; + protected ChannelSelectionOverlay ChannelSelectionOverlay; - public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || (channelSelectionOverlay.State.Value == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos)); + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) + || (ChannelSelectionOverlay.State.Value == Visibility.Visible && ChannelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos)); public ChatOverlay() { @@ -81,7 +84,7 @@ namespace osu.Game.Overlays Masking = true, Children = new[] { - channelSelectionOverlay = new ChannelSelectionOverlay + ChannelSelectionOverlay = new ChannelSelectionOverlay { RelativeSizeAxes = Axes.Both, }, @@ -154,31 +157,25 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = Color4.Black, }, - channelTabControl = new ChannelTabControl + ChannelTabControl = CreateChannelTabControl().With(d => { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - RelativeSizeAxes = Axes.Both, - OnRequestLeave = channelManager.LeaveChannel - }, + d.Anchor = Anchor.BottomLeft; + d.Origin = Anchor.BottomLeft; + d.RelativeSizeAxes = Axes.Both; + d.OnRequestLeave = channelManager.LeaveChannel; + }), } }, }, }, }; - channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue; - channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; - channelSelectionOverlay.State.ValueChanged += state => + ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue; + ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden; + ChannelSelectionOverlay.State.ValueChanged += state => { - if (state.NewValue == Visibility.Hidden && channelManager.JoinedChannels.Count == 0) - { - channelSelectionOverlay.Show(); - Hide(); - return; - } - - channelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible; + // Propagate the visibility state to ChannelSelectorActive + ChannelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible; if (state.NewValue == Visibility.Visible) { @@ -190,8 +187,8 @@ namespace osu.Game.Overlays textbox.HoldFocus = true; }; - channelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel); - channelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel; + ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel); + ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel; ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); ChatHeight.ValueChanged += height => @@ -217,11 +214,11 @@ namespace osu.Game.Overlays channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels; channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels; foreach (Channel channel in channelManager.JoinedChannels) - channelTabControl.AddChannel(channel); + ChannelTabControl.AddChannel(channel); channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged; channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged; - channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); + ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); currentChannel = channelManager.CurrentChannel.GetBoundCopy(); currentChannel.BindValueChanged(currentChannelChanged, true); @@ -236,7 +233,7 @@ namespace osu.Game.Overlays { textbox.Current.Disabled = true; currentChannelContainer.Clear(false); - channelSelectionOverlay.Show(); + ChannelSelectionOverlay.Show(); return; } @@ -245,8 +242,8 @@ namespace osu.Game.Overlays textbox.Current.Disabled = e.NewValue.ReadOnly; - if (channelTabControl.Current.Value != e.NewValue) - Scheduler.Add(() => channelTabControl.Current.Value = e.NewValue); + if (ChannelTabControl.Current.Value != e.NewValue) + Scheduler.Add(() => ChannelTabControl.Current.Value = e.NewValue); var loaded = loadedChannels.Find(d => d.Channel == e.NewValue); @@ -294,7 +291,7 @@ namespace osu.Game.Overlays double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height - if (channelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) + if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) targetChatHeight = 1f - channel_selection_min_height; ChatHeight.Value = targetChatHeight; @@ -311,9 +308,9 @@ namespace osu.Game.Overlays private void selectTab(int index) { - var channel = channelTabControl.Items.Skip(index).FirstOrDefault(); + var channel = ChannelTabControl.Items.Skip(index).FirstOrDefault(); if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel)) - channelTabControl.Current.Value = channel; + ChannelTabControl.Current.Value = channel; } protected override bool OnKeyDown(KeyDownEvent e) @@ -358,6 +355,7 @@ namespace osu.Game.Overlays this.FadeIn(transition_length, Easing.OutQuint); textbox.HoldFocus = true; + base.PopIn(); } @@ -366,7 +364,7 @@ namespace osu.Game.Overlays this.MoveToY(Height, transition_length, Easing.InSine); this.FadeOut(transition_length, Easing.InSine); - channelSelectionOverlay.Hide(); + ChannelSelectionOverlay.Hide(); textbox.HoldFocus = false; base.PopOut(); @@ -375,20 +373,20 @@ namespace osu.Game.Overlays private void onChannelAddedToJoinedChannels(IEnumerable channels) { foreach (Channel channel in channels) - channelTabControl.AddChannel(channel); + ChannelTabControl.AddChannel(channel); } private void onChannelRemovedFromJoinedChannels(IEnumerable channels) { foreach (Channel channel in channels) { - channelTabControl.RemoveChannel(channel); + ChannelTabControl.RemoveChannel(channel); loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); } } private void availableChannelsChanged(IEnumerable channels) - => channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); + => ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels); protected override void Dispose(bool isDisposing) {