Skip to content

Commit

Permalink
Merge pull request #235 from stanriders/add-timeline
Browse files Browse the repository at this point in the history
Add timeline to the object inspection screen
  • Loading branch information
smoogipoo authored Dec 6, 2024
2 parents 5239181 + 12bc655 commit 976d07b
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
Expand All @@ -16,26 +17,29 @@
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK.Input;

namespace PerformanceCalculatorGUI.Screens.ObjectInspection
{
public partial class ObjectInspector : OsuFocusedOverlayContainer
[Cached(typeof(IBeatSnapProvider))]
public partial class ObjectInspector : OsuFocusedOverlayContainer, IBeatSnapProvider
{
private DependencyContainer dependencies;

protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));

[Cached]
private BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
private BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4);

[Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; }
Expand All @@ -55,14 +59,15 @@ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnl

private ObjectDifficultyValuesContainer difficultyValuesContainer;
private IBeatmap playableBeatmap;
private EditorBeatmap editorBeatmap;

protected override bool BlockNonPositionalInput => true;

protected override bool DimMainContent => false;

private const int bottom_bar_height = 50;

private const int side_bar_width = 220;
private const int timeline_height = 50;

public ObjectInspector(ProcessorWorkingBeatmap working)
{
Expand All @@ -77,16 +82,19 @@ private void load(OverlayColourProvider colourProvider)

playableBeatmap = processorBeatmap.GetPlayableBeatmap(ruleset.Value, modifiedMods);
processorBeatmap.LoadTrack();
modifiedMods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(processorBeatmap.Track));

clock = new EditorClock(playableBeatmap, beatDivisor);
clock.ChangeSource(processorBeatmap.Track);
dependencies.CacheAs(clock);

var editorBeatmap = new EditorBeatmap(playableBeatmap);
editorBeatmap = new EditorBeatmap(playableBeatmap);
dependencies.CacheAs(editorBeatmap);

beatmap.Value = processorBeatmap;

Timeline timeline;

AddInternal(new Container
{
Origin = Anchor.Centre,
Expand All @@ -108,6 +116,22 @@ private void load(OverlayColourProvider colourProvider)
Padding = new MarginPadding { Bottom = bottom_bar_height },
Width = side_bar_width
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = side_bar_width },
Children = new Drawable[]
{
new Box
{
Colour = colourProvider.Background6,
Alpha = 0.35f,
RelativeSizeAxes = Axes.Both
},
timeline = new Timeline(new TimelineBlueprintContainer())
}
},
rulesetContainer = new Container
{
Origin = Anchor.TopRight,
Expand Down Expand Up @@ -155,6 +179,9 @@ private void load(OverlayColourProvider colourProvider)
});
dependencies.CacheAs(difficultyValuesContainer);

timeline.Height = timeline_height;
timeline.Children.Last().Height = timeline_height; // set inner container height to 55 to fix centering

rulesetContainer.Add(ruleset.Value.ShortName switch
{
"osu" => new OsuPlayfieldAdjustmentContainer
Expand Down Expand Up @@ -269,5 +296,11 @@ protected override bool OnKeyDown(KeyDownEvent e)

return base.OnKeyDown(e);
}

public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);

public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);

public int BeatDivisor => beatDivisor.Value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Collections.Generic;
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.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
using static osu.Game.Screens.Edit.Compose.Components.Timeline.TimelineHitObjectBlueprint;

namespace PerformanceCalculatorGUI.Screens.ObjectInspection
{
internal partial class TimelineBlueprintContainer : EditorBlueprintContainer
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => false;

public TimelineBlueprintContainer()
: base(null)
{
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Height = 1f;
}

protected override void LoadComplete()
{
base.LoadComplete();

foreach (var obj in Beatmap.HitObjects)
AddBlueprintFor(obj);
}

protected override SelectionBlueprintContainer CreateSelectionBlueprintContainer() => new TimelineSelectionBlueprintContainer { RelativeSizeAxes = Axes.Both };

protected override bool OnDragStart(DragStartEvent e) => false;

protected override SelectionHandler<HitObject> CreateSelectionHandler() => new EmptySelectionHandler();

protected override SelectionBlueprint<HitObject> CreateBlueprintFor(HitObject item) => new TimelineHitObjectBlueprint(item);

protected sealed override DragBox CreateDragBox() => new EmptyDragBox();

protected override void SelectAll()
{
}

protected override void OnBlueprintSelected(SelectionBlueprint<HitObject> blueprint)
{
}

protected override void OnBlueprintDeselected(SelectionBlueprint<HitObject> blueprint)
{
}

protected partial class TimelineSelectionBlueprintContainer : SelectionBlueprintContainer
{
protected override Container<SelectionBlueprint<HitObject>> Content { get; }

public TimelineSelectionBlueprintContainer()
{
AddInternal(new TimelinePart<SelectionBlueprint<HitObject>>(Content = new HitObjectOrderedSelectionContainer { RelativeSizeAxes = Axes.Both }) { RelativeSizeAxes = Axes.Both });
}
}

private partial class EmptySelectionHandler : SelectionHandler<HitObject>
{
protected override void DeleteItems(IEnumerable<HitObject> items)
{
}
}

protected partial class EmptyDragBox : DragBox
{
public EmptyDragBox()
{
RelativeSizeAxes = Axes.Both;
Alpha = 0;
}

protected override Drawable CreateBox() => Empty();

public override void HandleDrag(MouseButtonEvent e)
{
}
}

public partial class TimelineHitObjectBlueprint : SelectionBlueprint<HitObject>
{
private const float circle_size = 32;

private readonly ExtendableCircle circle;

private readonly Container colouredComponents;
private readonly OsuSpriteText comboIndexText;

[Resolved]
private ISkinSource skin { get; set; } = null!;

[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;

public TimelineHitObjectBlueprint(HitObject item)
: base(item)
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;

X = (float)item.StartTimeBindable.Value;

RelativePositionAxes = Axes.X;

RelativeSizeAxes = Axes.X;
Height = circle_size;
Width = 1;

AddRangeInternal(new Drawable[]
{
circle = new ExtendableCircle
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Alpha = 0.75f
},
colouredComponents = new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
comboIndexText = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Y = -1,
Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular),
},
}
},
});
}

protected override void LoadComplete()
{
base.LoadComplete();

switch (Item)
{
case IHasComboInformation comboInfo:
comboIndexText.Text = (comboInfo.IndexInCurrentComboBindable.Value + 1).ToString();
break;
}

updateColour();
}

protected override void OnSelected()
{
}

protected override void OnDeselected()
{
}

private void updateColour()
{
Color4 colour;

switch (Item)
{
case IHasDisplayColour displayColour:
colour = displayColour.DisplayColour.Value;
break;

case IHasComboInformation combo:
colour = combo.GetComboColour(skin);
break;

default:
colour = colourProvider.Highlight1;
break;
}

if (Item is IHasDuration duration && duration.Duration > 0)
circle.Colour = ColourInfo.GradientHorizontal(colour, colour.Lighten(0.4f));
else
circle.Colour = colour;

var averageColour = Interpolation.ValueAt(0.5, circle.Colour.TopLeft, circle.Colour.TopRight, 0, 1);
colouredComponents.Colour = OsuColour.ForegroundTextColourFor(averageColour);
}

protected override bool ShouldBeConsideredForInput(Drawable child) => false;

public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => false;
}
}
}

0 comments on commit 976d07b

Please sign in to comment.