mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 23:12:56 +08:00
Merge remote-tracking branch 'upstream/master' into adjustable-double-time
This commit is contained in:
commit
e37526888c
20
osu.Desktop/app.manifest
Normal file
20
osu.Desktop/app.manifest
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
<applicationRequestMinimum>
|
||||
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||
</applicationRequestMinimum>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</asmv1:assembly>
|
@ -8,6 +8,7 @@
|
||||
<Title>osu!lazer</Title>
|
||||
<Product>osu!lazer</Product>
|
||||
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Version>0.0.0</Version>
|
||||
<FileVersion>0.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
@ -11,6 +11,9 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
{
|
||||
public class LegacyCursor : CompositeDrawable
|
||||
{
|
||||
private NonPlayfieldSprite cursor;
|
||||
private bool spin;
|
||||
|
||||
public LegacyCursor()
|
||||
{
|
||||
Size = new Vector2(50);
|
||||
@ -22,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new NonPlayfieldSprite
|
||||
@ -30,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
new NonPlayfieldSprite
|
||||
cursor = new NonPlayfieldSprite
|
||||
{
|
||||
Texture = skin.GetTexture("cursor"),
|
||||
Anchor = Anchor.Centre,
|
||||
@ -38,5 +43,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
if (spin)
|
||||
cursor.Spin(10000, RotationDirection.Clockwise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
return this.GetAnimation(component.LookupName, true, false);
|
||||
|
||||
case OsuSkinComponents.SliderFollowCircle:
|
||||
return this.GetAnimation("sliderfollowcircle", true, true);
|
||||
var followCircle = this.GetAnimation("sliderfollowcircle", true, true);
|
||||
if (followCircle != null)
|
||||
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
|
||||
followCircle.Scale *= 0.5f;
|
||||
return followCircle;
|
||||
|
||||
case OsuSkinComponents.SliderBall:
|
||||
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
|
||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
SliderPathRadius,
|
||||
AllowSliderBallTint,
|
||||
CursorExpand,
|
||||
CursorRotate
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -25,6 +27,7 @@ namespace osu.Game.Tests.Visual.Editor
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(TimelineArea),
|
||||
typeof(TimelineHitObjectDisplay),
|
||||
typeof(Timeline),
|
||||
typeof(TimelineButton),
|
||||
typeof(CentreMarker)
|
||||
@ -35,6 +38,8 @@ namespace osu.Game.Tests.Visual.Editor
|
||||
{
|
||||
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||
|
||||
var editorBeatmap = new EditorBeatmap<HitObject>((Beatmap<HitObject>)Beatmap.Value.Beatmap);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
@ -50,6 +55,7 @@ namespace osu.Game.Tests.Visual.Editor
|
||||
},
|
||||
new TimelineArea
|
||||
{
|
||||
Child = new TimelineHitObjectDisplay(editorBeatmap),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -19,6 +20,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens;
|
||||
@ -55,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
beforeLoadAction?.Invoke();
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(Beatmap.Value.Track);
|
||||
|
||||
InputManager.Child = container = new TestPlayerLoaderContainer(
|
||||
loader = new TestPlayerLoader(() =>
|
||||
{
|
||||
@ -63,6 +68,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When <see cref="PlayerLoader"/> exits early, it has to wait for the player load task
|
||||
/// to complete before running disposal on player. This previously caused an issue where mod
|
||||
/// speed adjustments were undone too late, causing cross-screen pollution.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEarlyExit()
|
||||
{
|
||||
AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() }));
|
||||
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||
AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1);
|
||||
AddStep("exit loader", () => loader.Exit());
|
||||
AddUntilStep("wait for not current", () => !loader.IsCurrentScreen());
|
||||
AddAssert("player did not load", () => !player.IsLoaded);
|
||||
AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true);
|
||||
AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBlockLoadViaMouseMovement()
|
||||
{
|
||||
@ -196,6 +219,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public new VisualSettings VisualSettings => base.VisualSettings;
|
||||
|
||||
public new Task DisposalTask => base.DisposalTask;
|
||||
|
||||
public TestPlayerLoader(Func<Player> createPlayer)
|
||||
: base(createPlayer)
|
||||
{
|
||||
|
@ -1,9 +1,12 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
@ -11,13 +14,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
[TestFixture]
|
||||
public class TestScenePopupDialog : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(PopupDialogOkButton),
|
||||
typeof(PopupDialogCancelButton),
|
||||
typeof(PopupDialogButton),
|
||||
typeof(DialogButton),
|
||||
};
|
||||
|
||||
public TestScenePopupDialog()
|
||||
{
|
||||
Add(new TestPopupDialog
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
|
||||
});
|
||||
AddStep("new popup", () =>
|
||||
Add(new TestPopupDialog
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
|
||||
}));
|
||||
}
|
||||
|
||||
private class TestPopupDialog : PopupDialog
|
||||
|
@ -20,9 +20,10 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class DialogButton : OsuClickableContainer
|
||||
{
|
||||
private const float idle_width = 0.8f;
|
||||
private const float hover_width = 0.9f;
|
||||
|
||||
private const float hover_duration = 500;
|
||||
private const float glow_fade_duration = 250;
|
||||
private const float click_duration = 200;
|
||||
|
||||
public readonly BindableBool Selected = new BindableBool();
|
||||
@ -99,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Width = 0.8f,
|
||||
Width = idle_width,
|
||||
Masking = true,
|
||||
MaskingSmoothness = 2,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
@ -199,26 +200,50 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
private bool clickAnimating;
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
|
||||
flash();
|
||||
|
||||
this.Delay(click_duration).Schedule(delegate
|
||||
var flash = new Box
|
||||
{
|
||||
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
|
||||
spriteText.Spacing = Vector2.Zero;
|
||||
glowContainer.FadeOut();
|
||||
});
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ButtonColour,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Alpha = 0.05f
|
||||
};
|
||||
|
||||
colourContainer.Add(flash);
|
||||
flash.FadeOutFromOne(100).Expire();
|
||||
|
||||
clickAnimating = true;
|
||||
colourContainer.ResizeWidthTo(colourContainer.Width * 1.05f, 100, Easing.OutQuint)
|
||||
.OnComplete(_ =>
|
||||
{
|
||||
clickAnimating = false;
|
||||
Selected.TriggerChange();
|
||||
});
|
||||
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
colourContainer.ResizeWidthTo(hover_width * 0.98f, click_duration * 4, Easing.OutQuad);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
if (Selected.Value)
|
||||
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
base.OnHover(e);
|
||||
|
||||
Selected.Value = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -230,36 +255,23 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private void selectionChanged(ValueChangedEvent<bool> args)
|
||||
{
|
||||
if (clickAnimating)
|
||||
return;
|
||||
|
||||
if (args.NewValue)
|
||||
{
|
||||
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
||||
colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic);
|
||||
glowContainer.FadeIn(glow_fade_duration, Easing.Out);
|
||||
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
||||
glowContainer.FadeIn(hover_duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic);
|
||||
colourContainer.ResizeWidthTo(idle_width, hover_duration, Easing.OutElastic);
|
||||
spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic);
|
||||
glowContainer.FadeOut(glow_fade_duration, Easing.Out);
|
||||
glowContainer.FadeOut(hover_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
private void flash()
|
||||
{
|
||||
var flash = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
|
||||
colourContainer.Add(flash);
|
||||
|
||||
flash.Colour = ButtonColour;
|
||||
flash.Blending = BlendingParameters.Additive;
|
||||
flash.Alpha = 0.3f;
|
||||
flash.FadeOutFromOne(click_duration);
|
||||
flash.Expire();
|
||||
}
|
||||
|
||||
private void updateGlow()
|
||||
{
|
||||
leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
PP = PP,
|
||||
Beatmap = Beatmap,
|
||||
RulesetID = OnlineRulesetID,
|
||||
Hash = "online", // todo: temporary?
|
||||
Hash = Replay ? "online" : string.Empty, // todo: temporary?
|
||||
Rank = Rank,
|
||||
Ruleset = ruleset,
|
||||
Mods = mods,
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
return;
|
||||
|
||||
for (int i = 0; i < value.Count; i++)
|
||||
backgroundFlow.Add(new ScoreTableRowBackground(i));
|
||||
backgroundFlow.Add(new ScoreTableRowBackground(i, value[i]));
|
||||
|
||||
Columns = createHeaders(value[0]);
|
||||
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
|
||||
|
@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
{
|
||||
@ -17,8 +19,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
private readonly Box hoveredBackground;
|
||||
private readonly Box background;
|
||||
|
||||
public ScoreTableRowBackground(int index)
|
||||
private readonly int index;
|
||||
private readonly ScoreInfo score;
|
||||
|
||||
public ScoreTableRowBackground(int index, ScoreInfo score)
|
||||
{
|
||||
this.index = index;
|
||||
this.score = score;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 25;
|
||||
|
||||
@ -37,16 +45,21 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
Alpha = 0,
|
||||
},
|
||||
};
|
||||
|
||||
if (index % 2 != 0)
|
||||
background.Alpha = 0;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OsuColour colours, IAPIProvider api)
|
||||
{
|
||||
hoveredBackground.Colour = colours.Gray4;
|
||||
background.Colour = colours.Gray3;
|
||||
var isOwnScore = api.LocalUser.Value.Id == score.UserID;
|
||||
|
||||
if (isOwnScore)
|
||||
background.Colour = colours.GreenDarker;
|
||||
else if (index % 2 == 0)
|
||||
background.Colour = colours.Gray3;
|
||||
else
|
||||
background.Alpha = 0;
|
||||
|
||||
hoveredBackground.Colour = isOwnScore ? colours.GreenDark : colours.Gray4;
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
|
@ -167,10 +167,6 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
switch (e.Button)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
SelectNext(1);
|
||||
break;
|
||||
|
||||
case MouseButton.Right:
|
||||
SelectNext(-1);
|
||||
break;
|
||||
@ -180,6 +176,13 @@ namespace osu.Game.Overlays.Mods
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
SelectNext(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select the next available mod in a specified direction.
|
||||
/// </summary>
|
||||
|
@ -3,16 +3,14 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -36,21 +34,21 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override bool DimMainContent => false; // dimming is handled by main overlay
|
||||
|
||||
private class BackButton : OsuClickableContainer, IKeyBindingHandler<GlobalAction>
|
||||
private class BackButton : OsuButton
|
||||
{
|
||||
private AspectContainer aspect;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Size = new Vector2(Sidebar.DEFAULT_WIDTH);
|
||||
Children = new Drawable[]
|
||||
|
||||
BackgroundColour = Color4.Black;
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
aspect = new AspectContainer
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
@ -71,34 +69,8 @@ namespace osu.Game.Overlays
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
aspect.ScaleTo(1, 1000, Easing.OutElastic);
|
||||
return base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case GlobalAction.Back:
|
||||
Click();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,9 @@ namespace osu.Game.Rulesets.Edit
|
||||
where TObject : HitObject
|
||||
{
|
||||
protected IRulesetConfigManager Config { get; private set; }
|
||||
protected EditorBeatmap<TObject> EditorBeatmap { get; private set; }
|
||||
|
||||
protected new EditorBeatmap<TObject> EditorBeatmap { get; private set; }
|
||||
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
[Resolved]
|
||||
@ -148,7 +150,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
beatmapProcessor = Ruleset.CreateBeatmapProcessor(playableBeatmap);
|
||||
|
||||
EditorBeatmap = new EditorBeatmap<TObject>(playableBeatmap);
|
||||
base.EditorBeatmap = EditorBeatmap = new EditorBeatmap<TObject>(playableBeatmap);
|
||||
EditorBeatmap.HitObjectAdded += addHitObject;
|
||||
EditorBeatmap.HitObjectRemoved += removeHitObject;
|
||||
EditorBeatmap.StartTimeChanged += UpdateHitObject;
|
||||
@ -333,6 +335,11 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public abstract IEnumerable<DrawableHitObject> HitObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An editor-specific beatmap, exposing mutation events.
|
||||
/// </summary>
|
||||
public IEditorBeatmap EditorBeatmap { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user's cursor is currently in an area of the <see cref="HitObjectComposer"/> that is valid for placement.
|
||||
/// </summary>
|
||||
|
@ -14,12 +14,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
/// <summary>
|
||||
/// Represents a part of the summary timeline..
|
||||
/// </summary>
|
||||
public abstract class TimelinePart : CompositeDrawable
|
||||
public abstract class TimelinePart : Container
|
||||
{
|
||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private readonly Container timeline;
|
||||
|
||||
protected override Container<Drawable> Content => timeline;
|
||||
|
||||
protected TimelinePart()
|
||||
{
|
||||
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
|
||||
@ -50,8 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
||||
}
|
||||
|
||||
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
|
||||
|
||||
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
|
||||
{
|
||||
timeline.Clear();
|
||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
Child = waveform = new WaveformGraph
|
||||
Add(waveform = new WaveformGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Blue.Opacity(0.2f),
|
||||
@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
MidColour = colours.BlueDark,
|
||||
HighColour = colours.BlueDarker,
|
||||
Depth = float.MaxValue
|
||||
};
|
||||
});
|
||||
|
||||
// We don't want the centre marker to scroll
|
||||
AddInternal(new CentreMarker());
|
||||
|
@ -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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -11,17 +12,18 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class TimelineArea : CompositeDrawable
|
||||
public class TimelineArea : Container
|
||||
{
|
||||
private readonly Timeline timeline;
|
||||
private readonly Timeline timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
public TimelineArea()
|
||||
protected override Container<Drawable> Content => timeline;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
|
||||
OsuCheckbox hitObjectsCheckbox;
|
||||
OsuCheckbox hitSoundsCheckbox;
|
||||
OsuCheckbox waveformCheckbox;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@ -60,8 +62,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
Spacing = new Vector2(0, 4),
|
||||
Children = new[]
|
||||
{
|
||||
hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hit objects" },
|
||||
hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hit sounds" },
|
||||
waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" }
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
}
|
||||
},
|
||||
timeline = new Timeline { RelativeSizeAxes = Axes.Both }
|
||||
timeline
|
||||
},
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
@ -119,8 +119,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
};
|
||||
|
||||
hitObjectsCheckbox.Current.Value = true;
|
||||
hitSoundsCheckbox.Current.Value = true;
|
||||
waveformCheckbox.Current.Value = true;
|
||||
|
||||
timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
||||
|
@ -0,0 +1,108 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
internal class TimelineHitObjectDisplay : TimelinePart
|
||||
{
|
||||
private IEditorBeatmap beatmap { get; }
|
||||
|
||||
public TimelineHitObjectDisplay(IEditorBeatmap beatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
foreach (var h in beatmap.HitObjects)
|
||||
add(h);
|
||||
|
||||
beatmap.HitObjectAdded += add;
|
||||
beatmap.HitObjectRemoved += remove;
|
||||
beatmap.StartTimeChanged += h =>
|
||||
{
|
||||
remove(h);
|
||||
add(h);
|
||||
};
|
||||
}
|
||||
|
||||
private void remove(HitObject h)
|
||||
{
|
||||
foreach (var d in Children.OfType<TimelineHitObjectRepresentation>().Where(c => c.HitObject == h))
|
||||
d.Expire();
|
||||
}
|
||||
|
||||
private void add(HitObject h)
|
||||
{
|
||||
var yOffset = Children.Count(d => d.X == h.StartTime);
|
||||
|
||||
Add(new TimelineHitObjectRepresentation(h) { Y = -yOffset * TimelineHitObjectRepresentation.THICKNESS });
|
||||
}
|
||||
|
||||
private class TimelineHitObjectRepresentation : CompositeDrawable
|
||||
{
|
||||
public const float THICKNESS = 3;
|
||||
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
public TimelineHitObjectRepresentation(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.CentreLeft;
|
||||
|
||||
Width = (float)(hitObject.GetEndTime() - hitObject.StartTime);
|
||||
|
||||
X = (float)hitObject.StartTime;
|
||||
|
||||
RelativePositionAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
if (hitObject is IHasEndTime)
|
||||
{
|
||||
AddInternal(new Container
|
||||
{
|
||||
CornerRadius = 2,
|
||||
Masking = true,
|
||||
Size = new Vector2(1, THICKNESS),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativePositionAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Colour = Color4.Black,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AddInternal(new Circle
|
||||
{
|
||||
Size = new Vector2(16),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
AlwaysPresent = true,
|
||||
Colour = Color4.White,
|
||||
BorderColour = Color4.Black,
|
||||
BorderThickness = THICKNESS,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,32 +3,35 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Compose
|
||||
{
|
||||
public class ComposeScreen : EditorScreenWithTimeline
|
||||
{
|
||||
private HitObjectComposer composer;
|
||||
|
||||
protected override Drawable CreateMainContent()
|
||||
{
|
||||
var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
|
||||
composer = ruleset?.CreateHitObjectComposer();
|
||||
|
||||
var composer = ruleset?.CreateHitObjectComposer();
|
||||
if (ruleset == null || composer == null)
|
||||
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
|
||||
|
||||
if (composer != null)
|
||||
{
|
||||
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
|
||||
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
|
||||
|
||||
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
||||
// full access to all skin sources.
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
|
||||
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
|
||||
// full access to all skin sources.
|
||||
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
|
||||
|
||||
// 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(ruleset.CreateHitObjectComposer()));
|
||||
}
|
||||
|
||||
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
|
||||
// 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));
|
||||
}
|
||||
|
||||
protected override Drawable CreateTimelineContent() => new TimelineHitObjectDisplay(composer.EditorBeatmap);
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||
|
||||
private TimelineArea timelineArea;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
@ -64,7 +66,7 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Right = 5 },
|
||||
Child = CreateTimeline()
|
||||
Child = timelineArea = CreateTimelineArea()
|
||||
},
|
||||
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
@ -97,11 +99,15 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
mainContent.Add(content);
|
||||
content.FadeInFromZero(300, Easing.OutQuint);
|
||||
|
||||
LoadComponentAsync(CreateTimelineContent(), timelineArea.Add);
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract Drawable CreateMainContent();
|
||||
|
||||
protected virtual Drawable CreateTimeline() => new TimelineArea { RelativeSizeAxes = Axes.Both };
|
||||
protected virtual Drawable CreateTimelineContent() => new Container();
|
||||
|
||||
protected TimelineArea CreateTimelineArea() => new TimelineArea { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,11 @@ namespace osu.Game.Screens.Edit
|
||||
/// Invoked when a <see cref="HitObject"/> is removed from this <see cref="IEditorBeatmap"/>.
|
||||
/// </summary>
|
||||
event Action<HitObject> HitObjectRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the start time of a <see cref="HitObject"/> in this <see cref="EditorBeatmap{T}"/> was changed.
|
||||
/// </summary>
|
||||
event Action<HitObject> StartTimeChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -214,10 +214,13 @@ namespace osu.Game.Screens.Play
|
||||
base.Update();
|
||||
}
|
||||
|
||||
private bool speedAdjustmentsApplied;
|
||||
|
||||
private void updateRate()
|
||||
{
|
||||
if (sourceClock == null) return;
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
sourceClock.ResetSpeedAdjustments();
|
||||
|
||||
if (sourceClock is IHasTempoAdjust tempo)
|
||||
@ -239,7 +242,12 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void removeSourceClockAdjustments()
|
||||
{
|
||||
sourceClock.ResetSpeedAdjustments();
|
||||
if (speedAdjustmentsApplied)
|
||||
{
|
||||
sourceClock.ResetSpeedAdjustments();
|
||||
speedAdjustmentsApplied = false;
|
||||
}
|
||||
|
||||
(sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected override bool PlayResumeSound => false;
|
||||
|
||||
private Task loadTask;
|
||||
protected Task LoadTask { get; private set; }
|
||||
|
||||
protected Task DisposalTask { get; private set; }
|
||||
|
||||
private InputManager inputManager;
|
||||
private IdleTracker idleTracker;
|
||||
@ -159,7 +161,7 @@ namespace osu.Game.Screens.Play
|
||||
player.RestartCount = restartCount;
|
||||
player.RestartRequested = restartRequested;
|
||||
|
||||
loadTask = LoadComponentAsync(player, _ => info.Loading = false);
|
||||
LoadTask = LoadComponentAsync(player, _ => info.Loading = false);
|
||||
}
|
||||
|
||||
private void contentIn()
|
||||
@ -250,7 +252,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
loadTask = null;
|
||||
LoadTask = null;
|
||||
|
||||
//By default, we want to load the player and never be returned to.
|
||||
//Note that this may change if the player we load requested a re-run.
|
||||
@ -301,7 +303,7 @@ namespace osu.Game.Screens.Play
|
||||
if (isDisposing)
|
||||
{
|
||||
// if the player never got pushed, we should explicitly dispose it.
|
||||
loadTask?.ContinueWith(_ => player.Dispose());
|
||||
DisposalTask = LoadTask?.ContinueWith(_ => player.Dispose());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,13 @@ namespace osu.Game.Skinning
|
||||
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
||||
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
|
||||
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
|
||||
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
public SkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: this(component, allowFallback, confineMode)
|
||||
{
|
||||
createDefault = defaultImplementation;
|
||||
}
|
||||
|
||||
protected SkinnableDrawable(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
protected SkinnableDrawable(ISkinComponent component, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(allowFallback)
|
||||
{
|
||||
this.component = component;
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Skinning
|
||||
[Resolved]
|
||||
private TextureStore textures { get; set; }
|
||||
|
||||
public SkinnableSprite(string textureName, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
public SkinnableSprite(string textureName, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(new SpriteComponent(textureName), allowFallback, confineMode)
|
||||
{
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
public class SkinnableSpriteText : SkinnableDrawable, IHasText
|
||||
{
|
||||
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
public SkinnableSpriteText(ISkinComponent component, Func<ISkinComponent, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.NoScaling)
|
||||
: base(component, defaultImplementation, allowFallback, confineMode)
|
||||
{
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user