1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-16 06:52:36 +08:00

Compare commits

..

202 Commits

101 changed files with 2162 additions and 783 deletions
-3
View File
@@ -1,17 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Testing;
using osu.Game;
namespace osu.Desktop.Tests.Visual
{
[TestFixture]
public abstract class OsuTestCase : TestCase
{
[Test]
public override void RunTest()
{
using (var host = new HeadlessGameHost(realtime: false))
+15 -15
View File
@@ -69,28 +69,28 @@ namespace osu.Desktop.Tests.Visual
private class MyContextMenuContainer : Container, IHasContextMenu
{
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuContextMenuItem(@"Some option"),
new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
new OsuContextMenuItem(@"Another option"),
new OsuContextMenuItem(@"Choose me please"),
new OsuContextMenuItem(@"And me too"),
new OsuContextMenuItem(@"Trying to fill"),
new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
new OsuMenuItem(@"Some option"),
new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
new OsuMenuItem(@"Another option"),
new OsuMenuItem(@"Choose me please"),
new OsuMenuItem(@"And me too"),
new OsuMenuItem(@"Trying to fill"),
new OsuMenuItem(@"Destructive option", MenuItemType.Destructive),
};
}
private class AnotherContextMenuContainer : Container, IHasContextMenu
{
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuContextMenuItem(@"Simple option"),
new OsuContextMenuItem(@"Simple very very long option"),
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
new OsuMenuItem(@"Simple option"),
new OsuMenuItem(@"Simple very very long option"),
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
};
}
}
@@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{
new BeatmapSetInfo
{
OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -0,0 +1,84 @@
// 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.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
namespace osu.Desktop.Tests.Visual
{
public class TestCaseEditorMenuBar : OsuTestCase
{
public TestCaseEditorMenuBar()
{
Add(new EditorMenuBar
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 50,
Items = new[]
{
new EditorMenuBarItem("File")
{
Items = new[]
{
new EditorMenuItem("Clear All Notes"),
new EditorMenuItem("Open Difficulty..."),
new EditorMenuItem("Save"),
new EditorMenuItem("Create a new Difficulty..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Revert to Saved"),
new EditorMenuItem("Revert to Saved (Full)"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Test Beatmap"),
new EditorMenuItem("Open AiMod"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Upload Beatmap..."),
new EditorMenuItem("Export Package"),
new EditorMenuItem("Export Map Package"),
new EditorMenuItem("Import from..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Open Song Folder"),
new EditorMenuItem("Open .osu in Notepad"),
new EditorMenuItem("Open .osb in Notepad"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Exit"),
}
},
new EditorMenuBarItem("Timing")
{
Items = new[]
{
new EditorMenuItem("Time Signature"),
new EditorMenuItem("Metronome Clicks"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Add Timing Section"),
new EditorMenuItem("Add Inheriting Section"),
new EditorMenuItem("Reset Current Section"),
new EditorMenuItem("Delete Timing Section"),
new EditorMenuItem("Resnap Current Section"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Timing Setup"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive),
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive),
new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Set Current Position as Preview Point"),
}
},
new EditorMenuBarItem("Testing")
{
Items = new[]
{
new EditorMenuItem("Item 1"),
new EditorMenuItem("Item 2"),
new EditorMenuItem("Item 3"),
}
},
}
});
}
}
}
+11 -11
View File
@@ -1,13 +1,13 @@
// 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;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using OpenTK;
namespace osu.Desktop.Tests.Visual
{
@@ -24,7 +24,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.XH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -42,7 +42,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.X,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -60,7 +60,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.SH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -78,7 +78,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.S,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -96,7 +96,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.A,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -114,7 +114,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.B,
Accuracy = 98.26,
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -132,7 +132,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.C,
Accuracy = 96.54,
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -150,7 +150,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 60.25,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -168,7 +168,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 51.40,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -186,7 +186,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 42.22,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));
@@ -87,6 +87,7 @@
<Compile Include="Visual\TestCaseManiaHitObjects.cs" />
<Compile Include="Visual\TestCaseManiaPlayfield.cs" />
<Compile Include="Visual\TestCaseMedalOverlay.cs" />
<Compile Include="Visual\TestCaseEditorMenuBar.cs" />
<Compile Include="Visual\TestCaseMenuButtonSystem.cs" />
<Compile Include="Visual\TestCaseMenuOverlays.cs" />
<Compile Include="Visual\TestCaseMods.cs" />
-2
View File
@@ -93,8 +93,6 @@ namespace osu.Game.Rulesets.Catch
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!catch";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
-2
View File
@@ -105,8 +105,6 @@ namespace osu.Game.Rulesets.Mania
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
+4
View File
@@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
@@ -78,12 +79,14 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModRandom : Mod
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
}
public abstract class ManiaKeyMod : Mod
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
@@ -146,6 +149,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
@@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{
public override string Name => "Gravity";
public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0;
+1 -1
View File
@@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
if (action == Action)
{
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
background.FadeTo(0.6f, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
@@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers;
using System;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Configuration;
@@ -25,12 +24,6 @@ namespace osu.Game.Rulesets.Mania.UI
{
public const float HIT_TARGET_POSITION = 50;
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
/// </summary>
private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
private SpecialColumnPosition specialColumnPosition;
/// <summary>
/// The style to use for the special column.
+3
View File
@@ -96,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
@@ -106,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
@@ -126,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTarget : Mod
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;
-2
View File
@@ -112,8 +112,6 @@ namespace osu.Game.Rulesets.Osu
}
}
public override Mod GetAutoplayMod() => new OsuModAutoplay();
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);
@@ -1,8 +1,11 @@
// 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 System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
@@ -14,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgement>
{
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>(ScoringMode.Exponential);
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()
{
base.Reset();
@@ -34,9 +68,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
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)
{
base.PopulateScore(score);
@@ -57,28 +88,75 @@ namespace osu.Game.Rulesets.Osu.Scoring
comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1;
}
switch (judgement.Result)
switch (judgement.Score)
{
case HitResult.Hit:
Health.Value += 0.1f;
case OsuScoreResult.Hit300:
Health.Value += (10.2 - hpDrainRate) * 0.02;
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;
}
}
int score = 0;
int maxScore = 0;
calculateScore();
}
private void calculateScore()
{
int baseScore = 0;
double comboScore = 0;
int baseMaxScore = 0;
foreach (var j in Judgements)
{
score += j.ScoreValue;
maxScore += j.MaxScoreValue;
baseScore += j.ScoreValue;
baseMaxScore += j.MaxScoreValue;
comboScore += j.ScoreValue * (1 + Combo.Value / 10d);
}
TotalScore.Value = score;
Accuracy.Value = (double)score / maxScore;
Accuracy.Value = (double)baseScore / baseMaxScore;
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
}
}
}
@@ -268,6 +268,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
base.Reset();
Health.Value = 0;
Accuracy.Value = 1;
bonusScore = 0;
comboPortion = 0;
-2
View File
@@ -95,8 +95,6 @@ namespace osu.Game.Rulesets.Taiko
}
}
public override Mod GetAutoplayMod() => new TaikoModAutoplay();
public override string Description => "osu!taiko";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
+4 -4
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps
public class Beatmap<T>
where T : HitObject
{
public BeatmapInfo BeatmapInfo;
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public readonly List<Color4> ComboColors = new List<Color4>
@@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps
/// <summary>
/// The HitObjects this Beatmap contains.
/// </summary>
public List<T> HitObjects;
public List<T> HitObjects = new List<T>();
/// <summary>
/// Total amount of break time in the beatmap.
@@ -44,12 +44,13 @@ namespace osu.Game.Beatmaps
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null)
public Beatmap(Beatmap<T> original = null)
{
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
}
}
@@ -65,7 +66,6 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null)
: base(original)
{
HitObjects = original?.HitObjects;
}
}
}
+2
View File
@@ -52,6 +52,8 @@ namespace osu.Game.Beatmaps
[JsonProperty("file_sha2")]
public string Hash { get; set; }
public bool Hidden { get; set; }
/// <summary>
/// MD5 is kept for legacy support (matching against replays, osu-web-10 etc.).
/// </summary>
+117 -16
View File
@@ -20,6 +20,9 @@ using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using SQLite.Net;
using osu.Game.Online.API.Requests;
using System.Threading.Tasks;
using osu.Game.Online.API;
namespace osu.Game.Beatmaps
{
@@ -33,11 +36,21 @@ namespace osu.Game.Beatmaps
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetAdded;
/// <summary>
/// Fired when a single difficulty has been hidden.
/// </summary>
public event Action<BeatmapInfo> BeatmapHidden;
/// <summary>
/// Fired when a <see cref="BeatmapSetInfo"/> is removed from the database.
/// </summary>
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
/// <summary>
/// Fired when a single difficulty has been restored.
/// </summary>
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
/// </summary>
@@ -53,6 +66,10 @@ namespace osu.Game.Beatmaps
private readonly BeatmapStore beatmaps;
private readonly APIAccess api;
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
@@ -66,16 +83,19 @@ namespace osu.Game.Beatmaps
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
this.storage = storage;
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
this.api = api;
if (importHost != null)
ipc = new BeatmapIPCChannel(importHost, this);
@@ -162,24 +182,102 @@ namespace osu.Game.Beatmaps
// If we have an ID then we already exist in the database.
if (beatmapSetInfo.ID != 0) return;
lock (beatmaps)
beatmaps.Add(beatmapSetInfo);
beatmaps.Add(beatmapSetInfo);
}
/// <summary>
/// Downloads a beatmap.
/// </summary>
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
/// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
{
var existing = GetExistingDownload(beatmapSetInfo);
if (existing != null) return existing;
if (api == null) return null;
ProgressNotification downloadNotification = new ProgressNotification
{
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
};
var request = new DownloadBeatmapSetRequest(beatmapSetInfo);
request.DownloadProgressed += progress =>
{
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
request.Success += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
using (var stream = new MemoryStream(data))
using (var archive = new OszArchiveReader(stream))
Import(archive);
currentDownloads.Remove(request);
};
request.Failure += data =>
{
downloadNotification.State = ProgressNotificationState.Completed;
Logger.Error(data, "Failed to get beatmap download information");
currentDownloads.Remove(request);
};
downloadNotification.CancelRequested += () =>
{
request.Cancel();
currentDownloads.Remove(request);
downloadNotification.State = ProgressNotificationState.Cancelled;
return true;
};
currentDownloads.Add(request);
PostNotification?.Invoke(downloadNotification);
// don't run in the main api queue as this is a long-running task.
Task.Run(() => request.Perform(api));
return request;
}
/// <summary>
/// Get an existing download request if it exists.
/// </summary>
/// <param name="beatmap">The <see cref="BeatmapSetInfo"/> whose download request is wanted.</param>
/// <returns>The <see cref="DownloadBeatmapSetRequest"/> object if it exists, or null.</returns>
public DownloadBeatmapSetRequest GetExistingDownload(BeatmapSetInfo beatmap) => currentDownloads.Find(d => d.BeatmapSet.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
/// <summary>
/// Delete a beatmap from the manager.
/// Is a no-op for already deleted beatmaps.
/// </summary>
/// <param name="beatmapSet">The beatmap to delete.</param>
/// <param name="beatmapSet">The beatmap set to delete.</param>
public void Delete(BeatmapSetInfo beatmapSet)
{
lock (beatmaps)
if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmapSet.Protected)
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>
/// 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.
@@ -187,8 +285,7 @@ namespace osu.Game.Beatmaps
/// <param name="beatmapSet">The beatmap to restore.</param>
public void Undelete(BeatmapSetInfo beatmapSet)
{
lock (beatmaps)
if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
@@ -248,6 +345,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>
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
/// </summary>
@@ -255,7 +359,7 @@ namespace osu.Game.Beatmaps
/// <returns>Results from the provided query.</returns>
public List<BeatmapSetInfo> QueryBeatmapSets(Expression<Func<BeatmapSetInfo, bool>> query)
{
lock (beatmaps) return beatmaps.QueryAndPopulate(query);
return beatmaps.QueryAndPopulate(query);
}
/// <summary>
@@ -265,15 +369,12 @@ namespace osu.Game.Beatmaps
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapInfo QueryBeatmap(Func<BeatmapInfo, bool> query)
{
lock (beatmaps)
{
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
BeatmapInfo set = beatmaps.Query<BeatmapInfo>().FirstOrDefault(query);
if (set != null)
beatmaps.Populate(set);
if (set != null)
beatmaps.Populate(set);
return set;
}
return set;
}
/// <summary>
+41 -2
View File
@@ -16,11 +16,14 @@ namespace osu.Game.Beatmaps
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
public event Action<BeatmapInfo> BeatmapHidden;
public event Action<BeatmapInfo> BeatmapRestored;
/// <summary>
/// The current version of this store. Used for migrations (see <see cref="PerformMigration(int, int)"/>).
/// The initial version is 1.
/// </summary>
protected override int StoreVersion => 3;
protected override int StoreVersion => 4;
public BeatmapStore(SQLiteConnection connection)
: base(connection)
@@ -81,6 +84,10 @@ namespace osu.Game.Beatmaps
// Added MD5Hash column to BeatmapInfo
Connection.MigrateTable<BeatmapInfo>();
break;
case 4:
// Added Hidden column to BeatmapInfo
Connection.MigrateTable<BeatmapInfo>();
break;
}
}
}
@@ -100,7 +107,7 @@ namespace osu.Game.Beatmaps
}
/// <summary>
/// Delete a <see cref="BeatmapSetInfo"/> to the database.
/// Delete a <see cref="BeatmapSetInfo"/> from the database.
/// </summary>
/// <param name="beatmapSet">The beatmap to delete.</param>
/// <returns>Whether the beatmap's <see cref="BeatmapSetInfo.DeletePending"/> was changed.</returns>
@@ -131,6 +138,38 @@ namespace osu.Game.Beatmaps
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()
{
Connection.RunInTransaction(() =>
+18 -4
View File
@@ -11,6 +11,8 @@ namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapGroup : IStateful<BeatmapGroupState>
{
public event Action<BeatmapGroupState> StateChanged;
public BeatmapPanel SelectedPanel;
/// <summary>
@@ -23,19 +25,26 @@ namespace osu.Game.Beatmaps.Drawables
/// </summary>
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 BeatmapSetInfo BeatmapSet;
private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
state = value;
switch (value)
{
case BeatmapGroupState.Expanded:
@@ -54,7 +63,8 @@ namespace osu.Game.Beatmaps.Drawables
panel.State = PanelSelectedState.Hidden;
break;
}
state = value;
StateChanged?.Invoke(state);
}
}
@@ -66,14 +76,17 @@ namespace osu.Game.Beatmaps.Drawables
Header = new BeatmapSetHeader(beatmap)
{
GainedSelection = headerGainedSelection,
DeleteRequested = b => DeleteRequested(b),
RestoreHiddenRequested = b => RestoreHiddenRequested(b),
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)
{
Alpha = 0,
GainedSelection = panelGainedSelection,
HideRequested = p => HideDifficultyRequested?.Invoke(p),
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
RelativeSizeAxes = Axes.X,
}).ToList();
@@ -81,6 +94,7 @@ namespace osu.Game.Beatmaps.Drawables
Header.AddDifficultyIcons(BeatmapPanels);
}
private void headerGainedSelection(BeatmapSetHeader panel)
{
State = BeatmapGroupState.Expanded;
+13 -1
View File
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
@@ -14,16 +15,20 @@ using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapPanel : Panel
public class BeatmapPanel : Panel, IHasContextMenu
{
public BeatmapInfo Beatmap;
private readonly Sprite background;
public Action<BeatmapPanel> GainedSelection;
public Action<BeatmapPanel> StartRequested;
public Action<BeatmapPanel> EditRequested;
public Action<BeatmapInfo> HideRequested;
private readonly Triangles triangles;
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.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Beatmaps.Drawables
{
public class BeatmapSetHeader : Panel
public class BeatmapSetHeader : Panel, IHasContextMenu
{
public Action<BeatmapSetHeader> GainedSelection;
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreHiddenRequested;
private readonly SpriteText title;
private readonly SpriteText artist;
@@ -148,5 +157,23 @@ namespace osu.Game.Beatmaps.Drawables
foreach (var p in panels)
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,
Hard,
Insane,
Expert
Expert,
ExpertPlus
}
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
@@ -44,7 +45,8 @@ namespace osu.Game.Beatmaps.Drawables
if (rating < 2.25) return DifficultyRating.Normal;
if (rating < 3.75) return DifficultyRating.Hard;
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)
@@ -55,12 +57,14 @@ namespace osu.Game.Beatmaps.Drawables
return palette.Green;
default:
case DifficultyRating.Normal:
return palette.Yellow;
return palette.Blue;
case DifficultyRating.Hard:
return palette.Pink;
return palette.Yellow;
case DifficultyRating.Insane:
return palette.Purple;
return palette.Pink;
case DifficultyRating.Expert:
return palette.Purple;
case DifficultyRating.ExpertPlus:
return palette.Gray0;
}
}
+8 -1
View File
@@ -1,6 +1,7 @@
// 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;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -15,6 +16,8 @@ namespace osu.Game.Beatmaps.Drawables
{
public const float MAX_HEIGHT = 80;
public event Action<PanelSelectedState> StateChanged;
public override bool RemoveWhenNotAlive => false;
private readonly Container nestedContainer;
@@ -77,11 +80,15 @@ namespace osu.Game.Beatmaps.Drawables
set
{
if (state == value) return;
if (state == value)
return;
var last = state;
state = value;
ApplyState(last);
StateChanged?.Invoke(State);
}
}
+1 -7
View File
@@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -42,10 +41,7 @@ namespace osu.Game.Beatmaps
this.game = game;
}
protected override Beatmap GetBeatmap() => new Beatmap
{
HitObjects = new List<HitObject>(),
};
protected override Beatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4");
@@ -59,8 +55,6 @@ namespace osu.Game.Beatmaps
{
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
public override Mod GetAutoplayMod() => new ModAutoplay();
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset)
{
throw new NotImplementedException();
+1 -3
View File
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps.Formats
{
@@ -21,7 +20,7 @@ namespace osu.Game.Beatmaps.Formats
{
string line;
do { line = stream.ReadLine()?.Trim(); }
while (line != null && line.Length == 0);
while (line != null && line.Length == 0);
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format");
@@ -47,7 +46,6 @@ namespace osu.Game.Beatmaps.Formats
{
var beatmap = new Beatmap
{
HitObjects = new List<HitObject>(),
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata(),
+1 -1
View File
@@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
{
if (beatmap != null) return beatmap;
beatmap = GetBeatmap();
beatmap = GetBeatmap() ?? new Beatmap();
// use the database-backed info.
beatmap.BeatmapInfo = BeatmapInfo;
@@ -19,12 +19,12 @@ namespace osu.Game.Graphics.Containers
samplePopIn = audio.Sample.Get(@"UI/melodic-5");
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:
samplePopIn?.Play();
@@ -9,6 +9,6 @@ namespace osu.Game.Graphics.Cursor
{
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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using osu.Framework;
using osu.Framework.Graphics;
@@ -35,6 +36,8 @@ namespace osu.Game.Graphics.UserInterface
private class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
{
public event Action<Visibility> StateChanged;
public readonly SpriteIcon Chevron;
//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;
private Visibility state;
public Visibility State
{
get { return state; }
@@ -62,6 +66,8 @@ namespace osu.Game.Graphics.UserInterface
this.FadeOut(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>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterface
{
public class OsuContextMenu<TItem> : ContextMenu<TItem>
where TItem : ContextMenuItem
public class OsuContextMenu : OsuMenu
{
protected override Menu<TItem> CreateMenu() => new CustomMenu();
private const int fade_duration = 250;
public class CustomMenu : Menu<TItem>
public OsuContextMenu()
: base(Direction.Vertical)
{
private const int fade_duration = 250;
public CustomMenu()
MaskingContainer.CornerRadius = 5;
MaskingContainer.EdgeEffect = new EdgeEffectParameters
{
CornerRadius = 5;
ItemsContainer.Padding = new MarginPadding { Vertical = OsuContextMenuItem.MARGIN_VERTICAL };
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.1f),
Radius = 4,
};
}
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.1f),
Radius = 4,
};
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Background.Colour = colours.ContextMenuGray;
}
protected override void AnimateOpen() => this.FadeIn(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);
}
ItemsContainer.Padding = new MarginPadding { Vertical = DrawableOsuMenuItem.MARGIN_VERTICAL };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.ContextMenuGray;
}
protected override void AnimateOpen() => this.FadeIn(fade_duration, Easing.OutQuint);
protected override void AnimateClose() => this.FadeOut(fade_duration, 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);
}
}
}
+152 -74
View File
@@ -14,107 +14,185 @@ using OpenTK;
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 };
protected override Menu CreateMenu() => new OsuMenu();
private Color4? accentColour;
public virtual Color4 AccentColour
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
get { return accentColour; }
set
{
accentColour = value;
if (Header != null)
((OsuDropdownHeader)Header).AccentColour = value;
foreach (var item in MenuItems.OfType<OsuDropdownMenuItem>())
item.AccentColour = value;
updateAccentColour();
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (accentColour == null)
AccentColour = colours.PinkDarker;
if (accentColour == default(Color4))
accentColour = colours.PinkDarker;
updateAccentColour();
}
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour };
public class OsuDropdownMenuItem : DropdownMenuItem<T>
private void updateAccentColour()
{
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()
{
Foreground.Padding = new MarginPadding(2);
CornerRadius = 4;
BackgroundColour = Color4.Black.Opacity(0.5f);
Masking = true;
CornerRadius = 6;
// 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);
}
Children = new[]
// 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)
{
new FillFlowContainer
Width = newSize.X;
this.ResizeHeightTo(newSize.Y, 300, Easing.OutQuint);
}
else
{
Direction = FillDirection.Horizontal,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
Chevron = new SpriteIcon
{
AlwaysPresent = true,
Icon = FontAwesome.fa_chevron_right,
Colour = Color4.Black,
Alpha = 0.5f,
Size = new Vector2(8),
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
Label = new OsuSpriteText {
Text = text,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
}
}
Height = newSize.Y;
this.ResizeWidthTo(newSize.X, 300, Easing.OutQuint);
}
};
}
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;
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
get { return accentColour; }
set
{
accentColour = value;
BackgroundColourHover = BackgroundColourSelected = value;
FormatBackground();
FormatForeground();
foreach (var c in Children.OfType<IHasAccentColour>())
c.AccentColour = value;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Transparent;
BackgroundColourHover = accentColour ?? colours.PinkDarker;
BackgroundColourSelected = Color4.Black.Opacity(0.5f);
}
}
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuDropdownMenuItem(item) { AccentColour = accentColour };
public class OsuDropdownHeader : DropdownHeader
#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);
Masking = true;
CornerRadius = 6;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Transparent;
nonAccentHoverColour = colours.PinkDarker;
nonAccentSelectedColour = Color4.Black.Opacity(0.5f);
updateColours();
}
protected override void UpdateForegroundColour()
{
base.UpdateForegroundColour();
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[]
{
Chevron = new SpriteIcon
{
AlwaysPresent = true,
Icon = FontAwesome.fa_chevron_right,
Colour = Color4.Black,
Alpha = 0.5f,
Size = new Vector2(8),
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
Label = new OsuSpriteText
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
}
};
}
}
}
#endregion
}
#endregion
public class OsuDropdownHeader : DropdownHeader, IHasAccentColour
{
protected readonly SpriteText Text;
protected override string Label
@@ -125,14 +203,14 @@ namespace osu.Game.Graphics.UserInterface
protected readonly SpriteIcon Icon;
private Color4? accentColour;
private Color4 accentColour;
public virtual Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
get { return accentColour; }
set
{
accentColour = value;
BackgroundColourHover = value;
BackgroundColourHover = accentColour;
}
}
@@ -167,7 +245,7 @@ namespace osu.Game.Graphics.UserInterface
private void load(OsuColour colours)
{
BackgroundColour = Color4.Black.Opacity(0.5f);
BackgroundColourHover = accentColour ?? colours.PinkDarker;
BackgroundColourHover = colours.PinkDarker;
}
}
}
+146 -8
View File
@@ -1,32 +1,170 @@
// 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;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Graphics.UserInterface
{
public class OsuMenu : Menu
{
public OsuMenu()
public OsuMenu(Direction direction, bool topLevelMenu = false)
: base(direction, topLevelMenu)
{
CornerRadius = 4;
Background.Colour = Color4.Black.Opacity(0.5f);
BackgroundColour = Color4.Black.Opacity(0.5f);
MaskingContainer.CornerRadius = 4;
ItemsContainer.Padding = new MarginPadding(5);
}
protected override void AnimateOpen() => this.FadeIn(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;
this.ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, Easing.OutQuint);
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);
}
}
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableOsuMenuItem(item);
protected override Menu CreateSubMenu() => new OsuMenu(Direction.Vertical)
{
Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight
};
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 sealed override Drawable CreateContent() => text = CreateTextContainer();
protected virtual TextContainer CreateTextContainer() => new TextContainer();
protected 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 },
}
};
}
}
}
}
}
@@ -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>.
// 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.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.Input;
using osu.Framework.Platform;
namespace osu.Game.Graphics.UserInterface
{
@@ -15,6 +20,49 @@ namespace osu.Game.Graphics.UserInterface
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
{
private readonly CircularContainer circle;
@@ -51,5 +99,21 @@ namespace osu.Game.Graphics.UserInterface
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]
private void load(OsuColour colours)
{
if (accentColour == null)
if (accentColour == default(Color4))
AccentColour = colours.Blue;
}
private Color4? accentColour;
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
get { return accentColour; }
set
{
accentColour = value;
var dropDown = Dropdown as OsuTabDropdown;
if (dropDown != null)
dropDown.AccentColour = value;
foreach (var item in TabContainer.Children.OfType<OsuTabItem>())
item.AccentColour = value;
var dropdown = Dropdown as IHasAccentColour;
if (dropdown != null)
dropdown.AccentColour = value;
foreach (var i in TabContainer.Children.OfType<IHasAccentColour>())
i.AccentColour = value;
}
}
public class OsuTabItem : TabItem<T>
public class OsuTabItem : TabItem<T>, IHasAccentColour
{
protected readonly SpriteText Text;
private readonly Box box;
private Color4? accentColour;
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
get { return accentColour; }
set
{
accentColour = value;
@@ -103,7 +103,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (accentColour == null)
if (accentColour == default(Color4))
AccentColour = colours.Blue;
}
@@ -140,38 +140,55 @@ namespace osu.Game.Graphics.UserInterface
protected override void OnDeactivated() => fadeInactive();
}
// todo: this needs to go
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()
{
DropdownMenu.Anchor = Anchor.TopRight;
DropdownMenu.Origin = Anchor.TopRight;
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
{
public override Color4 AccentColour
{
get { return base.AccentColour; }
get
{
return base.AccentColour;
}
set
{
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()
{
RelativeSizeAxes = Axes.None;
@@ -220,6 +225,18 @@ namespace osu.Game.Graphics.UserInterface
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);
}
}
}
}
@@ -0,0 +1,24 @@
// 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.Game.Beatmaps;
using System;
namespace osu.Game.Online.API.Requests
{
public class DownloadBeatmapSetRequest : APIDownloadRequest
{
public readonly BeatmapSetInfo BeatmapSet;
public Action<float> DownloadProgressed;
public DownloadBeatmapSetRequest(BeatmapSetInfo set)
{
BeatmapSet = set;
Progress += (current, total) => DownloadProgressed?.Invoke((float) current / total);
}
protected override string Target => $@"beatmapsets/{BeatmapSet.OnlineBeatmapSetID}/download";
}
}
@@ -1,10 +1,14 @@
// 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 System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.IO.Network;
using osu.Game.Beatmaps;
using osu.Game.Users;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Online.API.Requests
@@ -16,6 +20,14 @@ namespace osu.Game.Online.API.Requests
public GetScoresRequest(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
Success += onSuccess;
}
private void onSuccess(GetScoresResponse r)
{
foreach (OnlineScore score in r.Scores)
score.ApplyBeatmap(beatmap);
}
protected override WebRequest CreateWebRequest()
@@ -32,6 +44,88 @@ namespace osu.Game.Online.API.Requests
public class GetScoresResponse
{
[JsonProperty(@"scores")]
public IEnumerable<Score> Scores;
public IEnumerable<OnlineScore> Scores;
}
}
public class OnlineScore : Score
{
[JsonProperty(@"score")]
private double totalScore
{
set { TotalScore = value; }
}
[JsonProperty(@"max_combo")]
private int maxCombo
{
set { MaxCombo = value; }
}
[JsonProperty(@"user")]
private User user
{
set { User = value; }
}
[JsonProperty(@"replay_data")]
private Replay replay
{
set { Replay = value; }
}
[JsonProperty(@"score_id")]
private long onlineScoreID
{
set { OnlineScoreID = value; }
}
[JsonProperty(@"created_at")]
private DateTimeOffset date
{
set { Date = value; }
}
[JsonProperty(@"statistics")]
private Dictionary<string, dynamic> jsonStats
{
set
{
foreach (var kvp in value)
{
string key = kvp.Key;
switch (key)
{
case @"count_300":
key = @"300";
break;
case @"count_100":
key = @"100";
break;
case @"count_50":
key = @"50";
break;
case @"count_miss":
key = @"x";
break;
default:
continue;
}
Statistics.Add(key, kvp.Value);
}
}
}
[JsonProperty(@"mods")]
private string[] modStrings { get; set; }
public void ApplyBeatmap(BeatmapInfo beatmap)
{
Beatmap = beatmap;
Ruleset = beatmap.Ruleset;
// Evaluate the mod string
Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.ShortenedName)).ToArray();
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ namespace osu.Game.Online.Chat
public Bindable<bool> Joined = new Bindable<bool>();
public bool ReadOnly => Name != "#lazer";
public bool ReadOnly => false;
public const int MAX_HISTORY = 300;
+19 -12
View File
@@ -230,13 +230,13 @@ namespace osu.Game
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
foreach (var overlay in singleDisplayOverlays)
{
overlay.StateChanged += (container, state) =>
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
foreach (var c in singleDisplayOverlays)
{
if (c == container) continue;
if (c == overlay) continue;
c.State = Visibility.Hidden;
}
};
@@ -245,7 +245,11 @@ namespace osu.Game
LoadComponentAsync(Toolbar = new Toolbar
{
Depth = -4,
OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
OnHome = delegate
{
hideAllOverlays();
intro?.ChildScreen?.MakeCurrent();
},
}, overlayContent.Add);
settings.StateChanged += delegate
@@ -310,6 +314,16 @@ namespace osu.Game
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
private void hideAllOverlays()
{
settings.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
social.State = Visibility.Hidden;
userProfile.State = Visibility.Hidden;
notificationOverlay.State = Visibility.Hidden;
}
private void screenChanged(Screen newScreen)
{
currentScreen = newScreen as OsuScreen;
@@ -323,19 +337,12 @@ namespace osu.Game
//central game screen change logic.
if (!currentScreen.ShowOverlays)
{
settings.State = Visibility.Hidden;
Toolbar.State = Visibility.Hidden;
hideAllOverlays();
musicController.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
social.State = Visibility.Hidden;
userProfile.State = Visibility.Hidden;
notificationOverlay.State = Visibility.Hidden;
Toolbar.State = Visibility.Hidden;
}
else
{
Toolbar.State = Visibility.Visible;
}
ScreenChanged?.Invoke(newScreen);
}
+7 -7
View File
@@ -106,9 +106,15 @@ namespace osu.Game
connection.CreateTable<StoreVersion>();
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
dependencies.Cache(RulesetStore = new RulesetStore(connection));
dependencies.Cache(FileStore = new FileStore(connection, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, Host));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, connection, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, connection, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(connection, RulesetStore));
dependencies.Cache(new OsuColour());
@@ -144,12 +150,6 @@ namespace osu.Game
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
BeatmapManager.DefaultBeatmap = defaultBeatmap;
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
Beatmap.ValueChanged += b =>
{
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
+3 -2
View File
@@ -76,7 +76,6 @@ namespace osu.Game.Overlays.Chat
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
Alpha = 0f,
},
},
},
@@ -109,7 +108,6 @@ namespace osu.Game.Overlays.Chat
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
Alpha = 0.8f,
},
},
},
@@ -151,6 +149,9 @@ namespace osu.Game.Overlays.Chat
joinedBind.ValueChanged += updateColour;
joinedBind.BindTo(channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
}
protected override bool OnHover(InputState state)
+99 -4
View File
@@ -16,15 +16,16 @@ using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
namespace osu.Game.Overlays.Chat
{
public class ChatTabControl : OsuTabControl<Channel>
{
protected override TabItem<Channel> CreateTabItem(Channel value) => new ChannelTabItem(value);
private const float shear_width = 10;
public Action<Channel> OnRequestLeave;
public readonly Bindable<bool> ChannelSelectorActive = new Bindable<bool>();
private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab;
@@ -49,6 +50,20 @@ namespace osu.Game.Overlays.Chat
ChannelSelectorActive.BindTo(selectorTab.Active);
}
protected override void AddTabItem(TabItem<Channel> item, bool addToDropdown = true)
{
if (selectorTab.Depth < float.MaxValue)
// performTabSort might've made selectorTab's position wonky, fix it
TabContainer.ChangeChildDepth(selectorTab, float.MaxValue);
base.AddTabItem(item, addToDropdown);
if (SelectedTab == null)
SelectTab(item);
}
protected override TabItem<Channel> CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
protected override void SelectTab(TabItem<Channel> tab)
{
if (tab is ChannelTabItem.ChannelSelectorTabItem)
@@ -62,18 +77,38 @@ namespace osu.Game.Overlays.Chat
base.SelectTab(tab);
}
private void tabCloseRequested(TabItem<Channel> tab)
{
int totalTabs = TabContainer.Count - 1; // account for selectorTab
int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs);
if (tab == SelectedTab && totalTabs > 1)
// Select the tab after tab-to-be-removed's index, or the tab before if current == last
SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
else if (totalTabs == 1 && !selectorTab.Active)
// Open channel selection overlay if all channel tabs will be closed after removing this tab
SelectTab(selectorTab);
OnRequestLeave?.Invoke(tab.Value);
}
private class ChannelTabItem : TabItem<Channel>
{
private Color4 backgroundInactive;
private Color4 backgroundHover;
private Color4 backgroundActive;
public override bool IsRemovable => !Pinned;
private readonly SpriteText text;
private readonly SpriteText textBold;
private readonly ClickableContainer closeButton;
private readonly Box box;
private readonly Box highlightBox;
private readonly SpriteIcon icon;
public Action<ChannelTabItem> OnRequestClose;
private void updateState()
{
if (Active)
@@ -108,6 +143,9 @@ namespace osu.Game.Overlays.Chat
protected override bool OnHover(InputState state)
{
if (IsRemovable)
closeButton.FadeIn(200, Easing.OutQuint);
if (!Active)
box.FadeColour(backgroundHover, transition_length, Easing.OutQuint);
return true;
@@ -115,6 +153,7 @@ namespace osu.Game.Overlays.Chat
protected override void OnHoverLost(InputState state)
{
closeButton.FadeOut(200, Easing.OutQuint);
updateState();
}
@@ -204,13 +243,69 @@ namespace osu.Game.Overlays.Chat
Font = @"Exo2.0-Bold",
TextSize = 18,
},
}
}
closeButton = new CloseButton
{
Alpha = 0,
Margin = new MarginPadding { Right = 20 },
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Action = delegate
{
if (IsRemovable) OnRequestClose?.Invoke(this);
},
},
},
},
};
}
public class CloseButton : ClickableContainer
{
private readonly SpriteIcon icon;
public CloseButton()
{
Size = new Vector2(20);
Child = icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.75f),
Icon = FontAwesome.fa_close,
RelativeSizeAxes = Axes.Both,
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.5f, 1000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(0.75f, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(Color4.Red, 200, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.FadeColour(Color4.White, 200, Easing.OutQuint);
base.OnHoverLost(state);
}
}
public class ChannelSelectorTabItem : ChannelTabItem
{
public override bool IsRemovable => false;
public ChannelSelectorTabItem(Channel value) : base(value)
{
Depth = float.MaxValue;
+25 -2
View File
@@ -160,6 +160,7 @@ namespace osu.Game.Overlays
channelTabs = new ChatTabControl
{
RelativeSizeAxes = Axes.Both,
OnRequestLeave = removeChannel,
},
}
},
@@ -169,7 +170,7 @@ namespace osu.Game.Overlays
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden;
channelSelection.StateChanged += (overlay, state) =>
channelSelection.StateChanged += state =>
{
channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible;
@@ -305,6 +306,7 @@ namespace osu.Game.Overlays
addChannel(channels.Find(c => c.Name == @"#lobby"));
channelSelection.OnRequestJoin = addChannel;
channelSelection.OnRequestLeave = removeChannel;
channelSelection.Sections = new[]
{
new ChannelSection
@@ -332,7 +334,15 @@ namespace osu.Game.Overlays
set
{
if (currentChannel == value || value == null) return;
if (currentChannel == value) return;
if (value == null)
{
currentChannel = null;
textbox.Current.Disabled = true;
currentChannelContainer.Clear(false);
return;
}
currentChannel = value;
@@ -391,6 +401,19 @@ namespace osu.Game.Overlays
channel.Joined.Value = true;
}
private void removeChannel(Channel channel)
{
if (channel == null) return;
if (channel == CurrentChannel) CurrentChannel = null;
careChannels.Remove(channel);
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
channelTabs.RemoveItem(channel);
channel.Joined.Value = false;
}
private void fetchInitialMessages(Channel channel)
{
var req = new GetMessagesRequest(new List<Channel> { channel }, null);
+1 -1
View File
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays
dialogContainer.Add(currentDialog);
currentDialog.Show();
currentDialog.StateChanged += onDialogOnStateChanged;
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
State = Visibility.Visible;
}
+27 -68
View File
@@ -4,8 +4,6 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using System.IO;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -20,8 +18,8 @@ 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;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
@@ -97,6 +95,11 @@ namespace osu.Game.Overlays.Direct
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
}
protected override bool OnHover(InputState state)
@@ -115,23 +118,8 @@ namespace osu.Game.Overlays.Direct
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
@@ -142,72 +130,43 @@ namespace osu.Game.Overlays.Direct
return;
}
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
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;
}
var request = beatmaps.Download(SetInfo);
attachDownload(request);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
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 =>
request.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;
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
progressBar.Current.Value = progress;
downloadNotification.State = ProgressNotificationState.Active;
downloadNotification.Progress = progress;
};
downloadRequest.Success += data =>
request.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()
+26 -14
View File
@@ -27,6 +27,7 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
@@ -46,9 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
if (beatmapSets.Any() && panels == null)
// real use case? currently only seems to be for test case
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
}
@@ -147,6 +165,8 @@ namespace osu.Game.Overlays
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.BeatmapSetAdded += setAdded;
@@ -156,6 +176,7 @@ namespace osu.Game.Overlays
{
// 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();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
@@ -237,20 +258,11 @@ namespace osu.Game.Overlays
getSetsRequest.Success += r =>
{
BeatmapSets = r?.Select(response => response.ToBeatmapSet(rulesets));
if (BeatmapSets == null) return;
BeatmapSets = r?.
Select(response => response.ToBeatmapSet(rulesets)).
Where(b => beatmaps.QueryBeatmapSet(q => q.OnlineBeatmapSetID == b.OnlineBeatmapSetID) == null);
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in BeatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
};
api.Queue(getSetsRequest);
+1 -1
View File
@@ -53,7 +53,7 @@ namespace osu.Game.Overlays
private const float hidden_width = 120;
private void keyBindingOverlay_StateChanged(VisibilityContainer container, Visibility visibility)
private void keyBindingOverlay_StateChanged(Visibility visibility)
{
switch (visibility)
{
@@ -1,6 +1,7 @@
// 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;
using OpenTK;
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_full = 0.6f;
public event Action<DisplayState> StateChanged;
private readonly Medal medal;
private readonly Container medalContainer;
private readonly Sprite medalSprite, medalGlow;
@@ -132,6 +135,8 @@ namespace osu.Game.Overlays.MedalSplash
state = value;
updateState();
StateChanged?.Invoke(State);
}
}
+25 -24
View File
@@ -15,15 +15,37 @@ namespace osu.Game.Overlays.Music
{
public class CollectionsDropdown<T> : OsuDropdown<T>
{
protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new CollectionsMenu();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
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
{
[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),
};
}
}
}
}
+31 -12
View File
@@ -8,6 +8,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
@@ -16,7 +17,7 @@ using OpenTK;
namespace osu.Game.Overlays.Music
{
internal class PlaylistItem : Container, IFilterable
internal class PlaylistItem : Container, IFilterable, IDraggable
{
private const float fade_duration = 100;
@@ -33,6 +34,8 @@ namespace osu.Game.Overlays.Music
public Action<BeatmapSetInfo> OnSelect;
public bool IsDraggable => handle.IsHovered;
private bool selected;
public bool Selected
{
@@ -68,15 +71,9 @@ namespace osu.Game.Overlays.Music
Children = new Drawable[]
{
handle = new SpriteIcon
handle = new PlaylistItemHandle
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Size = new Vector2(12),
Colour = colours.Gray5,
Icon = FontAwesome.fa_bars,
Alpha = 0f,
Margin = new MarginPadding { Left = 5, Top = 2 },
Colour = colours.Gray5
},
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);
return base.OnHover(state);
}
protected override void OnHoverLost(Framework.Input.InputState state)
protected override void OnHoverLost(InputState state)
{
handle.FadeOut(fade_duration);
}
protected override bool OnClick(Framework.Input.InputState state)
protected override bool OnClick(InputState state)
{
OnSelect?.Invoke(BeatmapSetInfo);
return true;
@@ -148,5 +145,27 @@ namespace osu.Game.Overlays.Music
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; }
}
}
+215 -68
View File
@@ -4,105 +4,252 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using OpenTK;
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;
private readonly SearchContainer search;
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;
}
}
private readonly ItemsScrollContainer items;
public PlaylistList()
{
Children = new Drawable[]
InternalChild = items = new ItemsScrollContainer
{
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
search = new SearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
items = new ItemSearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
}
},
},
RelativeSizeAxes = Axes.Both,
OnSelect = set => OnSelect?.Invoke(set)
};
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
public new MarginPadding Padding
{
items.Add(new PlaylistItem(beatmapSet) { OnSelect = itemSelected });
get { return base.Padding; }
set { base.Padding = value; }
}
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
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
{
PlaylistItem itemToRemove = items.Children.FirstOrDefault(item => item.BeatmapSetInfo.ID == beatmapSet.ID);
if (itemToRemove != null) items.Remove(itemToRemove);
get { return items.SelectedSet; }
set { items.SelectedSet = value; }
}
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
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 string[] FilterTerms => new string[] { };
public bool MatchingFilter
public Action<BeatmapSetInfo> OnSelect;
private readonly SearchContainer search;
private readonly FillFlowContainer<PlaylistItem> items;
public ItemsScrollContainer()
{
Children = new Drawable[]
{
search = new SearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
items = new ItemSearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
}
};
}
public IEnumerable<BeatmapSetInfo> Sets
{
set
{
if (value)
InvalidateLayout();
items.Clear();
value.ForEach(AddBeatmapSet);
}
}
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()
public string SearchTerm
{
LayoutDuration = 200;
LayoutEasing = Easing.OutQuint;
get { return search.SearchTerm; }
set { search.SearchTerm = value; }
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
{
items.Add(new PlaylistItem(beatmapSet)
{
OnSelect = set => OnSelect?.Invoke(set),
Depth = items.Count
});
}
public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
{
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
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
{
public string[] FilterTerms => new string[] { };
public bool MatchingFilter
{
set
{
if (value)
InvalidateLayout();
}
}
// Compare with reversed ChildID and Depth
protected override int Compare(Drawable x, Drawable y) => base.Compare(y, x);
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()
{
LayoutDuration = 200;
LayoutEasing = Easing.OutQuint;
}
}
}
}
}
}
+9 -9
View File
@@ -92,7 +92,7 @@ namespace osu.Game.Overlays.Music
protected override void LoadComplete()
{
base.LoadComplete();
beatmapBacking.ValueChanged += b => list.SelectedItem = b?.BeatmapSetInfo;
beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo;
beatmapBacking.TriggerChange();
}
@@ -126,24 +126,24 @@ namespace osu.Game.Overlays.Music
public void PlayPrevious()
{
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
var available = BeatmapSets.Reverse();
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
var playable = list.PreviousSet;
if (playable != null)
{
playSpecified(playable.Beatmaps[0]);
list.SelectedSet = playable;
}
}
public void PlayNext()
{
var currentID = beatmapBacking.Value?.BeatmapSetInfo.ID ?? -1;
var available = BeatmapSets;
var playable = available.SkipWhile(b => b.ID != currentID).Skip(1).FirstOrDefault() ?? available.FirstOrDefault();
var playable = list.NextSet;
if (playable != null)
{
playSpecified(playable.Beatmaps[0]);
list.SelectedSet = playable;
}
}
private void playSpecified(BeatmapInfo info)
+1 -1
View File
@@ -204,7 +204,7 @@ namespace osu.Game.Overlays
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()
@@ -12,8 +12,9 @@ namespace osu.Game.Overlays.SearchableList
{
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
{
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new SlimMenu();
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
protected override DropdownMenu CreateMenu() => new SlimMenu();
private class SlimDropdownHeader : OsuDropdownHeader
{
@@ -31,11 +32,11 @@ namespace osu.Game.Overlays.SearchableList
}
}
private class SlimMenu : OsuMenu
private class SlimMenu : OsuDropdownMenu
{
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>
{
protected override DropdownHeader CreateHeader() => new UserDropdownHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new UserDropdownMenu();
protected override DropdownMenuItem<UserAction> CreateMenuItem(string text, UserAction value) => new UserDropdownMenuItem(text, value) { AccentColour = AccentColour };
protected override DropdownHeader CreateHeader() => new UserDropdownHeader();
protected override DropdownMenu CreateMenu() => new UserDropdownMenu();
public Color4 StatusColour
{
@@ -277,6 +277,49 @@ namespace osu.Game.Overlays.Settings.Sections.General
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
{
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
@@ -13,6 +13,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
{
private OsuButton importButton;
private OsuButton deleteButton;
private OsuButton restoreButton;
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));
}
},
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));
}
},
};
}
}
+6
View File
@@ -1,6 +1,7 @@
// 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 System.Linq;
using osu.Framework;
using OpenTK;
@@ -19,6 +20,9 @@ namespace osu.Game.Overlays.Settings
private readonly FillFlowContainer<SidebarButton> content;
internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH;
internal const int EXPANDED_WIDTH = 200;
public event Action<ExpandedState> StateChanged;
protected override Container<SidebarButton> Content => content;
public Sidebar()
@@ -102,6 +106,8 @@ namespace osu.Game.Overlays.Settings
this.ResizeTo(new Vector2(EXPANDED_WIDTH, Height), 500, Easing.OutQuint);
break;
}
StateChanged?.Invoke(State);
}
}
@@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Toolbar
stateContainer.StateChanged -= stateChanged;
}
private void stateChanged(VisibilityContainer c, Visibility state)
private void stateChanged(Visibility state)
{
switch (state)
{
@@ -167,6 +167,8 @@ namespace osu.Game.Overlays
private class Wave : Container, IStateful<Visibility>
{
public event Action<Visibility> StateChanged;
public float FinalPosition;
public Wave()
@@ -200,6 +202,7 @@ namespace osu.Game.Overlays
}
private Visibility state;
public Visibility State
{
get { return state; }
@@ -216,6 +219,8 @@ namespace osu.Game.Overlays
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
break;
}
StateChanged?.Invoke(State);
}
}
}
+5
View File
@@ -16,6 +16,11 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public abstract string Name { get; }
/// <summary>
/// The shortened name of this mod.
/// </summary>
public abstract string ShortenedName { get; }
/// <summary>
/// The icon of this mod.
/// </summary>
+1
View File
@@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mods
public class ModAutoplay : Mod
{
public override string Name => "Autoplay";
public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0;
+1
View File
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public class ModCinema : ModAutoplay
{
public override string Name => "Cinema";
public override string ShortenedName => "CN";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
}
}
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModDaycore : ModHalfTime
{
public override string Name => "Daycore";
public override string ShortenedName => "DC";
public override FontAwesome Icon => FontAwesome.fa_question;
public override string Description => "whoaaaaa";
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public class ModDoubleTime : Mod, IApplicableToClock
{
public override string Name => "Double Time";
public override string ShortenedName => "DT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom";
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModEasy : Mod, IApplicableToDifficulty
{
public override string Name => "Easy";
public override string ShortenedName => "EZ";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";
+1
View File
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModFlashlight : Mod
{
public override string Name => "Flashlight";
public override string ShortenedName => "FL";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Restricted view area.";
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHalfTime : Mod, IApplicableToClock
{
public override string Name => "Half Time";
public override string ShortenedName => "HT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Less zoom";
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHardRock : Mod, IApplicableToDifficulty
{
public override string Name => "Hard Rock";
public override string ShortenedName => "HR";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";
+1
View File
@@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModHidden : Mod
{
public override string Name => "Hidden";
public override string ShortenedName => "HD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;
+1
View File
@@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNightcore : ModDoubleTime
{
public override string Name => "Nightcore";
public override string ShortenedName => "NC";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore;
public override string Description => "uguuuuuuuu";
+1
View File
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModNoFail : Mod
{
public override string Name => "NoFail";
public override string ShortenedName => "NF";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "You can't fail, no matter what.";
+1
View File
@@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModPerfect : ModSuddenDeath
{
public override string Name => "Perfect";
public override string ShortenedName => "PF";
public override string Description => "SS or quit.";
}
}
+1
View File
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModRelax : Mod
{
public override string Name => "Relax";
public override string ShortenedName => "RX";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
+1
View File
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
public abstract class ModSuddenDeath : Mod
{
public override string Name => "Sudden Death";
public override string ShortenedName => "SD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss a note and fail.";
+1
View File
@@ -6,6 +6,7 @@ namespace osu.Game.Rulesets.Mods
public class MultiMod : Mod
{
public override string Name => string.Empty;
public override string ShortenedName => string.Empty;
public override string Description => string.Empty;
public override double ScoreMultiplier => 0.0;
+17 -6
View File
@@ -1,15 +1,17 @@
// 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.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets
{
@@ -19,9 +21,18 @@ namespace osu.Game.Rulesets
public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
// Get all mod types as an IEnumerable<ModType>
.SelectMany(GetModsFor)
// Confine all mods of each mod type into a single IEnumerable<Mod>
.Where(mod => mod != null)
// Filter out all null mods
.SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod });
// Resolve MultiMods as their .Mods property
public abstract IEnumerable<Mod> GetModsFor(ModType type);
public abstract Mod GetAutoplayMod();
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo)
{
-41
View File
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
@@ -15,70 +14,30 @@ namespace osu.Game.Rulesets.Scoring
{
public ScoreRank Rank { get; set; }
[JsonProperty(@"score")]
public double TotalScore { get; set; }
public double Accuracy { get; set; }
public double Health { get; set; } = 1;
[JsonProperty(@"max_combo")]
public int MaxCombo { get; set; }
public int Combo { get; set; }
[JsonProperty(@"mods")]
protected string[] ModStrings { get; set; } //todo: parse to Mod objects
public RulesetInfo Ruleset { get; set; }
public Mod[] Mods { get; set; }
[JsonProperty(@"user")]
public User User;
[JsonProperty(@"replay_data")]
public Replay Replay;
public BeatmapInfo Beatmap;
[JsonProperty(@"score_id")]
public long OnlineScoreID;
[JsonProperty(@"created_at")]
public DateTimeOffset Date;
[JsonProperty(@"statistics")]
private Dictionary<string, dynamic> jsonStats
{
set
{
foreach (var kvp in value)
{
string key = kvp.Key;
switch (key)
{
case @"count_300":
key = @"300";
break;
case @"count_100":
key = @"100";
break;
case @"count_50":
key = @"50";
break;
case @"count_miss":
key = @"x";
break;
default:
continue;
}
Statistics.Add(key, kvp.Value);
}
}
}
public Dictionary<string, dynamic> Statistics = new Dictionary<string, dynamic>();
}
}
+3 -3
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.UI
private readonly SpriteIcon modIcon;
private readonly SpriteIcon background;
private const float icon_size = 80;
private const float background_size = 80;
public FontAwesome Icon
{
@@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.UI
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(icon_size),
Size = new Vector2(background_size),
Icon = FontAwesome.fa_osu_mod_bg,
Shadow = true,
},
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.UI
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = OsuColour.Gray(84),
Size = new Vector2(icon_size - 35),
Size = new Vector2(background_size - 35),
Y = 25,
Icon = mod.Icon
},
+4
View File
@@ -75,7 +75,11 @@ namespace osu.Game.Rulesets.UI
internal RulesetContainer(Ruleset ruleset)
{
Ruleset = ruleset;
}
[BackgroundDependencyLoader]
private void load()
{
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
}
+174
View File
@@ -8,6 +8,11 @@ using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Select;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
namespace osu.Game.Screens.Edit
{
@@ -17,6 +22,175 @@ namespace osu.Game.Screens.Edit
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
internal override bool ShowOverlays => false;
public Editor()
{
Add(new Container
{
RelativeSizeAxes = Axes.X,
Height = 40,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("111")
},
new EditorMenuBar
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
X = 100,
Items = new[]
{
new EditorMenuBarItem("File")
{
Items = new[]
{
new EditorMenuItem("Clear all notes"),
new EditorMenuItem("Open difficulty..."),
new EditorMenuItem("Save"),
new EditorMenuItem("Create new difficulty..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Revert to saved"),
new EditorMenuItem("Revert to saved (full"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Test beatmap"),
new EditorMenuItem("Open AiMod"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Upload Beatmap..."),
new EditorMenuItem("Export package"),
new EditorMenuItem("Export map package"),
new EditorMenuItem("Import from..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Open song folder"),
new EditorMenuItem("Open .osu in Notepad"),
new EditorMenuItem("Open .osb in Notepad"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Exit", MenuItemType.Standard, Exit)
}
},
new EditorMenuBarItem("Edit")
{
Items = new[]
{
new EditorMenuItem("Undo"),
new EditorMenuItem("Redo"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Cut"),
new EditorMenuItem("Copy"),
new EditorMenuItem("Paste"),
new EditorMenuItem("Delete"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Select all"),
new EditorMenuItem("Clone"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Reverse selection"),
new EditorMenuItem("Flip horizontally"),
new EditorMenuItem("Flip vertically"),
new EditorMenuItem("Rotate 90deg clockwise"),
new EditorMenuItem("Rotate 90deg anticlockwise"),
new EditorMenuItem("Rotate by..."),
new EditorMenuItem("Scale by..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Reset selected objects' samples"),
new EditorMenuItem("Reset all samples", MenuItemType.Destructive),
new EditorMenuItem("Reset combo colours", MenuItemType.Destructive),
new EditorMenuItem("Reset breaks", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Nudge backward"),
new EditorMenuItem("Nudge forward")
}
},
new EditorMenuBarItem("View")
{
Items = new[]
{
new EditorMenuItem("Compose"),
new EditorMenuItem("Design"),
new EditorMenuItem("Timing"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Song setup..."),
new EditorMenuItem("Timing setup..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Volume"),
new EditorMenuItem("Grid level"),
new EditorMenuItem("Show video"),
new EditorMenuItem("Show sample name"),
new EditorMenuItem("Snaking sliders"),
new EditorMenuItem("Hit animations"),
new EditorMenuItem("Follow points"),
new EditorMenuItem("Stacking")
}
},
new EditorMenuBarItem("Compose")
{
Items = new[]
{
new EditorMenuItem("Snap divisor"),
new EditorMenuItem("Audio rate"),
new EditorMenuItem("Grid snapping"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Create polygon cricles..."),
new EditorMenuItem("Convert slider to stream"),
new EditorMenuItem("Enable live mapping mode"),
new EditorMenuItem("Sample import")
}
},
new EditorMenuBarItem("Design")
{
Items = new[]
{
new EditorMenuItem("Move all elements in time...")
}
},
new EditorMenuBarItem("Timing")
{
Items = new[]
{
new EditorMenuItem("Time signature"),
new EditorMenuItem("Metronome clicks"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Add timing section"),
new EditorMenuItem("Add inheriting section"),
new EditorMenuItem("Reset current section"),
new EditorMenuItem("Delete timing section"),
new EditorMenuItem("Resnap current section"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Timing setup..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Resnap all notes", MenuItemType.Destructive),
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
new EditorMenuItem("Recalculate slider lengths", MenuItemType.Destructive),
new EditorMenuItem("Delete all timing sections", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Set current position as preview point")
}
},
new EditorMenuBarItem("Web")
{
Items = new[]
{
new EditorMenuItem("This Beatmap's information page"),
new EditorMenuItem("This Beatmap's thread"),
new EditorMenuItem("Quick reply")
}
},
new EditorMenuBarItem("Help")
{
Items = new[]
{
new EditorMenuItem("Show in-game help"),
new EditorMenuItem("View FAQ")
}
}
}
}
}
});
}
protected override void OnResuming(Screen last)
{
Beatmap.Value.Track?.Stop();
@@ -0,0 +1,122 @@
// 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.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuBar : OsuMenu
{
public EditorMenuBar()
: base(Direction.Horizontal, true)
{
ItemsContainer.Padding = new MarginPadding(0);
BackgroundColour = Color4.Transparent;
}
protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableEditorBarMenuItem(item);
private class DrawableEditorBarMenuItem : DrawableOsuMenuItem
{
private Color4 openedForegroundColour;
private Color4 openedBackgroundColour;
public DrawableEditorBarMenuItem(MenuItem item)
: base(item)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ForegroundColour = ForegroundColourHover = colours.BlueLight;
BackgroundColour = BackgroundColourHover = Color4.Transparent;
openedForegroundColour = Color4.White;
openedBackgroundColour = colours.Gray3;
}
protected override void UpdateBackgroundColour()
{
if (State == MenuItemState.Selected)
Background.FadeColour(openedBackgroundColour);
else
base.UpdateBackgroundColour();
}
protected override void UpdateForegroundColour()
{
if (State == MenuItemState.Selected)
Foreground.FadeColour(openedForegroundColour);
else
base.UpdateForegroundColour();
}
protected override Drawable CreateBackground() => new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Height = 2,
Masking = true,
CornerRadius = 4,
Child = new Box { RelativeSizeAxes = Axes.Both }
}
};
}
private class SubMenu : OsuMenu
{
public SubMenu()
: base(Direction.Vertical)
{
OriginPosition = new Vector2(5, 1);
ItemsContainer.Padding = new MarginPadding { Top = 5, Bottom = 5 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray3;
}
protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
protected override DrawableMenuItem CreateDrawableMenuItem(MenuItem item) => new DrawableSubMenuItem(item);
private class DrawableSubMenuItem : DrawableOsuMenuItem
{
public DrawableSubMenuItem(MenuItem item)
: base(item)
{
}
protected override bool OnHover(InputState state)
{
if (Item is EditorMenuItemSpacer)
return true;
return base.OnHover(state);
}
protected override bool OnClick(InputState state)
{
if (Item is EditorMenuItemSpacer)
return true;
return base.OnClick(state);
}
}
}
}
}
@@ -0,0 +1,15 @@
// 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.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuBarItem : MenuItem
{
public EditorMenuBarItem(string text)
: base(text)
{
}
}
}
@@ -0,0 +1,23 @@
// 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.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuItem : OsuMenuItem
{
private const int min_text_length = 40;
public EditorMenuItem(string text, MenuItemType type = MenuItemType.Standard)
: base(text.PadRight(min_text_length), type)
{
}
public EditorMenuItem(string text, MenuItemType type, Action action)
: base(text.PadRight(min_text_length), type, action)
{
}
}
}
@@ -0,0 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuItemSpacer : EditorMenuItem
{
public EditorMenuItemSpacer()
: base(" ")
{
}
}
}
+4
View File
@@ -28,6 +28,8 @@ namespace osu.Game.Screens.Menu
/// </summary>
public class Button : BeatSyncedContainer, IStateful<ButtonState>
{
public event Action<ButtonState> StateChanged;
private readonly Container iconText;
private readonly Container box;
private readonly Box boxHoverLayer;
@@ -266,6 +268,8 @@ namespace osu.Game.Screens.Menu
this.FadeOut(explode_duration / 4f * 3);
break;
}
StateChanged?.Invoke(State);
}
}
}
+4
View File
@@ -22,6 +22,8 @@ namespace osu.Game.Screens.Menu
{
public class ButtonSystem : Container, IStateful<MenuState>
{
public event Action<MenuState> StateChanged;
public Action OnEdit;
public Action OnExit;
public Action OnDirect;
@@ -294,6 +296,8 @@ namespace osu.Game.Screens.Menu
backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;
}
StateChanged?.Invoke(State);
}
}
+7 -1
View File
@@ -133,6 +133,8 @@ namespace osu.Game.Screens.Play
private class FadeContainer : Container, IStateful<Visibility>
{
public event Action<Visibility> StateChanged;
private Visibility state;
private ScheduledDelegate scheduledHide;
@@ -144,8 +146,10 @@ namespace osu.Game.Screens.Play
}
set
{
var lastState = state;
if (state == value)
return;
var lastState = state;
state = value;
scheduledHide?.Cancel();
@@ -164,6 +168,8 @@ namespace osu.Game.Screens.Play
this.FadeOut(1000, Easing.OutExpo);
break;
}
StateChanged?.Invoke(State);
}
}
+6
View File
@@ -1,6 +1,7 @@
// 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 System.Collections.Generic;
using System.Linq;
using osu.Framework;
@@ -170,6 +171,8 @@ namespace osu.Game.Screens.Play
private const float padding = 2;
public const float WIDTH = cube_size + padding;
public event Action<ColumnState> StateChanged;
private readonly List<Box> drawableRows = new List<Box>();
private float filled;
@@ -186,6 +189,7 @@ namespace osu.Game.Screens.Play
}
private ColumnState state;
public ColumnState State
{
get { return state; }
@@ -196,6 +200,8 @@ namespace osu.Game.Screens.Play
if (IsLoaded)
fillActive();
StateChanged?.Invoke(State);
}
}
+43 -6
View File
@@ -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)
{
if (beatmap == null)
if (beatmap == null || beatmap.Hidden)
{
SelectNext();
return;
@@ -140,6 +167,12 @@ namespace osu.Game.Screens.Select
public Action StartRequested;
public Action<BeatmapSetInfo> DeleteRequested;
public Action<BeatmapSetInfo> RestoreRequested;
public Action<BeatmapInfo> HideDifficultyRequested;
public void SelectNext(int direction = 1, bool skipDifficulties = true)
{
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
@@ -191,7 +224,8 @@ namespace osu.Game.Screens.Select
if (!visibleGroups.Any())
return;
randomSelectedBeatmaps.Push(new KeyValuePair<BeatmapGroup, BeatmapPanel>(selectedGroup, selectedGroup.SelectedPanel));
if (selectedGroup != null)
randomSelectedBeatmaps.Push(new KeyValuePair<BeatmapGroup, BeatmapPanel>(selectedGroup, selectedGroup.SelectedPanel));
BeatmapGroup group;
@@ -305,6 +339,9 @@ namespace osu.Game.Screens.Select
{
SelectionChanged = (g, p) => selectGroup(g, p),
StartRequested = b => StartRequested?.Invoke(),
DeleteRequested = b => DeleteRequested?.Invoke(b),
RestoreHiddenRequested = s => RestoreRequested?.Invoke(s),
HideDifficultyRequested = b => HideDifficultyRequested?.Invoke(b),
State = BeatmapGroupState.Collapsed
};
}
@@ -1,7 +1,6 @@
// 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.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
@@ -19,23 +18,18 @@ namespace osu.Game.Screens.Select
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;
HeaderText = @"Confirm deletion of";
BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}";
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton
{
Text = @"Yes. Totally. Delete it.",
Action = () =>
{
beatmap.Dispose();
manager.Delete(beatmap.BeatmapSetInfo);
},
Action = () => manager.Delete(beatmap),
},
new PopupDialogCancelButton
{
@@ -1,15 +1,15 @@
// 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 System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using System;
using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
@@ -1,19 +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 System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
using osu.Framework;
using osu.Game.Rulesets.Scoring;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Users;
namespace osu.Game.Screens.Select.Leaderboards
{
@@ -21,6 +24,8 @@ namespace osu.Game.Screens.Select.Leaderboards
{
public static readonly float HEIGHT = 60;
public event Action<Visibility> StateChanged;
public readonly int RankPosition;
public readonly Score Score;
@@ -38,14 +43,17 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly ScoreComponentLabel maxCombo;
private readonly ScoreComponentLabel accuracy;
private readonly Container flagBadgeContainer;
private readonly FillFlowContainer<ScoreModIcon> modsContainer;
private readonly FillFlowContainer<ModIcon> modsContainer;
private Visibility state;
public Visibility State
{
get { return state; }
set
{
if (state == value)
return;
state = value;
switch (state)
@@ -88,6 +96,8 @@ namespace osu.Game.Screens.Select.Leaderboards
break;
}
StateChanged?.Invoke(State);
}
}
@@ -239,7 +249,7 @@ namespace osu.Game.Screens.Select.Leaderboards
},
},
},
modsContainer = new FillFlowContainer<ScoreModIcon>
modsContainer = new FillFlowContainer<ModIcon>
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
@@ -252,13 +262,13 @@ namespace osu.Game.Screens.Select.Leaderboards
},
};
if (Score.Mods != null)
foreach (Mod mod in Score.Mods)
{
foreach (Mod mod in Score.Mods)
modsContainer.Add(new ModIcon(mod)
{
// TODO: Get actual mod colours
modsContainer.Add(new ScoreModIcon(mod.Icon, OsuColour.FromHex(@"ffcc22")));
}
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.375f)
});
}
}
@@ -267,13 +277,13 @@ namespace osu.Game.Screens.Select.Leaderboards
public override void Hide() => State = Visibility.Hidden;
public override void Show() => State = Visibility.Visible;
protected override bool OnHover(Framework.Input.InputState state)
protected override bool OnHover(InputState state)
{
background.FadeTo(0.5f, 300, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(Framework.Input.InputState state)
protected override void OnHoverLost(InputState state)
{
background.FadeTo(background_alpha, 200, Easing.OutQuint);
base.OnHoverLost(state);
@@ -303,8 +313,8 @@ namespace osu.Game.Screens.Select.Leaderboards
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = font,
TextSize = textSize,
FixedWidth = true,
TextSize = textSize,
Text = text,
Colour = glowColour,
Shadow = false,
@@ -326,36 +336,6 @@ namespace osu.Game.Screens.Select.Leaderboards
}
}
private class ScoreModIcon : Container
{
public ScoreModIcon(FontAwesome icon, Color4 colour)
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = FontAwesome.fa_osu_mod_bg,
Colour = colour,
Shadow = true,
Size = new Vector2(30),
},
new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = icon,
Colour = OsuColour.Gray(84),
Size = new Vector2(18),
Position = new Vector2(0f, 2f),
},
};
}
}
private class ScoreComponentLabel : Container
{
public ScoreComponentLabel(FontAwesome icon, string value)
+2 -4
View File
@@ -54,13 +54,11 @@ namespace osu.Game.Screens.Select
ValidForResume = false;
Push(new Editor());
}, 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);
+27 -12
View File
@@ -106,6 +106,9 @@ namespace osu.Game.Screens.Select
Origin = Anchor.CentreRight,
SelectionChanged = carouselSelectionChanged,
BeatmapsChanged = carouselBeatmapsLoaded,
DeleteRequested = b => promptDelete(b),
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
HideDifficultyRequested = b => manager.Hide(b),
StartRequested = () => carouselRaisedStart(),
});
Add(FilterControl = new FilterControl
@@ -163,7 +166,7 @@ namespace osu.Game.Screens.Select
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
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)
@@ -174,6 +177,8 @@ namespace osu.Game.Screens.Select
manager.BeatmapSetAdded += onBeatmapSetAdded;
manager.BeatmapSetRemoved += onBeatmapSetRemoved;
manager.BeatmapHidden += onBeatmapHidden;
manager.BeatmapRestored += onBeatmapRestored;
dialogOverlay = dialog;
@@ -190,6 +195,9 @@ namespace osu.Game.Screens.Select
carousel.AllowSelection = !Beatmap.Disabled;
}
private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);
private void carouselBeatmapsLoaded()
{
if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false)
@@ -236,7 +244,7 @@ namespace osu.Game.Screens.Select
ensurePlayingSelected(preview);
}
changeBackground(Beatmap.Value);
UpdateBeatmap(Beatmap.Value);
};
selectionChangedDebounce?.Cancel();
@@ -304,7 +312,7 @@ namespace osu.Game.Screens.Select
{
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
{
changeBackground(Beatmap);
UpdateBeatmap(Beatmap);
ensurePlayingSelected();
}
@@ -345,12 +353,19 @@ namespace osu.Game.Screens.Select
{
manager.BeatmapSetAdded -= onBeatmapSetAdded;
manager.BeatmapSetRemoved -= onBeatmapSetRemoved;
manager.BeatmapHidden -= onBeatmapHidden;
manager.BeatmapRestored -= onBeatmapRestored;
}
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;
if (backgroundModeBeatmap != null)
@@ -378,10 +393,7 @@ namespace osu.Game.Screens.Select
}
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet)
{
carousel.AddBeatmap(beatmapSet);
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet) => carousel.AddBeatmap(beatmapSet);
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{
@@ -390,10 +402,12 @@ namespace osu.Game.Screens.Select
Beatmap.SetDefault();
}
private void promptDelete()
private void promptDelete(BeatmapSetInfo beatmap)
{
if (Beatmap != null && !Beatmap.IsDefault)
dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap));
if (beatmap == null)
return;
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@@ -409,7 +423,8 @@ namespace osu.Game.Screens.Select
case Key.Delete:
if (state.Keyboard.ShiftPressed)
{
promptDelete();
if (!Beatmap.IsDefault)
promptDelete(Beatmap.Value.BeatmapSetInfo);
return true;
}
break;

Some files were not shown because too many files have changed in this diff Show More