1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-20 14:40:19 +08:00
Files
osu-lazer/osu.Game.Rulesets.Osu/Edit/PolygonGenerationPopover.cs
T
De4n e05b6f44b9 Update editor slider controls to new design (#36346)
(partially) Closes: #36233
Surpasses: #36244 

This PR meant to be one of the last steps that finally make editor use
the new forms. Initially it meant to only change one
SliderWithTextBoxInput in "Effects section" in timing screen, however
soon after it was obvious that there's many other places that still
using it. This currently won't affect
IndeterminateSliderWithTextBoxInput that is being used in hitsounds, for
example, since I think it needs more consideration.
Anyways, with this PR, SliderWithTextBoxInput, will no longer be used at
all, as it's going to be replaced with modern FormSliderBar

Comparison:
|master|this PR|
|:---:|:---:|
|<img width="510" height="316"
alt="532203751-eb965923-d3a8-441d-a7c8-5c364a6328ad"
src="https://github.com/user-attachments/assets/268b45b8-e235-494f-91a5-d00db057dba8"
/>|<img width="540" height="321"
alt="535466527-3a700a8b-bc3c-4610-998f-a4e55ee03eed"
src="https://github.com/user-attachments/assets/20cd4b58-b0bd-49bc-8c48-7de5cf8556b3"
/>|
|<img width="694" height="639"
alt="534509844-f00e4da4-53c4-45e8-80ea-1be62da6c83b"
src="https://github.com/user-attachments/assets/398c4484-a867-4df1-9de3-0940aa748a01"
/>|<img width="720" height="433" alt="изображение"
src="https://github.com/user-attachments/assets/b6359443-a224-4a55-b171-07e8f013cf46"
/>|
|<img width="715" height="353"
alt="534509421-a6ac950f-16e8-4a16-bca6-1a781f82135f"
src="https://github.com/user-attachments/assets/4854312b-772f-4b81-a800-89e58d4c715d"
/>|<img width="710" height="296" alt="изображение"
src="https://github.com/user-attachments/assets/a7fed53e-e006-4285-92c9-bb84cb603f60"
/>|
|<img width="717" height="374"
alt="534509478-80222623-7766-481d-8682-088276d415ee"
src="https://github.com/user-attachments/assets/8143b6dc-4599-45d5-bd3b-f059caf3d93d"
/>|<img width="718" height="328" alt="изображение"
src="https://github.com/user-attachments/assets/bffa04de-983c-45ae-a1ec-373701ea0e49"
/>|
|<img width="702" height="446"
alt="534509935-58954060-7ac1-4392-8754-a58f909e86aa"
src="https://github.com/user-attachments/assets/2bb67a2d-3f57-42a1-96ce-b30b4891e1a4"
/>|<img width="722" height="386" alt="изображение"
src="https://github.com/user-attachments/assets/01b7fff4-7f31-4aac-90c9-353b15f4964e"
/>|
2026-01-16 20:08:52 +09:00

236 lines
9.2 KiB
C#

// 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;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
namespace osu.Game.Rulesets.Osu.Edit
{
public partial class PolygonGenerationPopover : OsuPopover
{
private FormSliderBar<double> distanceSnapInput { get; set; } = null!;
private FormSliderBar<int> offsetAngleInput { get; set; } = null!;
private FormSliderBar<int> repeatCountInput { get; set; } = null!;
private FormSliderBar<int> pointInput { get; set; } = null!;
private RoundedButton commitButton = null!;
private readonly List<HitCircle> insertedCircles = new List<HitCircle>();
private bool began;
private bool committed;
[Resolved]
private IBeatSnapProvider beatSnapProvider { get; set; } = null!;
[Resolved]
private EditorClock editorClock { get; set; } = null!;
[Resolved]
private EditorBeatmap editorBeatmap { get; set; } = null!;
[Resolved]
private IEditorChangeHandler? changeHandler { get; set; }
[Resolved]
private HitObjectComposer composer { get; set; } = null!;
private Bindable<TernaryState> newComboState = null!;
[BackgroundDependencyLoader]
private void load()
{
var selectionHandler = (EditorSelectionHandler)composer.BlueprintContainer.SelectionHandler;
newComboState = selectionHandler.SelectionNewComboState.GetBoundCopy();
AllowableAnchors = new[] { Anchor.CentreLeft, Anchor.CentreRight };
Child = new FillFlowContainer
{
Width = 220,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(5),
Children = new Drawable[]
{
distanceSnapInput = new FormSliderBar<double>
{
Caption = "Distance snap",
Current = new BindableNumber<double>(1)
{
MinValue = 0.1,
MaxValue = 6,
Precision = 0.1,
Value = ((OsuHitObjectComposer)composer).DistanceSnapProvider.DistanceSpacingMultiplier.Value,
},
TabbableContentContainer = this
},
offsetAngleInput = new FormSliderBar<int>
{
Caption = "Offset angle",
Current = new BindableNumber<int>
{
MinValue = 0,
MaxValue = 180,
Precision = 1
},
TabbableContentContainer = this
},
repeatCountInput = new FormSliderBar<int>
{
Caption = "Repeats",
Current = new BindableNumber<int>(1)
{
MinValue = 1,
MaxValue = 10,
Precision = 1
},
TabbableContentContainer = this
},
pointInput = new FormSliderBar<int>
{
Caption = "Vertices",
Current = new BindableNumber<int>(3)
{
MinValue = 3,
MaxValue = 32,
Precision = 1,
},
TabbableContentContainer = this
},
commitButton = new RoundedButton
{
RelativeSizeAxes = Axes.X,
Text = "Create",
Action = commit
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
changeHandler?.BeginChange();
began = true;
distanceSnapInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
offsetAngleInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
repeatCountInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
pointInput.Current.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
newComboState.BindValueChanged(_ => Scheduler.AddOnce(tryCreatePolygon));
tryCreatePolygon();
}
private void tryCreatePolygon()
{
double startTime = beatSnapProvider.SnapTime(editorClock.CurrentTime);
TimingControlPoint timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(startTime);
double timeSpacing = timingPoint.BeatLength / editorBeatmap.BeatDivisor;
IHasSliderVelocity lastWithSliderVelocity = editorBeatmap.HitObjects.Where(ho => ho.GetEndTime() <= startTime).OfType<IHasSliderVelocity>().LastOrDefault() ?? new Slider();
double velocity = OsuHitObject.BASE_SCORING_DISTANCE * editorBeatmap.Difficulty.SliderMultiplier
/ LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(lastWithSliderVelocity, timingPoint, OsuRuleset.SHORT_NAME);
double length = distanceSnapInput.Current.Value * velocity * timeSpacing;
float polygonRadius = (float)(length / (2 * Math.Sin(double.Pi / pointInput.Current.Value)));
int totalPoints = pointInput.Current.Value * repeatCountInput.Current.Value;
if (insertedCircles.Count > totalPoints)
{
editorBeatmap.RemoveRange(insertedCircles.GetRange(totalPoints, insertedCircles.Count - totalPoints));
insertedCircles.RemoveRange(totalPoints, insertedCircles.Count - totalPoints);
}
var newlyAdded = new List<HitCircle>();
for (int i = 0; i < totalPoints; ++i)
{
float angle = float.DegreesToRadians(offsetAngleInput.Current.Value) + (i + 1) * (2 * float.Pi / pointInput.Current.Value);
var position = OsuPlayfield.BASE_SIZE / 2 + new Vector2(polygonRadius * float.Cos(angle), polygonRadius * float.Sin(angle));
bool newCombo = i == 0 && newComboState.Value == TernaryState.True;
HitCircle circle;
if (i < insertedCircles.Count)
{
circle = insertedCircles[i];
circle.Position = position;
circle.StartTime = startTime;
circle.NewCombo = newCombo;
editorBeatmap.Update(circle);
}
else
{
circle = new HitCircle
{
Position = position,
StartTime = startTime,
NewCombo = newCombo,
};
newlyAdded.Add(circle);
// TODO: probably ensure samples also follow current ternary status (not trivial)
circle.Samples.Add(circle.CreateHitSampleInfo());
}
if (position.X < 0 || position.Y < 0 || position.X > OsuPlayfield.BASE_SIZE.X || position.Y > OsuPlayfield.BASE_SIZE.Y)
{
commitButton.Enabled.Value = false;
editorBeatmap.RemoveRange(insertedCircles);
insertedCircles.Clear();
return;
}
startTime = beatSnapProvider.SnapTime(startTime + timeSpacing);
}
var previousNewComboState = newComboState.Value;
insertedCircles.AddRange(newlyAdded);
editorBeatmap.AddRange(newlyAdded);
// When adding new hitObjects, newCombo state will get reset to false when no objects are selected.
// Since this is the case when this popover is showing, we need to restore the previous newCombo state
newComboState.Value = previousNewComboState;
commitButton.Enabled.Value = true;
}
private void commit()
{
changeHandler?.EndChange();
committed = true;
Hide();
}
protected override void PopOut()
{
base.PopOut();
if (began && !committed)
{
editorBeatmap.RemoveRange(insertedCircles);
changeHandler?.EndChange();
}
}
}
}