mirror of
https://github.com/ppy/osu.git
synced 2025-02-13 20:33:11 +08:00
Merge remote-tracking branch 'upstream/master' into quit-chat-channels
This commit is contained in:
commit
c6fa899b15
@ -1 +1 @@
|
|||||||
Subproject commit d492c2ffe6ce5dac2a8e05118d86e6907186329b
|
Subproject commit 14a33d110e2ed32e3a875bc2acd2bade244ba045
|
@ -69,28 +69,28 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
|
|
||||||
private class MyContextMenuContainer : Container, IHasContextMenu
|
private class MyContextMenuContainer : Container, IHasContextMenu
|
||||||
{
|
{
|
||||||
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
|
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||||
{
|
{
|
||||||
new OsuContextMenuItem(@"Some option"),
|
new OsuMenuItem(@"Some option"),
|
||||||
new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
|
new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
|
||||||
new OsuContextMenuItem(@"Another option"),
|
new OsuMenuItem(@"Another option"),
|
||||||
new OsuContextMenuItem(@"Choose me please"),
|
new OsuMenuItem(@"Choose me please"),
|
||||||
new OsuContextMenuItem(@"And me too"),
|
new OsuMenuItem(@"And me too"),
|
||||||
new OsuContextMenuItem(@"Trying to fill"),
|
new OsuMenuItem(@"Trying to fill"),
|
||||||
new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
|
new OsuMenuItem(@"Destructive option", MenuItemType.Destructive),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AnotherContextMenuContainer : Container, IHasContextMenu
|
private class AnotherContextMenuContainer : Container, IHasContextMenu
|
||||||
{
|
{
|
||||||
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
|
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||||
{
|
{
|
||||||
new OsuContextMenuItem(@"Simple option"),
|
new OsuMenuItem(@"Simple option"),
|
||||||
new OsuContextMenuItem(@"Simple very very long option"),
|
new OsuMenuItem(@"Simple very very long option"),
|
||||||
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
|
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
|
||||||
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
|
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
|
||||||
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
|
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
|
||||||
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
|
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
Normal file
35
osu.Game.Rulesets.Osu/Replays/OsuReplayInputHandler.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Replays
|
||||||
|
{
|
||||||
|
public class OsuReplayInputHandler : FramedReplayInputHandler
|
||||||
|
{
|
||||||
|
public OsuReplayInputHandler(Replay replay)
|
||||||
|
: base(replay)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<InputState> GetPendingStates()
|
||||||
|
{
|
||||||
|
List<OsuAction> actions = new List<OsuAction>();
|
||||||
|
|
||||||
|
if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton);
|
||||||
|
if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton);
|
||||||
|
|
||||||
|
return new List<InputState>
|
||||||
|
{
|
||||||
|
new ReplayState<OsuAction>
|
||||||
|
{
|
||||||
|
Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)),
|
||||||
|
PressedActions = actions
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -14,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
{
|
{
|
||||||
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgement>
|
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgement>
|
||||||
{
|
{
|
||||||
|
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>(ScoringMode.Exponential);
|
||||||
|
|
||||||
public OsuScoreProcessor()
|
public OsuScoreProcessor()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -23,6 +28,35 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float hpDrainRate;
|
||||||
|
|
||||||
|
private int totalAccurateJudgements;
|
||||||
|
|
||||||
|
private readonly Dictionary<OsuScoreResult, int> scoreResultCounts = new Dictionary<OsuScoreResult, int>();
|
||||||
|
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
|
||||||
|
|
||||||
|
private double comboMaxScore;
|
||||||
|
|
||||||
|
protected override void ComputeTargets(Beatmap<OsuHitObject> beatmap)
|
||||||
|
{
|
||||||
|
hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate;
|
||||||
|
totalAccurateJudgements = beatmap.HitObjects.Count;
|
||||||
|
|
||||||
|
foreach (var h in beatmap.HitObjects)
|
||||||
|
{
|
||||||
|
if (h != null)
|
||||||
|
{
|
||||||
|
// TODO: add support for other object types.
|
||||||
|
AddJudgement(new OsuJudgement
|
||||||
|
{
|
||||||
|
MaxScore = OsuScoreResult.Hit300,
|
||||||
|
Score = OsuScoreResult.Hit300,
|
||||||
|
Result = HitResult.Hit
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Reset()
|
protected override void Reset()
|
||||||
{
|
{
|
||||||
base.Reset();
|
base.Reset();
|
||||||
@ -34,9 +68,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
comboResultCounts.Clear();
|
comboResultCounts.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<OsuScoreResult, int> scoreResultCounts = new Dictionary<OsuScoreResult, int>();
|
|
||||||
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
|
|
||||||
|
|
||||||
public override void PopulateScore(Score score)
|
public override void PopulateScore(Score score)
|
||||||
{
|
{
|
||||||
base.PopulateScore(score);
|
base.PopulateScore(score);
|
||||||
@ -57,28 +88,75 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1;
|
comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (judgement.Result)
|
switch (judgement.Score)
|
||||||
{
|
{
|
||||||
case HitResult.Hit:
|
case OsuScoreResult.Hit300:
|
||||||
Health.Value += 0.1f;
|
Health.Value += (10.2 - hpDrainRate) * 0.02;
|
||||||
break;
|
break;
|
||||||
case HitResult.Miss:
|
|
||||||
Health.Value -= 0.2f;
|
case OsuScoreResult.Hit100:
|
||||||
|
Health.Value += (8 - hpDrainRate) * 0.02;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuScoreResult.Hit50:
|
||||||
|
Health.Value += (4 - hpDrainRate) * 0.02;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuScoreResult.SliderTick:
|
||||||
|
Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OsuScoreResult.Miss:
|
||||||
|
Health.Value -= hpDrainRate * 0.04;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int score = 0;
|
calculateScore();
|
||||||
int maxScore = 0;
|
}
|
||||||
|
|
||||||
|
private void calculateScore()
|
||||||
|
{
|
||||||
|
int baseScore = 0;
|
||||||
|
double comboScore = 0;
|
||||||
|
|
||||||
|
int baseMaxScore = 0;
|
||||||
|
|
||||||
foreach (var j in Judgements)
|
foreach (var j in Judgements)
|
||||||
{
|
{
|
||||||
score += j.ScoreValue;
|
baseScore += j.ScoreValue;
|
||||||
maxScore += j.MaxScoreValue;
|
baseMaxScore += j.MaxScoreValue;
|
||||||
|
|
||||||
|
comboScore += j.ScoreValue * (1 + Combo.Value / 10d);
|
||||||
}
|
}
|
||||||
|
|
||||||
TotalScore.Value = score;
|
Accuracy.Value = (double)baseScore / baseMaxScore;
|
||||||
Accuracy.Value = (double)score / maxScore;
|
|
||||||
|
if (comboScore > comboMaxScore)
|
||||||
|
comboMaxScore = comboScore;
|
||||||
|
|
||||||
|
if (baseScore == 0)
|
||||||
|
TotalScore.Value = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// temporary to make scoring feel more like score v1 without being score v1.
|
||||||
|
float exponentialFactor = Mode.Value == ScoringMode.Exponential ? (float)Judgements.Count / 100 : 1;
|
||||||
|
|
||||||
|
TotalScore.Value =
|
||||||
|
(int)
|
||||||
|
(
|
||||||
|
exponentialFactor *
|
||||||
|
700000 * comboScore / comboMaxScore +
|
||||||
|
300000 * Math.Pow(Accuracy.Value, 10) * ((double)Judgements.Count / totalAccurateJudgements) +
|
||||||
|
0 /* bonusScore */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ScoringMode
|
||||||
|
{
|
||||||
|
Standardised,
|
||||||
|
Exponential
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,11 @@ using osu.Game.Rulesets.Osu.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
{
|
{
|
||||||
@ -49,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
|
||||||
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||||
<Compile Include="OsuInputManager.cs" />
|
<Compile Include="OsuInputManager.cs" />
|
||||||
|
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||||
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
||||||
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
||||||
<Compile Include="UI\OsuSettings.cs" />
|
<Compile Include="UI\OsuSettings.cs" />
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using OpenTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Replays
|
namespace osu.Game.Rulesets.Taiko.Replays
|
||||||
{
|
{
|
||||||
@ -17,21 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates()
|
||||||
{
|
{
|
||||||
var keys = new List<Key>();
|
var actions = new List<TaikoAction>();
|
||||||
|
|
||||||
if (CurrentFrame?.MouseRight1 == true)
|
if (CurrentFrame?.MouseRight1 == true)
|
||||||
keys.Add(Key.F);
|
actions.Add(TaikoAction.LeftCentre);
|
||||||
if (CurrentFrame?.MouseRight2 == true)
|
if (CurrentFrame?.MouseRight2 == true)
|
||||||
keys.Add(Key.J);
|
actions.Add(TaikoAction.RightCentre);
|
||||||
if (CurrentFrame?.MouseLeft1 == true)
|
if (CurrentFrame?.MouseLeft1 == true)
|
||||||
keys.Add(Key.D);
|
actions.Add(TaikoAction.LeftRim);
|
||||||
if (CurrentFrame?.MouseLeft2 == true)
|
if (CurrentFrame?.MouseLeft2 == true)
|
||||||
keys.Add(Key.K);
|
actions.Add(TaikoAction.RightRim);
|
||||||
|
|
||||||
return new List<InputState>
|
return new List<InputState> { new ReplayState<TaikoAction> { PressedActions = actions } };
|
||||||
{
|
|
||||||
new InputState { Keyboard = new ReplayKeyboardState(keys) }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,6 +268,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
base.Reset();
|
base.Reset();
|
||||||
|
|
||||||
Health.Value = 0;
|
Health.Value = 0;
|
||||||
|
Accuracy.Value = 1;
|
||||||
|
|
||||||
bonusScore = 0;
|
bonusScore = 0;
|
||||||
comboPortion = 0;
|
comboPortion = 0;
|
||||||
|
@ -52,6 +52,8 @@ namespace osu.Game.Beatmaps
|
|||||||
[JsonProperty("file_sha2")]
|
[JsonProperty("file_sha2")]
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
|
|
||||||
|
public bool Hidden { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
|
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -33,11 +33,21 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when a single difficulty has been hidden.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<BeatmapInfo> BeatmapHidden;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
|
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when a single difficulty has been restored.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<BeatmapInfo> BeatmapRestored;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,6 +81,8 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmaps = new BeatmapStore(connection);
|
beatmaps = new BeatmapStore(connection);
|
||||||
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||||
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
||||||
|
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||||
|
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||||
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
@ -162,7 +174,6 @@ namespace osu.Game.Beatmaps
|
|||||||
// If we have an ID then we already exist in the database.
|
// If we have an ID then we already exist in the database.
|
||||||
if (beatmapSetInfo.ID != 0) return;
|
if (beatmapSetInfo.ID != 0) return;
|
||||||
|
|
||||||
lock (beatmaps)
|
|
||||||
beatmaps.Add(beatmapSetInfo);
|
beatmaps.Add(beatmapSetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,16 +181,27 @@ namespace osu.Game.Beatmaps
|
|||||||
/// Delete a beatmap from the manager.
|
/// Delete a beatmap from the manager.
|
||||||
/// Is a no-op for already deleted beatmaps.
|
/// Is a no-op for already deleted beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapSet">The beatmap to delete.</param>
|
/// <param name="beatmapSet">The beatmap set to delete.</param>
|
||||||
public void Delete(BeatmapSetInfo beatmapSet)
|
public void Delete(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
lock (beatmaps)
|
|
||||||
if (!beatmaps.Delete(beatmapSet)) return;
|
if (!beatmaps.Delete(beatmapSet)) return;
|
||||||
|
|
||||||
if (!beatmapSet.Protected)
|
if (!beatmapSet.Protected)
|
||||||
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
|
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete a beatmap difficulty.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap difficulty to hide.</param>
|
||||||
|
public void Hide(BeatmapInfo beatmap) => beatmaps.Hide(beatmap);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore a beatmap difficulty.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap difficulty to restore.</param>
|
||||||
|
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged.
|
/// Returns a <see cref="BeatmapSetInfo"/> to a usable state if it has previously been deleted but not yet purged.
|
||||||
/// Is a no-op for already usable beatmaps.
|
/// Is a no-op for already usable beatmaps.
|
||||||
@ -187,7 +209,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="beatmapSet">The beatmap to restore.</param>
|
/// <param name="beatmapSet">The beatmap to restore.</param>
|
||||||
public void Undelete(BeatmapSetInfo beatmapSet)
|
public void Undelete(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
lock (beatmaps)
|
|
||||||
if (!beatmaps.Undelete(beatmapSet)) return;
|
if (!beatmaps.Undelete(beatmapSet)) return;
|
||||||
|
|
||||||
if (!beatmapSet.Protected)
|
if (!beatmapSet.Protected)
|
||||||
@ -248,6 +269,13 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refresh an existing instance of a <see cref="BeatmapSetInfo"/> from the store.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmapSet">A stale instance.</param>
|
||||||
|
/// <returns>A fresh instance.</returns>
|
||||||
|
public BeatmapSetInfo Refresh(BeatmapSetInfo beatmapSet) => QueryBeatmapSet(s => s.ID == beatmapSet.ID);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -255,7 +283,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>Results from the provided query.</returns>
|
/// <returns>Results from the provided query.</returns>
|
||||||
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
|
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
|
||||||
{
|
{
|
||||||
lock (beatmaps) return beatmaps.QueryAndPopulate(query);
|
return beatmaps.QueryAndPopulate(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -264,8 +292,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||||
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
|
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
|
||||||
{
|
|
||||||
lock (beatmaps)
|
|
||||||
{
|
{
|
||||||
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
|
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
|
||||||
|
|
||||||
@ -274,7 +300,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
|
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s.
|
||||||
|
@ -16,11 +16,14 @@ namespace osu.Game.Beatmaps
|
|||||||
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
public event Action<BeatmapSetInfo> BeatmapSetAdded;
|
||||||
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
|
||||||
|
|
||||||
|
public event Action<BeatmapInfo> BeatmapHidden;
|
||||||
|
public event Action<BeatmapInfo> BeatmapRestored;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of this store. Used for migrations (see <see cref="PerformMigration(int, int)"/>).
|
/// The current version of this store. Used for migrations (see <see cref="PerformMigration(int, int)"/>).
|
||||||
/// The initial version is 1.
|
/// The initial version is 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override int StoreVersion => 3;
|
protected override int StoreVersion => 4;
|
||||||
|
|
||||||
public BeatmapStore(SQLiteConnection connection)
|
public BeatmapStore(SQLiteConnection connection)
|
||||||
: base(connection)
|
: base(connection)
|
||||||
@ -81,6 +84,10 @@ namespace osu.Game.Beatmaps
|
|||||||
// Added MD5Hash column to BeatmapInfo
|
// Added MD5Hash column to BeatmapInfo
|
||||||
Connection.MigrateTable<BeatmapInfo>();
|
Connection.MigrateTable<BeatmapInfo>();
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
// Added Hidden column to BeatmapInfo
|
||||||
|
Connection.MigrateTable<BeatmapInfo>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +107,7 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delete a <see cref="BeatmapSetInfo"/> to the database.
|
/// Delete a <see cref="BeatmapSetInfo"/> from the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapSet">The beatmap to delete.</param>
|
/// <param name="beatmapSet">The beatmap to delete.</param>
|
||||||
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
|
||||||
@ -131,6 +138,38 @@ namespace osu.Game.Beatmaps
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide a <see cref="BeatmapInfo"/> in the database.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to hide.</param>
|
||||||
|
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
|
||||||
|
public bool Hide(BeatmapInfo beatmap)
|
||||||
|
{
|
||||||
|
if (beatmap.Hidden) return false;
|
||||||
|
|
||||||
|
beatmap.Hidden = true;
|
||||||
|
Connection.Update(beatmap);
|
||||||
|
|
||||||
|
BeatmapHidden?.Invoke(beatmap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore a previously hidden <see cref="BeatmapInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to restore.</param>
|
||||||
|
/// <returns>Whether the beatmap's <see cref="BeatmapInfo.Hidden"/> was changed.</returns>
|
||||||
|
public bool Restore(BeatmapInfo beatmap)
|
||||||
|
{
|
||||||
|
if (!beatmap.Hidden) return false;
|
||||||
|
|
||||||
|
beatmap.Hidden = false;
|
||||||
|
Connection.Update(beatmap);
|
||||||
|
|
||||||
|
BeatmapRestored?.Invoke(beatmap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void cleanupPendingDeletions()
|
private void cleanupPendingDeletions()
|
||||||
{
|
{
|
||||||
Connection.RunInTransaction(() =>
|
Connection.RunInTransaction(() =>
|
||||||
|
@ -11,6 +11,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
public class BeatmapGroup : IStateful<BeatmapGroupState>
|
public class BeatmapGroup : IStateful<BeatmapGroupState>
|
||||||
{
|
{
|
||||||
|
public event Action<BeatmapGroupState> StateChanged;
|
||||||
|
|
||||||
public BeatmapPanel SelectedPanel;
|
public BeatmapPanel SelectedPanel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,19 +25,26 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<BeatmapInfo> StartRequested;
|
public Action<BeatmapInfo> StartRequested;
|
||||||
|
|
||||||
public BeatmapSetHeader Header;
|
public Action<BeatmapSetInfo> DeleteRequested;
|
||||||
|
|
||||||
private BeatmapGroupState state;
|
public Action<BeatmapSetInfo> RestoreHiddenRequested;
|
||||||
|
|
||||||
|
public Action<BeatmapInfo> HideDifficultyRequested;
|
||||||
|
|
||||||
|
public BeatmapSetHeader Header;
|
||||||
|
|
||||||
public List<BeatmapPanel> BeatmapPanels;
|
public List<BeatmapPanel> BeatmapPanels;
|
||||||
|
|
||||||
public BeatmapSetInfo BeatmapSet;
|
public BeatmapSetInfo BeatmapSet;
|
||||||
|
|
||||||
|
private BeatmapGroupState state;
|
||||||
public BeatmapGroupState State
|
public BeatmapGroupState State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
state = value;
|
||||||
|
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case BeatmapGroupState.Expanded:
|
case BeatmapGroupState.Expanded:
|
||||||
@ -54,7 +63,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
panel.State = PanelSelectedState.Hidden;
|
panel.State = PanelSelectedState.Hidden;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
state = value;
|
|
||||||
|
StateChanged?.Invoke(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,14 +76,17 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Header = new BeatmapSetHeader(beatmap)
|
Header = new BeatmapSetHeader(beatmap)
|
||||||
{
|
{
|
||||||
GainedSelection = headerGainedSelection,
|
GainedSelection = headerGainedSelection,
|
||||||
|
DeleteRequested = b => DeleteRequested(b),
|
||||||
|
RestoreHiddenRequested = b => RestoreHiddenRequested(b),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
};
|
};
|
||||||
|
|
||||||
BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.OrderBy(b => b.StarDifficulty).ToList();
|
BeatmapSet.Beatmaps = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).ToList();
|
||||||
BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
|
BeatmapPanels = BeatmapSet.Beatmaps.Select(b => new BeatmapPanel(b)
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
GainedSelection = panelGainedSelection,
|
GainedSelection = panelGainedSelection,
|
||||||
|
HideRequested = p => HideDifficultyRequested?.Invoke(p),
|
||||||
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
|
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
}).ToList();
|
}).ToList();
|
||||||
@ -81,6 +94,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Header.AddDifficultyIcons(BeatmapPanels);
|
Header.AddDifficultyIcons(BeatmapPanels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void headerGainedSelection(BeatmapSetHeader panel)
|
private void headerGainedSelection(BeatmapSetHeader panel)
|
||||||
{
|
{
|
||||||
State = BeatmapGroupState.Expanded;
|
State = BeatmapGroupState.Expanded;
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
@ -14,16 +15,20 @@ using OpenTK.Graphics;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
public class BeatmapPanel : Panel
|
public class BeatmapPanel : Panel, IHasContextMenu
|
||||||
{
|
{
|
||||||
public BeatmapInfo Beatmap;
|
public BeatmapInfo Beatmap;
|
||||||
private readonly Sprite background;
|
private readonly Sprite background;
|
||||||
|
|
||||||
public Action<BeatmapPanel> GainedSelection;
|
public Action<BeatmapPanel> GainedSelection;
|
||||||
public Action<BeatmapPanel> StartRequested;
|
public Action<BeatmapPanel> StartRequested;
|
||||||
|
public Action<BeatmapPanel> EditRequested;
|
||||||
|
public Action<BeatmapInfo> HideRequested;
|
||||||
|
|
||||||
private readonly Triangles triangles;
|
private readonly Triangles triangles;
|
||||||
private readonly StarCounter starCounter;
|
private readonly StarCounter starCounter;
|
||||||
|
|
||||||
@ -148,5 +153,12 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||||
|
{
|
||||||
|
new OsuMenuItem("Play", MenuItemType.Highlighted, () => StartRequested?.Invoke(this)),
|
||||||
|
new OsuMenuItem("Edit", MenuItemType.Standard, () => EditRequested?.Invoke(this)),
|
||||||
|
new OsuMenuItem("Hide", MenuItemType.Destructive, () => HideRequested?.Invoke(Beatmap)),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,31 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables
|
namespace osu.Game.Beatmaps.Drawables
|
||||||
{
|
{
|
||||||
public class BeatmapSetHeader : Panel
|
public class BeatmapSetHeader : Panel, IHasContextMenu
|
||||||
{
|
{
|
||||||
public Action<BeatmapSetHeader> GainedSelection;
|
public Action<BeatmapSetHeader> GainedSelection;
|
||||||
|
|
||||||
|
public Action<BeatmapSetInfo> DeleteRequested;
|
||||||
|
|
||||||
|
public Action<BeatmapSetInfo> RestoreHiddenRequested;
|
||||||
|
|
||||||
private readonly SpriteText title;
|
private readonly SpriteText title;
|
||||||
private readonly SpriteText artist;
|
private readonly SpriteText artist;
|
||||||
|
|
||||||
@ -148,5 +157,23 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
foreach (var p in panels)
|
foreach (var p in panels)
|
||||||
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
|
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MenuItem[] ContextMenuItems
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<MenuItem> items = new List<MenuItem>();
|
||||||
|
|
||||||
|
if (State == PanelSelectedState.NotSelected)
|
||||||
|
items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => State = PanelSelectedState.Selected));
|
||||||
|
|
||||||
|
if (beatmap.BeatmapSetInfo.Beatmaps.Any(b => b.Hidden))
|
||||||
|
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => RestoreHiddenRequested?.Invoke(beatmap.BeatmapSetInfo)));
|
||||||
|
|
||||||
|
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => DeleteRequested?.Invoke(beatmap.BeatmapSetInfo)));
|
||||||
|
|
||||||
|
return items.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -33,7 +33,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Normal,
|
Normal,
|
||||||
Hard,
|
Hard,
|
||||||
Insane,
|
Insane,
|
||||||
Expert
|
Expert,
|
||||||
|
ExpertPlus
|
||||||
}
|
}
|
||||||
|
|
||||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
||||||
@ -44,7 +45,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
if (rating < 2.25) return DifficultyRating.Normal;
|
if (rating < 2.25) return DifficultyRating.Normal;
|
||||||
if (rating < 3.75) return DifficultyRating.Hard;
|
if (rating < 3.75) return DifficultyRating.Hard;
|
||||||
if (rating < 5.25) return DifficultyRating.Insane;
|
if (rating < 5.25) return DifficultyRating.Insane;
|
||||||
return DifficultyRating.Expert;
|
if (rating < 6.75) return DifficultyRating.Expert;
|
||||||
|
return DifficultyRating.ExpertPlus;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 getColour(BeatmapInfo beatmap)
|
private Color4 getColour(BeatmapInfo beatmap)
|
||||||
@ -55,12 +57,14 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
return palette.Green;
|
return palette.Green;
|
||||||
default:
|
default:
|
||||||
case DifficultyRating.Normal:
|
case DifficultyRating.Normal:
|
||||||
return palette.Yellow;
|
return palette.Blue;
|
||||||
case DifficultyRating.Hard:
|
case DifficultyRating.Hard:
|
||||||
return palette.Pink;
|
return palette.Yellow;
|
||||||
case DifficultyRating.Insane:
|
case DifficultyRating.Insane:
|
||||||
return palette.Purple;
|
return palette.Pink;
|
||||||
case DifficultyRating.Expert:
|
case DifficultyRating.Expert:
|
||||||
|
return palette.Purple;
|
||||||
|
case DifficultyRating.ExpertPlus:
|
||||||
return palette.Gray0;
|
return palette.Gray0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -15,6 +16,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
public const float MAX_HEIGHT = 80;
|
public const float MAX_HEIGHT = 80;
|
||||||
|
|
||||||
|
public event Action<PanelSelectedState> StateChanged;
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
private readonly Container nestedContainer;
|
private readonly Container nestedContainer;
|
||||||
@ -77,11 +80,15 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (state == value) return;
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
var last = state;
|
var last = state;
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
ApplyState(last);
|
ApplyState(last);
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ namespace osu.Game.Graphics.Containers
|
|||||||
samplePopIn = audio.Sample.Get(@"UI/melodic-5");
|
samplePopIn = audio.Sample.Get(@"UI/melodic-5");
|
||||||
samplePopOut = audio.Sample.Get(@"UI/melodic-4");
|
samplePopOut = audio.Sample.Get(@"UI/melodic-4");
|
||||||
|
|
||||||
StateChanged += OsuFocusedOverlayContainer_StateChanged;
|
StateChanged += onStateChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OsuFocusedOverlayContainer_StateChanged(VisibilityContainer arg1, Visibility arg2)
|
private void onStateChanged(Visibility visibility)
|
||||||
{
|
{
|
||||||
switch (arg2)
|
switch (visibility)
|
||||||
{
|
{
|
||||||
case Visibility.Visible:
|
case Visibility.Visible:
|
||||||
samplePopIn?.Play();
|
samplePopIn?.Play();
|
||||||
|
@ -9,6 +9,6 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
{
|
{
|
||||||
public class OsuContextMenuContainer : ContextMenuContainer
|
public class OsuContextMenuContainer : ContextMenuContainer
|
||||||
{
|
{
|
||||||
protected override ContextMenu<ContextMenuItem> CreateContextMenu() => new OsuContextMenu<ContextMenuItem>();
|
protected override Menu CreateMenu() => new OsuContextMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -35,6 +36,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
private class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
|
private class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
|
||||||
{
|
{
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
public readonly SpriteIcon Chevron;
|
public readonly SpriteIcon Chevron;
|
||||||
|
|
||||||
//don't allow clicking between transitions and don't make the chevron clickable
|
//don't allow clicking between transitions and don't make the chevron clickable
|
||||||
@ -42,6 +45,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
public override bool HandleInput => State == Visibility.Visible;
|
public override bool HandleInput => State == Visibility.Visible;
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
|
|
||||||
public Visibility State
|
public Visibility State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
@ -62,6 +66,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||||
this.ScaleTo(new Vector2(0.8f, 1f), transition_duration, Easing.OutQuint);
|
this.ScaleTo(new Vector2(0.8f, 1f), transition_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,52 +1,39 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuContextMenu<TItem> : ContextMenu<TItem>
|
public class OsuContextMenu : OsuMenu
|
||||||
where TItem : ContextMenuItem
|
|
||||||
{
|
|
||||||
protected override Menu<TItem> CreateMenu() => new CustomMenu();
|
|
||||||
|
|
||||||
public class CustomMenu : Menu<TItem>
|
|
||||||
{
|
{
|
||||||
private const int fade_duration = 250;
|
private const int fade_duration = 250;
|
||||||
|
|
||||||
public CustomMenu()
|
public OsuContextMenu()
|
||||||
|
: base(Direction.Vertical)
|
||||||
{
|
{
|
||||||
CornerRadius = 5;
|
MaskingContainer.CornerRadius = 5;
|
||||||
ItemsContainer.Padding = new MarginPadding { Vertical = OsuContextMenuItem.MARGIN_VERTICAL };
|
MaskingContainer.EdgeEffect = new EdgeEffectParameters
|
||||||
Masking = true;
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
Colour = Color4.Black.Opacity(0.1f),
|
Colour = Color4.Black.Opacity(0.1f),
|
||||||
Radius = 4,
|
Radius = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL };
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Background.Colour = colours.ContextMenuGray;
|
BackgroundColour = colours.ContextMenuGray;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint);
|
protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint);
|
||||||
protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint);
|
protected override void AnimateClose() => this.FadeOut(fade_duration, Easing.OutQuint);
|
||||||
|
|
||||||
protected override void UpdateContentHeight()
|
|
||||||
{
|
|
||||||
var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
|
|
||||||
this.ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,114 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
|
||||||
{
|
|
||||||
public class OsuContextMenuItem : ContextMenuItem
|
|
||||||
{
|
|
||||||
private const int transition_length = 80;
|
|
||||||
private const int margin_horizontal = 17;
|
|
||||||
public const int MARGIN_VERTICAL = 4;
|
|
||||||
private const int text_size = 17;
|
|
||||||
|
|
||||||
private OsuSpriteText text;
|
|
||||||
private OsuSpriteText textBold;
|
|
||||||
|
|
||||||
private SampleChannel sampleClick;
|
|
||||||
private SampleChannel sampleHover;
|
|
||||||
|
|
||||||
private readonly MenuItemType type;
|
|
||||||
|
|
||||||
protected override Container CreateTextContainer(string title) => new Container
|
|
||||||
{
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
text = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
TextSize = text_size,
|
|
||||||
Text = title,
|
|
||||||
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
|
|
||||||
},
|
|
||||||
textBold = new OsuSpriteText
|
|
||||||
{
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Alpha = 0,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
TextSize = text_size,
|
|
||||||
Text = title,
|
|
||||||
Font = @"Exo2.0-Bold",
|
|
||||||
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public OsuContextMenuItem(string title, MenuItemType type = MenuItemType.Standard) : base(title)
|
|
||||||
{
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(AudioManager audio)
|
|
||||||
{
|
|
||||||
sampleHover = audio.Sample.Get(@"UI/generic-hover");
|
|
||||||
sampleClick = audio.Sample.Get(@"UI/generic-click");
|
|
||||||
|
|
||||||
BackgroundColour = Color4.Transparent;
|
|
||||||
BackgroundColourHover = OsuColour.FromHex(@"172023");
|
|
||||||
|
|
||||||
updateTextColour();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextColour()
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case MenuItemType.Standard:
|
|
||||||
textBold.Colour = text.Colour = Color4.White;
|
|
||||||
break;
|
|
||||||
case MenuItemType.Destructive:
|
|
||||||
textBold.Colour = text.Colour = Color4.Red;
|
|
||||||
break;
|
|
||||||
case MenuItemType.Highlighted:
|
|
||||||
textBold.Colour = text.Colour = OsuColour.FromHex(@"ffcc22");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
|
||||||
{
|
|
||||||
sampleHover.Play();
|
|
||||||
textBold.FadeIn(transition_length, Easing.OutQuint);
|
|
||||||
text.FadeOut(transition_length, Easing.OutQuint);
|
|
||||||
return base.OnHover(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
|
||||||
{
|
|
||||||
textBold.FadeOut(transition_length, Easing.OutQuint);
|
|
||||||
text.FadeIn(transition_length, Easing.OutQuint);
|
|
||||||
base.OnHoverLost(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
|
||||||
{
|
|
||||||
sampleClick.Play();
|
|
||||||
return base.OnClick(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,51 +14,158 @@ using OpenTK;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuDropdown<T> : Dropdown<T>
|
public class OsuDropdown<T> : Dropdown<T>, IHasAccentColour
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new OsuDropdownHeader { AccentColour = AccentColour };
|
private Color4 accentColour;
|
||||||
|
public Color4 AccentColour
|
||||||
protected override Menu CreateMenu() => new OsuMenu();
|
|
||||||
|
|
||||||
private Color4? accentColour;
|
|
||||||
public virtual Color4 AccentColour
|
|
||||||
{
|
{
|
||||||
get { return accentColour.GetValueOrDefault(); }
|
get { return accentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
if (Header != null)
|
updateAccentColour();
|
||||||
((OsuDropdownHeader)Header).AccentColour = value;
|
|
||||||
foreach (var item in MenuItems.OfType<OsuDropdownMenuItem>())
|
|
||||||
item.AccentColour = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == null)
|
if (accentColour == default(Color4))
|
||||||
AccentColour = colours.PinkDarker;
|
accentColour = colours.PinkDarker;
|
||||||
|
updateAccentColour();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour };
|
private void updateAccentColour()
|
||||||
|
|
||||||
public class OsuDropdownMenuItem : DropdownMenuItem<T>
|
|
||||||
{
|
{
|
||||||
public OsuDropdownMenuItem(string text, T current) : base(text, current)
|
var header = Header as IHasAccentColour;
|
||||||
|
if (header != null) header.AccentColour = accentColour;
|
||||||
|
|
||||||
|
var menu = Menu as IHasAccentColour;
|
||||||
|
if (menu != null) menu.AccentColour = accentColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DropdownHeader CreateHeader() => new OsuDropdownHeader();
|
||||||
|
|
||||||
|
protected override DropdownMenu CreateMenu() => new OsuDropdownMenu();
|
||||||
|
|
||||||
|
#region OsuDropdownMenu
|
||||||
|
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
|
||||||
|
{
|
||||||
|
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
|
||||||
|
public OsuDropdownMenu()
|
||||||
|
{
|
||||||
|
CornerRadius = 4;
|
||||||
|
BackgroundColour = Color4.Black.Opacity(0.5f);
|
||||||
|
|
||||||
|
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
|
||||||
|
ItemsContainer.Padding = new MarginPadding(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
|
||||||
|
protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint);
|
||||||
|
protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint);
|
||||||
|
|
||||||
|
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
|
||||||
|
protected override void UpdateSize(Vector2 newSize)
|
||||||
|
{
|
||||||
|
if (Direction == Direction.Vertical)
|
||||||
|
{
|
||||||
|
Width = newSize.X;
|
||||||
|
this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Height = newSize.Y;
|
||||||
|
this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get { return accentColour; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
accentColour = value;
|
||||||
|
foreach (var c in Children.OfType<IHasAccentColour>())
|
||||||
|
c.AccentColour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
|
||||||
|
|
||||||
|
#region DrawableOsuDropdownMenuItem
|
||||||
|
protected class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
|
||||||
|
{
|
||||||
|
private Color4? accentColour;
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get { return accentColour ?? nonAccentSelectedColour; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
accentColour = value;
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColours()
|
||||||
|
{
|
||||||
|
BackgroundColourHover = accentColour ?? nonAccentHoverColour;
|
||||||
|
BackgroundColourSelected = accentColour ?? nonAccentSelectedColour;
|
||||||
|
UpdateBackgroundColour();
|
||||||
|
UpdateForegroundColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 nonAccentHoverColour;
|
||||||
|
private Color4 nonAccentSelectedColour;
|
||||||
|
|
||||||
|
public DrawableOsuDropdownMenuItem(MenuItem item)
|
||||||
|
: base(item)
|
||||||
{
|
{
|
||||||
Foreground.Padding = new MarginPadding(2);
|
Foreground.Padding = new MarginPadding(2);
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = 6;
|
CornerRadius = 6;
|
||||||
|
}
|
||||||
|
|
||||||
Children = new[]
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
BackgroundColour = Color4.Transparent;
|
||||||
|
|
||||||
|
nonAccentHoverColour = colours.PinkDarker;
|
||||||
|
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateForegroundColour()
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal,
|
base.UpdateForegroundColour();
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
var content = Foreground.Children.FirstOrDefault() as Content;
|
||||||
|
if (content != null) content.Chevron.Alpha = IsHovered ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateContent() => new Content();
|
||||||
|
|
||||||
|
protected new class Content : FillFlowContainer, IHasText
|
||||||
|
{
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get { return Label.Text; }
|
||||||
|
set { Label.Text = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly OsuSpriteText Label;
|
||||||
|
public readonly SpriteIcon Chevron;
|
||||||
|
|
||||||
|
public Content()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
Direction = FillDirection.Horizontal;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Chevron = new SpriteIcon
|
Chevron = new SpriteIcon
|
||||||
@ -72,49 +179,20 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
},
|
},
|
||||||
Label = new OsuSpriteText {
|
Label = new OsuSpriteText
|
||||||
Text = text,
|
{
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4? accentColour;
|
|
||||||
|
|
||||||
protected readonly SpriteIcon Chevron;
|
|
||||||
protected readonly OsuSpriteText Label;
|
|
||||||
|
|
||||||
protected override void FormatForeground(bool hover = false)
|
|
||||||
{
|
|
||||||
base.FormatForeground(hover);
|
|
||||||
Chevron.Alpha = hover ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color4 AccentColour
|
|
||||||
{
|
|
||||||
get { return accentColour.GetValueOrDefault(); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
accentColour = value;
|
|
||||||
BackgroundColourHover = BackgroundColourSelected = value;
|
|
||||||
FormatBackground();
|
|
||||||
FormatForeground();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
BackgroundColour = Color4.Transparent;
|
|
||||||
BackgroundColourHover = accentColour ?? colours.PinkDarker;
|
|
||||||
BackgroundColourSelected = Color4.Black.Opacity(0.5f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public class OsuDropdownHeader : DropdownHeader
|
public class OsuDropdownHeader : DropdownHeader, IHasAccentColour
|
||||||
{
|
{
|
||||||
protected readonly SpriteText Text;
|
protected readonly SpriteText Text;
|
||||||
protected override string Label
|
protected override string Label
|
||||||
@ -125,14 +203,14 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
protected readonly SpriteIcon Icon;
|
protected readonly SpriteIcon Icon;
|
||||||
|
|
||||||
private Color4? accentColour;
|
private Color4 accentColour;
|
||||||
public virtual Color4 AccentColour
|
public virtual Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return accentColour.GetValueOrDefault(); }
|
get { return accentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
BackgroundColourHover = value;
|
BackgroundColourHover = accentColour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +245,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
BackgroundColour = Color4.Black.Opacity(0.5f);
|
BackgroundColour = Color4.Black.Opacity(0.5f);
|
||||||
BackgroundColourHover = accentColour ?? colours.PinkDarker;
|
BackgroundColourHover = colours.PinkDarker;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,165 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using OpenTK;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuMenu : Menu
|
public class OsuMenu : Menu
|
||||||
{
|
{
|
||||||
public OsuMenu()
|
public OsuMenu(Direction direction)
|
||||||
|
: base(direction)
|
||||||
{
|
{
|
||||||
CornerRadius = 4;
|
BackgroundColour = Color4.Black.Opacity(0.5f);
|
||||||
Background.Colour = Color4.Black.Opacity(0.5f);
|
|
||||||
|
|
||||||
|
MaskingContainer.CornerRadius = 4;
|
||||||
ItemsContainer.Padding = new MarginPadding(5);
|
ItemsContainer.Padding = new MarginPadding(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint);
|
protected override void AnimateOpen() => this.FadeIn(300, Easing.OutQuint);
|
||||||
|
|
||||||
protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint);
|
protected override void AnimateClose() => this.FadeOut(300, Easing.OutQuint);
|
||||||
|
|
||||||
protected override void UpdateContentHeight()
|
protected override void UpdateSize(Vector2 newSize)
|
||||||
{
|
{
|
||||||
var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
|
if (Direction == Direction.Vertical)
|
||||||
this.ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, Easing.OutQuint);
|
{
|
||||||
|
Width = newSize.X;
|
||||||
|
this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Height = newSize.Y;
|
||||||
|
this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item);
|
||||||
|
|
||||||
|
protected class DrawableOsuMenuItem : DrawableMenuItem
|
||||||
|
{
|
||||||
|
private const int margin_horizontal = 17;
|
||||||
|
private const int text_size = 17;
|
||||||
|
private const int transition_length = 80;
|
||||||
|
public const int MARGIN_VERTICAL = 4;
|
||||||
|
|
||||||
|
private SampleChannel sampleClick;
|
||||||
|
private SampleChannel sampleHover;
|
||||||
|
|
||||||
|
private TextContainer text;
|
||||||
|
|
||||||
|
public DrawableOsuMenuItem(MenuItem item)
|
||||||
|
: base(item)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
sampleHover = audio.Sample.Get(@"UI/generic-hover");
|
||||||
|
sampleClick = audio.Sample.Get(@"UI/generic-click");
|
||||||
|
|
||||||
|
BackgroundColour = Color4.Transparent;
|
||||||
|
BackgroundColourHover = OsuColour.FromHex(@"172023");
|
||||||
|
|
||||||
|
updateTextColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTextColour()
|
||||||
|
{
|
||||||
|
switch ((Item as OsuMenuItem)?.Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case MenuItemType.Standard:
|
||||||
|
text.Colour = Color4.White;
|
||||||
|
break;
|
||||||
|
case MenuItemType.Destructive:
|
||||||
|
text.Colour = Color4.Red;
|
||||||
|
break;
|
||||||
|
case MenuItemType.Highlighted:
|
||||||
|
text.Colour = OsuColour.FromHex(@"ffcc22");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
sampleHover.Play();
|
||||||
|
text.BoldText.FadeIn(transition_length, Easing.OutQuint);
|
||||||
|
text.NormalText.FadeOut(transition_length, Easing.OutQuint);
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
text.BoldText.FadeOut(transition_length, Easing.OutQuint);
|
||||||
|
text.NormalText.FadeIn(transition_length, Easing.OutQuint);
|
||||||
|
base.OnHoverLost(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
sampleClick.Play();
|
||||||
|
return base.OnClick(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateContent() => text = new TextContainer();
|
||||||
|
|
||||||
|
private class TextContainer : Container, IHasText
|
||||||
|
{
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get { return NormalText.Text; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
NormalText.Text = value;
|
||||||
|
BoldText.Text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly SpriteText NormalText;
|
||||||
|
public readonly SpriteText BoldText;
|
||||||
|
|
||||||
|
public TextContainer()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft;
|
||||||
|
Origin = Anchor.CentreLeft;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
NormalText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
TextSize = text_size,
|
||||||
|
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
|
||||||
|
},
|
||||||
|
BoldText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Alpha = 0,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
TextSize = text_size,
|
||||||
|
Font = @"Exo2.0-Bold",
|
||||||
|
Margin = new MarginPadding { Horizontal = margin_horizontal, Vertical = MARGIN_VERTICAL },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
osu.Game/Graphics/UserInterface/OsuMenuItem.cs
Normal file
25
osu.Game/Graphics/UserInterface/OsuMenuItem.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public class OsuMenuItem : MenuItem
|
||||||
|
{
|
||||||
|
public readonly MenuItemType Type;
|
||||||
|
|
||||||
|
public OsuMenuItem(string text, MenuItemType type = MenuItemType.Standard)
|
||||||
|
: base(text)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsuMenuItem(string text, MenuItemType type, Action action)
|
||||||
|
: base(text, action)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,16 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
using OpenTK.Input;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
@ -15,6 +20,49 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
public override bool AllowClipboardExport => false;
|
public override bool AllowClipboardExport => false;
|
||||||
|
|
||||||
|
private readonly CapsWarning warning;
|
||||||
|
|
||||||
|
private GameHost host;
|
||||||
|
|
||||||
|
public OsuPasswordTextBox()
|
||||||
|
{
|
||||||
|
Add(warning = new CapsWarning
|
||||||
|
{
|
||||||
|
Size = new Vector2(20),
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Margin = new MarginPadding { Right = 10 },
|
||||||
|
Alpha = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(GameHost host)
|
||||||
|
{
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Key == Key.CapsLock)
|
||||||
|
updateCapsWarning(host.CapsLockEnabled);
|
||||||
|
return base.OnKeyDown(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(InputState state)
|
||||||
|
{
|
||||||
|
updateCapsWarning(host.CapsLockEnabled);
|
||||||
|
base.OnFocus(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnFocusLost(InputState state)
|
||||||
|
{
|
||||||
|
updateCapsWarning(false);
|
||||||
|
base.OnFocusLost(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint);
|
||||||
|
|
||||||
public class PasswordMaskChar : Container
|
public class PasswordMaskChar : Container
|
||||||
{
|
{
|
||||||
private readonly CircularContainer circle;
|
private readonly CircularContainer circle;
|
||||||
@ -51,5 +99,21 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
circle.ResizeTo(new Vector2(0.8f), 500, Easing.OutQuint);
|
circle.ResizeTo(new Vector2(0.8f), 500, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CapsWarning : SpriteIcon, IHasTooltip
|
||||||
|
{
|
||||||
|
public string TooltipText => @"Caps lock is active";
|
||||||
|
|
||||||
|
public CapsWarning()
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colour)
|
||||||
|
{
|
||||||
|
Colour = colour.YellowLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,34 +37,34 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == null)
|
if (accentColour == default(Color4))
|
||||||
AccentColour = colours.Blue;
|
AccentColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4? accentColour;
|
private Color4 accentColour;
|
||||||
public Color4 AccentColour
|
public Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return accentColour.GetValueOrDefault(); }
|
get { return accentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
var dropDown = Dropdown as OsuTabDropdown;
|
var dropdown = Dropdown as IHasAccentColour;
|
||||||
if (dropDown != null)
|
if (dropdown != null)
|
||||||
dropDown.AccentColour = value;
|
dropdown.AccentColour = value;
|
||||||
foreach (var item in TabContainer.Children.OfType<OsuTabItem>())
|
foreach (var i in TabContainer.Children.OfType<IHasAccentColour>())
|
||||||
item.AccentColour = value;
|
i.AccentColour = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OsuTabItem : TabItem<T>
|
public class OsuTabItem : TabItem<T>, IHasAccentColour
|
||||||
{
|
{
|
||||||
protected readonly SpriteText Text;
|
protected readonly SpriteText Text;
|
||||||
private readonly Box box;
|
private readonly Box box;
|
||||||
|
|
||||||
private Color4? accentColour;
|
private Color4 accentColour;
|
||||||
public Color4 AccentColour
|
public Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return accentColour.GetValueOrDefault(); }
|
get { return accentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
@ -103,7 +103,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == null)
|
if (accentColour == default(Color4))
|
||||||
AccentColour = colours.Blue;
|
AccentColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,38 +140,55 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override void OnDeactivated() => fadeInactive();
|
protected override void OnDeactivated() => fadeInactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: this needs to go
|
||||||
private class OsuTabDropdown : OsuDropdown<T>
|
private class OsuTabDropdown : OsuDropdown<T>
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader
|
|
||||||
{
|
|
||||||
AccentColour = AccentColour,
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value)
|
|
||||||
{
|
|
||||||
var item = base.CreateMenuItem(text, value);
|
|
||||||
item.ForegroundColourHover = Color4.Black;
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsuTabDropdown()
|
public OsuTabDropdown()
|
||||||
{
|
{
|
||||||
DropdownMenu.Anchor = Anchor.TopRight;
|
|
||||||
DropdownMenu.Origin = Anchor.TopRight;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
DropdownMenu.Background.Colour = Color4.Black.Opacity(0.7f);
|
|
||||||
DropdownMenu.MaxHeight = 400;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override DropdownMenu CreateMenu() => new OsuTabDropdownMenu();
|
||||||
|
|
||||||
|
protected override DropdownHeader CreateHeader() => new OsuTabDropdownHeader
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight
|
||||||
|
};
|
||||||
|
|
||||||
|
private class OsuTabDropdownMenu : OsuDropdownMenu
|
||||||
|
{
|
||||||
|
public OsuTabDropdownMenu()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight;
|
||||||
|
Origin = Anchor.TopRight;
|
||||||
|
|
||||||
|
BackgroundColour = Color4.Black.Opacity(0.7f);
|
||||||
|
MaxHeight = 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuTabDropdownMenuItem(item) { AccentColour = AccentColour };
|
||||||
|
|
||||||
|
private class DrawableOsuTabDropdownMenuItem : DrawableOsuDropdownMenuItem
|
||||||
|
{
|
||||||
|
public DrawableOsuTabDropdownMenuItem(MenuItem item)
|
||||||
|
: base(item)
|
||||||
|
{
|
||||||
|
ForegroundColourHover = Color4.Black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected class OsuTabDropdownHeader : OsuDropdownHeader
|
protected class OsuTabDropdownHeader : OsuDropdownHeader
|
||||||
{
|
{
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return base.AccentColour; }
|
get
|
||||||
|
{
|
||||||
|
return base.AccentColour;
|
||||||
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.AccentColour = value;
|
base.AccentColour = value;
|
||||||
@ -179,18 +196,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
|
||||||
{
|
|
||||||
Foreground.Colour = BackgroundColour;
|
|
||||||
return base.OnHover(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
|
||||||
{
|
|
||||||
Foreground.Colour = BackgroundColourHover;
|
|
||||||
base.OnHoverLost(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsuTabDropdownHeader()
|
public OsuTabDropdownHeader()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.None;
|
RelativeSizeAxes = Axes.None;
|
||||||
@ -220,6 +225,18 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
Padding = new MarginPadding { Left = 5, Right = 5 };
|
Padding = new MarginPadding { Left = 5, Right = 5 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
Foreground.Colour = BackgroundColour;
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
Foreground.Colour = BackgroundColourHover;
|
||||||
|
base.OnHoverLost(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
67
osu.Game/Graphics/UserInterface/ProgressBar.cs
Normal file
67
osu.Game/Graphics/UserInterface/ProgressBar.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterface
|
||||||
|
{
|
||||||
|
public class ProgressBar : SliderBar<double>
|
||||||
|
{
|
||||||
|
public Action<double> OnSeek;
|
||||||
|
|
||||||
|
private readonly Box fill;
|
||||||
|
private readonly Box background;
|
||||||
|
|
||||||
|
public Color4 FillColour
|
||||||
|
{
|
||||||
|
set { fill.Colour = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color4 BackgroundColour
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
background.Alpha = 1;
|
||||||
|
background.Colour = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double EndTime
|
||||||
|
{
|
||||||
|
set { CurrentNumber.MaxValue = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public double CurrentTime
|
||||||
|
{
|
||||||
|
set { CurrentNumber.Value = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProgressBar()
|
||||||
|
{
|
||||||
|
CurrentNumber.MinValue = 0;
|
||||||
|
CurrentNumber.MaxValue = 1;
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
fill = new Box { RelativeSizeAxes = Axes.Y }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateValue(float value)
|
||||||
|
{
|
||||||
|
fill.Width = value * UsableWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUserChange() => OnSeek?.Invoke(Current);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Handlers;
|
using osu.Framework.Input.Handlers;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -29,5 +31,18 @@ namespace osu.Game.Input.Handlers
|
|||||||
public override bool IsActive => true;
|
public override bool IsActive => true;
|
||||||
|
|
||||||
public override int Priority => 0;
|
public override int Priority => 0;
|
||||||
|
|
||||||
|
public class ReplayState<T> : InputState
|
||||||
|
where T : struct
|
||||||
|
{
|
||||||
|
public List<T> PressedActions;
|
||||||
|
|
||||||
|
public override InputState Clone()
|
||||||
|
{
|
||||||
|
var clone = (ReplayState<T>)base.Clone();
|
||||||
|
clone.PressedActions = new List<T>(PressedActions);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,11 +11,11 @@ namespace osu.Game.Online.API
|
|||||||
/// An API request with a well-defined response type.
|
/// An API request with a well-defined response type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
||||||
public class APIRequest<T> : APIRequest
|
public abstract class APIRequest<T> : APIRequest
|
||||||
{
|
{
|
||||||
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
||||||
|
|
||||||
public APIRequest()
|
protected APIRequest()
|
||||||
{
|
{
|
||||||
base.Success += onSuccess;
|
base.Success += onSuccess;
|
||||||
}
|
}
|
||||||
@ -28,10 +28,36 @@ namespace osu.Game.Online.API
|
|||||||
public new event APISuccessHandler<T> Success;
|
public new event APISuccessHandler<T> Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class APIDownloadRequest : APIRequest
|
||||||
|
{
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var request = new WebRequest(Uri);
|
||||||
|
request.DownloadProgress += request_Progress;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
|
||||||
|
|
||||||
|
protected APIDownloadRequest()
|
||||||
|
{
|
||||||
|
base.Success += onSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSuccess()
|
||||||
|
{
|
||||||
|
Success?.Invoke(WebRequest.ResponseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event APIProgressHandler Progress;
|
||||||
|
|
||||||
|
public new event APISuccessHandler<byte[]> Success;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AN API request with no specified response type.
|
/// AN API request with no specified response type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class APIRequest
|
public abstract class APIRequest
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum amount of time before this request will fail.
|
/// The maximum amount of time before this request will fail.
|
||||||
@ -42,7 +68,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
||||||
|
|
||||||
protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}";
|
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
|
||||||
|
|
||||||
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
||||||
|
|
||||||
@ -52,7 +78,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public double StartTime => startTime ?? -1;
|
public double StartTime => startTime ?? -1;
|
||||||
|
|
||||||
private APIAccess api;
|
protected APIAccess API;
|
||||||
protected WebRequest WebRequest;
|
protected WebRequest WebRequest;
|
||||||
|
|
||||||
public event APISuccessHandler Success;
|
public event APISuccessHandler Success;
|
||||||
@ -64,7 +90,7 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public void Perform(APIAccess api)
|
public void Perform(APIAccess api)
|
||||||
{
|
{
|
||||||
this.api = api;
|
API = api;
|
||||||
|
|
||||||
if (checkAndProcessFailure())
|
if (checkAndProcessFailure())
|
||||||
return;
|
return;
|
||||||
@ -109,9 +135,9 @@ namespace osu.Game.Online.API
|
|||||||
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
/// <returns>Whether we are in a failed or cancelled state.</returns>
|
||||||
private bool checkAndProcessFailure()
|
private bool checkAndProcessFailure()
|
||||||
{
|
{
|
||||||
if (api == null || pendingFailure == null) return cancelled;
|
if (API == null || pendingFailure == null) return cancelled;
|
||||||
|
|
||||||
api.Scheduler.Add(pendingFailure);
|
API.Scheduler.Add(pendingFailure);
|
||||||
pendingFailure = null;
|
pendingFailure = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -119,5 +145,6 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public delegate void APIFailureHandler(Exception e);
|
public delegate void APIFailureHandler(Exception e);
|
||||||
public delegate void APISuccessHandler();
|
public delegate void APISuccessHandler();
|
||||||
|
public delegate void APIProgressHandler(long current, long total);
|
||||||
public delegate void APISuccessHandler<in T>(T content);
|
public delegate void APISuccessHandler<in T>(T content);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,9 @@ namespace osu.Game.Online.API.Requests
|
|||||||
[JsonProperty(@"favourite_count")]
|
[JsonProperty(@"favourite_count")]
|
||||||
private int favouriteCount { get; set; }
|
private int favouriteCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"id")]
|
||||||
|
private int onlineId { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"beatmaps")]
|
[JsonProperty(@"beatmaps")]
|
||||||
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
|
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
|
||||||
|
|
||||||
@ -53,6 +56,7 @@ namespace osu.Game.Online.API.Requests
|
|||||||
{
|
{
|
||||||
return new BeatmapSetInfo
|
return new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
|
OnlineBeatmapSetID = onlineId,
|
||||||
Metadata = this,
|
Metadata = this,
|
||||||
OnlineInfo = new BeatmapSetOnlineInfo
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
public Bindable<bool> Joined = new Bindable<bool>();
|
public Bindable<bool> Joined = new Bindable<bool>();
|
||||||
|
|
||||||
public bool ReadOnly => Name != "#lazer";
|
public bool ReadOnly => false;
|
||||||
|
|
||||||
public const int MAX_HISTORY = 300;
|
public const int MAX_HISTORY = 300;
|
||||||
|
|
||||||
|
@ -219,15 +219,28 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.Cache(settings);
|
dependencies.Cache(settings);
|
||||||
dependencies.Cache(social);
|
dependencies.Cache(social);
|
||||||
|
dependencies.Cache(direct);
|
||||||
dependencies.Cache(chat);
|
dependencies.Cache(chat);
|
||||||
dependencies.Cache(userProfile);
|
dependencies.Cache(userProfile);
|
||||||
dependencies.Cache(musicController);
|
dependencies.Cache(musicController);
|
||||||
dependencies.Cache(notificationOverlay);
|
dependencies.Cache(notificationOverlay);
|
||||||
dependencies.Cache(dialogOverlay);
|
dependencies.Cache(dialogOverlay);
|
||||||
|
|
||||||
// ensure both overlays aren't presented at the same time
|
// ensure only one of these overlays are open at once.
|
||||||
chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State;
|
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
|
||||||
social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State;
|
foreach (var overlay in singleDisplayOverlays)
|
||||||
|
{
|
||||||
|
overlay.StateChanged += state =>
|
||||||
|
{
|
||||||
|
if (state == Visibility.Hidden) return;
|
||||||
|
|
||||||
|
foreach (var c in singleDisplayOverlays)
|
||||||
|
{
|
||||||
|
if (c == overlay) continue;
|
||||||
|
c.State = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
LoadComponentAsync(Toolbar = new Toolbar
|
LoadComponentAsync(Toolbar = new Toolbar
|
||||||
{
|
{
|
||||||
|
@ -152,14 +152,10 @@ namespace osu.Game
|
|||||||
|
|
||||||
Beatmap.ValueChanged += b =>
|
Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
// compare to last baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
||||||
if (lastBeatmap?.Track != b.Track)
|
if (lastBeatmap?.Track != b.Track)
|
||||||
{
|
{
|
||||||
// this disposal is done to stop the audio track.
|
lastBeatmap?.Track?.Dispose();
|
||||||
// it may not be exactly what we want for cases beatmaps are reused, as it will
|
|
||||||
// trigger a fresh load of contained resources.
|
|
||||||
lastBeatmap?.Dispose();
|
|
||||||
|
|
||||||
Audio.Track.AddItem(b.Track);
|
Audio.Track.AddItem(b.Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
|
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
|
||||||
channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden;
|
channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden;
|
||||||
channelSelection.StateChanged += (overlay, state) =>
|
channelSelection.StateChanged += state =>
|
||||||
{
|
{
|
||||||
channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible;
|
channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Overlays
|
|||||||
dialogContainer.Add(currentDialog);
|
dialogContainer.Add(currentDialog);
|
||||||
|
|
||||||
currentDialog.Show();
|
currentDialog.Show();
|
||||||
currentDialog.StateChanged += onDialogOnStateChanged;
|
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
|
||||||
State = Visibility.Visible;
|
State = Visibility.Visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
@ -25,23 +26,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
|
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
|
||||||
{
|
{
|
||||||
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
|
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
|
||||||
CornerRadius = 4;
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
Radius = 3f,
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.FadeInFromZero(200, Easing.Out);
|
|
||||||
bottomPanel.LayoutDuration = 200;
|
bottomPanel.LayoutDuration = 200;
|
||||||
bottomPanel.LayoutEasing = Easing.Out;
|
bottomPanel.LayoutEasing = Easing.Out;
|
||||||
bottomPanel.Origin = Anchor.BottomLeft;
|
bottomPanel.Origin = Anchor.BottomLeft;
|
||||||
@ -50,14 +39,10 @@ namespace osu.Game.Overlays.Direct
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, LocalisationEngine localisation)
|
private void load(OsuColour colours, LocalisationEngine localisation)
|
||||||
{
|
{
|
||||||
Children = new[]
|
Content.CornerRadius = 4;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
},
|
|
||||||
CreateBackground(),
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -185,7 +170,13 @@ namespace osu.Game.Overlays.Direct
|
|||||||
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
|
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
StartDownload();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,35 +28,15 @@ namespace osu.Game.Overlays.Direct
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = height;
|
Height = height;
|
||||||
CornerRadius = 5;
|
|
||||||
Masking = true;
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
Radius = 3f,
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
this.FadeInFromZero(200, Easing.Out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(LocalisationEngine localisation)
|
private void load(LocalisationEngine localisation)
|
||||||
{
|
{
|
||||||
Children = new[]
|
Content.CornerRadius = 5;
|
||||||
|
|
||||||
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
},
|
|
||||||
CreateBackground(),
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -144,10 +124,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Size = new Vector2(height - vertical_padding * 2),
|
Size = new Vector2(height - vertical_padding * 2),
|
||||||
|
Action = StartDownload
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DownloadButton : OsuClickableContainer
|
private class DownloadButton : OsuClickableContainer
|
||||||
|
@ -2,26 +2,220 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Beatmaps.IO;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
{
|
{
|
||||||
public abstract class DirectPanel : Container
|
public abstract class DirectPanel : Container
|
||||||
{
|
{
|
||||||
protected readonly BeatmapSetInfo SetInfo;
|
public readonly BeatmapSetInfo SetInfo;
|
||||||
|
|
||||||
|
protected Box BlackBackground;
|
||||||
|
|
||||||
|
private const double hover_transition_time = 400;
|
||||||
|
|
||||||
|
private Container content;
|
||||||
|
|
||||||
|
private APIAccess api;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private BeatmapManager beatmaps;
|
||||||
|
private NotificationOverlay notifications;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
protected DirectPanel(BeatmapSetInfo setInfo)
|
protected DirectPanel(BeatmapSetInfo setInfo)
|
||||||
{
|
{
|
||||||
SetInfo = setInfo;
|
SetInfo = setInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Offset = new Vector2(0f, 1f),
|
||||||
|
Radius = 2f,
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Offset = new Vector2(0f, 5f),
|
||||||
|
Radius = 10f,
|
||||||
|
Colour = Color4.Black.Opacity(0.3f),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
|
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
|
||||||
|
{
|
||||||
|
this.api = api;
|
||||||
|
this.beatmaps = beatmaps;
|
||||||
|
this.notifications = notifications;
|
||||||
|
|
||||||
|
AddInternal(content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = edgeEffectNormal,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
// temporary blackness until the actual background loads.
|
||||||
|
BlackBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
},
|
||||||
|
CreateBackground(),
|
||||||
|
progressBar = new ProgressBar
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Height = 0,
|
||||||
|
Alpha = 0,
|
||||||
|
BackgroundColour = Color4.Black.Opacity(0.7f),
|
||||||
|
FillColour = colours.Blue,
|
||||||
|
Depth = -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(InputState state)
|
||||||
|
{
|
||||||
|
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
|
||||||
|
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
|
return base.OnHover(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(InputState state)
|
||||||
|
{
|
||||||
|
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
|
||||||
|
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
|
base.OnHoverLost(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should eventually be moved to a more central place, like BeatmapManager.
|
||||||
|
private DownloadBeatmapSetRequest downloadRequest;
|
||||||
|
|
||||||
|
protected void StartDownload()
|
||||||
|
{
|
||||||
|
if (api == null) return;
|
||||||
|
|
||||||
|
// we already have an active download running.
|
||||||
|
if (downloadRequest != null)
|
||||||
|
{
|
||||||
|
content.MoveToX(-5, 50, Easing.OutSine).Then()
|
||||||
|
.MoveToX(5, 100, Easing.InOutSine).Then()
|
||||||
|
.MoveToX(-5, 100, Easing.InOutSine).Then()
|
||||||
|
.MoveToX(0, 50, Easing.InSine).Then();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api.LocalUser.Value.IsSupporter)
|
||||||
|
{
|
||||||
|
notifications.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_superpowers,
|
||||||
|
Text = "You gotta be a supporter to download for now 'yo"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.FadeIn(400, Easing.OutQuint);
|
||||||
|
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
|
||||||
|
|
||||||
|
progressBar.Current.Value = 0;
|
||||||
|
|
||||||
|
ProgressNotification downloadNotification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Text = $"Downloading {SetInfo.Metadata.Artist} - {SetInfo.Metadata.Title}",
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest = new DownloadBeatmapSetRequest(SetInfo);
|
||||||
|
downloadRequest.Failure += e =>
|
||||||
|
{
|
||||||
|
progressBar.Current.Value = 0;
|
||||||
|
progressBar.FadeOut(500);
|
||||||
|
downloadNotification.State = ProgressNotificationState.Completed;
|
||||||
|
Logger.Error(e, "Failed to get beatmap download information");
|
||||||
|
|
||||||
|
downloadRequest = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest.Progress += (current, total) =>
|
||||||
|
{
|
||||||
|
float progress = (float)current / total;
|
||||||
|
|
||||||
|
progressBar.Current.Value = progress;
|
||||||
|
|
||||||
|
downloadNotification.State = ProgressNotificationState.Active;
|
||||||
|
downloadNotification.Progress = progress;
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadRequest.Success += data =>
|
||||||
|
{
|
||||||
|
progressBar.Current.Value = 1;
|
||||||
|
progressBar.FadeOut(500);
|
||||||
|
|
||||||
|
downloadNotification.State = ProgressNotificationState.Completed;
|
||||||
|
|
||||||
|
using (var stream = new MemoryStream(data))
|
||||||
|
using (var archive = new OszArchiveReader(stream))
|
||||||
|
beatmaps.Import(archive);
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadNotification.CancelRequested += () =>
|
||||||
|
{
|
||||||
|
downloadRequest.Cancel();
|
||||||
|
downloadRequest = null;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
notifications.Post(downloadNotification);
|
||||||
|
|
||||||
|
// don't run in the main api queue as this is a long-running task.
|
||||||
|
Task.Run(() => downloadRequest.Perform(api));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadBeatmapSetRequest : APIDownloadRequest
|
||||||
|
{
|
||||||
|
private readonly BeatmapSetInfo beatmapSet;
|
||||||
|
|
||||||
|
public DownloadBeatmapSetRequest(BeatmapSetInfo beatmapSet)
|
||||||
|
{
|
||||||
|
this.beatmapSet = beatmapSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => $@"beatmapsets/{beatmapSet.OnlineBeatmapSetID}/download";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
this.FadeInFromZero(200, Easing.Out);
|
||||||
|
}
|
||||||
|
|
||||||
protected List<DifficultyIcon> GetDifficultyIcons()
|
protected List<DifficultyIcon> GetDifficultyIcons()
|
||||||
{
|
{
|
||||||
var icons = new List<DifficultyIcon>();
|
var icons = new List<DifficultyIcon>();
|
||||||
@ -38,7 +232,11 @@ namespace osu.Game.Overlays.Direct
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fill,
|
FillMode = FillMode.Fill,
|
||||||
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
|
OnLoadComplete = d =>
|
||||||
|
{
|
||||||
|
d.FadeInFromZero(400, Easing.Out);
|
||||||
|
BlackBackground.Delay(400).FadeOut();
|
||||||
|
},
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private readonly FillFlowContainer resultCountsContainer;
|
private readonly FillFlowContainer resultCountsContainer;
|
||||||
private readonly OsuSpriteText resultCountsText;
|
private readonly OsuSpriteText resultCountsText;
|
||||||
private readonly FillFlowContainer<DirectPanel> panels;
|
private FillFlowContainer<DirectPanel> panels;
|
||||||
|
|
||||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
||||||
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
||||||
@ -48,17 +48,6 @@ namespace osu.Game.Overlays
|
|||||||
if (beatmapSets?.Equals(value) ?? false) return;
|
if (beatmapSets?.Equals(value) ?? false) return;
|
||||||
beatmapSets = value;
|
beatmapSets = value;
|
||||||
|
|
||||||
if (BeatmapSets == null)
|
|
||||||
{
|
|
||||||
foreach (var p in panels.Children)
|
|
||||||
{
|
|
||||||
p.FadeOut(200);
|
|
||||||
p.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +97,6 @@ namespace osu.Game.Overlays
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
panels = new FillFlowContainer<DirectPanel>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Spacing = new Vector2(panel_padding),
|
|
||||||
Margin = new MarginPadding { Top = 10 },
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
|
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
|
||||||
@ -161,11 +143,19 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets)
|
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
|
||||||
{
|
{
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
resultCountsContainer.Colour = colours.Yellow;
|
resultCountsContainer.Colour = colours.Yellow;
|
||||||
|
|
||||||
|
beatmaps.BeatmapSetAdded += setAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAdded(BeatmapSetInfo set)
|
||||||
|
{
|
||||||
|
// if a new map was imported, we should remove it from search results (download completed etc.)
|
||||||
|
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateResultCounts()
|
private void updateResultCounts()
|
||||||
@ -185,9 +175,22 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void recreatePanels(PanelDisplayStyle displayStyle)
|
private void recreatePanels(PanelDisplayStyle displayStyle)
|
||||||
{
|
{
|
||||||
|
if (panels != null)
|
||||||
|
{
|
||||||
|
panels.FadeOut(200);
|
||||||
|
panels.Expire();
|
||||||
|
panels = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (BeatmapSets == null) return;
|
if (BeatmapSets == null) return;
|
||||||
|
|
||||||
panels.ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
|
var newPanels = new FillFlowContainer<DirectPanel>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(panel_padding),
|
||||||
|
Margin = new MarginPadding { Top = 10 },
|
||||||
|
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
|
||||||
{
|
{
|
||||||
switch (displayStyle)
|
switch (displayStyle)
|
||||||
{
|
{
|
||||||
@ -196,6 +199,13 @@ namespace osu.Game.Overlays
|
|||||||
default:
|
default:
|
||||||
return new DirectListPanel(b);
|
return new DirectListPanel(b);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
LoadComponentAsync(newPanels, p =>
|
||||||
|
{
|
||||||
|
if (panels != null) ScrollFlow.Remove(panels);
|
||||||
|
ScrollFlow.Add(panels = newPanels);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,14 +122,14 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
this.FadeEdgeEffectTo<Container>(1, transition_time, Easing.OutQuint);
|
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
return base.OnHover(state);
|
return base.OnHover(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(InputState state)
|
protected override void OnHoverLost(InputState state)
|
||||||
{
|
{
|
||||||
this.FadeEdgeEffectTo<Container>(0, transition_time, Easing.OutQuint);
|
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
|
||||||
|
|
||||||
base.OnHoverLost(state);
|
base.OnHoverLost(state);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private const float hidden_width = 120;
|
private const float hidden_width = 120;
|
||||||
|
|
||||||
private void keyBindingOverlay_StateChanged(VisibilityContainer container, Visibility visibility)
|
private void keyBindingOverlay_StateChanged(Visibility visibility)
|
||||||
{
|
{
|
||||||
switch (visibility)
|
switch (visibility)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -19,6 +20,8 @@ namespace osu.Game.Overlays.MedalSplash
|
|||||||
private const float scale_when_unlocked = 0.76f;
|
private const float scale_when_unlocked = 0.76f;
|
||||||
private const float scale_when_full = 0.6f;
|
private const float scale_when_full = 0.6f;
|
||||||
|
|
||||||
|
public event Action<DisplayState> StateChanged;
|
||||||
|
|
||||||
private readonly Medal medal;
|
private readonly Medal medal;
|
||||||
private readonly Container medalContainer;
|
private readonly Container medalContainer;
|
||||||
private readonly Sprite medalSprite, medalGlow;
|
private readonly Sprite medalSprite, medalGlow;
|
||||||
@ -132,6 +135,8 @@ namespace osu.Game.Overlays.MedalSplash
|
|||||||
|
|
||||||
state = value;
|
state = value;
|
||||||
updateState();
|
updateState();
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,15 +15,37 @@ namespace osu.Game.Overlays.Music
|
|||||||
{
|
{
|
||||||
public class CollectionsDropdown<T> : OsuDropdown<T>
|
public class CollectionsDropdown<T> : OsuDropdown<T>
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour };
|
|
||||||
protected override Menu CreateMenu() => new CollectionsMenu();
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
AccentColour = colours.Gray6;
|
AccentColour = colours.Gray6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override DropdownHeader CreateHeader() => new CollectionsHeader();
|
||||||
|
|
||||||
|
protected override DropdownMenu CreateMenu() => new CollectionsMenu();
|
||||||
|
|
||||||
|
private class CollectionsMenu : OsuDropdownMenu
|
||||||
|
{
|
||||||
|
public CollectionsMenu()
|
||||||
|
{
|
||||||
|
CornerRadius = 5;
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Color4.Black.Opacity(0.3f),
|
||||||
|
Radius = 3,
|
||||||
|
Offset = new Vector2(0f, 1f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
BackgroundColour = colours.Gray4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class CollectionsHeader : OsuDropdownHeader
|
private class CollectionsHeader : OsuDropdownHeader
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -48,26 +70,5 @@ namespace osu.Game.Overlays.Music
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CollectionsMenu : OsuMenu
|
|
||||||
{
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
Background.Colour = colours.Gray4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CollectionsMenu()
|
|
||||||
{
|
|
||||||
CornerRadius = 5;
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Colour = Color4.Black.Opacity(0.3f),
|
|
||||||
Radius = 3,
|
|
||||||
Offset = new Vector2(0f, 1f),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -16,7 +17,7 @@ using OpenTK;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Music
|
namespace osu.Game.Overlays.Music
|
||||||
{
|
{
|
||||||
internal class PlaylistItem : Container, IFilterable
|
internal class PlaylistItem : Container, IFilterable, IDraggable
|
||||||
{
|
{
|
||||||
private const float fade_duration = 100;
|
private const float fade_duration = 100;
|
||||||
|
|
||||||
@ -33,6 +34,8 @@ namespace osu.Game.Overlays.Music
|
|||||||
|
|
||||||
public Action<BeatmapSetInfo> OnSelect;
|
public Action<BeatmapSetInfo> OnSelect;
|
||||||
|
|
||||||
|
public bool IsDraggable => handle.IsHovered;
|
||||||
|
|
||||||
private bool selected;
|
private bool selected;
|
||||||
public bool Selected
|
public bool Selected
|
||||||
{
|
{
|
||||||
@ -68,15 +71,9 @@ namespace osu.Game.Overlays.Music
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
handle = new SpriteIcon
|
handle = new PlaylistItemHandle
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopLeft,
|
Colour = colours.Gray5
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Size = new Vector2(12),
|
|
||||||
Colour = colours.Gray5,
|
|
||||||
Icon = FontAwesome.fa_bars,
|
|
||||||
Alpha = 0f,
|
|
||||||
Margin = new MarginPadding { Left = 5, Top = 2 },
|
|
||||||
},
|
},
|
||||||
text = new OsuTextFlowContainer
|
text = new OsuTextFlowContainer
|
||||||
{
|
{
|
||||||
@ -114,19 +111,19 @@ namespace osu.Game.Overlays.Music
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(Framework.Input.InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
handle.FadeIn(fade_duration);
|
handle.FadeIn(fade_duration);
|
||||||
|
|
||||||
return base.OnHover(state);
|
return base.OnHover(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnHoverLost(Framework.Input.InputState state)
|
protected override void OnHoverLost(InputState state)
|
||||||
{
|
{
|
||||||
handle.FadeOut(fade_duration);
|
handle.FadeOut(fade_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(Framework.Input.InputState state)
|
protected override bool OnClick(InputState state)
|
||||||
{
|
{
|
||||||
OnSelect?.Invoke(BeatmapSetInfo);
|
OnSelect?.Invoke(BeatmapSetInfo);
|
||||||
return true;
|
return true;
|
||||||
@ -148,5 +145,27 @@ namespace osu.Game.Overlays.Music
|
|||||||
this.FadeTo(matching ? 1 : 0, 200);
|
this.FadeTo(matching ? 1 : 0, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class PlaylistItemHandle : SpriteIcon
|
||||||
|
{
|
||||||
|
|
||||||
|
public PlaylistItemHandle()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopLeft;
|
||||||
|
Origin = Anchor.TopLeft;
|
||||||
|
Size = new Vector2(12);
|
||||||
|
Icon = FontAwesome.fa_bars;
|
||||||
|
Alpha = 0f;
|
||||||
|
Margin = new MarginPadding { Left = 5, Top = 2 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDraggable : IDrawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this <see cref="IDraggable"/> can be dragged in its current state.
|
||||||
|
/// </summary>
|
||||||
|
bool IsDraggable { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,55 +4,63 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Music
|
namespace osu.Game.Overlays.Music
|
||||||
{
|
{
|
||||||
internal class PlaylistList : Container
|
internal class PlaylistList : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly FillFlowContainer<PlaylistItem> items;
|
|
||||||
|
|
||||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
items.Children = value.Select(item => new PlaylistItem(item) { OnSelect = itemSelected }).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeatmapSetInfo FirstVisibleSet => items.Children.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo;
|
|
||||||
|
|
||||||
private void itemSelected(BeatmapSetInfo b)
|
|
||||||
{
|
|
||||||
OnSelect?.Invoke(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Action<BeatmapSetInfo> OnSelect;
|
public Action<BeatmapSetInfo> OnSelect;
|
||||||
|
|
||||||
private readonly SearchContainer search;
|
private readonly ItemsScrollContainer items;
|
||||||
|
|
||||||
public void Filter(string searchTerm) => search.SearchTerm = searchTerm;
|
|
||||||
|
|
||||||
public BeatmapSetInfo SelectedItem
|
|
||||||
{
|
|
||||||
get { return items.Children.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (PlaylistItem s in items.Children)
|
|
||||||
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlaylistList()
|
public PlaylistList()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
InternalChild = items = new ItemsScrollContainer
|
||||||
{
|
|
||||||
new OsuScrollContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
OnSelect = set => OnSelect?.Invoke(set)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public new MarginPadding Padding
|
||||||
|
{
|
||||||
|
get { return base.Padding; }
|
||||||
|
set { base.Padding = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BeatmapSetInfo> BeatmapSets { set { items.Sets = value; } }
|
||||||
|
|
||||||
|
public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet;
|
||||||
|
public BeatmapSetInfo NextSet => items.NextSet;
|
||||||
|
public BeatmapSetInfo PreviousSet => items.PreviousSet;
|
||||||
|
|
||||||
|
public BeatmapSetInfo SelectedSet
|
||||||
|
{
|
||||||
|
get { return items.SelectedSet; }
|
||||||
|
set { items.SelectedSet = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet);
|
||||||
|
public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
|
||||||
|
|
||||||
|
public void Filter(string searchTerm) => items.SearchTerm = searchTerm;
|
||||||
|
|
||||||
|
private class ItemsScrollContainer : OsuScrollContainer
|
||||||
|
{
|
||||||
|
public Action<BeatmapSetInfo> OnSelect;
|
||||||
|
|
||||||
|
private readonly SearchContainer search;
|
||||||
|
private readonly FillFlowContainer<PlaylistItem> items;
|
||||||
|
|
||||||
|
public ItemsScrollContainer()
|
||||||
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
search = new SearchContainer
|
search = new SearchContainer
|
||||||
@ -68,22 +76,157 @@ namespace osu.Game.Overlays.Music
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BeatmapSetInfo> Sets
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
items.Clear();
|
||||||
|
value.ForEach(AddBeatmapSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SearchTerm
|
||||||
|
{
|
||||||
|
get { return search.SearchTerm; }
|
||||||
|
set { search.SearchTerm = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
|
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
items.Add(new PlaylistItem(beatmapSet) { OnSelect = itemSelected });
|
items.Add(new PlaylistItem(beatmapSet)
|
||||||
|
{
|
||||||
|
OnSelect = set => OnSelect?.Invoke(set),
|
||||||
|
Depth = items.Count
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
PlaylistItem itemToRemove = items.Children.FirstOrDefault(item => item.BeatmapSetInfo.ID == beatmapSet.ID);
|
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
|
||||||
if (itemToRemove != null) items.Remove(itemToRemove);
|
if (itemToRemove == null)
|
||||||
|
return false;
|
||||||
|
return items.Remove(itemToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BeatmapSetInfo SelectedSet
|
||||||
|
{
|
||||||
|
get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
foreach (PlaylistItem s in items.Children)
|
||||||
|
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo;
|
||||||
|
public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo;
|
||||||
|
public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo;
|
||||||
|
|
||||||
|
private Vector2 nativeDragPosition;
|
||||||
|
private PlaylistItem draggedItem;
|
||||||
|
|
||||||
|
protected override bool OnDragStart(InputState state)
|
||||||
|
{
|
||||||
|
nativeDragPosition = state.Mouse.NativeState.Position;
|
||||||
|
draggedItem = items.FirstOrDefault(d => d.IsDraggable);
|
||||||
|
return draggedItem != null || base.OnDragStart(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDrag(InputState state)
|
||||||
|
{
|
||||||
|
nativeDragPosition = state.Mouse.NativeState.Position;
|
||||||
|
if (draggedItem == null)
|
||||||
|
return base.OnDrag(state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnd(InputState state)
|
||||||
|
{
|
||||||
|
nativeDragPosition = state.Mouse.NativeState.Position;
|
||||||
|
var handled = draggedItem != null || base.OnDragEnd(state);
|
||||||
|
draggedItem = null;
|
||||||
|
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (draggedItem == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
updateScrollPosition();
|
||||||
|
updateDragPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateScrollPosition()
|
||||||
|
{
|
||||||
|
const float start_offset = 10;
|
||||||
|
const double max_power = 50;
|
||||||
|
const double exp_base = 1.05;
|
||||||
|
|
||||||
|
var localPos = ToLocalSpace(nativeDragPosition);
|
||||||
|
|
||||||
|
if (localPos.Y < start_offset)
|
||||||
|
{
|
||||||
|
if (Current <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var power = Math.Min(max_power, Math.Abs(start_offset - localPos.Y));
|
||||||
|
ScrollBy(-(float)Math.Pow(exp_base, power));
|
||||||
|
}
|
||||||
|
else if (localPos.Y > DrawHeight - start_offset)
|
||||||
|
{
|
||||||
|
if (IsScrolledToEnd())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var power = Math.Min(max_power, Math.Abs(DrawHeight - start_offset - localPos.Y));
|
||||||
|
ScrollBy((float)Math.Pow(exp_base, power));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDragPosition()
|
||||||
|
{
|
||||||
|
var itemsPos = items.ToLocalSpace(nativeDragPosition);
|
||||||
|
|
||||||
|
int srcIndex = (int)draggedItem.Depth;
|
||||||
|
|
||||||
|
// Find the last item with position < mouse position. Note we can't directly use
|
||||||
|
// the item positions as they are being transformed
|
||||||
|
float heightAccumulator = 0;
|
||||||
|
int dstIndex = 0;
|
||||||
|
for (; dstIndex < items.Count; dstIndex++)
|
||||||
|
{
|
||||||
|
// Using BoundingBox here takes care of scale, paddings, etc...
|
||||||
|
heightAccumulator += items[dstIndex].BoundingBox.Height;
|
||||||
|
if (heightAccumulator > itemsPos.Y)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dstIndex = MathHelper.Clamp(dstIndex, 0, items.Count - 1);
|
||||||
|
|
||||||
|
if (srcIndex == dstIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (srcIndex < dstIndex)
|
||||||
|
{
|
||||||
|
for (int i = srcIndex + 1; i <= dstIndex; i++)
|
||||||
|
items.ChangeChildDepth(items[i], i - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = dstIndex; i < srcIndex; i++)
|
||||||
|
items.ChangeChildDepth(items[i], i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.ChangeChildDepth(draggedItem, dstIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
|
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
|
||||||
{
|
{
|
||||||
public string[] FilterTerms => new string[] { };
|
public string[] FilterTerms => new string[] { };
|
||||||
@ -96,6 +239,9 @@ namespace osu.Game.Overlays.Music
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare with reversed ChildID and Depth
|
||||||
|
protected override int Compare(Drawable x, Drawable y) => base.Compare(y, x);
|
||||||
|
|
||||||
public IEnumerable<IFilterable> FilterableChildren => Children;
|
public IEnumerable<IFilterable> FilterableChildren => Children;
|
||||||
|
|
||||||
public ItemSearchContainer()
|
public ItemSearchContainer()
|
||||||
@ -106,3 +252,4 @@ namespace osu.Game.Overlays.Music
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Music
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
beatmapBacking.ValueChanged += b => list.SelectedItem = b?.BeatmapSetInfo;
|
beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo;
|
||||||
beatmapBacking.TriggerChange();
|
beatmapBacking.TriggerChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,24 +126,24 @@ namespace osu.Game.Overlays.Music
|
|||||||
|
|
||||||
public void PlayPrevious()
|
public void PlayPrevious()
|
||||||
{
|
{
|
||||||
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
|
var playable = list.PreviousSet;
|
||||||
var available = BeatmapSets.Reverse();
|
|
||||||
|
|
||||||
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
|
|
||||||
|
|
||||||
if (playable != null)
|
if (playable != null)
|
||||||
|
{
|
||||||
playSpecified(playable.Beatmaps[0]);
|
playSpecified(playable.Beatmaps[0]);
|
||||||
|
list.SelectedSet = playable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayNext()
|
public void PlayNext()
|
||||||
{
|
{
|
||||||
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
|
var playable = list.NextSet;
|
||||||
var available = BeatmapSets;
|
|
||||||
|
|
||||||
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
|
|
||||||
|
|
||||||
if (playable != null)
|
if (playable != null)
|
||||||
|
{
|
||||||
playSpecified(playable.Beatmaps[0]);
|
playSpecified(playable.Beatmaps[0]);
|
||||||
|
list.SelectedSet = playable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playSpecified(BeatmapInfo info)
|
private void playSpecified(BeatmapInfo info)
|
||||||
|
@ -15,7 +15,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -205,7 +204,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
beatmapBacking.BindTo(game.Beatmap);
|
beatmapBacking.BindTo(game.Beatmap);
|
||||||
|
|
||||||
playlist.StateChanged += (c, s) => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -349,23 +348,23 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
|
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
|
||||||
{
|
{
|
||||||
OnLoadComplete = d =>
|
OnLoadComplete = newBackground =>
|
||||||
{
|
{
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
case TransformDirection.Next:
|
case TransformDirection.Next:
|
||||||
d.Position = new Vector2(400, 0);
|
newBackground.Position = new Vector2(400, 0);
|
||||||
d.MoveToX(0, 500, Easing.OutCubic);
|
newBackground.MoveToX(0, 500, Easing.OutCubic);
|
||||||
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
|
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
|
||||||
break;
|
break;
|
||||||
case TransformDirection.Prev:
|
case TransformDirection.Prev:
|
||||||
d.Position = new Vector2(-400, 0);
|
newBackground.Position = new Vector2(-400, 0);
|
||||||
d.MoveToX(0, 500, Easing.OutCubic);
|
newBackground.MoveToX(0, 500, Easing.OutCubic);
|
||||||
currentBackground.MoveToX(400, 500, Easing.OutCubic);
|
currentBackground.MoveToX(400, 500, Easing.OutCubic);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentBackground.Expire();
|
currentBackground.Expire();
|
||||||
currentBackground = d;
|
currentBackground = newBackground;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -434,49 +433,5 @@ namespace osu.Game.Overlays
|
|||||||
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgressBar : SliderBar<double>
|
|
||||||
{
|
|
||||||
public Action<double> OnSeek;
|
|
||||||
|
|
||||||
private readonly Box fill;
|
|
||||||
|
|
||||||
public Color4 FillColour
|
|
||||||
{
|
|
||||||
set { fill.Colour = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public double EndTime
|
|
||||||
{
|
|
||||||
set { CurrentNumber.MaxValue = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public double CurrentTime
|
|
||||||
{
|
|
||||||
set { CurrentNumber.Value = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgressBar()
|
|
||||||
{
|
|
||||||
CurrentNumber.MinValue = 0;
|
|
||||||
CurrentNumber.MaxValue = 1;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
fill = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Y
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateValue(float value)
|
|
||||||
{
|
|
||||||
fill.Width = value * UsableWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnUserChange() => OnSeek?.Invoke(Current);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,11 +152,14 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
break;
|
break;
|
||||||
case ProgressNotificationState.Active:
|
case ProgressNotificationState.Active:
|
||||||
case ProgressNotificationState.Queued:
|
case ProgressNotificationState.Queued:
|
||||||
|
if (CancelRequested?.Invoke() != false)
|
||||||
State = ProgressNotificationState.Cancelled;
|
State = ProgressNotificationState.Cancelled;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Func<bool> CancelRequested { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The function to post completion notifications back to.
|
/// The function to post completion notifications back to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -12,8 +12,9 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
{
|
{
|
||||||
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour };
|
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
|
||||||
protected override Menu CreateMenu() => new SlimMenu();
|
|
||||||
|
protected override DropdownMenu CreateMenu() => new SlimMenu();
|
||||||
|
|
||||||
private class SlimDropdownHeader : OsuDropdownHeader
|
private class SlimDropdownHeader : OsuDropdownHeader
|
||||||
{
|
{
|
||||||
@ -31,11 +32,11 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SlimMenu : OsuMenu
|
private class SlimMenu : OsuDropdownMenu
|
||||||
{
|
{
|
||||||
public SlimMenu()
|
public SlimMenu()
|
||||||
{
|
{
|
||||||
Background.Colour = Color4.Black.Opacity(0.7f);
|
BackgroundColour = Color4.Black.Opacity(0.7f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,9 +257,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
|
|
||||||
private class UserDropdown : OsuEnumDropdown<UserAction>
|
private class UserDropdown : OsuEnumDropdown<UserAction>
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new UserDropdownHeader { AccentColour = AccentColour };
|
protected override DropdownHeader CreateHeader() => new UserDropdownHeader();
|
||||||
protected override Menu CreateMenu() => new UserDropdownMenu();
|
|
||||||
protected override DropdownMenuItem<UserAction> CreateMenuItem(string text, UserAction value) => new UserDropdownMenuItem(text, value) { AccentColour = AccentColour };
|
protected override DropdownMenu CreateMenu() => new UserDropdownMenu();
|
||||||
|
|
||||||
public Color4 StatusColour
|
public Color4 StatusColour
|
||||||
{
|
{
|
||||||
@ -277,6 +277,49 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
AccentColour = colours.Gray5;
|
AccentColour = colours.Gray5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UserDropdownMenu : OsuDropdownMenu
|
||||||
|
{
|
||||||
|
public UserDropdownMenu()
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
Margin = new MarginPadding { Bottom = 5 };
|
||||||
|
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Color4.Black.Opacity(0.25f),
|
||||||
|
Radius = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
ItemsContainer.Padding = new MarginPadding();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
BackgroundColour = colours.Gray3;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableUserDropdownMenuItem(item);
|
||||||
|
|
||||||
|
private class DrawableUserDropdownMenuItem : DrawableOsuDropdownMenuItem
|
||||||
|
{
|
||||||
|
public DrawableUserDropdownMenuItem(MenuItem item)
|
||||||
|
: base(item)
|
||||||
|
{
|
||||||
|
Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 };
|
||||||
|
CornerRadius = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateContent() => new Content
|
||||||
|
{
|
||||||
|
Label = { Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 } }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class UserDropdownHeader : OsuDropdownHeader
|
private class UserDropdownHeader : OsuDropdownHeader
|
||||||
{
|
{
|
||||||
public const float LABEL_LEFT_MARGIN = 20;
|
public const float LABEL_LEFT_MARGIN = 20;
|
||||||
@ -324,38 +367,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UserDropdownMenu : OsuMenu
|
|
||||||
{
|
|
||||||
public UserDropdownMenu()
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Bottom = 5 };
|
|
||||||
CornerRadius = 5;
|
|
||||||
ItemsContainer.Padding = new MarginPadding(0);
|
|
||||||
Masking = true;
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Colour = Color4.Black.Opacity(0.25f),
|
|
||||||
Radius = 4,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
Background.Colour = colours.Gray3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class UserDropdownMenuItem : OsuDropdownMenuItem
|
|
||||||
{
|
|
||||||
public UserDropdownMenuItem(string text, UserAction current) : base(text, current)
|
|
||||||
{
|
|
||||||
Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 5 };
|
|
||||||
Label.Margin = new MarginPadding { Left = UserDropdownHeader.LABEL_LEFT_MARGIN - 11 };
|
|
||||||
CornerRadius = 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum UserAction
|
private enum UserAction
|
||||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
{
|
{
|
||||||
private OsuButton importButton;
|
private OsuButton importButton;
|
||||||
private OsuButton deleteButton;
|
private OsuButton deleteButton;
|
||||||
|
private OsuButton restoreButton;
|
||||||
|
|
||||||
protected override string Header => "General";
|
protected override string Header => "General";
|
||||||
|
|
||||||
@ -41,6 +42,20 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
Task.Run(() => beatmaps.DeleteAll()).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
restoreButton = new OsuButton
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Text = "Restore all hidden difficulties",
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
restoreButton.Enabled.Value = false;
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (var b in beatmaps.QueryBeatmaps(b => b.Hidden))
|
||||||
|
beatmaps.Restore(b);
|
||||||
|
}).ContinueWith(t => Schedule(() => restoreButton.Enabled.Value = true));
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -19,6 +20,9 @@ namespace osu.Game.Overlays.Settings
|
|||||||
private readonly FillFlowContainer<SidebarButton> content;
|
private readonly FillFlowContainer<SidebarButton> content;
|
||||||
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
|
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
|
||||||
internal const int EXPANDED_WIDTH = 200;
|
internal const int EXPANDED_WIDTH = 200;
|
||||||
|
|
||||||
|
public event Action<ExpandedState> StateChanged;
|
||||||
|
|
||||||
protected override Container<SidebarButton> Content => content;
|
protected override Container<SidebarButton> Content => content;
|
||||||
|
|
||||||
public Sidebar()
|
public Sidebar()
|
||||||
@ -102,6 +106,8 @@ namespace osu.Game.Overlays.Settings
|
|||||||
this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint);
|
this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new ToolbarDirectButton(),
|
||||||
new ToolbarChatButton(),
|
new ToolbarChatButton(),
|
||||||
new ToolbarSocialButton(),
|
new ToolbarSocialButton(),
|
||||||
new ToolbarMusicButton(),
|
new ToolbarMusicButton(),
|
||||||
|
22
osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
Normal file
22
osu.Game/Overlays/Toolbar/ToolbarDirectButton.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Toolbar
|
||||||
|
{
|
||||||
|
internal class ToolbarDirectButton : ToolbarOverlayToggleButton
|
||||||
|
{
|
||||||
|
public ToolbarDirectButton()
|
||||||
|
{
|
||||||
|
SetIcon(FontAwesome.fa_osu_chevron_down_o);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(DirectOverlay direct)
|
||||||
|
{
|
||||||
|
StateContainer = direct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
stateContainer.StateChanged -= stateChanged;
|
stateContainer.StateChanged -= stateChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stateChanged(VisibilityContainer c, Visibility state)
|
private void stateChanged(Visibility state)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
@ -167,6 +167,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private class Wave : Container, IStateful<Visibility>
|
private class Wave : Container, IStateful<Visibility>
|
||||||
{
|
{
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
public float FinalPosition;
|
public float FinalPosition;
|
||||||
|
|
||||||
public Wave()
|
public Wave()
|
||||||
@ -200,6 +202,7 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
|
|
||||||
public Visibility State
|
public Visibility State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
@ -216,6 +219,8 @@ namespace osu.Game.Overlays
|
|||||||
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
|
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
@ -18,7 +17,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
|
||||||
/// It handles logic of any frames which *must* be executed.
|
/// It handles logic of any frames which *must* be executed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FramedReplayInputHandler : ReplayInputHandler
|
public abstract class FramedReplayInputHandler : ReplayInputHandler
|
||||||
{
|
{
|
||||||
private readonly Replay replay;
|
private readonly Replay replay;
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1);
|
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1);
|
||||||
|
|
||||||
public FramedReplayInputHandler(Replay replay)
|
protected FramedReplayInputHandler(Replay replay)
|
||||||
{
|
{
|
||||||
this.replay = replay;
|
this.replay = replay;
|
||||||
}
|
}
|
||||||
@ -51,7 +50,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2? position
|
protected Vector2? Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -62,23 +61,7 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates() => new List<InputState>();
|
||||||
{
|
|
||||||
var buttons = new HashSet<MouseButton>();
|
|
||||||
if (CurrentFrame?.MouseLeft ?? false)
|
|
||||||
buttons.Add(MouseButton.Left);
|
|
||||||
if (CurrentFrame?.MouseRight ?? false)
|
|
||||||
buttons.Add(MouseButton.Right);
|
|
||||||
|
|
||||||
return new List<InputState>
|
|
||||||
{
|
|
||||||
new InputState
|
|
||||||
{
|
|
||||||
Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons),
|
|
||||||
Keyboard = new ReplayKeyboardState(new List<Key>())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
public bool AtLastFrame => currentFrameIndex == Frames.Count - 1;
|
||||||
public bool AtFirstFrame => currentFrameIndex == 0;
|
public bool AtFirstFrame => currentFrameIndex == 0;
|
||||||
@ -133,10 +116,9 @@ namespace osu.Game.Rulesets.Replays
|
|||||||
|
|
||||||
protected class ReplayMouseState : MouseState
|
protected class ReplayMouseState : MouseState
|
||||||
{
|
{
|
||||||
public ReplayMouseState(Vector2 position, IEnumerable<MouseButton> list)
|
public ReplayMouseState(Vector2 position)
|
||||||
{
|
{
|
||||||
Position = position;
|
Position = position;
|
||||||
list.ForEach(b => SetPressed(b, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -43,7 +42,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The input manager for this RulesetContainer.
|
/// The input manager for this RulesetContainer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly PlayerInputManager InputManager = new PlayerInputManager();
|
internal IHasReplayHandler ReplayInputManager => KeyBindingInputManager as IHasReplayHandler;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key conversion input manager for this RulesetContainer.
|
/// The key conversion input manager for this RulesetContainer.
|
||||||
@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we have a replay loaded currently.
|
/// Whether we have a replay loaded currently.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasReplayLoaded => InputManager.ReplayInputHandler != null;
|
public bool HasReplayLoaded => ReplayInputManager?.ReplayInputHandler != null;
|
||||||
|
|
||||||
public abstract IEnumerable<HitObject> Objects { get; }
|
public abstract IEnumerable<HitObject> Objects { get; }
|
||||||
|
|
||||||
@ -76,11 +75,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
internal RulesetContainer(Ruleset ruleset)
|
internal RulesetContainer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
KeyBindingInputManager = CreateInputManager();
|
KeyBindingInputManager = CreateInputManager();
|
||||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
@ -102,7 +97,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <returns>The input manager.</returns>
|
/// <returns>The input manager.</returns>
|
||||||
public abstract PassThroughInputManager CreateInputManager();
|
public abstract PassThroughInputManager CreateInputManager();
|
||||||
|
|
||||||
protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay);
|
protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
|
||||||
|
|
||||||
public Replay Replay { get; private set; }
|
public Replay Replay { get; private set; }
|
||||||
|
|
||||||
@ -112,8 +107,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <param name="replay">The replay, null for local input.</param>
|
/// <param name="replay">The replay, null for local input.</param>
|
||||||
public virtual void SetReplay(Replay replay)
|
public virtual void SetReplay(Replay replay)
|
||||||
{
|
{
|
||||||
|
if (ReplayInputManager == null)
|
||||||
|
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available");
|
||||||
|
|
||||||
Replay = replay;
|
Replay = replay;
|
||||||
InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
|
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,13 +265,12 @@ namespace osu.Game.Rulesets.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InputManager.Add(content = new Container
|
KeyBindingInputManager.Add(content = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[] { KeyBindingInputManager }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddInternal(InputManager);
|
AddInternal(KeyBindingInputManager);
|
||||||
KeyBindingInputManager.Add(Playfield = CreatePlayfield());
|
KeyBindingInputManager.Add(Playfield = CreatePlayfield());
|
||||||
|
|
||||||
loadObjects();
|
loadObjects();
|
||||||
@ -283,8 +280,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
base.SetReplay(replay);
|
base.SetReplay(replay);
|
||||||
|
|
||||||
if (InputManager?.ReplayInputHandler != null)
|
if (ReplayInputManager?.ReplayInputHandler != null)
|
||||||
InputManager.ReplayInputHandler.ToScreenSpace = Playfield.ScaledContent.ToScreenSpace;
|
ReplayInputManager.ReplayInputHandler.ToScreenSpace = input => Playfield.ScaledContent.ToScreenSpace(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,20 +1,194 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using OpenTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
{
|
{
|
||||||
public abstract class RulesetInputManager<T> : DatabasedKeyBindingInputManager<T>, ICanAttachKeyCounter
|
public abstract class RulesetInputManager<T> : DatabasedKeyBindingInputManager<T>, ICanAttachKeyCounter, IHasReplayHandler
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Action mapping (for replays)
|
||||||
|
|
||||||
|
private List<T> lastPressedActions = new List<T>();
|
||||||
|
|
||||||
|
protected override void HandleNewState(InputState state)
|
||||||
|
{
|
||||||
|
base.HandleNewState(state);
|
||||||
|
|
||||||
|
var replayState = state as ReplayInputHandler.ReplayState<T>;
|
||||||
|
|
||||||
|
if (replayState == null) return;
|
||||||
|
|
||||||
|
// Here we handle states specifically coming from a replay source.
|
||||||
|
// These have extra action information rather than keyboard keys or mouse buttons.
|
||||||
|
|
||||||
|
List<T> newActions = replayState.PressedActions;
|
||||||
|
|
||||||
|
foreach (var released in lastPressedActions.Except(newActions))
|
||||||
|
PropagateReleased(KeyBindingInputQueue, released);
|
||||||
|
|
||||||
|
foreach (var pressed in newActions.Except(lastPressedActions))
|
||||||
|
PropagatePressed(KeyBindingInputQueue, pressed);
|
||||||
|
|
||||||
|
lastPressedActions = newActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IHasReplayHandler
|
||||||
|
|
||||||
|
private ReplayInputHandler replayInputHandler;
|
||||||
|
public ReplayInputHandler ReplayInputHandler
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return replayInputHandler;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
||||||
|
|
||||||
|
replayInputHandler = value;
|
||||||
|
UseParentState = replayInputHandler == null;
|
||||||
|
|
||||||
|
if (replayInputHandler != null)
|
||||||
|
AddHandler(replayInputHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Clock control
|
||||||
|
|
||||||
|
private ManualClock clock;
|
||||||
|
private IFrameBasedClock parentClock;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
||||||
|
parentClock = Clock;
|
||||||
|
|
||||||
|
Clock = new FramedClock(clock = new ManualClock
|
||||||
|
{
|
||||||
|
CurrentTime = parentClock.CurrentTime,
|
||||||
|
Rate = parentClock.Rate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether we are running up-to-date with our parent clock.
|
||||||
|
/// If not, we will need to keep processing children until we catch up.
|
||||||
|
/// </summary>
|
||||||
|
private bool requireMoreUpdateLoops;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether we are in a valid state (ie. should we keep processing children frames).
|
||||||
|
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
|
||||||
|
/// </summary>
|
||||||
|
private bool validState;
|
||||||
|
|
||||||
|
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
||||||
|
|
||||||
|
private bool isAttached => replayInputHandler != null && !UseParentState;
|
||||||
|
|
||||||
|
private const int max_catch_up_updates_per_frame = 50;
|
||||||
|
|
||||||
|
public override bool UpdateSubTree()
|
||||||
|
{
|
||||||
|
requireMoreUpdateLoops = true;
|
||||||
|
validState = true;
|
||||||
|
|
||||||
|
int loops = 0;
|
||||||
|
|
||||||
|
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
||||||
|
if (!base.UpdateSubTree())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
if (parentClock == null) return;
|
||||||
|
|
||||||
|
clock.Rate = parentClock.Rate;
|
||||||
|
clock.IsRunning = parentClock.IsRunning;
|
||||||
|
|
||||||
|
if (!isAttached)
|
||||||
|
{
|
||||||
|
clock.CurrentTime = parentClock.CurrentTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
||||||
|
|
||||||
|
if (newTime == null)
|
||||||
|
{
|
||||||
|
// we shouldn't execute for this time value. probably waiting on more replay data.
|
||||||
|
validState = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clock.CurrentTime = newTime.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||||
|
base.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Setting application (disables etc.)
|
||||||
|
|
||||||
|
private Bindable<bool> mouseDisabled;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void TransformState(InputState state)
|
||||||
|
{
|
||||||
|
base.TransformState(state);
|
||||||
|
|
||||||
|
// we don't want to transform the state if a replay is present (for now, at least).
|
||||||
|
if (replayInputHandler != null) return;
|
||||||
|
|
||||||
|
var mouse = state.Mouse as Framework.Input.MouseState;
|
||||||
|
|
||||||
|
if (mouse != null)
|
||||||
|
{
|
||||||
|
if (mouseDisabled.Value)
|
||||||
|
{
|
||||||
|
mouse.SetPressed(MouseButton.Left, false);
|
||||||
|
mouse.SetPressed(MouseButton.Right, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Key Counter Attachment
|
||||||
|
|
||||||
public void Attach(KeyCounterCollection keyCounter)
|
public void Attach(KeyCounterCollection keyCounter)
|
||||||
{
|
{
|
||||||
var receptor = new ActionReceptor(keyCounter);
|
var receptor = new ActionReceptor(keyCounter);
|
||||||
@ -35,8 +209,22 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public bool OnReleased(T action) => Target.Children.OfType<KeyCounterAction<T>>().Any(c => c.OnReleased(action));
|
public bool OnReleased(T action) => Target.Children.OfType<KeyCounterAction<T>>().Any(c => c.OnReleased(action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expose the <see cref="ReplayInputHandler"/> in a capable <see cref="InputManager"/>.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasReplayHandler
|
||||||
|
{
|
||||||
|
ReplayInputHandler ReplayInputHandler { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Supports attaching a <see cref="KeyCounterCollection"/>.
|
||||||
|
/// Keys will be populated automatically and a receptor will be injected inside.
|
||||||
|
/// </summary>
|
||||||
public interface ICanAttachKeyCounter
|
public interface ICanAttachKeyCounter
|
||||||
{
|
{
|
||||||
void Attach(KeyCounterCollection keyCounter);
|
void Attach(KeyCounterCollection keyCounter);
|
||||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Button : BeatSyncedContainer, IStateful<ButtonState>
|
public class Button : BeatSyncedContainer, IStateful<ButtonState>
|
||||||
{
|
{
|
||||||
|
public event Action<ButtonState> StateChanged;
|
||||||
|
|
||||||
private readonly Container iconText;
|
private readonly Container iconText;
|
||||||
private readonly Container box;
|
private readonly Container box;
|
||||||
private readonly Box boxHoverLayer;
|
private readonly Box boxHoverLayer;
|
||||||
@ -266,6 +268,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
this.FadeOut(explode_duration / 4f * 3);
|
this.FadeOut(explode_duration / 4f * 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
public class ButtonSystem : Container, IStateful<MenuState>
|
public class ButtonSystem : Container, IStateful<MenuState>
|
||||||
{
|
{
|
||||||
|
public event Action<MenuState> StateChanged;
|
||||||
|
|
||||||
public Action OnEdit;
|
public Action OnEdit;
|
||||||
public Action OnExit;
|
public Action OnExit;
|
||||||
public Action OnDirect;
|
public Action OnDirect;
|
||||||
@ -294,6 +296,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
|
backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
|
||||||
settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;
|
settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using OpenTK.Input;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Input.Handlers;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
|
||||||
{
|
|
||||||
public class PlayerInputManager : PassThroughInputManager
|
|
||||||
{
|
|
||||||
private ManualClock clock;
|
|
||||||
private IFrameBasedClock parentClock;
|
|
||||||
|
|
||||||
private ReplayInputHandler replayInputHandler;
|
|
||||||
public ReplayInputHandler ReplayInputHandler
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return replayInputHandler;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (replayInputHandler != null) RemoveHandler(replayInputHandler);
|
|
||||||
|
|
||||||
replayInputHandler = value;
|
|
||||||
UseParentState = replayInputHandler == null;
|
|
||||||
|
|
||||||
if (replayInputHandler != null)
|
|
||||||
AddHandler(replayInputHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuConfigManager config)
|
|
||||||
{
|
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
|
||||||
parentClock = Clock;
|
|
||||||
|
|
||||||
Clock = new FramedClock(clock = new ManualClock
|
|
||||||
{
|
|
||||||
CurrentTime = parentClock.CurrentTime,
|
|
||||||
Rate = parentClock.Rate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we running up-to-date with our parent clock.
|
|
||||||
/// If not, we will need to keep processing children until we catch up.
|
|
||||||
/// </summary>
|
|
||||||
private bool requireMoreUpdateLoops;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we in a valid state (ie. should we keep processing children frames).
|
|
||||||
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
|
|
||||||
/// </summary>
|
|
||||||
private bool validState;
|
|
||||||
|
|
||||||
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
|
||||||
|
|
||||||
private bool isAttached => replayInputHandler != null && !UseParentState;
|
|
||||||
|
|
||||||
private const int max_catch_up_updates_per_frame = 50;
|
|
||||||
|
|
||||||
public override bool UpdateSubTree()
|
|
||||||
{
|
|
||||||
requireMoreUpdateLoops = true;
|
|
||||||
validState = true;
|
|
||||||
|
|
||||||
int loops = 0;
|
|
||||||
|
|
||||||
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
|
||||||
if (!base.UpdateSubTree())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
if (parentClock == null) return;
|
|
||||||
|
|
||||||
clock.Rate = parentClock.Rate;
|
|
||||||
clock.IsRunning = parentClock.IsRunning;
|
|
||||||
|
|
||||||
if (!isAttached)
|
|
||||||
{
|
|
||||||
clock.CurrentTime = parentClock.CurrentTime;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
|
||||||
|
|
||||||
if (newTime == null)
|
|
||||||
{
|
|
||||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
|
||||||
validState = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clock.CurrentTime = newTime.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
|
||||||
base.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void TransformState(InputState state)
|
|
||||||
{
|
|
||||||
base.TransformState(state);
|
|
||||||
|
|
||||||
// we don't want to transform the state if a replay is present (for now, at least).
|
|
||||||
if (replayInputHandler != null) return;
|
|
||||||
|
|
||||||
var mouse = state.Mouse as Framework.Input.MouseState;
|
|
||||||
|
|
||||||
if (mouse != null)
|
|
||||||
{
|
|
||||||
if (mouseDisabled.Value)
|
|
||||||
{
|
|
||||||
mouse.SetPressed(MouseButton.Left, false);
|
|
||||||
mouse.SetPressed(MouseButton.Right, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -133,6 +133,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private class FadeContainer : Container, IStateful<Visibility>
|
private class FadeContainer : Container, IStateful<Visibility>
|
||||||
{
|
{
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
private ScheduledDelegate scheduledHide;
|
private ScheduledDelegate scheduledHide;
|
||||||
|
|
||||||
@ -144,8 +146,10 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
var lastState = state;
|
if (state == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var lastState = state;
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
scheduledHide?.Cancel();
|
scheduledHide?.Cancel();
|
||||||
@ -164,6 +168,8 @@ namespace osu.Game.Screens.Play
|
|||||||
this.FadeOut(1000, Easing.OutExpo);
|
this.FadeOut(1000, Easing.OutExpo);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
@ -170,6 +171,8 @@ namespace osu.Game.Screens.Play
|
|||||||
private const float padding = 2;
|
private const float padding = 2;
|
||||||
public const float WIDTH = cube_size + padding;
|
public const float WIDTH = cube_size + padding;
|
||||||
|
|
||||||
|
public event Action<ColumnState> StateChanged;
|
||||||
|
|
||||||
private readonly List<Box> drawableRows = new List<Box>();
|
private readonly List<Box> drawableRows = new List<Box>();
|
||||||
|
|
||||||
private float filled;
|
private float filled;
|
||||||
@ -186,6 +189,7 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ColumnState state;
|
private ColumnState state;
|
||||||
|
|
||||||
public ColumnState State
|
public ColumnState State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
@ -196,6 +200,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
if (IsLoaded)
|
if (IsLoaded)
|
||||||
fillActive();
|
fillActive();
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,17 +107,44 @@ namespace osu.Game.Screens.Select
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveBeatmap(BeatmapSetInfo beatmapSet)
|
public void RemoveBeatmap(BeatmapSetInfo beatmapSet) => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID));
|
||||||
|
|
||||||
|
internal void UpdateBeatmap(BeatmapInfo beatmap)
|
||||||
{
|
{
|
||||||
Schedule(delegate
|
// todo: this method should not run more than once for the same BeatmapSetInfo.
|
||||||
|
var set = manager.Refresh(beatmap.BeatmapSet);
|
||||||
|
|
||||||
|
// todo: this method should be smarter as to not recreate panels that haven't changed, etc.
|
||||||
|
var group = groups.Find(b => b.BeatmapSet.ID == set.ID);
|
||||||
|
|
||||||
|
if (group == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newGroup = createGroup(set);
|
||||||
|
|
||||||
|
int i = groups.IndexOf(group);
|
||||||
|
groups.RemoveAt(i);
|
||||||
|
groups.Insert(i, newGroup);
|
||||||
|
|
||||||
|
if (selectedGroup == group && newGroup.BeatmapPanels.Count == 0)
|
||||||
|
selectedGroup = null;
|
||||||
|
|
||||||
|
Filter(null, false);
|
||||||
|
|
||||||
|
//check if we can/need to maintain our current selection.
|
||||||
|
if (selectedGroup == group && newGroup.BeatmapPanels.Count > 0)
|
||||||
{
|
{
|
||||||
removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID));
|
var newSelection =
|
||||||
});
|
newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID) ??
|
||||||
|
newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))];
|
||||||
|
|
||||||
|
selectGroup(newGroup, newSelection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
|
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
|
||||||
{
|
{
|
||||||
if (beatmap == null)
|
if (beatmap == null || beatmap.Hidden)
|
||||||
{
|
{
|
||||||
SelectNext();
|
SelectNext();
|
||||||
return;
|
return;
|
||||||
@ -140,6 +167,12 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
public Action StartRequested;
|
public Action StartRequested;
|
||||||
|
|
||||||
|
public Action<BeatmapSetInfo> DeleteRequested;
|
||||||
|
|
||||||
|
public Action<BeatmapSetInfo> RestoreRequested;
|
||||||
|
|
||||||
|
public Action<BeatmapInfo> HideDifficultyRequested;
|
||||||
|
|
||||||
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
||||||
{
|
{
|
||||||
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
|
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
|
||||||
@ -191,6 +224,7 @@ namespace osu.Game.Screens.Select
|
|||||||
if (!visibleGroups.Any())
|
if (!visibleGroups.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (selectedGroup != null)
|
||||||
randomSelectedBeatmaps.Push(new KeyValuePair<BeatmapGroup, BeatmapPanel>(selectedGroup, selectedGroup.SelectedPanel));
|
randomSelectedBeatmaps.Push(new KeyValuePair<BeatmapGroup, BeatmapPanel>(selectedGroup, selectedGroup.SelectedPanel));
|
||||||
|
|
||||||
BeatmapGroup group;
|
BeatmapGroup group;
|
||||||
@ -305,6 +339,9 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
SelectionChanged = (g, p) => selectGroup(g, p),
|
SelectionChanged = (g, p) => selectGroup(g, p),
|
||||||
StartRequested = b => StartRequested?.Invoke(),
|
StartRequested = b => StartRequested?.Invoke(),
|
||||||
|
DeleteRequested = b => DeleteRequested?.Invoke(b),
|
||||||
|
RestoreHiddenRequested = s => RestoreRequested?.Invoke(s),
|
||||||
|
HideDifficultyRequested = b => HideDifficultyRequested?.Invoke(b),
|
||||||
State = BeatmapGroupState.Collapsed
|
State = BeatmapGroupState.Collapsed
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -19,23 +18,18 @@ namespace osu.Game.Screens.Select
|
|||||||
manager = beatmapManager;
|
manager = beatmapManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BeatmapDeleteDialog(WorkingBeatmap beatmap)
|
public BeatmapDeleteDialog(BeatmapSetInfo beatmap)
|
||||||
{
|
{
|
||||||
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
|
||||||
|
|
||||||
Icon = FontAwesome.fa_trash_o;
|
Icon = FontAwesome.fa_trash_o;
|
||||||
HeaderText = @"Confirm deletion of";
|
HeaderText = @"Confirm deletion of";
|
||||||
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
|
|
||||||
Buttons = new PopupDialogButton[]
|
Buttons = new PopupDialogButton[]
|
||||||
{
|
{
|
||||||
new PopupDialogOkButton
|
new PopupDialogOkButton
|
||||||
{
|
{
|
||||||
Text = @"Yes. Totally. Delete it.",
|
Text = @"Yes. Totally. Delete it.",
|
||||||
Action = () =>
|
Action = () => manager.Delete(beatmap),
|
||||||
{
|
|
||||||
beatmap.Dispose();
|
|
||||||
manager.Delete(beatmap.BeatmapSetInfo);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new PopupDialogCancelButton
|
new PopupDialogCancelButton
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -21,6 +22,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
{
|
{
|
||||||
public static readonly float HEIGHT = 60;
|
public static readonly float HEIGHT = 60;
|
||||||
|
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
public readonly int RankPosition;
|
public readonly int RankPosition;
|
||||||
public readonly Score Score;
|
public readonly Score Score;
|
||||||
|
|
||||||
@ -41,11 +44,14 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
private readonly FillFlowContainer<ScoreModIcon> modsContainer;
|
private readonly FillFlowContainer<ScoreModIcon> modsContainer;
|
||||||
|
|
||||||
private Visibility state;
|
private Visibility state;
|
||||||
|
|
||||||
public Visibility State
|
public Visibility State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (state == value)
|
||||||
|
return;
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
@ -88,6 +94,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,13 +54,11 @@ namespace osu.Game.Screens.Select
|
|||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
Push(new Editor());
|
Push(new Editor());
|
||||||
}, Key.Number3);
|
}, Key.Number3);
|
||||||
|
|
||||||
Beatmap.ValueChanged += beatmap_ValueChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beatmap_ValueChanged(WorkingBeatmap beatmap)
|
protected override void UpdateBeatmap(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
if (!IsCurrentScreen) return;
|
base.UpdateBeatmap(beatmap);
|
||||||
|
|
||||||
beatmap.Mods.BindTo(modSelect.SelectedMods);
|
beatmap.Mods.BindTo(modSelect.SelectedMods);
|
||||||
|
|
||||||
|
@ -106,6 +106,9 @@ namespace osu.Game.Screens.Select
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
SelectionChanged = carouselSelectionChanged,
|
SelectionChanged = carouselSelectionChanged,
|
||||||
BeatmapsChanged = carouselBeatmapsLoaded,
|
BeatmapsChanged = carouselBeatmapsLoaded,
|
||||||
|
DeleteRequested = b => promptDelete(b),
|
||||||
|
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
|
||||||
|
HideDifficultyRequested = b => manager.Hide(b),
|
||||||
StartRequested = () => carouselRaisedStart(),
|
StartRequested = () => carouselRaisedStart(),
|
||||||
});
|
});
|
||||||
Add(FilterControl = new FilterControl
|
Add(FilterControl = new FilterControl
|
||||||
@ -163,7 +166,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
|
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
|
||||||
Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3);
|
Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3);
|
||||||
|
|
||||||
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue);
|
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager == null)
|
if (manager == null)
|
||||||
@ -174,6 +177,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
manager.BeatmapSetAdded += onBeatmapSetAdded;
|
manager.BeatmapSetAdded += onBeatmapSetAdded;
|
||||||
manager.BeatmapSetRemoved += onBeatmapSetRemoved;
|
manager.BeatmapSetRemoved += onBeatmapSetRemoved;
|
||||||
|
manager.BeatmapHidden += onBeatmapHidden;
|
||||||
|
manager.BeatmapRestored += onBeatmapRestored;
|
||||||
|
|
||||||
dialogOverlay = dialog;
|
dialogOverlay = dialog;
|
||||||
|
|
||||||
@ -190,6 +195,9 @@ namespace osu.Game.Screens.Select
|
|||||||
carousel.AllowSelection = !Beatmap.Disabled;
|
carousel.AllowSelection = !Beatmap.Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
|
||||||
|
private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);
|
||||||
|
|
||||||
private void carouselBeatmapsLoaded()
|
private void carouselBeatmapsLoaded()
|
||||||
{
|
{
|
||||||
if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
|
if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
|
||||||
@ -236,7 +244,7 @@ namespace osu.Game.Screens.Select
|
|||||||
ensurePlayingSelected(preview);
|
ensurePlayingSelected(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeBackground(Beatmap.Value);
|
UpdateBeatmap(Beatmap.Value);
|
||||||
};
|
};
|
||||||
|
|
||||||
selectionChangedDebounce?.Cancel();
|
selectionChangedDebounce?.Cancel();
|
||||||
@ -304,7 +312,7 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
||||||
{
|
{
|
||||||
changeBackground(Beatmap);
|
UpdateBeatmap(Beatmap);
|
||||||
ensurePlayingSelected();
|
ensurePlayingSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,12 +353,19 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
manager.BeatmapSetAdded -= onBeatmapSetAdded;
|
manager.BeatmapSetAdded -= onBeatmapSetAdded;
|
||||||
manager.BeatmapSetRemoved -= onBeatmapSetRemoved;
|
manager.BeatmapSetRemoved -= onBeatmapSetRemoved;
|
||||||
|
manager.BeatmapHidden -= onBeatmapHidden;
|
||||||
|
manager.BeatmapRestored -= onBeatmapRestored;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialAddSetsTask?.Cancel();
|
initialAddSetsTask?.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void changeBackground(WorkingBeatmap beatmap)
|
/// <summary>
|
||||||
|
/// Allow components in SongSelect to update their loaded beatmap details.
|
||||||
|
/// This is a debounced call (unlike directly binding to WorkingBeatmap.ValueChanged).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The working beatmap.</param>
|
||||||
|
protected virtual void UpdateBeatmap(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
var backgroundModeBeatmap = Background as BackgroundScreenBeatmap;
|
var backgroundModeBeatmap = Background as BackgroundScreenBeatmap;
|
||||||
if (backgroundModeBeatmap != null)
|
if (backgroundModeBeatmap != null)
|
||||||
@ -378,10 +393,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBeatmapSet(BeatmapSetInfo beatmapSet)
|
private void addBeatmapSet(BeatmapSetInfo beatmapSet) => carousel.AddBeatmap(beatmapSet);
|
||||||
{
|
|
||||||
carousel.AddBeatmap(beatmapSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
|
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||||
{
|
{
|
||||||
@ -390,10 +402,12 @@ namespace osu.Game.Screens.Select
|
|||||||
Beatmap.SetDefault();
|
Beatmap.SetDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void promptDelete()
|
private void promptDelete(BeatmapSetInfo beatmap)
|
||||||
{
|
{
|
||||||
if (Beatmap != null && !Beatmap.IsDefault)
|
if (beatmap == null)
|
||||||
dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap));
|
return;
|
||||||
|
|
||||||
|
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
@ -409,7 +423,8 @@ namespace osu.Game.Screens.Select
|
|||||||
case Key.Delete:
|
case Key.Delete:
|
||||||
if (state.Keyboard.ShiftPressed)
|
if (state.Keyboard.ShiftPressed)
|
||||||
{
|
{
|
||||||
promptDelete();
|
if (!Beatmap.IsDefault)
|
||||||
|
promptDelete(Beatmap.Value.BeatmapSetInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
<Compile Include="Graphics\UserInterface\LineGraph.cs" />
|
<Compile Include="Graphics\UserInterface\LineGraph.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\MenuItemType.cs" />
|
<Compile Include="Graphics\UserInterface\MenuItemType.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\OsuContextMenu.cs" />
|
<Compile Include="Graphics\UserInterface\OsuContextMenu.cs" />
|
||||||
<Compile Include="Graphics\UserInterface\OsuContextMenuItem.cs" />
|
<Compile Include="Graphics\UserInterface\OsuMenuItem.cs" />
|
||||||
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
|
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
|
||||||
<Compile Include="Input\Bindings\DatabasedKeyBindingInputManager.cs" />
|
<Compile Include="Input\Bindings\DatabasedKeyBindingInputManager.cs" />
|
||||||
<Compile Include="Input\KeyBindingStore.cs" />
|
<Compile Include="Input\KeyBindingStore.cs" />
|
||||||
@ -116,6 +116,7 @@
|
|||||||
<Compile Include="Overlays\Music\PlaylistList.cs" />
|
<Compile Include="Overlays\Music\PlaylistList.cs" />
|
||||||
<Compile Include="Overlays\OnScreenDisplay.cs" />
|
<Compile Include="Overlays\OnScreenDisplay.cs" />
|
||||||
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
||||||
|
<Compile Include="Graphics\UserInterface\ProgressBar.cs" />
|
||||||
<Compile Include="Overlays\Settings\Sections\Maintenance\GeneralSettings.cs" />
|
<Compile Include="Overlays\Settings\Sections\Maintenance\GeneralSettings.cs" />
|
||||||
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
|
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
|
||||||
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
|
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
|
||||||
@ -129,6 +130,7 @@
|
|||||||
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
|
<Compile Include="Overlays\Profile\Sections\RanksSection.cs" />
|
||||||
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
|
<Compile Include="Overlays\Profile\Sections\RecentSection.cs" />
|
||||||
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
|
<Compile Include="Graphics\Containers\ConstrainedIconContainer.cs" />
|
||||||
|
<Compile Include="Overlays\Toolbar\ToolbarDirectButton.cs" />
|
||||||
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
|
||||||
<Compile Include="Rulesets\UI\RulesetInputManager.cs" />
|
<Compile Include="Rulesets\UI\RulesetInputManager.cs" />
|
||||||
<Compile Include="Screens\Play\KeyCounterAction.cs" />
|
<Compile Include="Screens\Play\KeyCounterAction.cs" />
|
||||||
@ -321,7 +323,6 @@
|
|||||||
<Compile Include="Screens\Multiplayer\MatchCreate.cs" />
|
<Compile Include="Screens\Multiplayer\MatchCreate.cs" />
|
||||||
<Compile Include="Screens\Play\FailOverlay.cs" />
|
<Compile Include="Screens\Play\FailOverlay.cs" />
|
||||||
<Compile Include="Screens\Play\MenuOverlay.cs" />
|
<Compile Include="Screens\Play\MenuOverlay.cs" />
|
||||||
<Compile Include="Screens\Play\PlayerInputManager.cs" />
|
|
||||||
<Compile Include="Screens\Play\PlayerLoader.cs" />
|
<Compile Include="Screens\Play\PlayerLoader.cs" />
|
||||||
<Compile Include="Screens\Play\ReplayPlayer.cs" />
|
<Compile Include="Screens\Play\ReplayPlayer.cs" />
|
||||||
<Compile Include="Screens\Play\SkipButton.cs" />
|
<Compile Include="Screens\Play\SkipButton.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user