mirror of
https://github.com/ppy/osu.git
synced 2025-02-16 03:02:56 +08:00
Merge branch 'master' into multiplayer-leaderboard-user-mods-2
This commit is contained in:
commit
cd649f7d97
@ -21,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ppy.localisationanalyser.tools": {
|
"ppy.localisationanalyser.tools": {
|
||||||
"version": "2022.417.0",
|
"version": "2022.607.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"localisation"
|
"localisation"
|
||||||
]
|
]
|
||||||
|
@ -52,10 +52,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.527.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.527.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.530.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.605.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
<PackageReference Include="Realm" Version="10.11.2" />
|
<PackageReference Include="Realm" Version="10.14.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -14,11 +14,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
[TestCase(2.3449735700206298d, 151, "diffcalc-test")]
|
[TestCase(2.3449735700206298d, 242, "diffcalc-test")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(2.7879104989252959d, 151, "diffcalc-test")]
|
[TestCase(2.7879104989252959d, 242, "diffcalc-test")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new ManiaModDoubleTime());
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
foreach (var v in base.ToDatabaseAttributes())
|
foreach (var v in base.ToDatabaseAttributes())
|
||||||
yield return v;
|
yield return v;
|
||||||
|
|
||||||
// Todo: osu!mania doesn't output MaxCombo attribute for some reason.
|
yield return (ATTRIB_ID_MAX_COMBO, MaxCombo);
|
||||||
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
yield return (ATTRIB_ID_DIFFICULTY, StarRating);
|
||||||
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
yield return (ATTRIB_ID_GREAT_HIT_WINDOW, GreatHitWindow);
|
||||||
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
yield return (ATTRIB_ID_SCORE_MULTIPLIER, ScoreMultiplier);
|
||||||
@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
base.FromDatabaseAttributes(values);
|
base.FromDatabaseAttributes(values);
|
||||||
|
|
||||||
|
MaxCombo = (int)values[ATTRIB_ID_MAX_COMBO];
|
||||||
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
StarRating = values[ATTRIB_ID_DIFFICULTY];
|
||||||
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
GreatHitWindow = values[ATTRIB_ID_GREAT_HIT_WINDOW];
|
||||||
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
ScoreMultiplier = values[ATTRIB_ID_SCORE_MULTIPLIER];
|
||||||
|
@ -52,10 +52,18 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
|
// This is done the way it is to introduce fractional differences in order to match osu-stable for the time being.
|
||||||
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
|
GreatHitWindow = Math.Ceiling((int)(getHitWindow300(mods) * clockRate) / clockRate),
|
||||||
ScoreMultiplier = getScoreMultiplier(mods),
|
ScoreMultiplier = getScoreMultiplier(mods),
|
||||||
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
|
MaxCombo = beatmap.HitObjects.Sum(maxComboForObject)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int maxComboForObject(HitObject hitObject)
|
||||||
|
{
|
||||||
|
if (hitObject is HoldNote hold)
|
||||||
|
return 1 + (int)((hold.EndTime - hold.StartTime) / 100);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||||
{
|
{
|
||||||
var sortedObjects = beatmap.HitObjects.ToArray();
|
var sortedObjects = beatmap.HitObjects.ToArray();
|
||||||
|
@ -79,7 +79,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
Result = { BindTarget = SpinsPerMinute },
|
Result = { BindTarget = SpinsPerMinute },
|
||||||
},
|
},
|
||||||
ticks = new Container<DrawableSpinnerTick>(),
|
ticks = new Container<DrawableSpinnerTick>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
new AspectContainer
|
new AspectContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableSpinnerTick : DrawableOsuHitObject
|
public class DrawableSpinnerTick : DrawableOsuHitObject
|
||||||
@ -10,13 +12,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected DrawableSpinner DrawableSpinner => (DrawableSpinner)ParentHitObject;
|
protected DrawableSpinner DrawableSpinner => (DrawableSpinner)ParentHitObject;
|
||||||
|
|
||||||
public DrawableSpinnerTick()
|
public DrawableSpinnerTick()
|
||||||
: base(null)
|
: this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawableSpinnerTick(SpinnerTick spinnerTick)
|
public DrawableSpinnerTick(SpinnerTick spinnerTick)
|
||||||
: base(spinnerTick)
|
: base(spinnerTick)
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration;
|
protected override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration;
|
||||||
|
@ -69,8 +69,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
double startTime = StartTime + (float)(i + 1) / totalSpins * Duration;
|
double startTime = StartTime + (float)(i + 1) / totalSpins * Duration;
|
||||||
|
|
||||||
AddNested(i < SpinsRequired
|
AddNested(i < SpinsRequired
|
||||||
? new SpinnerTick { StartTime = startTime, Position = Position }
|
? new SpinnerTick { StartTime = startTime }
|
||||||
: new SpinnerBonusTick { StartTime = startTime, Position = Position });
|
: new SpinnerBonusTick { StartTime = startTime });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(source.FindProvider(s => s.GetTexture("spinner-top") != null) is DefaultLegacySkin))
|
var topProvider = source.FindProvider(s => s.GetTexture("spinner-top") != null);
|
||||||
|
|
||||||
|
if (topProvider is LegacySkinTransformer transformer && !(transformer.Skin is DefaultLegacySkin))
|
||||||
{
|
{
|
||||||
AddInternal(ApproachCircle = new Sprite
|
AddInternal(ApproachCircle = new Sprite
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -321,12 +322,14 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
private class ProxyContainer : LifetimeManagementContainer
|
private class ProxyContainer : LifetimeManagementContainer
|
||||||
{
|
{
|
||||||
public new MarginPadding Padding
|
|
||||||
{
|
|
||||||
set => base.Padding = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(Drawable proxy) => AddInternal(proxy);
|
public void Add(Drawable proxy) => AddInternal(proxy);
|
||||||
|
|
||||||
|
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds)
|
||||||
|
{
|
||||||
|
// DrawableHitObject disables masking.
|
||||||
|
// Hitobject content is proxied and unproxied based on hit status and the IsMaskedAway value could get stuck because of this.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,7 +710,7 @@ namespace osu.Game.Tests.Database
|
|||||||
|
|
||||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||||
|
|
||||||
realm.Realm.Write(() =>
|
await realm.Realm.WriteAsync(() =>
|
||||||
{
|
{
|
||||||
foreach (var b in imported.Beatmaps)
|
foreach (var b in imported.Beatmaps)
|
||||||
b.OnlineID = -1;
|
b.OnlineID = -1;
|
||||||
|
@ -59,12 +59,14 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
// No header shouldn't cause any change
|
// No header shouldn't cause any change
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame());
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame());
|
||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(1));
|
||||||
|
|
||||||
// Reset with a miss instead.
|
// Reset with a miss instead.
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
||||||
@ -74,6 +76,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
||||||
|
|
||||||
// Reset with no judged hit.
|
// Reset with no judged hit.
|
||||||
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
scoreProcessor.ResetFromReplayFrame(new OsuReplayFrame
|
||||||
@ -83,6 +86,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.Combo.Value, Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestJudgement : Judgement
|
private class TestJudgement : Judgement
|
||||||
|
@ -59,11 +59,13 @@ namespace osu.Game.Tests.Skins
|
|||||||
AddAssert("Check float parse lookup", () => requester.GetConfig<string, float>("FloatTest")?.Value == 1.1f);
|
AddAssert("Check float parse lookup", () => requester.GetConfig<string, float>("FloatTest")?.Value == 1.1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase("0", false)]
|
||||||
public void TestBoolLookup()
|
[TestCase("1", true)]
|
||||||
|
[TestCase("2", true)] // https://github.com/ppy/osu/issues/18579
|
||||||
|
public void TestBoolLookup(string originalValue, bool expectedParsedValue)
|
||||||
{
|
{
|
||||||
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["BoolTest"] = "1");
|
AddStep("Add config values", () => userSource.Configuration.ConfigDictionary["BoolTest"] = originalValue);
|
||||||
AddAssert("Check bool parse lookup", () => requester.GetConfig<string, bool>("BoolTest")?.Value == true);
|
AddAssert("Check bool parse lookup", () => requester.GetConfig<string, bool>("BoolTest")?.Value == expectedParsedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
48
osu.Game.Tests/Visual/Editing/TestSceneTapButton.cs
Normal file
48
osu.Game.Tests/Visual/Editing/TestSceneTapButton.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.Edit.Timing;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneTapButton : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private TapButton tapButton;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly OverlayColourProvider overlayColour = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
AddStep("create button", () =>
|
||||||
|
{
|
||||||
|
Child = tapButton = new TapButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(4),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
bool pressed = false;
|
||||||
|
|
||||||
|
AddRepeatStep("Press button", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(tapButton);
|
||||||
|
if (!pressed)
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
else
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
|
||||||
|
pressed = !pressed;
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Timing;
|
using osu.Game.Screens.Edit.Timing;
|
||||||
@ -77,34 +77,6 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
timingInfo.Text = $"offset: {selectedGroup.Value.Time:N2} bpm: {selectedGroup.Value.ControlPoints.OfType<TimingControlPoint>().First().BPM:N2}";
|
timingInfo.Text = $"offset: {selectedGroup.Value.Time:N2} bpm: {selectedGroup.Value.ControlPoints.OfType<TimingControlPoint>().First().BPM:N2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestNoop()
|
|
||||||
{
|
|
||||||
AddStep("do nothing", () => { });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestTapThenReset()
|
|
||||||
{
|
|
||||||
AddStep("click tap button", () =>
|
|
||||||
{
|
|
||||||
control.ChildrenOfType<RoundedButton>()
|
|
||||||
.Last()
|
|
||||||
.TriggerClick();
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for track playing", () => Clock.IsRunning);
|
|
||||||
|
|
||||||
AddStep("click reset button", () =>
|
|
||||||
{
|
|
||||||
control.ChildrenOfType<RoundedButton>()
|
|
||||||
.First()
|
|
||||||
.TriggerClick();
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for track stopped", () => !Clock.IsRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBasic()
|
public void TestBasic()
|
||||||
{
|
{
|
||||||
@ -115,7 +87,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
AddStep("click tap button", () =>
|
AddStep("click tap button", () =>
|
||||||
{
|
{
|
||||||
control.ChildrenOfType<RoundedButton>()
|
control.ChildrenOfType<OsuButton>()
|
||||||
.Last()
|
.Last()
|
||||||
.TriggerClick();
|
.TriggerClick();
|
||||||
});
|
});
|
||||||
@ -129,6 +101,28 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTapThenReset()
|
||||||
|
{
|
||||||
|
AddStep("click tap button", () =>
|
||||||
|
{
|
||||||
|
control.ChildrenOfType<OsuButton>()
|
||||||
|
.Last()
|
||||||
|
.TriggerClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track playing", () => Clock.IsRunning);
|
||||||
|
|
||||||
|
AddStep("click reset button", () =>
|
||||||
|
{
|
||||||
|
control.ChildrenOfType<OsuButton>()
|
||||||
|
.First()
|
||||||
|
.TriggerClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track stopped", () => !Clock.IsRunning);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
Beatmap.Disabled = false;
|
Beatmap.Disabled = false;
|
||||||
|
48
osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs
Normal file
48
osu.Game.Tests/Visual/Editing/TestSceneTimelineZoom.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneTimelineZoom : TimelineTestScene
|
||||||
|
{
|
||||||
|
public override Drawable CreateTestComponent() => Empty();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleRangeUpdatesOnZoomChange()
|
||||||
|
{
|
||||||
|
double initialVisibleRange = 0;
|
||||||
|
|
||||||
|
AddStep("reset zoom", () => TimelineArea.Timeline.Zoom = 100);
|
||||||
|
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
||||||
|
|
||||||
|
AddStep("scale zoom", () => TimelineArea.Timeline.Zoom = 200);
|
||||||
|
AddAssert("range halved", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange / 2, 1));
|
||||||
|
AddStep("descale zoom", () => TimelineArea.Timeline.Zoom = 50);
|
||||||
|
AddAssert("range doubled", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange * 2, 1));
|
||||||
|
|
||||||
|
AddStep("restore zoom", () => TimelineArea.Timeline.Zoom = 100);
|
||||||
|
AddAssert("range restored", () => Precision.AlmostEquals(TimelineArea.Timeline.VisibleRange, initialVisibleRange, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestVisibleRangeConstantOnSizeChange()
|
||||||
|
{
|
||||||
|
double initialVisibleRange = 0;
|
||||||
|
|
||||||
|
AddStep("reset timeline size", () => TimelineArea.Timeline.Width = 1);
|
||||||
|
AddStep("get initial range", () => initialVisibleRange = TimelineArea.Timeline.VisibleRange);
|
||||||
|
|
||||||
|
AddStep("scale timeline size", () => TimelineArea.Timeline.Width = 2);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
AddStep("descale timeline size", () => TimelineArea.Timeline.Width = 0.5f);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
|
||||||
|
AddStep("restore timeline size", () => TimelineArea.Timeline.Width = 1);
|
||||||
|
AddAssert("same range", () => TimelineArea.Timeline.VisibleRange == initialVisibleRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(30)
|
Colour = OsuColour.Gray(30)
|
||||||
},
|
},
|
||||||
scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
|
scrollContainer = new ZoomableScrollContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new MenuCursor()
|
new MenuCursor()
|
||||||
@ -62,7 +67,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestWidthInitialization()
|
public void TestWidthInitialization()
|
||||||
{
|
{
|
||||||
AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0);
|
AddAssert("Inner container width was initialized", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestWidthUpdatesOnDrawSizeChanges()
|
||||||
|
{
|
||||||
|
AddStep("Shrink scroll container", () => scrollContainer.Width = 0.5f);
|
||||||
|
AddAssert("Scroll container width shrunk", () => scrollContainer.DrawWidth == scrollContainer.Parent.DrawWidth / 2);
|
||||||
|
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -178,6 +179,36 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddAssert("1 notification fired", () => testContainer.NotificationOverlay.UnreadCount.Value == 1);
|
AddAssert("1 notification fired", () => testContainer.NotificationOverlay.UnreadCount.Value == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that <see cref="MessageNotifier"/> handles channels which have not been or could not be resolved (i.e. <see cref="Channel.Id"/> = 0).
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestSendInUnresolvedChannel()
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
Channel unresolved = null;
|
||||||
|
|
||||||
|
AddRepeatStep("join unresolved channels", () => testContainer.ChannelManager.JoinChannel(unresolved = new Channel(new APIUser
|
||||||
|
{
|
||||||
|
Id = 100 + i,
|
||||||
|
Username = $"Foreign #{i++}",
|
||||||
|
})), 5);
|
||||||
|
|
||||||
|
AddStep("send message in unresolved channel", () =>
|
||||||
|
{
|
||||||
|
Debug.Assert(unresolved.Id == 0);
|
||||||
|
|
||||||
|
unresolved.AddLocalEcho(new LocalEchoMessage
|
||||||
|
{
|
||||||
|
Sender = API.LocalUser.Value,
|
||||||
|
ChannelId = unresolved.Id,
|
||||||
|
Content = "Some message",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("no notifications fired", () => testContainer.NotificationOverlay.UnreadCount.Value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
private void receiveMessage(APIUser sender, Channel channel, string content) => channel.AddNewMessages(createMessage(sender, channel, content));
|
private void receiveMessage(APIUser sender, Channel channel, string content) => channel.AddNewMessages(createMessage(sender, channel, content));
|
||||||
|
|
||||||
private Message createMessage(APIUser sender, Channel channel, string content) => new Message(messageIdCounter++)
|
private Message createMessage(APIUser sender, Channel channel, string content) => new Message(messageIdCounter++)
|
||||||
|
@ -128,11 +128,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
AddAssert("Ensure no adjacent day separators", () =>
|
AddAssert("Ensure no adjacent day separators", () =>
|
||||||
{
|
{
|
||||||
var indices = chatDisplay.FillFlow.OfType<DrawableChannel.DaySeparator>().Select(ds => chatDisplay.FillFlow.IndexOf(ds));
|
var indices = chatDisplay.FillFlow.OfType<DaySeparator>().Select(ds => chatDisplay.FillFlow.IndexOf(ds));
|
||||||
|
|
||||||
foreach (int i in indices)
|
foreach (int i in indices)
|
||||||
{
|
{
|
||||||
if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator)
|
if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DaySeparator)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
beatmap.Metadata.Author = author;
|
beatmap.Metadata.Author = author;
|
||||||
beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title";
|
beatmap.Metadata.Title = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap title";
|
||||||
beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist";
|
beatmap.Metadata.Artist = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong beatmap artist";
|
||||||
|
beatmap.DifficultyName = "Verrrrrrrrrrrrrrrrrrry looooooooooooooooooooooooong difficulty name";
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
@ -319,6 +319,15 @@ namespace osu.Game.Beatmaps
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteAllVideos()
|
||||||
|
{
|
||||||
|
realm.Write(r =>
|
||||||
|
{
|
||||||
|
var items = r.All<BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected);
|
||||||
|
beatmapModelManager.DeleteVideos(items.ToList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void UndeleteAll()
|
public void UndeleteAll()
|
||||||
{
|
{
|
||||||
realm.Run(r => beatmapModelManager.Undelete(r.All<BeatmapSetInfo>().Where(s => s.DeletePending).ToList()));
|
realm.Run(r => beatmapModelManager.Undelete(r.All<BeatmapSetInfo>().Where(s => s.DeletePending).ToList()));
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Database;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Stores;
|
using osu.Game.Stores;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -33,6 +34,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||||
|
|
||||||
|
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv" };
|
||||||
|
|
||||||
public BeatmapModelManager(RealmAccess realm, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null)
|
public BeatmapModelManager(RealmAccess realm, Storage storage, BeatmapOnlineLookupQueue? onlineLookupQueue = null)
|
||||||
: base(realm, storage, onlineLookupQueue)
|
: base(realm, storage, onlineLookupQueue)
|
||||||
{
|
{
|
||||||
@ -114,5 +117,50 @@ namespace osu.Game.Beatmaps
|
|||||||
item.CopyChangesToRealm(existing);
|
item.CopyChangesToRealm(existing);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete videos from a list of beatmaps.
|
||||||
|
/// This will post notifications tracking progress.
|
||||||
|
/// </summary>
|
||||||
|
public void DeleteVideos(List<BeatmapSetInfo> items, bool silent = false)
|
||||||
|
{
|
||||||
|
if (items.Count == 0) return;
|
||||||
|
|
||||||
|
var notification = new ProgressNotification
|
||||||
|
{
|
||||||
|
Progress = 0,
|
||||||
|
Text = $"Preparing to delete all {HumanisedModelName} videos...",
|
||||||
|
CompletionText = "No videos found to delete!",
|
||||||
|
State = ProgressNotificationState.Active,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int deleted = 0;
|
||||||
|
|
||||||
|
foreach (var b in items)
|
||||||
|
{
|
||||||
|
if (notification.State == ProgressNotificationState.Cancelled)
|
||||||
|
// user requested abort
|
||||||
|
return;
|
||||||
|
|
||||||
|
var video = b.Files.FirstOrDefault(f => VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal)));
|
||||||
|
|
||||||
|
if (video != null)
|
||||||
|
{
|
||||||
|
DeleteFile(b, video);
|
||||||
|
deleted++;
|
||||||
|
notification.CompletionText = $"Deleted {deleted} {HumanisedModelName} video(s)!";
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.Text = $"Deleting videos from {HumanisedModelName}s ({deleted} deleted)";
|
||||||
|
|
||||||
|
notification.Progress = (float)++i / items.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.State = ProgressNotificationState.Completed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
protected readonly BeatmapDownloadTracker DownloadTracker;
|
protected readonly BeatmapDownloadTracker DownloadTracker;
|
||||||
|
|
||||||
protected BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
protected BeatmapCard(APIBeatmapSet beatmapSet, bool allowExpansion = true)
|
||||||
: base(HoverSampleSet.Submit)
|
: base(HoverSampleSet.Button)
|
||||||
{
|
{
|
||||||
Expanded = new BindableBool { Disabled = !allowExpansion };
|
Expanded = new BindableBool { Disabled = !allowExpansion };
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -245,10 +244,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -226,10 +225,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (BeatmapSet.HasVideo)
|
if (BeatmapSet.HasVideo)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.HasStoryboard)
|
if (BeatmapSet.HasStoryboard)
|
||||||
leftIconArea.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) });
|
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(20) });
|
||||||
|
|
||||||
if (BeatmapSet.FeaturedInSpotlight)
|
if (BeatmapSet.FeaturedInSpotlight)
|
||||||
{
|
{
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
{
|
{
|
||||||
public class IconPill : CircularContainer
|
public abstract class IconPill : CircularContainer, IHasTooltip
|
||||||
{
|
{
|
||||||
public Vector2 IconSize
|
public Vector2 IconSize
|
||||||
{
|
{
|
||||||
@ -20,7 +22,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
|
|
||||||
private readonly Container iconContainer;
|
private readonly Container iconContainer;
|
||||||
|
|
||||||
public IconPill(IconUsage icon)
|
protected IconPill(IconUsage icon)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
@ -47,5 +49,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract LocalisableString TooltipText { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
osu.Game/Beatmaps/Drawables/Cards/StoryboardIconPill.cs
Normal file
19
osu.Game/Beatmaps/Drawables/Cards/StoryboardIconPill.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
|
{
|
||||||
|
public class StoryboardIconPill : IconPill
|
||||||
|
{
|
||||||
|
public StoryboardIconPill()
|
||||||
|
: base(FontAwesome.Solid.Image)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoStoryboard;
|
||||||
|
}
|
||||||
|
}
|
19
osu.Game/Beatmaps/Drawables/Cards/VideoIconPill.cs
Normal file
19
osu.Game/Beatmaps/Drawables/Cards/VideoIconPill.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
|
{
|
||||||
|
public class VideoIconPill : IconPill
|
||||||
|
{
|
||||||
|
public VideoIconPill()
|
||||||
|
: base(FontAwesome.Solid.Film)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LocalisableString TooltipText => BeatmapsetsStrings.ShowInfoVideo;
|
||||||
|
}
|
||||||
|
}
|
@ -392,7 +392,7 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
total_writes_async.Value++;
|
total_writes_async.Value++;
|
||||||
using (var realm = getRealmInstance())
|
using (var realm = getRealmInstance())
|
||||||
await realm.WriteAsync(action);
|
await realm.WriteAsync(() => action(realm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ScalingContainer : Container
|
public class ScalingContainer : Container
|
||||||
{
|
{
|
||||||
private const float duration = 500;
|
internal const float TRANSITION_DURATION = 500;
|
||||||
|
|
||||||
private Bindable<float> sizeX;
|
private Bindable<float> sizeX;
|
||||||
private Bindable<float> sizeY;
|
private Bindable<float> sizeY;
|
||||||
@ -99,7 +99,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
if (applyUIScale)
|
if (applyUIScale)
|
||||||
{
|
{
|
||||||
uiScale = osuConfig.GetBindable<float>(OsuSetting.UIScale);
|
uiScale = osuConfig.GetBindable<float>(OsuSetting.UIScale);
|
||||||
uiScale.BindValueChanged(args => this.TransformTo(nameof(CurrentScale), args.NewValue, duration, Easing.OutQuart), true);
|
uiScale.BindValueChanged(args => this.TransformTo(nameof(CurrentScale), args.NewValue, TRANSITION_DURATION, Easing.OutQuart), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,10 +163,10 @@ namespace osu.Game.Graphics.Containers
|
|||||||
backgroundStack.Push(new ScalingBackgroundScreen());
|
backgroundStack.Push(new ScalingBackgroundScreen());
|
||||||
}
|
}
|
||||||
|
|
||||||
backgroundStack.FadeIn(duration);
|
backgroundStack.FadeIn(TRANSITION_DURATION);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
backgroundStack?.FadeOut(duration);
|
backgroundStack?.FadeOut(TRANSITION_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
RectangleF targetRect = new RectangleF(Vector2.Zero, Vector2.One);
|
RectangleF targetRect = new RectangleF(Vector2.Zero, Vector2.One);
|
||||||
@ -195,13 +195,13 @@ namespace osu.Game.Graphics.Containers
|
|||||||
if (requiresMasking)
|
if (requiresMasking)
|
||||||
sizableContainer.Masking = true;
|
sizableContainer.Masking = true;
|
||||||
|
|
||||||
sizableContainer.MoveTo(targetRect.Location, duration, Easing.OutQuart);
|
sizableContainer.MoveTo(targetRect.Location, TRANSITION_DURATION, Easing.OutQuart);
|
||||||
sizableContainer.ResizeTo(targetRect.Size, duration, Easing.OutQuart);
|
sizableContainer.ResizeTo(targetRect.Size, TRANSITION_DURATION, Easing.OutQuart);
|
||||||
|
|
||||||
// Of note, this will not work great in the case of nested ScalingContainers where multiple are applying corner radius.
|
// Of note, this will not work great in the case of nested ScalingContainers where multiple are applying corner radius.
|
||||||
// Masking and corner radius should likely only be applied at one point in the full game stack to fix this.
|
// Masking and corner radius should likely only be applied at one point in the full game stack to fix this.
|
||||||
// An example of how this can occur is when the skin editor is visible and the game screen scaling is set to "Everything".
|
// An example of how this can occur is when the skin editor is visible and the game screen scaling is set to "Everything".
|
||||||
sizableContainer.TransformTo(nameof(CornerRadius), requiresMasking ? corner_radius : 0, duration, requiresMasking ? Easing.OutQuart : Easing.None)
|
sizableContainer.TransformTo(nameof(CornerRadius), requiresMasking ? corner_radius : 0, TRANSITION_DURATION, requiresMasking ? Easing.OutQuart : Easing.None)
|
||||||
.OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
|
.OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
Size = TwoLayerButton.SIZE_EXTENDED;
|
Size = TwoLayerButton.SIZE_EXTENDED;
|
||||||
|
|
||||||
Child = button = new TwoLayerButton(HoverSampleSet.Submit)
|
Child = button = new TwoLayerButton
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
|
@ -56,8 +56,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private readonly SpriteText spriteText;
|
private readonly SpriteText spriteText;
|
||||||
private Vector2 hoverSpacing => new Vector2(3f, 0f);
|
private Vector2 hoverSpacing => new Vector2(3f, 0f);
|
||||||
|
|
||||||
public DialogButton()
|
public DialogButton(HoverSampleSet sampleSet = HoverSampleSet.Button)
|
||||||
: base(HoverSampleSet.Submit)
|
: base(sampleSet)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
Icon = FontAwesome.Solid.ExternalLinkAlt,
|
Icon = FontAwesome.Solid.ExternalLinkAlt,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
new HoverClickSounds(HoverSampleSet.Submit)
|
new HoverClickSounds()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
@ -37,7 +38,10 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
|
if (buttons.Contains(e.Button) && Contains(e.ScreenSpaceMousePosition))
|
||||||
sampleClick?.Play();
|
{
|
||||||
|
sampleClick.Frequency.Value = 0.99 + RNG.NextDouble(0.02);
|
||||||
|
sampleClick.Play();
|
||||||
|
}
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,6 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[Description("default")]
|
[Description("default")]
|
||||||
Default,
|
Default,
|
||||||
|
|
||||||
[Description("submit")]
|
|
||||||
Submit,
|
|
||||||
|
|
||||||
[Description("button")]
|
[Description("button")]
|
||||||
Button,
|
Button,
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
public class ShearedToggleButton : ShearedButton
|
public class ShearedToggleButton : ShearedButton
|
||||||
{
|
{
|
||||||
|
private Sample? sampleClick;
|
||||||
private Sample? sampleOff;
|
private Sample? sampleOff;
|
||||||
private Sample? sampleOn;
|
private Sample? sampleOn;
|
||||||
|
|
||||||
@ -39,8 +40,9 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleOn = audio.Samples.Get(@"UI/check-on");
|
sampleClick = audio.Samples.Get(@"UI/default-select");
|
||||||
sampleOff = audio.Samples.Get(@"UI/check-off");
|
sampleOn = audio.Samples.Get(@"UI/dropdown-open");
|
||||||
|
sampleOff = audio.Samples.Get(@"UI/dropdown-close");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
||||||
@ -67,6 +69,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
private void playSample()
|
private void playSample()
|
||||||
{
|
{
|
||||||
|
sampleClick?.Play();
|
||||||
|
|
||||||
if (Active.Value)
|
if (Active.Value)
|
||||||
sampleOn?.Play();
|
sampleOn?.Play();
|
||||||
else
|
else
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -65,6 +67,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
if (BeatmapModelManager.VIDEO_EXTENSIONS.Contains(File.Extension))
|
||||||
|
return FontAwesome.Regular.FileVideo;
|
||||||
|
|
||||||
switch (File.Extension)
|
switch (File.Extension)
|
||||||
{
|
{
|
||||||
case @".ogg":
|
case @".ogg":
|
||||||
@ -77,12 +82,6 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
case @".png":
|
case @".png":
|
||||||
return FontAwesome.Regular.FileImage;
|
return FontAwesome.Regular.FileImage;
|
||||||
|
|
||||||
case @".mp4":
|
|
||||||
case @".avi":
|
|
||||||
case @".mov":
|
|
||||||
case @".flv":
|
|
||||||
return FontAwesome.Regular.FileVideo;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FontAwesome.Regular.File;
|
return FontAwesome.Regular.File;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
||||||
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
||||||
new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
|
new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
|
||||||
|
new KeyBinding(new[] { InputKey.T }, GlobalAction.EditorTapForBPM),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally),
|
new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.EditorFlipHorizontally),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically),
|
new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing),
|
||||||
@ -322,5 +323,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DeselectAllMods))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DeselectAllMods))]
|
||||||
DeselectAllMods,
|
DeselectAllMods,
|
||||||
|
|
||||||
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTapForBPM))]
|
||||||
|
EditorTapForBPM,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString OutputDevice => new TranslatableString(getKey(@"output_device"), @"Output device");
|
public static LocalisableString OutputDevice => new TranslatableString(getKey(@"output_device"), @"Output device");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Master"
|
/// "Hitsound stereo separation"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString PositionalLevel => new TranslatableString(getKey(@"positional_hitsound_audio_level"), @"Hitsound stereo separation");
|
public static LocalisableString PositionalLevel => new TranslatableString(getKey(@"positional_hitsound_audio_level"), @"Hitsound stereo separation");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Level"
|
/// "Master"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString MasterVolume => new TranslatableString(getKey(@"master_volume"), @"Master");
|
public static LocalisableString MasterVolume => new TranslatableString(getKey(@"master_volume"), @"Master");
|
||||||
|
|
||||||
@ -69,6 +69,6 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString OffsetWizard => new TranslatableString(getKey(@"offset_wizard"), @"Offset wizard");
|
public static LocalisableString OffsetWizard => new TranslatableString(getKey(@"offset_wizard"), @"Offset wizard");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
|
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Tap for BPM"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString EditorTapForBPM => new TranslatableString(getKey(@"editor_tap_for_bpm"), @"Tap for BPM");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Cycle grid display mode"
|
/// "Cycle grid display mode"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -205,7 +210,7 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface");
|
public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Toggle Mod Select"
|
/// "Toggle mod select"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle mod select");
|
public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle mod select");
|
||||||
|
|
||||||
@ -294,6 +299,6 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ToggleChatFocus => new TranslatableString(getKey(@"toggle_chat_focus"), @"Toggle chat focus");
|
public static LocalisableString ToggleChatFocus => new TranslatableString(getKey(@"toggle_chat_focus"), @"Toggle chat focus");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString JoystickGamepad => new TranslatableString(getKey(@"joystick_gamepad"), @"Joystick / Gamepad");
|
public static LocalisableString JoystickGamepad => new TranslatableString(getKey(@"joystick_gamepad"), @"Joystick / Gamepad");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Deadzone Threshold"
|
/// "Deadzone"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone");
|
public static LocalisableString DeadzoneThreshold => new TranslatableString(getKey(@"deadzone_threshold"), @"Deadzone");
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString UnableToRunExclusiveFullscreen => new TranslatableString(getKey(@"unable_to_run_exclusive_fullscreen"), @"Unable to run exclusive fullscreen. You'll still experience some input latency.");
|
public static LocalisableString UnableToRunExclusiveFullscreen => new TranslatableString(getKey(@"unable_to_run_exclusive_fullscreen"), @"Unable to run exclusive fullscreen. You'll still experience some input latency.");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Using fullscreen on macOS makes interacting with the menu bar and spaces no longer work, and may lead to freezes if a system dialog is presented. Using borderless is recommended."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString FullscreenMacOSNote => new TranslatableString(getKey(@"fullscreen_macos_note"), @"Using fullscreen on macOS makes interacting with the menu bar and spaces no longer work, and may lead to freezes if a system dialog is presented. Using borderless is recommended.");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,6 +29,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString DeleteAllBeatmaps => new TranslatableString(getKey(@"delete_all_beatmaps"), @"Delete ALL beatmaps");
|
public static LocalisableString DeleteAllBeatmaps => new TranslatableString(getKey(@"delete_all_beatmaps"), @"Delete ALL beatmaps");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Delete ALL beatmap videos"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DeleteAllBeatmapVideos => new TranslatableString(getKey(@"delete_all_beatmap_videos"), @"Delete ALL beatmap videos");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Import scores from stable"
|
/// "Import scores from stable"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -121,8 +121,16 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
if (isFailing) return;
|
if (isFailing) return;
|
||||||
|
|
||||||
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
try
|
||||||
WebRequest.Perform();
|
{
|
||||||
|
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
||||||
|
WebRequest.Perform();
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// ignore this. internally Perform is running async and the fail state may have changed since
|
||||||
|
// the last check of `isFailing` above.
|
||||||
|
}
|
||||||
|
|
||||||
if (isFailing) return;
|
if (isFailing) return;
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ namespace osu.Game.Online.Chat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DrawableLinkCompiler(IEnumerable<Drawable> parts)
|
public DrawableLinkCompiler(IEnumerable<Drawable> parts)
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
{
|
||||||
Parts = parts.ToList();
|
Parts = parts.ToList();
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Online.Chat
|
|||||||
if (!messages.Any())
|
if (!messages.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var channel = channelManager.JoinedChannels.SingleOrDefault(c => c.Id == messages.First().ChannelId);
|
var channel = channelManager.JoinedChannels.SingleOrDefault(c => c.Id > 0 && c.Id == messages.First().ChannelId);
|
||||||
|
|
||||||
if (channel == null)
|
if (channel == null)
|
||||||
return;
|
return;
|
||||||
|
@ -155,9 +155,6 @@ namespace osu.Game.Online.Chat
|
|||||||
{
|
{
|
||||||
public Func<Message, ChatLine> CreateChatLineAction;
|
public Func<Message, ChatLine> CreateChatLineAction;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
public StandAloneDrawableChannel(Channel channel)
|
public StandAloneDrawableChannel(Channel channel)
|
||||||
: base(channel)
|
: base(channel)
|
||||||
{
|
{
|
||||||
@ -166,25 +163,40 @@ namespace osu.Game.Online.Chat
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
|
ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
|
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
|
||||||
|
|
||||||
protected override Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time)
|
protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new StandAloneDaySeparator(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class StandAloneDaySeparator : DaySeparator
|
||||||
|
{
|
||||||
|
protected override float TextSize => 14;
|
||||||
|
protected override float LineHeight => 1;
|
||||||
|
protected override float Spacing => 10;
|
||||||
|
protected override float DateAlign => 120;
|
||||||
|
|
||||||
|
public StandAloneDaySeparator(DateTimeOffset time)
|
||||||
|
: base(time)
|
||||||
{
|
{
|
||||||
TextSize = 14,
|
}
|
||||||
Colour = colours.Yellow,
|
|
||||||
LineHeight = 1,
|
[BackgroundDependencyLoader]
|
||||||
Padding = new MarginPadding { Horizontal = 10 },
|
private void load(OsuColour colours)
|
||||||
Margin = new MarginPadding { Vertical = 5 },
|
{
|
||||||
};
|
Height = 25;
|
||||||
|
Colour = colours.Yellow;
|
||||||
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
|
Padding = new MarginPadding { Horizontal = 10 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class StandAloneMessage : ChatLine
|
protected class StandAloneMessage : ChatLine
|
||||||
{
|
{
|
||||||
protected override float TextSize => 15;
|
protected override float TextSize => 15;
|
||||||
|
|
||||||
protected override float HorizontalPadding => 10;
|
protected override float HorizontalPadding => 10;
|
||||||
protected override float MessagePadding => 120;
|
protected override float MessagePadding => 120;
|
||||||
protected override float TimestampPadding => 50;
|
protected override float TimestampPadding => 50;
|
||||||
|
@ -197,6 +197,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
|
|
||||||
APIRoom.Playlist.Clear();
|
APIRoom.Playlist.Clear();
|
||||||
APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(createPlaylistItem));
|
APIRoom.Playlist.AddRange(joinedRoom.Playlist.Select(createPlaylistItem));
|
||||||
|
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == joinedRoom.Settings.PlaylistItemId);
|
||||||
|
|
||||||
Debug.Assert(LocalUser != null);
|
Debug.Assert(LocalUser != null);
|
||||||
addUserToAPIRoom(LocalUser);
|
addUserToAPIRoom(LocalUser);
|
||||||
@ -737,6 +738,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
APIRoom.Type.Value = Room.Settings.MatchType;
|
APIRoom.Type.Value = Room.Settings.MatchType;
|
||||||
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
|
APIRoom.QueueMode.Value = Room.Settings.QueueMode;
|
||||||
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
|
APIRoom.AutoStartDuration.Value = Room.Settings.AutoStartDuration;
|
||||||
|
APIRoom.CurrentPlaylistItem.Value = APIRoom.Playlist.Single(item => item.ID == settings.PlaylistItemId);
|
||||||
|
|
||||||
RoomUpdated?.Invoke();
|
RoomUpdated?.Invoke();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,13 @@ namespace osu.Game.Online.Rooms
|
|||||||
/// Used for serialising to the API.
|
/// Used for serialising to the API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("beatmap_id")]
|
[JsonProperty("beatmap_id")]
|
||||||
private int onlineBeatmapId => Beatmap.OnlineID;
|
private int onlineBeatmapId
|
||||||
|
{
|
||||||
|
get => Beatmap.OnlineID;
|
||||||
|
// This setter is only required for client-side serialise-then-deserialise operations.
|
||||||
|
// Serialisation is supposed to emit only a `beatmap_id`, but a (non-null) `beatmap` is required on deserialise.
|
||||||
|
set => Beatmap = new APIBeatmap { OnlineID = value };
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A beatmap representing this playlist item.
|
/// A beatmap representing this playlist item.
|
||||||
|
@ -162,6 +162,13 @@ namespace osu.Game.Online.Rooms
|
|||||||
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
Password.BindValueChanged(p => HasPassword.Value = !string.IsNullOrEmpty(p.NewValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies values from another <see cref="Room"/> into this one.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// **Beware**: This will store references between <see cref="Room"/>s.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="other">The <see cref="Room"/> to copy values from.</param>
|
||||||
public void CopyFrom(Room other)
|
public void CopyFrom(Room other)
|
||||||
{
|
{
|
||||||
RoomID.Value = other.RoomID.Value;
|
RoomID.Value = other.RoomID.Value;
|
||||||
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -658,11 +659,14 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override IDictionary<FrameworkSetting, object> GetFrameworkConfigDefaults()
|
protected override IDictionary<FrameworkSetting, object> GetFrameworkConfigDefaults()
|
||||||
=> new Dictionary<FrameworkSetting, object>
|
{
|
||||||
|
return new Dictionary<FrameworkSetting, object>
|
||||||
{
|
{
|
||||||
// General expectation that osu! starts in fullscreen by default (also gives the most predictable performance)
|
// General expectation that osu! starts in fullscreen by default (also gives the most predictable performance).
|
||||||
{ FrameworkSetting.WindowMode, WindowMode.Fullscreen }
|
// However, macOS is bound to have issues when using exclusive fullscreen as it takes full control away from OS, therefore borderless is default there.
|
||||||
|
{ FrameworkSetting.WindowMode, RuntimeInfo.OS == RuntimeInfo.Platform.macOS ? WindowMode.Borderless : WindowMode.Fullscreen }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
|
@ -80,6 +80,9 @@ namespace osu.Game
|
|||||||
|
|
||||||
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
|
public virtual bool UseDevelopmentServer => DebugUtils.IsDebugBuild;
|
||||||
|
|
||||||
|
internal EndpointConfiguration CreateEndpoints() =>
|
||||||
|
UseDevelopmentServer ? (EndpointConfiguration)new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration();
|
||||||
|
|
||||||
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
|
public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -268,7 +271,7 @@ namespace osu.Game
|
|||||||
dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler));
|
dependencies.Cache(SkinManager = new SkinManager(Storage, realm, Host, Resources, Audio, Scheduler));
|
||||||
dependencies.CacheAs<ISkinSource>(SkinManager);
|
dependencies.CacheAs<ISkinSource>(SkinManager);
|
||||||
|
|
||||||
EndpointConfiguration endpoints = UseDevelopmentServer ? (EndpointConfiguration)new DevelopmentEndpointConfiguration() : new ProductionEndpointConfiguration();
|
EndpointConfiguration endpoints = CreateEndpoints();
|
||||||
|
|
||||||
MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl;
|
MessageFormatter.WebsiteRootUrl = endpoints.WebsiteRootUrl;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -69,7 +70,7 @@ namespace osu.Game.Overlays.AccountCreation
|
|||||||
},
|
},
|
||||||
usernameTextBox = new OsuTextBox
|
usernameTextBox = new OsuTextBox
|
||||||
{
|
{
|
||||||
PlaceholderText = UsersStrings.LoginUsername,
|
PlaceholderText = UsersStrings.LoginUsername.ToLower(),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
TabbableContentContainer = this
|
TabbableContentContainer = this
|
||||||
},
|
},
|
||||||
@ -91,7 +92,7 @@ namespace osu.Game.Overlays.AccountCreation
|
|||||||
},
|
},
|
||||||
passwordTextBox = new OsuPasswordTextBox
|
passwordTextBox = new OsuPasswordTextBox
|
||||||
{
|
{
|
||||||
PlaceholderText = "password",
|
PlaceholderText = UsersStrings.LoginPassword.ToLower(),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
TabbableContentContainer = this,
|
TabbableContentContainer = this,
|
||||||
},
|
},
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
||||||
Text = LabelFor(Value)
|
Text = LabelFor(Value)
|
||||||
},
|
},
|
||||||
new HoverClickSounds()
|
new HoverClickSounds(HoverSampleSet.TabSelect)
|
||||||
});
|
});
|
||||||
|
|
||||||
Enabled.Value = true;
|
Enabled.Value = true;
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Chat
|
namespace osu.Game.Overlays.Chat
|
||||||
@ -24,85 +20,19 @@ namespace osu.Game.Overlays.Chat
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
ChatLineFlow.Padding = new MarginPadding(0);
|
ChatLineFlow.Padding = new MarginPadding(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Drawable CreateDaySeparator(DateTimeOffset time) => new ChatOverlayDaySeparator(time);
|
protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new ChatOverlayDaySeparator(time);
|
||||||
|
|
||||||
private class ChatOverlayDaySeparator : Container
|
private class ChatOverlayDaySeparator : DaySeparator
|
||||||
{
|
{
|
||||||
private readonly DateTimeOffset time;
|
|
||||||
|
|
||||||
public ChatOverlayDaySeparator(DateTimeOffset time)
|
public ChatOverlayDaySeparator(DateTimeOffset time)
|
||||||
|
: base(time)
|
||||||
{
|
{
|
||||||
this.time = time;
|
// TODO: Remove once DrawableChannel & ChatLine padding is fixed
|
||||||
}
|
Padding = new MarginPadding { Horizontal = 15 };
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OverlayColourProvider colourProvider)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
Padding = new MarginPadding { Horizontal = 15, Vertical = 20 };
|
|
||||||
Child = new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.Absolute, 200),
|
|
||||||
new Dimension(GridSizeMode.Absolute, 15),
|
|
||||||
new Dimension(),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(GridSizeMode.Absolute, 15),
|
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
|
||||||
},
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
new Circle
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
Colour = colourProvider.Background5,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 2,
|
|
||||||
},
|
|
||||||
Drawable.Empty(),
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(),
|
|
||||||
Font = OsuFont.Torus.With(size: 15, weight: FontWeight.SemiBold),
|
|
||||||
Colour = colourProvider.Content1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Drawable.Empty(),
|
|
||||||
new Circle
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Colour = colourProvider.Background5,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
105
osu.Game/Overlays/Chat/DaySeparator.cs
Normal file
105
osu.Game/Overlays/Chat/DaySeparator.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Chat
|
||||||
|
{
|
||||||
|
public class DaySeparator : Container
|
||||||
|
{
|
||||||
|
protected virtual float TextSize => 15;
|
||||||
|
|
||||||
|
protected virtual float LineHeight => 2;
|
||||||
|
|
||||||
|
protected virtual float DateAlign => 200;
|
||||||
|
|
||||||
|
protected virtual float Spacing => 15;
|
||||||
|
|
||||||
|
private readonly DateTimeOffset time;
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private OverlayColourProvider? colourProvider { get; set; }
|
||||||
|
|
||||||
|
public DaySeparator(DateTimeOffset time)
|
||||||
|
{
|
||||||
|
this.time = time;
|
||||||
|
Height = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
RowDimensions = new[] { new Dimension() },
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Absolute, DateAlign),
|
||||||
|
new Dimension(GridSizeMode.Absolute, Spacing),
|
||||||
|
new Dimension(),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
RowDimensions = new[] { new Dimension() },
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, Spacing),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = LineHeight,
|
||||||
|
Colour = colourProvider?.Background5 ?? Colour4.White,
|
||||||
|
},
|
||||||
|
Drawable.Empty(),
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Text = time.ToLocalTime().ToString("dd MMMM yyyy").ToUpper(),
|
||||||
|
Font = OsuFont.Torus.With(size: TextSize, weight: FontWeight.SemiBold),
|
||||||
|
Colour = colourProvider?.Content1 ?? Colour4.White,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Drawable.Empty(),
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = LineHeight,
|
||||||
|
Colour = colourProvider?.Background5 ?? Colour4.White,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,14 +7,9 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -40,9 +35,6 @@ namespace osu.Game.Overlays.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
public DrawableChannel(Channel channel)
|
public DrawableChannel(Channel channel)
|
||||||
{
|
{
|
||||||
Channel = channel;
|
Channel = channel;
|
||||||
@ -67,7 +59,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
Padding = new MarginPadding { Bottom = 5 },
|
Padding = new MarginPadding { Bottom = 5 },
|
||||||
Child = ChatLineFlow = new FillFlowContainer
|
Child = ChatLineFlow = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding { Left = 20, Right = 20 },
|
Padding = new MarginPadding { Horizontal = 15 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
@ -121,11 +113,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
|
|
||||||
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
|
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
|
||||||
|
|
||||||
protected virtual Drawable CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time)
|
protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time);
|
||||||
{
|
|
||||||
Colour = colours.ChatBlue.Lighten(0.7f),
|
|
||||||
Margin = new MarginPadding { Vertical = 10 },
|
|
||||||
};
|
|
||||||
|
|
||||||
private void newMessagesArrived(IEnumerable<Message> newMessages) => Schedule(() =>
|
private void newMessagesArrived(IEnumerable<Message> newMessages) => Schedule(() =>
|
||||||
{
|
{
|
||||||
@ -203,69 +191,5 @@ namespace osu.Game.Overlays.Chat
|
|||||||
});
|
});
|
||||||
|
|
||||||
private IEnumerable<ChatLine> chatLines => ChatLineFlow.Children.OfType<ChatLine>();
|
private IEnumerable<ChatLine> chatLines => ChatLineFlow.Children.OfType<ChatLine>();
|
||||||
|
|
||||||
public class DaySeparator : Container
|
|
||||||
{
|
|
||||||
public float TextSize
|
|
||||||
{
|
|
||||||
get => text.Font.Size;
|
|
||||||
set => text.Font = text.Font.With(size: value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float lineHeight = 2;
|
|
||||||
|
|
||||||
public float LineHeight
|
|
||||||
{
|
|
||||||
get => lineHeight;
|
|
||||||
set => lineHeight = leftBox.Height = rightBox.Height = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly SpriteText text;
|
|
||||||
private readonly Box leftBox;
|
|
||||||
private readonly Box rightBox;
|
|
||||||
|
|
||||||
public DaySeparator(DateTimeOffset time)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
Child = new GridContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
ColumnDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(),
|
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
|
||||||
new Dimension(),
|
|
||||||
},
|
|
||||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize), },
|
|
||||||
Content = new[]
|
|
||||||
{
|
|
||||||
new Drawable[]
|
|
||||||
{
|
|
||||||
leftBox = new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = lineHeight,
|
|
||||||
},
|
|
||||||
text = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Horizontal = 10 },
|
|
||||||
Text = time.ToLocalTime().ToString("dd MMM yyyy"),
|
|
||||||
},
|
|
||||||
rightBox = new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = lineHeight,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -16,6 +18,7 @@ using osu.Framework.Localisation;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -32,6 +35,8 @@ namespace osu.Game.Overlays.Chat.Listing
|
|||||||
public IEnumerable<LocalisableString> FilterTerms => new LocalisableString[] { Channel.Name, Channel.Topic ?? string.Empty };
|
public IEnumerable<LocalisableString> FilterTerms => new LocalisableString[] { Channel.Name, Channel.Topic ?? string.Empty };
|
||||||
public bool MatchingFilter { set => this.FadeTo(value ? 1f : 0f, 100); }
|
public bool MatchingFilter { set => this.FadeTo(value ? 1f : 0f, 100); }
|
||||||
|
|
||||||
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds();
|
||||||
|
|
||||||
private Box hoverBox = null!;
|
private Box hoverBox = null!;
|
||||||
private SpriteIcon checkbox = null!;
|
private SpriteIcon checkbox = null!;
|
||||||
private OsuSpriteText channelText = null!;
|
private OsuSpriteText channelText = null!;
|
||||||
@ -46,14 +51,20 @@ namespace osu.Game.Overlays.Chat.Listing
|
|||||||
|
|
||||||
private const float vertical_margin = 1.5f;
|
private const float vertical_margin = 1.5f;
|
||||||
|
|
||||||
|
private Sample? sampleJoin;
|
||||||
|
private Sample? sampleLeave;
|
||||||
|
|
||||||
public ChannelListingItem(Channel channel)
|
public ChannelListingItem(Channel channel)
|
||||||
{
|
{
|
||||||
Channel = channel;
|
Channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
|
sampleJoin = audio.Samples.Get(@"UI/check-on");
|
||||||
|
sampleLeave = audio.Samples.Get(@"UI/check-off");
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -156,7 +167,19 @@ namespace osu.Game.Overlays.Chat.Listing
|
|||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
Action = () => (channelJoined.Value ? OnRequestLeave : OnRequestJoin)?.Invoke(Channel);
|
Action = () =>
|
||||||
|
{
|
||||||
|
if (channelJoined.Value)
|
||||||
|
{
|
||||||
|
OnRequestLeave?.Invoke(Channel);
|
||||||
|
sampleLeave?.Play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnRequestJoin?.Invoke(Channel);
|
||||||
|
sampleJoin?.Play();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
@ -14,7 +14,6 @@ using osu.Framework.Platform;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.News
|
namespace osu.Game.Overlays.News
|
||||||
@ -29,7 +28,6 @@ namespace osu.Game.Overlays.News
|
|||||||
private TextFlowContainer main;
|
private TextFlowContainer main;
|
||||||
|
|
||||||
public NewsCard(APINewsPost post)
|
public NewsCard(APINewsPost post)
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
{
|
||||||
this.post = post;
|
this.post = post;
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@ using System.Diagnostics;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.News.Sidebar
|
namespace osu.Game.Overlays.News.Sidebar
|
||||||
{
|
{
|
||||||
@ -129,7 +128,6 @@ namespace osu.Game.Overlays.News.Sidebar
|
|||||||
private readonly APINewsPost post;
|
private readonly APINewsPost post;
|
||||||
|
|
||||||
public PostButton(APINewsPost post)
|
public PostButton(APINewsPost post)
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
{
|
||||||
this.post = post;
|
this.post = post;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
|||||||
private Sample sampleOpen;
|
private Sample sampleOpen;
|
||||||
private Sample sampleClose;
|
private Sample sampleClose;
|
||||||
|
|
||||||
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds();
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverClickSounds();
|
||||||
|
|
||||||
public ExpandDetailsButton()
|
public ExpandDetailsButton()
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Sections
|
namespace osu.Game.Overlays.Profile.Sections
|
||||||
{
|
{
|
||||||
@ -18,7 +17,6 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
private readonly IBeatmapInfo beatmapInfo;
|
private readonly IBeatmapInfo beatmapInfo;
|
||||||
|
|
||||||
protected BeatmapMetadataContainer(IBeatmapInfo beatmapInfo)
|
protected BeatmapMetadataContainer(IBeatmapInfo beatmapInfo)
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
{
|
||||||
this.beatmapInfo = beatmapInfo;
|
this.beatmapInfo = beatmapInfo;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
@ -224,6 +225,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
|
|
||||||
private void updateScreenModeWarning()
|
private void updateScreenModeWarning()
|
||||||
{
|
{
|
||||||
|
if (RuntimeInfo.OS == RuntimeInfo.Platform.macOS)
|
||||||
|
{
|
||||||
|
if (windowModeDropdown.Current.Value == WindowMode.Fullscreen)
|
||||||
|
windowModeDropdown.SetNoticeText(LayoutSettingsStrings.FullscreenMacOSNote, true);
|
||||||
|
else
|
||||||
|
windowModeDropdown.ClearNoticeText();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (windowModeDropdown.Current.Value != WindowMode.Fullscreen)
|
if (windowModeDropdown.Current.Value != WindowMode.Fullscreen)
|
||||||
{
|
{
|
||||||
windowModeDropdown.SetNoticeText(GraphicsSettingsStrings.NotFullscreenNote, true);
|
windowModeDropdown.SetNoticeText(GraphicsSettingsStrings.NotFullscreenNote, true);
|
||||||
|
@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
private SettingsButton deleteSkinsButton;
|
private SettingsButton deleteSkinsButton;
|
||||||
private SettingsButton restoreButton;
|
private SettingsButton restoreButton;
|
||||||
private SettingsButton undeleteButton;
|
private SettingsButton undeleteButton;
|
||||||
|
private SettingsButton deleteBeatmapVideosButton;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, IDialogOverlay dialogOverlay)
|
private void load(BeatmapManager beatmaps, ScoreManager scores, SkinManager skins, [CanBeNull] CollectionManager collectionManager, [CanBeNull] LegacyImportManager legacyImportManager, IDialogOverlay dialogOverlay)
|
||||||
@ -58,6 +59,19 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Add(deleteBeatmapVideosButton = new DangerousSettingsButton
|
||||||
|
{
|
||||||
|
Text = MaintenanceSettingsStrings.DeleteAllBeatmapVideos,
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
dialogOverlay?.Push(new MassVideoDeleteConfirmationDialog(() =>
|
||||||
|
{
|
||||||
|
deleteBeatmapVideosButton.Enabled.Value = false;
|
||||||
|
Task.Run(beatmaps.DeleteAllVideos).ContinueWith(t => Schedule(() => deleteBeatmapVideosButton.Enabled.Value = true));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (legacyImportManager?.SupportsImportFromStable == true)
|
if (legacyImportManager?.SupportsImportFromStable == true)
|
||||||
{
|
{
|
||||||
Add(importScoresButton = new SettingsButton
|
Add(importScoresButton = new SettingsButton
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||||
|
{
|
||||||
|
public class MassVideoDeleteConfirmationDialog : MassDeleteConfirmationDialog
|
||||||
|
{
|
||||||
|
public MassVideoDeleteConfirmationDialog(Action deleteAction)
|
||||||
|
: base(deleteAction)
|
||||||
|
{
|
||||||
|
BodyText = "All beatmap videos? This cannot be undone!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -83,7 +82,6 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
private RealmAccess realm { get; set; }
|
private RealmAccess realm { get; set; }
|
||||||
|
|
||||||
protected ToolbarButton()
|
protected ToolbarButton()
|
||||||
: base(HoverSampleSet.Toolbar)
|
|
||||||
{
|
{
|
||||||
Width = Toolbar.HEIGHT;
|
Width = Toolbar.HEIGHT;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
@ -11,7 +11,6 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
private AnalogClockDisplay analog;
|
private AnalogClockDisplay analog;
|
||||||
|
|
||||||
public ToolbarClock()
|
public ToolbarClock()
|
||||||
: base(HoverSampleSet.Toolbar)
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
|
@ -15,7 +15,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Bindings;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -28,7 +27,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>, IStateful<SelectionState>
|
public class VolumeMeter : Container, IStateful<SelectionState>
|
||||||
{
|
{
|
||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
private CircularProgress volumeCircleGlow;
|
private CircularProgress volumeCircleGlow;
|
||||||
@ -80,7 +79,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, AudioManager audio)
|
private void load(OsuColour colours, AudioManager audio)
|
||||||
{
|
{
|
||||||
hoverSample = audio.Samples.Get($"UI/{HoverSampleSet.Button.GetDescription()}-hover");
|
hoverSample = audio.Samples.Get($@"UI/{HoverSampleSet.Button.GetDescription()}-hover");
|
||||||
notchSample = audio.Samples.Get(@"UI/notch-tick");
|
notchSample = audio.Samples.Get(@"UI/notch-tick");
|
||||||
sampleLastPlaybackTime = Time.Current;
|
sampleLastPlaybackTime = Time.Current;
|
||||||
|
|
||||||
@ -132,7 +131,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Name = "Progress under covers for smoothing",
|
Name = @"Progress under covers for smoothing",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Rotation = 180,
|
Rotation = 180,
|
||||||
Child = volumeCircle = new CircularProgress
|
Child = volumeCircle = new CircularProgress
|
||||||
@ -144,7 +143,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
},
|
},
|
||||||
new Circle
|
new Circle
|
||||||
{
|
{
|
||||||
Name = "Inner Cover",
|
Name = @"Inner Cover",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -153,7 +152,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Progress overlay for glow",
|
Name = @"Progress overlay for glow",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -365,27 +364,6 @@ namespace osu.Game.Overlays.Volume
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
|
||||||
{
|
|
||||||
if (!IsHovered)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (e.Action)
|
|
||||||
{
|
|
||||||
case GlobalAction.SelectPreviousGroup:
|
|
||||||
State = SelectionState.Selected;
|
|
||||||
adjust(1, false);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case GlobalAction.SelectNextGroup:
|
|
||||||
State = SelectionState.Selected;
|
|
||||||
adjust(-1, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Input.Bindings;
|
|||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Overlays
|
namespace osu.Game.Overlays
|
||||||
{
|
{
|
||||||
@ -171,6 +172,30 @@ namespace osu.Game.Overlays
|
|||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.Left:
|
||||||
|
Adjust(GlobalAction.PreviousVolumeMeter);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key.Right:
|
||||||
|
Adjust(GlobalAction.NextVolumeMeter);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key.Down:
|
||||||
|
Adjust(GlobalAction.DecreaseVolume);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Key.Up:
|
||||||
|
Adjust(GlobalAction.IncreaseVolume);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
schedulePopOut();
|
schedulePopOut();
|
||||||
|
@ -460,6 +460,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
currentMaximumScoringValues.BaseScore = maximum.BaseScore;
|
currentMaximumScoringValues.BaseScore = maximum.BaseScore;
|
||||||
currentMaximumScoringValues.MaxCombo = maximum.MaxCombo;
|
currentMaximumScoringValues.MaxCombo = maximum.MaxCombo;
|
||||||
|
|
||||||
|
Combo.Value = frame.Header.Combo;
|
||||||
HighestCombo.Value = frame.Header.MaxCombo;
|
HighestCombo.Value = frame.Header.MaxCombo;
|
||||||
|
|
||||||
scoreResultCounts.Clear();
|
scoreResultCounts.Clear();
|
||||||
|
@ -16,13 +16,15 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class EditorSidebar : Container<EditorSidebarSection>
|
internal class EditorSidebar : Container<EditorSidebarSection>
|
||||||
{
|
{
|
||||||
|
public const float WIDTH = 250;
|
||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
protected override Container<EditorSidebarSection> Content { get; }
|
protected override Container<EditorSidebarSection> Content { get; }
|
||||||
|
|
||||||
public EditorSidebar()
|
public EditorSidebar()
|
||||||
{
|
{
|
||||||
Width = 250;
|
Width = WIDTH;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
|
@ -1,99 +1,29 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osuTK;
|
using osu.Game.Overlays;
|
||||||
using osuTK.Graphics;
|
using osu.Game.Screens.Edit.Timing;
|
||||||
using osuTK.Input;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||||
{
|
{
|
||||||
public class TimelineButton : CompositeDrawable
|
public class TimelineButton : IconButton
|
||||||
{
|
{
|
||||||
public Action Action;
|
[BackgroundDependencyLoader]
|
||||||
public readonly BindableBool Enabled = new BindableBool(true);
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
|
||||||
public IconUsage Icon
|
|
||||||
{
|
{
|
||||||
get => button.Icon;
|
// These are using colourProvider but don't match the design.
|
||||||
set => button.Icon = value;
|
// Just something to fit until someone implements the updated design.
|
||||||
|
IconColour = colourProvider.Background1;
|
||||||
|
IconHoverColour = colourProvider.Content2;
|
||||||
|
|
||||||
|
HoverColour = colourProvider.Background1;
|
||||||
|
FlashColour = colourProvider.Content2;
|
||||||
|
|
||||||
|
Add(new RepeatingButtonBehaviour(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IconButton button;
|
protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
|
||||||
|
|
||||||
public TimelineButton()
|
|
||||||
{
|
|
||||||
InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() };
|
|
||||||
|
|
||||||
button.Enabled.BindTo(Enabled);
|
|
||||||
Width = button.Width;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
button.Size = new Vector2(button.Width, DrawHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimelineIconButton : IconButton
|
|
||||||
{
|
|
||||||
public TimelineIconButton()
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre;
|
|
||||||
Origin = Anchor.Centre;
|
|
||||||
IconColour = OsuColour.Gray(0.35f);
|
|
||||||
IconHoverColour = Color4.White;
|
|
||||||
HoverColour = OsuColour.Gray(0.25f);
|
|
||||||
FlashColour = OsuColour.Gray(0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScheduledDelegate repeatSchedule;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The initial delay before mouse down repeat begins.
|
|
||||||
/// </summary>
|
|
||||||
private const int repeat_initial_delay = 250;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The delay between mouse down repeats after the initial repeat.
|
|
||||||
/// </summary>
|
|
||||||
private const int repeat_tick_rate = 70;
|
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
|
||||||
{
|
|
||||||
// don't actuate a click since we are manually handling repeats.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
|
||||||
{
|
|
||||||
if (e.Button == MouseButton.Left)
|
|
||||||
{
|
|
||||||
Action clickAction = () => base.OnClick(new ClickEvent(e.CurrentState, e.Button));
|
|
||||||
|
|
||||||
// run once for initial down
|
|
||||||
clickAction();
|
|
||||||
|
|
||||||
Scheduler.Add(repeatSchedule = new ScheduledDelegate(clickAction, Clock.CurrentTime + repeat_initial_delay, repeat_tick_rate));
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnMouseDown(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
|
||||||
{
|
|
||||||
repeatSchedule?.Cancel();
|
|
||||||
base.OnMouseUp(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Transforms;
|
using osu.Framework.Graphics.Transforms;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Layout;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -40,10 +41,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private IFrameBasedClock editorClock { get; set; }
|
private IFrameBasedClock editorClock { get; set; }
|
||||||
|
|
||||||
|
private readonly LayoutValue zoomedContentWidthCache = new LayoutValue(Invalidation.DrawSize);
|
||||||
|
|
||||||
public ZoomableScrollContainer()
|
public ZoomableScrollContainer()
|
||||||
: base(Direction.Horizontal)
|
: base(Direction.Horizontal)
|
||||||
{
|
{
|
||||||
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
|
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
|
||||||
|
|
||||||
|
AddLayout(zoomedContentWidthCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float minZoom = 1;
|
private float minZoom = 1;
|
||||||
@ -103,12 +108,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.Update();
|
||||||
|
|
||||||
// This width only gets updated on the application of a transform, so this needs to be initialized here.
|
if (!zoomedContentWidthCache.IsValid)
|
||||||
updateZoomedContentWidth();
|
updateZoomedContentWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e)
|
protected override bool OnScroll(ScrollEvent e)
|
||||||
@ -128,7 +133,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
return base.OnScroll(e);
|
return base.OnScroll(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateZoomedContentWidth() => zoomedContent.Width = DrawWidth * currentZoom;
|
private void updateZoomedContentWidth()
|
||||||
|
{
|
||||||
|
zoomedContent.Width = DrawWidth * currentZoom;
|
||||||
|
zoomedContentWidthCache.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
private float zoomTarget = 1;
|
private float zoomTarget = 1;
|
||||||
|
|
||||||
@ -199,8 +208,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset;
|
float targetOffset = expectedWidth * (focusPoint / contentSize) - focusOffset;
|
||||||
|
|
||||||
d.currentZoom = newZoom;
|
d.currentZoom = newZoom;
|
||||||
|
|
||||||
d.updateZoomedContentWidth();
|
d.updateZoomedContentWidth();
|
||||||
|
|
||||||
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
|
// Temporarily here to make sure ScrollTo gets the correct DrawSize for scrollable area.
|
||||||
// TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is.
|
// TODO: Make sure draw size gets invalidated properly on the framework side, and remove this once it is.
|
||||||
d.Invalidate(Invalidation.DrawSize);
|
d.Invalidate(Invalidation.DrawSize);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -9,6 +11,7 @@ using System.Threading.Tasks;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
@ -27,10 +30,10 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
|
|
||||||
public IEnumerable<string> HandledExtensions => handledExtensions;
|
public IEnumerable<string> HandledExtensions => handledExtensions;
|
||||||
|
|
||||||
private readonly Bindable<FileInfo> currentFile = new Bindable<FileInfo>();
|
private readonly Bindable<FileInfo?> currentFile = new Bindable<FileInfo?>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuGameBase game { get; set; }
|
private OsuGameBase game { get; set; } = null!;
|
||||||
|
|
||||||
public FileChooserLabelledTextBox(params string[] handledExtensions)
|
public FileChooserLabelledTextBox(params string[] handledExtensions)
|
||||||
{
|
{
|
||||||
@ -45,7 +48,7 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
currentFile.BindValueChanged(onFileSelected);
|
currentFile.BindValueChanged(onFileSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFileSelected(ValueChangedEvent<FileInfo> file)
|
private void onFileSelected(ValueChangedEvent<FileInfo?> file)
|
||||||
{
|
{
|
||||||
if (file.NewValue == null)
|
if (file.NewValue == null)
|
||||||
return;
|
return;
|
||||||
@ -65,14 +68,16 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
game.UnregisterImportHandler(this);
|
|
||||||
|
if (game.IsNotNull())
|
||||||
|
game.UnregisterImportHandler(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Popover GetPopover() => new FileChooserPopover(handledExtensions, currentFile);
|
public override Popover GetPopover() => new FileChooserPopover(handledExtensions, currentFile);
|
||||||
|
|
||||||
private class FileChooserPopover : OsuPopover
|
private class FileChooserPopover : OsuPopover
|
||||||
{
|
{
|
||||||
public FileChooserPopover(string[] handledExtensions, Bindable<FileInfo> currentFile)
|
public FileChooserPopover(string[] handledExtensions, Bindable<FileInfo?> currentFile)
|
||||||
{
|
{
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Padding = new MarginPadding(10);
|
Padding = new MarginPadding(10) { Bottom = 0 };
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OverlayColourProvider overlayColourProvider { get; set; }
|
private OverlayColourProvider overlayColourProvider { get; set; }
|
||||||
|
|
||||||
|
public bool EnableClicking { get; set; } = true;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
@ -281,6 +283,9 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
|
if (!EnableClicking)
|
||||||
|
return;
|
||||||
|
|
||||||
var channel = clunk?.GetChannel();
|
var channel = clunk?.GetChannel();
|
||||||
|
|
||||||
if (channel != null)
|
if (channel != null)
|
||||||
|
92
osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs
Normal file
92
osu.Game/Screens/Edit/Timing/RepeatingButtonBehaviour.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a component that provides the behaviour of triggering button clicks repeatedly while holding with mouse.
|
||||||
|
/// </summary>
|
||||||
|
public class RepeatingButtonBehaviour : Component
|
||||||
|
{
|
||||||
|
private const double initial_delay = 300;
|
||||||
|
private const double minimum_delay = 80;
|
||||||
|
|
||||||
|
private readonly Drawable button;
|
||||||
|
|
||||||
|
private Sample sample;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An additive modifier for the frequency of the sample played on next actuation.
|
||||||
|
/// This can be adjusted during the button's <see cref="Drawable.OnClick"/> event to affect the repeat sample playback of that click.
|
||||||
|
/// </summary>
|
||||||
|
public double SampleFrequencyModifier { get; set; }
|
||||||
|
|
||||||
|
public RepeatingButtonBehaviour(Drawable button)
|
||||||
|
{
|
||||||
|
this.button = button;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(AudioManager audio)
|
||||||
|
{
|
||||||
|
sample = audio.Samples.Get(@"UI/notch-tick");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
beginRepeat();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
adjustDelegate?.Cancel();
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate adjustDelegate;
|
||||||
|
private double adjustDelay = initial_delay;
|
||||||
|
|
||||||
|
private void beginRepeat()
|
||||||
|
{
|
||||||
|
adjustDelegate?.Cancel();
|
||||||
|
|
||||||
|
adjustDelay = initial_delay;
|
||||||
|
adjustNext();
|
||||||
|
|
||||||
|
void adjustNext()
|
||||||
|
{
|
||||||
|
if (IsHovered)
|
||||||
|
{
|
||||||
|
button.TriggerClick();
|
||||||
|
adjustDelay = Math.Max(minimum_delay, adjustDelay * 0.9f);
|
||||||
|
|
||||||
|
var channel = sample?.GetChannel();
|
||||||
|
|
||||||
|
if (channel != null)
|
||||||
|
{
|
||||||
|
double repeatModifier = 0.05f * (Math.Abs(adjustDelay - initial_delay) / minimum_delay);
|
||||||
|
channel.Frequency.Value = 1 + repeatModifier + SampleFrequencyModifier;
|
||||||
|
channel.Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adjustDelay = initial_delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustDelegate = Scheduler.AddDelayed(adjustNext, adjustDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
{
|
{
|
||||||
Flow = new FillFlowContainer
|
Flow = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding(20),
|
Padding = new MarginPadding(10) { Top = 0 },
|
||||||
Spacing = new Vector2(20),
|
Spacing = new Vector2(20),
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
|
418
osu.Game/Screens/Edit/Timing/TapButton.cs
Normal file
418
osu.Game/Screens/Edit/Timing/TapButton.cs
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
|
{
|
||||||
|
internal class TapButton : CircularContainer, IKeyBindingHandler<GlobalAction>
|
||||||
|
{
|
||||||
|
public const float SIZE = 140;
|
||||||
|
|
||||||
|
public readonly BindableBool IsHandlingTapping = new BindableBool();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Bindable<ControlPointGroup>? selectedGroup { get; set; }
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private IBeatSyncProvider? beatSyncSource { get; set; }
|
||||||
|
|
||||||
|
private Circle hoverLayer = null!;
|
||||||
|
|
||||||
|
private CircularContainer innerCircle = null!;
|
||||||
|
private Box innerCircleHighlight = null!;
|
||||||
|
|
||||||
|
private int currentLight;
|
||||||
|
|
||||||
|
private Container scaleContainer = null!;
|
||||||
|
private Container lights = null!;
|
||||||
|
private Container lightsGlow = null!;
|
||||||
|
private OsuSpriteText bpmText = null!;
|
||||||
|
private Container textContainer = null!;
|
||||||
|
|
||||||
|
private bool grabbedMouseDown;
|
||||||
|
|
||||||
|
private ScheduledDelegate? resetDelegate;
|
||||||
|
|
||||||
|
private const int light_count = 8;
|
||||||
|
|
||||||
|
private const int initial_taps_to_ignore = 4;
|
||||||
|
|
||||||
|
private const int max_taps_to_consider = 128;
|
||||||
|
|
||||||
|
private const double transition_length = 500;
|
||||||
|
|
||||||
|
private const float angular_light_gap = 0.007f;
|
||||||
|
|
||||||
|
private readonly List<double> tapTimings = new List<double>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Size = new Vector2(SIZE);
|
||||||
|
|
||||||
|
const float ring_width = 22;
|
||||||
|
const float light_padding = 3;
|
||||||
|
|
||||||
|
InternalChild = scaleContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4
|
||||||
|
},
|
||||||
|
lights = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Name = @"outer masking",
|
||||||
|
Masking = true,
|
||||||
|
BorderThickness = light_padding,
|
||||||
|
BorderColour = colourProvider.Background4,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.Black,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
Name = @"inner masking",
|
||||||
|
Size = new Vector2(SIZE - ring_width * 2 + light_padding * 2),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
},
|
||||||
|
lightsGlow = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
innerCircle = new CircularContainer
|
||||||
|
{
|
||||||
|
Size = new Vector2(SIZE - ring_width * 2),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Background2,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
innerCircleHighlight = new Box
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Colour3,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
},
|
||||||
|
textContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background1,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.Torus.With(size: 34, weight: FontWeight.SemiBold),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Y = 5,
|
||||||
|
Text = "Tap",
|
||||||
|
},
|
||||||
|
bpmText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.Torus.With(size: 23, weight: FontWeight.Regular),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Y = -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hoverLayer = new Circle
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background1.Opacity(0.3f),
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
Alpha = 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < light_count; i++)
|
||||||
|
{
|
||||||
|
var light = new Light
|
||||||
|
{
|
||||||
|
Rotation = (i + 1) * (360f / light_count) + 360 * angular_light_gap / 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
lights.Add(light);
|
||||||
|
lightsGlow.Add(light.Glow.CreateProxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||||
|
hoverLayer.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
private ColourInfo textColour
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (grabbedMouseDown)
|
||||||
|
return colourProvider.Background4;
|
||||||
|
|
||||||
|
if (IsHovered)
|
||||||
|
return colourProvider.Content2;
|
||||||
|
|
||||||
|
return colourProvider.Background1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
hoverLayer.FadeIn(transition_length, Easing.OutQuint);
|
||||||
|
textContainer.FadeColour(textColour, transition_length, Easing.OutQuint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
hoverLayer.FadeOut(transition_length, Easing.OutQuint);
|
||||||
|
textContainer.FadeColour(textColour, transition_length, Easing.OutQuint);
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
const double in_duration = 100;
|
||||||
|
|
||||||
|
grabbedMouseDown = true;
|
||||||
|
IsHandlingTapping.Value = true;
|
||||||
|
|
||||||
|
resetDelegate?.Cancel();
|
||||||
|
|
||||||
|
handleTap();
|
||||||
|
|
||||||
|
textContainer.FadeColour(textColour, in_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
scaleContainer.ScaleTo(0.99f, in_duration, Easing.OutQuint);
|
||||||
|
innerCircle.ScaleTo(0.96f, in_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
innerCircleHighlight
|
||||||
|
.FadeIn(50, Easing.OutQuint)
|
||||||
|
.FlashColour(Color4.White, 1000, Easing.OutQuint);
|
||||||
|
|
||||||
|
lights[currentLight % light_count].Hide();
|
||||||
|
lights[(currentLight + light_count / 2) % light_count].Hide();
|
||||||
|
|
||||||
|
currentLight++;
|
||||||
|
|
||||||
|
lights[currentLight % light_count].Show();
|
||||||
|
lights[(currentLight + light_count / 2) % light_count].Show();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
const double out_duration = 800;
|
||||||
|
|
||||||
|
grabbedMouseDown = false;
|
||||||
|
|
||||||
|
textContainer.FadeColour(textColour, out_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
scaleContainer.ScaleTo(1, out_duration, Easing.OutQuint);
|
||||||
|
innerCircle.ScaleTo(1, out_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
innerCircleHighlight.FadeOut(out_duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
resetDelegate = Scheduler.AddDelayed(reset, 1000);
|
||||||
|
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
if (e.Action == GlobalAction.EditorTapForBPM && !e.Repeat)
|
||||||
|
{
|
||||||
|
// Direct through mouse handling to achieve animation
|
||||||
|
OnMouseDown(new MouseDownEvent(e.CurrentState, MouseButton.Left));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
if (e.Action == GlobalAction.EditorTapForBPM)
|
||||||
|
OnMouseUp(new MouseUpEvent(e.CurrentState, MouseButton.Left));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTap()
|
||||||
|
{
|
||||||
|
tapTimings.Add(Clock.CurrentTime);
|
||||||
|
|
||||||
|
if (tapTimings.Count > initial_taps_to_ignore + max_taps_to_consider)
|
||||||
|
tapTimings.RemoveAt(0);
|
||||||
|
|
||||||
|
if (tapTimings.Count < initial_taps_to_ignore * 2)
|
||||||
|
{
|
||||||
|
bpmText.Text = new string('.', tapTimings.Count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double averageBeatLength = (tapTimings.Last() - tapTimings.Skip(initial_taps_to_ignore).First()) / (tapTimings.Count - initial_taps_to_ignore - 1);
|
||||||
|
double clockRate = beatSyncSource?.Clock?.Rate ?? 1;
|
||||||
|
|
||||||
|
double bpm = Math.Round(60000 / averageBeatLength / clockRate);
|
||||||
|
|
||||||
|
bpmText.Text = $"{bpm} BPM";
|
||||||
|
|
||||||
|
var timingPoint = selectedGroup?.Value.ControlPoints.OfType<TimingControlPoint>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (timingPoint != null)
|
||||||
|
{
|
||||||
|
// Intentionally use the rounded BPM here.
|
||||||
|
timingPoint.BeatLength = 60000 / bpm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset()
|
||||||
|
{
|
||||||
|
bpmText.FadeOut(transition_length, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(tapTimings.Count > 0 ? transition_length : 0))
|
||||||
|
{
|
||||||
|
Schedule(() => bpmText.Text = "the beat!");
|
||||||
|
bpmText.FadeIn(800, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var light in lights)
|
||||||
|
light.Hide();
|
||||||
|
|
||||||
|
tapTimings.Clear();
|
||||||
|
currentLight = 0;
|
||||||
|
IsHandlingTapping.Value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Light : CompositeDrawable
|
||||||
|
{
|
||||||
|
public Drawable Glow { get; private set; } = null!;
|
||||||
|
|
||||||
|
private Container fillContent = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
Size = new Vector2(0.98f); // Avoid bleed into masking edge.
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new CircularProgress
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Current = { Value = 1f / light_count - angular_light_gap },
|
||||||
|
Colour = colourProvider.Background2,
|
||||||
|
},
|
||||||
|
fillContent = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
Colour = colourProvider.Colour1,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new CircularProgress
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Current = { Value = 1f / light_count - angular_light_gap },
|
||||||
|
Blending = BlendingParameters.Additive
|
||||||
|
},
|
||||||
|
// Please do not try and make sense of this.
|
||||||
|
// Getting the visual effect I was going for relies on what I can only imagine is broken implementation
|
||||||
|
// of `PadExtent`. If that's ever fixed in the future this will likely need to be adjusted.
|
||||||
|
Glow = new CircularProgress
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Current = { Value = 1f / light_count - 0.01f },
|
||||||
|
Blending = BlendingParameters.Additive
|
||||||
|
}.WithEffect(new GlowEffect
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Colour1.Opacity(0.4f),
|
||||||
|
BlurSigma = new Vector2(9f),
|
||||||
|
Strength = 10,
|
||||||
|
PadExtent = true
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
fillContent
|
||||||
|
.FadeIn(50, Easing.OutQuint)
|
||||||
|
.FlashColour(Color4.White, 1000, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Hide()
|
||||||
|
{
|
||||||
|
fillContent
|
||||||
|
.FadeOut(300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,45 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Timing
|
namespace osu.Game.Screens.Edit.Timing
|
||||||
{
|
{
|
||||||
public class TapTimingControl : CompositeDrawable
|
public class TapTimingControl : CompositeDrawable
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorClock editorClock { get; set; }
|
private EditorClock editorClock { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorBeatmap beatmap { get; set; }
|
private EditorBeatmap beatmap { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<ControlPointGroup> selectedGroup { get; set; }
|
private Bindable<ControlPointGroup> selectedGroup { get; set; } = null!;
|
||||||
|
|
||||||
|
private readonly BindableBool isHandlingTapping = new BindableBool();
|
||||||
|
|
||||||
|
private MetronomeDisplay metronome = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colourProvider, OsuColour colours)
|
private void load(OverlayColourProvider colourProvider, OsuColour colours)
|
||||||
{
|
{
|
||||||
|
const float padding = 10;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
@ -48,8 +60,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
RowDimensions = new[]
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
new Dimension(GridSizeMode.Absolute, 200),
|
new Dimension(GridSizeMode.Absolute, 200),
|
||||||
new Dimension(GridSizeMode.Absolute, 60),
|
new Dimension(GridSizeMode.Absolute, 50),
|
||||||
new Dimension(GridSizeMode.Absolute, 60),
|
new Dimension(GridSizeMode.Absolute, TapButton.SIZE + padding),
|
||||||
},
|
},
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
@ -58,6 +70,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding(padding),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new GridContainer
|
new GridContainer
|
||||||
@ -72,7 +85,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new MetronomeDisplay
|
metronome = new MetronomeDisplay
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
@ -89,15 +102,14 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding(10),
|
Padding = new MarginPadding { Bottom = padding, Horizontal = padding },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new TimingAdjustButton(1)
|
new TimingAdjustButton(1)
|
||||||
{
|
{
|
||||||
Text = "Offset",
|
Text = "Offset",
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.48f,
|
Size = new Vector2(0.48f, 1),
|
||||||
Height = 50,
|
|
||||||
Action = adjustOffset,
|
Action = adjustOffset,
|
||||||
},
|
},
|
||||||
new TimingAdjustButton(0.1)
|
new TimingAdjustButton(0.1)
|
||||||
@ -105,9 +117,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Text = "BPM",
|
Text = "BPM",
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Width = 0.48f,
|
Size = new Vector2(0.48f, 1),
|
||||||
Height = 50,
|
|
||||||
Action = adjustBpm,
|
Action = adjustBpm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,33 +129,70 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding(10),
|
Padding = new MarginPadding { Bottom = padding, Horizontal = padding },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new RoundedButton
|
new Container
|
||||||
{
|
{
|
||||||
Text = "Reset",
|
RelativeSizeAxes = Axes.Y,
|
||||||
BackgroundColour = colours.Pink,
|
Anchor = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
Origin = Anchor.CentreRight,
|
||||||
Width = 0.3f,
|
Height = 0.98f,
|
||||||
Action = reset,
|
Width = TapButton.SIZE / 1.3f,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 15,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new InlineButton(FontAwesome.Solid.Stop, Anchor.TopLeft)
|
||||||
|
{
|
||||||
|
BackgroundColour = colourProvider.Background1,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.49f,
|
||||||
|
Action = reset,
|
||||||
|
},
|
||||||
|
new InlineButton(FontAwesome.Solid.Play, Anchor.BottomLeft)
|
||||||
|
{
|
||||||
|
BackgroundColour = colourProvider.Background1,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.49f,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Action = start,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
new RoundedButton
|
new TapButton
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.Centre,
|
||||||
Text = "Play from start",
|
IsHandlingTapping = { BindTarget = isHandlingTapping }
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
BackgroundColour = colourProvider.Background1,
|
|
||||||
Width = 0.68f,
|
|
||||||
Action = tap,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isHandlingTapping.BindValueChanged(handling =>
|
||||||
|
{
|
||||||
|
metronome.EnableClicking = !handling.NewValue;
|
||||||
|
|
||||||
|
if (handling.NewValue)
|
||||||
|
start();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start()
|
||||||
|
{
|
||||||
|
editorClock.Seek(selectedGroup.Value.Time);
|
||||||
|
editorClock.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset()
|
||||||
|
{
|
||||||
|
editorClock.Stop();
|
||||||
|
editorClock.Seek(selectedGroup.Value.Time);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void adjustOffset(double adjust)
|
private void adjustOffset(double adjust)
|
||||||
@ -176,16 +224,66 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
timing.BeatLength = 60000 / (timing.BPM + adjust);
|
timing.BeatLength = 60000 / (timing.BPM + adjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tap()
|
private class InlineButton : OsuButton
|
||||||
{
|
{
|
||||||
editorClock.Seek(selectedGroup.Value.Time);
|
private readonly IconUsage icon;
|
||||||
editorClock.Start();
|
private readonly Anchor anchor;
|
||||||
}
|
|
||||||
|
|
||||||
private void reset()
|
private SpriteIcon spriteIcon = null!;
|
||||||
{
|
|
||||||
editorClock.Stop();
|
[Resolved]
|
||||||
editorClock.Seek(selectedGroup.Value.Time);
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
public InlineButton(IconUsage icon, Anchor anchor)
|
||||||
|
{
|
||||||
|
this.icon = icon;
|
||||||
|
this.anchor = anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Content.CornerRadius = 0;
|
||||||
|
Content.Masking = false;
|
||||||
|
|
||||||
|
BackgroundColour = colourProvider.Background2;
|
||||||
|
|
||||||
|
Content.Add(new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding(15),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
spriteIcon = new SpriteIcon
|
||||||
|
{
|
||||||
|
Icon = icon,
|
||||||
|
Size = new Vector2(22),
|
||||||
|
Anchor = anchor,
|
||||||
|
Origin = anchor,
|
||||||
|
Colour = colourProvider.Background1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
// scale looks bad so don't call base.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
spriteIcon.FadeColour(colourProvider.Content2, 200, Easing.OutQuint);
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
spriteIcon.FadeColour(colourProvider.Background1, 200, Easing.OutQuint);
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
|
||||||
using osu.Framework.Audio.Sample;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -26,32 +23,24 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
public Action<double> Action;
|
public Action<double> Action;
|
||||||
|
|
||||||
private readonly double adjustAmount;
|
private readonly double adjustAmount;
|
||||||
private ScheduledDelegate adjustDelegate;
|
|
||||||
|
|
||||||
private const int max_multiplier = 10;
|
private const int max_multiplier = 10;
|
||||||
|
|
||||||
private const int adjust_levels = 4;
|
private const int adjust_levels = 4;
|
||||||
|
|
||||||
private const double initial_delay = 300;
|
|
||||||
|
|
||||||
private const double minimum_delay = 80;
|
|
||||||
|
|
||||||
public Container Content { get; set; }
|
public Container Content { get; set; }
|
||||||
|
|
||||||
private double adjustDelay = initial_delay;
|
|
||||||
|
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
private readonly OsuSpriteText text;
|
private readonly OsuSpriteText text;
|
||||||
|
|
||||||
private Sample sample;
|
|
||||||
|
|
||||||
public LocalisableString Text
|
public LocalisableString Text
|
||||||
{
|
{
|
||||||
get => text.Text;
|
get => text.Text;
|
||||||
set => text.Text = value;
|
set => text.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly RepeatingButtonBehaviour repeatBehaviour;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OverlayColourProvider colourProvider { get; set; }
|
private OverlayColourProvider colourProvider { get; set; }
|
||||||
|
|
||||||
@ -82,13 +71,13 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddInternal(repeatBehaviour = new RepeatingButtonBehaviour(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load()
|
||||||
{
|
{
|
||||||
sample = audio.Samples.Get(@"UI/notch-tick");
|
|
||||||
|
|
||||||
background.Colour = colourProvider.Background3;
|
background.Colour = colourProvider.Background3;
|
||||||
|
|
||||||
for (int i = 1; i <= adjust_levels; i++)
|
for (int i = 1; i <= adjust_levels; i++)
|
||||||
@ -98,57 +87,22 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
beginRepeat();
|
var hoveredBox = Content.OfType<IncrementBox>().FirstOrDefault(d => d.IsHovered);
|
||||||
|
if (hoveredBox == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Action(adjustAmount * hoveredBox.Multiplier);
|
||||||
|
|
||||||
|
hoveredBox.Flash();
|
||||||
|
|
||||||
|
repeatBehaviour.SampleFrequencyModifier = (hoveredBox.Multiplier / max_multiplier) * 0.2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
|
||||||
{
|
|
||||||
adjustDelegate?.Cancel();
|
|
||||||
base.OnMouseUp(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void beginRepeat()
|
|
||||||
{
|
|
||||||
adjustDelegate?.Cancel();
|
|
||||||
|
|
||||||
adjustDelay = initial_delay;
|
|
||||||
adjustNext();
|
|
||||||
|
|
||||||
void adjustNext()
|
|
||||||
{
|
|
||||||
var hoveredBox = Content.OfType<IncrementBox>().FirstOrDefault(d => d.IsHovered);
|
|
||||||
|
|
||||||
if (hoveredBox != null)
|
|
||||||
{
|
|
||||||
Action(adjustAmount * hoveredBox.Multiplier);
|
|
||||||
|
|
||||||
adjustDelay = Math.Max(minimum_delay, adjustDelay * 0.9f);
|
|
||||||
|
|
||||||
hoveredBox.Flash();
|
|
||||||
|
|
||||||
var channel = sample?.GetChannel();
|
|
||||||
|
|
||||||
if (channel != null)
|
|
||||||
{
|
|
||||||
double repeatModifier = 0.05f * (Math.Abs(adjustDelay - initial_delay) / minimum_delay);
|
|
||||||
double multiplierModifier = (hoveredBox.Multiplier / max_multiplier) * 0.2f;
|
|
||||||
|
|
||||||
channel.Frequency.Value = 1 + multiplierModifier + repeatModifier;
|
|
||||||
channel.Play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
adjustDelay = initial_delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
adjustDelegate = Scheduler.AddDelayed(adjustNext, adjustDelay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class IncrementBox : CompositeDrawable
|
private class IncrementBox : CompositeDrawable
|
||||||
{
|
{
|
||||||
public readonly float Multiplier;
|
public readonly float Multiplier;
|
||||||
@ -187,7 +141,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
Origin = direction,
|
Origin = direction,
|
||||||
Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold),
|
Font = OsuFont.Default.With(size: 10, weight: FontWeight.Bold),
|
||||||
Text = $"{(index > 0 ? "+" : "-")}{Math.Abs(Multiplier * amount)}",
|
Text = $"{(index > 0 ? "+" : "-")}{Math.Abs(Multiplier * amount)}",
|
||||||
Padding = new MarginPadding(5),
|
Padding = new MarginPadding(2),
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -51,6 +53,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
|
|
||||||
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
|
||||||
|
|
||||||
|
private RoundedButton addButton;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private EditorClock clock { get; set; }
|
private EditorClock clock { get; set; }
|
||||||
|
|
||||||
@ -105,9 +109,8 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
},
|
},
|
||||||
new RoundedButton
|
addButton = new RoundedButton
|
||||||
{
|
{
|
||||||
Text = "+ Add at current time",
|
|
||||||
Action = addNew,
|
Action = addNew,
|
||||||
Size = new Vector2(160, 30),
|
Size = new Vector2(160, 30),
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
@ -122,7 +125,14 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
selectedGroup.BindValueChanged(selected =>
|
||||||
|
{
|
||||||
|
deleteButton.Enabled.Value = selected.NewValue != null;
|
||||||
|
|
||||||
|
addButton.Text = selected.NewValue != null
|
||||||
|
? "+ Clone to current time"
|
||||||
|
: "+ Add at current time";
|
||||||
|
}, true);
|
||||||
|
|
||||||
controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups);
|
controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups);
|
||||||
controlPointGroups.BindCollectionChanged((sender, args) =>
|
controlPointGroups.BindCollectionChanged((sender, args) =>
|
||||||
@ -132,13 +142,23 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
selectedGroup.Value = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
trackActivePoint();
|
trackActivePoint();
|
||||||
|
|
||||||
|
addButton.Enabled.Value = clock.CurrentTimeAccurate != selectedGroup.Value?.Time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Type trackedType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given the user has selected a control point group, we want to track any group which is
|
/// Given the user has selected a control point group, we want to track any group which is
|
||||||
/// active at the current point in time which matches the type the user has selected.
|
/// active at the current point in time which matches the type the user has selected.
|
||||||
@ -149,16 +169,27 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
private void trackActivePoint()
|
private void trackActivePoint()
|
||||||
{
|
{
|
||||||
// For simplicity only match on the first type of the active control point.
|
// For simplicity only match on the first type of the active control point.
|
||||||
var selectedPointType = selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
if (selectedGroup.Value == null)
|
||||||
|
trackedType = null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the selected group only has one control point, update the tracking type.
|
||||||
|
if (selectedGroup.Value.ControlPoints.Count == 1)
|
||||||
|
trackedType = selectedGroup.Value?.ControlPoints.Single().GetType();
|
||||||
|
// If the selected group has more than one control point, choose the first as the tracking type
|
||||||
|
// if we don't already have a singular tracked type.
|
||||||
|
else if (trackedType == null)
|
||||||
|
trackedType = selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedPointType != null)
|
if (trackedType != null)
|
||||||
{
|
{
|
||||||
// We don't have an efficient way of looking up groups currently, only individual point types.
|
// We don't have an efficient way of looking up groups currently, only individual point types.
|
||||||
// To improve the efficiency of this in the future, we should reconsider the overall structure of ControlPointInfo.
|
// To improve the efficiency of this in the future, we should reconsider the overall structure of ControlPointInfo.
|
||||||
|
|
||||||
// Find the next group which has the same type as the selected one.
|
// Find the next group which has the same type as the selected one.
|
||||||
var found = Beatmap.ControlPointInfo.Groups
|
var found = Beatmap.ControlPointInfo.Groups
|
||||||
.Where(g => g.ControlPoints.Any(cp => cp.GetType() == selectedPointType))
|
.Where(g => g.ControlPoints.Any(cp => cp.GetType() == trackedType))
|
||||||
.LastOrDefault(g => g.Time <= clock.CurrentTimeAccurate);
|
.LastOrDefault(g => g.Time <= clock.CurrentTimeAccurate);
|
||||||
|
|
||||||
if (found != null)
|
if (found != null)
|
||||||
@ -189,7 +220,7 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
// Try and create matching types from the currently selected control point.
|
// Try and create matching types from the currently selected control point.
|
||||||
var selected = selectedGroup.Value;
|
var selected = selectedGroup.Value;
|
||||||
|
|
||||||
if (selected != null)
|
if (selected != null && selected != group)
|
||||||
{
|
{
|
||||||
foreach (var controlPoint in selected.ControlPoints)
|
foreach (var controlPoint in selected.ControlPoints)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
private void load(AudioManager audio)
|
private void load(AudioManager audio)
|
||||||
{
|
{
|
||||||
sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select");
|
sampleSelect = audio.Samples.Get($@"UI/{HoverSampleSet.Default.GetDescription()}-select");
|
||||||
sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Submit.GetDescription()}-select");
|
sampleJoin = audio.Samples.Get($@"UI/{HoverSampleSet.Button.GetDescription()}-select");
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -159,6 +159,8 @@ namespace osu.Game.Screens.Ranking.Expanded
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Text = beatmap.DifficultyName,
|
Text = beatmap.DifficultyName,
|
||||||
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold),
|
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold),
|
||||||
|
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
|
||||||
|
Truncate = true,
|
||||||
},
|
},
|
||||||
new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12))
|
new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12))
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
@ -633,7 +634,7 @@ namespace osu.Game.Screens.Select
|
|||||||
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
||||||
{
|
{
|
||||||
// handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed).
|
// handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed).
|
||||||
if ((invalidation & Invalidation.Layout) > 0)
|
if (invalidation.HasFlagFast(Invalidation.DrawSize))
|
||||||
itemsCache.Invalidate();
|
itemsCache.Invalidate();
|
||||||
|
|
||||||
return base.OnInvalidate(invalidation, source);
|
return base.OnInvalidate(invalidation, source);
|
||||||
|
@ -8,6 +8,7 @@ using System.Threading;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -244,7 +245,7 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hideRequested != null)
|
if (hideRequested != null)
|
||||||
items.Add(new OsuMenuItem("Hide", MenuItemType.Destructive, () => hideRequested(beatmapInfo)));
|
items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo)));
|
||||||
|
|
||||||
return items.ToArray();
|
return items.ToArray();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Screens.Select.Filter
|
|||||||
[Description("Author")]
|
[Description("Author")]
|
||||||
Author,
|
Author,
|
||||||
|
|
||||||
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowStatsBpm))]
|
[LocalisableDescription(typeof(SortStrings), nameof(SortStrings.ArtistTracksBpm))]
|
||||||
BPM,
|
BPM,
|
||||||
|
|
||||||
[Description("Date Added")]
|
[Description("Date Added")]
|
||||||
@ -28,10 +28,10 @@ namespace osu.Game.Screens.Select.Filter
|
|||||||
Length,
|
Length,
|
||||||
|
|
||||||
// todo: pending support (https://github.com/ppy/osu/issues/4917)
|
// todo: pending support (https://github.com/ppy/osu/issues/4917)
|
||||||
// [LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchFiltersRank))]
|
// [Description("Rank Achieved")]
|
||||||
// RankAchieved,
|
// RankAchieved,
|
||||||
|
|
||||||
[LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowInfoSource))]
|
[Description("Source")]
|
||||||
Source,
|
Source,
|
||||||
|
|
||||||
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))]
|
[LocalisableDescription(typeof(BeatmapsStrings), nameof(BeatmapsStrings.ListingSearchSortingTitle))]
|
||||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Screens.Select
|
|||||||
private readonly Box light;
|
private readonly Box light;
|
||||||
|
|
||||||
public FooterButton()
|
public FooterButton()
|
||||||
: base(HoverSampleSet.Button)
|
: base(HoverSampleSet.Toolbar)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Shear = SHEAR;
|
Shear = SHEAR;
|
||||||
|
@ -77,7 +77,7 @@ namespace osu.Game.Screens.Select.Options
|
|||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public BeatmapOptionsButton()
|
public BeatmapOptionsButton()
|
||||||
: base(HoverSampleSet.Submit)
|
: base(HoverSampleSet.Button)
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
@ -29,6 +29,8 @@ namespace osu.Game.Skinning.Editor
|
|||||||
{
|
{
|
||||||
public const double TRANSITION_DURATION = 500;
|
public const double TRANSITION_DURATION = 500;
|
||||||
|
|
||||||
|
public const float MENU_HEIGHT = 40;
|
||||||
|
|
||||||
public readonly BindableList<ISkinnableDrawable> SelectedComponents = new BindableList<ISkinnableDrawable>();
|
public readonly BindableList<ISkinnableDrawable> SelectedComponents = new BindableList<ISkinnableDrawable>();
|
||||||
|
|
||||||
protected override bool StartHidden => true;
|
protected override bool StartHidden => true;
|
||||||
@ -78,8 +80,6 @@ namespace osu.Game.Skinning.Editor
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
const float menu_height = 40;
|
|
||||||
|
|
||||||
InternalChild = new OsuContextMenuContainer
|
InternalChild = new OsuContextMenuContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -102,7 +102,7 @@ namespace osu.Game.Skinning.Editor
|
|||||||
Name = "Menu container",
|
Name = "Menu container",
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Depth = float.MinValue,
|
Depth = float.MinValue,
|
||||||
Height = menu_height,
|
Height = MENU_HEIGHT,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new EditorMenuBar
|
new EditorMenuBar
|
||||||
@ -322,7 +322,10 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
this.FadeIn(TRANSITION_DURATION, Easing.OutQuint);
|
this
|
||||||
|
// align animation to happen after the majority of the ScalingContainer animation completes.
|
||||||
|
.Delay(ScalingContainer.TRANSITION_DURATION * 0.3f)
|
||||||
|
.FadeIn(TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
|
@ -12,6 +12,8 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Edit.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Skinning.Editor
|
namespace osu.Game.Skinning.Editor
|
||||||
{
|
{
|
||||||
@ -33,6 +35,8 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
private OsuScreen lastTargetScreen;
|
private OsuScreen lastTargetScreen;
|
||||||
|
|
||||||
|
private Vector2 lastDrawSize;
|
||||||
|
|
||||||
public SkinEditorOverlay(ScalingContainer scalingContainer)
|
public SkinEditorOverlay(ScalingContainer scalingContainer)
|
||||||
{
|
{
|
||||||
this.scalingContainer = scalingContainer;
|
this.scalingContainer = scalingContainer;
|
||||||
@ -81,15 +85,42 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
protected override void PopOut() => skinEditor?.Hide();
|
protected override void PopOut() => skinEditor?.Hide();
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (game.DrawSize != lastDrawSize)
|
||||||
|
{
|
||||||
|
lastDrawSize = game.DrawSize;
|
||||||
|
updateScreenSizing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateScreenSizing()
|
||||||
|
{
|
||||||
|
if (skinEditor?.State.Value != Visibility.Visible) return;
|
||||||
|
|
||||||
|
const float padding = 10;
|
||||||
|
|
||||||
|
float relativeSidebarWidth = (EditorSidebar.WIDTH + padding) / DrawWidth;
|
||||||
|
float relativeToolbarHeight = (SkinEditorSceneLibrary.HEIGHT + SkinEditor.MENU_HEIGHT + padding) / DrawHeight;
|
||||||
|
|
||||||
|
var rect = new RectangleF(
|
||||||
|
relativeSidebarWidth,
|
||||||
|
relativeToolbarHeight,
|
||||||
|
1 - relativeSidebarWidth * 2,
|
||||||
|
1f - relativeToolbarHeight - padding / DrawHeight);
|
||||||
|
|
||||||
|
scalingContainer.SetCustomRect(rect, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateComponentVisibility()
|
private void updateComponentVisibility()
|
||||||
{
|
{
|
||||||
Debug.Assert(skinEditor != null);
|
Debug.Assert(skinEditor != null);
|
||||||
|
|
||||||
const float toolbar_padding_requirement = 0.18f;
|
|
||||||
|
|
||||||
if (skinEditor.State.Value == Visibility.Visible)
|
if (skinEditor.State.Value == Visibility.Visible)
|
||||||
{
|
{
|
||||||
scalingContainer.SetCustomRect(new RectangleF(toolbar_padding_requirement, 0.2f, 0.8f - toolbar_padding_requirement, 0.7f), true);
|
Scheduler.AddOnce(updateScreenSizing);
|
||||||
|
|
||||||
game?.Toolbar.Hide();
|
game?.Toolbar.Hide();
|
||||||
game?.CloseAllOverlays();
|
game?.CloseAllOverlays();
|
||||||
@ -127,6 +158,9 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
private void setTarget(OsuScreen target)
|
private void setTarget(OsuScreen target)
|
||||||
{
|
{
|
||||||
|
if (target == null)
|
||||||
|
return;
|
||||||
|
|
||||||
Debug.Assert(skinEditor != null);
|
Debug.Assert(skinEditor != null);
|
||||||
|
|
||||||
if (!target.IsLoaded)
|
if (!target.IsLoaded)
|
||||||
|
@ -27,6 +27,8 @@ namespace osu.Game.Skinning.Editor
|
|||||||
{
|
{
|
||||||
public class SkinEditorSceneLibrary : CompositeDrawable
|
public class SkinEditorSceneLibrary : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
public const float HEIGHT = BUTTON_HEIGHT + padding * 2;
|
||||||
|
|
||||||
public const float BUTTON_HEIGHT = 40;
|
public const float BUTTON_HEIGHT = 40;
|
||||||
|
|
||||||
private const float padding = 10;
|
private const float padding = 10;
|
||||||
@ -42,7 +44,7 @@ namespace osu.Game.Skinning.Editor
|
|||||||
|
|
||||||
public SkinEditorSceneLibrary()
|
public SkinEditorSceneLibrary()
|
||||||
{
|
{
|
||||||
Height = BUTTON_HEIGHT + padding * 2;
|
Height = HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -303,8 +303,13 @@ namespace osu.Game.Skinning
|
|||||||
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val))
|
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val))
|
||||||
{
|
{
|
||||||
// special case for handling skins which use 1 or 0 to signify a boolean state.
|
// special case for handling skins which use 1 or 0 to signify a boolean state.
|
||||||
|
// ..or in some cases 2 (https://github.com/ppy/osu/issues/18579).
|
||||||
if (typeof(TValue) == typeof(bool))
|
if (typeof(TValue) == typeof(bool))
|
||||||
val = val == "1" ? "true" : "false";
|
{
|
||||||
|
val = bool.TryParse(val, out bool boolVal)
|
||||||
|
? Convert.ChangeType(boolVal, typeof(bool)).ToString()
|
||||||
|
: Convert.ChangeType(Convert.ToInt32(val), typeof(bool)).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
var bindable = new Bindable<TValue>();
|
var bindable = new Bindable<TValue>();
|
||||||
if (val != null)
|
if (val != null)
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Skinning
|
|||||||
/// The <see cref="ISkin"/> which is being transformed.
|
/// The <see cref="ISkin"/> which is being transformed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NotNull]
|
[NotNull]
|
||||||
protected internal ISkin Skin { get; }
|
public ISkin Skin { get; }
|
||||||
|
|
||||||
protected LegacySkinTransformer([NotNull] ISkin skin)
|
protected LegacySkinTransformer([NotNull] ISkin skin)
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
@ -44,9 +46,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
|||||||
switch (request)
|
switch (request)
|
||||||
{
|
{
|
||||||
case CreateRoomRequest createRoomRequest:
|
case CreateRoomRequest createRoomRequest:
|
||||||
var apiRoom = new Room();
|
var apiRoom = cloneRoom(createRoomRequest.Room);
|
||||||
|
|
||||||
apiRoom.CopyFrom(createRoomRequest.Room);
|
|
||||||
|
|
||||||
// Passwords are explicitly not copied between rooms.
|
// Passwords are explicitly not copied between rooms.
|
||||||
apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value);
|
apiRoom.HasPassword.Value = !string.IsNullOrEmpty(createRoomRequest.Room.Password.Value);
|
||||||
@ -178,12 +178,31 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
|||||||
|
|
||||||
private Room createResponseRoom(Room room, bool withParticipants)
|
private Room createResponseRoom(Room room, bool withParticipants)
|
||||||
{
|
{
|
||||||
var responseRoom = new Room();
|
var responseRoom = cloneRoom(room);
|
||||||
responseRoom.CopyFrom(room);
|
|
||||||
|
// Password is hidden from the response, and is only propagated via HasPassword.
|
||||||
|
bool hadPassword = responseRoom.HasPassword.Value;
|
||||||
responseRoom.Password.Value = null;
|
responseRoom.Password.Value = null;
|
||||||
|
responseRoom.HasPassword.Value = hadPassword;
|
||||||
|
|
||||||
if (!withParticipants)
|
if (!withParticipants)
|
||||||
responseRoom.RecentParticipants.Clear();
|
responseRoom.RecentParticipants.Clear();
|
||||||
|
|
||||||
return responseRoom;
|
return responseRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Room cloneRoom(Room source)
|
||||||
|
{
|
||||||
|
var result = JsonConvert.DeserializeObject<Room>(JsonConvert.SerializeObject(source));
|
||||||
|
Debug.Assert(result != null);
|
||||||
|
|
||||||
|
// Playlist item IDs aren't serialised.
|
||||||
|
if (source.CurrentPlaylistItem.Value != null)
|
||||||
|
result.CurrentPlaylistItem.Value.ID = source.CurrentPlaylistItem.Value.ID;
|
||||||
|
for (int i = 0; i < source.Playlist.Count; i++)
|
||||||
|
result.Playlist[i].ID = source.Playlist[i].ID;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
namespace osu.Game.Users.Drawables
|
namespace osu.Game.Users.Drawables
|
||||||
@ -74,11 +73,6 @@ namespace osu.Game.Users.Drawables
|
|||||||
{
|
{
|
||||||
private LocalisableString tooltip = default_tooltip_text;
|
private LocalisableString tooltip = default_tooltip_text;
|
||||||
|
|
||||||
public ClickableArea()
|
|
||||||
: base(HoverSampleSet.Submit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override LocalisableString TooltipText
|
public override LocalisableString TooltipText
|
||||||
{
|
{
|
||||||
get => Enabled.Value ? tooltip : default;
|
get => Enabled.Value ? tooltip : default;
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Users.Drawables
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
new HoverClickSounds(HoverSampleSet.Submit)
|
new HoverClickSounds()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Users
|
|||||||
protected Drawable Background { get; private set; }
|
protected Drawable Background { get; private set; }
|
||||||
|
|
||||||
protected UserPanel(APIUser user)
|
protected UserPanel(APIUser user)
|
||||||
: base(HoverSampleSet.Submit)
|
: base(HoverSampleSet.Button)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
@ -43,7 +43,7 @@ namespace osu.Game.Utils
|
|||||||
sentrySession = SentrySdk.Init(options =>
|
sentrySession = SentrySdk.Init(options =>
|
||||||
{
|
{
|
||||||
// Not setting the dsn will completely disable sentry.
|
// Not setting the dsn will completely disable sentry.
|
||||||
if (game.IsDeployedBuild)
|
if (game.IsDeployedBuild && game.CreateEndpoints().WebsiteRootUrl.EndsWith(@".ppy.sh", StringComparison.Ordinal))
|
||||||
options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2";
|
options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2";
|
||||||
|
|
||||||
options.AutoSessionTracking = true;
|
options.AutoSessionTracking = true;
|
||||||
@ -159,6 +159,7 @@ namespace osu.Game.Utils
|
|||||||
Game = game.Clock.CurrentTime,
|
Game = game.Clock.CurrentTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
scope.SetTag(@"beatmap", $"{beatmap.OnlineID}");
|
||||||
scope.SetTag(@"ruleset", ruleset.ShortName);
|
scope.SetTag(@"ruleset", ruleset.ShortName);
|
||||||
scope.SetTag(@"os", $"{RuntimeInfo.OS} ({Environment.OSVersion})");
|
scope.SetTag(@"os", $"{RuntimeInfo.OS} ({Environment.OSVersion})");
|
||||||
scope.SetTag(@"processor count", Environment.ProcessorCount.ToString());
|
scope.SetTag(@"processor count", Environment.ProcessorCount.ToString());
|
||||||
|
@ -31,12 +31,12 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
<PackageReference Include="Microsoft.Toolkit.HighPerformance" Version="7.1.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="ppy.LocalisationAnalyser" Version="2022.417.0">
|
<PackageReference Include="ppy.LocalisationAnalyser" Version="2022.607.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.11.2" />
|
<PackageReference Include="Realm" Version="10.14.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2022.530.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2022.605.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.527.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.527.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.17.1" />
|
<PackageReference Include="Sentry" Version="3.17.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
<PackageReference Include="SharpCompress" Version="0.31.0" />
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user