Merge branch 'master' into replay-pausing-support

This commit is contained in:
Dan Balasescu
2020-11-27 00:09:40 +09:00
committed by GitHub
66 changed files with 1213 additions and 541 deletions

View File

@ -187,7 +187,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (e.Button == MouseButton.Right)
return false;
if (movementBlueprint != null)
if (movementBlueprints != null)
{
isDraggingBlueprint = true;
changeHandler?.BeginChange();
@ -299,7 +299,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
SelectionBlueprints.Remove(blueprint);
if (movementBlueprint == blueprint)
if (movementBlueprints?.Contains(blueprint) == true)
finishSelectionMovement();
OnBlueprintRemoved(hitObject);
@ -424,8 +424,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region Selection Movement
private Vector2? movementBlueprintOriginalPosition;
private SelectionBlueprint movementBlueprint;
private Vector2[] movementBlueprintOriginalPositions;
private SelectionBlueprint[] movementBlueprints;
private bool isDraggingBlueprint;
/// <summary>
@ -442,8 +442,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
return;
// 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.HitObject.StartTime).First();
movementBlueprintOriginalPosition = movementBlueprint.ScreenSpaceSelectionPoint; // todo: unsure if correct
movementBlueprints = SelectionHandler.SelectedBlueprints.OrderBy(b => b.HitObject.StartTime).ToArray();
movementBlueprintOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSelectionPoint).ToArray();
}
/// <summary>
@ -453,30 +453,47 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <returns>Whether a movement was active.</returns>
private bool moveCurrentSelection(DragEvent e)
{
if (movementBlueprint == null)
if (movementBlueprints == null)
return false;
if (snapProvider == null)
return true;
Debug.Assert(movementBlueprintOriginalPosition != null);
Debug.Assert(movementBlueprintOriginalPositions != null);
HitObject draggedObject = movementBlueprint.HitObject;
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
// check for positional snap for every object in selection (for things like object-object snapping)
for (var i = 0; i < movementBlueprintOriginalPositions.Length; i++)
{
var testPosition = movementBlueprintOriginalPositions[i] + distanceTravelled;
var positionalResult = snapProvider.SnapScreenSpacePositionToValidPosition(testPosition);
if (positionalResult.ScreenSpacePosition == testPosition) continue;
// attempt to move the objects, and abort any time based snapping if we can.
if (SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprints[i], positionalResult.ScreenSpacePosition)))
return true;
}
// if no positional snapping could be performed, try unrestricted snapping from the earliest
// hitobject in the selection.
// The final movement position, relative to movementBlueprintOriginalPosition.
Vector2 movePosition = movementBlueprintOriginalPosition.Value + e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
Vector2 movePosition = movementBlueprintOriginalPositions.First() + distanceTravelled;
// Retrieve a snapped position.
var result = snapProvider.SnapScreenSpacePositionToValidTime(movePosition);
// Move the hitobjects.
if (!SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprint, result.ScreenSpacePosition)))
if (!SelectionHandler.HandleMovement(new MoveSelectionEvent(movementBlueprints.First(), result.ScreenSpacePosition)))
return true;
if (result.Time.HasValue)
{
// Apply the start time at the newly snapped-to position
double offset = result.Time.Value - draggedObject.StartTime;
double offset = result.Time.Value - movementBlueprints.First().HitObject.StartTime;
foreach (HitObject obj in Beatmap.SelectedHitObjects)
{
@ -494,11 +511,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <returns>Whether a movement was active.</returns>
private bool finishSelectionMovement()
{
if (movementBlueprint == null)
if (movementBlueprints == null)
return false;
movementBlueprintOriginalPosition = null;
movementBlueprint = null;
movementBlueprintOriginalPositions = null;
movementBlueprints = null;
return true;
}

View File

@ -224,6 +224,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
/// </summary>
public double VisibleRange => track.Length / Zoom;
public SnapResult SnapScreenSpacePositionToValidPosition(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, null);
public SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) =>
new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition))));

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -19,8 +18,8 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@ -28,32 +27,26 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
public class TimelineHitObjectBlueprint : SelectionBlueprint
{
private readonly Circle circle;
private const float thickness = 5;
private const float shadow_radius = 5;
private const float circle_size = 24;
public Action<DragEvent> OnDragHandled;
[UsedImplicitly]
private readonly Bindable<double> startTime;
public Action<DragEvent> OnDragHandled;
private Bindable<int> indexInCurrentComboBindable;
private Bindable<int> comboIndexBindable;
private readonly Circle circle;
private readonly DragBar dragBar;
private readonly List<Container> shadowComponents = new List<Container>();
private DrawableHitObject drawableHitObject;
private Bindable<Color4> comboColour;
private readonly Container mainComponents;
private readonly OsuSpriteText comboIndexText;
private Bindable<int> comboIndex;
private const float thickness = 5;
private const float shadow_radius = 5;
private const float circle_size = 24;
[Resolved]
private ISkinSource skin { get; set; }
public TimelineHitObjectBlueprint(HitObject hitObject)
: base(hitObject)
@ -152,46 +145,42 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
updateShadows();
}
[BackgroundDependencyLoader(true)]
private void load(HitObjectComposer composer)
{
if (composer != null)
{
// best effort to get the drawable representation for grabbing colour and what not.
drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject);
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (HitObject is IHasComboInformation comboInfo)
{
comboIndex = comboInfo.IndexInCurrentComboBindable.GetBoundCopy();
comboIndex.BindValueChanged(combo =>
{
comboIndexText.Text = (combo.NewValue + 1).ToString();
}, true);
indexInCurrentComboBindable = comboInfo.IndexInCurrentComboBindable.GetBoundCopy();
indexInCurrentComboBindable.BindValueChanged(_ => updateComboIndex(), true);
comboIndexBindable = comboInfo.ComboIndexBindable.GetBoundCopy();
comboIndexBindable.BindValueChanged(_ => updateComboColour(), true);
skin.SourceChanged += updateComboColour;
}
}
if (drawableHitObject != null)
{
comboColour = drawableHitObject.AccentColour.GetBoundCopy();
comboColour.BindValueChanged(colour =>
{
if (HitObject is IHasDuration)
mainComponents.Colour = ColourInfo.GradientHorizontal(drawableHitObject.AccentColour.Value, Color4.White);
else
mainComponents.Colour = drawableHitObject.AccentColour.Value;
private void updateComboIndex() => comboIndexText.Text = (indexInCurrentComboBindable.Value + 1).ToString();
var col = mainComponents.Colour.TopLeft.Linear;
float brightness = col.R + col.G + col.B;
private void updateComboColour()
{
if (!(HitObject is IHasComboInformation combo))
return;
// decide the combo index colour based on brightness?
comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White;
}, true);
}
var comboColours = skin.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value ?? Array.Empty<Color4>();
var comboColour = combo.GetComboColour(comboColours);
if (HitObject is IHasDuration)
mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, Color4.White);
else
mainComponents.Colour = comboColour;
var col = mainComponents.Colour.TopLeft.Linear;
float brightness = col.R + col.G + col.B;
// decide the combo index colour based on brightness?
comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White;
}
protected override void Update()

View File

@ -1,6 +1,7 @@
// 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.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -43,6 +44,21 @@ namespace osu.Game.Screens.Edit.Compose
if (ruleset == null || composer == null)
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
return wrapSkinnableContent(composer);
}
protected override Drawable CreateTimelineContent()
{
if (ruleset == null || composer == null)
return base.CreateTimelineContent();
return wrapSkinnableContent(new TimelineBlueprintContainer(composer));
}
private Drawable wrapSkinnableContent(Drawable content)
{
Debug.Assert(ruleset != null);
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
@ -51,9 +67,7 @@ namespace osu.Game.Screens.Edit.Compose
// load the skinning hierarchy first.
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(composer));
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(content));
}
protected override Drawable CreateTimelineContent() => composer == null ? base.CreateTimelineContent() : new TimelineBlueprintContainer(composer);
}
}

View File

@ -375,6 +375,9 @@ namespace osu.Game.Screens.Edit
protected override bool OnScroll(ScrollEvent e)
{
if (e.ControlPressed || e.AltPressed || e.SuperPressed)
return false;
const double precision = 1;
double scrollComponent = e.ScrollDelta.X + e.ScrollDelta.Y;

View File

@ -3,10 +3,12 @@
using System;
using System.IO;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
@ -21,6 +23,9 @@ namespace osu.Game.Screens.Edit.Setup
private readonly IBindable<FileInfo> currentFile = new Bindable<FileInfo>();
[Resolved]
private SectionsContainer<SetupSection> sectionsContainer { get; set; }
public FileChooserLabelledTextBox()
{
currentFile.BindValueChanged(onFileSelected);
@ -47,14 +52,16 @@ namespace osu.Game.Screens.Edit.Setup
public void DisplayFileChooser()
{
Target.Child = new FileSelector(validFileExtensions: ResourcesSection.AudioExtensions)
FileSelector fileSelector;
Target.Child = fileSelector = new FileSelector(validFileExtensions: ResourcesSection.AudioExtensions)
{
RelativeSizeAxes = Axes.X,
Height = 400,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
CurrentFile = { BindTarget = currentFile }
};
sectionsContainer.ScrollTo(fileSelector);
}
internal class FileChooserOsuTextBox : OsuTextBox