1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 23:03:26 +08:00

Compare commits

...

285 Commits

145 changed files with 3283 additions and 1367 deletions
+1 -4
View File
@@ -1,20 +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 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())
using (var host = new HeadlessGameHost(realtime: false))
host.Run(new OsuTestCaseTestRunner(this));
}
+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(), },
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using OpenTK;
@@ -40,8 +41,8 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
new DrawableNote(new Note { StartTime = 5000 }, ManiaAction.Key1) { AccentColour = Color4.Red },
new DrawableNote(new Note { StartTime = 6000 }, ManiaAction.Key1) { AccentColour = Color4.Red }
}
}
}
@@ -66,7 +67,7 @@ namespace osu.Desktop.Tests.Visual
{
StartTime = 5000,
Duration = 1000
}) { AccentColour = Color4.Red }
}, ManiaAction.Key1) { AccentColour = Color4.Red }
}
}
}
@@ -1,111 +1,34 @@
// 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.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
using OpenTK;
using OpenTK.Input;
using osu.Game.Rulesets;
namespace osu.Desktop.Tests.Visual
{
internal class TestCaseManiaPlayfield : OsuTestCase
{
private const double start_time = 500;
private const double duration = 500;
public override string Description => @"Mania playfield";
protected override double TimePerAction => 200;
private RulesetInfo maniaRuleset;
public TestCaseManiaPlayfield()
{
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
};
const double start_time = 500;
const double duration = 500;
Func<double, bool, SpeedAdjustmentContainer> createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
{
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
Action<bool> createPlayfieldWithNotes = gravity =>
{
Clear();
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
ManiaPlayfield playField;
Add(playField = new ManiaPlayfield(4)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1, -1),
Clock = new FramedClock(rateAdjustClock)
});
if (!gravity)
playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
for (double t = start_time; t <= start_time + duration; t += 100)
{
if (gravity)
playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
playField.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, new Bindable<Key>(Key.D)));
if (gravity)
playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
playField.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 3
}, new Bindable<Key>(Key.K)));
}
if (gravity)
playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playField.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 1
}, new Bindable<Key>(Key.F)));
if (gravity)
playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playField.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 2
}, new Bindable<Key>(Key.J)));
};
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
@@ -114,29 +37,105 @@ namespace osu.Desktop.Tests.Visual
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
}
private void triggerKeyDown(Column column)
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs
{
Key = column.Key,
Repeat = false
});
maniaRuleset = rulesets.GetRuleset(3);
}
private void triggerKeyUp(Column column)
private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
{
column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
private void createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
{
Clear();
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(cols)
{
Key = column.Key
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = specialPos
});
playfield.Inverted.Value = inverted;
}
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
{
Clear();
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(4)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Clock = new FramedClock(rateAdjustClock)
});
playfield.Inverted.Value = inverted;
if (!gravity)
playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
for (double t = start_time; t <= start_time + duration; t += 100)
{
if (gravity)
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, ManiaAction.Key1));
if (gravity)
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 3
}, ManiaAction.Key4));
}
if (gravity)
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 1
}, ManiaAction.Key2));
if (gravity)
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 2
}, ManiaAction.Key3));
}
}
}
@@ -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));
@@ -64,8 +64,8 @@ namespace osu.Desktop.Tests.Visual
AddStep("Reverse direction", () =>
{
horizontalRulesetContainer.Playfield.Reversed.Toggle();
verticalRulesetContainer.Playfield.Reversed.Toggle();
horizontalRulesetContainer.Playfield.Reverse();
verticalRulesetContainer.Playfield.Reverse();
});
}
@@ -210,6 +210,8 @@ namespace osu.Desktop.Tests.Visual
content = new Container { RelativeSizeAxes = Axes.Both }
};
}
public void Reverse() => Reversed.Toggle();
}
@@ -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" />
+7 -8
View File
@@ -20,16 +20,11 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly VersionManager versionManager;
private VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
{
versionManager = new VersionManager
{
Depth = int.MinValue,
State = Visibility.Hidden
};
}
public override Storage GetStorageForStableInstall()
@@ -88,11 +83,15 @@ namespace osu.Desktop
{
base.LoadComplete();
LoadComponentAsync(versionManager, Add);
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue });
ScreenChanged += s =>
{
if (!versionManager.IsPresent && s is Intro)
if (s is Intro && s.ChildScreen == null)
{
Add(versionManager);
versionManager.State = Visibility.Visible;
}
};
}
+44 -1
View File
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@@ -19,6 +20,7 @@ using OpenTK.Graphics;
using System.Net.Http;
using osu.Framework.Logging;
using osu.Game;
using osu.Game.Configuration;
namespace osu.Desktop.Overlays
{
@@ -26,17 +28,22 @@ namespace osu.Desktop.Overlays
{
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
private OsuConfigManager config;
private OsuGameBase game;
public override bool HandleInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game)
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
Alpha = 0;
Children = new Drawable[]
@@ -91,6 +98,42 @@ namespace osu.Desktop.Overlays
checkForUpdateAsync();
}
protected override void LoadComplete()
{
base.LoadComplete();
var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion)
{
config.Set(OsuSetting.Version, version);
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
}
}
private class UpdateCompleteNotification : SimpleNotification
{
public UpdateCompleteNotification(string version)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
return true;
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = colours.BlueDark;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
-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 };
+23 -3
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.ComponentModel;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
@@ -8,14 +9,33 @@ namespace osu.Game.Rulesets.Mania
{
public class ManiaInputManager : RulesetInputManager<ManiaAction>
{
public ManiaInputManager(RulesetInfo ruleset)
: base(ruleset, 0, SimultaneousBindingMode.Unique)
public ManiaInputManager(RulesetInfo ruleset, int variant)
: base(ruleset, variant, SimultaneousBindingMode.Unique)
{
}
}
public enum ManiaAction
{
// placeholder
[Description("Special")]
Special,
[Description("Key 1")]
Key1 = 10,
[Description("Key 2")]
Key2,
[Description("Key 3")]
Key3,
[Description("Key 4")]
Key4,
[Description("Key 5")]
Key5,
[Description("Key 6")]
Key6,
[Description("Key 7")]
Key7,
[Description("Key 8")]
Key8,
[Description("Key 9")]
Key9
}
}
+39 -2
View File
@@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Scoring;
@@ -104,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 };
@@ -120,5 +119,43 @@ namespace osu.Game.Rulesets.Mania
: base(rulesetInfo)
{
}
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
{
var leftKeys = new[]
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
};
var rightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
};
ManiaAction currentKey = ManiaAction.Key1;
var bindings = new List<KeyBinding>();
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
for (int i = 0; i < variant / 2; i++)
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
if (variant % 2 == 1)
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
return bindings;
}
public override string GetVariantName(int variant) => $"{variant}K";
}
}
+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;
@@ -5,20 +5,18 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using OpenTK.Input;
using osu.Framework.Input;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// Visualises a <see cref="HoldNote"/> hit object.
/// </summary>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
{
private readonly DrawableNote head;
private readonly DrawableNote tail;
@@ -36,8 +34,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
private bool hasBroken;
public DrawableHoldNote(HoldNote hitObject, Bindable<Key> key = null)
: base(hitObject, key)
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
@@ -58,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
},
head = new DrawableHeadNote(this, key)
head = new DrawableHeadNote(this, action)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
tail = new DrawableTailNote(this, key)
tail = new DrawableTailNote(this, action)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
@@ -106,16 +104,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public bool OnPressed(ManiaAction action)
{
// Make sure the keypress happened within the body of the hold note
// Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
return false;
if (args.Key != Key)
return false;
if (args.Repeat)
if (action != Action)
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
@@ -126,13 +121,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
public bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
return false;
if (args.Key != Key)
if (action != Action)
return false;
holdStartTime = null;
@@ -151,8 +146,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
public DrawableHeadNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
: base(holdNote.HitObject.Head, key)
public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action)
: base(holdNote.HitObject.Head, action)
{
this.holdNote = holdNote;
@@ -160,9 +155,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Y = 0;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public override bool OnPressed(ManiaAction action)
{
if (!base.OnKeyDown(state, args))
if (!base.OnPressed(action))
return false;
// We only want to trigger a holding state from the head if the head has received a judgement
@@ -188,8 +183,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
public DrawableTailNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
: base(holdNote.HitObject.Tail, key)
public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action)
: base(holdNote.HitObject.Tail, action)
{
this.holdNote = holdNote;
@@ -210,7 +205,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tailJudgement.HasBroken = holdNote.hasBroken;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
@@ -219,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (Judgement.Result != HitResult.None)
return false;
if (args.Key != Key)
if (action != Action)
return false;
UpdateJudgement(true);
@@ -227,12 +224,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// Handled by the hold note, which will set holding = false
return false;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
// Tail doesn't handle key down
return false;
}
}
}
}
@@ -2,8 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
@@ -15,17 +13,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// The key that will trigger input for this hit object.
/// </summary>
protected Bindable<Key> Key { get; private set; } = new Bindable<Key>();
protected ManiaAction Action { get; }
public new TObject HitObject;
protected DrawableManiaHitObject(TObject hitObject, Bindable<Key> key = null)
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject)
{
HitObject = hitObject;
if (key != null)
Key.BindTo(key);
if (action != null)
Action = action.Value;
}
public override Color4 AccentColour
@@ -3,10 +3,8 @@
using System;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
@@ -16,12 +14,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// Visualises a <see cref="Note"/> hit object.
/// </summary>
public class DrawableNote : DrawableManiaHitObject<Note>
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
{
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject, Bindable<Key> key = null)
: base(hitObject, key)
public DrawableNote(Note hitObject, ManiaAction action)
: base(hitObject, action)
{
RelativeSizeAxes = Axes.X;
Height = 100;
@@ -81,18 +79,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public virtual bool OnPressed(ManiaAction action)
{
if (Judgement.Result != HitResult.None)
return false;
if (args.Key != Key)
return false;
if (args.Repeat)
if (action != Action)
return false;
return UpdateJudgement(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
}
}
+14 -22
View File
@@ -3,17 +3,15 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using System;
using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Judgements;
@@ -32,10 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
private const float column_width = 45;
private const float special_column_width = 70;
/// <summary>
/// The key that will trigger input actions for this column and hit objects contained inside it.
/// </summary>
public Bindable<Key> Key = new Bindable<Key>();
public ManiaAction Action;
private readonly Box background;
private readonly Container hitTargetBar;
@@ -101,8 +96,8 @@ namespace osu.Game.Rulesets.Mania.UI
// For column lighting, we need to capture input events before the notes
new InputTarget
{
KeyDown = onKeyDown,
KeyUp = onKeyUp
Pressed = onPressed,
Released = onReleased
}
}
},
@@ -199,23 +194,20 @@ namespace osu.Game.Rulesets.Mania.UI
HitObjects.Add(hitObject);
}
private bool onKeyDown(InputState state, KeyDownEventArgs args)
private bool onPressed(ManiaAction action)
{
if (args.Repeat)
return false;
if (args.Key == Key)
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);
}
return false;
}
private bool onKeyUp(InputState state, KeyUpEventArgs args)
private bool onReleased(ManiaAction action)
{
if (args.Key == Key)
if (action == Action)
{
background.FadeTo(0.2f, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
@@ -227,10 +219,10 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// This is a simple container which delegates various input events that have to be captured before the notes.
/// </summary>
private class InputTarget : Container
private class InputTarget : Container, IKeyBindingHandler<ManiaAction>
{
public Func<InputState, KeyDownEventArgs, bool> KeyDown;
public Func<InputState, KeyUpEventArgs, bool> KeyUp;
public Func<ManiaAction, bool> Pressed;
public Func<ManiaAction, bool> Released;
public InputTarget()
{
@@ -239,8 +231,8 @@ namespace osu.Game.Rulesets.Mania.UI
Alpha = 0;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false;
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false;
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
}
}
}
+21 -28
View File
@@ -11,9 +11,9 @@ 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;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
@@ -24,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.
@@ -45,6 +39,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
/// <summary>
/// Whether this playfield should be inverted. This flips everything inside the playfield.
/// </summary>
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
private readonly FlowContainer<Column> columns;
public IEnumerable<Column> Columns => columns.Children;
@@ -64,6 +63,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
Inverted.Value = true;
InternalChildren = new Drawable[]
{
new Container
@@ -122,15 +123,26 @@ namespace osu.Game.Rulesets.Mania.UI
}
};
var currentAction = ManiaAction.Key1;
for (int i = 0; i < columnCount; i++)
{
var c = new Column();
c.Reversed.BindTo(Reversed);
c.VisibleTimeRange.BindTo(VisibleTimeRange);
c.IsSpecial = isSpecialColumn(i);
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
columns.Add(c);
AddNested(c);
}
Inverted.ValueChanged += invertedChanged;
Inverted.TriggerChange();
}
private void invertedChanged(bool newValue)
{
Scale = new Vector2(1, newValue ? -1 : 1);
}
[BackgroundDependencyLoader]
@@ -145,15 +157,11 @@ namespace osu.Game.Rulesets.Mania.UI
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
for (int i = 0; i < columnCount; i++)
foreach (var column in Columns)
{
Column column = Columns.ElementAt(i);
column.IsSpecial = isSpecialColumn(i);
if (!column.IsSpecial)
continue;
column.Key.Value = Key.Space;
column.AccentColour = specialColumnColour;
}
@@ -167,21 +175,6 @@ namespace osu.Game.Rulesets.Mania.UI
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
// We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
// Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
for (int i = 0; i < nonSpecialColumns.Count; i++)
{
Column column = nonSpecialColumns[i];
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
if (keyOffset >= 0 && keyOffset < default_keys.Length)
column.Key.Value = default_keys[keyOffset];
else
// There is no default key defined for this column. Let's set this to Unknown for now
// however note that this will be gone after bindings are in place
column.Key.Value = Key.Unknown;
}
}
/// <summary>
@@ -5,9 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -85,7 +83,7 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, availableColumns);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
{
@@ -109,15 +107,15 @@ namespace osu.Game.Rulesets.Mania.UI
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{
Bindable<Key> key = Playfield.Columns.ElementAt(h.Column).Key;
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote, key);
return new DrawableHoldNote(holdNote, action);
var note = h as Note;
if (note != null)
return new DrawableNote(note, key);
return new DrawableNote(note, action);
return null;
}
+22 -1
View File
@@ -7,8 +7,13 @@ using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
@@ -28,10 +33,23 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1.06;
}
public class OsuModHardRock : ModHardRock
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject>
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
{
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
{
var newControlPoints = new List<Vector2>();
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
s.ControlPoints = newControlPoints;
s.Curve?.Calculate(); // Recalculate the slider curve
});
}
}
public class OsuModSuddenDeath : ModSuddenDeath
@@ -78,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;
@@ -88,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;
@@ -108,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);
@@ -0,0 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Rulesets.Replays;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Replays
{
public class OsuReplayInputHandler : FramedReplayInputHandler
{
public OsuReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<InputState> GetPendingStates()
{
List<OsuAction> actions = new List<OsuAction>();
if (CurrentFrame?.MouseLeft ?? false) actions.Add(OsuAction.LeftButton);
if (CurrentFrame?.MouseRight ?? false) actions.Add(OsuAction.RightButton);
return new List<InputState>
{
new ReplayState<OsuAction>
{
Mouse = new ReplayMouseState(ToScreenSpace(Position ?? Vector2.Zero)),
PressedActions = actions
}
};
}
}
}
@@ -1,8 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// 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
}
}
}
@@ -10,9 +10,11 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Osu.UI
{
@@ -49,6 +51,8 @@ namespace osu.Game.Rulesets.Osu.UI
return null;
}
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuReplayInputHandler(replay);
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
}
}
@@ -78,6 +78,7 @@
<Compile Include="OsuDifficulty\Skills\Speed.cs" />
<Compile Include="OsuDifficulty\Utils\History.cs" />
<Compile Include="OsuInputManager.cs" />
<Compile Include="Replays\OsuReplayInputHandler.cs" />
<Compile Include="UI\Cursor\CursorTrail.cs" />
<Compile Include="UI\Cursor\GameplayCursor.cs" />
<Compile Include="UI\OsuSettings.cs" />
@@ -4,7 +4,6 @@
using osu.Game.Rulesets.Replays;
using System.Collections.Generic;
using osu.Framework.Input;
using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Replays
{
@@ -17,21 +16,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
public override List<InputState> GetPendingStates()
{
var keys = new List<Key>();
var actions = new List<TaikoAction>();
if (CurrentFrame?.MouseRight1 == true)
keys.Add(Key.F);
actions.Add(TaikoAction.LeftCentre);
if (CurrentFrame?.MouseRight2 == true)
keys.Add(Key.J);
actions.Add(TaikoAction.RightCentre);
if (CurrentFrame?.MouseLeft1 == true)
keys.Add(Key.D);
actions.Add(TaikoAction.LeftRim);
if (CurrentFrame?.MouseLeft2 == true)
keys.Add(Key.K);
actions.Add(TaikoAction.RightRim);
return new List<InputState>
{
new InputState { Keyboard = new ReplayKeyboardState(keys) }
};
return new List<InputState> { new ReplayState<TaikoAction> { PressedActions = actions } };
}
}
}
@@ -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 };
@@ -13,7 +13,6 @@ using osu.Framework.Platform;
using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
namespace osu.Game.Tests.Beatmaps.IO
{
@@ -98,16 +97,14 @@ namespace osu.Game.Tests.Beatmaps.IO
private OsuGameBase loadOsu(GameHost host)
{
host.Storage.DeleteDatabase(@"client");
var osu = new OsuGameBase();
Task.Run(() => host.Run(osu));
while (!osu.IsLoaded)
Thread.Sleep(1);
//reset beatmap database (sqlite and storage backing)
osu.Dependencies.Get<RulesetStore>().Reset();
osu.Dependencies.Get<BeatmapManager>().Reset();
return osu;
}
+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;
+4 -1
View File
@@ -70,6 +70,8 @@ namespace osu.Game.Configuration
// Update
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
Set(OsuSetting.Version, string.Empty);
}
public OsuConfigManager(Storage storage) : base(storage)
@@ -106,6 +108,7 @@ namespace osu.Game.Configuration
SnakingInSliders,
SnakingOutSliders,
ShowFpsDisplay,
ChatDisplayHeight
ChatDisplayHeight,
Version
}
}
@@ -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);
}
}
+4 -2
View File
@@ -24,6 +24,8 @@ namespace osu.Game.Graphics.UserInterface
private SampleChannel sampleClick;
private SampleChannel sampleHover;
protected Triangles Triangles;
public OsuButton()
{
Height = 40;
@@ -52,7 +54,7 @@ namespace osu.Game.Graphics.UserInterface
AddRange(new Drawable[]
{
new Triangles
Triangles = new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourDark = colours.BlueDarker,
@@ -120,4 +122,4 @@ namespace osu.Game.Graphics.UserInterface
}
}
}
}
}
@@ -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,67 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public class ProgressBar : SliderBar<double>
{
public Action<double> OnSeek;
private readonly Box fill;
private readonly Box background;
public Color4 FillColour
{
set { fill.Colour = value; }
}
public Color4 BackgroundColour
{
set
{
background.Alpha = 1;
background.Colour = value;
}
}
public double EndTime
{
set { CurrentNumber.MaxValue = value; }
}
public double CurrentTime
{
set { CurrentNumber.Value = value; }
}
public ProgressBar()
{
CurrentNumber.MinValue = 0;
CurrentNumber.MaxValue = 1;
RelativeSizeAxes = Axes.X;
Children = new Drawable[]
{
background = new Box
{
Alpha = 0,
RelativeSizeAxes = Axes.Both
},
fill = new Box { RelativeSizeAxes = Axes.Y }
};
}
protected override void UpdateValue(float value)
{
fill.Width = value * UsableWidth;
}
protected override void OnUserChange() => OnSeek?.Invoke(Current);
}
}
@@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.Platform;
using OpenTK;
@@ -29,5 +31,18 @@ namespace osu.Game.Input.Handlers
public override bool IsActive => true;
public override int Priority => 0;
public class ReplayState<T> : InputState
where T : struct
{
public List<T> PressedActions;
public override InputState Clone()
{
var clone = (ReplayState<T>)base.Clone();
clone.PressedActions = new List<T>(PressedActions);
return clone;
}
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ namespace osu.Game.Input
{
var ruleset = info.CreateInstance();
foreach (var variant in ruleset.AvailableVariants)
insertDefaults(ruleset.GetDefaultKeyBindings(), info.ID, variant);
insertDefaults(ruleset.GetDefaultKeyBindings(variant), info.ID, variant);
}
}
+35 -8
View File
@@ -11,11 +11,11 @@ namespace osu.Game.Online.API
/// An API request with a well-defined response type.
/// </summary>
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
public class APIRequest<T> : APIRequest
public abstract class APIRequest<T> : APIRequest
{
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
public APIRequest()
protected APIRequest()
{
base.Success += onSuccess;
}
@@ -28,10 +28,36 @@ namespace osu.Game.Online.API
public new event APISuccessHandler<T> Success;
}
public abstract class APIDownloadRequest : APIRequest
{
protected override WebRequest CreateWebRequest()
{
var request = new WebRequest(Uri);
request.DownloadProgress += request_Progress;
return request;
}
private void request_Progress(WebRequest request, long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
protected APIDownloadRequest()
{
base.Success += onSuccess;
}
private void onSuccess()
{
Success?.Invoke(WebRequest.ResponseData);
}
public event APIProgressHandler Progress;
public new event APISuccessHandler<byte[]> Success;
}
/// <summary>
/// AN API request with no specified response type.
/// </summary>
public class APIRequest
public abstract class APIRequest
{
/// <summary>
/// The maximum amount of time before this request will fail.
@@ -42,7 +68,7 @@ namespace osu.Game.Online.API
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
protected virtual string Uri => $@"{api.Endpoint}/api/v2/{Target}";
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
@@ -52,7 +78,7 @@ namespace osu.Game.Online.API
public double StartTime => startTime ?? -1;
private APIAccess api;
protected APIAccess API;
protected WebRequest WebRequest;
public event APISuccessHandler Success;
@@ -64,7 +90,7 @@ namespace osu.Game.Online.API
public void Perform(APIAccess api)
{
this.api = api;
API = api;
if (checkAndProcessFailure())
return;
@@ -109,9 +135,9 @@ namespace osu.Game.Online.API
/// <returns>Whether we are in a failed or cancelled state.</returns>
private bool checkAndProcessFailure()
{
if (api == null || pendingFailure == null) return cancelled;
if (API == null || pendingFailure == null) return cancelled;
api.Scheduler.Add(pendingFailure);
API.Scheduler.Add(pendingFailure);
pendingFailure = null;
return true;
}
@@ -119,5 +145,6 @@ namespace osu.Game.Online.API
public delegate void APIFailureHandler(Exception e);
public delegate void APISuccessHandler();
public delegate void APIProgressHandler(long current, long total);
public delegate void APISuccessHandler<in T>(T content);
}
@@ -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";
}
}
@@ -46,6 +46,9 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"favourite_count")]
private int favouriteCount { get; set; }
[JsonProperty(@"id")]
private int onlineId { get; set; }
[JsonProperty(@"beatmaps")]
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
@@ -53,6 +56,7 @@ namespace osu.Game.Online.API.Requests
{
return new BeatmapSetInfo
{
OnlineBeatmapSetID = onlineId,
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
@@ -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;
+33 -13
View File
@@ -219,20 +219,37 @@ namespace osu.Game
dependencies.Cache(settings);
dependencies.Cache(social);
dependencies.Cache(direct);
dependencies.Cache(chat);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(notificationOverlay);
dependencies.Cache(dialogOverlay);
// ensure both overlays aren't presented at the same time
chat.StateChanged += (container, state) => social.State = state == Visibility.Visible ? Visibility.Hidden : social.State;
social.StateChanged += (container, state) => chat.State = state == Visibility.Visible ? Visibility.Hidden : chat.State;
// ensure only one of these overlays are open at once.
var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
foreach (var overlay in singleDisplayOverlays)
{
overlay.StateChanged += state =>
{
if (state == Visibility.Hidden) return;
foreach (var c in singleDisplayOverlays)
{
if (c == overlay) continue;
c.State = Visibility.Hidden;
}
};
}
LoadComponentAsync(Toolbar = new Toolbar
{
Depth = -4,
OnHome = delegate { intro?.ChildScreen?.MakeCurrent(); },
OnHome = delegate
{
hideAllOverlays();
intro?.ChildScreen?.MakeCurrent();
},
}, overlayContent.Add);
settings.StateChanged += delegate
@@ -297,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;
@@ -310,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);
}
+9 -13
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,22 +150,12 @@ 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 baetmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
if (lastBeatmap?.Track != b.Track)
{
// this disposal is done to stop the audio track.
// it may not be exactly what we want for cases beatmaps are reused, as it will
// trigger a fresh load of contained resources.
lastBeatmap?.Dispose();
lastBeatmap?.Track?.Dispose();
Audio.Track.AddItem(b.Track);
}
+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;
}
+11 -20
View File
@@ -12,6 +12,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Framework.Input;
namespace osu.Game.Overlays.Direct
{
@@ -25,23 +26,11 @@ namespace osu.Game.Overlays.Direct
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
CornerRadius = 4;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.25f),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
bottomPanel.LayoutDuration = 200;
bottomPanel.LayoutEasing = Easing.Out;
bottomPanel.Origin = Anchor.BottomLeft;
@@ -50,14 +39,10 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
Children = new[]
Content.CornerRadius = 4;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
CreateBackground(),
new Box
{
RelativeSizeAxes = Axes.Both,
@@ -185,7 +170,13 @@ namespace osu.Game.Overlays.Direct
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
};
});
}
protected override bool OnClick(InputState state)
{
StartDownload();
return true;
}
}
}
+5 -24
View File
@@ -28,35 +28,15 @@ namespace osu.Game.Overlays.Direct
{
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.25f),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
{
Children = new[]
Content.CornerRadius = 5;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
CreateBackground(),
new Box
{
RelativeSizeAxes = Axes.Both,
@@ -144,10 +124,11 @@ namespace osu.Game.Overlays.Direct
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Size = new Vector2(height - vertical_padding * 2),
Action = StartDownload
},
},
},
};
});
}
private class DownloadButton : OsuClickableContainer
+159 -2
View File
@@ -2,26 +2,179 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Direct
{
public abstract class DirectPanel : Container
{
protected readonly BeatmapSetInfo SetInfo;
public readonly BeatmapSetInfo SetInfo;
protected Box BlackBackground;
private const double hover_transition_time = 400;
private Container content;
private APIAccess api;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
protected override Container<Drawable> Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
{
SetInfo = setInfo;
}
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 2f,
Colour = Color4.Black.Opacity(0.25f),
};
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 5f),
Radius = 10f,
Colour = Color4.Black.Opacity(0.3f),
};
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
{
this.api = api;
this.beatmaps = beatmaps;
this.notifications = notifications;
AddInternal(content = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = edgeEffectNormal,
Children = new[]
{
// temporary blackness until the actual background loads.
BlackBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
CreateBackground(),
progressBar = new ProgressBar
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 0,
Alpha = 0,
BackgroundColour = Color4.Black.Opacity(0.7f),
FillColour = colours.Blue,
Depth = -1,
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
}
protected override bool OnHover(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
protected void StartDownload()
{
if (!api.LocalUser.Value.IsSupporter)
{
notifications.Post(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be a supporter to download for now 'yo"
});
return;
}
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;
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
Logger.Error(e, "Failed to get beatmap download information");
};
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FadeOut(500);
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
}
protected List<DifficultyIcon> GetDifficultyIcons()
{
var icons = new List<DifficultyIcon>();
@@ -38,7 +191,11 @@ namespace osu.Game.Overlays.Direct
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(400, Easing.Out),
OnLoadComplete = d =>
{
d.FadeInFromZero(400, Easing.Out);
BlackBackground.Delay(400).FadeOut();
},
})
{
RelativeSizeAxes = Axes.Both,
+63 -41
View File
@@ -27,10 +27,11 @@ namespace osu.Game.Overlays
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private readonly FillFlowContainer<DirectPanel> panels;
private FillFlowContainer<DirectPanel> panels;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
@@ -46,20 +47,26 @@ namespace osu.Game.Overlays
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value;
if (BeatmapSets == null)
{
foreach (var p in panels.Children)
{
p.FadeOut(200);
p.Expire();
}
if (beatmapSets == null) return;
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(' '));
}
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
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);
}
}
@@ -108,13 +115,6 @@ namespace osu.Game.Overlays
},
}
},
panels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(panel_padding),
Margin = new MarginPadding { Top = 10 },
},
};
Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; };
@@ -161,11 +161,22 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets)
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.BeatmapSetAdded += setAdded;
}
private void setAdded(BeatmapSetInfo set)
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
@@ -185,18 +196,38 @@ namespace osu.Game.Overlays
private void recreatePanels(PanelDisplayStyle displayStyle)
{
if (panels != null)
{
panels.FadeOut(200);
panels.Expire();
panels = null;
}
if (BeatmapSets == null) return;
panels.ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
{
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
return new DirectGridPanel(b) { Width = 400 };
default:
return new DirectListPanel(b);
}
});
var newPanels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(panel_padding),
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
{
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
return new DirectGridPanel(b) { Width = 400 };
default:
return new DirectListPanel(b);
}
})
};
LoadComponentAsync(newPanels, p =>
{
if (panels != null) ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
});
}
private GetBeatmapSetsRequest getSetsRequest;
@@ -227,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);
@@ -3,21 +3,29 @@
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
namespace osu.Game.Overlays.KeyBinding
{
public class GlobalKeyBindingsSection : KeyBindingsSection
public class GlobalKeyBindingsSection : SettingsSection
{
private readonly string name;
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override string Header => name;
public GlobalKeyBindingsSection(KeyBindingInputManager manager, string name)
public GlobalKeyBindingsSection(KeyBindingInputManager manager)
{
this.name = name;
Add(new DefaultBindingsSubsection(manager));
}
Defaults = manager.DefaultKeyBindings;
private class DefaultBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => string.Empty;
public DefaultBindingsSubsection(KeyBindingInputManager manager)
: base(null)
{
Defaults = manager.DefaultKeyBindings;
}
}
}
}
+17 -5
View File
@@ -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 System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@@ -22,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding
{
internal class KeyBindingRow : Container, IFilterable
{
private readonly Enum action;
private readonly object action;
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;
private const float transition_time = 150;
@@ -50,7 +49,7 @@ namespace osu.Game.Overlays.KeyBinding
public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
public KeyBindingRow(Enum action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
{
this.action = action;
this.bindings = bindings;
@@ -110,16 +109,27 @@ namespace osu.Game.Overlays.KeyBinding
buttons.Add(new KeyButton(b));
}
public void RestoreDefaults()
{
int i = 0;
foreach (var d in Defaults)
{
var button = buttons[i++];
button.UpdateKeyCombination(d);
store.Update(button.KeyBinding);
}
}
protected override bool OnHover(InputState state)
{
this.FadeEdgeEffectTo<Container>(1, transition_time, Easing.OutQuint);
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
this.FadeEdgeEffectTo<Container>(0, transition_time, Easing.OutQuint);
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
@@ -130,6 +140,8 @@ namespace osu.Game.Overlays.KeyBinding
public bool AllowMainMouseButtons;
public IEnumerable<KeyCombination> Defaults;
private bool isModifier(Key k) => k < Key.F1;
protected override bool OnClick(InputState state) => true;
@@ -1,49 +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 System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Input;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using OpenTK;
namespace osu.Game.Overlays.KeyBinding
{
public abstract class KeyBindingsSection : SettingsSection
{
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
protected RulesetInfo Ruleset;
protected KeyBindingsSection()
{
FlowContent.Spacing = new Vector2(0, 1);
}
[BackgroundDependencyLoader]
private void load(KeyBindingStore store)
{
var enumType = Defaults?.FirstOrDefault()?.Action?.GetType();
if (enumType == null) return;
// for now let's just assume a variant of zero.
// this will need to be implemented in a better way in the future.
int? variant = null;
if (Ruleset != null)
variant = 0;
var bindings = store.Query(Ruleset?.ID, variant);
foreach (Enum v in Enum.GetValues(enumType))
// one row per valid action.
Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v)))
{
AllowMainMouseButtons = Ruleset != null
});
}
}
}
@@ -0,0 +1,72 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using OpenTK;
using osu.Game.Graphics;
namespace osu.Game.Overlays.KeyBinding
{
public abstract class KeyBindingsSubsection : SettingsSubsection
{
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
protected RulesetInfo Ruleset;
private readonly int? variant;
protected KeyBindingsSubsection(int? variant)
{
this.variant = variant;
FlowContent.Spacing = new Vector2(0, 1);
}
[BackgroundDependencyLoader]
private void load(KeyBindingStore store)
{
var bindings = store.Query(Ruleset?.ID, variant);
foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
{
// one row per valid action.
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => b.Action.Equals((int)defaultGroup.Key)))
{
AllowMainMouseButtons = Ruleset != null,
Defaults = defaultGroup.Select(d => d.KeyCombination)
});
}
Add(new ResetButton
{
Action = () => Children.OfType<KeyBindingRow>().ForEach(k => k.RestoreDefaults())
});
}
}
internal class ResetButton : OsuButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Text = "Reset";
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Top = 5 };
Height = 20;
Content.CornerRadius = 5;
BackgroundColour = colours.PinkDark;
Triangles.ColourDark = colours.PinkDarker;
Triangles.ColourLight = colours.Pink;
}
}
}
@@ -2,20 +2,26 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class RulesetBindingsSection : KeyBindingsSection
public class RulesetBindingsSection : SettingsSection
{
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override string Header => Ruleset.Name;
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => ruleset.Name;
private readonly RulesetInfo ruleset;
public RulesetBindingsSection(RulesetInfo ruleset)
{
Ruleset = ruleset;
this.ruleset = ruleset;
Defaults = ruleset.CreateInstance().GetDefaultKeyBindings();
var r = ruleset.CreateInstance();
foreach (var variant in r.AvailableVariants)
Add(new VariantBindingsSubsection(ruleset, variant));
}
}
}
@@ -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.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class VariantBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => variantName;
private readonly string variantName;
public VariantBindingsSubsection(RulesetInfo ruleset, int variant)
: base(variant)
{
Ruleset = ruleset;
var rulesetInstance = ruleset.CreateInstance();
variantName = rulesetInstance.GetVariantName(variant);
Defaults = rulesetInstance.GetDefaultKeyBindings(variant);
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader(permitNulls: true)]
private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
{
AddSection(new GlobalKeyBindingsSection(global, "Global"));
AddSection(new GlobalKeyBindingsSection(global));
foreach (var ruleset in rulesets.AllRulesets)
AddSection(new RulesetBindingsSection(ruleset));
+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)
+7 -52
View File
@@ -15,7 +15,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Framework.Threading;
@@ -205,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()
@@ -349,23 +348,23 @@ namespace osu.Game.Overlays
playerContainer.Add(new AsyncLoadWrapper(new Background(beatmap)
{
OnLoadComplete = d =>
OnLoadComplete = newBackground =>
{
switch (direction)
{
case TransformDirection.Next:
d.Position = new Vector2(400, 0);
d.MoveToX(0, 500, Easing.OutCubic);
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
d.Position = new Vector2(-400, 0);
d.MoveToX(0, 500, Easing.OutCubic);
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
currentBackground.MoveToX(400, 500, Easing.OutCubic);
break;
}
currentBackground.Expire();
currentBackground = d;
currentBackground = newBackground;
}
})
{
@@ -434,49 +433,5 @@ namespace osu.Game.Overlays
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
}
private class ProgressBar : SliderBar<double>
{
public Action<double> OnSeek;
private readonly Box fill;
public Color4 FillColour
{
set { fill.Colour = value; }
}
public double EndTime
{
set { CurrentNumber.MaxValue = value; }
}
public double CurrentTime
{
set { CurrentNumber.Value = value; }
}
public ProgressBar()
{
CurrentNumber.MinValue = 0;
CurrentNumber.MaxValue = 1;
RelativeSizeAxes = Axes.X;
Children = new Drawable[]
{
fill = new Box
{
RelativeSizeAxes = Axes.Y
}
};
}
protected override void UpdateValue(float value)
{
fill.Width = value * UsableWidth;
}
protected override void OnUserChange() => OnSeek?.Invoke(Current);
}
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ namespace osu.Game.Overlays
private void notificationClosed()
{
// hide ourselves if all notifications have been dismissed.
if (sections.Select(c => c.DisplayedCount).Sum() > 0)
if (sections.Select(c => c.DisplayedCount).Sum() == 0)
State = Visibility.Hidden;
}
@@ -142,12 +142,12 @@ namespace osu.Game.Overlays.Notifications
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
}
private bool wasClosed;
public bool WasClosed;
public virtual void Close()
{
if (wasClosed) return;
wasClosed = true;
if (WasClosed) return;
WasClosed = true;
Closed?.Invoke();
this.FadeOut(100);
@@ -24,12 +24,9 @@ namespace osu.Game.Overlays.Notifications
private FlowContainer<Notification> notifications;
public int DisplayedCount => notifications.Count;
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
public void Add(Notification notification)
{
notifications.Add(notification);
}
public void Add(Notification notification) => notifications.Add(notification);
public IEnumerable<Type> AcceptTypes;
@@ -152,11 +152,14 @@ namespace osu.Game.Overlays.Notifications
break;
case ProgressNotificationState.Active:
case ProgressNotificationState.Queued:
State = ProgressNotificationState.Cancelled;
if (CancelRequested?.Invoke() != false)
State = ProgressNotificationState.Cancelled;
break;
}
}
public Func<bool> CancelRequested { get; set; }
/// <summary>
/// The function to post completion notifications back to.
/// </summary>
@@ -4,17 +4,16 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.Notifications
{
public class SimpleNotification : Notification
{
private string text;
private string text = string.Empty;
public string Text
{
get { return text; }
@@ -36,7 +35,7 @@ namespace osu.Game.Overlays.Notifications
}
}
private readonly SpriteText textDrawable;
private readonly TextFlowContainer textDrawable;
private readonly SpriteIcon iconDrawable;
protected Box IconBackgound;
@@ -59,9 +58,8 @@ namespace osu.Game.Overlays.Notifications
}
});
Content.Add(textDrawable = new OsuSpriteText
Content.Add(textDrawable = new TextFlowContainer(t => t.TextSize = 16)
{
TextSize = 16,
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@@ -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));
}
},
};
}
}
@@ -7,14 +7,15 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Settings
{
public abstract class SettingsSubsection : FillFlowContainer, IHasFilterableChildren
{
protected override Container<Drawable> Content => content;
protected override Container<Drawable> Content => FlowContent;
private readonly Container<Drawable> content;
protected readonly FillFlowContainer FlowContent;
protected abstract string Header { get; }
@@ -33,6 +34,19 @@ namespace osu.Game.Overlays.Settings
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
FlowContent = new FillFlowContainer
{
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
};
}
[BackgroundDependencyLoader]
private void load()
{
AddRangeInternal(new Drawable[]
{
new OsuSpriteText
@@ -41,13 +55,7 @@ namespace osu.Game.Overlays.Settings
Margin = new MarginPadding { Bottom = 10 },
Font = @"Exo2.0-Black",
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
FlowContent
});
}
}
+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);
}
}

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