1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 16:12:57 +08:00

Merge branch 'master' into better-skin-hashing

This commit is contained in:
Dean Herbert 2020-09-14 20:16:33 +09:00
commit 2b48ae42be
19 changed files with 209 additions and 146 deletions

View File

@ -27,6 +27,17 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("get beatmap", () => editorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
}
[Test]
public void TestSelectedObjects()
{
HitCircle obj = null;
AddStep("add hitobject", () => editorBeatmap.Add(obj = new HitCircle { StartTime = 1000 }));
AddStep("select hitobject", () => editorBeatmap.SelectedHitObjects.Add(obj));
AddAssert("confirm 1 selected", () => editorBeatmap.SelectedHitObjects.Count == 1);
AddStep("deselect hitobject", () => editorBeatmap.SelectedHitObjects.Remove(obj));
AddAssert("confirm 0 selected", () => editorBeatmap.SelectedHitObjects.Count == 0);
}
[Test]
public void TestUndoFromInitialState()
{

View File

@ -6,9 +6,11 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
@ -143,6 +145,29 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("Track was restarted", () => Game.MusicController.IsPlaying);
}
[Test]
public void TestModSelectInput()
{
TestSongSelect songSelect = null;
PushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddStep("Change ruleset to osu!taiko", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(Key.Number2);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(Key.Number2);
});
AddAssert("Ruleset changed to osu!taiko", () => Game.Toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single().Current.Value.ID == 1);
AddAssert("Mods overlay still visible", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
}
private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape));

View File

@ -231,7 +231,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.Click(MouseButton.Left);
});
private IEnumerable<Dropdown<CollectionMenuItem>.DropdownMenu.DrawableDropdownMenuItem> getCollectionDropdownItems()
=> control.ChildrenOfType<CollectionFilterDropdown>().Single().ChildrenOfType<Dropdown<CollectionMenuItem>.DropdownMenu.DrawableDropdownMenuItem>();
private IEnumerable<Dropdown<CollectionFilterMenuItem>.DropdownMenu.DrawableDropdownMenuItem> getCollectionDropdownItems()
=> control.ChildrenOfType<CollectionFilterDropdown>().Single().ChildrenOfType<Dropdown<CollectionFilterMenuItem>.DropdownMenu.DrawableDropdownMenuItem>();
}
}

View File

@ -62,6 +62,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = anchor,
Anchor = anchor,
Children = new Drawable[]
{
new DrawableTeamHeader(colour)

View File

@ -12,22 +12,26 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Collections;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Screens.Select
namespace osu.Game.Collections
{
/// <summary>
/// A dropdown to select the <see cref="CollectionMenuItem"/> to filter beatmaps using.
/// A dropdown to select the <see cref="CollectionFilterMenuItem"/> to filter beatmaps using.
/// </summary>
public class CollectionFilterDropdown : OsuDropdown<CollectionMenuItem>
public class CollectionFilterDropdown : OsuDropdown<CollectionFilterMenuItem>
{
/// <summary>
/// Whether to show the "manage collections..." menu item in the dropdown.
/// </summary>
protected virtual bool ShowManageCollectionsItem => true;
private readonly IBindableList<BeatmapCollection> collections = new BindableList<BeatmapCollection>();
private readonly IBindableList<BeatmapInfo> beatmaps = new BindableList<BeatmapInfo>();
private readonly BindableList<CollectionMenuItem> filters = new BindableList<CollectionMenuItem>();
private readonly BindableList<CollectionFilterMenuItem> filters = new BindableList<CollectionFilterMenuItem>();
[Resolved(CanBeNull = true)]
private ManageCollectionsDialog manageCollectionsDialog { get; set; }
@ -62,17 +66,19 @@ namespace osu.Game.Screens.Select
var selectedItem = SelectedItem?.Value?.Collection;
filters.Clear();
filters.Add(new AllBeatmapsCollectionMenuItem());
filters.AddRange(collections.Select(c => new CollectionMenuItem(c)));
filters.Add(new ManageCollectionsMenuItem());
filters.Add(new AllBeatmapsCollectionFilterMenuItem());
filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c)));
if (ShowManageCollectionsItem)
filters.Add(new ManageCollectionsFilterMenuItem());
Current.Value = filters.SingleOrDefault(f => f.Collection != null && f.Collection == selectedItem) ?? filters[0];
}
/// <summary>
/// Occurs when the <see cref="CollectionMenuItem"/> selection has changed.
/// Occurs when the <see cref="CollectionFilterMenuItem"/> selection has changed.
/// </summary>
private void filterChanged(ValueChangedEvent<CollectionMenuItem> filter)
private void filterChanged(ValueChangedEvent<CollectionFilterMenuItem> filter)
{
// Binding the beatmaps will trigger a collection change event, which results in an infinite-loop. This is rebound later, when it's safe to do so.
beatmaps.CollectionChanged -= filterBeatmapsChanged;
@ -87,7 +93,7 @@ namespace osu.Game.Screens.Select
// Never select the manage collection filter - rollback to the previous filter.
// This is done after the above since it is important that bindable is unbound from OldValue, which is lost after forcing it back to the old value.
if (filter.NewValue is ManageCollectionsMenuItem)
if (filter.NewValue is ManageCollectionsFilterMenuItem)
{
Current.Value = filter.OldValue;
manageCollectionsDialog?.Show();
@ -104,18 +110,22 @@ namespace osu.Game.Screens.Select
Current.TriggerChange();
}
protected override string GenerateItemText(CollectionMenuItem item) => item.CollectionName.Value;
protected override string GenerateItemText(CollectionFilterMenuItem item) => item.CollectionName.Value;
protected override DropdownHeader CreateHeader() => new CollectionDropdownHeader
protected sealed override DropdownHeader CreateHeader() => CreateCollectionHeader().With(d =>
{
SelectedItem = { BindTarget = Current }
};
d.SelectedItem.BindTarget = Current;
});
protected override DropdownMenu CreateMenu() => new CollectionDropdownMenu();
protected sealed override DropdownMenu CreateMenu() => CreateCollectionMenu();
protected virtual CollectionDropdownHeader CreateCollectionHeader() => new CollectionDropdownHeader();
protected virtual CollectionDropdownMenu CreateCollectionMenu() => new CollectionDropdownMenu();
public class CollectionDropdownHeader : OsuDropdownHeader
{
public readonly Bindable<CollectionMenuItem> SelectedItem = new Bindable<CollectionMenuItem>();
public readonly Bindable<CollectionFilterMenuItem> SelectedItem = new Bindable<CollectionFilterMenuItem>();
private readonly Bindable<string> collectionName = new Bindable<string>();
protected override string Label
@ -152,7 +162,7 @@ namespace osu.Game.Screens.Select
private void updateText() => base.Label = collectionName.Value;
}
private class CollectionDropdownMenu : OsuDropdownMenu
protected class CollectionDropdownMenu : OsuDropdownMenu
{
public CollectionDropdownMenu()
{
@ -162,10 +172,10 @@ namespace osu.Game.Screens.Select
protected override DrawableDropdownMenuItem CreateDrawableDropdownMenuItem(MenuItem item) => new CollectionDropdownMenuItem(item);
}
private class CollectionDropdownMenuItem : OsuDropdownMenu.DrawableOsuDropdownMenuItem
protected class CollectionDropdownMenuItem : OsuDropdownMenu.DrawableOsuDropdownMenuItem
{
[NotNull]
protected new CollectionMenuItem Item => ((DropdownMenuItem<CollectionMenuItem>)base.Item).Value;
protected new CollectionFilterMenuItem Item => ((DropdownMenuItem<CollectionFilterMenuItem>)base.Item).Value;
[Resolved]
private OsuColour colours { get; set; }

View File

@ -3,14 +3,13 @@
using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Game.Collections;
namespace osu.Game.Screens.Select
namespace osu.Game.Collections
{
/// <summary>
/// A <see cref="BeatmapCollection"/> filter.
/// </summary>
public class CollectionMenuItem
public class CollectionFilterMenuItem
{
/// <summary>
/// The collection to filter beatmaps from.
@ -26,27 +25,27 @@ namespace osu.Game.Screens.Select
public readonly Bindable<string> CollectionName;
/// <summary>
/// Creates a new <see cref="CollectionMenuItem"/>.
/// Creates a new <see cref="CollectionFilterMenuItem"/>.
/// </summary>
/// <param name="collection">The collection to filter beatmaps from.</param>
public CollectionMenuItem([CanBeNull] BeatmapCollection collection)
public CollectionFilterMenuItem([CanBeNull] BeatmapCollection collection)
{
Collection = collection;
CollectionName = Collection?.Name.GetBoundCopy() ?? new Bindable<string>("All beatmaps");
}
}
public class AllBeatmapsCollectionMenuItem : CollectionMenuItem
public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem
{
public AllBeatmapsCollectionMenuItem()
public AllBeatmapsCollectionFilterMenuItem()
: base(null)
{
}
}
public class ManageCollectionsMenuItem : CollectionMenuItem
public class ManageCollectionsFilterMenuItem : CollectionFilterMenuItem
{
public ManageCollectionsMenuItem()
public ManageCollectionsFilterMenuItem()
: base(null)
{
CollectionName.Value = "Manage collections...";

View File

@ -390,6 +390,9 @@ namespace osu.Game.Overlays.Mods
protected override bool OnKeyDown(KeyDownEvent e)
{
// don't absorb control as ToolbarRulesetSelector uses control + number to navigate
if (e.ControlPressed) return false;
switch (e.Key)
{
case Key.Number1:

View File

@ -7,25 +7,29 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Collections;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Music
{
public class CollectionsDropdown<T> : OsuDropdown<T>
/// <summary>
/// A <see cref="CollectionFilterDropdown"/> for use in the <see cref="NowPlayingOverlay"/>.
/// </summary>
public class CollectionDropdown : CollectionFilterDropdown
{
protected override bool ShowManageCollectionsItem => false;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Gray6;
}
protected override DropdownHeader CreateHeader() => new CollectionsHeader();
protected override CollectionDropdownHeader CreateCollectionHeader() => new CollectionsHeader();
protected override DropdownMenu CreateMenu() => new CollectionsMenu();
protected override CollectionDropdownMenu CreateCollectionMenu() => new CollectionsMenu();
private class CollectionsMenu : OsuDropdownMenu
private class CollectionsMenu : CollectionDropdownMenu
{
public CollectionsMenu()
{
@ -40,7 +44,7 @@ namespace osu.Game.Overlays.Music
}
}
private class CollectionsHeader : OsuDropdownHeader
private class CollectionsHeader : CollectionDropdownHeader
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)

View File

@ -8,13 +8,15 @@ using osu.Game.Graphics.UserInterface;
using osuTK;
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
namespace osu.Game.Overlays.Music
{
public class FilterControl : Container
{
public Action<FilterCriteria> FilterChanged;
public readonly FilterTextBox Search;
private readonly CollectionDropdown collectionDropdown;
public FilterControl()
{
@ -32,21 +34,27 @@ namespace osu.Game.Overlays.Music
RelativeSizeAxes = Axes.X,
Height = 40,
},
new CollectionsDropdown<PlaylistCollection>
{
RelativeSizeAxes = Axes.X,
Items = new[] { PlaylistCollection.All },
}
collectionDropdown = new CollectionDropdown { RelativeSizeAxes = Axes.X }
},
},
};
Search.Current.ValueChanged += current_ValueChanged;
}
private void current_ValueChanged(ValueChangedEvent<string> e) => FilterChanged?.Invoke(e.NewValue);
protected override void LoadComplete()
{
base.LoadComplete();
public Action<string> FilterChanged;
Search.Current.BindValueChanged(_ => updateCriteria());
collectionDropdown.Current.BindValueChanged(_ => updateCriteria(), true);
}
private void updateCriteria() => FilterChanged?.Invoke(createCriteria());
private FilterCriteria createCriteria() => new FilterCriteria
{
SearchText = Search.Current.Value,
Collection = collectionDropdown.Current.Value?.Collection
};
public class FilterTextBox : SearchTextBox
{

View File

@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
using osu.Game.Collections;
namespace osu.Game.Overlays.Music
{
public class FilterCriteria
{
/// <summary>
/// The search text.
/// </summary>
public string SearchText;
/// <summary>
/// The collection to filter beatmaps from.
/// </summary>
[CanBeNull]
public BeatmapCollection Collection;
}
}

View File

@ -24,7 +24,15 @@ namespace osu.Game.Overlays.Music
set => base.Padding = value;
}
public void Filter(string searchTerm) => ((SearchContainer<RearrangeableListItem<BeatmapSetInfo>>)ListContainer).SearchTerm = searchTerm;
public void Filter(FilterCriteria criteria)
{
var items = (SearchContainer<RearrangeableListItem<BeatmapSetInfo>>)ListContainer;
foreach (var item in items.OfType<PlaylistItem>())
item.InSelectedCollection = criteria.Collection?.Beatmaps.Any(b => b.BeatmapSet.Equals(item.Model)) ?? true;
items.SearchTerm = criteria.SearchText;
}
public BeatmapSetInfo FirstVisibleSet => Items.FirstOrDefault(i => ((PlaylistItem)ItemMap[i]).MatchingFilter);

View File

@ -95,23 +95,40 @@ namespace osu.Game.Overlays.Music
return true;
}
private bool inSelectedCollection = true;
public bool InSelectedCollection
{
get => inSelectedCollection;
set
{
if (inSelectedCollection == value)
return;
inSelectedCollection = value;
updateFilter();
}
}
public IEnumerable<string> FilterTerms { get; }
private bool matching = true;
private bool matchingFilter = true;
public bool MatchingFilter
{
get => matching;
get => matchingFilter && inSelectedCollection;
set
{
if (matching == value) return;
if (matchingFilter == value)
return;
matching = value;
this.FadeTo(matching ? 1 : 0, 200);
matchingFilter = value;
updateFilter();
}
}
private void updateFilter() => this.FadeTo(MatchingFilter ? 1 : 0, 200);
public bool FilteringActive { get; set; }
}
}

View File

@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Music
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
FilterChanged = search => list.Filter(search),
FilterChanged = criteria => list.Filter(criteria),
Padding = new MarginPadding(10),
},
},
@ -124,10 +124,4 @@ namespace osu.Game.Overlays.Music
beatmap.Value.Track.Restart();
}
}
//todo: placeholder
public enum PlaylistCollection
{
All
}
}

View File

@ -82,57 +82,49 @@ namespace osu.Game.Rulesets.Edit
return;
}
InternalChild = new GridContainer
const float toolbar_width = 200;
InternalChildren = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Content = new[]
new Container
{
new Drawable[]
Name = "Content",
Padding = new MarginPadding { Left = toolbar_width },
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new FillFlowContainer
// layers below playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[]
{
Name = "Sidebar",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 10 },
Spacing = new Vector2(10),
Children = new Drawable[]
{
new ToolboxGroup("toolbox") { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } },
new ToolboxGroup("toggles")
{
ChildrenEnumerable = Toggles.Select(b => new SettingsCheckbox
{
Bindable = b,
LabelText = b?.Description ?? "unknown"
})
}
}
},
new Container
{
Name = "Content",
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
// layers below playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[]
{
LayerBelowRuleset,
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
}),
drawableRulesetWrapper,
// layers above playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
.WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects))
}
}
},
LayerBelowRuleset,
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
}),
drawableRulesetWrapper,
// layers above playfield
drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer()
.WithChild(BlueprintContainer = CreateBlueprintContainer(HitObjects))
}
},
ColumnDimensions = new[]
new FillFlowContainer
{
new Dimension(GridSizeMode.Absolute, 200),
}
Name = "Sidebar",
RelativeSizeAxes = Axes.Y,
Width = toolbar_width,
Padding = new MarginPadding { Right = 10 },
Spacing = new Vector2(10),
Children = new Drawable[]
{
new ToolboxGroup("toolbox") { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } },
new ToolboxGroup("toggles")
{
ChildrenEnumerable = Toggles.Select(b => new SettingsCheckbox
{
Bindable = b,
LabelText = b?.Description ?? "unknown"
})
}
}
},
};
toolboxCollection.Items = CompositionTools

View File

@ -367,14 +367,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
selectionHandler.HandleSelected(blueprint);
SelectionBlueprints.ChangeChildDepth(blueprint, 1);
beatmap.SelectedHitObjects.Add(blueprint.HitObject);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
{
selectionHandler.HandleDeselected(blueprint);
SelectionBlueprints.ChangeChildDepth(blueprint, 0);
beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
}
#endregion

View File

@ -146,7 +146,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
internal void HandleSelected(SelectionBlueprint blueprint)
{
selectedBlueprints.Add(blueprint);
EditorBeatmap.SelectedHitObjects.Add(blueprint.HitObject);
// there are potentially multiple SelectionHandlers active, but we only want to add hitobjects to the selected list once.
if (!EditorBeatmap.SelectedHitObjects.Contains(blueprint.HitObject))
EditorBeatmap.SelectedHitObjects.Add(blueprint.HitObject);
UpdateVisibility();
}
@ -158,6 +161,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
internal void HandleDeselected(SelectionBlueprint blueprint)
{
selectedBlueprints.Remove(blueprint);
EditorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject);
UpdateVisibility();

View File

@ -369,7 +369,7 @@ namespace osu.Game.Screens.Edit
public override bool OnExiting(IScreen next)
{
if (!exitConfirmed && dialogOverlay != null && HasUnsavedChanges)
if (!exitConfirmed && dialogOverlay != null && HasUnsavedChanges && !(dialogOverlay.CurrentDialog is PromptForSaveDialog))
{
dialogOverlay?.Push(new PromptForSaveDialog(confirmExit, confirmExitWithSave));
return true;

View File

@ -1,35 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Music;
namespace osu.Game.Screens.Play.PlayerSettings
{
public class CollectionSettings : PlayerSettingsGroup
{
public CollectionSettings()
: base("collections")
{
}
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"Add current song to",
},
new CollectionsDropdown<PlaylistCollection>
{
RelativeSizeAxes = Axes.X,
Items = new[] { PlaylistCollection.All },
},
};
}
}
}

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Collections;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;