Merge branch 'master' into combo-colour-brightness-limit

This commit is contained in:
Dean Herbert
2022-11-01 17:50:26 +09:00
370 changed files with 7992 additions and 2333 deletions

View File

@ -5,6 +5,7 @@
using Markdig;
using Markdig.Extensions.AutoLinks;
using Markdig.Extensions.CustomContainers;
using Markdig.Extensions.EmphasisExtras;
using Markdig.Extensions.Footnotes;
using Markdig.Extensions.Tables;
@ -32,6 +33,12 @@ namespace osu.Game.Graphics.Containers.Markdown
/// <seealso cref="AutoLinkExtension"/>
protected virtual bool Autolinks => false;
/// <summary>
/// Allows this markdown container to parse custom containers (used for flags and infoboxes).
/// </summary>
/// <seealso cref="CustomContainerExtension"/>
protected virtual bool CustomContainers => false;
public OsuMarkdownContainer()
{
LineSpacing = 21;
@ -107,6 +114,9 @@ namespace osu.Game.Graphics.Containers.Markdown
if (Autolinks)
pipeline = pipeline.UseAutoLinks();
if (CustomContainers)
pipeline.UseCustomContainers();
return pipeline.Build();
}
}

View File

@ -3,6 +3,9 @@
#nullable disable
using System;
using System.Linq;
using Markdig.Extensions.CustomContainers;
using Markdig.Syntax.Inlines;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -11,6 +14,9 @@ using osu.Framework.Graphics.Containers.Markdown;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Users;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Graphics.Containers.Markdown
{
@ -33,6 +39,31 @@ namespace osu.Game.Graphics.Containers.Markdown
protected override SpriteText CreateEmphasisedSpriteText(bool bold, bool italic)
=> CreateSpriteText().With(t => t.Font = t.Font.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, italics: italic));
protected override void AddCustomComponent(CustomContainerInline inline)
{
if (!(inline.FirstChild is LiteralInline literal))
{
base.AddCustomComponent(inline);
return;
}
string[] attributes = literal.Content.ToString().Trim(' ', '{', '}').Split();
string flagAttribute = attributes.SingleOrDefault(a => a.StartsWith(@"flag", StringComparison.Ordinal));
if (flagAttribute == null)
{
base.AddCustomComponent(inline);
return;
}
string flag = flagAttribute.Split('=').Last().Trim('"');
if (!Enum.TryParse<CountryCode>(flag, out var countryCode))
countryCode = CountryCode.Unknown;
AddDrawable(new DrawableFlag(countryCode) { Size = new Vector2(20, 15) });
}
private class OsuMarkdownInlineCode : Container
{
[Resolved]

View File

@ -29,6 +29,7 @@ namespace osu.Game.Graphics.Containers
private Bindable<float> sizeY;
private Bindable<float> posX;
private Bindable<float> posY;
private Bindable<bool> applySafeAreaPadding;
private Bindable<MarginPadding> safeAreaPadding;
@ -132,6 +133,9 @@ namespace osu.Game.Graphics.Containers
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
posY.ValueChanged += _ => Scheduler.AddOnce(updateSize);
applySafeAreaPadding = config.GetBindable<bool>(OsuSetting.SafeAreaConsiderations);
applySafeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize));
safeAreaPadding = safeArea.SafeAreaPadding.GetBoundCopy();
safeAreaPadding.BindValueChanged(_ => Scheduler.AddOnce(updateSize));
}
@ -192,7 +196,7 @@ namespace osu.Game.Graphics.Containers
bool requiresMasking = targetRect.Size != Vector2.One
// For the top level scaling container, for now we apply masking if safe areas are in use.
// In the future this can likely be removed as more of the actual UI supports overflowing into the safe areas.
|| (targetMode == ScalingMode.Everything && safeAreaPadding.Value.Total != Vector2.Zero);
|| (targetMode == ScalingMode.Everything && (applySafeAreaPadding.Value && safeAreaPadding.Value.Total != Vector2.Zero));
if (requiresMasking)
sizableContainer.Masking = true;
@ -225,6 +229,9 @@ namespace osu.Game.Graphics.Containers
[Resolved]
private ISafeArea safeArea { get; set; }
[Resolved]
private OsuConfigManager config { get; set; }
private readonly bool confineHostCursor;
private readonly LayoutValue cursorRectCache = new LayoutValue(Invalidation.RequiredParentSizeToFit);
@ -259,7 +266,7 @@ namespace osu.Game.Graphics.Containers
{
if (host.Window == null) return;
bool coversWholeScreen = Size == Vector2.One && safeArea.SafeAreaPadding.Value.Total == Vector2.Zero;
bool coversWholeScreen = Size == Vector2.One && (!config.Get<bool>(OsuSetting.SafeAreaConsiderations) || safeArea.SafeAreaPadding.Value.Total == Vector2.Zero);
host.Window.CursorConfineRect = coversWholeScreen ? null : ToScreenSpace(DrawRectangle).AABBFloat;
}
}

View File

@ -13,7 +13,7 @@ using osu.Game.Configuration;
namespace osu.Game.Graphics.Cursor
{
/// <summary>
/// A container which provides the main <see cref="Cursor.MenuCursor"/>.
/// A container which provides the main <see cref="MenuCursorContainer"/>.
/// Also handles cases where a more localised cursor is provided by another component (via <see cref="IProvideCursor"/>).
/// </summary>
public class GlobalCursorDisplay : Container, IProvideCursor
@ -23,7 +23,9 @@ namespace osu.Game.Graphics.Cursor
/// </summary>
internal bool ShowCursor = true;
public CursorContainer MenuCursor { get; }
CursorContainer IProvideCursor.Cursor => MenuCursor;
public MenuCursorContainer MenuCursor { get; }
public bool ProvidingUserCursor => true;
@ -42,8 +44,8 @@ namespace osu.Game.Graphics.Cursor
{
AddRangeInternal(new Drawable[]
{
MenuCursor = new MenuCursor { State = { Value = Visibility.Hidden } },
Content = new Container { RelativeSizeAxes = Axes.Both }
Content = new Container { RelativeSizeAxes = Axes.Both },
MenuCursor = new MenuCursorContainer { State = { Value = Visibility.Hidden } }
});
}
@ -64,7 +66,7 @@ namespace osu.Game.Graphics.Cursor
if (!hasValidInput || !ShowCursor)
{
currentOverrideProvider?.MenuCursor?.Hide();
currentOverrideProvider?.Cursor?.Hide();
currentOverrideProvider = null;
return;
}
@ -83,8 +85,8 @@ namespace osu.Game.Graphics.Cursor
if (currentOverrideProvider == newOverrideProvider)
return;
currentOverrideProvider?.MenuCursor?.Hide();
newOverrideProvider.MenuCursor?.Show();
currentOverrideProvider?.Cursor?.Hide();
newOverrideProvider.Cursor?.Show();
currentOverrideProvider = newOverrideProvider;
}

View File

@ -17,10 +17,10 @@ namespace osu.Game.Graphics.Cursor
/// The cursor provided by this <see cref="IDrawable"/>.
/// May be null if no cursor should be visible.
/// </summary>
CursorContainer MenuCursor { get; }
CursorContainer Cursor { get; }
/// <summary>
/// Whether <see cref="MenuCursor"/> should be displayed as the singular user cursor. This will temporarily hide any other user cursor.
/// Whether <see cref="Cursor"/> should be displayed as the singular user cursor. This will temporarily hide any other user cursor.
/// This value is checked every frame and may be used to control whether multiple cursors are displayed (e.g. watching replays).
/// </summary>
bool ProvidingUserCursor { get; }

View File

@ -1,10 +1,7 @@
// 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.
#nullable disable
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
@ -21,24 +18,43 @@ using osuTK;
namespace osu.Game.Graphics.Cursor
{
public class MenuCursor : CursorContainer
public class MenuCursorContainer : CursorContainer
{
private readonly IBindable<bool> screenshotCursorVisibility = new Bindable<bool>(true);
public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
private bool hideCursorOnNonMouseInput;
public bool HideCursorOnNonMouseInput
{
get => hideCursorOnNonMouseInput;
set
{
if (hideCursorOnNonMouseInput == value)
return;
hideCursorOnNonMouseInput = value;
updateState();
}
}
protected override Drawable CreateCursor() => activeCursor = new Cursor();
private Cursor activeCursor;
private Cursor activeCursor = null!;
private Bindable<bool> cursorRotate;
private DragRotationState dragRotationState;
private Vector2 positionMouseDown;
private Sample tapSample;
private Vector2 lastMovePosition;
[BackgroundDependencyLoader(true)]
private void load([NotNull] OsuConfigManager config, [CanBeNull] ScreenshotManager screenshotManager, AudioManager audio)
private Bindable<bool> cursorRotate = null!;
private Sample tapSample = null!;
private MouseInputDetector mouseInputDetector = null!;
private bool visible;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, ScreenshotManager? screenshotManager, AudioManager audio)
{
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
@ -46,6 +62,45 @@ namespace osu.Game.Graphics.Cursor
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
tapSample = audio.Samples.Get(@"UI/cursor-tap");
Add(mouseInputDetector = new MouseInputDetector());
}
[Resolved]
private OsuGame? game { get; set; }
private readonly IBindable<bool> lastInputWasMouse = new BindableBool();
private readonly IBindable<bool> isIdle = new BindableBool();
protected override void LoadComplete()
{
base.LoadComplete();
lastInputWasMouse.BindTo(mouseInputDetector.LastInputWasMouseSource);
lastInputWasMouse.BindValueChanged(_ => updateState(), true);
if (game != null)
{
isIdle.BindTo(game.IsIdle);
isIdle.BindValueChanged(_ => updateState());
}
}
protected override void UpdateState(ValueChangedEvent<Visibility> state) => updateState();
private void updateState()
{
bool combinedVisibility = State.Value == Visibility.Visible && (lastInputWasMouse.Value || !hideCursorOnNonMouseInput) && !isIdle.Value;
if (visible == combinedVisibility)
return;
visible = combinedVisibility;
if (visible)
PopIn();
else
PopOut();
}
protected override void Update()
@ -163,11 +218,11 @@ namespace osu.Game.Graphics.Cursor
public class Cursor : Container
{
private Container cursorContainer;
private Bindable<float> cursorScale;
private Container cursorContainer = null!;
private Bindable<float> cursorScale = null!;
private const float base_scale = 0.15f;
public Sprite AdditiveLayer;
public Sprite AdditiveLayer = null!;
public Cursor()
{
@ -204,6 +259,40 @@ namespace osu.Game.Graphics.Cursor
}
}
private class MouseInputDetector : Component
{
/// <summary>
/// Whether the last input applied to the game is sourced from mouse.
/// </summary>
public IBindable<bool> LastInputWasMouseSource => lastInputWasMouseSource;
private readonly Bindable<bool> lastInputWasMouseSource = new Bindable<bool>();
public MouseInputDetector()
{
RelativeSizeAxes = Axes.Both;
}
protected override bool Handle(UIEvent e)
{
switch (e)
{
case MouseDownEvent:
case MouseMoveEvent:
lastInputWasMouseSource.Value = true;
return false;
case KeyDownEvent keyDown when !keyDown.Repeat:
case JoystickPressEvent:
case MidiDownEvent:
lastInputWasMouseSource.Value = false;
return false;
}
return false;
}
}
private enum DragRotationState
{
NotDragging,

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Localisation;
using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Overlays.OSD;
@ -94,15 +93,7 @@ namespace osu.Game.Graphics.UserInterface
private void copyUrl()
{
host.GetClipboard()?.SetText(Link);
onScreenDisplay?.Display(new CopyUrlToast(ToastStrings.UrlCopied));
}
private class CopyUrlToast : Toast
{
public CopyUrlToast(LocalisableString value)
: base(UserInterfaceStrings.GeneralHeader, value, "")
{
}
onScreenDisplay?.Display(new CopyUrlToast());
}
}
}

View File

@ -15,6 +15,9 @@ namespace osu.Game.Graphics.UserInterface
[Description("button")]
Button,
[Description("button-sidebar")]
ButtonSidebar,
[Description("toolbar")]
Toolbar,

View File

@ -26,24 +26,24 @@ namespace osu.Game.Graphics.UserInterface
{
set
{
if (labelText != null)
labelText.Text = value;
if (LabelTextFlowContainer != null)
LabelTextFlowContainer.Text = value;
}
}
public MarginPadding LabelPadding
{
get => labelText?.Padding ?? new MarginPadding();
get => LabelTextFlowContainer?.Padding ?? new MarginPadding();
set
{
if (labelText != null)
labelText.Padding = value;
if (LabelTextFlowContainer != null)
LabelTextFlowContainer.Padding = value;
}
}
protected readonly Nub Nub;
private readonly OsuTextFlowContainer labelText;
protected readonly OsuTextFlowContainer LabelTextFlowContainer;
private Sample sampleChecked;
private Sample sampleUnchecked;
@ -56,7 +56,7 @@ namespace osu.Game.Graphics.UserInterface
Children = new Drawable[]
{
labelText = new OsuTextFlowContainer(ApplyLabelParameters)
LabelTextFlowContainer = new OsuTextFlowContainer(ApplyLabelParameters)
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@ -70,19 +70,19 @@ namespace osu.Game.Graphics.UserInterface
Nub.Anchor = Anchor.CentreRight;
Nub.Origin = Anchor.CentreRight;
Nub.Margin = new MarginPadding { Right = nub_padding };
labelText.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 };
LabelTextFlowContainer.Padding = new MarginPadding { Right = Nub.EXPANDED_SIZE + nub_padding * 2 };
}
else
{
Nub.Anchor = Anchor.CentreLeft;
Nub.Origin = Anchor.CentreLeft;
Nub.Margin = new MarginPadding { Left = nub_padding };
labelText.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 };
LabelTextFlowContainer.Padding = new MarginPadding { Left = Nub.EXPANDED_SIZE + nub_padding * 2 };
}
Nub.Current.BindTo(Current);
Current.DisabledChanged += disabled => labelText.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
Current.DisabledChanged += disabled => LabelTextFlowContainer.Alpha = Nub.Alpha = disabled ? 0.3f : 1;
}
/// <summary>

View File

@ -44,6 +44,8 @@ namespace osu.Game.Graphics.UserInterface
public virtual LocalisableString TooltipText { get; private set; }
public bool PlaySamplesOnAdjust { get; set; } = true;
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
@ -187,6 +189,9 @@ namespace osu.Game.Graphics.UserInterface
private void playSample(T value)
{
if (!PlaySamplesOnAdjust)
return;
if (Clock == null || Clock.CurrentTime - lastSampleTime <= 30)
return;

View File

@ -31,6 +31,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } };
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);

View File

@ -0,0 +1,38 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Graphics.UserInterfaceV2
{
internal class OsuDirectorySelectorHiddenToggle : OsuCheckbox
{
public OsuDirectorySelectorHiddenToggle()
{
RelativeSizeAxes = Axes.None;
AutoSizeAxes = Axes.None;
Size = new Vector2(100, 50);
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
LabelTextFlowContainer.Anchor = Anchor.CentreLeft;
LabelTextFlowContainer.Origin = Anchor.CentreLeft;
LabelText = @"Show hidden";
}
[BackgroundDependencyLoader(true)]
private void load(OverlayColourProvider? overlayColourProvider, OsuColour colours)
{
if (overlayColourProvider != null)
return;
Nub.AccentColour = colours.GreySeaFoamLighter;
Nub.GlowingAccentColour = Color4.White;
Nub.GlowColour = Color4.White;
}
}
}

View File

@ -33,6 +33,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } };
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);