mirror of
https://github.com/ppy/osu.git
synced 2024-12-18 03:05:10 +08:00
Merge remote-tracking branch 'upstream/master' into grids-4
This commit is contained in:
commit
b179e086c6
@ -66,7 +66,7 @@ namespace osu.Desktop.Updater
|
|||||||
{
|
{
|
||||||
Activated = () =>
|
Activated = () =>
|
||||||
{
|
{
|
||||||
restartToApplyUpdate();
|
Task.Run(restartToApplyUpdate);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -88,7 +88,11 @@ namespace osu.Desktop.Updater
|
|||||||
{
|
{
|
||||||
notification = new UpdateProgressNotification
|
notification = new UpdateProgressNotification
|
||||||
{
|
{
|
||||||
CompletionClickAction = restartToApplyUpdate,
|
CompletionClickAction = () =>
|
||||||
|
{
|
||||||
|
Task.Run(restartToApplyUpdate);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Schedule(() => notificationOverlay.Post(notification));
|
Schedule(() => notificationOverlay.Post(notification));
|
||||||
@ -127,13 +131,10 @@ namespace osu.Desktop.Updater
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool restartToApplyUpdate()
|
private async Task restartToApplyUpdate()
|
||||||
{
|
{
|
||||||
// TODO: Migrate this to async flow whenever available (see https://github.com/ppy/osu/pull/28743#discussion_r1740505665).
|
await updateManager.WaitExitThenApplyUpdatesAsync(pendingUpdate?.TargetFullRelease).ConfigureAwait(false);
|
||||||
// Currently there's an internal Thread.Sleep(300) which will cause a stutter when the user clicks to restart.
|
|
||||||
updateManager.WaitExitThenApplyUpdates(pendingUpdate?.TargetFullRelease);
|
|
||||||
Schedule(() => game.AttemptExit());
|
Schedule(() => game.AttemptExit());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageReference Include="Velopack" Version="0.0.598-g933b2ab" />
|
<PackageReference Include="Velopack" Version="0.0.626" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||||
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier,
|
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier,
|
||||||
Mods = mods,
|
Mods = mods,
|
||||||
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet)),
|
MaxCombo = beatmap.GetMaxCombo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
|
@ -81,7 +81,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||||
double drainRate = beatmap.Difficulty.DrainRate;
|
double drainRate = beatmap.Difficulty.DrainRate;
|
||||||
int maxCombo = beatmap.GetMaxCombo();
|
|
||||||
|
|
||||||
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
|
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
|
||||||
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
|
int sliderCount = beatmap.HitObjects.Count(h => h is Slider);
|
||||||
@ -104,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
||||||
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
||||||
DrainRate = drainRate,
|
DrainRate = drainRate,
|
||||||
MaxCombo = maxCombo,
|
MaxCombo = beatmap.GetMaxCombo(),
|
||||||
HitCircleCount = hitCirclesCount,
|
HitCircleCount = hitCirclesCount,
|
||||||
SliderCount = sliderCount,
|
SliderCount = sliderCount,
|
||||||
SpinnerCount = spinnerCount,
|
SpinnerCount = spinnerCount,
|
||||||
|
@ -105,9 +105,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
// is not looking to change the duration of the slider but expand the whole pattern.
|
// is not looking to change the duration of the slider but expand the whole pattern.
|
||||||
if (objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider)
|
if (objectsInScale.Count == 1 && objectsInScale.First().Key is Slider slider)
|
||||||
{
|
{
|
||||||
var originalInfo = objectsInScale[slider];
|
scaleSlider(slider, scale, actualOrigin, objectsInScale[slider], axisRotation);
|
||||||
Debug.Assert(originalInfo.PathControlPointPositions != null && originalInfo.PathControlPointTypes != null);
|
|
||||||
scaleSlider(slider, scale, originalInfo.PathControlPointPositions, originalInfo.PathControlPointTypes, axisRotation);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -159,21 +157,25 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scaleSlider(Slider slider, Vector2 scale, Vector2[] originalPathPositions, PathType?[] originalPathTypes, float axisRotation = 0)
|
private void scaleSlider(Slider slider, Vector2 scale, Vector2 origin, OriginalHitObjectState originalInfo, float axisRotation = 0)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(originalInfo.PathControlPointPositions != null && originalInfo.PathControlPointTypes != null);
|
||||||
|
|
||||||
scale = Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON));
|
scale = Vector2.ComponentMax(scale, new Vector2(Precision.FLOAT_EPSILON));
|
||||||
|
|
||||||
// Maintain the path types in case they were defaulted to bezier at some point during scaling
|
// Maintain the path types in case they were defaulted to bezier at some point during scaling
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
slider.Path.ControlPoints[i].Position = GeometryUtils.GetScaledPosition(scale, Vector2.Zero, originalPathPositions[i], axisRotation);
|
slider.Path.ControlPoints[i].Position = GeometryUtils.GetScaledPosition(scale, Vector2.Zero, originalInfo.PathControlPointPositions[i], axisRotation);
|
||||||
slider.Path.ControlPoints[i].Type = originalPathTypes[i];
|
slider.Path.ControlPoints[i].Type = originalInfo.PathControlPointTypes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snap the slider's length to the current beat divisor
|
// Snap the slider's length to the current beat divisor
|
||||||
// to calculate the final resulting duration / bounding box before the final checks.
|
// to calculate the final resulting duration / bounding box before the final checks.
|
||||||
slider.SnapTo(snapProvider);
|
slider.SnapTo(snapProvider);
|
||||||
|
|
||||||
|
slider.Position = GeometryUtils.GetScaledPosition(scale, origin, originalInfo.Position, axisRotation);
|
||||||
|
|
||||||
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
||||||
Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider });
|
Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider });
|
||||||
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
||||||
@ -182,7 +184,9 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < slider.Path.ControlPoints.Count; i++)
|
||||||
slider.Path.ControlPoints[i].Position = originalPathPositions[i];
|
slider.Path.ControlPoints[i].Position = originalInfo.PathControlPointPositions[i];
|
||||||
|
|
||||||
|
slider.Position = originalInfo.Position;
|
||||||
|
|
||||||
// Snap the slider's length again to undo the potentially-invalid length applied by the previous snap.
|
// Snap the slider's length again to undo the potentially-invalid length applied by the previous snap.
|
||||||
slider.SnapTo(snapProvider);
|
slider.SnapTo(snapProvider);
|
||||||
|
@ -10,7 +10,10 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -35,6 +38,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
private OsuCheckbox xCheckBox = null!;
|
private OsuCheckbox xCheckBox = null!;
|
||||||
private OsuCheckbox yCheckBox = null!;
|
private OsuCheckbox yCheckBox = null!;
|
||||||
|
|
||||||
|
private BindableList<HitObject> selectedItems { get; } = new BindableList<HitObject>();
|
||||||
|
|
||||||
public PreciseScalePopover(OsuSelectionScaleHandler scaleHandler, OsuGridToolboxGroup gridToolbox)
|
public PreciseScalePopover(OsuSelectionScaleHandler scaleHandler, OsuGridToolboxGroup gridToolbox)
|
||||||
{
|
{
|
||||||
this.scaleHandler = scaleHandler;
|
this.scaleHandler = scaleHandler;
|
||||||
@ -44,8 +49,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(EditorBeatmap editorBeatmap)
|
||||||
{
|
{
|
||||||
|
selectedItems.BindTo(editorBeatmap.SelectedHitObjects);
|
||||||
|
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Width = 220,
|
Width = 220,
|
||||||
@ -191,14 +198,26 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
updateAxisCheckBoxesEnabled();
|
updateAxisCheckBoxesEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2? getOriginPosition(PreciseScaleInfo scale) =>
|
private Vector2? getOriginPosition(PreciseScaleInfo scale)
|
||||||
scale.Origin switch
|
|
||||||
{
|
{
|
||||||
ScaleOrigin.GridCentre => gridToolbox.StartPosition.Value,
|
switch (scale.Origin)
|
||||||
ScaleOrigin.PlayfieldCentre => OsuPlayfield.BASE_SIZE / 2,
|
{
|
||||||
ScaleOrigin.SelectionCentre => null,
|
case ScaleOrigin.GridCentre:
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(scale))
|
return gridToolbox.StartPosition.Value;
|
||||||
};
|
|
||||||
|
case ScaleOrigin.PlayfieldCentre:
|
||||||
|
return OsuPlayfield.BASE_SIZE / 2;
|
||||||
|
|
||||||
|
case ScaleOrigin.SelectionCentre:
|
||||||
|
if (selectedItems.Count == 1 && selectedItems.First() is Slider slider)
|
||||||
|
return slider.Position;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Axes getAdjustAxis(PreciseScaleInfo scale) => scale.XAxis ? scale.YAxis ? Axes.Both : Axes.X : Axes.Y;
|
private Axes getAdjustAxis(PreciseScaleInfo scale) => scale.XAxis ? scale.YAxis ? Axes.Both : Axes.X : Axes.Y;
|
||||||
|
|
||||||
|
@ -91,19 +91,34 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
drawableObjectPiece.ApplyCustomUpdateState -= applyDimToDrawableHitObject;
|
drawableObjectPiece.ApplyCustomUpdateState -= applyDimToDrawableHitObject;
|
||||||
drawableObjectPiece.ApplyCustomUpdateState += applyDimToDrawableHitObject;
|
drawableObjectPiece.ApplyCustomUpdateState += applyDimToDrawableHitObject;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// but at the end apply the transforms now regardless of whether this is a DHO or not.
|
||||||
|
// the above is just to ensure they don't get overwritten later.
|
||||||
applyDim(piece);
|
applyDim(piece);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void applyDim(Drawable piece)
|
protected override void ClearNestedHitObjects()
|
||||||
|
{
|
||||||
|
base.ClearNestedHitObjects();
|
||||||
|
|
||||||
|
// any dimmable pieces that are DHOs will be pooled separately.
|
||||||
|
// `applyDimToDrawableHitObject` is a closure that implicitly captures `this`,
|
||||||
|
// and because of separate pooling of parent and child objects, there is no guarantee that the pieces will be associated with `this` again on re-use.
|
||||||
|
// therefore, clean up the subscription here to avoid crosstalk.
|
||||||
|
// not doing so can result in the callback attempting to read things from `this` when it is in a completely bogus state (not in use or similar).
|
||||||
|
foreach (var piece in DimmablePieces.OfType<DrawableHitObject>())
|
||||||
|
piece.ApplyCustomUpdateState -= applyDimToDrawableHitObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyDim(Drawable piece)
|
||||||
{
|
{
|
||||||
piece.FadeColour(new Color4(195, 195, 195, 255));
|
piece.FadeColour(new Color4(195, 195, 195, 255));
|
||||||
using (piece.BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW))
|
using (piece.BeginDelayedSequence(InitialLifetimeOffset - OsuHitWindows.MISS_WINDOW))
|
||||||
piece.FadeColour(Color4.White, 100);
|
piece.FadeColour(Color4.White, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyDimToDrawableHitObject(DrawableHitObject dho, ArmedState _) => applyDim(dho);
|
private void applyDimToDrawableHitObject(DrawableHitObject dho, ArmedState _) => applyDim(dho);
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
|||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
|
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
using osu.Game.Rulesets.Taiko.Scoring;
|
using osu.Game.Rulesets.Taiko.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty
|
namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||||
@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
ColourDifficulty = colourRating,
|
ColourDifficulty = colourRating,
|
||||||
PeakDifficulty = combinedRating,
|
PeakDifficulty = combinedRating,
|
||||||
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
|
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
MaxCombo = beatmap.GetMaxCombo(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
|
52
osu.Game.Tests/Utils/BindableValueAccessorTest.cs
Normal file
52
osu.Game.Tests/Utils/BindableValueAccessorTest.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Utils
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class BindableValueAccessorTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void GetValue()
|
||||||
|
{
|
||||||
|
const int value = 1337;
|
||||||
|
|
||||||
|
BindableInt bindable = new BindableInt(value);
|
||||||
|
Assert.That(BindableValueAccessor.GetValue(bindable), Is.EqualTo(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SetValue()
|
||||||
|
{
|
||||||
|
const int value = 1337;
|
||||||
|
|
||||||
|
BindableInt bindable = new BindableInt();
|
||||||
|
BindableValueAccessor.SetValue(bindable, value);
|
||||||
|
|
||||||
|
Assert.That(bindable.Value, Is.EqualTo(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetInvalidBindable()
|
||||||
|
{
|
||||||
|
BindableList<object> list = new BindableList<object>();
|
||||||
|
Assert.That(BindableValueAccessor.GetValue(list), Is.EqualTo(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SetInvalidBindable()
|
||||||
|
{
|
||||||
|
const int value = 1337;
|
||||||
|
|
||||||
|
BindableList<int> list = new BindableList<int> { value };
|
||||||
|
BindableValueAccessor.SetValue(list, 2);
|
||||||
|
|
||||||
|
Assert.That(list, Has.Exactly(1).Items);
|
||||||
|
Assert.That(list[0], Is.EqualTo(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,14 +5,18 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
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.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
@ -23,6 +27,7 @@ using osu.Game.Screens.Ranking.Statistics;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Ranking.Statistics.User;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -80,6 +85,69 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
loadPanel(null);
|
loadPanel(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStatisticsShownCorrectlyIfUpdateDeliveredBeforeLoad()
|
||||||
|
{
|
||||||
|
UserStatisticsWatcher userStatisticsWatcher = null!;
|
||||||
|
ScoreInfo score = null!;
|
||||||
|
|
||||||
|
AddStep("create user statistics watcher", () => Add(userStatisticsWatcher = new UserStatisticsWatcher()));
|
||||||
|
AddStep("set user statistics update", () =>
|
||||||
|
{
|
||||||
|
score = TestResources.CreateTestScoreInfo();
|
||||||
|
score.OnlineID = 1234;
|
||||||
|
((Bindable<UserStatisticsUpdate>)userStatisticsWatcher.LatestUpdate).Value = new UserStatisticsUpdate(score,
|
||||||
|
new UserStatistics
|
||||||
|
{
|
||||||
|
Level = new UserStatistics.LevelInfo
|
||||||
|
{
|
||||||
|
Current = 5,
|
||||||
|
Progress = 20,
|
||||||
|
},
|
||||||
|
GlobalRank = 38000,
|
||||||
|
CountryRank = 12006,
|
||||||
|
PP = 2134,
|
||||||
|
RankedScore = 21123849,
|
||||||
|
Accuracy = 0.985,
|
||||||
|
PlayCount = 13375,
|
||||||
|
PlayTime = 354490,
|
||||||
|
TotalScore = 128749597,
|
||||||
|
TotalHits = 0,
|
||||||
|
MaxCombo = 1233,
|
||||||
|
}, new UserStatistics
|
||||||
|
{
|
||||||
|
Level = new UserStatistics.LevelInfo
|
||||||
|
{
|
||||||
|
Current = 5,
|
||||||
|
Progress = 30,
|
||||||
|
},
|
||||||
|
GlobalRank = 36000,
|
||||||
|
CountryRank = 12000,
|
||||||
|
PP = (decimal)2134.5,
|
||||||
|
RankedScore = 23897015,
|
||||||
|
Accuracy = 0.984,
|
||||||
|
PlayCount = 13376,
|
||||||
|
PlayTime = 35789,
|
||||||
|
TotalScore = 132218497,
|
||||||
|
TotalHits = 0,
|
||||||
|
MaxCombo = 1233,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("load user statistics panel", () => Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
CachedDependencies = [(typeof(UserStatisticsWatcher), userStatisticsWatcher)],
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new UserStatisticsPanel(score)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
State = { Value = Visibility.Visible },
|
||||||
|
Score = { Value = score, }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AddUntilStep("overall ranking present", () => this.ChildrenOfType<OverallRanking>().Any());
|
||||||
|
AddUntilStep("loading spinner not visible", () => this.ChildrenOfType<LoadingLayer>().All(l => l.State.Value == Visibility.Hidden));
|
||||||
|
}
|
||||||
|
|
||||||
private void loadPanel(ScoreInfo score) => AddStep("load panel", () =>
|
private void loadPanel(ScoreInfo score) => AddStep("load panel", () =>
|
||||||
{
|
{
|
||||||
Child = new UserStatisticsPanel(score)
|
Child = new UserStatisticsPanel(score)
|
||||||
|
@ -9,6 +9,11 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
{
|
{
|
||||||
public partial class TestSceneDirectorySelector : ThemeComparisonTestScene
|
public partial class TestSceneDirectorySelector : ThemeComparisonTestScene
|
||||||
{
|
{
|
||||||
|
public TestSceneDirectorySelector()
|
||||||
|
: base(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override Drawable CreateContent() => new OsuDirectorySelector
|
protected override Drawable CreateContent() => new OsuDirectorySelector
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
|
@ -1,37 +1,49 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Tests.Visual.UserInterface;
|
using osu.Game.Tests.Visual.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Settings
|
namespace osu.Game.Tests.Visual.Settings
|
||||||
{
|
{
|
||||||
public partial class TestSceneFileSelector : ThemeComparisonTestScene
|
public partial class TestSceneFileSelector : ThemeComparisonTestScene
|
||||||
{
|
{
|
||||||
[Resolved]
|
public TestSceneFileSelector()
|
||||||
private OsuColour colours { get; set; } = null!;
|
: base(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJpgFilesOnly()
|
public void TestJpgFilesOnly()
|
||||||
{
|
{
|
||||||
AddStep("create", () =>
|
AddStep("create", () =>
|
||||||
{
|
{
|
||||||
ContentContainer.Children = new Drawable[]
|
var colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine);
|
||||||
|
|
||||||
|
ContentContainer.Child = new DependencyProvidingContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
CachedDependencies = new (Type, object)[]
|
||||||
|
{
|
||||||
|
(typeof(OverlayColourProvider), colourProvider)
|
||||||
|
},
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colours.GreySeaFoam
|
Colour = colourProvider.Background3
|
||||||
},
|
},
|
||||||
new OsuFileSelector(validFileExtensions: new[] { ".jpg" })
|
new OsuFileSelector(validFileExtensions: new[] { ".jpg" })
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ 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.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Cursor;
|
using osu.Game.Graphics.Cursor;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
@ -94,6 +95,11 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
Instantaneous = false,
|
Instantaneous = false,
|
||||||
TabbableContentContainer = this,
|
TabbableContentContainer = this,
|
||||||
},
|
},
|
||||||
|
new FormEnumDropdown<CountdownType>
|
||||||
|
{
|
||||||
|
Caption = EditorSetupStrings.EnableCountdown,
|
||||||
|
HintText = EditorSetupStrings.CountdownDescription,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,9 @@ namespace osu.Game.Tournament.Screens.Setup
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private MatchIPCInfo ipc { get; set; } = null!;
|
private MatchIPCInfo ipc { get; set; } = null!;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||||
|
|
||||||
private OsuDirectorySelector directorySelector = null!;
|
private OsuDirectorySelector directorySelector = null!;
|
||||||
private DialogOverlay? overlay;
|
private DialogOverlay? overlay;
|
||||||
|
|
||||||
|
@ -198,8 +198,11 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
if (beatmapSet.OnlineID > 0)
|
if (beatmapSet.OnlineID > 0)
|
||||||
{
|
{
|
||||||
|
// Required local for iOS. Will cause runtime crash if inlined.
|
||||||
|
int onlineId = beatmapSet.OnlineID;
|
||||||
|
|
||||||
// OnlineID should really be unique, but to avoid catastrophic failure let's iterate just to be sure.
|
// OnlineID should really be unique, but to avoid catastrophic failure let's iterate just to be sure.
|
||||||
foreach (var existingSetWithSameOnlineID in realm.All<BeatmapSetInfo>().Where(b => b.OnlineID == beatmapSet.OnlineID))
|
foreach (var existingSetWithSameOnlineID in realm.All<BeatmapSetInfo>().Where(b => b.OnlineID == onlineId))
|
||||||
{
|
{
|
||||||
existingSetWithSameOnlineID.DeletePending = true;
|
existingSetWithSameOnlineID.DeletePending = true;
|
||||||
existingSetWithSameOnlineID.OnlineID = -1;
|
existingSetWithSameOnlineID.OnlineID = -1;
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@ -15,6 +14,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
@ -228,10 +228,7 @@ namespace osu.Game.Configuration
|
|||||||
return b.Value;
|
return b.Value;
|
||||||
|
|
||||||
case IBindable u:
|
case IBindable u:
|
||||||
// An unknown (e.g. enum) generic type.
|
return BindableValueAccessor.GetValue(u);
|
||||||
var valueMethod = u.GetType().GetProperty(nameof(IBindable<int>.Value));
|
|
||||||
Debug.Assert(valueMethod != null);
|
|
||||||
return valueMethod.GetValue(u)!;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// fall back for non-bindable cases.
|
// fall back for non-bindable cases.
|
||||||
|
251
osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs
Normal file
251
osu.Game/Graphics/UserInterfaceV2/FormDropdown.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
// 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.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
public partial class FormDropdown<T> : OsuDropdown<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Caption describing this slider bar, displayed on top of the controls.
|
||||||
|
/// </summary>
|
||||||
|
public LocalisableString Caption { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hint text containing an extended description of this slider bar, displayed in a tooltip when hovering the caption.
|
||||||
|
/// </summary>
|
||||||
|
public LocalisableString HintText { get; init; }
|
||||||
|
|
||||||
|
private FormDropdownHeader header = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
header.Caption = Caption;
|
||||||
|
header.HintText = HintText;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override DropdownHeader CreateHeader() => header = new FormDropdownHeader
|
||||||
|
{
|
||||||
|
Dropdown = this,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override DropdownMenu CreateMenu() => new FormDropdownMenu();
|
||||||
|
|
||||||
|
private partial class FormDropdownHeader : DropdownHeader
|
||||||
|
{
|
||||||
|
public FormDropdown<T> Dropdown { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override DropdownSearchBar CreateSearchBar() => SearchBar = new FormDropdownSearchBar();
|
||||||
|
|
||||||
|
private LocalisableString captionText;
|
||||||
|
private LocalisableString hintText;
|
||||||
|
private LocalisableString labelText;
|
||||||
|
|
||||||
|
public LocalisableString Caption
|
||||||
|
{
|
||||||
|
get => captionText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
captionText = value;
|
||||||
|
|
||||||
|
if (caption.IsNotNull())
|
||||||
|
caption.Caption = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalisableString HintText
|
||||||
|
{
|
||||||
|
get => hintText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
hintText = value;
|
||||||
|
|
||||||
|
if (caption.IsNotNull())
|
||||||
|
caption.TooltipText = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override LocalisableString Label
|
||||||
|
{
|
||||||
|
get => labelText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
labelText = value;
|
||||||
|
|
||||||
|
if (label.IsNotNull())
|
||||||
|
label.Text = labelText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new FormDropdownSearchBar SearchBar { get; set; } = null!;
|
||||||
|
|
||||||
|
private FormFieldCaption caption = null!;
|
||||||
|
private OsuSpriteText label = null!;
|
||||||
|
private SpriteIcon chevron = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.None;
|
||||||
|
Height = 50;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
Foreground.AutoSizeAxes = Axes.None;
|
||||||
|
Foreground.RelativeSizeAxes = Axes.Both;
|
||||||
|
Foreground.Padding = new MarginPadding(9);
|
||||||
|
Foreground.Children = new Drawable[]
|
||||||
|
{
|
||||||
|
caption = new FormFieldCaption
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
Caption = Caption,
|
||||||
|
TooltipText = HintText,
|
||||||
|
},
|
||||||
|
label = new OsuSpriteText
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
},
|
||||||
|
chevron = new SpriteIcon
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Solid.ChevronDown,
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Size = new Vector2(16),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
AddInternal(new HoverClickSounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Dropdown.Current.BindDisabledChanged(_ => updateState());
|
||||||
|
SearchBar.SearchTerm.BindValueChanged(_ => updateState(), true);
|
||||||
|
Dropdown.Menu.StateChanged += _ =>
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
updateChevron();
|
||||||
|
};
|
||||||
|
SearchBar.TextBox.OnCommit += (_, _) =>
|
||||||
|
{
|
||||||
|
Background.FlashColour(ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark2), 800, Easing.OutQuint);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
label.Alpha = string.IsNullOrEmpty(SearchBar.SearchTerm.Value) ? 1 : 0;
|
||||||
|
|
||||||
|
caption.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
|
||||||
|
label.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||||
|
chevron.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||||
|
DisabledColour = Colour4.White;
|
||||||
|
|
||||||
|
bool dropdownOpen = Dropdown.Menu.State == MenuState.Open;
|
||||||
|
|
||||||
|
if (!Dropdown.Current.Disabled)
|
||||||
|
{
|
||||||
|
BorderThickness = IsHovered || dropdownOpen ? 2 : 0;
|
||||||
|
BorderColour = dropdownOpen ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||||
|
|
||||||
|
if (dropdownOpen)
|
||||||
|
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||||
|
else if (IsHovered)
|
||||||
|
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4);
|
||||||
|
else
|
||||||
|
Background.Colour = colourProvider.Background5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Background.Colour = colourProvider.Background4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateChevron()
|
||||||
|
{
|
||||||
|
bool open = Dropdown.Menu.State == MenuState.Open;
|
||||||
|
chevron.ScaleTo(open ? new Vector2(1f, -1f) : Vector2.One, 300, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class FormDropdownSearchBar : DropdownSearchBar
|
||||||
|
{
|
||||||
|
public FormTextBox.InnerTextBox TextBox { get; private set; } = null!;
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn();
|
||||||
|
protected override void PopOut() => this.FadeOut();
|
||||||
|
|
||||||
|
protected override TextBox CreateTextBox() => TextBox = new FormTextBox.InnerTextBox();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
TextBox.Anchor = Anchor.BottomLeft;
|
||||||
|
TextBox.Origin = Anchor.BottomLeft;
|
||||||
|
TextBox.RelativeSizeAxes = Axes.X;
|
||||||
|
TextBox.Margin = new MarginPadding(9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class FormDropdownMenu : OsuDropdownMenu
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
ItemsContainer.Padding = new MarginPadding(9);
|
||||||
|
Margin = new MarginPadding { Top = 5 };
|
||||||
|
|
||||||
|
MaskingContainer.BorderThickness = 2;
|
||||||
|
MaskingContainer.BorderColour = colourProvider.Highlight1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class FormEnumDropdown<T> : FormDropdown<T>
|
||||||
|
where T : struct, Enum
|
||||||
|
{
|
||||||
|
public FormEnumDropdown()
|
||||||
|
{
|
||||||
|
Items = Enum.GetValues<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,72 @@
|
|||||||
// 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 disable
|
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
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.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
public partial class OsuDirectorySelector : DirectorySelector
|
public partial class OsuDirectorySelector : DirectorySelector
|
||||||
{
|
{
|
||||||
public const float ITEM_HEIGHT = 20;
|
public const float ITEM_HEIGHT = 16;
|
||||||
|
|
||||||
public OsuDirectorySelector(string initialPath = null)
|
private Box hiddenToggleBackground = null!;
|
||||||
|
|
||||||
|
public OsuDirectorySelector(string? initialPath = null)
|
||||||
: base(initialPath)
|
: base(initialPath)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding(10);
|
AddInternal(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background5,
|
||||||
|
Depth = float.MaxValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
hiddenToggleBackground.Colour = colourProvider.Background4;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();
|
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = 20,
|
||||||
|
Vertical = 15,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
||||||
|
|
||||||
protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } };
|
protected override Drawable CreateHiddenToggleButton() => new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
hiddenToggleBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new OsuDirectorySelectorHiddenToggle
|
||||||
|
{
|
||||||
|
Current = { BindTarget = ShowHiddenItems },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string? displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
||||||
|
|
||||||
protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300);
|
protected override void NotifySelectionError() => this.FlashColour(Colour4.Red, 300);
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,51 @@
|
|||||||
// 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 disable
|
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
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.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
internal partial class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay
|
internal partial class OsuDirectorySelectorBreadcrumbDisplay : DirectorySelectorBreadcrumbDisplay
|
||||||
{
|
{
|
||||||
protected override Drawable CreateCaption() => new OsuSpriteText
|
public const float HEIGHT = 45;
|
||||||
|
public const float HORIZONTAL_PADDING = 20;
|
||||||
|
|
||||||
|
protected override Drawable CreateCaption() => Empty().With(d =>
|
||||||
{
|
{
|
||||||
Text = "Current Directory: ",
|
d.Origin = Anchor.CentreLeft;
|
||||||
Font = OsuFont.Default.With(size: OsuDirectorySelector.ITEM_HEIGHT),
|
d.Anchor = Anchor.CentreLeft;
|
||||||
};
|
d.Alpha = 0;
|
||||||
|
});
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer();
|
protected override DirectorySelectorDirectory CreateRootDirectoryItem() => new OsuBreadcrumbDisplayComputer();
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName);
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string? displayName = null) => new OsuBreadcrumbDisplayDirectory(directory, displayName);
|
||||||
|
|
||||||
public OsuDirectorySelectorBreadcrumbDisplay()
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding(15);
|
((FillFlowContainer)InternalChild).Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = HORIZONTAL_PADDING,
|
||||||
|
Vertical = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddInternal(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
Depth = 1,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory
|
private partial class OsuBreadcrumbDisplayComputer : OsuBreadcrumbDisplayDirectory
|
||||||
@ -40,26 +58,67 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class OsuBreadcrumbDisplayDirectory : OsuDirectorySelectorDirectory
|
private partial class OsuBreadcrumbDisplayDirectory : DirectorySelectorDirectory
|
||||||
{
|
{
|
||||||
public OsuBreadcrumbDisplayDirectory(DirectoryInfo directory, string displayName = null)
|
public OsuBreadcrumbDisplayDirectory(DirectoryInfo? directory, string? displayName = null)
|
||||||
: base(directory, displayName)
|
: base(directory, displayName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.CentreLeft;
|
||||||
|
Origin = Anchor.CentreLeft;
|
||||||
|
|
||||||
|
Flow.AutoSizeAxes = Axes.X;
|
||||||
|
Flow.Height = 25;
|
||||||
|
Flow.Margin = new MarginPadding { Horizontal = 10, };
|
||||||
|
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new Background
|
||||||
|
{
|
||||||
|
Depth = 1
|
||||||
|
},
|
||||||
|
new HoverClickSounds(),
|
||||||
|
});
|
||||||
|
|
||||||
Flow.Add(new SpriteIcon
|
Flow.Add(new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Icon = FontAwesome.Solid.ChevronRight,
|
Icon = FontAwesome.Solid.ChevronRight,
|
||||||
Size = new Vector2(FONT_SIZE / 2)
|
Size = new Vector2(FONT_SIZE / 2),
|
||||||
|
Margin = new MarginPadding { Left = 5, },
|
||||||
});
|
});
|
||||||
|
Flow.Colour = colourProvider.Light3;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? base.Icon : null;
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText().With(t => t.Font = OsuFont.Default.With(weight: FontWeight.SemiBold));
|
||||||
|
|
||||||
|
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar) ? FontAwesome.Solid.Database : null;
|
||||||
|
|
||||||
|
internal partial class Background : CompositeDrawable
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider overlayColourProvider)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
InternalChild = new Box
|
||||||
|
{
|
||||||
|
Colour = overlayColourProvider.Background3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,66 +1,41 @@
|
|||||||
// 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 disable
|
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
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.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
internal partial class OsuDirectorySelectorDirectory : DirectorySelectorDirectory
|
internal partial class OsuDirectorySelectorDirectory : DirectorySelectorDirectory
|
||||||
{
|
{
|
||||||
public OsuDirectorySelectorDirectory(DirectoryInfo directory, string displayName = null)
|
public OsuDirectorySelectorDirectory(DirectoryInfo directory, string? displayName = null)
|
||||||
: base(directory, displayName)
|
: base(directory, displayName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Flow.AutoSizeAxes = Axes.X;
|
Flow.AutoSizeAxes = Axes.X;
|
||||||
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new Background
|
|
||||||
{
|
|
||||||
Depth = 1
|
|
||||||
},
|
|
||||||
new HoverClickSounds()
|
new HoverClickSounds()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Colour = colours.Orange1;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText().With(t => t.Font = OsuFont.Default.With(weight: FontWeight.Bold));
|
||||||
|
|
||||||
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
|
protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
|
||||||
? FontAwesome.Solid.Database
|
? FontAwesome.Solid.Database
|
||||||
: FontAwesome.Regular.Folder;
|
: FontAwesome.Regular.Folder;
|
||||||
|
|
||||||
internal partial class Background : CompositeDrawable
|
|
||||||
{
|
|
||||||
[BackgroundDependencyLoader(true)]
|
|
||||||
private void load(OverlayColourProvider overlayColourProvider, OsuColour colours)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
Masking = true;
|
|
||||||
CornerRadius = 5;
|
|
||||||
|
|
||||||
InternalChild = new Box
|
|
||||||
{
|
|
||||||
Colour = overlayColourProvider?.Background5 ?? colours.GreySeaFoamDarker,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,15 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.None;
|
RelativeSizeAxes = Axes.None;
|
||||||
AutoSizeAxes = Axes.None;
|
AutoSizeAxes = Axes.None;
|
||||||
Size = new Vector2(100, 50);
|
Size = new Vector2(140, OsuDirectorySelectorBreadcrumbDisplay.HEIGHT);
|
||||||
|
Margin = new MarginPadding { Right = OsuDirectorySelectorBreadcrumbDisplay.HORIZONTAL_PADDING, };
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.CentreLeft;
|
||||||
Origin = Anchor.CentreLeft;
|
Origin = Anchor.CentreLeft;
|
||||||
LabelTextFlowContainer.Anchor = Anchor.CentreLeft;
|
LabelTextFlowContainer.Anchor = Anchor.CentreLeft;
|
||||||
LabelTextFlowContainer.Origin = Anchor.CentreLeft;
|
LabelTextFlowContainer.Origin = Anchor.CentreLeft;
|
||||||
LabelText = @"Show hidden";
|
LabelText = @"Show hidden";
|
||||||
|
|
||||||
|
Scale = new Vector2(0.8f);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
@ -14,5 +16,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
: base(directory, "..")
|
: base(directory, "..")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
Colour = colourProvider.Content1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,74 @@
|
|||||||
// 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 disable
|
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
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.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
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;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterfaceV2
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
{
|
{
|
||||||
public partial class OsuFileSelector : FileSelector
|
public partial class OsuFileSelector : FileSelector
|
||||||
{
|
{
|
||||||
public OsuFileSelector(string initialPath = null, string[] validFileExtensions = null)
|
private Box hiddenToggleBackground = null!;
|
||||||
|
|
||||||
|
public OsuFileSelector(string? initialPath = null, string[]? validFileExtensions = null)
|
||||||
: base(initialPath, validFileExtensions)
|
: base(initialPath, validFileExtensions)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
Padding = new MarginPadding(10);
|
AddInternal(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background5,
|
||||||
|
Depth = float.MaxValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
hiddenToggleBackground.Colour = colourProvider.Background4;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();
|
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Horizontal = 20,
|
||||||
|
Vertical = 15,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
protected override DirectorySelectorBreadcrumbDisplay CreateBreadcrumb() => new OsuDirectorySelectorBreadcrumbDisplay();
|
||||||
|
|
||||||
protected override Drawable CreateHiddenToggleButton() => new OsuDirectorySelectorHiddenToggle { Current = { BindTarget = ShowHiddenItems } };
|
protected override Drawable CreateHiddenToggleButton() => new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
hiddenToggleBackground = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new OsuDirectorySelectorHiddenToggle
|
||||||
|
{
|
||||||
|
Current = { BindTarget = ShowHiddenItems },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
protected override DirectorySelectorDirectory CreateParentDirectoryItem(DirectoryInfo directory) => new OsuDirectorySelectorParentDirectory(directory);
|
||||||
|
|
||||||
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
protected override DirectorySelectorDirectory CreateDirectoryItem(DirectoryInfo directory, string? displayName = null) => new OsuDirectorySelectorDirectory(directory, displayName);
|
||||||
|
|
||||||
protected override DirectoryListingFile CreateFileItem(FileInfo file) => new OsuDirectoryListingFile(file);
|
protected override DirectoryListingFile CreateFileItem(FileInfo file) => new OsuDirectoryListingFile(file);
|
||||||
|
|
||||||
@ -51,19 +82,17 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
Flow.AutoSizeAxes = Axes.X;
|
Flow.AutoSizeAxes = Axes.X;
|
||||||
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
Flow.Height = OsuDirectorySelector.ITEM_HEIGHT;
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuDirectorySelectorDirectory.Background
|
|
||||||
{
|
|
||||||
Depth = 1
|
|
||||||
},
|
|
||||||
new HoverClickSounds()
|
new HoverClickSounds()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Colour = colourProvider.Light3;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IconUsage? Icon
|
protected override IconUsage? Icon
|
||||||
@ -91,7 +120,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText().With(t => t.Font = OsuFont.Default.With(weight: FontWeight.SemiBold));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,10 @@ namespace osu.Game.Online
|
|||||||
// Used to interact with manager classes that don't support interface types. Will eventually be replaced.
|
// Used to interact with manager classes that don't support interface types. Will eventually be replaced.
|
||||||
var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID };
|
var beatmapSetInfo = new BeatmapSetInfo { OnlineID = TrackedItem.OnlineID };
|
||||||
|
|
||||||
realmSubscription = realm.RegisterForNotifications(r => r.All<BeatmapSetInfo>().Where(s => s.OnlineID == TrackedItem.OnlineID && !s.DeletePending), (items, _) =>
|
// Required local for iOS. Will cause runtime crash if inlined.
|
||||||
|
int onlineId = TrackedItem.OnlineID;
|
||||||
|
|
||||||
|
realmSubscription = realm.RegisterForNotifications(r => r.All<BeatmapSetInfo>().Where(s => s.OnlineID == onlineId && !s.DeletePending), (items, _) =>
|
||||||
{
|
{
|
||||||
if (items.Any())
|
if (items.Any())
|
||||||
Schedule(() => UpdateState(DownloadState.LocallyAvailable));
|
Schedule(() => UpdateState(DownloadState.LocallyAvailable));
|
||||||
|
@ -46,10 +46,15 @@ namespace osu.Game.Online
|
|||||||
Downloader.DownloadBegan += downloadBegan;
|
Downloader.DownloadBegan += downloadBegan;
|
||||||
Downloader.DownloadFailed += downloadFailed;
|
Downloader.DownloadFailed += downloadFailed;
|
||||||
|
|
||||||
|
// Required local for iOS. Will cause runtime crash if inlined.
|
||||||
|
long onlineId = TrackedItem.OnlineID;
|
||||||
|
long legacyOnlineId = TrackedItem.LegacyOnlineID;
|
||||||
|
string hash = TrackedItem.Hash;
|
||||||
|
|
||||||
realmSubscription = realm.RegisterForNotifications(r => r.All<ScoreInfo>().Where(s =>
|
realmSubscription = realm.RegisterForNotifications(r => r.All<ScoreInfo>().Where(s =>
|
||||||
((s.OnlineID > 0 && s.OnlineID == TrackedItem.OnlineID)
|
((s.OnlineID > 0 && s.OnlineID == onlineId)
|
||||||
|| (s.LegacyOnlineID > 0 && s.LegacyOnlineID == TrackedItem.LegacyOnlineID)
|
|| (s.LegacyOnlineID > 0 && s.LegacyOnlineID == legacyOnlineId)
|
||||||
|| (!string.IsNullOrEmpty(s.Hash) && s.Hash == TrackedItem.Hash))
|
|| (!string.IsNullOrEmpty(s.Hash) && s.Hash == hash))
|
||||||
&& !s.DeletePending), (items, _) =>
|
&& !s.DeletePending), (items, _) =>
|
||||||
{
|
{
|
||||||
if (items.Any())
|
if (items.Any())
|
||||||
|
@ -314,6 +314,7 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
private partial class DirectoryChooserPopover : OsuPopover
|
private partial class DirectoryChooserPopover : OsuPopover
|
||||||
{
|
{
|
||||||
public DirectoryChooserPopover(Bindable<DirectoryInfo?> currentDirectory)
|
public DirectoryChooserPopover(Bindable<DirectoryInfo?> currentDirectory)
|
||||||
|
: base(false)
|
||||||
{
|
{
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
@ -325,6 +326,13 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
Body.BorderColour = colourProvider.Highlight1;
|
||||||
|
Body.BorderThickness = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,11 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual DirectoryInfo InitialPath => null;
|
protected virtual DirectoryInfo InitialPath => null;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = new Container
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
@ -64,7 +67,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colours.GreySeaFoamDark
|
Colour = colourProvider.Background4,
|
||||||
},
|
},
|
||||||
new GridContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
|
@ -266,8 +266,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
// TODO: special case for handling number types
|
// TODO: special case for handling number types
|
||||||
|
|
||||||
PropertyInfo property = targetSetting.GetType().GetProperty(nameof(Bindable<bool>.Value))!;
|
BindableValueAccessor.SetValue(targetSetting, BindableValueAccessor.GetValue(sourceSetting));
|
||||||
property.SetValue(targetSetting, property.GetValue(sourceSetting));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ using osu.Framework.Localisation;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Setup
|
namespace osu.Game.Screens.Edit.Setup
|
||||||
@ -118,6 +119,7 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
protected override string PopOutSampleName => "UI/overlay-big-pop-out";
|
protected override string PopOutSampleName => "UI/overlay-big-pop-out";
|
||||||
|
|
||||||
public FileChooserPopover(string[] handledExtensions, Bindable<FileInfo?> currentFile, string? chooserPath)
|
public FileChooserPopover(string[] handledExtensions, Bindable<FileInfo?> currentFile, string? chooserPath)
|
||||||
|
: base(false)
|
||||||
{
|
{
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
@ -129,6 +131,13 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
Body.BorderColour = colourProvider.Highlight1;
|
||||||
|
Body.BorderThickness = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using osu.Framework.Screens;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Import
|
namespace osu.Game.Screens.Import
|
||||||
@ -36,8 +37,8 @@ namespace osu.Game.Screens.Import
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuGameBase game { get; set; }
|
private OsuGameBase game { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Cached]
|
||||||
private OsuColour colours { get; set; }
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load()
|
private void load()
|
||||||
@ -52,11 +53,6 @@ namespace osu.Game.Screens.Import
|
|||||||
Size = new Vector2(0.9f, 0.8f),
|
Size = new Vector2(0.9f, 0.8f),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Colour = colours.GreySeaFoamDark,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
fileSelector = new OsuFileSelector(validFileExtensions: game.HandledExtensions.ToArray())
|
fileSelector = new OsuFileSelector(validFileExtensions: game.HandledExtensions.ToArray())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -72,7 +68,7 @@ namespace osu.Game.Screens.Import
|
|||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
Colour = colours.GreySeaFoamDarker,
|
Colour = colourProvider.Background4,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
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;
|
||||||
@ -13,12 +14,11 @@ using osu.Game.Audio;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Database;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Online.API;
|
|
||||||
using osu.Game.Online.API.Requests;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -34,7 +34,7 @@ namespace osu.Game.Screens.Play
|
|||||||
public partial class SoloSpectatorScreen : SpectatorScreen, IPreviewTrackOwner
|
public partial class SoloSpectatorScreen : SpectatorScreen, IPreviewTrackOwner
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; } = null!;
|
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private PreviewTrackManager previewTrackManager { get; set; } = null!;
|
private PreviewTrackManager previewTrackManager { get; set; } = null!;
|
||||||
@ -60,7 +60,7 @@ namespace osu.Game.Screens.Play
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private SpectatorGameplayState? immediateSpectatorGameplayState;
|
private SpectatorGameplayState? immediateSpectatorGameplayState;
|
||||||
|
|
||||||
private GetBeatmapSetRequest? onlineBeatmapRequest;
|
private ScheduledDelegate? beatmapFetchCallback;
|
||||||
|
|
||||||
private APIBeatmapSet? beatmapSet;
|
private APIBeatmapSet? beatmapSet;
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private void clearDisplay()
|
private void clearDisplay()
|
||||||
{
|
{
|
||||||
watchButton.Enabled.Value = false;
|
watchButton.Enabled.Value = false;
|
||||||
onlineBeatmapRequest?.Cancel();
|
beatmapFetchCallback?.Cancel();
|
||||||
beatmapPanelContainer.Clear();
|
beatmapPanelContainer.Clear();
|
||||||
previewTrackManager.StopAnyPlaying(this);
|
previewTrackManager.StopAnyPlaying(this);
|
||||||
}
|
}
|
||||||
@ -244,15 +244,17 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
Debug.Assert(state.BeatmapID != null);
|
Debug.Assert(state.BeatmapID != null);
|
||||||
|
|
||||||
onlineBeatmapRequest = new GetBeatmapSetRequest(state.BeatmapID.Value, BeatmapSetLookupType.BeatmapId);
|
beatmapLookupCache.GetBeatmapAsync(state.BeatmapID.Value).ContinueWith(t => beatmapFetchCallback = Schedule(() =>
|
||||||
onlineBeatmapRequest.Success += beatmapSet => Schedule(() =>
|
|
||||||
{
|
{
|
||||||
this.beatmapSet = beatmapSet;
|
var beatmap = t.GetResultSafely();
|
||||||
beatmapPanelContainer.Child = new BeatmapCardNormal(this.beatmapSet, allowExpansion: false);
|
|
||||||
checkForAutomaticDownload();
|
|
||||||
});
|
|
||||||
|
|
||||||
api.Queue(onlineBeatmapRequest);
|
if (beatmap?.BeatmapSet == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmapSet = beatmap.BeatmapSet;
|
||||||
|
beatmapPanelContainer.Child = new BeatmapCardNormal(beatmapSet, allowExpansion: false);
|
||||||
|
checkForAutomaticDownload();
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForAutomaticDownload()
|
private void checkForAutomaticDownload()
|
||||||
|
@ -234,9 +234,12 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
if (LoadedBeatmapSuccessfully)
|
if (LoadedBeatmapSuccessfully)
|
||||||
{
|
{
|
||||||
|
// compare: https://github.com/ppy/osu/blob/ccf1acce56798497edfaf92d3ece933469edcf0a/osu.Game/Screens/Play/Player.cs#L848-L851
|
||||||
|
var scoreCopy = Score.DeepClone();
|
||||||
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await submitScore(Score.DeepClone()).ConfigureAwait(false);
|
await submitScore(scoreCopy).ConfigureAwait(false);
|
||||||
spectatorClient.EndPlaying(GameplayState);
|
spectatorClient.EndPlaying(GameplayState);
|
||||||
}).FireAndForget();
|
}).FireAndForget();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
|||||||
{
|
{
|
||||||
if (update.NewValue?.Score.MatchesOnlineID(achievedScore) == true)
|
if (update.NewValue?.Score.MatchesOnlineID(achievedScore) == true)
|
||||||
DisplayedUserStatisticsUpdate.Value = update.NewValue;
|
DisplayedUserStatisticsUpdate.Value = update.NewValue;
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,10 @@ namespace osu.Game.Skinning
|
|||||||
invalidateCache();
|
invalidateCache();
|
||||||
Debug.Assert(fileToStoragePathMapping != null);
|
Debug.Assert(fileToStoragePathMapping != null);
|
||||||
|
|
||||||
realmSubscription = realm?.RegisterForNotifications(r => r.All<T>().Where(s => s.ID == source.ID), skinChanged);
|
// Required local for iOS. Will cause runtime crash if inlined.
|
||||||
|
Guid id = source.ID;
|
||||||
|
|
||||||
|
realmSubscription = realm?.RegisterForNotifications(r => r.All<T>().Where(s => s.ID == id), skinChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
|
@ -131,9 +131,12 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
Realm.Run(r =>
|
Realm.Run(r =>
|
||||||
{
|
{
|
||||||
|
// Required local for iOS. Will cause runtime crash if inlined.
|
||||||
|
Guid currentSkinId = CurrentSkinInfo.Value.ID;
|
||||||
|
|
||||||
// choose from only user skins, removing the current selection to ensure a new one is chosen.
|
// choose from only user skins, removing the current selection to ensure a new one is chosen.
|
||||||
var randomChoices = r.All<SkinInfo>()
|
var randomChoices = r.All<SkinInfo>()
|
||||||
.Where(s => !s.DeletePending && s.ID != CurrentSkinInfo.Value.ID)
|
.Where(s => !s.DeletePending && s.ID != currentSkinId)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (randomChoices.Length == 0)
|
if (randomChoices.Length == 0)
|
||||||
|
39
osu.Game/Utils/BindableValueAccessor.cs
Normal file
39
osu.Game/Utils/BindableValueAccessor.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
|
|
||||||
|
namespace osu.Game.Utils
|
||||||
|
{
|
||||||
|
internal static class BindableValueAccessor
|
||||||
|
{
|
||||||
|
private static readonly MethodInfo get_method = typeof(BindableValueAccessor).GetMethod(nameof(getValue), BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||||
|
private static readonly MethodInfo set_method = typeof(BindableValueAccessor).GetMethod(nameof(setValue), BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||||
|
|
||||||
|
public static object GetValue(IBindable bindable)
|
||||||
|
{
|
||||||
|
Type? bindableWithValueType = bindable.GetType().GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IBindable<>));
|
||||||
|
if (bindableWithValueType == null)
|
||||||
|
return bindable;
|
||||||
|
|
||||||
|
return get_method.MakeGenericMethod(bindableWithValueType.GenericTypeArguments[0]).Invoke(null, [bindable])!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetValue(IBindable bindable, object value)
|
||||||
|
{
|
||||||
|
Type? bindableWithValueType = bindable.GetType().EnumerateBaseTypes().SingleOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Bindable<>));
|
||||||
|
if (bindableWithValueType == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
set_method.MakeGenericMethod(bindableWithValueType.GenericTypeArguments[0]).Invoke(null, [bindable, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object getValue<T>(object bindable) => ((IBindable<T>)bindable).Value!;
|
||||||
|
|
||||||
|
private static object setValue<T>(object bindable, object value) => ((Bindable<T>)bindable).Value = (T)value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user