-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28523 from bdach/break-display-and-adjustment
Display breaks on editor timeline & allow manually adjusting their duration
- Loading branch information
Showing
5 changed files
with
336 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 0 additions & 23 deletions
23
osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs
This file was deleted.
Oops, something went wrong.
216 changes: 216 additions & 0 deletions
216
osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBreak.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// 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.Diagnostics; | ||
using System.Linq; | ||
using osu.Framework.Allocation; | ||
using osu.Framework.Extensions.Color4Extensions; | ||
using osu.Framework.Graphics; | ||
using osu.Framework.Graphics.Containers; | ||
using osu.Framework.Graphics.Shapes; | ||
using osu.Framework.Input.Events; | ||
using osu.Game.Beatmaps.Timing; | ||
using osu.Game.Graphics; | ||
using osu.Game.Graphics.Sprites; | ||
using osu.Game.Rulesets.Objects; | ||
using osuTK; | ||
|
||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline | ||
{ | ||
public partial class TimelineBreak : CompositeDrawable | ||
{ | ||
public BreakPeriod Break { get; } | ||
|
||
public TimelineBreak(BreakPeriod b) | ||
{ | ||
Break = b; | ||
} | ||
|
||
[BackgroundDependencyLoader] | ||
private void load(OsuColour colours) | ||
{ | ||
RelativePositionAxes = Axes.X; | ||
RelativeSizeAxes = Axes.Both; | ||
Origin = Anchor.TopLeft; | ||
Padding = new MarginPadding { Horizontal = -5 }; | ||
|
||
InternalChildren = new Drawable[] | ||
{ | ||
new Container | ||
{ | ||
RelativeSizeAxes = Axes.Both, | ||
Padding = new MarginPadding { Horizontal = 5 }, | ||
Child = new Box | ||
{ | ||
RelativeSizeAxes = Axes.Both, | ||
Colour = colours.PurpleLight, | ||
Alpha = 0.4f, | ||
}, | ||
}, | ||
new DragHandle(Break, isStartHandle: true) | ||
{ | ||
Anchor = Anchor.TopLeft, | ||
Origin = Anchor.TopLeft, | ||
Action = (time, breakPeriod) => breakPeriod.StartTime = time, | ||
}, | ||
new DragHandle(Break, isStartHandle: false) | ||
{ | ||
Anchor = Anchor.TopRight, | ||
Origin = Anchor.TopRight, | ||
Action = (time, breakPeriod) => breakPeriod.EndTime = time, | ||
}, | ||
}; | ||
} | ||
|
||
protected override void Update() | ||
{ | ||
base.Update(); | ||
|
||
X = (float)Break.StartTime; | ||
Width = (float)Break.Duration; | ||
} | ||
|
||
private partial class DragHandle : FillFlowContainer | ||
{ | ||
public new Anchor Anchor | ||
{ | ||
get => base.Anchor; | ||
init => base.Anchor = value; | ||
} | ||
|
||
public Action<double, BreakPeriod>? Action { get; init; } | ||
|
||
private readonly BreakPeriod breakPeriod; | ||
private readonly bool isStartHandle; | ||
|
||
private Container handle = null!; | ||
private (double min, double max)? allowedDragRange; | ||
|
||
[Resolved] | ||
private EditorBeatmap beatmap { get; set; } = null!; | ||
|
||
[Resolved] | ||
private Timeline timeline { get; set; } = null!; | ||
|
||
[Resolved] | ||
private IEditorChangeHandler? changeHandler { get; set; } | ||
|
||
[Resolved] | ||
private OsuColour colours { get; set; } = null!; | ||
|
||
public DragHandle(BreakPeriod breakPeriod, bool isStartHandle) | ||
{ | ||
this.breakPeriod = breakPeriod; | ||
this.isStartHandle = isStartHandle; | ||
} | ||
|
||
[BackgroundDependencyLoader] | ||
private void load() | ||
{ | ||
AutoSizeAxes = Axes.X; | ||
RelativeSizeAxes = Axes.Y; | ||
Direction = FillDirection.Horizontal; | ||
Spacing = new Vector2(5); | ||
|
||
Children = new Drawable[] | ||
{ | ||
handle = new Container | ||
{ | ||
Anchor = Anchor, | ||
Origin = Anchor, | ||
RelativeSizeAxes = Axes.Y, | ||
CornerRadius = 5, | ||
Masking = true, | ||
Child = new Box | ||
{ | ||
RelativeSizeAxes = Axes.Both, | ||
Colour = Colour4.White, | ||
}, | ||
}, | ||
new OsuSpriteText | ||
{ | ||
BypassAutoSizeAxes = Axes.X, | ||
Anchor = Anchor, | ||
Origin = Anchor, | ||
Text = "Break", | ||
Margin = new MarginPadding { Top = 2, }, | ||
}, | ||
}; | ||
} | ||
|
||
protected override void LoadComplete() | ||
{ | ||
base.LoadComplete(); | ||
|
||
updateState(); | ||
FinishTransforms(true); | ||
} | ||
|
||
protected override bool OnHover(HoverEvent e) | ||
{ | ||
updateState(); | ||
return true; | ||
} | ||
|
||
protected override void OnHoverLost(HoverLostEvent e) | ||
{ | ||
updateState(); | ||
base.OnHoverLost(e); | ||
} | ||
|
||
protected override bool OnDragStart(DragStartEvent e) | ||
{ | ||
changeHandler?.BeginChange(); | ||
updateState(); | ||
|
||
double min = beatmap.HitObjects.Last(ho => ho.GetEndTime() <= breakPeriod.StartTime).GetEndTime(); | ||
double max = beatmap.HitObjects.First(ho => ho.StartTime >= breakPeriod.EndTime).StartTime; | ||
|
||
if (isStartHandle) | ||
max = Math.Min(max, breakPeriod.EndTime - BreakPeriod.MIN_BREAK_DURATION); | ||
else | ||
min = Math.Max(min, breakPeriod.StartTime + BreakPeriod.MIN_BREAK_DURATION); | ||
|
||
allowedDragRange = (min, max); | ||
|
||
return true; | ||
} | ||
|
||
protected override void OnDrag(DragEvent e) | ||
{ | ||
base.OnDrag(e); | ||
|
||
Debug.Assert(allowedDragRange != null); | ||
|
||
if (timeline.FindSnappedPositionAndTime(e.ScreenSpaceMousePosition).Time is double time | ||
&& time > allowedDragRange.Value.min | ||
&& time < allowedDragRange.Value.max) | ||
{ | ||
Action?.Invoke(time, breakPeriod); | ||
} | ||
|
||
updateState(); | ||
} | ||
|
||
protected override void OnDragEnd(DragEndEvent e) | ||
{ | ||
changeHandler?.EndChange(); | ||
updateState(); | ||
base.OnDragEnd(e); | ||
} | ||
|
||
private void updateState() | ||
{ | ||
bool active = IsHovered || IsDragged; | ||
|
||
var colour = colours.PurpleLighter; | ||
if (active) | ||
colour = colour.Lighten(0.3f); | ||
|
||
this.FadeColour(colour, 400, Easing.OutQuint); | ||
handle.ResizeWidthTo(active ? 20 : 10, 400, Easing.OutElasticHalf); | ||
} | ||
} | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBreakDisplay.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// 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.Bindables; | ||
using osu.Framework.Caching; | ||
using osu.Game.Beatmaps.Timing; | ||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; | ||
|
||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline | ||
{ | ||
public partial class TimelineBreakDisplay : TimelinePart<TimelineBreak> | ||
{ | ||
[Resolved] | ||
private Timeline timeline { get; set; } = null!; | ||
|
||
/// <summary> | ||
/// The visible time/position range of the timeline. | ||
/// </summary> | ||
private (float min, float max) visibleRange = (float.MinValue, float.MaxValue); | ||
|
||
private readonly Cached breakCache = new Cached(); | ||
|
||
private readonly BindableList<BreakPeriod> breaks = new BindableList<BreakPeriod>(); | ||
|
||
protected override void LoadBeatmap(EditorBeatmap beatmap) | ||
{ | ||
base.LoadBeatmap(beatmap); | ||
|
||
// TODO: this will have to be mutable soon enough | ||
breaks.AddRange(beatmap.Breaks); | ||
} | ||
|
||
protected override void Update() | ||
{ | ||
base.Update(); | ||
|
||
if (DrawWidth <= 0) return; | ||
|
||
(float, float) newRange = ( | ||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X) / DrawWidth * Content.RelativeChildSize.X, | ||
(ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X) / DrawWidth * Content.RelativeChildSize.X); | ||
|
||
if (visibleRange != newRange) | ||
{ | ||
visibleRange = newRange; | ||
breakCache.Invalidate(); | ||
} | ||
|
||
if (!breakCache.IsValid) | ||
{ | ||
recreateBreaks(); | ||
breakCache.Validate(); | ||
} | ||
} | ||
|
||
private void recreateBreaks() | ||
{ | ||
// Remove groups outside the visible range | ||
foreach (TimelineBreak drawableBreak in this) | ||
{ | ||
if (!shouldBeVisible(drawableBreak.Break)) | ||
drawableBreak.Expire(); | ||
} | ||
|
||
// Add remaining ones | ||
for (int i = 0; i < breaks.Count; i++) | ||
{ | ||
var breakPeriod = breaks[i]; | ||
|
||
if (!shouldBeVisible(breakPeriod)) | ||
continue; | ||
|
||
bool alreadyVisible = false; | ||
|
||
foreach (var b in this) | ||
{ | ||
if (ReferenceEquals(b.Break, breakPeriod)) | ||
{ | ||
alreadyVisible = true; | ||
break; | ||
} | ||
} | ||
|
||
if (alreadyVisible) | ||
continue; | ||
|
||
Add(new TimelineBreak(breakPeriod)); | ||
} | ||
} | ||
|
||
private bool shouldBeVisible(BreakPeriod breakPeriod) => breakPeriod.EndTime >= visibleRange.min && breakPeriod.StartTime <= visibleRange.max; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters