mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 17:13:06 +08:00
Merge branch 'master' into spectator-replay-watcher
This commit is contained in:
commit
79aecc9a98
@ -35,6 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
objects.Add(new Note { StartTime = time });
|
||||
|
||||
// don't hit the first note
|
||||
if (i > 0)
|
||||
{
|
||||
frames.Add(new ManiaReplayFrame(time + 10, ManiaAction.Key1));
|
||||
|
@ -2,29 +2,23 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneHUDOverlay : SkinnableTestScene
|
||||
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
|
||||
{
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
|
||||
@ -37,17 +31,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddRepeatStep("increase combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value++;
|
||||
}, 10);
|
||||
AddRepeatStep("increase combo", () => { hudOverlay.ComboCounter.Current.Value++; }, 10);
|
||||
|
||||
AddStep("reset combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value = 0;
|
||||
});
|
||||
AddStep("reset combo", () => { hudOverlay.ComboCounter.Current.Value = 0; });
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -77,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddStep("set showhud false", () => hudOverlays.ForEach(h => h.ShowHud.Value = false));
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
|
||||
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
|
||||
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
@ -86,10 +72,31 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMomentaryShowHUD()
|
||||
{
|
||||
createNew();
|
||||
|
||||
HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringBreaks;
|
||||
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set hud to never show", () => config.Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("trigger momentary show", () => InputManager.PressKey(Key.ControlLeft));
|
||||
AddUntilStep("wait for visible", () => hideTarget.IsPresent);
|
||||
|
||||
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("set original config value", () => config.Set(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExternalHideDoesntAffectConfig()
|
||||
{
|
||||
HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringBreaks;
|
||||
HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
|
||||
|
||||
createNew();
|
||||
|
||||
@ -113,14 +120,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("set keycounter visible false", () =>
|
||||
{
|
||||
config.Set<bool>(OsuSetting.KeyOverlay, false);
|
||||
hudOverlays.ForEach(h => h.KeyCounter.AlwaysVisible.Value = false);
|
||||
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
|
||||
});
|
||||
|
||||
AddStep("set showhud false", () => hudOverlays.ForEach(h => h.ShowHud.Value = false));
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
|
||||
AddAssert("key counters hidden", () => !keyCounterFlow.IsPresent);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlays.ForEach(h => h.ShowHud.Value = true));
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
|
||||
@ -131,22 +138,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
SetContents(() =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||
hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
|
||||
hudOverlay.ComboCounter.Current.Value = 1;
|
||||
hudOverlay.ComboCounter.Current.Value = 1;
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
return hudOverlay;
|
||||
});
|
||||
Child = hudOverlay;
|
||||
});
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableHUDOverlay : SkinnableTestScene
|
||||
{
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[Test]
|
||||
public void TestComboCounterIncrementing()
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddRepeatStep("increase combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value++;
|
||||
}, 10);
|
||||
|
||||
AddStep("reset combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value = 0;
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFadesInOnLoadComplete()
|
||||
{
|
||||
float? initialAlpha = null;
|
||||
|
||||
createNew(h => h.OnLoadComplete += _ => initialAlpha = hideTarget.Alpha);
|
||||
AddUntilStep("wait for load", () => hudOverlay.IsAlive);
|
||||
AddAssert("initial alpha was less than 1", () => initialAlpha < 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHideExternally()
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddStep("set showhud false", () => hudOverlays.ForEach(h => h.ShowHud.Value = false));
|
||||
|
||||
AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent);
|
||||
AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent);
|
||||
|
||||
// Key counter flow container should not be affected by this, only the key counter display will be hidden as checked above.
|
||||
AddAssert("key counter flow not affected", () => keyCounterFlow.IsPresent);
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> action = null)
|
||||
{
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
SetContents(() =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
|
||||
hudOverlay.ComboCounter.Current.Value = 1;
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
return hudOverlay;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
|
||||
private void addPanelStep(ScoreInfo score, PanelState state = PanelState.Expanded) => AddStep("add panel", () =>
|
||||
{
|
||||
Child = panel = new ScorePanel(score)
|
||||
Child = panel = new ScorePanel(score, true)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -12,9 +12,6 @@ namespace osu.Game.Configuration
|
||||
[Description("Hide during gameplay")]
|
||||
HideDuringGameplay,
|
||||
|
||||
[Description("Hide during breaks")]
|
||||
HideDuringBreaks,
|
||||
|
||||
Always
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ namespace osu.Game.Configuration
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")),
|
||||
new TrackedSetting<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode, m => new SettingDescription(m, "HUD Visibility", m.GetDescription())),
|
||||
new TrackedSetting<ScalingMode>(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())),
|
||||
};
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
|
||||
new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay),
|
||||
new KeyBinding(InputKey.Control, GlobalAction.HoldForHUD),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> AudioControlKeyBindings => new[]
|
||||
@ -187,5 +188,8 @@ namespace osu.Game.Input.Bindings
|
||||
|
||||
[Description("Timing Mode")]
|
||||
EditorTimingMode,
|
||||
|
||||
[Description("Hold for HUD")]
|
||||
HoldForHUD,
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,10 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -22,7 +24,7 @@ using osuTK.Input;
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
[Cached]
|
||||
public class HUDOverlay : Container
|
||||
public class HUDOverlay : Container, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public const float FADE_DURATION = 400;
|
||||
|
||||
@ -67,6 +69,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
internal readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
||||
|
||||
private bool holdingForHUD;
|
||||
|
||||
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter };
|
||||
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
||||
@ -217,17 +221,18 @@ namespace osu.Game.Screens.Play
|
||||
if (ShowHud.Disabled)
|
||||
return;
|
||||
|
||||
if (holdingForHUD)
|
||||
{
|
||||
ShowHud.Value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (configVisibilityMode.Value)
|
||||
{
|
||||
case HUDVisibilityMode.Never:
|
||||
ShowHud.Value = false;
|
||||
break;
|
||||
|
||||
case HUDVisibilityMode.HideDuringBreaks:
|
||||
// always show during replay as we want the seek bar to be visible.
|
||||
ShowHud.Value = replayLoaded.Value || !IsBreakTime.Value;
|
||||
break;
|
||||
|
||||
case HUDVisibilityMode.HideDuringGameplay:
|
||||
// always show during replay as we want the seek bar to be visible.
|
||||
ShowHud.Value = replayLoaded.Value || IsBreakTime.Value;
|
||||
@ -277,9 +282,21 @@ namespace osu.Game.Screens.Play
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Tab:
|
||||
configVisibilityMode.Value = configVisibilityMode.Value != HUDVisibilityMode.Never
|
||||
? HUDVisibilityMode.Never
|
||||
: HUDVisibilityMode.HideDuringGameplay;
|
||||
switch (configVisibilityMode.Value)
|
||||
{
|
||||
case HUDVisibilityMode.Never:
|
||||
configVisibilityMode.Value = HUDVisibilityMode.HideDuringGameplay;
|
||||
break;
|
||||
|
||||
case HUDVisibilityMode.HideDuringGameplay:
|
||||
configVisibilityMode.Value = HUDVisibilityMode.Always;
|
||||
break;
|
||||
|
||||
case HUDVisibilityMode.Always:
|
||||
configVisibilityMode.Value = HUDVisibilityMode.Never;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -351,5 +368,29 @@ namespace osu.Game.Screens.Play
|
||||
HealthDisplay?.BindHealthProcessor(processor);
|
||||
FailingLayer?.BindHealthProcessor(processor);
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.HoldForHUD:
|
||||
holdingForHUD = true;
|
||||
updateVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(GlobalAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.HoldForHUD:
|
||||
holdingForHUD = false;
|
||||
updateVisibility();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
private const float padding = 10;
|
||||
|
||||
private readonly ScoreInfo score;
|
||||
private readonly bool withFlair;
|
||||
|
||||
private readonly List<StatisticDisplay> statisticDisplays = new List<StatisticDisplay>();
|
||||
|
||||
private FillFlowContainer starAndModDisplay;
|
||||
@ -41,9 +43,11 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
/// Creates a new <see cref="ExpandedPanelMiddleContent"/>.
|
||||
/// </summary>
|
||||
/// <param name="score">The score to display.</param>
|
||||
public ExpandedPanelMiddleContent(ScoreInfo score)
|
||||
/// <param name="withFlair">Whether to add flair for a new score being set.</param>
|
||||
public ExpandedPanelMiddleContent(ScoreInfo score, bool withFlair = false)
|
||||
{
|
||||
this.score = score;
|
||||
this.withFlair = withFlair;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Masking = true;
|
||||
@ -266,6 +270,9 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
delay += 200;
|
||||
}
|
||||
}
|
||||
|
||||
if (!withFlair)
|
||||
FinishTransforms(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ namespace osu.Game.Screens.Ranking
|
||||
};
|
||||
|
||||
if (Score != null)
|
||||
ScorePanelList.AddScore(Score);
|
||||
ScorePanelList.AddScore(Score, true);
|
||||
|
||||
if (player != null && allowRetry)
|
||||
{
|
||||
|
@ -85,6 +85,8 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
public readonly ScoreInfo Score;
|
||||
|
||||
private bool displayWithFlair;
|
||||
|
||||
private Container content;
|
||||
|
||||
private Container topLayerContainer;
|
||||
@ -97,9 +99,10 @@ namespace osu.Game.Screens.Ranking
|
||||
private Container middleLayerContentContainer;
|
||||
private Drawable middleLayerContent;
|
||||
|
||||
public ScorePanel(ScoreInfo score)
|
||||
public ScorePanel(ScoreInfo score, bool isNewLocalScore = false)
|
||||
{
|
||||
Score = score;
|
||||
displayWithFlair = isNewLocalScore;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -188,7 +191,7 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
state = value;
|
||||
|
||||
if (LoadState >= LoadState.Ready)
|
||||
if (IsLoaded)
|
||||
updateState();
|
||||
|
||||
StateChanged?.Invoke(value);
|
||||
@ -209,7 +212,10 @@ namespace osu.Game.Screens.Ranking
|
||||
middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint);
|
||||
|
||||
topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0));
|
||||
middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0));
|
||||
middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score, displayWithFlair).With(d => d.Alpha = 0));
|
||||
|
||||
// only the first expanded display should happen with flair.
|
||||
displayWithFlair = false;
|
||||
break;
|
||||
|
||||
case PanelState.Contracted:
|
||||
|
@ -95,9 +95,10 @@ namespace osu.Game.Screens.Ranking
|
||||
/// Adds a <see cref="ScoreInfo"/> to this list.
|
||||
/// </summary>
|
||||
/// <param name="score">The <see cref="ScoreInfo"/> to add.</param>
|
||||
public ScorePanel AddScore(ScoreInfo score)
|
||||
/// <param name="isNewLocalScore">Whether this is a score that has just been achieved locally. Controls whether flair is added to the display or not.</param>
|
||||
public ScorePanel AddScore(ScoreInfo score, bool isNewLocalScore = false)
|
||||
{
|
||||
var panel = new ScorePanel(score)
|
||||
var panel = new ScorePanel(score, isNewLocalScore)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@ -117,19 +118,24 @@ namespace osu.Game.Screens.Ranking
|
||||
d.Origin = Anchor.Centre;
|
||||
}));
|
||||
|
||||
if (SelectedScore.Value == score)
|
||||
selectedScoreChanged(new ValueChangedEvent<ScoreInfo>(SelectedScore.Value, SelectedScore.Value));
|
||||
else
|
||||
if (IsLoaded)
|
||||
{
|
||||
// We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done.
|
||||
// But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel.
|
||||
if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score))
|
||||
if (SelectedScore.Value == score)
|
||||
{
|
||||
// A somewhat hacky property is used here because we need to:
|
||||
// 1) Scroll after the scroll container's visible range is updated.
|
||||
// 2) Scroll before the scroll container's scroll position is updated.
|
||||
// Without this, we would have a 1-frame positioning error which looks very jarring.
|
||||
scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing;
|
||||
SelectedScore.TriggerChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done.
|
||||
// But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel.
|
||||
if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score))
|
||||
{
|
||||
// A somewhat hacky property is used here because we need to:
|
||||
// 1) Scroll after the scroll container's visible range is updated.
|
||||
// 2) Scroll before the scroll container's scroll position is updated.
|
||||
// Without this, we would have a 1-frame positioning error which looks very jarring.
|
||||
scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,11 +148,15 @@ namespace osu.Game.Screens.Ranking
|
||||
/// <param name="score">The <see cref="ScoreInfo"/> to present.</param>
|
||||
private void selectedScoreChanged(ValueChangedEvent<ScoreInfo> score)
|
||||
{
|
||||
// Contract the old panel.
|
||||
foreach (var t in flow.Where(t => t.Panel.Score == score.OldValue))
|
||||
// avoid contracting panels unnecessarily when TriggerChange is fired manually.
|
||||
if (score.OldValue != score.NewValue)
|
||||
{
|
||||
t.Panel.State = PanelState.Contracted;
|
||||
t.Margin = new MarginPadding();
|
||||
// Contract the old panel.
|
||||
foreach (var t in flow.Where(t => t.Panel.Score == score.OldValue))
|
||||
{
|
||||
t.Panel.State = PanelState.Contracted;
|
||||
t.Margin = new MarginPadding();
|
||||
}
|
||||
}
|
||||
|
||||
// Find the panel corresponding to the new score.
|
||||
@ -162,12 +172,16 @@ namespace osu.Game.Screens.Ranking
|
||||
expandedTrackingComponent.Margin = new MarginPadding { Horizontal = expanded_panel_spacing };
|
||||
expandedPanel.State = PanelState.Expanded;
|
||||
|
||||
// Scroll to the new panel. This is done manually since we need:
|
||||
// 1) To scroll after the scroll container's visible range is updated.
|
||||
// 2) To account for the centre anchor/origins of panels.
|
||||
// In the end, it's easier to compute the scroll position manually.
|
||||
float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing);
|
||||
scroll.ScrollTo(scrollOffset);
|
||||
// requires schedule after children to ensure the flow (and thus ScrollContainer's ScrollableExtent) has been updated.
|
||||
ScheduleAfterChildren(() =>
|
||||
{
|
||||
// Scroll to the new panel. This is done manually since we need:
|
||||
// 1) To scroll after the scroll container's visible range is updated.
|
||||
// 2) To account for the centre anchor/origins of panels.
|
||||
// In the end, it's easier to compute the scroll position manually.
|
||||
float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing);
|
||||
scroll.ScrollTo(scrollOffset);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -180,9 +180,8 @@ namespace osu.Game.Tests.Beatmaps
|
||||
private readonly BeatmapInfo skinBeatmapInfo;
|
||||
private readonly IResourceStore<byte[]> resourceStore;
|
||||
|
||||
public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore<byte[]> resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio,
|
||||
double length = 60000)
|
||||
: base(beatmap, storyboard, referenceClock, audio, length)
|
||||
public TestWorkingBeatmap(BeatmapInfo skinBeatmapInfo, IResourceStore<byte[]> resourceStore, IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio)
|
||||
: base(beatmap, storyboard, referenceClock, audio)
|
||||
{
|
||||
this.skinBeatmapInfo = skinBeatmapInfo;
|
||||
this.resourceStore = resourceStore;
|
||||
|
@ -23,6 +23,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Storyboards;
|
||||
@ -222,18 +223,23 @@ namespace osu.Game.Tests.Visual
|
||||
/// <param name="storyboard">The storyboard.</param>
|
||||
/// <param name="referenceClock">An optional clock which should be used instead of a stopwatch for virtual time progression.</param>
|
||||
/// <param name="audio">Audio manager. Required if a reference clock isn't provided.</param>
|
||||
/// <param name="length">The length of the returned virtual track.</param>
|
||||
public ClockBackedTestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, double length = 60000)
|
||||
public ClockBackedTestWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio)
|
||||
: base(beatmap, storyboard, audio)
|
||||
{
|
||||
double trackLength = 60000;
|
||||
|
||||
if (beatmap.HitObjects.Count > 0)
|
||||
// add buffer after last hitobject to allow for final replay frames etc.
|
||||
trackLength = Math.Max(trackLength, beatmap.HitObjects.Max(h => h.GetEndTime()) + 2000);
|
||||
|
||||
if (referenceClock != null)
|
||||
{
|
||||
store = new TrackVirtualStore(referenceClock);
|
||||
audio.AddItem(store);
|
||||
track = store.GetVirtual(length);
|
||||
track = store.GetVirtual(trackLength);
|
||||
}
|
||||
else
|
||||
track = audio?.Tracks.GetVirtual(length);
|
||||
track = audio?.Tracks.GetVirtual(trackLength);
|
||||
}
|
||||
|
||||
~ClockBackedTestWorkingBeatmap()
|
||||
|
Loading…
Reference in New Issue
Block a user