1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-26 16:12:54 +08:00

Merge pull request #28788 from bdach/timeline-redesign

Redesign editor timelines to address most frequent user complaints
This commit is contained in:
Bartłomiej Dach 2024-07-12 10:58:59 +02:00 committed by GitHub
commit 518dc17518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 125 additions and 127 deletions

View File

@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Editing
AddStep("set preview time to -1", () => EditorBeatmap.PreviewTime.Value = -1);
AddAssert("preview time line should not show", () => !Editor.ChildrenOfType<PreviewTimePart>().Single().Children.Any());
AddStep("set preview time to 1000", () => EditorBeatmap.PreviewTime.Value = 1000);
AddAssert("preview time line should show", () => Editor.ChildrenOfType<PreviewTimePart>().Single().Children.Single().Alpha == 1);
AddAssert("preview time line should show", () => Editor.ChildrenOfType<PreviewTimePart>().Single().Children.Single().Alpha, () => Is.GreaterThan(0));
}
}
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps.ControlPoints
set => ScrollSpeedBindable.Value = value;
}
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple;
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1;
/// <summary>
/// Whether this control point enables Kiai mode.

View File

@ -26,7 +26,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary>
private const double default_beat_length = 60000.0 / 60.0;
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1;
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Red2;
public static readonly TimingControlPoint DEFAULT = new TimingControlPoint
{

View File

@ -31,7 +31,7 @@ namespace osu.Game.Screens.Edit
RelativeSizeAxes = Axes.X;
Height = 60;
Height = 50;
Masking = true;
EdgeEffect = new EdgeEffectParameters
@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit
RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, 170),
new Dimension(GridSizeMode.Absolute, 150),
new Dimension(),
new Dimension(GridSizeMode.Absolute, 220),
new Dimension(GridSizeMode.Absolute, HitObjectComposer.TOOLBOX_CONTRACTED_SIZE_RIGHT),

View File

@ -46,8 +46,8 @@ namespace osu.Game.Screens.Edit.Components
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Scale = new Vector2(1.2f),
IconScale = new Vector2(1.2f),
Icon = FontAwesome.Regular.PlayCircle,
Action = togglePause,
},

View File

@ -37,8 +37,8 @@ namespace osu.Game.Screens.Edit.Components
{
Colour = colours.Orange1,
Anchor = Anchor.CentreLeft,
Font = OsuFont.Torus.With(size: 18, weight: FontWeight.SemiBold),
Position = new Vector2(2, 5),
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
Position = new Vector2(2, 4),
}
};
}
@ -87,7 +87,8 @@ namespace osu.Game.Screens.Edit.Components
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Top = 5,
Top = 4,
Bottom = 1,
Horizontal = -2
},
Child = new Container
@ -107,12 +108,13 @@ namespace osu.Game.Screens.Edit.Components
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Spacing = new Vector2(-2, 0),
Font = OsuFont.Torus.With(size: 36, fixedWidth: true, weight: FontWeight.Light),
Font = OsuFont.Torus.With(size: 32, fixedWidth: true, weight: FontWeight.Light),
},
inputTextBox = new OsuTextBox
inputTextBox = new TimestampTextBox
{
Width = 150,
Height = 36,
Position = new Vector2(-2, 4),
Width = 128,
Height = 26,
Alpha = 0,
CommitOnFocusLost = true,
},
@ -160,6 +162,14 @@ namespace osu.Game.Screens.Edit.Components
showingHoverLayer = shouldShowHoverLayer;
}
}
private partial class TimestampTextBox : OsuTextBox
{
public TimestampTextBox()
{
TextContainer.Height = 0.8f;
}
}
}
}
}

View File

@ -24,6 +24,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
public BookmarkVisualisation(double startTime)
: base(startTime)
{
Width = 2;
}
[BackgroundDependencyLoader]

View File

@ -64,7 +64,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
RelativeSizeAxes = Axes.Both;
InternalChild = new Circle { RelativeSizeAxes = Axes.Both };
Colour = colours.Gray7;
Colour = colours.Gray6;
}
}
}

View File

@ -16,9 +16,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
public ControlPointVisualisation(ControlPoint point)
{
Point = point;
Height = 0.25f;
Origin = Anchor.TopCentre;
Alpha = 0.3f;
Blending = BlendingParameters.Additive;
}
[BackgroundDependencyLoader]

View File

@ -3,12 +3,11 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
@ -91,14 +90,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
Width = (float)(nextControlPoint.Time - effect.Time);
AddInternal(new PointVisualisation
AddInternal(new Circle
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.TopLeft,
Width = 1,
Height = 0.25f,
Anchor = Anchor.BottomLeft,
Origin = Anchor.CentreLeft,
Height = 0.4f,
Depth = float.MaxValue,
Colour = effect.GetRepresentingColour(colours).Darken(0.5f),
Colour = effect.GetRepresentingColour(colours),
});
}
}

View File

@ -39,19 +39,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
switch (point)
{
case TimingControlPoint:
AddInternal(new ControlPointVisualisation(point) { Y = 0, });
break;
case DifficultyControlPoint:
AddInternal(new ControlPointVisualisation(point) { Y = 0.25f, });
break;
case SampleControlPoint:
AddInternal(new ControlPointVisualisation(point) { Y = 0.5f, });
AddInternal(new ControlPointVisualisation(point)
{
Y = -0.4f,
});
break;
case EffectControlPoint effect:
AddInternal(new EffectPointVisualisation(effect) { Y = 0.75f });
AddInternal(new EffectPointVisualisation(effect));
break;
}
}

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
@ -73,8 +73,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
public MarkerVisualisation()
{
const float box_height = 4;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
@ -82,32 +80,18 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
AutoSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14, box_height),
},
new Triangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Scale = new Vector2(1, -1),
Size = new Vector2(10, 5),
Y = box_height,
},
new Triangle
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(10, 5),
Y = -box_height,
},
new Box
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(14, box_height),
},
new Box
{
@ -121,7 +105,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Red1;
private void load(OverlayColourProvider colours) => Colour = colours.Highlight1;
}
}
}

View File

@ -32,6 +32,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
public PreviewTimeVisualisation(double time)
: base(time)
{
Alpha = 0.8f;
}
[BackgroundDependencyLoader]

View File

@ -23,29 +23,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
Children = new Drawable[]
{
new MarkerPart { RelativeSizeAxes = Axes.Both },
new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Y = -10,
Height = 0.35f
},
new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
new PreviewTimePart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
new Container
{
Name = "centre line",
@ -75,13 +52,35 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
},
}
},
new PreviewTimePart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.4f,
},
new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.4f
},
new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.10f
}
Height = 0.15f
},
new MarkerPart { RelativeSizeAxes = Axes.Both },
};
}
}

View File

@ -11,12 +11,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// </summary>
public partial class PointVisualisation : Circle
{
public readonly double StartTime;
public const float MAX_WIDTH = 4;
public PointVisualisation(double startTime)
: this()
{
X = (float)startTime;
StartTime = startTime;
}
public PointVisualisation()
@ -28,7 +31,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
Origin = Anchor.Centre;
Width = MAX_WIDTH;
Height = 0.75f;
Height = 0.4f;
}
}
}

View File

@ -150,7 +150,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
new TextFlowContainer(s => s.Font = s.Font.With(size: 14))
{
Padding = new MarginPadding { Horizontal = 15, Vertical = 8 },
Padding = new MarginPadding { Horizontal = 15, Vertical = 2 },
Text = "beat snap",
RelativeSizeAxes = Axes.X,
TextAnchor = Anchor.TopCentre,
@ -159,7 +159,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, 30),
new Dimension(GridSizeMode.Absolute, 40),
new Dimension(GridSizeMode.Absolute, 20),
new Dimension(GridSizeMode.Absolute, 15)
}
@ -526,7 +526,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
AlwaysDisplayed = alwaysDisplayed;
Divisor = divisor;
Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor);
Size = new Vector2(6f, 18) * BindableBeatDivisor.GetSize(divisor);
Alpha = alwaysDisplayed ? 1 : 0;
InternalChild = new Box { RelativeSizeAxes = Axes.Both };

View File

@ -2,10 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
@ -23,7 +25,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
{
InternalChildren = new Drawable[]
{
new Box
@ -32,21 +38,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = bar_width,
Blending = BlendingParameters.Additive,
Colour = ColourInfo.GradientVertical(colours.Colour2.Opacity(0.6f), colours.Colour2.Opacity(0)),
},
new Triangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(triangle_width, triangle_width * 0.8f),
Scale = new Vector2(1, -1)
Scale = new Vector2(1, -1),
Colour = colours.Colour2,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Red1;
}
}
}

View File

@ -33,6 +33,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public DifficultyPointPiece(HitObject hitObject)
{
HitObject = hitObject;
Y = -2.5f;
speedMultiplier = (hitObject as IHasSliderVelocity)?.SliderVelocityMultiplierBindable.GetBoundCopy();
}

View File

@ -35,6 +35,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public SamplePointPiece(HitObject hitObject)
{
HitObject = hitObject;
Y = 2.5f;
}
public bool AlternativeColor { get; init; }

View File

@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
[Cached]
public partial class Timeline : ZoomableScrollContainer, IPositionSnapProvider
{
private const float timeline_height = 72;
private const float timeline_height = 80;
private const float timeline_expanded_height = 94;
private readonly Drawable userContent;
@ -97,6 +97,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
// We don't want the centre marker to scroll
AddInternal(centreMarker = new CentreMarker());
ticks = new TimelineTickDisplay
{
Padding = new MarginPadding { Vertical = 2, },
};
AddRange(new Drawable[]
{
controlPoints = new TimelineControlPointDisplay
@ -104,6 +109,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
RelativeSizeAxes = Axes.X,
Height = timeline_expanded_height,
},
ticks,
mainContent = new Container
{
RelativeSizeAxes = Axes.X,
@ -119,8 +125,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
MidColour = colours.BlueDark,
HighColour = colours.BlueDarker,
},
ticks.CreateProxy(),
centreMarker.CreateProxy(),
ticks = new TimelineTickDisplay(),
new Box
{
Name = "zero marker",
@ -175,7 +181,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (visible.NewValue)
{
this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint);
mainContent.MoveToY(20, 200, Easing.OutQuint);
mainContent.MoveToY(15, 200, Easing.OutQuint);
// delay the fade in else masking looks weird.
controlPoints.Delay(180).FadeIn(400, Easing.OutQuint);

View File

@ -29,11 +29,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public Action<BreakPeriod>? OnDeleted { get; init; }
private Box background = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
public TimelineBreak(BreakPeriod b)
{
Break.Value = b;
@ -53,11 +48,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 5 },
Child = background = new Box
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray5,
Alpha = 0.7f,
Alpha = 0.9f,
},
},
new DragHandle(isStartHandle: true)
@ -88,23 +83,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateState();
base.OnHoverLost(e);
}
private void updateState()
{
background.FadeColour(IsHovered ? colours.Gray6 : colours.Gray5, 400, Easing.OutQuint);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => OnDeleted?.Invoke(Break.Value)),
@ -159,7 +137,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Anchor = Anchor,
Origin = Anchor,
RelativeSizeAxes = Axes.Y,
CornerRadius = 5,
CornerRadius = 4,
Masking = true,
Child = new Box
{
@ -249,8 +227,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (active)
colour = colour.Lighten(0.3f);
this.FadeColour(colour, 400, Easing.OutQuint);
handle.ResizeWidthTo(active ? 20 : 10, 400, Easing.OutElasticHalf);
handle.FadeColour(colour, 400, Easing.OutQuint);
handle.ResizeWidthTo(active ? 10 : 8, 400, Easing.OutElasticHalf);
}
}
}

View File

@ -24,7 +24,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Origin = Anchor.TopLeft;
X = (float)group.Time;
// offset visually to avoid overlapping timeline tick display.
X = (float)group.Time + 6;
}
protected override void LoadComplete()

View File

@ -519,7 +519,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
Type = EdgeEffectType.Shadow,
Radius = 5,
Colour = Color4.Black.Opacity(0.4f)
Colour = Color4.Black.Opacity(0.05f)
}
};
}

View File

@ -9,6 +9,7 @@ using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
@ -41,16 +42,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
RelativeSizeAxes = Axes.Both;
}
private readonly BindableBool showTimingChanges = new BindableBool(true);
private readonly Cached tickCache = new Cached();
[BackgroundDependencyLoader]
private void load()
private void load(OsuConfigManager configManager)
{
beatDivisor.BindValueChanged(_ => invalidateTicks());
if (changeHandler != null)
// currently this is the best way to handle any kind of timing changes.
changeHandler.OnStateChange += invalidateTicks;
configManager.BindWith(OsuSetting.EditorTimelineShowTimingChanges, showTimingChanges);
showTimingChanges.BindValueChanged(_ => invalidateTicks());
}
private void invalidateTicks()
@ -141,8 +147,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
var line = getNextUsableLine();
line.X = xPos;
line.Width = PointVisualisation.MAX_WIDTH * size.X;
line.Height = 0.9f * size.Y;
line.Anchor = Anchor.CentreLeft;
line.Origin = Anchor.Centre;
line.Height = 0.6f + size.Y * 0.4f;
line.Width = PointVisualisation.MAX_WIDTH * (0.6f + 0.4f * size.X);
line.Colour = colour;
}

View File

@ -75,7 +75,13 @@ namespace osu.Game.Screens.Edit.Compose
Children = new Drawable[]
{
new TimelineBlueprintContainer(composer),
new TimelineBreakDisplay { RelativeSizeAxes = Axes.Both, },
new TimelineBreakDisplay
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Height = 0.75f,
},
}
});
}

View File

@ -328,7 +328,7 @@ namespace osu.Game.Screens.Edit
{
Name = "Screen container",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 40, Bottom = 60 },
Padding = new MarginPadding { Top = 40, Bottom = 40 },
Child = screenContainer = new Container<EditorScreen>
{
RelativeSizeAxes = Axes.Both,