mirror of
https://github.com/SK-la/osu-framework.git
synced 2026-03-15 03:20:30 +00:00
Merge branch 'master' into dispose-waveform-stream
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"jetbrains.resharper.globaltools": {
|
||||
"version": "2022.2.3",
|
||||
"version": "2023.3.3",
|
||||
"commands": [
|
||||
"jb"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
is_global = true
|
||||
|
||||
# .NET Code Style
|
||||
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CompareOfFloatsByEqualityOperator/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertClosureToMethodGroup/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertConditionalTernaryExpressionToSwitchExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertConstructorToMemberInitializers/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfDoToWhile/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToConditionalTernaryExpression/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfStatementToNullCoalescingAssignment/@EntryIndexedValue">WARNING</s:String>
|
||||
@@ -81,6 +82,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToConstant_002ELocal/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToLambdaExpression/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToLocalFunction/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToPrimaryConstructor/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToStaticClass/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToUsingDeclaration/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertTypeCheckPatternToNullCheck/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
@@ -165,6 +167,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantImmediateDelegateInvocation/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantLambdaSignatureParentheses/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantReadonlyModifier/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantTypeDeclarationBody/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantTypeSpecificationInDefaultExpression/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantLinebreak/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantSpace/@EntryIndexedValue">WARNING</s:String>
|
||||
@@ -250,6 +253,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedType_002EGlobal/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseAwaitUsing/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseCollectionCountProperty/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseCollectionExpression/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseConfigureAwaitFalseForAsyncDisposable/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInFormatString/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseFormatSpecifierInInterpolation/@EntryIndexedValue">WARNING</s:String>
|
||||
@@ -262,6 +266,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseObjectOrCollectionInitializer/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UsePatternMatching/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseStringInterpolation/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseUtf8StringLiteral/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseWithExpressionToCopyTuple/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VariableCanBeMadeConst/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VirtualMemberCallInConstructor/@EntryIndexedValue">HINT</s:String>
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace osu.Framework.Benchmarks
|
||||
public class BenchmarkBindableList
|
||||
{
|
||||
private readonly BindableList<int> list = new BindableList<int>();
|
||||
private IBindableList<int> iList => list;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
@@ -31,5 +32,19 @@ namespace osu.Framework.Benchmarks
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int EnumerateInterface()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
foreach (int val in iList)
|
||||
result += val;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,28 +22,28 @@ namespace osu.Framework.Benchmarks
|
||||
[Benchmark]
|
||||
public void TestSprite()
|
||||
{
|
||||
var _ = new Sprite();
|
||||
_ = new Sprite();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Benchmark]
|
||||
public void TestCompositeDrawable()
|
||||
{
|
||||
var _ = new SimpleComposite();
|
||||
_ = new SimpleComposite();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Benchmark]
|
||||
public void TestContainer()
|
||||
{
|
||||
var _ = new Container();
|
||||
_ = new Container();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Benchmark]
|
||||
public void TestSpriteText()
|
||||
{
|
||||
var _ = new SpriteText();
|
||||
_ = new SpriteText();
|
||||
}
|
||||
|
||||
public partial class SimpleComposite : CompositeDrawable
|
||||
|
||||
153
osu.Framework.Benchmarks/BenchmarkSlimReadOnlyDictionary.cs
Normal file
153
osu.Framework.Benchmarks/BenchmarkSlimReadOnlyDictionary.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using osu.Framework.Extensions.ListExtensions;
|
||||
|
||||
namespace osu.Framework.Benchmarks
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkSlimReadOnlyDictionary
|
||||
{
|
||||
private readonly Dictionary<int, int> dictionary = new Dictionary<int, int>();
|
||||
private ReadOnlyDictionary<int, int> readOnlyDictionary = null!;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
readOnlyDictionary = new ReadOnlyDictionary<int, int>(dictionary);
|
||||
|
||||
int[] values = { 0, 1, 2, 3, 4, 5, 3, 2, 3, 1, 4, 5, -1 };
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
dictionary[i] = values[i];
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Dictionary()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach ((_, int v) in dictionary)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int DictionaryAsReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach ((_, int v) in readOnlyDictionary)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int DictionaryAsSlimReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach ((_, int v) in dictionary.AsSlimReadOnly())
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Keys()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in dictionary.Keys)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int KeysAsReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in readOnlyDictionary.Keys)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int KeysAsSlimReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in dictionary.AsSlimReadOnly().Keys)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int Values()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in dictionary.Values)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ValuesAsReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in readOnlyDictionary.Values)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ValuesAsSlimReadOnly()
|
||||
{
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
foreach (int v in dictionary.AsSlimReadOnly().Values)
|
||||
sum += v;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Extensions.ListExtensions;
|
||||
namespace osu.Framework.Benchmarks
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class BenchmarkSlimReadOnlyCollection
|
||||
public class BenchmarkSlimReadOnlyList
|
||||
{
|
||||
private readonly List<int> list = new List<int> { 0, 1, 2, 3, 4, 5, 3, 2, 3, 1, 4, 5, -1 };
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace osu.Framework.SourceGeneration.Tests
|
||||
{
|
||||
var newTree = CSharpSyntaxTree.ParseText(content, path: filename);
|
||||
|
||||
if (sources.ContainsKey(filename))
|
||||
if (!sources.TryAdd(filename, newTree))
|
||||
{
|
||||
var oldTree = sources[filename];
|
||||
sources[filename] = newTree;
|
||||
@@ -104,7 +104,6 @@ namespace osu.Framework.SourceGeneration.Tests
|
||||
}
|
||||
else
|
||||
{
|
||||
sources.Add(filename, newTree);
|
||||
Compilation = Compilation.AddSyntaxTrees(newTree);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace FlappyDon.Game.Elements
|
||||
if (GroundY > 0.0f)
|
||||
groundPlane = GroundY / 2.0f;
|
||||
else
|
||||
groundPlane = Parent.DrawHeight - DrawHeight;
|
||||
groundPlane = Parent!.DrawHeight - DrawHeight;
|
||||
|
||||
Y = Math.Min(Y, groundPlane);
|
||||
|
||||
|
||||
@@ -863,7 +863,8 @@ namespace osu.Framework.Tests.Bindables
|
||||
[Test]
|
||||
public void TestGetEnumeratorDoesNotReturnNull()
|
||||
{
|
||||
Assert.NotNull(bindableStringByteDictionary.GetEnumerator());
|
||||
using var enumerator = bindableStringByteDictionary.GetEnumerator();
|
||||
Assert.NotNull(enumerator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -873,8 +874,10 @@ namespace osu.Framework.Tests.Bindables
|
||||
|
||||
var dict = new BindableDictionary<string, byte>(array);
|
||||
|
||||
var enumerator = dict.GetEnumerator();
|
||||
using var enumerator = dict.GetEnumerator();
|
||||
|
||||
// ReSharper disable once NotDisposedResource
|
||||
// Array enumerator is not disposable
|
||||
Assert.AreNotEqual(array.GetEnumerator(), enumerator);
|
||||
}
|
||||
|
||||
|
||||
@@ -1452,7 +1452,8 @@ namespace osu.Framework.Tests.Bindables
|
||||
[Test]
|
||||
public void TestGetEnumeratorDoesNotReturnNull()
|
||||
{
|
||||
Assert.NotNull(bindableStringList.GetEnumerator());
|
||||
using var enumerator = bindableStringList.GetEnumerator();
|
||||
Assert.NotNull(enumerator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -1461,8 +1462,10 @@ namespace osu.Framework.Tests.Bindables
|
||||
string[] array = { "" };
|
||||
var list = new BindableList<string>(array);
|
||||
|
||||
var enumerator = list.GetEnumerator();
|
||||
using var enumerator = list.GetEnumerator();
|
||||
|
||||
// ReSharper disable once NotDisposedResource
|
||||
// Array enumerator is not disposable
|
||||
Assert.AreNotEqual(array.GetEnumerator(), enumerator);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ namespace osu.Framework.Tests.Containers
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var unused = new Container
|
||||
_ = new Container
|
||||
{
|
||||
Children = (IReadOnlyList<Drawable>)Activator.CreateInstance(containerType)
|
||||
Children = (IReadOnlyList<Drawable>)Activator.CreateInstance(containerType)!
|
||||
};
|
||||
});
|
||||
|
||||
@@ -39,12 +39,12 @@ namespace osu.Framework.Tests.Containers
|
||||
{
|
||||
var unused = new Container();
|
||||
|
||||
unused.AddRange((IEnumerable<Drawable>)Activator.CreateInstance(containerType));
|
||||
unused.AddRange((IEnumerable<Drawable>)Activator.CreateInstance(containerType)!);
|
||||
});
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var unused = new AudioContainer
|
||||
_ = new AudioContainer
|
||||
{
|
||||
Children = (IReadOnlyList<Drawable>)Activator.CreateInstance(containerType)!
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Framework.Tests.Containers
|
||||
Child = new Container(),
|
||||
};
|
||||
|
||||
var unused2 = new Container { Child = unused1.Child };
|
||||
_ = new Container { Child = unused1.Child };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ namespace osu.Framework.Tests.IO
|
||||
bool hasThrown = false;
|
||||
request.Failed += exception => hasThrown = exception != null;
|
||||
|
||||
var _ = request.PerformAsync();
|
||||
_ = request.PerformAsync();
|
||||
|
||||
Assert.DoesNotThrow(request.Abort);
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace osu.Framework.Tests.Visual.Audio
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
Y = (int)(e.MousePosition.Y / (Parent.DrawHeight / notes));
|
||||
Y = (int)(e.MousePosition.Y / (Parent!.DrawHeight / notes));
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace osu.Framework.Tests.Visual.Drawables
|
||||
{
|
||||
AddSliderStep("resize easings", default_size / 2, default_size * 2, default_size, size =>
|
||||
{
|
||||
easingsContainer?.Children?.OfType<Visualiser>().ForEach(easing => easing.ResizeTo(new Vector2(size)));
|
||||
easingsContainer?.Children.OfType<Visualiser>().ForEach(easing => easing.ResizeTo(new Vector2(size)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,27 @@ namespace osu.Framework.Tests.Visual.Drawables
|
||||
AddStep("zero child size", () => Assert.Throws<ArgumentException>(() => boxes[0].TransformRelativeChildSizeTo(Vector2.Zero)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNestedAbsoluteSequence()
|
||||
{
|
||||
AddStep("Animate", () =>
|
||||
{
|
||||
setup();
|
||||
animate();
|
||||
});
|
||||
|
||||
AddStep("start absolute sequence", () =>
|
||||
{
|
||||
using (BeginAbsoluteSequence(0))
|
||||
{
|
||||
using (boxes[0].BeginAbsoluteSequence(Time.Current))
|
||||
{
|
||||
boxes[0].FadeInFromZero(1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setup()
|
||||
{
|
||||
finalizeTriggered = false;
|
||||
|
||||
@@ -836,6 +836,160 @@ namespace osu.Framework.Tests.Visual.Layout
|
||||
void assertContentChangeEventWasFired() => AddAssert("Content change event was fired", () => gridContentChangeEventWasFired);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPaddingRelativeSingle()
|
||||
{
|
||||
FillBox box = null;
|
||||
|
||||
AddStep("set content", () =>
|
||||
{
|
||||
grid.Content = new[] { new Drawable[] { box = new FillBox() }, };
|
||||
grid.RowDimensions = grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Relative, 1f) };
|
||||
});
|
||||
|
||||
checkCorrectBoxSize(new MarginPadding(20));
|
||||
checkCorrectBoxSize(new MarginPadding { Horizontal = 20 });
|
||||
checkCorrectBoxSize(new MarginPadding { Vertical = 20 });
|
||||
checkCorrectBoxSize(new MarginPadding { Left = 20 });
|
||||
checkCorrectBoxSize(new MarginPadding { Right = 20 });
|
||||
checkCorrectBoxSize(new MarginPadding { Top = 20 });
|
||||
checkCorrectBoxSize(new MarginPadding { Bottom = 20 });
|
||||
return;
|
||||
|
||||
void checkCorrectBoxSize(MarginPadding padding)
|
||||
{
|
||||
setPadding(padding);
|
||||
AddAssert("box size is correct", () => Precision.AlmostEquals(grid.DrawSize - padding.Total, box.DrawSize));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPaddingAutosizeSingle()
|
||||
{
|
||||
FillBox box = null;
|
||||
|
||||
AddStep("set content", () =>
|
||||
{
|
||||
gridParent.RelativeSizeAxes = Axes.None;
|
||||
gridParent.AutoSizeAxes = Axes.Both;
|
||||
|
||||
grid.RelativeSizeAxes = Axes.None;
|
||||
grid.AutoSizeAxes = Axes.Both;
|
||||
grid.Content = new[] { new Drawable[] { box = new FillBox { RelativeSizeAxes = Axes.None, Size = new Vector2(100) } }, };
|
||||
grid.RowDimensions = grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) };
|
||||
});
|
||||
|
||||
checkCorrectGridSize(new MarginPadding(20));
|
||||
checkCorrectGridSize(new MarginPadding { Horizontal = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Vertical = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Left = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Right = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Top = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Bottom = 20 });
|
||||
return;
|
||||
|
||||
void checkCorrectGridSize(MarginPadding padding)
|
||||
{
|
||||
setPadding(padding);
|
||||
AddAssert("grid size is correct", () => Precision.AlmostEquals(grid.DrawSize, box.DrawSize + padding.Total));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPaddingAutosizeMultiple()
|
||||
{
|
||||
FillBox[] boxes = new FillBox[3];
|
||||
const float box_size = 100f;
|
||||
|
||||
AddStep("set content", () =>
|
||||
{
|
||||
gridParent.RelativeSizeAxes = Axes.None;
|
||||
gridParent.AutoSizeAxes = Axes.Both;
|
||||
|
||||
grid.RelativeSizeAxes = Axes.None;
|
||||
grid.AutoSizeAxes = Axes.Both;
|
||||
grid.RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) };
|
||||
grid.ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
};
|
||||
grid.Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
boxes[0] = new FillBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.None,
|
||||
Size = new Vector2(box_size)
|
||||
},
|
||||
boxes[1] = new FillBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.None,
|
||||
Size = new Vector2(box_size)
|
||||
},
|
||||
boxes[2] = new FillBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.None,
|
||||
Size = new Vector2(box_size)
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
checkCorrectGridSize(new MarginPadding(20));
|
||||
checkCorrectGridSize(new MarginPadding { Horizontal = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Vertical = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Left = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Right = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Top = 20 });
|
||||
checkCorrectGridSize(new MarginPadding { Bottom = 20 });
|
||||
return;
|
||||
|
||||
void checkCorrectGridSize(MarginPadding padding)
|
||||
{
|
||||
setPadding(padding);
|
||||
AddAssert("grid size is correct", () => Precision.AlmostEquals(grid.DrawSize, new Vector2(box_size * 3, box_size) + padding.Total));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestPaddingRelativeMultiple(bool row)
|
||||
{
|
||||
FillBox[] boxes = new FillBox[3];
|
||||
|
||||
setSingleDimensionContent(() => new[]
|
||||
{
|
||||
new Drawable[] { boxes[0] = new FillBox(), boxes[1] = new FillBox(), boxes[2] = new FillBox() }
|
||||
}, row: row);
|
||||
|
||||
checkSizes(new MarginPadding(20));
|
||||
checkSizes(new MarginPadding { Horizontal = 20 });
|
||||
checkSizes(new MarginPadding { Vertical = 20 });
|
||||
checkSizes(new MarginPadding { Left = 20 });
|
||||
checkSizes(new MarginPadding { Right = 20 });
|
||||
checkSizes(new MarginPadding { Top = 20 });
|
||||
checkSizes(new MarginPadding { Bottom = 20 });
|
||||
return;
|
||||
|
||||
void checkSizes(MarginPadding padding)
|
||||
{
|
||||
setPadding(padding);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int local = i;
|
||||
|
||||
if (row)
|
||||
AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2((grid.DrawWidth - padding.TotalHorizontal) / 3f, grid.DrawHeight - padding.TotalVertical)));
|
||||
else
|
||||
AddAssert($"box {local} has correct size", () => Precision.AlmostEquals(boxes[local].DrawSize, new Vector2(grid.DrawWidth - padding.TotalHorizontal, (grid.DrawHeight - padding.TotalVertical) / 3f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns drawable dimension along desired axis.
|
||||
/// </summary>
|
||||
@@ -888,6 +1042,11 @@ namespace osu.Framework.Tests.Visual.Layout
|
||||
grid.ColumnDimensions = dimensions;
|
||||
});
|
||||
|
||||
private void setPadding(MarginPadding padding) => AddStep($"set padding {padding.Left}, {padding.Top}, {padding.Right}, {padding.Bottom}", () =>
|
||||
{
|
||||
grid.Padding = padding;
|
||||
});
|
||||
|
||||
private partial class FillBox : Box
|
||||
{
|
||||
public FillBox()
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace osu.Framework.Tests.Visual.Layout
|
||||
{
|
||||
for (int row = 0; row < getGrid().Content.Count; row++)
|
||||
{
|
||||
if (!Precision.AlmostEquals(expectedHeight, getGrid().Content[row][0].Parent.DrawHeight))
|
||||
if (!Precision.AlmostEquals(expectedHeight, getGrid().Content[row][0].Parent!.DrawHeight))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,47 +39,43 @@ namespace osu.Framework.Tests.Visual.Sprites
|
||||
Cell(1, 2).Child = createTest("drawable - fixed size", () => new TestDrawableAnimation(Axes.Both) { Size = new Vector2(100, 50) });
|
||||
}
|
||||
|
||||
private Drawable createTest(string name, Func<Drawable> animationCreationFunc) => new Container
|
||||
private Drawable createTest(string name, Func<Drawable> animationCreationFunc) => new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(10),
|
||||
Child = new GridContainer
|
||||
Content = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
new Drawable[]
|
||||
{
|
||||
new Drawable[]
|
||||
new SpriteText
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = name
|
||||
},
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderColour = Color4.OrangeRed,
|
||||
BorderThickness = 2,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
animationCreationFunc()
|
||||
}
|
||||
}
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = name
|
||||
},
|
||||
},
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||
}
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderColour = Color4.OrangeRed,
|
||||
BorderThickness = 2,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
animationCreationFunc()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||
};
|
||||
|
||||
private partial class TestDrawableAnimation : DrawableAnimation
|
||||
|
||||
@@ -64,35 +64,31 @@ namespace osu.Framework.Tests.Visual.Sprites
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = $"useManaulMipmaps: {useManualMipmaps}"
|
||||
},
|
||||
new Container
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 20 },
|
||||
Child = new GridContainer
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColumnDimensions = new[]
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
new MipmapSprite(0),
|
||||
new MipmapSprite(1)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
new Drawable[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
new Dimension(GridSizeMode.Relative, 0.5f),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new MipmapSprite(0),
|
||||
new MipmapSprite(1)
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new MipmapSprite(2),
|
||||
new MipmapSprite(3)
|
||||
}
|
||||
new MipmapSprite(2),
|
||||
new MipmapSprite(3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,21 @@ namespace osu.Framework.Tests.Visual.Sprites
|
||||
{
|
||||
private ScheduledDelegate? scheduledDelegate;
|
||||
|
||||
[Test]
|
||||
public void TestPreloadIcon()
|
||||
{
|
||||
SpriteIcon? icon = null;
|
||||
|
||||
AddStep("create icon", () => icon = new SpriteIcon
|
||||
{
|
||||
Size = new Vector2(20),
|
||||
Icon = FontAwesome.Solid.Anchor
|
||||
});
|
||||
AddStep("preload icon", () => LoadComponent(icon));
|
||||
AddStep("change icon", () => icon!.Icon = FontAwesome.Solid.Ad);
|
||||
AddStep("add icon", () => Add(icon));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOneIconAtATime()
|
||||
{
|
||||
|
||||
@@ -41,47 +41,43 @@ namespace osu.Framework.Tests.Visual.Sprites
|
||||
});
|
||||
}
|
||||
|
||||
private Drawable createTest(string name, Func<Drawable> animationCreationFunc) => new Container
|
||||
private Drawable createTest(string name, Func<Drawable> animationCreationFunc) => new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(10),
|
||||
Child = new GridContainer
|
||||
Content = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
new Drawable[]
|
||||
{
|
||||
new Drawable[]
|
||||
new SpriteText
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = name
|
||||
},
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderColour = Color4.OrangeRed,
|
||||
BorderThickness = 2,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
animationCreationFunc()
|
||||
}
|
||||
}
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = name
|
||||
},
|
||||
},
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||
}
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderColour = Color4.OrangeRed,
|
||||
BorderThickness = 2,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
},
|
||||
animationCreationFunc()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +290,43 @@ namespace osu.Framework.Tests.Visual.UserInterface
|
||||
AddUntilStep("only one item", () => delayedList.ChildrenOfType<BasicRearrangeableListItem<int>>().Count() == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDragSynchronisation()
|
||||
{
|
||||
TestRearrangeableList another = null!;
|
||||
|
||||
addItems(3);
|
||||
AddStep("add another list", () =>
|
||||
{
|
||||
another = new TestRearrangeableList
|
||||
{
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Size = new Vector2(300, 200),
|
||||
};
|
||||
Add(another);
|
||||
});
|
||||
AddStep("bind lists", () =>
|
||||
{
|
||||
another.Items.BindTo(list.Items);
|
||||
});
|
||||
|
||||
AddStep("move mouse to first dragger", () => InputManager.MoveMouseTo(getDragger(0)));
|
||||
AddStep("begin a drag", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("move the mouse", () => InputManager.MoveMouseTo(getDragger(0), new Vector2(0, 80)));
|
||||
AddStep("end the drag", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddUntilStep("0 is the last in original", () => list.Items.Last() == 0);
|
||||
|
||||
AddAssert("0 is the last in bound", () => another.Items.Last() == 0);
|
||||
|
||||
AddAssert("items flow updated", () =>
|
||||
{
|
||||
var item = (BasicRearrangeableListItem<int>)another.ListContainer.FlowingChildren.Last();
|
||||
return item.Model == 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void addDragSteps(int from, int to, int[] expectedSequence)
|
||||
{
|
||||
AddStep($"move to {from}", () =>
|
||||
|
||||
@@ -70,29 +70,29 @@ namespace osu.Framework.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("get tooltip instance", () => originalInstance = tooltipContainer.CurrentTooltip);
|
||||
AddAssert("custom tooltip used", () => originalInstance.GetType() == typeof(CustomTooltip));
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent).Text);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent!).Text);
|
||||
|
||||
hoverTooltipProvider(() => customTooltipTextB);
|
||||
|
||||
AddAssert("custom tooltip reused", () => tooltipContainer.CurrentTooltip == originalInstance);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent).Text);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent!).Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDifferentCustomTooltips()
|
||||
{
|
||||
hoverTooltipProvider(() => customTooltipTextA);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent).Text);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextA.TooltipContent!).Text);
|
||||
|
||||
AddAssert("current tooltip type normal", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltip));
|
||||
|
||||
hoverTooltipProvider(() => customTooltipTextAlt);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextAlt.TooltipContent).Text);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextAlt.TooltipContent!).Text);
|
||||
|
||||
AddAssert("current tooltip type alt", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltipAlt));
|
||||
|
||||
hoverTooltipProvider(() => customTooltipTextB);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent).Text);
|
||||
assertTooltipText(() => ((CustomContent)customTooltipTextB.TooltipContent!).Text);
|
||||
|
||||
AddAssert("current tooltip type normal", () => tooltipContainer.CurrentTooltip.GetType() == typeof(CustomTooltip));
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace osu.Framework.Allocation
|
||||
public void Register(Type type, InjectDependencyDelegate injectDel, CacheDependencyDelegate cacheDel)
|
||||
{
|
||||
// The DependencyActivator constructor stores itself to a static dictionary.
|
||||
var _ = new DependencyActivator(
|
||||
_ = new DependencyActivator(
|
||||
type,
|
||||
injectDel ?? ((_, _) => { }),
|
||||
cacheDel ?? ((_, d, _) => d));
|
||||
|
||||
@@ -37,10 +37,7 @@ namespace osu.Framework.Audio.Sample
|
||||
throw new ObjectDisposedException(ToString(), "Can not get a channel from a disposed sample.");
|
||||
|
||||
var channel = CreateChannel();
|
||||
|
||||
if (channel != null)
|
||||
channel.OnPlay = onPlay;
|
||||
|
||||
channel.OnPlay = onPlay;
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Framework.Bindables
|
||||
@@ -13,6 +14,7 @@ namespace osu.Framework.Bindables
|
||||
public class BindableNumber<T> : RangeConstrainedBindable<T>, IBindableNumber<T>
|
||||
where T : struct, IComparable<T>, IConvertible, IEquatable<T>
|
||||
{
|
||||
[CanBeNull]
|
||||
public event Action<T> PrecisionChanged;
|
||||
|
||||
public BindableNumber(T defaultValue = default)
|
||||
|
||||
@@ -37,5 +37,10 @@ namespace osu.Framework.Bindables
|
||||
|
||||
/// <inheritdoc cref="IBindable.GetBoundCopy"/>
|
||||
IBindableList<T> GetBoundCopy();
|
||||
|
||||
// We want this enumerator to reduce enumeration overhead, but it causes mono / android crashes for silly reasons.
|
||||
// See https://sentry.ppy.sh/share/issue/c7cda39d8ab94897a2bb350059fd3652/
|
||||
|
||||
// new List<T>.Enumerator GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Framework.Configuration.Tracking
|
||||
@@ -14,6 +15,7 @@ namespace osu.Framework.Configuration.Tracking
|
||||
/// <typeparam name="TValue">The type of the tracked value.</typeparam>
|
||||
public abstract class TrackedSetting<TValue> : ITrackedSetting
|
||||
{
|
||||
[CanBeNull]
|
||||
public event Action<SettingDescription> SettingChanged;
|
||||
|
||||
private readonly object setting;
|
||||
|
||||
@@ -17,5 +17,17 @@ namespace osu.Framework.Extensions.ListExtensions
|
||||
/// <returns>The read-only view.</returns>
|
||||
public static SlimReadOnlyListWrapper<T> AsSlimReadOnly<T>(this List<T> list)
|
||||
=> new SlimReadOnlyListWrapper<T>(list);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new read-only view into a <see cref="Dictionary{TKey, TValue}"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Enumeration does not allocate the enumerator.</remarks>
|
||||
/// <param name="dictionary">The dictionary to create the view of.</param>
|
||||
/// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
|
||||
/// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
|
||||
/// <returns>The read-only view.</returns>
|
||||
public static SlimReadOnlyDictionaryWrapper<TKey, TValue> AsSlimReadOnly<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
|
||||
where TKey : notnull
|
||||
=> new SlimReadOnlyDictionaryWrapper<TKey, TValue>(dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1310,6 +1310,9 @@ namespace osu.Framework.Graphics.Containers
|
||||
return SchedulerAfterChildren.Add(action);
|
||||
}
|
||||
|
||||
[ThreadStatic]
|
||||
private static List<AbsoluteSequenceSender> absoluteSequenceActions;
|
||||
|
||||
public override IDisposable BeginAbsoluteSequence(double newTransformStartTime, bool recursive = true)
|
||||
{
|
||||
EnsureTransformMutationAllowed();
|
||||
@@ -1317,20 +1320,30 @@ namespace osu.Framework.Graphics.Containers
|
||||
if (!recursive || internalChildren.Count == 0)
|
||||
return base.BeginAbsoluteSequence(newTransformStartTime, false);
|
||||
|
||||
List<AbsoluteSequenceSender> disposalActions = new List<AbsoluteSequenceSender>(internalChildren.Count + 1);
|
||||
absoluteSequenceActions ??= new List<AbsoluteSequenceSender>();
|
||||
absoluteSequenceActions.EnsureCapacity(internalChildren.Count + 1);
|
||||
|
||||
base.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, disposalActions);
|
||||
int startCount = absoluteSequenceActions.Count;
|
||||
|
||||
base.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, absoluteSequenceActions);
|
||||
foreach (var c in internalChildren)
|
||||
c.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, disposalActions);
|
||||
c.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, absoluteSequenceActions);
|
||||
|
||||
return new ValueInvokeOnDisposal<List<AbsoluteSequenceSender>>(disposalActions, actions =>
|
||||
int sequenceLength = absoluteSequenceActions.Count - startCount;
|
||||
|
||||
return new ValueInvokeOnDisposal<AbsoluteSequenceRange>(new AbsoluteSequenceRange(absoluteSequenceActions, sequenceLength), static range =>
|
||||
{
|
||||
foreach (var a in actions)
|
||||
a.Dispose();
|
||||
int startIndex = range.Sequence.Count - range.Length;
|
||||
|
||||
for (int i = startIndex; i < range.Sequence.Count; i++)
|
||||
range.Sequence[i].Dispose();
|
||||
|
||||
range.Sequence.RemoveRange(startIndex, range.Length);
|
||||
});
|
||||
}
|
||||
|
||||
private readonly record struct AbsoluteSequenceRange(List<AbsoluteSequenceSender> Sequence, int Length);
|
||||
|
||||
internal override void CollectAbsoluteSequenceActionsFromSubTree(double newTransformStartTime, List<AbsoluteSequenceSender> actions)
|
||||
{
|
||||
base.CollectAbsoluteSequenceActionsFromSubTree(newTransformStartTime, actions);
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
@@ -132,7 +133,11 @@ namespace osu.Framework.Graphics.Containers
|
||||
array[arrayIndex++] = c;
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item) => Remove(item, true);
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
return Remove(item, true);
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
|
||||
@@ -297,7 +302,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
/// Removes a range of children. This is equivalent to calling <see cref="Remove(T, bool)"/> on
|
||||
/// each element of the range in order.
|
||||
/// </summary>
|
||||
public void RemoveRange(IEnumerable<T> range, bool disposeImmediately)
|
||||
public void RemoveRange([CanBeNull] IEnumerable<T> range, bool disposeImmediately)
|
||||
{
|
||||
if (range == null)
|
||||
return;
|
||||
|
||||
@@ -20,6 +20,16 @@ namespace osu.Framework.Graphics.Containers
|
||||
/// </summary>
|
||||
public partial class GridContainer : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Shrinks the space children may occupy within this <see cref="GridContainer"/>
|
||||
/// by the specified amount on each side.
|
||||
/// </summary>
|
||||
public new MarginPadding Padding
|
||||
{
|
||||
get => base.Padding;
|
||||
set => base.Padding = value;
|
||||
}
|
||||
|
||||
public GridContainer()
|
||||
{
|
||||
AddLayout(cellLayout);
|
||||
@@ -206,8 +216,8 @@ namespace osu.Framework.Graphics.Containers
|
||||
if (cellLayout.IsValid)
|
||||
return;
|
||||
|
||||
float[] widths = distribute(columnDimensions, DrawWidth, getCellSizesAlongAxis(Axes.X, DrawWidth));
|
||||
float[] heights = distribute(rowDimensions, DrawHeight, getCellSizesAlongAxis(Axes.Y, DrawHeight));
|
||||
float[] widths = distribute(columnDimensions, DrawWidth - Padding.TotalHorizontal, getCellSizesAlongAxis(Axes.X, DrawWidth - Padding.TotalHorizontal));
|
||||
float[] heights = distribute(rowDimensions, DrawHeight - Padding.TotalVertical, getCellSizesAlongAxis(Axes.Y, DrawHeight - Padding.TotalVertical));
|
||||
|
||||
for (int col = 0; col < cellColumns; col++)
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
loadDrawable(() => CreateDrawable(model) ?? Empty());
|
||||
}
|
||||
|
||||
private void loadDrawable(Func<Drawable>? createDrawableFunc)
|
||||
private void loadDrawable(Func<Drawable?>? createDrawableFunc)
|
||||
{
|
||||
// Remove the previous wrapper if the inner drawable hasn't finished loading.
|
||||
if (currentWrapper?.DelayedLoadCompleted == false)
|
||||
@@ -174,7 +174,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
/// <param name="createContentFunc">A function that creates the wrapped <see cref="Drawable"/>.</param>
|
||||
/// <param name="timeBeforeLoad">The time before loading should begin.</param>
|
||||
/// <returns>A <see cref="DelayedLoadWrapper"/> or null if <paramref name="createContentFunc"/> returns null.</returns>
|
||||
private DelayedLoadWrapper createWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
||||
private DelayedLoadWrapper createWrapper(Func<Drawable?> createContentFunc, double timeBeforeLoad)
|
||||
{
|
||||
// Note that this only becomes null after the first consumption.
|
||||
// ie. the `createContentFunc` cannot provide a null.
|
||||
@@ -185,7 +185,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
try
|
||||
{
|
||||
// optimisation to use already constructed object (used above for null check).
|
||||
return content ?? createContentFunc();
|
||||
return content ?? createContentFunc()!;
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -110,6 +110,11 @@ namespace osu.Framework.Graphics.Containers
|
||||
removeItems(e.OldItems);
|
||||
addItems(e.NewItems);
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
sortItems();
|
||||
OnItemsChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +187,7 @@ namespace osu.Framework.Graphics.Containers
|
||||
|
||||
// If the async load didn't complete, the item wouldn't exist in the container and an exception would be thrown
|
||||
if (drawable.Parent == ListContainer)
|
||||
ListContainer.SetLayoutPosition(drawable, i);
|
||||
ListContainer!.SetLayoutPosition(drawable, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
@@ -16,6 +17,8 @@ namespace osu.Framework.Graphics.Containers
|
||||
public abstract class TextPart : ITextPart
|
||||
{
|
||||
public IEnumerable<Drawable> Drawables { get; }
|
||||
|
||||
[CanBeNull]
|
||||
public event Action<IEnumerable<Drawable>> DrawablePartsRecreated;
|
||||
|
||||
private readonly List<Drawable> drawables = new List<Drawable>();
|
||||
|
||||
@@ -238,7 +238,10 @@ namespace osu.Framework.Graphics
|
||||
lock (LoadLock)
|
||||
{
|
||||
if (!isDirectAsyncContext && IsLongRunning)
|
||||
throw new InvalidOperationException($"Tried to load long-running drawable type {GetType().ReadableName()} in a non-direct async context. See https://git.io/Je1YF for more details.");
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Tried to load long-running drawable type {GetType().ReadableName()} in a non-direct async context. See https://git.io/Je1YF for more details.");
|
||||
}
|
||||
|
||||
if (IsDisposed)
|
||||
throw new ObjectDisposedException(ToString(), "Attempting to load an already disposed drawable.");
|
||||
@@ -1901,6 +1904,8 @@ namespace osu.Framework.Graphics
|
||||
/// <returns>The vector in other's coordinates.</returns>
|
||||
public Vector2 ToSpaceOfOtherDrawable(Vector2 input, IDrawable other)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(other);
|
||||
|
||||
if (other == this)
|
||||
return input;
|
||||
|
||||
@@ -1926,14 +1931,14 @@ namespace osu.Framework.Graphics
|
||||
/// </summary>
|
||||
/// <param name="input">A vector in local coordinates.</param>
|
||||
/// <returns>The vector in Parent's coordinates.</returns>
|
||||
public Vector2 ToParentSpace(Vector2 input) => ToSpaceOfOtherDrawable(input, Parent);
|
||||
public Vector2 ToParentSpace(Vector2 input) => ToSpaceOfOtherDrawable(input, Parent!);
|
||||
|
||||
/// <summary>
|
||||
/// Accepts a rectangle in local coordinates and converts it to a quad in Parent's space.
|
||||
/// </summary>
|
||||
/// <param name="input">A rectangle in local coordinates.</param>
|
||||
/// <returns>The quad in Parent's coordinates.</returns>
|
||||
public Quad ToParentSpace(RectangleF input) => ToSpaceOfOtherDrawable(input, Parent);
|
||||
public Quad ToParentSpace(RectangleF input) => ToSpaceOfOtherDrawable(input, Parent!);
|
||||
|
||||
/// <summary>
|
||||
/// Accepts a vector in local coordinates and converts it to coordinates in screen space.
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Graphics.Rendering;
|
||||
using osu.Framework.Platform;
|
||||
@@ -70,6 +71,7 @@ namespace osu.Framework.Graphics.Performance
|
||||
|
||||
private FrameStatisticsMode state;
|
||||
|
||||
[CanBeNull]
|
||||
public event Action<FrameStatisticsMode> StateChanged;
|
||||
|
||||
public FrameStatisticsMode State
|
||||
|
||||
@@ -29,11 +29,6 @@ namespace osu.Framework.Graphics.Sprites
|
||||
{
|
||||
this.store = store;
|
||||
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
updateTexture();
|
||||
}
|
||||
|
||||
@@ -121,7 +116,7 @@ namespace osu.Framework.Graphics.Sprites
|
||||
if (icon.Equals(value)) return;
|
||||
|
||||
icon = value;
|
||||
if (IsLoaded)
|
||||
if (LoadState > LoadState.NotLoaded)
|
||||
updateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace osu.Framework.Graphics.Sprites
|
||||
// Pre-cache the characters in the texture store
|
||||
foreach (char character in localisedText.Value)
|
||||
{
|
||||
var unused = store.Get(font.FontName, character) ?? store.Get(null, character);
|
||||
_ = store.Get(font.FontName, character) ?? store.Get(null, character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace osu.Framework.Graphics.Transforms
|
||||
EnsureTransformMutationAllowed();
|
||||
|
||||
if (delay == 0)
|
||||
return null;
|
||||
return new ValueInvokeOnDisposal(() => { });
|
||||
|
||||
AddDelay(delay, recursive);
|
||||
double newTransformDelay = TransformDelay;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osuTK.Graphics;
|
||||
@@ -27,6 +28,7 @@ namespace osu.Framework.Graphics.UserInterface
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="Menu"/>'s <see cref="State"/> changes.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public event Action<MenuState> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
@@ -695,6 +697,7 @@ namespace osu.Framework.Graphics.UserInterface
|
||||
/// <summary>
|
||||
/// Invoked when this <see cref="DrawableMenuItem"/>'s <see cref="State"/> changes.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public event Action<MenuItemState> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -597,7 +597,7 @@ namespace osu.Framework.Graphics.UserInterface
|
||||
|
||||
private int getCharacterClosestTo(Vector2 pos)
|
||||
{
|
||||
pos = Parent.ToSpaceOfOtherDrawable(pos, TextFlow);
|
||||
pos = Parent!.ToSpaceOfOtherDrawable(pos, TextFlow);
|
||||
|
||||
int i = 0;
|
||||
|
||||
|
||||
@@ -405,7 +405,7 @@ namespace osu.Framework.Graphics.Veldrid
|
||||
Debug.Assert(device.BackendType == GraphicsBackend.Vulkan);
|
||||
|
||||
var info = device.GetVulkanInfo();
|
||||
var physicalDevice = info.PhysicalDevice;
|
||||
IntPtr physicalDevice = info.PhysicalDevice;
|
||||
|
||||
uint instanceExtensionsCount = 0;
|
||||
var result = VulkanNative.vkEnumerateInstanceExtensionProperties((byte*)null, ref instanceExtensionsCount, IntPtr.Zero);
|
||||
|
||||
@@ -165,10 +165,8 @@ namespace osu.Framework.IO.Stores
|
||||
return;
|
||||
|
||||
// Check if there's already a reload action bound
|
||||
if (actionList.ContainsKey(name))
|
||||
if (!actionList.TryAdd(name, onReload))
|
||||
throw new InvalidOperationException($"A reload delegate is already bound to the resource '{name}'.");
|
||||
|
||||
actionList[name] = onReload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace osu.Framework.Input.Bindings
|
||||
{
|
||||
public KeyCombination KeyCombination { get; set; }
|
||||
|
||||
public object Action { get; set; }
|
||||
public object Action { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new instance.
|
||||
|
||||
@@ -444,7 +444,7 @@ namespace osu.Framework.Input
|
||||
|
||||
private readonly List<Drawable> highFrequencyDrawables = new List<Drawable>();
|
||||
|
||||
private MouseMoveEvent highFrequencyMoveEvent;
|
||||
private MouseMoveEvent lastMouseMove;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
@@ -458,10 +458,11 @@ namespace osu.Framework.Input
|
||||
|
||||
var pendingInputs = GetPendingInputs();
|
||||
|
||||
if (pendingInputs.Count > 0)
|
||||
lastMouseMove = null;
|
||||
|
||||
foreach (var result in pendingInputs)
|
||||
{
|
||||
result.Apply(CurrentState, this);
|
||||
}
|
||||
|
||||
if (CurrentState.Mouse.IsPositionValid)
|
||||
{
|
||||
@@ -477,10 +478,9 @@ namespace osu.Framework.Input
|
||||
{
|
||||
// conditional avoid allocs of MouseMoveEvent when state is guaranteed to not have been mutated.
|
||||
// can be removed if we pool/change UIEvent allocation to be more efficient.
|
||||
if (highFrequencyMoveEvent == null || pendingInputs.Count > 0)
|
||||
highFrequencyMoveEvent = new MouseMoveEvent(CurrentState);
|
||||
lastMouseMove ??= new MouseMoveEvent(CurrentState);
|
||||
|
||||
PropagateBlockableEvent(highFrequencyDrawables.AsSlimReadOnly(), highFrequencyMoveEvent);
|
||||
PropagateBlockableEvent(highFrequencyDrawables.AsSlimReadOnly(), lastMouseMove);
|
||||
}
|
||||
|
||||
highFrequencyDrawables.Clear();
|
||||
@@ -973,7 +973,7 @@ namespace osu.Framework.Input
|
||||
manager.HandleButtonStateChange(e.State, e.Kind);
|
||||
}
|
||||
|
||||
private bool handleMouseMove(InputState state, Vector2 lastPosition) => PropagateBlockableEvent(PositionalInputQueue, new MouseMoveEvent(state, lastPosition));
|
||||
private bool handleMouseMove(InputState state, Vector2 lastPosition) => PropagateBlockableEvent(PositionalInputQueue, lastMouseMove = new MouseMoveEvent(state, lastPosition));
|
||||
|
||||
private bool handleScroll(InputState state, Vector2 lastScroll, bool isPrecise) =>
|
||||
PropagateBlockableEvent(PositionalInputQueue, new ScrollEvent(state, state.Mouse.Scroll - lastScroll, isPrecise));
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace osu.Framework.Lists
|
||||
/// <summary>
|
||||
/// Invoked when an element of the array is changed via <see cref="this[int]"/>.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public event Action ArrayElementChanged;
|
||||
|
||||
[NotNull]
|
||||
|
||||
53
osu.Framework/Lists/SlimReadOnlyDictionaryWrapper.cs
Normal file
53
osu.Framework/Lists/SlimReadOnlyDictionaryWrapper.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace osu.Framework.Lists
|
||||
{
|
||||
public readonly struct SlimReadOnlyDictionaryWrapper<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
|
||||
where TKey : notnull
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> dict;
|
||||
|
||||
public SlimReadOnlyDictionaryWrapper(Dictionary<TKey, TValue> dict)
|
||||
{
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
|
||||
=> dict.GetEnumerator();
|
||||
|
||||
public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
|
||||
=> dict.GetEnumerator();
|
||||
|
||||
public Dictionary<TKey, TValue>.KeyCollection Keys
|
||||
=> dict.Keys;
|
||||
|
||||
public Dictionary<TKey, TValue>.ValueCollection Values
|
||||
=> dict.Values;
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
=> dict.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
=> dict.TryGetValue(key, out value);
|
||||
|
||||
public TValue this[TKey key]
|
||||
=> dict[key];
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys
|
||||
=> dict.Keys;
|
||||
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
|
||||
=> dict.Values;
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> dict.Count;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace osu.Framework.Physics
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
public Drawable Simulation { get; set; }
|
||||
public Drawable Simulation { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Controls how elastic the material is. A value of 1 means perfect elasticity
|
||||
@@ -96,7 +96,7 @@ namespace osu.Framework.Physics
|
||||
/// </summary>
|
||||
protected float ComputeI()
|
||||
{
|
||||
Matrix3 mat = DrawInfo.Matrix * Parent.DrawInfo.MatrixInverse;
|
||||
Matrix3 mat = DrawInfo.Matrix * Parent!.DrawInfo.MatrixInverse;
|
||||
Vector2 size = DrawSize;
|
||||
|
||||
// Inertial moment for a linearly transformed rectangle with a given size around its center.
|
||||
@@ -256,7 +256,7 @@ namespace osu.Framework.Physics
|
||||
/// </summary>
|
||||
public void ReadState()
|
||||
{
|
||||
Matrix3 mat = Parent.DrawInfo.Matrix * ScreenToSimulationSpace;
|
||||
Matrix3 mat = Parent!.DrawInfo.Matrix * ScreenToSimulationSpace;
|
||||
Centre = Vector2Extensions.Transform(BoundingBox.Centre, mat);
|
||||
RotationRadians = MathUtils.DegreesToRadians(Rotation); // TODO: Fix rotations
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace osu.Framework.Physics
|
||||
/// </summary>
|
||||
public virtual void ApplyState()
|
||||
{
|
||||
Matrix3 mat = SimulationToScreenSpace * Parent.DrawInfo.MatrixInverse;
|
||||
Matrix3 mat = SimulationToScreenSpace * Parent!.DrawInfo.MatrixInverse;
|
||||
Position = Vector2Extensions.Transform(Centre, mat) + (Position - BoundingBox.Centre);
|
||||
Rotation = MathUtils.RadiansToDegrees(RotationRadians); // TODO: Fix rotations
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace osu.Framework.Platform.MacOS
|
||||
return IntPtr.Zero;
|
||||
|
||||
var result = generalPasteboard.ReadObjectsForClasses(classArray, null);
|
||||
var objects = result?.ToArray();
|
||||
IntPtr[]? objects = result?.ToArray();
|
||||
|
||||
return objects?.Length > 0 ? objects[0] : IntPtr.Zero;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Framework.Platform.MacOS
|
||||
base.Create();
|
||||
|
||||
// replace [SDLView scrollWheel:(NSEvent *)] with our own version
|
||||
var viewClass = Class.Get("SDLView");
|
||||
IntPtr viewClass = Class.Get("SDLView");
|
||||
scrollWheelHandler = scrollWheel;
|
||||
originalScrollWheel = Class.SwizzleMethod(viewClass, "scrollWheel:", "v@:@", scrollWheelHandler);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
|
||||
public static IntPtr Get(string name)
|
||||
{
|
||||
var id = objc_getClass(name);
|
||||
IntPtr id = objc_getClass(name);
|
||||
if (id == IntPtr.Zero)
|
||||
throw new ArgumentException("Unknown class: " + name);
|
||||
|
||||
@@ -48,12 +48,12 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
/// <returns>A selector for the newly registered method, containing the old implementation.</returns>
|
||||
public static IntPtr SwizzleMethod(IntPtr classHandle, string selector, string typeString, Delegate action)
|
||||
{
|
||||
var targetSelector = Selector.Get(selector);
|
||||
var targetMethod = class_getInstanceMethod(classHandle, targetSelector);
|
||||
var newMethodImplementation = Marshal.GetFunctionPointerForDelegate(action);
|
||||
var newSelector = Selector.Get($"orig_{selector}");
|
||||
IntPtr targetSelector = Selector.Get(selector);
|
||||
IntPtr targetMethod = class_getInstanceMethod(classHandle, targetSelector);
|
||||
IntPtr newMethodImplementation = Marshal.GetFunctionPointerForDelegate(action);
|
||||
IntPtr newSelector = Selector.Get($"orig_{selector}");
|
||||
class_replaceMethod(classHandle, newSelector, newMethodImplementation, typeString);
|
||||
var newMethod = class_getInstanceMethod(classHandle, newSelector);
|
||||
IntPtr newMethod = class_getInstanceMethod(classHandle, newSelector);
|
||||
method_exchangeImplementations(targetMethod, newMethod);
|
||||
return newSelector;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
|
||||
fixed (char* ptrFirstChar = str)
|
||||
{
|
||||
var handle = SendIntPtr(Class.Get("NSString"), Selector.Get("alloc"));
|
||||
IntPtr handle = SendIntPtr(Class.Get("NSString"), Selector.Get("alloc"));
|
||||
return SendIntPtr(handle, Selector.Get("initWithCharacters:length:"), (IntPtr)ptrFirstChar, str.Length);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
image.Save(stream, TiffFormat.Instance);
|
||||
byte[] array = stream.ToArray();
|
||||
|
||||
var handle = SendIntPtr(Class.Get("NSImage"), Selector.Get("alloc"));
|
||||
IntPtr handle = SendIntPtr(Class.Get("NSImage"), Selector.Get("alloc"));
|
||||
return SendIntPtr(handle, Selector.Get("initWithData:"), NSData.FromBytes(array));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
|
||||
internal static NSArray ArrayWithObjects(IntPtr[] objs)
|
||||
{
|
||||
var mutableArray = Cocoa.SendIntPtr(mutable_class_pointer, sel_array);
|
||||
IntPtr mutableArray = Cocoa.SendIntPtr(mutable_class_pointer, sel_array);
|
||||
foreach (IntPtr obj in objs)
|
||||
Cocoa.SendVoid(mutableArray, sel_add_object, obj);
|
||||
return new NSArray(mutableArray);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
|
||||
internal byte[] ToBytes()
|
||||
{
|
||||
var pointer = Cocoa.SendIntPtr(Handle, sel_bytes);
|
||||
IntPtr pointer = Cocoa.SendIntPtr(Handle, sel_bytes);
|
||||
int size = Cocoa.SendInt(Handle, sel_length);
|
||||
|
||||
byte[] bytes = new byte[size];
|
||||
@@ -34,7 +34,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
{
|
||||
fixed (byte* ptr = bytes)
|
||||
{
|
||||
var handle = Cocoa.SendIntPtr(class_pointer, sel_data_with_bytes, (IntPtr)ptr, (ulong)bytes.LongLength);
|
||||
IntPtr handle = Cocoa.SendIntPtr(class_pointer, sel_data_with_bytes, (IntPtr)ptr, (ulong)bytes.LongLength);
|
||||
return new NSData(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace osu.Framework.Platform.MacOS.Native
|
||||
|
||||
internal NSArray? ReadObjectsForClasses(NSArray classArray, NSDictionary? optionDict)
|
||||
{
|
||||
var result = Cocoa.SendIntPtr(Handle, sel_read_objects_for_classes, classArray.Handle, optionDict?.Handle ?? IntPtr.Zero);
|
||||
IntPtr result = Cocoa.SendIntPtr(Handle, sel_read_objects_for_classes, classArray.Handle, optionDict?.Handle ?? IntPtr.Zero);
|
||||
return result == IntPtr.Zero ? null : new NSArray(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1022,7 +1022,7 @@ namespace osu.Framework.Platform.SDL2
|
||||
/// <returns><c>true</c> if the <paramref name="bytePointer"/> was successfully converted to a string.</returns>
|
||||
public static unsafe bool TryGetStringFromBytePointer(byte* bytePointer, out string str)
|
||||
{
|
||||
var ptr = new IntPtr(bytePointer);
|
||||
IntPtr ptr = new IntPtr(bytePointer);
|
||||
|
||||
if (ptr == IntPtr.Zero)
|
||||
{
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace osu.Framework.Platform.SDL2
|
||||
var namesInfo = type.GetRuntimeFields().First(x => x.Name == "_EntryPointNamesInstance");
|
||||
var offsetsInfo = type.GetRuntimeFields().First(x => x.Name == "_EntryPointNameOffsetsInstance");
|
||||
|
||||
var entryPointsInstance = (IntPtr[]?)pointsInfo.GetValue(bindings);
|
||||
IntPtr[]? entryPointsInstance = (IntPtr[]?)pointsInfo.GetValue(bindings);
|
||||
byte[]? entryPointNamesInstance = (byte[]?)namesInfo.GetValue(bindings);
|
||||
int[]? entryPointNameOffsetsInstance = (int[]?)offsetsInfo.GetValue(bindings);
|
||||
|
||||
|
||||
@@ -315,9 +315,9 @@ namespace osu.Framework.Platform
|
||||
if (controllers.ContainsKey(instanceID))
|
||||
return;
|
||||
|
||||
var joystick = SDL.SDL_JoystickOpen(which);
|
||||
IntPtr joystick = SDL.SDL_JoystickOpen(which);
|
||||
|
||||
var controller = IntPtr.Zero;
|
||||
IntPtr controller = IntPtr.Zero;
|
||||
if (SDL.SDL_IsGameController(which) == SDL.SDL_bool.SDL_TRUE)
|
||||
controller = SDL.SDL_GameControllerOpen(which);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace osu.Framework.Platform.Windows
|
||||
public override void SetText(string text)
|
||||
{
|
||||
int bytes = (text.Length + 1) * 2;
|
||||
var source = Marshal.StringToHGlobalUni(text);
|
||||
IntPtr source = Marshal.StringToHGlobalUni(text);
|
||||
|
||||
setClipboard(source, bytes, cf_unicodetext);
|
||||
}
|
||||
@@ -119,11 +119,11 @@ namespace osu.Framework.Platform.Windows
|
||||
EmptyClipboard();
|
||||
|
||||
// IMPORTANT: SetClipboardData requires memory that was acquired with GlobalAlloc using GMEM_MOVABLE.
|
||||
var hGlobal = GlobalAlloc(ghnd, (UIntPtr)bytes);
|
||||
IntPtr hGlobal = GlobalAlloc(ghnd, (UIntPtr)bytes);
|
||||
|
||||
try
|
||||
{
|
||||
var target = GlobalLock(hGlobal);
|
||||
IntPtr target = GlobalLock(hGlobal);
|
||||
if (target == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
@@ -132,21 +133,20 @@ namespace osu.Framework.Statistics
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
private static unsafe Type getTypeFromHandle(IntPtr handle)
|
||||
{
|
||||
// This is super unsafe code which is dependent upon internal CLR structures.
|
||||
#pragma warning disable CS8500
|
||||
TypedReferenceAccess tr = new TypedReferenceAccess { Type = handle };
|
||||
return __reftype(*(TypedReference*)&tr);
|
||||
return TypedReference.GetTargetType(*(TypedReference*)&tr);
|
||||
#pragma warning restore CS8500
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matches the internal layout of <see cref="TypedReference"/>.
|
||||
/// See: https://source.dot.net/#System.Private.CoreLib/src/System/TypedReference.cs
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct TypedReferenceAccess
|
||||
{
|
||||
[JetBrains.Annotations.UsedImplicitly]
|
||||
public IntPtr Value;
|
||||
|
||||
[JetBrains.Annotations.UsedImplicitly]
|
||||
public readonly IntPtr Value;
|
||||
public IntPtr Type;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,82 +33,78 @@ namespace osu.Framework.Testing.Drawables
|
||||
Colour = FrameworkColour.GreenDark,
|
||||
},
|
||||
},
|
||||
new Container
|
||||
new GridContainer
|
||||
{
|
||||
Padding = new MarginPadding(section_padding),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new GridContainer
|
||||
Padding = new MarginPadding(section_padding),
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColumnDimensions = new[]
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
new ToolbarRunAllStepsSection { RelativeSizeAxes = Axes.Y },
|
||||
new ToolbarRateSection { RelativeSizeAxes = Axes.Both },
|
||||
new Container
|
||||
{
|
||||
new ToolbarRunAllStepsSection { RelativeSizeAxes = Axes.Y },
|
||||
new ToolbarRateSection { RelativeSizeAxes = Axes.Both },
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Horizontal = section_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Horizontal = section_padding },
|
||||
Children = new Drawable[]
|
||||
new Container //Backdrop of the record section
|
||||
{
|
||||
new Container //Backdrop of the record section
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-section_padding),
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-section_padding),
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = FrameworkColour.GreenDarker,
|
||||
},
|
||||
Colour = FrameworkColour.GreenDarker,
|
||||
},
|
||||
new ToolbarRecordSection { RelativeSizeAxes = Axes.Y },
|
||||
}
|
||||
},
|
||||
new Container
|
||||
},
|
||||
new ToolbarRecordSection { RelativeSizeAxes = Axes.Y },
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Left = section_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Left = section_padding },
|
||||
Children = new Drawable[]
|
||||
new Container //Backdrop of the bg section
|
||||
{
|
||||
new Container //Backdrop of the bg section
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-section_padding),
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-section_padding),
|
||||
Child = new Box
|
||||
Colour = FrameworkColour.GreenDarker.Darken(0.5f),
|
||||
},
|
||||
},
|
||||
new BasicButton
|
||||
{
|
||||
Text = "bg",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 40,
|
||||
Action = () => browser.CurrentTest.ChangeBackgroundColour(
|
||||
new ColourInfo
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = FrameworkColour.GreenDarker.Darken(0.5f),
|
||||
},
|
||||
},
|
||||
new BasicButton
|
||||
{
|
||||
Text = "bg",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 40,
|
||||
Action = () => browser.CurrentTest.ChangeBackgroundColour(
|
||||
new ColourInfo
|
||||
{
|
||||
TopLeft = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
TopRight = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
BottomLeft = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
BottomRight = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1)
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
TopLeft = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
TopRight = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
BottomLeft = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
|
||||
BottomRight = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1)
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -94,6 +94,8 @@ namespace osu.Framework.Testing
|
||||
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(drawable);
|
||||
|
||||
if (drawable is Game)
|
||||
throw new InvalidOperationException($"Use {nameof(AddGame)} when testing a game instance.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user