Merge remote-tracking branch 'origin/master' into editor-selection-tool

This commit is contained in:
smoogipoo 2017-12-14 14:20:03 +09:00
commit 9c59db4be3
11 changed files with 202 additions and 74 deletions

@ -1 +1 @@
Subproject commit 797a351db2e852fef5296453641ffbf6b2f6dc11
Subproject commit e57f32e483fa47ee066becc9e0a1b3187b340606

View File

@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@ -18,6 +17,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Osu
{
@ -33,21 +34,28 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap)
{
new BeatmapStatistic
IEnumerable<HitObject> hitObjects = beatmap.Beatmap.HitObjects;
IEnumerable<HitObject> circles = hitObjects.Where(d => !(d is IHasEndTime));
IEnumerable<HitObject> sliders = hitObjects.Where(s => s is IHasCurve);
return new[]
{
Name = @"Circle count",
Content = beatmap.Beatmap.HitObjects.Count(h => h is HitCircle).ToString(),
Icon = FontAwesome.fa_dot_circle_o
},
new BeatmapStatistic
{
Name = @"Slider count",
Content = beatmap.Beatmap.HitObjects.Count(h => h is Slider).ToString(),
Icon = FontAwesome.fa_circle_o
}
};
new BeatmapStatistic
{
Name = @"Circle Count",
Content = circles.Count().ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.Count().ToString(),
Icon = FontAwesome.fa_circle
}
};
}
public override IEnumerable<Mod> GetModsFor(ModType type)
{

View File

@ -0,0 +1,69 @@
// 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.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select;
namespace osu.Game.Tests.Visual
{
public class TestCaseBeatmapInfoWedge : OsuTestCase
{
private BeatmapManager beatmaps;
private readonly Random random;
private readonly BeatmapInfoWedge infoWedge;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
public TestCaseBeatmapInfoWedge()
{
random = new Random(0123);
Add(infoWedge = new BeatmapInfoWedge
{
Size = new Vector2(0.5f, 245),
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
{
Top = 20,
},
});
AddStep("show", () =>
{
Content.FadeInFromZero(250);
infoWedge.State = Visibility.Visible;
infoWedge.UpdateBeatmap(beatmap);
});
AddStep("hide", () =>
{
infoWedge.State = Visibility.Hidden;
Content.FadeOut(100);
});
AddStep("random beatmap", randomBeatmap);
AddStep("null beatmap", () => infoWedge.UpdateBeatmap(beatmap.Default));
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapManager beatmaps)
{
this.beatmaps = beatmaps;
beatmap.BindTo(game.Beatmap);
}
private void randomBeatmap()
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count == 0)
return;
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
beatmap.Value = beatmaps.GetWorkingBeatmap(b);
infoWedge.UpdateBeatmap(beatmap);
}
}
}

View File

@ -90,6 +90,7 @@
<Compile Include="Beatmaps\Formats\LegacyBeatmapDecoderTest.cs" />
<Compile Include="Visual\TestCaseBeatmapDetailArea.cs" />
<Compile Include="Visual\TestCaseBeatmapDetails.cs" />
<Compile Include="Visual\TestCaseBeatmapInfoWedge.cs" />
<Compile Include="Visual\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Visual\TestCaseBeatmapScoresContainer.cs" />
<Compile Include="Visual\TestCaseBeatmapSetOverlay.cs" />

View File

@ -40,36 +40,45 @@ namespace osu.Game.Beatmaps.Drawables
public BeatmapSetInfo BeatmapSet;
private BeatmapGroupState state;
public BeatmapGroupState State
{
get { return state; }
set
{
state = value;
switch (value)
{
case BeatmapGroupState.Expanded:
Header.State = PanelSelectedState.Selected;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = panel == SelectedPanel ? PanelSelectedState.Selected : PanelSelectedState.NotSelected;
break;
case BeatmapGroupState.Collapsed:
Header.State = PanelSelectedState.NotSelected;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
case BeatmapGroupState.Hidden:
Header.State = PanelSelectedState.Hidden;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
}
UpdateState();
StateChanged?.Invoke(state);
}
}
public void UpdateState()
{
switch (state)
{
case BeatmapGroupState.Expanded:
Header.State = PanelSelectedState.Selected;
foreach (BeatmapPanel panel in BeatmapPanels)
if (panel == SelectedPanel)
panel.State = PanelSelectedState.Selected;
else if (panel.Filtered)
panel.State = PanelSelectedState.Hidden;
else
panel.State = PanelSelectedState.NotSelected;
break;
case BeatmapGroupState.Collapsed:
Header.State = PanelSelectedState.NotSelected;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
case BeatmapGroupState.Hidden:
Header.State = PanelSelectedState.Hidden;
foreach (BeatmapPanel panel in BeatmapPanels)
panel.State = PanelSelectedState.Hidden;
break;
}
}
public BeatmapGroup(BeatmapSetInfo beatmapSet, BeatmapManager manager)
{
if (beatmapSet == null)
@ -88,7 +97,7 @@ namespace osu.Game.Beatmaps.Drawables
RelativeSizeAxes = Axes.X,
};
BeatmapPanels = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.StarDifficulty).Select(b => new BeatmapPanel(b)
BeatmapPanels = BeatmapSet.Beatmaps.Where(b => !b.Hidden).OrderBy(b => b.RulesetID).ThenBy(b => b.StarDifficulty).Select(b => new BeatmapPanel(b)
{
Alpha = 0,
GainedSelection = panelGainedSelection,
@ -108,7 +117,7 @@ namespace osu.Game.Beatmaps.Drawables
//we want to make sure one of our children is selected in the case none have been selected yet.
if (SelectedPanel == null)
BeatmapPanels.First().State = PanelSelectedState.Selected;
BeatmapPanels.First(p => !p.Filtered).State = PanelSelectedState.Selected;
}
private void panelGainedSelection(BeatmapPanel panel)

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -61,6 +62,8 @@ namespace osu.Game.Beatmaps.Drawables
return base.OnClick(state);
}
public BindableBool Filtered = new BindableBool();
protected override void ApplyState(PanelSelectedState last = PanelSelectedState.Hidden)
{
if (!IsLoaded) return;

View File

@ -7,6 +7,7 @@ using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -157,8 +158,7 @@ namespace osu.Game.Beatmaps.Drawables
if (panels == null)
throw new ArgumentNullException(nameof(panels));
foreach (var p in panels)
difficultyIcons.Add(new DifficultyIcon(p.Beatmap));
difficultyIcons.AddRange(panels.Select(p => new FilterableDifficultyIcon(p)));
}
public MenuItem[] ContextMenuItems
@ -178,5 +178,18 @@ namespace osu.Game.Beatmaps.Drawables
return items.ToArray();
}
}
public class FilterableDifficultyIcon : DifficultyIcon
{
private readonly BindableBool filtered = new BindableBool();
public FilterableDifficultyIcon(BeatmapPanel panel)
: base(panel.Beatmap)
{
filtered.BindTo(panel.Filtered);
filtered.ValueChanged += v => this.FadeTo(v ? 0.1f : 1, 100);
filtered.TriggerChange();
}
}
}
}

View File

@ -131,7 +131,7 @@ namespace osu.Game.Screens.Select
var newSelection =
newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID);
if(newSelection == null && oldGroup != null && selectedPanel != null)
if (newSelection == null && oldGroup != null && selectedPanel != null)
newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, oldGroup.BeatmapPanels.IndexOf(selectedPanel))];
selectGroup(newGroup, newSelection);
@ -178,42 +178,62 @@ namespace osu.Game.Screens.Select
SelectionChanged?.Invoke(null);
}
/// <summary>
/// Increment selection in the carousel in a chosen direction.
/// </summary>
/// <param name="direction">The direction to increment. Negative is backwards.</param>
/// <param name="skipDifficulties">Whether to skip individual difficulties and only increment over full groups.</param>
public void SelectNext(int direction = 1, bool skipDifficulties = true)
{
// todo: we may want to refactor and remove this as an optimisation in the future.
if (groups.All(g => g.State == BeatmapGroupState.Hidden))
{
selectNullBeatmap();
return;
}
if (!skipDifficulties && selectedGroup != null)
{
int i = selectedGroup.BeatmapPanels.IndexOf(selectedPanel) + direction;
int originalIndex = Math.Max(0, groups.IndexOf(selectedGroup));
int currentIndex = originalIndex;
if (i >= 0 && i < selectedGroup.BeatmapPanels.Count)
{
//changing difficulty panel, not set.
selectGroup(selectedGroup, selectedGroup.BeatmapPanels[i]);
return;
}
}
// local function to increment the index in the required direction, wrapping over extremities.
int incrementIndex() => currentIndex = (currentIndex + direction + groups.Count) % groups.Count;
int startIndex = Math.Max(0, groups.IndexOf(selectedGroup));
int index = startIndex;
// in the case we are skipping difficulties, we want to increment the index once before starting to find out new target
// (we don't care about the currently selected group).
if (skipDifficulties)
incrementIndex();
do
{
index = (index + direction + groups.Count) % groups.Count;
if (groups[index].State != BeatmapGroupState.Hidden)
{
if (skipDifficulties)
SelectBeatmap(groups[index].SelectedPanel != null ? groups[index].SelectedPanel.Beatmap : groups[index].BeatmapPanels.First().Beatmap);
else
SelectBeatmap(direction == 1 ? groups[index].BeatmapPanels.First().Beatmap : groups[index].BeatmapPanels.Last().Beatmap);
var group = groups[currentIndex];
if (group.State == BeatmapGroupState.Hidden) continue;
// we are only interested in non-filtered panels.
IEnumerable<BeatmapPanel> validPanels = group.BeatmapPanels.Where(p => !p.Filtered);
// if we are considering difficulties, we need to do a few extrea steps.
if (!skipDifficulties)
{
// we want to reverse the panel order if we are searching backwards.
if (direction < 0)
validPanels = validPanels.Reverse();
// if we are currently on the selected panel, let's try to find a valid difficulty before leaving to the next group.
// the first valid difficulty is found by skipping to the selected panel and then one further.
if (currentIndex == originalIndex)
validPanels = validPanels.SkipWhile(p => p != selectedPanel).Skip(1);
}
var next = validPanels.FirstOrDefault();
// at this point, we can perform the selection change if we have a valid new target, else continue to increment in the specified direction.
if (next != null)
{
selectGroup(group, next);
return;
}
} while (index != startIndex);
} while (incrementIndex() != originalIndex);
}
private IEnumerable<BeatmapGroup> getVisibleGroups() => groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden);
@ -307,6 +327,8 @@ namespace osu.Game.Screens.Select
computeYPositions();
selectedGroup?.UpdateState();
if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden)
SelectNext();
else
@ -421,11 +443,13 @@ namespace osu.Game.Screens.Select
panel.MoveToX(-50, 500, Easing.OutExpo);
bool isHidden = panel.State == PanelSelectedState.Hidden;
//on first display we want to begin hidden under our group's header.
if (panel.Alpha == 0)
if (isHidden || panel.Alpha == 0)
panel.MoveToY(headerY);
movePanel(panel, true, animated, ref currentY);
movePanel(panel, !isHidden, animated, ref currentY);
}
}
else
@ -459,8 +483,8 @@ namespace osu.Game.Screens.Select
{
try
{
if (panel == null)
panel = group.BeatmapPanels.First();
if (panel == null || panel.Filtered == true)
panel = group.BeatmapPanels.First(p => !p.Filtered);
if (selectedPanel == panel) return;

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Rulesets;
using osu.Game.Screens.Select.Filter;
@ -18,19 +19,26 @@ namespace osu.Game.Screens.Select
public RulesetInfo Ruleset;
public bool AllowConvertedBeatmaps;
private bool canConvert(BeatmapInfo beatmapInfo) => beatmapInfo.RulesetID == Ruleset.ID || beatmapInfo.RulesetID == 0 && Ruleset.ID > 0 && AllowConvertedBeatmaps;
public void Filter(List<BeatmapGroup> groups)
{
foreach (var g in groups)
{
var set = g.BeatmapSet;
bool hasCurrentMode = AllowConvertedBeatmaps || set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
// we only support converts from osu! mode to other modes for now.
// in the future this will have to change, at which point this condition will become a touch more complicated.
bool hasCurrentMode = set.Beatmaps.Any(canConvert);
bool match = hasCurrentMode;
if (!string.IsNullOrEmpty(SearchText))
match &= set.Metadata.SearchableTerms.Any(term => term.IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0);
foreach (var panel in g.BeatmapPanels)
panel.Filtered.Value = !canConvert(panel.Beatmap);
switch (g.State)
{
case BeatmapGroupState.Hidden:

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Threading;
using OpenTK;
using OpenTK.Input;
@ -309,14 +308,7 @@ namespace osu.Game.Screens.Select
carousel.Filter(criteria, debounce);
}
private void onBeatmapSetAdded(BeatmapSetInfo s)
{
Schedule(() =>
{
carousel.UpdateBeatmapSet(s);
carousel.SelectBeatmap(s.Beatmaps.First());
});
}
private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => carousel.UpdateBeatmapSet(s));
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s));

View File

@ -602,6 +602,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-frame
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=EnumMember/@EntryIndexedValue">&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalFunctions/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>