diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs new file mode 100644 index 0000000000..e1dbf04133 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneShearedSearchTextBox : OsuTestScene + { + [Test] + public void TestAllColourSchemes() + { + foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast()) + AddStep($"set {scheme} scheme", () => Child = createContent(scheme)); + } + + private Drawable createContent(OverlayColourScheme colourScheme) + { + var colourProvider = new OverlayColourProvider(colourScheme); + + return new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(OverlayColourProvider), colourProvider) + }, + Children = new Drawable[] + { + new ShearedSearchTextBox + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Width = 0.5f + } + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs new file mode 100644 index 0000000000..e3dc118faa --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -0,0 +1,192 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +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.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue + { + private const float icon_container_width = 50; + private const float corner_radius = 10; + private const float height = 42; + private readonly Vector2 shear = new Vector2(0.2f, 0); + + public FocusedTextBox TextBox; + + public Bindable Current + { + get => TextBox.Current; + set => TextBox.Current = value; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = height; + Shear = shear; + Masking = true; + CornerRadius = corner_radius; + InternalChild = new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background3, + RelativeSizeAxes = Axes.Both + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new Container + { + Name = @"Search box container", + RelativeSizeAxes = Axes.Both, + CornerRadius = corner_radius, + Masking = true, + Children = new Drawable[] + { + new Box + { + Colour = colourProvider.Background4, + RelativeSizeAxes = Axes.Both + }, + TextBox = new SearchTextBox + { + Shear = -shear, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding + { + Horizontal = corner_radius + } + } + } + }, + new Container + { + Name = @"Icon container", + RelativeSizeAxes = Axes.Y, + Width = icon_container_width, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Children = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(16), + Shear = -shear + } + } + } + } + }, + ColumnDimensions = new[] { new Dimension(), new Dimension(GridSizeMode.AutoSize) } + } + } + }; + } + + public override bool HandleNonPositionalInput => TextBox.HandleNonPositionalInput; + + private class SearchTextBox : FocusedTextBox + { + protected virtual bool AllowCommit => false; + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundFocused = colourProvider.Background4; + BackgroundUnfocused = colourProvider.Background4; + Placeholder.Colour = Color4.White; + PlaceholderText = @"Search"; + } + + protected override SpriteText CreatePlaceholder() => new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) + }; + + protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer + { + AutoSizeAxes = Axes.Both, + Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) }, + }; + + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case PlatformAction.MoveBackwardLine: + case PlatformAction.MoveForwardLine: + // Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox + // as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text) + // Avoid handling it here to allow other components to potentially consume the shortcut. + case PlatformAction.DeleteForwardChar: + return false; + } + + return base.OnPressed(e); + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (!e.ControlPressed && !e.ShiftPressed) + { + switch (e.Key) + { + case Key.Left: + case Key.Right: + case Key.Up: + case Key.Down: + return false; + } + } + + if (!AllowCommit) + { + switch (e.Key) + { + case Key.KeypadEnter: + case Key.Enter: + return false; + } + } + + if (e.ShiftPressed) + { + switch (e.Key) + { + case Key.Delete: + return false; + } + } + + return base.OnKeyDown(e); + } + } + } +}