diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 21b3b38388..afe14de3ea 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -4,11 +4,9 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Threading; -using osu.Game.Overlays; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osuTK; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts @@ -26,7 +24,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts [BackgroundDependencyLoader] private void load() { - Add(marker = new MarkerVisualisation()); + Add(marker = new CentreMarker + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + Width = 10, + TriangleHeightRatio = 0.5f + }); } protected override bool OnDragStart(DragStartEvent e) => true; @@ -68,44 +73,5 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { // block base call so we don't clear our marker (can be reused on beatmap change). } - - private partial class MarkerVisualisation : CompositeDrawable - { - public MarkerVisualisation() - { - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - RelativePositionAxes = Axes.X; - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - InternalChildren = new Drawable[] - { - new Triangle - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - Scale = new Vector2(1, -1), - Size = new Vector2(10, 5), - }, - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(10, 5), - }, - new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Y, - Width = 1.4f, - EdgeSmoothness = new Vector2(1, 0) - } - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) => Colour = colours.Highlight1; - } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs index c63dfdfb55..145049e1dd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/CentreMarker.cs @@ -4,7 +4,10 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Game.Overlays; using osuTK; @@ -12,47 +15,118 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public partial class CentreMarker : CompositeDrawable { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) + public float TriangleHeightRatio { - const float triangle_width = 8; - const float bar_width = 2f; + get => triangles.TriangleHeightRatio; + set => triangles.TriangleHeightRatio = value; + } + private readonly VerticalTriangles triangles; + + public CentreMarker() + { RelativeSizeAxes = Axes.Y; - - Anchor = Anchor.TopCentre; - Origin = Anchor.TopCentre; - - Size = new Vector2(triangle_width, 1); - + Masking = true; InternalChildren = new Drawable[] { - new Circle + new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Width = bar_width, - Colour = colours.Colour2, + Width = 1.4f, + EdgeSmoothness = new Vector2(1, 0) }, - new Triangle + triangles = new VerticalTriangles { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangle_width, triangle_width * 0.8f), - Scale = new Vector2(1, -1), - EdgeSmoothness = new Vector2(1, 0), - Colour = colours.Colour2, - }, - new Triangle - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Size = new Vector2(triangle_width, triangle_width * 0.8f), - Scale = new Vector2(1, 1), - Colour = colours.Colour2, - }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + EdgeSmoothness = Vector2.One + } }; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colours) => Colour = colours.Highlight1; + + /// + /// Triangles drawn at the top and bottom of . + /// + /// + /// Since framework-side triangles don't support antialiasing we are using custom implementation involving rotated smoothened boxes to avoid + /// mismatch in antialiasing between top and bottom triangles when drawable moves across the screen. + /// To "trim" boxes we must enable masking at the top level. + /// + private partial class VerticalTriangles : Sprite + { + private float triangleHeightRatio = 1f; + + public float TriangleHeightRatio + { + get => triangleHeightRatio; + set + { + triangleHeightRatio = value; + Invalidate(Invalidation.DrawNode); + } + } + + [BackgroundDependencyLoader] + private void load(IRenderer renderer) + { + Texture = renderer.WhitePixel; + } + + protected override DrawNode CreateDrawNode() => new VerticalTrianglesDrawNode(this); + + private class VerticalTrianglesDrawNode : SpriteDrawNode + { + public new VerticalTriangles Source => (VerticalTriangles)base.Source; + + public VerticalTrianglesDrawNode(VerticalTriangles source) + : base(source) + { + } + + private float triangleScreenSpaceHeight; + + public override void ApplyState() + { + base.ApplyState(); + + triangleScreenSpaceHeight = ScreenSpaceDrawQuad.Width * Source.TriangleHeightRatio; + } + + protected override void Blit(IRenderer renderer) + { + if (triangleScreenSpaceHeight == 0 || DrawRectangle.Width == 0 || DrawRectangle.Height == 0) + return; + + Vector2 inflation = new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / (DrawRectangle.Width * Source.TriangleHeightRatio)); + + Quad topTriangle = new Quad + ( + ScreenSpaceDrawQuad.TopLeft, + ScreenSpaceDrawQuad.TopLeft + new Vector2(ScreenSpaceDrawQuad.Width * 0.5f, -triangleScreenSpaceHeight), + ScreenSpaceDrawQuad.TopLeft + new Vector2(ScreenSpaceDrawQuad.Width * 0.5f, triangleScreenSpaceHeight), + ScreenSpaceDrawQuad.TopRight + ); + + Quad bottomTriangle = new Quad + ( + ScreenSpaceDrawQuad.BottomLeft, + ScreenSpaceDrawQuad.BottomLeft + new Vector2(ScreenSpaceDrawQuad.Width * 0.5f, -triangleScreenSpaceHeight), + ScreenSpaceDrawQuad.BottomLeft + new Vector2(ScreenSpaceDrawQuad.Width * 0.5f, triangleScreenSpaceHeight), + ScreenSpaceDrawQuad.BottomRight + ); + + renderer.DrawQuad(Texture, topTriangle, DrawColourInfo.Colour, inflationPercentage: inflation); + renderer.DrawQuad(Texture, bottomTriangle, DrawColourInfo.Colour, inflationPercentage: inflation); + } + + protected override bool CanDrawOpaqueInterior => false; + } + } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index cbf49e62e7..cbafea7600 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -107,7 +107,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline CentreMarker centreMarker; // We don't want the centre marker to scroll - AddInternal(centreMarker = new CentreMarker()); + AddInternal(centreMarker = new CentreMarker + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 8, + TriangleHeightRatio = 0.8f, + Colour = colourProvider.Colour2 + }); AddRange(new Drawable[] {