mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Merge branch 'master' into replay-pausing-support
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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))));
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user