1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 19:27:24 +08:00

Merge pull request #21228 from Terochi/recent-messages-implementation

Add ability to view chat send history in input box
This commit is contained in:
Dean Herbert 2022-11-23 14:58:42 +09:00 committed by GitHub
commit 172e798847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 280 additions and 9 deletions

View File

@ -6,10 +6,10 @@
using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Utils;
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
{

View File

@ -5,7 +5,7 @@
using System;
using NUnit.Framework;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Utils;
namespace osu.Game.Tests.NonVisual
{

View File

@ -0,0 +1,178 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneHistoryTextBox : OsuManualInputManagerTestScene
{
private const string temp = "Temp message";
private int messageCounter;
private HistoryTextBox box = null!;
private OsuSpriteText text = null!;
[SetUp]
public void SetUp()
{
Schedule(() =>
{
Children = new Drawable[]
{
box = new HistoryTextBox(5)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.99f,
},
text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Width = 0.99f,
Y = -box.Height,
Font = OsuFont.Default.With(size: 20),
}
};
box.OnCommit += (_, _) =>
{
if (string.IsNullOrEmpty(box.Text))
return;
text.Text = $"{nameof(box.OnCommit)}: {box.Text}";
box.Text = string.Empty;
box.TakeFocus();
text.FadeOutFromOne(1000, Easing.InQuint);
};
messageCounter = 0;
box.TakeFocus();
});
}
[Test]
public void TestEmptyHistory()
{
AddStep("Set text", () => box.Text = temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text is unchanged", () => box.Text == temp);
AddStep("Move up", () => InputManager.Key(Key.Up));
AddAssert("Text is unchanged", () => box.Text == temp);
}
[Test]
public void TestPartialHistory()
{
addMessages(3);
AddStep("Set text", () => box.Text = temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text is unchanged", () => box.Text == temp);
AddRepeatStep("Move up", () => InputManager.Key(Key.Up), 3);
AddAssert("Same as 1st message", () => box.Text == "Message 1");
AddStep("Move up", () => InputManager.Key(Key.Up));
AddAssert("Same as 1st message", () => box.Text == "Message 1");
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Same as 2nd message", () => box.Text == "Message 2");
AddRepeatStep("Move down", () => InputManager.Key(Key.Down), 2);
AddAssert("Temporary message restored", () => box.Text == temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text is unchanged", () => box.Text == temp);
}
[Test]
public void TestFullHistory()
{
addMessages(7);
AddStep("Set text", () => box.Text = temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text is unchanged", () => box.Text == temp);
AddRepeatStep("Move up", () => InputManager.Key(Key.Up), 5);
AddAssert("Same as 3rd message", () => box.Text == "Message 3");
AddStep("Move up", () => InputManager.Key(Key.Up));
AddAssert("Same as 3rd message", () => box.Text == "Message 3");
AddRepeatStep("Move down", () => InputManager.Key(Key.Down), 4);
AddAssert("Same as 7th message", () => box.Text == "Message 7");
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Temporary message restored", () => box.Text == temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text is unchanged", () => box.Text == temp);
}
[Test]
public void TestChangedHistory()
{
addMessages(2);
AddStep("Set text", () => box.Text = temp);
AddStep("Move up", () => InputManager.Key(Key.Up));
AddStep("Change text", () => box.Text = "New message");
AddStep("Move down", () => InputManager.Key(Key.Down));
AddStep("Move up", () => InputManager.Key(Key.Up));
AddAssert("Changes lost", () => box.Text == "Message 2");
}
[Test]
public void TestInputOnEdge()
{
addMessages(2);
AddStep("Set text", () => box.Text = temp);
AddStep("Move down", () => InputManager.Key(Key.Down));
AddAssert("Text unchanged", () => box.Text == temp);
AddRepeatStep("Move up", () => InputManager.Key(Key.Up), 2);
AddAssert("Same as 1st message", () => box.Text == "Message 1");
AddStep("Move up", () => InputManager.Key(Key.Up));
AddAssert("Text unchanged", () => box.Text == "Message 1");
}
[Test]
public void TestResetIndex()
{
addMessages(2);
AddRepeatStep("Move Up", () => InputManager.Key(Key.Up), 2);
AddAssert("Same as 1st message", () => box.Text == "Message 1");
AddStep("Change text", () => box.Text = "New message");
AddStep("Move Up", () => InputManager.Key(Key.Up));
AddAssert("Same as previous message", () => box.Text == "Message 2");
}
private void addMessages(int count)
{
AddRepeatStep("Add messages", () =>
{
box.Text = $"Message {++messageCounter}";
InputManager.Key(Key.Enter);
}, count);
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Input.Events;
using osu.Game.Utils;
using osuTK.Input;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// A <see cref="FocusedTextBox"/> which additionally retains a history of text committed, up to a limit
/// (100 by default, specified in constructor).
/// The history of committed text can be navigated using up/down arrows.
/// This resembles the operation of command-line terminals.
/// </summary>
public class HistoryTextBox : FocusedTextBox
{
private readonly LimitedCapacityQueue<string> messageHistory;
public int HistoryCount => messageHistory.Count;
private int selectedIndex;
private string originalMessage = string.Empty;
/// <summary>
/// Creates a new <see cref="HistoryTextBox"/>.
/// </summary>
/// <param name="capacity">
/// The maximum number of committed lines to keep in history.
/// When exceeded, the oldest lines in history will be dropped to make space for new ones.
/// </param>
public HistoryTextBox(int capacity = 100)
{
messageHistory = new LimitedCapacityQueue<string>(capacity);
Current.ValueChanged += text =>
{
if (selectedIndex != HistoryCount && text.NewValue != messageHistory[selectedIndex])
{
selectedIndex = HistoryCount;
}
};
}
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.Up:
if (selectedIndex == 0)
return true;
if (selectedIndex == HistoryCount)
originalMessage = Text;
Text = messageHistory[--selectedIndex];
return true;
case Key.Down:
if (selectedIndex == HistoryCount)
return true;
if (selectedIndex == HistoryCount - 1)
{
selectedIndex = HistoryCount;
Text = originalMessage;
return true;
}
Text = messageHistory[++selectedIndex];
return true;
}
return base.OnKeyDown(e);
}
protected override void Commit()
{
if (!string.IsNullOrEmpty(Text))
messageHistory.Enqueue(Text);
selectedIndex = HistoryCount;
base.Commit();
}
}
}

View File

@ -120,17 +120,20 @@ namespace osu.Game.Online.Chat
AddInternal(drawableChannel);
}
public class ChatTextBox : FocusedTextBox
public class ChatTextBox : HistoryTextBox
{
protected override bool OnKeyDown(KeyDownEvent e)
{
// Chat text boxes are generally used in places where they retain focus, but shouldn't block interaction with other
// elements on the same screen.
switch (e.Key)
if (!HoldFocus)
{
case Key.Up:
case Key.Down:
return false;
switch (e.Key)
{
case Key.Up:
case Key.Down:
return false;
}
}
return base.OnKeyDown(e);

View File

@ -7,7 +7,7 @@ using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.Chat
{
public class ChatTextBox : FocusedTextBox
public class ChatTextBox : HistoryTextBox
{
public readonly BindableBool ShowSearch = new BindableBool();

View File

@ -7,7 +7,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Difficulty.Utils
namespace osu.Game.Utils
{
/// <summary>
/// An indexed queue with limited capacity.