diff --git a/osu.Android.props b/osu.Android.props index 924e9c4a16..e5fed09c07 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 854fc4c91c..a438dc8be4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -14,8 +14,8 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Scoring; -using osuTK; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index e7486ef9b0..0ab3e8825b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -28,53 +29,67 @@ namespace osu.Game.Rulesets.Osu.Skinning Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } + private Container circleSprites; + private Sprite hitCircleSprite; + private Sprite hitCircleOverlay; + + private SkinnableSpriteText hitCircleText; + private readonly IBindable state = new Bindable(); private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + [Resolved] + private ISkinSource skin { get; set; } + [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) + private void load(DrawableHitObject drawableObject) { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; - Sprite hitCircleSprite; - SkinnableSpriteText hitCircleText; - InternalChildren = new Drawable[] { - hitCircleSprite = new Sprite + circleSprites = new Container { - Texture = getTextureWithFallback(string.Empty), - Colour = drawableObject.AccentColour.Value, Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + hitCircleSprite = new Sprite + { + Texture = getTextureWithFallback(string.Empty), + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + hitCircleOverlay = new Sprite + { + Texture = getTextureWithFallback("overlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, - }, confineMode: ConfineMode.NoScaling), - new Sprite + }, confineMode: ConfineMode.NoScaling) { - Texture = getTextureWithFallback("overlay"), Anchor = Anchor.Centre, Origin = Anchor.Centre, - } + }, }; bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; - if (!overlayAboveNumber) - ChangeInternalChildDepth(hitCircleText, -float.MaxValue); + if (overlayAboveNumber) + AddInternal(hitCircleOverlay.CreateProxy()); state.BindTo(drawableObject.State); - state.BindValueChanged(updateState, true); - accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); - indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); Texture getTextureWithFallback(string name) { @@ -87,6 +102,15 @@ namespace osu.Game.Rulesets.Osu.Skinning } } + protected override void LoadComplete() + { + base.LoadComplete(); + + state.BindValueChanged(updateState, true); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); + indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); + } + private void updateState(ValueChangedEvent state) { const double legacy_fade_duration = 240; @@ -94,8 +118,21 @@ namespace osu.Game.Rulesets.Osu.Skinning switch (state.NewValue) { case ArmedState.Hit: - this.FadeOut(legacy_fade_duration, Easing.Out); - this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + circleSprites.FadeOut(legacy_fade_duration, Easing.Out); + circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + + var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; + + if (legacyVersion >= 2.0m) + // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. + hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out); + else + { + // old skins scale and fade it normally along other pieces. + hitCircleText.FadeOut(legacy_fade_duration, Easing.Out); + hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + } + break; } } diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index b4d51d11c9..b2299398e1 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer(); + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { AlignWithStoryboard = true }; protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay(); diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index 9c8be868b0..0d1a5a8304 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -11,10 +11,19 @@ namespace osu.Game.Rulesets.Osu.UI public class OsuPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer { protected override Container Content => content; - private readonly Container content; + private readonly ScalingContainer content; private const float playfield_size_adjust = 0.8f; + /// + /// When true, an offset is applied to allow alignment with historical storyboards displayed in the same parent space. + /// This will shift the playfield downwards slightly. + /// + public bool AlignWithStoryboard + { + set => content.PlayfieldShift = value; + } + public OsuPlayfieldAdjustmentContainer() { Anchor = Anchor.Centre; @@ -39,6 +48,8 @@ namespace osu.Game.Rulesets.Osu.UI /// private class ScalingContainer : Container { + internal bool PlayfieldShift { get; set; } + protected override void Update() { base.Update(); @@ -55,6 +66,7 @@ namespace osu.Game.Rulesets.Osu.UI // Scale = 819.2 / 512 // Scale = 1.6 Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); + Position = new Vector2(0, (PlayfieldShift ? 8f : 0f) * Scale.X); // Size = 0.625 Size = Vector2.Divide(Vector2.One, Scale); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 26f7c3b93b..b5752214bd 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -67,8 +67,6 @@ namespace osu.Game [NotNull] private readonly NotificationOverlay notifications = new NotificationOverlay(); - private NowPlayingOverlay nowPlaying; - private BeatmapListingOverlay beatmapListing; private DashboardOverlay dashboard; @@ -650,7 +648,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(nowPlaying = new NowPlayingOverlay + loadComponentSingleFile(new NowPlayingOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -862,18 +860,6 @@ namespace osu.Game switch (action) { - case GlobalAction.ToggleNowPlaying: - nowPlaying.ToggleVisibility(); - return true; - - case GlobalAction.ToggleChat: - chatOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSocial: - dashboard.ToggleVisibility(); - return true; - case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); @@ -889,18 +875,6 @@ namespace osu.Game Toolbar.ToggleVisibility(); return true; - case GlobalAction.ToggleSettings: - Settings.ToggleVisibility(); - return true; - - case GlobalAction.ToggleDirect: - beatmapListing.ToggleVisibility(); - return true; - - case GlobalAction.ToggleNotifications: - notifications.ToggleVisibility(); - return true; - case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 64430c77ac..cde305fffd 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar SetIcon(OsuIcon.ChevronDownCircle); TooltipMain = "Beatmap listing"; TooltipSub = "Browse for new beatmaps"; + + Hotkey = GlobalAction.ToggleDirect; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 86a3f5d8aa..0afc6642b2 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -1,27 +1,34 @@ // 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.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Toolbar { - public class ToolbarButton : OsuClickableContainer + public abstract class ToolbarButton : OsuClickableContainer, IKeyBindingHandler { public const float WIDTH = Toolbar.HEIGHT * 1.4f; + protected GlobalAction? Hotkey { get; set; } + public void SetIcon(Drawable icon) { IconContainer.Icon = icon; @@ -66,9 +73,13 @@ namespace osu.Game.Overlays.Toolbar private readonly FillFlowContainer tooltipContainer; private readonly SpriteText tooltip1; private readonly SpriteText tooltip2; + private readonly SpriteText keyBindingTooltip; protected FillFlowContainer Flow; - public ToolbarButton() + [Resolved] + private KeyBindingStore keyBindings { get; set; } + + protected ToolbarButton() : base(HoverSampleSet.Loud) { Width = WIDTH; @@ -123,7 +134,7 @@ namespace osu.Game.Overlays.Toolbar Origin = TooltipAnchor, Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5), Alpha = 0, - Children = new[] + Children = new Drawable[] { tooltip1 = new OsuSpriteText { @@ -132,17 +143,44 @@ namespace osu.Game.Overlays.Toolbar Shadow = true, Font = OsuFont.GetFont(size: 22, weight: FontWeight.Bold), }, - tooltip2 = new OsuSpriteText + new FillFlowContainer { + AutoSizeAxes = Axes.Both, Anchor = TooltipAnchor, Origin = TooltipAnchor, - Shadow = true, + Direction = FillDirection.Horizontal, + Children = new[] + { + tooltip2 = new OsuSpriteText { Shadow = true }, + keyBindingTooltip = new OsuSpriteText { Shadow = true } + } } } } }; } + private readonly Cached tooltipKeyBinding = new Cached(); + + [BackgroundDependencyLoader] + private void load() + { + keyBindings.KeyBindingChanged += () => tooltipKeyBinding.Invalidate(); + updateKeyBindingTooltip(); + } + + private void updateKeyBindingTooltip() + { + if (tooltipKeyBinding.IsValid) + return; + + var binding = keyBindings.Query().Find(b => (GlobalAction)b.Action == Hotkey); + var keyBindingString = binding?.KeyCombination.ReadableString(); + keyBindingTooltip.Text = !string.IsNullOrEmpty(keyBindingString) ? $" ({keyBindingString})" : string.Empty; + + tooltipKeyBinding.Validate(); + } + protected override bool OnMouseDown(MouseDownEvent e) => true; protected override bool OnClick(ClickEvent e) @@ -154,6 +192,8 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { + updateKeyBindingTooltip(); + HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); return base.OnHover(e); @@ -164,6 +204,21 @@ namespace osu.Game.Overlays.Toolbar HoverBackground.FadeOut(200); tooltipContainer.FadeOut(100); } + + public bool OnPressed(GlobalAction action) + { + if (action == Hotkey) + { + Click(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } public class OpaqueBackground : Container diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index ec7da54571..dee4be0c1f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar SetIcon(FontAwesome.Solid.Comments); TooltipMain = "Chat"; TooltipSub = "Join the real-time discussion"; + + Hotkey = GlobalAction.ToggleChat; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index e642f0c453..4845c9a99f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -2,33 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { - public class ToolbarHomeButton : ToolbarButton, IKeyBindingHandler + public class ToolbarHomeButton : ToolbarButton { public ToolbarHomeButton() { Icon = FontAwesome.Solid.Home; TooltipMain = "Home"; TooltipSub = "Return to the main menu"; - } - public bool OnPressed(GlobalAction action) - { - if (action == GlobalAction.Home) - { - Click(); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { + Hotkey = GlobalAction.Home; } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 712da12208..f9aa2de497 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -2,17 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { public class ToolbarMusicButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarMusicButton() { Icon = FontAwesome.Solid.Music; TooltipMain = "Now playing"; TooltipSub = "Manage the currently playing track"; + + Hotkey = GlobalAction.ToggleNowPlaying; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index dbd6c557d3..a699fd907f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -28,6 +29,8 @@ namespace osu.Game.Overlays.Toolbar TooltipMain = "Notifications"; TooltipSub = "Waiting for 'ya"; + Hotkey = GlobalAction.ToggleNotifications; + Add(countDisplay = new CountCircle { Alpha = 0, diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs index 79942012f9..ed2a23ec2a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.Solid.Cog; TooltipMain = "Settings"; TooltipSub = "Change your settings"; + + Hotkey = GlobalAction.ToggleSettings; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index 0dbb552c15..6faa58c559 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.Solid.Users; TooltipMain = "Friends"; TooltipSub = "Interact with those close to you"; + + Hotkey = GlobalAction.ToggleSocial; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 3f7dc957fb..83a1077d70 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotSupportedException(); - public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException(); + public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException(); public BindableNumber Volume => throw new NotSupportedException(); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 627c2f3d33..18c3052ca3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f443937017..b034253d88 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - +