mirror of
https://github.com/osukey/osukey.git
synced 2025-07-01 08:20:00 +09:00
Merge remote-tracking branch 'refs/remotes/ppy/master' into no-control-overlay-headers
This commit is contained in:
@ -111,7 +111,6 @@ platform :ios do
|
|||||||
|
|
||||||
souyuz(
|
souyuz(
|
||||||
platform: "ios",
|
platform: "ios",
|
||||||
build_target: "osu_iOS",
|
|
||||||
plist_path: "../osu.iOS/Info.plist"
|
plist_path: "../osu.iOS/Info.plist"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -54,6 +54,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.122.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.125.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -13,7 +13,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public class ManiaSelectionBlueprint : SelectionBlueprint
|
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||||
{
|
{
|
||||||
public Vector2 ScreenSpaceDragPosition { get; private set; }
|
public Vector2 ScreenSpaceDragPosition { get; private set; }
|
||||||
public Vector2 DragPosition { get; private set; }
|
public Vector2 DragPosition { get; private set; }
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -70,10 +71,12 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
|
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
|
||||||
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
|
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
||||||
delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight;
|
delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
|
||||||
|
|
||||||
foreach (var b in SelectedBlueprints)
|
foreach (var selectionBlueprint in SelectedBlueprints)
|
||||||
{
|
{
|
||||||
|
var b = (OverlaySelectionBlueprint)selectionBlueprint;
|
||||||
|
|
||||||
var hitObject = b.DrawableObject;
|
var hitObject = b.DrawableObject;
|
||||||
var objectParent = (HitObjectContainer)hitObject.Parent;
|
var objectParent = (HitObjectContainer)hitObject.Parent;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Masks
|
namespace osu.Game.Rulesets.Mania.Edit.Masks
|
||||||
{
|
{
|
||||||
public abstract class ManiaSelectionBlueprint : SelectionBlueprint
|
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||||
{
|
{
|
||||||
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
||||||
: base(drawableObject)
|
: base(drawableObject)
|
||||||
|
@ -7,10 +7,10 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
||||||
{
|
{
|
||||||
public abstract class OsuSelectionBlueprint<T> : SelectionBlueprint
|
public abstract class OsuSelectionBlueprint<T> : OverlaySelectionBlueprint
|
||||||
where T : OsuHitObject
|
where T : OsuHitObject
|
||||||
{
|
{
|
||||||
protected T HitObject => (T)DrawableObject.HitObject;
|
protected new T HitObject => (T)DrawableObject.HitObject;
|
||||||
|
|
||||||
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
|
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
|
||||||
: base(drawableObject)
|
: base(drawableObject)
|
||||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
: base(slider)
|
: base(slider)
|
||||||
{
|
{
|
||||||
this.position = position;
|
this.position = position;
|
||||||
|
|
||||||
InternalChild = CirclePiece = new HitCirclePiece();
|
InternalChild = CirclePiece = new HitCirclePiece();
|
||||||
|
|
||||||
Select();
|
Select();
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
||||||
};
|
};
|
||||||
|
|
||||||
public override Vector2 SelectionPoint => HeadBlueprint.SelectionPoint;
|
public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
|
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
|
||||||
|
|
||||||
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
public override OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
@ -22,12 +23,11 @@ using osuTK.Graphics;
|
|||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editor
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorComposeTimeline : EditorClockTestScene
|
public class TestSceneTimelineBlueprintContainer : EditorClockTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(TimelineArea),
|
typeof(TimelineArea),
|
||||||
typeof(TimelineHitObjectDisplay),
|
|
||||||
typeof(Timeline),
|
typeof(Timeline),
|
||||||
typeof(TimelineButton),
|
typeof(TimelineButton),
|
||||||
typeof(CentreMarker)
|
typeof(CentreMarker)
|
||||||
@ -38,9 +38,10 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
{
|
{
|
||||||
Beatmap.Value = new WaveformTestBeatmap(audio);
|
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||||
|
|
||||||
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap);
|
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap, BeatDivisor);
|
||||||
|
|
||||||
Dependencies.Cache(editorBeatmap);
|
Dependencies.Cache(editorBeatmap);
|
||||||
|
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -57,7 +58,7 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
},
|
},
|
||||||
new TimelineArea
|
new TimelineArea
|
||||||
{
|
{
|
||||||
Child = new TimelineHitObjectDisplay(editorBeatmap),
|
Child = new TimelineBlueprintContainer(),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
36
osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs
Normal file
36
osu.Game.Tests/Visual/Menus/TestSceneSongTicker.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// 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.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Menus
|
||||||
|
{
|
||||||
|
public class TestSceneSongTicker : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private MusicController musicController = new MusicController();
|
||||||
|
|
||||||
|
public TestSceneSongTicker()
|
||||||
|
{
|
||||||
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
musicController,
|
||||||
|
new SongTicker
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
new NowPlayingOverlay
|
||||||
|
{
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
State = { Value = Visibility.Visible }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -437,6 +437,53 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
|
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFilteringByUserStarDifficulty()
|
||||||
|
{
|
||||||
|
BeatmapSetInfo set = null;
|
||||||
|
|
||||||
|
loadBeatmaps(new List<BeatmapSetInfo>());
|
||||||
|
|
||||||
|
AddStep("add mixed difficulty set", () =>
|
||||||
|
{
|
||||||
|
set = createTestBeatmapSet(1);
|
||||||
|
set.Beatmaps.Clear();
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; i++)
|
||||||
|
{
|
||||||
|
set.Beatmaps.Add(new BeatmapInfo
|
||||||
|
{
|
||||||
|
Version = $"Stars: {i}",
|
||||||
|
StarDifficulty = i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
carousel.UpdateBeatmapSet(set);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select added set", () => carousel.SelectBeatmap(set.Beatmaps[0], false));
|
||||||
|
|
||||||
|
AddStep("filter [5..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5 } }));
|
||||||
|
AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
|
||||||
|
checkVisibleItemCount(true, 11);
|
||||||
|
|
||||||
|
AddStep("filter to [0..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Max = 7 } }));
|
||||||
|
AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
|
||||||
|
checkVisibleItemCount(true, 7);
|
||||||
|
|
||||||
|
AddStep("filter to [5..7]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 5, Max = 7 } }));
|
||||||
|
AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
|
||||||
|
checkVisibleItemCount(true, 3);
|
||||||
|
|
||||||
|
AddStep("filter [2..2]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 2, Max = 2 } }));
|
||||||
|
AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
|
||||||
|
checkVisibleItemCount(true, 1);
|
||||||
|
|
||||||
|
AddStep("filter to [0..]", () => carousel.Filter(new FilterCriteria { UserStarDifficulty = { Min = 0 } }));
|
||||||
|
AddUntilStep("Wait for debounce", () => !carousel.PendingFilterTask);
|
||||||
|
checkVisibleItemCount(true, 15);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null)
|
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null)
|
||||||
{
|
{
|
||||||
createCarousel();
|
createCarousel();
|
||||||
|
@ -239,11 +239,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case @"Source":
|
case @"Source":
|
||||||
beatmap.BeatmapInfo.Metadata.Source = pair.Value;
|
metadata.Source = pair.Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"Tags":
|
case @"Tags":
|
||||||
beatmap.BeatmapInfo.Metadata.Tags = pair.Value;
|
metadata.Tags = pair.Value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case @"BeatmapID":
|
case @"BeatmapID":
|
||||||
@ -300,13 +300,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case LegacyEventType.Background:
|
case LegacyEventType.Background:
|
||||||
string bgFilename = split[2].Trim('"');
|
beatmap.BeatmapInfo.Metadata.BackgroundFile = CleanFilename(split[2]);
|
||||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = bgFilename.ToStandardisedPath();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LegacyEventType.Video:
|
case LegacyEventType.Video:
|
||||||
string videoFilename = split[2].Trim('"');
|
beatmap.BeatmapInfo.Metadata.VideoFile = CleanFilename(split[2]);
|
||||||
beatmap.BeatmapInfo.Metadata.VideoFile = videoFilename.ToStandardisedPath();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LegacyEventType.Break:
|
case LegacyEventType.Break:
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -115,7 +116,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':')
|
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':')
|
||||||
{
|
{
|
||||||
var split = line.Trim().Split(new[] { separator }, 2);
|
var split = line.Split(separator, 2);
|
||||||
|
|
||||||
return new KeyValuePair<string, string>
|
return new KeyValuePair<string, string>
|
||||||
(
|
(
|
||||||
@ -124,6 +125,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected string CleanFilename(string path) => path.Trim('"').ToStandardisedPath();
|
||||||
|
|
||||||
protected enum Section
|
protected enum Section
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
@ -7,7 +7,6 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
@ -65,13 +64,16 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
private void handleEvents(string line)
|
private void handleEvents(string line)
|
||||||
{
|
{
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
|
var lineSpan = line.AsSpan();
|
||||||
|
|
||||||
while (line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal))
|
while (lineSpan.StartsWith(" ", StringComparison.Ordinal) || lineSpan.StartsWith("_", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
lineSpan = lineSpan.Slice(1);
|
||||||
++depth;
|
++depth;
|
||||||
line = line.Substring(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
line = lineSpan.ToString();
|
||||||
|
|
||||||
decodeVariables(ref line);
|
decodeVariables(ref line);
|
||||||
|
|
||||||
string[] split = line.Split(',');
|
string[] split = line.Split(',');
|
||||||
@ -89,7 +91,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var layer = parseLayer(split[1]);
|
var layer = parseLayer(split[1]);
|
||||||
var origin = parseOrigin(split[2]);
|
var origin = parseOrigin(split[2]);
|
||||||
var path = cleanFilename(split[3]);
|
var path = CleanFilename(split[3]);
|
||||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||||
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
|
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
|
||||||
@ -101,7 +103,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var layer = parseLayer(split[1]);
|
var layer = parseLayer(split[1]);
|
||||||
var origin = parseOrigin(split[2]);
|
var origin = parseOrigin(split[2]);
|
||||||
var path = cleanFilename(split[3]);
|
var path = CleanFilename(split[3]);
|
||||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||||
var frameCount = int.Parse(split[6]);
|
var frameCount = int.Parse(split[6]);
|
||||||
@ -116,7 +118,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||||
var layer = parseLayer(split[2]);
|
var layer = parseLayer(split[2]);
|
||||||
var path = cleanFilename(split[3]);
|
var path = CleanFilename(split[3]);
|
||||||
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
|
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
|
||||||
storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume));
|
storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume));
|
||||||
break;
|
break;
|
||||||
@ -331,7 +333,5 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string cleanFilename(string path) => path.Trim('"').ToStandardisedPath();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
Set(OsuSetting.ShowConvertedBeatmaps, true);
|
Set(OsuSetting.ShowConvertedBeatmaps, true);
|
||||||
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
|
Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1);
|
||||||
Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1);
|
Set(OsuSetting.DisplayStarsMaximum, 10.1, 0, 10.1, 0.1);
|
||||||
|
|
||||||
Set(OsuSetting.SongSelectGroupingMode, GroupMode.All);
|
Set(OsuSetting.SongSelectGroupingMode, GroupMode.All);
|
||||||
Set(OsuSetting.SongSelectSortingMode, SortMode.Title);
|
Set(OsuSetting.SongSelectSortingMode, SortMode.Title);
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
@ -85,14 +84,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
set => strip.Colour = value;
|
set => strip.Colour = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TabFillFlowContainer CreateTabFlow() => new OsuTabFillFlowContainer
|
|
||||||
{
|
|
||||||
Direction = FillDirection.Full,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Depth = -1,
|
|
||||||
Masking = true
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
@ -284,10 +275,5 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OsuTabFillFlowContainer : TabFillFlowContainer
|
|
||||||
{
|
|
||||||
protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osuTK;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Chat.Tabs
|
namespace osu.Game.Overlays.Chat.Tabs
|
||||||
{
|
{
|
||||||
@ -113,5 +114,18 @@ namespace osu.Game.Overlays.Chat.Tabs
|
|||||||
|
|
||||||
OnRequestLeave?.Invoke(tab.Value);
|
OnRequestLeave?.Invoke(tab.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override TabFillFlowContainer CreateTabFlow() => new ChannelTabFillFlowContainer
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Full,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = -1,
|
||||||
|
Masking = true
|
||||||
|
};
|
||||||
|
|
||||||
|
private class ChannelTabFillFlowContainer : TabFillFlowContainer
|
||||||
|
{
|
||||||
|
protected override int Compare(Drawable x, Drawable y) => CompareReverseChildID(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -10,11 +12,20 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
{
|
{
|
||||||
public class SongSelectSettings : SettingsSubsection
|
public class SongSelectSettings : SettingsSubsection
|
||||||
{
|
{
|
||||||
|
private Bindable<double> minStars;
|
||||||
|
private Bindable<double> maxStars;
|
||||||
|
|
||||||
protected override string Header => "Song Select";
|
protected override string Header => "Song Select";
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
|
minStars = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum);
|
||||||
|
maxStars = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum);
|
||||||
|
|
||||||
|
minStars.ValueChanged += min => maxStars.Value = Math.Max(min.NewValue, maxStars.Value);
|
||||||
|
maxStars.ValueChanged += max => minStars.Value = Math.Min(max.NewValue, minStars.Value);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
@ -27,19 +38,19 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
LabelText = "Show converted beatmaps",
|
LabelText = "Show converted beatmaps",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps),
|
Bindable = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps),
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, StarSlider>
|
new SettingsSlider<double, StarsSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Display beatmaps from",
|
LabelText = "Display beatmaps from",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum),
|
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum),
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
Keywords = new[] { "star", "difficulty" }
|
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, StarSlider>
|
new SettingsSlider<double, MaximumStarsSlider>
|
||||||
{
|
{
|
||||||
LabelText = "up to",
|
LabelText = "up to",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum),
|
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum),
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
Keywords = new[] { "star", "difficulty" }
|
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<RandomSelectAlgorithm>
|
new SettingsEnumDropdown<RandomSelectAlgorithm>
|
||||||
{
|
{
|
||||||
@ -49,7 +60,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StarSlider : OsuSliderBar<double>
|
private class MaximumStarsSlider : StarsSlider
|
||||||
|
{
|
||||||
|
public override string TooltipText => Current.IsDefault ? "no limit" : base.TooltipText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StarsSlider : OsuSliderBar<double>
|
||||||
{
|
{
|
||||||
public override string TooltipText => Current.Value.ToString(@"0.## stars");
|
public override string TooltipText => Current.Value.ToString(@"0.## stars");
|
||||||
}
|
}
|
||||||
|
34
osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs
Normal file
34
osu.Game/Rulesets/Edit/OverlaySelectionBlueprint.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// 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.Graphics.Primitives;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Edit
|
||||||
|
{
|
||||||
|
public abstract class OverlaySelectionBlueprint : SelectionBlueprint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="DrawableHitObject"/> which this <see cref="OverlaySelectionBlueprint"/> applies to.
|
||||||
|
/// </summary>
|
||||||
|
public readonly DrawableHitObject DrawableObject;
|
||||||
|
|
||||||
|
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
|
||||||
|
|
||||||
|
protected OverlaySelectionBlueprint(DrawableHitObject drawableObject)
|
||||||
|
: base(drawableObject.HitObject)
|
||||||
|
{
|
||||||
|
DrawableObject = drawableObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
public override Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre;
|
||||||
|
|
||||||
|
public override Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
public override Vector2 GetInstantDelta(Vector2 screenSpacePosition) => DrawableObject.Parent.ToLocalSpace(screenSpacePosition) - DrawableObject.Position;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SelectionBlueprint : CompositeDrawable, IStateful<SelectionState>
|
public abstract class SelectionBlueprint : CompositeDrawable, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
|
public readonly HitObject HitObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when this <see cref="SelectionBlueprint"/> has been selected.
|
/// Invoked when this <see cref="SelectionBlueprint"/> has been selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -30,26 +32,24 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<SelectionBlueprint> Deselected;
|
public event Action<SelectionBlueprint> Deselected;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="DrawableHitObject"/> which this <see cref="SelectionBlueprint"/> applies to.
|
|
||||||
/// </summary>
|
|
||||||
public readonly DrawableHitObject DrawableObject;
|
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
|
|
||||||
public override bool HandlePositionalInput => ShouldBeAlive;
|
public override bool HandlePositionalInput => ShouldBeAlive;
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private HitObjectComposer composer { get; set; }
|
private HitObjectComposer composer { get; set; }
|
||||||
|
|
||||||
protected SelectionBlueprint(DrawableHitObject drawableObject)
|
protected SelectionBlueprint(HitObject hitObject)
|
||||||
{
|
{
|
||||||
DrawableObject = drawableObject;
|
HitObject = hitObject;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
Alpha = 0;
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SelectionState state;
|
private SelectionState state;
|
||||||
@ -66,58 +66,68 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
switch (state)
|
if (IsLoaded)
|
||||||
{
|
updateState();
|
||||||
case SelectionState.Selected:
|
|
||||||
Show();
|
|
||||||
Selected?.Invoke(this);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SelectionState.NotSelected:
|
|
||||||
Hide();
|
|
||||||
Deselected?.Invoke(this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChanged?.Invoke(state);
|
StateChanged?.Invoke(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case SelectionState.Selected:
|
||||||
|
OnSelected();
|
||||||
|
Selected?.Invoke(this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SelectionState.NotSelected:
|
||||||
|
OnDeselected();
|
||||||
|
Deselected?.Invoke(this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDeselected() => Hide();
|
||||||
|
|
||||||
|
protected virtual void OnSelected() => Show();
|
||||||
|
|
||||||
// When not selected, input is only required for the blueprint itself to receive IsHovering
|
// When not selected, input is only required for the blueprint itself to receive IsHovering
|
||||||
protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected;
|
protected override bool ShouldBeConsideredForInput(Drawable child) => State == SelectionState.Selected;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects this <see cref="SelectionBlueprint"/>, causing it to become visible.
|
/// Selects this <see cref="OverlaySelectionBlueprint"/>, causing it to become visible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Select() => State = SelectionState.Selected;
|
public void Select() => State = SelectionState.Selected;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deselects this <see cref="SelectionBlueprint"/>, causing it to become invisible.
|
/// Deselects this <see cref="OverlaySelectionBlueprint"/>, causing it to become invisible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Deselect() => State = SelectionState.NotSelected;
|
public void Deselect() => State = SelectionState.NotSelected;
|
||||||
|
|
||||||
public bool IsSelected => State == SelectionState.Selected;
|
public bool IsSelected => State == SelectionState.Selected;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the <see cref="HitObject"/>, invoking <see cref="HitObject.ApplyDefaults"/> and re-processing the beatmap.
|
/// Updates the <see cref="Objects.HitObject"/>, invoking <see cref="Objects.HitObject.ApplyDefaults"/> and re-processing the beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void UpdateHitObject() => composer?.UpdateHitObject(DrawableObject.HitObject);
|
protected void UpdateHitObject() => composer?.UpdateHitObject(HitObject);
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.ReceivePositionalInputAt(screenSpacePos);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="MenuItem"/>s to be displayed in the context menu for this <see cref="SelectionBlueprint"/>.
|
/// The <see cref="MenuItem"/>s to be displayed in the context menu for this <see cref="OverlaySelectionBlueprint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual MenuItem[] ContextMenuItems => Array.Empty<MenuItem>();
|
public virtual MenuItem[] ContextMenuItems => Array.Empty<MenuItem>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The screen-space point that causes this <see cref="SelectionBlueprint"/> to be selected.
|
/// The screen-space point that causes this <see cref="OverlaySelectionBlueprint"/> to be selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual Vector2 SelectionPoint => DrawableObject.ScreenSpaceDrawQuad.Centre;
|
public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The screen-space quad that outlines this <see cref="SelectionBlueprint"/> for selections.
|
/// The screen-space quad that outlines this <see cref="OverlaySelectionBlueprint"/> for selections.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual Quad SelectionQuad => DrawableObject.ScreenSpaceDrawQuad;
|
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
public virtual Vector2 GetInstantDelta(Vector2 screenSpacePosition) => Parent.ToLocalSpace(screenSpacePosition) - Position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,24 @@ using osu.Game.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||||
{
|
{
|
||||||
|
public class TimelinePart : TimelinePart<Drawable>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a part of the summary timeline..
|
/// Represents a part of the summary timeline..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TimelinePart : Container
|
public class TimelinePart<T> : Container<T> where T : Drawable
|
||||||
{
|
{
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
|
||||||
private readonly Container timeline;
|
private readonly Container<T> content;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => timeline;
|
protected override Container<T> Content => content;
|
||||||
|
|
||||||
public TimelinePart()
|
public TimelinePart(Container<T> content = null)
|
||||||
{
|
{
|
||||||
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
|
AddInternal(this.content = content ?? new Container<T> { RelativeSizeAxes = Axes.Both });
|
||||||
|
|
||||||
Beatmap.ValueChanged += b =>
|
Beatmap.ValueChanged += b =>
|
||||||
{
|
{
|
||||||
@ -44,17 +48,17 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
// the track may not be loaded completely (only has a length once it is).
|
// the track may not be loaded completely (only has a length once it is).
|
||||||
if (!Beatmap.Value.Track.IsLoaded)
|
if (!Beatmap.Value.Track.IsLoaded)
|
||||||
{
|
{
|
||||||
timeline.RelativeChildSize = Vector2.One;
|
content.RelativeChildSize = Vector2.One;
|
||||||
Schedule(updateRelativeChildSize);
|
Schedule(updateRelativeChildSize);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
|
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
timeline.Clear();
|
content.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
@ -31,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
protected DragBox DragBox { get; private set; }
|
protected DragBox DragBox { get; private set; }
|
||||||
|
|
||||||
private SelectionBlueprintContainer selectionBlueprints;
|
private Container<SelectionBlueprint> selectionBlueprints;
|
||||||
|
|
||||||
private SelectionHandler selectionHandler;
|
private SelectionHandler selectionHandler;
|
||||||
|
|
||||||
@ -41,6 +42,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap beatmap { get; set; }
|
private EditorBeatmap beatmap { get; set; }
|
||||||
|
|
||||||
|
private readonly BindableList<HitObject> selectedHitObjects = new BindableList<HitObject>();
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private IDistanceSnapProvider snapProvider { get; set; }
|
private IDistanceSnapProvider snapProvider { get; set; }
|
||||||
|
|
||||||
@ -59,12 +62,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
DragBox = CreateDragBox(select),
|
DragBox = CreateDragBox(select),
|
||||||
selectionHandler,
|
selectionHandler,
|
||||||
selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both },
|
selectionBlueprints = CreateSelectionBlueprintContainer(),
|
||||||
DragBox.CreateProxy().With(p => p.Depth = float.MinValue)
|
DragBox.CreateProxy().With(p => p.Depth = float.MinValue)
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
AddBlueprintFor(obj);
|
AddBlueprintFor(obj);
|
||||||
|
|
||||||
|
selectedHitObjects.BindTo(beatmap.SelectedHitObjects);
|
||||||
|
selectedHitObjects.ItemsAdded += objects =>
|
||||||
|
{
|
||||||
|
foreach (var o in objects)
|
||||||
|
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
|
||||||
|
};
|
||||||
|
|
||||||
|
selectedHitObjects.ItemsRemoved += objects =>
|
||||||
|
{
|
||||||
|
foreach (var o in objects)
|
||||||
|
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -75,6 +91,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
beatmap.HitObjectRemoved += removeBlueprintFor;
|
beatmap.HitObjectRemoved += removeBlueprintFor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual Container<SelectionBlueprint> CreateSelectionBlueprintContainer() =>
|
||||||
|
new Container<SelectionBlueprint> { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="SelectionHandler"/> which outlines <see cref="DrawableHitObject"/>s and handles movement of selections.
|
/// Creates a <see cref="SelectionHandler"/> which outlines <see cref="DrawableHitObject"/>s and handles movement of selections.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -91,6 +110,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
beginClickSelection(e);
|
beginClickSelection(e);
|
||||||
|
prepareSelectionMovement();
|
||||||
|
|
||||||
return e.Button == MouseButton.Left;
|
return e.Button == MouseButton.Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +139,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (clickedBlueprint == null)
|
if (clickedBlueprint == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
adjustableClock?.Seek(clickedBlueprint.DrawableObject.HitObject.StartTime);
|
adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +147,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
// Special case for when a drag happened instead of a click
|
// Special case for when a drag happened instead of a click
|
||||||
Schedule(() => endClickSelection());
|
Schedule(() => endClickSelection());
|
||||||
|
|
||||||
|
finishSelectionMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
@ -133,15 +156,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (e.Button == MouseButton.Right)
|
if (e.Button == MouseButton.Right)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!beginSelectionMovement())
|
if (movementBlueprint != null)
|
||||||
{
|
return true;
|
||||||
if (!DragBox.UpdateDrag(e))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DragBox.FadeIn(250, Easing.OutQuint);
|
if (DragBox.HandleDrag(e))
|
||||||
|
{
|
||||||
|
DragBox.Show();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
protected override void OnDrag(DragEvent e)
|
||||||
@ -149,8 +173,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (e.Button == MouseButton.Right)
|
if (e.Button == MouseButton.Right)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!moveCurrentSelection(e))
|
if (DragBox.State == Visibility.Visible)
|
||||||
DragBox.UpdateDrag(e);
|
DragBox.HandleDrag(e);
|
||||||
|
|
||||||
|
moveCurrentSelection(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDragEnd(DragEndEvent e)
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
@ -158,9 +184,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (e.Button == MouseButton.Right)
|
if (e.Button == MouseButton.Right)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!finishSelectionMovement())
|
if (DragBox.State == Visibility.Visible)
|
||||||
{
|
{
|
||||||
DragBox.FadeOut(250, Easing.OutQuint);
|
DragBox.Hide();
|
||||||
selectionHandler.UpdateVisibility();
|
selectionHandler.UpdateVisibility();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +226,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
private void removeBlueprintFor(HitObject hitObject)
|
private void removeBlueprintFor(HitObject hitObject)
|
||||||
{
|
{
|
||||||
var blueprint = selectionBlueprints.SingleOrDefault(m => m.DrawableObject.HitObject == hitObject);
|
var blueprint = selectionBlueprints.SingleOrDefault(m => m.HitObject == hitObject);
|
||||||
if (blueprint == null)
|
if (blueprint == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -248,7 +274,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered))
|
if (!allowDeselection && selectionHandler.SelectedBlueprints.Any(s => s.IsHovered))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveBlueprints)
|
foreach (SelectionBlueprint blueprint in selectionBlueprints.AliveChildren)
|
||||||
{
|
{
|
||||||
if (blueprint.IsHovered)
|
if (blueprint.IsHovered)
|
||||||
{
|
{
|
||||||
@ -305,6 +331,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
selectionHandler.HandleSelected(blueprint);
|
selectionHandler.HandleSelected(blueprint);
|
||||||
selectionBlueprints.ChangeChildDepth(blueprint, 1);
|
selectionBlueprints.ChangeChildDepth(blueprint, 1);
|
||||||
|
beatmap.SelectedHitObjects.Add(blueprint.HitObject);
|
||||||
|
|
||||||
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
|
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
|
||||||
}
|
}
|
||||||
@ -313,6 +340,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
selectionHandler.HandleDeselected(blueprint);
|
selectionHandler.HandleDeselected(blueprint);
|
||||||
selectionBlueprints.ChangeChildDepth(blueprint, 0);
|
selectionBlueprints.ChangeChildDepth(blueprint, 0);
|
||||||
|
beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
|
||||||
|
|
||||||
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
|
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
|
||||||
}
|
}
|
||||||
@ -321,27 +349,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
#region Selection Movement
|
#region Selection Movement
|
||||||
|
|
||||||
private Vector2? screenSpaceMovementStartPosition;
|
private Vector2? movementBlueprintOriginalPosition;
|
||||||
private SelectionBlueprint movementBlueprint;
|
private SelectionBlueprint movementBlueprint;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to begin the movement of any selected blueprints.
|
/// Attempts to begin the movement of any selected blueprints.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Whether movement began.</returns>
|
private void prepareSelectionMovement()
|
||||||
private bool beginSelectionMovement()
|
|
||||||
{
|
{
|
||||||
Debug.Assert(movementBlueprint == null);
|
if (!selectionHandler.SelectedBlueprints.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
// Any selected blueprint that is hovered can begin the movement of the group, however only the earliest hitobject is used for movement
|
// Any selected blueprint that is hovered can begin the movement of the group, however only the earliest hitobject is used for movement
|
||||||
// A special case is added for when a click selection occurred before the drag
|
// A special case is added for when a click selection occurred before the drag
|
||||||
if (!clickSelectionBegan && !selectionHandler.SelectedBlueprints.Any(b => b.IsHovered))
|
if (!clickSelectionBegan && !selectionHandler.SelectedBlueprints.Any(b => b.IsHovered))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
// Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject
|
// Movement is tracked from the blueprint of the earliest hitobject, since it only makes sense to distance snap from that hitobject
|
||||||
movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.DrawableObject.HitObject.StartTime).First();
|
movementBlueprint = selectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).First();
|
||||||
screenSpaceMovementStartPosition = movementBlueprint.DrawableObject.ToScreenSpace(movementBlueprint.DrawableObject.OriginPosition);
|
movementBlueprintOriginalPosition = movementBlueprint.SelectionPoint; // todo: unsure if correct
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -354,17 +380,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (movementBlueprint == null)
|
if (movementBlueprint == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Debug.Assert(screenSpaceMovementStartPosition != null);
|
Debug.Assert(movementBlueprintOriginalPosition != null);
|
||||||
|
|
||||||
Vector2 startPosition = screenSpaceMovementStartPosition.Value;
|
HitObject draggedObject = movementBlueprint.HitObject;
|
||||||
HitObject draggedObject = movementBlueprint.DrawableObject.HitObject;
|
|
||||||
|
|
||||||
// The final movement position, relative to screenSpaceMovementStartPosition
|
// The final movement position, relative to screenSpaceMovementStartPosition
|
||||||
Vector2 movePosition = startPosition + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||||
|
|
||||||
(Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime);
|
(Vector2 snappedPosition, double snappedTime) = snapProvider.GetSnappedPosition(ToLocalSpace(movePosition), draggedObject.StartTime);
|
||||||
|
|
||||||
// Move the hitobjects
|
// Move the hitobjects
|
||||||
if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, startPosition, ToScreenSpace(snappedPosition))))
|
if (!selectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, ToScreenSpace(snappedPosition))))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Apply the start time at the newly snapped-to position
|
// Apply the start time at the newly snapped-to position
|
||||||
@ -384,7 +410,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (movementBlueprint == null)
|
if (movementBlueprint == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
screenSpaceMovementStartPosition = null;
|
movementBlueprintOriginalPosition = null;
|
||||||
movementBlueprint = null;
|
movementBlueprint = null;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -402,30 +428,5 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
beatmap.HitObjectRemoved -= removeBlueprintFor;
|
beatmap.HitObjectRemoved -= removeBlueprintFor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectionBlueprintContainer : Container<SelectionBlueprint>
|
|
||||||
{
|
|
||||||
public IEnumerable<SelectionBlueprint> AliveBlueprints => AliveInternalChildren.Cast<SelectionBlueprint>();
|
|
||||||
|
|
||||||
protected override int Compare(Drawable x, Drawable y)
|
|
||||||
{
|
|
||||||
if (!(x is SelectionBlueprint xBlueprint) || !(y is SelectionBlueprint yBlueprint))
|
|
||||||
return base.Compare(x, y);
|
|
||||||
|
|
||||||
return Compare(xBlueprint, yBlueprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Compare(SelectionBlueprint x, SelectionBlueprint y)
|
|
||||||
{
|
|
||||||
// dpeth is used to denote selected status (we always want selected blueprints to handle input first).
|
|
||||||
int d = x.Depth.CompareTo(y.Depth);
|
|
||||||
if (d != 0)
|
|
||||||
return d;
|
|
||||||
|
|
||||||
// Put earlier hitobjects towards the end of the list, so they handle input first
|
|
||||||
int i = y.DrawableObject.HitObject.StartTime.CompareTo(x.DrawableObject.HitObject.StartTime);
|
|
||||||
return i == 0 ? CompareReverseChildID(x, y) : i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return CreateBlueprintFor(drawable);
|
return CreateBlueprintFor(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
|
public virtual OverlaySelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
|
||||||
|
|
||||||
protected override void AddBlueprintFor(HitObject hitObject)
|
protected override void AddBlueprintFor(HitObject hitObject)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -15,11 +16,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A box that displays the drag selection and provides selection events for users to handle.
|
/// A box that displays the drag selection and provides selection events for users to handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DragBox : CompositeDrawable
|
public class DragBox : CompositeDrawable, IStateful<Visibility>
|
||||||
{
|
{
|
||||||
private readonly Action<RectangleF> performSelection;
|
protected readonly Action<RectangleF> PerformSelection;
|
||||||
|
|
||||||
private Drawable box;
|
protected Drawable Box;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="DragBox"/>.
|
/// Creates a new <see cref="DragBox"/>.
|
||||||
@ -27,7 +28,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <param name="performSelection">A delegate that performs drag selection.</param>
|
/// <param name="performSelection">A delegate that performs drag selection.</param>
|
||||||
public DragBox(Action<RectangleF> performSelection)
|
public DragBox(Action<RectangleF> performSelection)
|
||||||
{
|
{
|
||||||
this.performSelection = performSelection;
|
PerformSelection = performSelection;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
@ -37,25 +38,27 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = box = new Container
|
InternalChild = Box = CreateBox();
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
BorderColour = Color4.White,
|
|
||||||
BorderThickness = SelectionHandler.BORDER_RADIUS,
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.1f
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual Drawable CreateBox() => new Container
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
BorderColour = Color4.White,
|
||||||
|
BorderThickness = SelectionHandler.BORDER_RADIUS,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.1f
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle a forwarded mouse event.
|
/// Handle a forwarded mouse event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="e">The mouse event.</param>
|
/// <param name="e">The mouse event.</param>
|
||||||
/// <returns>Whether the event should be handled and blocking.</returns>
|
/// <returns>Whether the event should be handled and blocking.</returns>
|
||||||
public virtual bool UpdateDrag(MouseButtonEvent e)
|
public virtual bool HandleDrag(MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
var dragPosition = e.ScreenSpaceMousePosition;
|
var dragPosition = e.ScreenSpaceMousePosition;
|
||||||
var dragStartPosition = e.ScreenSpaceMouseDownPosition;
|
var dragStartPosition = e.ScreenSpaceMouseDownPosition;
|
||||||
@ -68,11 +71,32 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
var topLeft = ToLocalSpace(dragRectangle.TopLeft);
|
var topLeft = ToLocalSpace(dragRectangle.TopLeft);
|
||||||
var bottomRight = ToLocalSpace(dragRectangle.BottomRight);
|
var bottomRight = ToLocalSpace(dragRectangle.BottomRight);
|
||||||
|
|
||||||
box.Position = topLeft;
|
Box.Position = topLeft;
|
||||||
box.Size = bottomRight - topLeft;
|
Box.Size = bottomRight - topLeft;
|
||||||
|
|
||||||
performSelection?.Invoke(dragRectangle);
|
PerformSelection?.Invoke(dragRectangle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Visibility state;
|
||||||
|
|
||||||
|
public Visibility State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == state) return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
this.FadeTo(state == Visibility.Hidden ? 0 : 1, 250, Easing.OutQuint);
|
||||||
|
StateChanged?.Invoke(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Hide() => State = Visibility.Hidden;
|
||||||
|
|
||||||
|
public override void Show() => State = Visibility.Visible;
|
||||||
|
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Screens.Edit.Compose.Components
|
namespace osu.Game.Screens.Edit.Compose.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An event which occurs when a <see cref="SelectionBlueprint"/> is moved.
|
/// An event which occurs when a <see cref="OverlaySelectionBlueprint"/> is moved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MoveSelectionEvent
|
public class MoveSelectionEvent
|
||||||
{
|
{
|
||||||
@ -16,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly SelectionBlueprint Blueprint;
|
public readonly SelectionBlueprint Blueprint;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The starting screen-space position of the hitobject.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Vector2 ScreenSpaceStartPosition;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The expected screen-space position of the hitobject at the current cursor position.
|
/// The expected screen-space position of the hitobject at the current cursor position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -29,18 +24,14 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The distance between <see cref="ScreenSpacePosition"/> and the hitobject's current position, in the coordinate-space of the hitobject's parent.
|
/// The distance between <see cref="ScreenSpacePosition"/> and the hitobject's current position, in the coordinate-space of the hitobject's parent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// This does not use <see cref="ScreenSpaceStartPosition"/> and does not represent the cumulative movement distance.
|
|
||||||
/// </remarks>
|
|
||||||
public readonly Vector2 InstantDelta;
|
public readonly Vector2 InstantDelta;
|
||||||
|
|
||||||
public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition)
|
public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
Blueprint = blueprint;
|
Blueprint = blueprint;
|
||||||
ScreenSpaceStartPosition = screenSpaceStartPosition;
|
|
||||||
ScreenSpacePosition = screenSpacePosition;
|
ScreenSpacePosition = screenSpacePosition;
|
||||||
|
|
||||||
InstantDelta = Blueprint.DrawableObject.Parent.ToLocalSpace(ScreenSpacePosition) - Blueprint.DrawableObject.Position;
|
InstantDelta = Blueprint.GetInstantDelta(ScreenSpacePosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
public IEnumerable<SelectionBlueprint> SelectedBlueprints => selectedBlueprints;
|
public IEnumerable<SelectionBlueprint> SelectedBlueprints => selectedBlueprints;
|
||||||
private readonly List<SelectionBlueprint> selectedBlueprints;
|
private readonly List<SelectionBlueprint> selectedBlueprints;
|
||||||
|
|
||||||
public IEnumerable<HitObject> SelectedHitObjects => selectedBlueprints.Select(b => b.DrawableObject.HitObject);
|
public IEnumerable<HitObject> SelectedHitObjects => selectedBlueprints.Select(b => b.HitObject);
|
||||||
|
|
||||||
private Drawable outline;
|
private Drawable outline;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IPlacementHandler placementHandler { get; set; }
|
private EditorBeatmap editorBeatmap { get; set; }
|
||||||
|
|
||||||
public SelectionHandler()
|
public SelectionHandler()
|
||||||
{
|
{
|
||||||
@ -104,7 +104,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// Handle a blueprint becoming selected.
|
/// Handle a blueprint becoming selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="blueprint">The blueprint.</param>
|
/// <param name="blueprint">The blueprint.</param>
|
||||||
internal void HandleSelected(SelectionBlueprint blueprint) => selectedBlueprints.Add(blueprint);
|
internal void HandleSelected(SelectionBlueprint blueprint)
|
||||||
|
{
|
||||||
|
selectedBlueprints.Add(blueprint);
|
||||||
|
editorBeatmap.SelectedHitObjects.Add(blueprint.HitObject);
|
||||||
|
|
||||||
|
UpdateVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle a blueprint becoming deselected.
|
/// Handle a blueprint becoming deselected.
|
||||||
@ -113,6 +119,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
internal void HandleDeselected(SelectionBlueprint blueprint)
|
internal void HandleDeselected(SelectionBlueprint blueprint)
|
||||||
{
|
{
|
||||||
selectedBlueprints.Remove(blueprint);
|
selectedBlueprints.Remove(blueprint);
|
||||||
|
editorBeatmap.SelectedHitObjects.Remove(blueprint.HitObject);
|
||||||
|
|
||||||
// We don't want to update visibility if > 0, since we may be deselecting blueprints during drag-selection
|
// We don't want to update visibility if > 0, since we may be deselecting blueprints during drag-selection
|
||||||
if (selectedBlueprints.Count == 0)
|
if (selectedBlueprints.Count == 0)
|
||||||
@ -141,14 +148,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
DeselectAll?.Invoke();
|
DeselectAll?.Invoke();
|
||||||
blueprint.Select();
|
blueprint.Select();
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateVisibility();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteSelected()
|
private void deleteSelected()
|
||||||
{
|
{
|
||||||
foreach (var h in selectedBlueprints.ToList())
|
foreach (var h in selectedBlueprints.ToList())
|
||||||
placementHandler.Delete(h.DrawableObject.HitObject);
|
editorBeatmap.Remove(h.HitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -11,10 +12,14 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class Timeline : ZoomableScrollContainer
|
[Cached(typeof(IDistanceSnapProvider))]
|
||||||
|
[Cached]
|
||||||
|
public class Timeline : ZoomableScrollContainer, IDistanceSnapProvider
|
||||||
{
|
{
|
||||||
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
|
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
|
||||||
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
@ -162,5 +167,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
if (trackWasPlaying)
|
if (trackWasPlaying)
|
||||||
adjustableClock.Start();
|
adjustableClock.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private EditorBeatmap beatmap { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBeatSnapProvider beatSnapProvider { get; set; }
|
||||||
|
|
||||||
|
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
|
||||||
|
{
|
||||||
|
var targetTime = (position.X / Content.DrawWidth) * track.Length;
|
||||||
|
return (position, beatSnapProvider.SnapTime(targetTime, targetTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public float DurationToDistance(double referenceTime, double duration) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public double DistanceToDuration(double referenceTime, float distance) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public double GetSnappedDurationFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
// 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 System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
|
{
|
||||||
|
internal class TimelineBlueprintContainer : BlueprintContainer
|
||||||
|
{
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private Timeline timeline { get; set; }
|
||||||
|
|
||||||
|
private DragEvent lastDragEvent;
|
||||||
|
|
||||||
|
public TimelineBlueprintContainer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
Height = 0.4f;
|
||||||
|
|
||||||
|
AddInternal(new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.Black,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.1f,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
DragBox.Alpha = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Container<SelectionBlueprint> CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
protected override void OnDrag(DragEvent e)
|
||||||
|
{
|
||||||
|
if (timeline != null)
|
||||||
|
{
|
||||||
|
var timelineQuad = timeline.ScreenSpaceDrawQuad;
|
||||||
|
var mouseX = e.ScreenSpaceMousePosition.X;
|
||||||
|
|
||||||
|
// scroll if in a drag and dragging outside visible extents
|
||||||
|
if (mouseX > timelineQuad.TopRight.X)
|
||||||
|
timeline.ScrollBy((float)((mouseX - timelineQuad.TopRight.X) / 10 * Clock.ElapsedFrameTime));
|
||||||
|
else if (mouseX < timelineQuad.TopLeft.X)
|
||||||
|
timeline.ScrollBy((float)((mouseX - timelineQuad.TopLeft.X) / 10 * Clock.ElapsedFrameTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnDrag(e);
|
||||||
|
lastDragEvent = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDragEnd(DragEndEvent e)
|
||||||
|
{
|
||||||
|
base.OnDragEnd(e);
|
||||||
|
lastDragEvent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
// trigger every frame so drags continue to update selection while playback is scrolling the timeline.
|
||||||
|
if (IsDragged)
|
||||||
|
OnDrag(lastDragEvent);
|
||||||
|
|
||||||
|
base.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SelectionHandler CreateSelectionHandler() => new TimelineSelectionHandler();
|
||||||
|
|
||||||
|
protected override SelectionBlueprint CreateBlueprintFor(HitObject hitObject) => new TimelineHitObjectBlueprint(hitObject);
|
||||||
|
|
||||||
|
protected override DragBox CreateDragBox(Action<RectangleF> performSelect) => new TimelineDragBox(performSelect);
|
||||||
|
|
||||||
|
internal class TimelineSelectionHandler : SelectionHandler
|
||||||
|
{
|
||||||
|
// for now we always allow movement. snapping is provided by the Timeline's "distance" snap implementation
|
||||||
|
public override bool HandleMovement(MoveSelectionEvent moveEvent) => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TimelineDragBox : DragBox
|
||||||
|
{
|
||||||
|
private Vector2 lastMouseDown;
|
||||||
|
private float localMouseDown;
|
||||||
|
|
||||||
|
public TimelineDragBox(Action<RectangleF> performSelect)
|
||||||
|
: base(performSelect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Drawable CreateBox() => new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Alpha = 0.3f
|
||||||
|
};
|
||||||
|
|
||||||
|
public override bool HandleDrag(MouseButtonEvent e)
|
||||||
|
{
|
||||||
|
// store the original position of the mouse down, as we may be scrolled during selection.
|
||||||
|
if (lastMouseDown != e.ScreenSpaceMouseDownPosition)
|
||||||
|
{
|
||||||
|
lastMouseDown = e.ScreenSpaceMouseDownPosition;
|
||||||
|
localMouseDown = e.MouseDownPosition.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
float selection1 = localMouseDown;
|
||||||
|
float selection2 = e.MousePosition.X;
|
||||||
|
|
||||||
|
Box.X = Math.Min(selection1, selection2);
|
||||||
|
Box.Width = Math.Abs(selection1 - selection2);
|
||||||
|
|
||||||
|
PerformSelection?.Invoke(Box.ScreenSpaceDrawQuad.AABBFloat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class TimelineSelectionBlueprintContainer : Container<SelectionBlueprint>
|
||||||
|
{
|
||||||
|
protected override Container<SelectionBlueprint> Content { get; }
|
||||||
|
|
||||||
|
public TimelineSelectionBlueprintContainer()
|
||||||
|
{
|
||||||
|
AddInternal(new TimelinePart<SelectionBlueprint>(Content = new Container<SelectionBlueprint> { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
// 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.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
|
{
|
||||||
|
public class TimelineHitObjectBlueprint : SelectionBlueprint
|
||||||
|
{
|
||||||
|
private readonly Circle circle;
|
||||||
|
|
||||||
|
private readonly Container extensionBar;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
private readonly Bindable<double> startTime;
|
||||||
|
|
||||||
|
public const float THICKNESS = 3;
|
||||||
|
|
||||||
|
private const float circle_size = 16;
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || circle.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
public TimelineHitObjectBlueprint(HitObject hitObject)
|
||||||
|
: base(hitObject)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft;
|
||||||
|
Origin = Anchor.CentreLeft;
|
||||||
|
|
||||||
|
startTime = hitObject.StartTimeBindable.GetBoundCopy();
|
||||||
|
startTime.BindValueChanged(time => X = (float)time.NewValue, true);
|
||||||
|
|
||||||
|
RelativePositionAxes = Axes.X;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
if (hitObject is IHasEndTime)
|
||||||
|
{
|
||||||
|
AddInternal(extensionBar = new Container
|
||||||
|
{
|
||||||
|
CornerRadius = 2,
|
||||||
|
Masking = true,
|
||||||
|
Size = new Vector2(1, THICKNESS),
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Colour = Color4.Black,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AddInternal(circle = new Circle
|
||||||
|
{
|
||||||
|
Size = new Vector2(circle_size),
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Colour = Color4.White,
|
||||||
|
BorderColour = Color4.Black,
|
||||||
|
BorderThickness = THICKNESS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// no bindable so we perform this every update
|
||||||
|
Width = (float)(HitObject.GetEndTime() - HitObject.StartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSelected()
|
||||||
|
{
|
||||||
|
circle.BorderColour = Color4.Orange;
|
||||||
|
if (extensionBar != null)
|
||||||
|
extensionBar.Colour = Color4.Orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDeselected()
|
||||||
|
{
|
||||||
|
circle.BorderColour = Color4.Black;
|
||||||
|
if (extensionBar != null)
|
||||||
|
extensionBar.Colour = Color4.Black;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Quad SelectionQuad
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// correctly include the circle in the selection quad region, as it is usually outside the blueprint itself.
|
||||||
|
var circleQuad = circle.ScreenSpaceDrawQuad;
|
||||||
|
var actualQuad = ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
return new Quad(circleQuad.TopLeft, Vector2.ComponentMax(actualQuad.TopRight, circleQuad.TopRight),
|
||||||
|
circleQuad.BottomLeft, Vector2.ComponentMax(actualQuad.BottomRight, circleQuad.BottomRight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector2 SelectionPoint => ScreenSpaceDrawQuad.TopLeft;
|
||||||
|
}
|
||||||
|
}
|
@ -1,140 +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 System;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Primitives;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|
||||||
{
|
|
||||||
internal class TimelineHitObjectDisplay : BlueprintContainer
|
|
||||||
{
|
|
||||||
private EditorBeatmap beatmap { get; }
|
|
||||||
|
|
||||||
private readonly TimelinePart content;
|
|
||||||
|
|
||||||
public TimelineHitObjectDisplay(EditorBeatmap beatmap)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
this.beatmap = beatmap;
|
|
||||||
|
|
||||||
AddInternal(content = new TimelinePart { RelativeSizeAxes = Axes.Both });
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
foreach (var h in beatmap.HitObjects)
|
|
||||||
add(h);
|
|
||||||
|
|
||||||
beatmap.HitObjectAdded += add;
|
|
||||||
beatmap.HitObjectRemoved += remove;
|
|
||||||
beatmap.StartTimeChanged += h =>
|
|
||||||
{
|
|
||||||
remove(h);
|
|
||||||
add(h);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
DragBox.Alpha = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void remove(HitObject h)
|
|
||||||
{
|
|
||||||
foreach (var d in content.OfType<TimelineHitObjectRepresentation>().Where(c => c.HitObject == h))
|
|
||||||
d.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void add(HitObject h)
|
|
||||||
{
|
|
||||||
var yOffset = content.Count(d => d.X == h.StartTime);
|
|
||||||
|
|
||||||
content.Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
|
||||||
{
|
|
||||||
base.OnMouseDown(e);
|
|
||||||
|
|
||||||
return false; // tempoerary until we correctly handle selections.
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override DragBox CreateDragBox(Action<RectangleF> performSelect) => new NoDragDragBox(performSelect);
|
|
||||||
|
|
||||||
internal class NoDragDragBox : DragBox
|
|
||||||
{
|
|
||||||
public NoDragDragBox(Action<RectangleF> performSelect)
|
|
||||||
: base(performSelect)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool UpdateDrag(MouseButtonEvent e) => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelineHitObjectRepresentation : CompositeDrawable
|
|
||||||
{
|
|
||||||
public const float THICKNESS = 3;
|
|
||||||
|
|
||||||
public readonly HitObject HitObject;
|
|
||||||
|
|
||||||
public TimelineHitObjectRepresentation(HitObject hitObject)
|
|
||||||
{
|
|
||||||
HitObject = hitObject;
|
|
||||||
Anchor = Anchor.CentreLeft;
|
|
||||||
Origin = Anchor.CentreLeft;
|
|
||||||
|
|
||||||
Width = (float)(hitObject.GetEndTime() - hitObject.StartTime);
|
|
||||||
|
|
||||||
X = (float)hitObject.StartTime;
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.X;
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
|
|
||||||
if (hitObject is IHasEndTime)
|
|
||||||
{
|
|
||||||
AddInternal(new Container
|
|
||||||
{
|
|
||||||
CornerRadius = 2,
|
|
||||||
Masking = true,
|
|
||||||
Size = new Vector2(1, THICKNESS),
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
RelativePositionAxes = Axes.X,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
Child = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
AddInternal(new Circle
|
|
||||||
{
|
|
||||||
Size = new Vector2(16),
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativePositionAxes = Axes.X,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Colour = Color4.White,
|
|
||||||
BorderColour = Color4.Black,
|
|
||||||
BorderThickness = THICKNESS,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -32,6 +32,6 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
|
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineHitObjectDisplay(EditorBeatmap);
|
protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<HitObject> StartTimeChanged;
|
public event Action<HitObject> StartTimeChanged;
|
||||||
|
|
||||||
|
public BindableList<HitObject> SelectedHitObjects { get; } = new BindableList<HitObject>();
|
||||||
|
|
||||||
public readonly IBeatmap PlayableBeatmap;
|
public readonly IBeatmap PlayableBeatmap;
|
||||||
|
|
||||||
private readonly BindableBeatDivisor beatDivisor;
|
private readonly BindableBeatDivisor beatDivisor;
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
private TimelineArea timelineArea;
|
private Container timelineContainer;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
|
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
|
||||||
@ -62,11 +62,10 @@ namespace osu.Game.Screens.Edit
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
timelineContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Right = 5 },
|
Padding = new MarginPadding { Right = 5 },
|
||||||
Child = timelineArea = CreateTimelineArea()
|
|
||||||
},
|
},
|
||||||
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
||||||
},
|
},
|
||||||
@ -100,14 +99,16 @@ namespace osu.Game.Screens.Edit
|
|||||||
mainContent.Add(content);
|
mainContent.Add(content);
|
||||||
content.FadeInFromZero(300, Easing.OutQuint);
|
content.FadeInFromZero(300, Easing.OutQuint);
|
||||||
|
|
||||||
LoadComponentAsync(CreateTimelineContent(), timelineArea.Add);
|
LoadComponentAsync(new TimelineArea
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = CreateTimelineContent()
|
||||||
|
}, timelineContainer.Add);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Drawable CreateMainContent();
|
protected abstract Drawable CreateMainContent();
|
||||||
|
|
||||||
protected virtual Drawable CreateTimelineContent() => new Container();
|
protected virtual Drawable CreateTimelineContent() => new Container();
|
||||||
|
|
||||||
protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private ExitConfirmOverlay exitConfirmOverlay;
|
private ExitConfirmOverlay exitConfirmOverlay;
|
||||||
|
|
||||||
|
private ParallaxContainer buttonsContainer;
|
||||||
|
private SongTicker songTicker;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
|
private void load(DirectOverlay direct, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
|
||||||
{
|
{
|
||||||
@ -89,9 +92,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
new ParallaxContainer
|
buttonsContainer = new ParallaxContainer
|
||||||
{
|
{
|
||||||
ParallaxAmount = 0.01f,
|
ParallaxAmount = 0.01f,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -107,6 +110,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
sideFlashes = new MenuSideFlashes(),
|
sideFlashes = new MenuSideFlashes(),
|
||||||
|
songTicker = new SongTicker
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Margin = new MarginPadding { Right = 15, Top = 5 }
|
||||||
|
},
|
||||||
|
exitConfirmOverlay?.CreateProxy() ?? Drawable.Empty()
|
||||||
});
|
});
|
||||||
|
|
||||||
buttons.StateChanged += state =>
|
buttons.StateChanged += state =>
|
||||||
@ -190,7 +200,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
buttons.State = ButtonSystemState.TopLevel;
|
buttons.State = ButtonSystemState.TopLevel;
|
||||||
|
|
||||||
this.FadeIn(FADE_IN_DURATION, Easing.OutQuint);
|
this.FadeIn(FADE_IN_DURATION, Easing.OutQuint);
|
||||||
this.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint);
|
buttonsContainer.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint);
|
||||||
|
|
||||||
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
|
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
|
||||||
}
|
}
|
||||||
@ -227,7 +237,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
buttons.State = ButtonSystemState.EnteringMode;
|
buttons.State = ButtonSystemState.EnteringMode;
|
||||||
|
|
||||||
this.FadeOut(FADE_OUT_DURATION, Easing.InSine);
|
this.FadeOut(FADE_OUT_DURATION, Easing.InSine);
|
||||||
this.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine);
|
buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine);
|
||||||
|
|
||||||
sideFlashes.FadeOut(64, Easing.OutQuint);
|
sideFlashes.FadeOut(64, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
@ -262,6 +272,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
|
|
||||||
buttons.State = ButtonSystemState.Exit;
|
buttons.State = ButtonSystemState.Exit;
|
||||||
|
|
||||||
|
songTicker.Hide();
|
||||||
|
|
||||||
this.FadeOut(3000);
|
this.FadeOut(3000);
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
72
osu.Game/Screens/Menu/SongTicker.cs
Normal file
72
osu.Game/Screens/Menu/SongTicker.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Menu
|
||||||
|
{
|
||||||
|
public class SongTicker : Container
|
||||||
|
{
|
||||||
|
private const int fade_duration = 800;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<WorkingBeatmap> beatmap { get; set; }
|
||||||
|
|
||||||
|
private readonly OsuSpriteText title, artist;
|
||||||
|
|
||||||
|
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||||
|
|
||||||
|
public SongTicker()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 3),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
title = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Font = OsuFont.GetFont(size: 24, weight: FontWeight.Light, italics: true)
|
||||||
|
},
|
||||||
|
artist = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Font = OsuFont.GetFont(size: 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
beatmap.BindValueChanged(_ => Scheduler.AddOnce(show), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show()
|
||||||
|
{
|
||||||
|
var metadata = beatmap.Value.Metadata;
|
||||||
|
|
||||||
|
title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
|
||||||
|
artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
|
||||||
|
|
||||||
|
this.FadeInFromZero(fade_duration / 2f)
|
||||||
|
.Delay(4000)
|
||||||
|
.Then().FadeOut(fade_duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -33,7 +33,7 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
private const float shear_width = 36.75f;
|
private const float shear_width = 36.75f;
|
||||||
|
|
||||||
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGED_CONTAINER_SIZE.Y, 0);
|
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
||||||
|
|
||||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
|
@ -44,6 +44,8 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) ||
|
match &= !criteria.Artist.HasFilter || criteria.Artist.Matches(Beatmap.Metadata.Artist) ||
|
||||||
criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode);
|
criteria.Artist.Matches(Beatmap.Metadata.ArtistUnicode);
|
||||||
|
|
||||||
|
match &= !criteria.UserStarDifficulty.HasFilter || criteria.UserStarDifficulty.IsInRange(Beatmap.StarDifficulty);
|
||||||
|
|
||||||
if (match)
|
if (match)
|
||||||
{
|
{
|
||||||
var terms = new List<string>();
|
var terms = new List<string>();
|
||||||
|
@ -42,9 +42,15 @@ namespace osu.Game.Screens.Select
|
|||||||
Group = groupMode.Value,
|
Group = groupMode.Value,
|
||||||
Sort = sortMode.Value,
|
Sort = sortMode.Value,
|
||||||
AllowConvertedBeatmaps = showConverted.Value,
|
AllowConvertedBeatmaps = showConverted.Value,
|
||||||
Ruleset = ruleset.Value
|
Ruleset = ruleset.Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!minimumStars.IsDefault)
|
||||||
|
criteria.UserStarDifficulty.Min = minimumStars.Value;
|
||||||
|
|
||||||
|
if (!maximumStars.IsDefault)
|
||||||
|
criteria.UserStarDifficulty.Max = maximumStars.Value;
|
||||||
|
|
||||||
FilterQueryParser.ApplyQueries(criteria, query);
|
FilterQueryParser.ApplyQueries(criteria, query);
|
||||||
return criteria;
|
return criteria;
|
||||||
}
|
}
|
||||||
@ -142,7 +148,9 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
private readonly IBindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
private Bindable<bool> showConverted;
|
private readonly Bindable<bool> showConverted = new Bindable<bool>();
|
||||||
|
private readonly Bindable<double> minimumStars = new Bindable<double>();
|
||||||
|
private readonly Bindable<double> maximumStars = new Bindable<double>();
|
||||||
|
|
||||||
public readonly Box Background;
|
public readonly Box Background;
|
||||||
|
|
||||||
@ -151,9 +159,15 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
sortTabs.AccentColour = colours.GreenLight;
|
sortTabs.AccentColour = colours.GreenLight;
|
||||||
|
|
||||||
showConverted = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps);
|
config.BindWith(OsuSetting.ShowConvertedBeatmaps, showConverted);
|
||||||
showConverted.ValueChanged += _ => updateCriteria();
|
showConverted.ValueChanged += _ => updateCriteria();
|
||||||
|
|
||||||
|
config.BindWith(OsuSetting.DisplayStarsMinimum, minimumStars);
|
||||||
|
minimumStars.ValueChanged += _ => updateCriteria();
|
||||||
|
|
||||||
|
config.BindWith(OsuSetting.DisplayStarsMaximum, maximumStars);
|
||||||
|
maximumStars.ValueChanged += _ => updateCriteria();
|
||||||
|
|
||||||
ruleset.BindTo(parentRuleset);
|
ruleset.BindTo(parentRuleset);
|
||||||
ruleset.BindValueChanged(_ => updateCriteria());
|
ruleset.BindValueChanged(_ => updateCriteria());
|
||||||
|
|
||||||
|
@ -26,6 +26,12 @@ namespace osu.Game.Screens.Select
|
|||||||
public OptionalTextFilter Creator;
|
public OptionalTextFilter Creator;
|
||||||
public OptionalTextFilter Artist;
|
public OptionalTextFilter Artist;
|
||||||
|
|
||||||
|
public OptionalRange<double> UserStarDifficulty = new OptionalRange<double>
|
||||||
|
{
|
||||||
|
IsLowerInclusive = true,
|
||||||
|
IsUpperInclusive = true
|
||||||
|
};
|
||||||
|
|
||||||
public string[] SearchTerms = Array.Empty<string>();
|
public string[] SearchTerms = Array.Empty<string>();
|
||||||
|
|
||||||
public RulesetInfo Ruleset;
|
public RulesetInfo Ruleset;
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
public abstract class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>
|
public abstract class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
public static readonly Vector2 WEDGED_CONTAINER_SIZE = new Vector2(0.5f, 245);
|
public static readonly float WEDGE_HEIGHT = 245;
|
||||||
|
|
||||||
protected const float BACKGROUND_BLUR = 20;
|
protected const float BACKGROUND_BLUR = 20;
|
||||||
private const float left_area_padding = 20;
|
private const float left_area_padding = 20;
|
||||||
@ -96,98 +96,116 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new ParallaxContainer
|
|
||||||
{
|
|
||||||
Masking = true,
|
|
||||||
ParallaxAmount = 0.005f,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new WedgeBackground
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Right = -150 },
|
|
||||||
Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(WEDGED_CONTAINER_SIZE.X, 1),
|
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
Bottom = Footer.HEIGHT,
|
|
||||||
Top = WEDGED_CONTAINER_SIZE.Y + left_area_padding,
|
|
||||||
Left = left_area_padding,
|
|
||||||
Right = left_area_padding * 2,
|
|
||||||
},
|
|
||||||
Child = BeatmapDetails = new BeatmapDetailArea
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Top = 10, Right = 5 },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Width = 2, //avoid horizontal masking so the panels don't clip when screen stack is pushed.
|
|
||||||
Child = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Width = 0.5f,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding
|
|
||||||
{
|
|
||||||
Top = FilterControl.HEIGHT,
|
|
||||||
Bottom = Footer.HEIGHT
|
|
||||||
},
|
|
||||||
Child = Carousel = new BeatmapCarousel
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(1 - WEDGED_CONTAINER_SIZE.X, 1),
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
SelectionChanged = updateSelectedBeatmap,
|
|
||||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
FilterControl = new FilterControl
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = FilterControl.HEIGHT,
|
|
||||||
FilterChanged = ApplyFilterToCarousel,
|
|
||||||
Background = { Width = 2 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
beatmapInfoWedge = new BeatmapInfoWedge
|
|
||||||
{
|
|
||||||
Size = WEDGED_CONTAINER_SIZE,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Top = left_area_padding,
|
|
||||||
Right = left_area_padding,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new ResetScrollContainer(() => Carousel.ScrollToSelected())
|
new ResetScrollContainer(() => Carousel.ScrollToSelected())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = 250,
|
Width = 250,
|
||||||
}
|
},
|
||||||
|
new VerticalMaskingContainer
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new GridContainer // used for max width implementation
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 850),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new ParallaxContainer
|
||||||
|
{
|
||||||
|
ParallaxAmount = 0.005f,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new WedgeBackground
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Right = -150 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = FilterControl.HEIGHT,
|
||||||
|
Bottom = Footer.HEIGHT
|
||||||
|
},
|
||||||
|
Child = Carousel = new BeatmapCarousel
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
SelectionChanged = updateSelectedBeatmap,
|
||||||
|
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FilterControl = new FilterControl
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = FilterControl.HEIGHT,
|
||||||
|
FilterChanged = ApplyFilterToCarousel,
|
||||||
|
Background = { Width = 2 },
|
||||||
|
},
|
||||||
|
new GridContainer // used for max width implementation
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Relative, 0.5f, maxSize: 650),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
beatmapInfoWedge = new BeatmapInfoWedge
|
||||||
|
{
|
||||||
|
Height = WEDGE_HEIGHT,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = left_area_padding,
|
||||||
|
Right = left_area_padding,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Bottom = Footer.HEIGHT,
|
||||||
|
Top = WEDGE_HEIGHT + left_area_padding,
|
||||||
|
Left = left_area_padding,
|
||||||
|
Right = left_area_padding * 2,
|
||||||
|
},
|
||||||
|
Child = BeatmapDetails = new BeatmapDetailArea
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = 10, Right = 5 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ShowFooter)
|
if (ShowFooter)
|
||||||
@ -705,6 +723,29 @@ namespace osu.Game.Screens.Select
|
|||||||
return base.OnKeyDown(e);
|
return base.OnKeyDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class VerticalMaskingContainer : Container
|
||||||
|
{
|
||||||
|
private const float panel_overflow = 1.2f;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content { get; }
|
||||||
|
|
||||||
|
public VerticalMaskingContainer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Masking = true;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
Width = panel_overflow; //avoid horizontal masking so the panels don't clip when screen stack is pushed.
|
||||||
|
InternalChild = Content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Width = 1 / panel_overflow,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ResetScrollContainer : Container
|
private class ResetScrollContainer : Container
|
||||||
{
|
{
|
||||||
private readonly Action onHoverAction;
|
private readonly Action onHoverAction;
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AddBlueprint(SelectionBlueprint blueprint)
|
protected void AddBlueprint(OverlaySelectionBlueprint blueprint)
|
||||||
{
|
{
|
||||||
Add(blueprint.With(d =>
|
Add(blueprint.With(d =>
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.122.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.125.0" />
|
||||||
<PackageReference Include="Sentry" Version="1.2.0" />
|
<PackageReference Include="Sentry" Version="1.2.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.122.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.125.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2020.122.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2020.125.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Reference in New Issue
Block a user