diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 2199c2cb8..1244c0bd5 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -15,7 +15,7 @@
]
},
"nvika": {
- "version": "3.0.0",
+ "version": "4.0.0",
"commands": [
"nvika"
]
diff --git a/osu-framework.sln.DotSettings b/osu-framework.sln.DotSettings
index 45f036361..31759a235 100644
--- a/osu-framework.sln.DotSettings
+++ b/osu-framework.sln.DotSettings
@@ -342,6 +342,7 @@
API
ARGB
BPM
+ CG
FBO
CCL
GC
diff --git a/osu.Framework.Android/osu.Framework.Android.csproj b/osu.Framework.Android/osu.Framework.Android.csproj
index 77975c826..f2992c216 100644
--- a/osu.Framework.Android/osu.Framework.Android.csproj
+++ b/osu.Framework.Android/osu.Framework.Android.csproj
@@ -18,7 +18,6 @@
-
diff --git a/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainer.cs b/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainer.cs
index 31f5d3800..6a7c741ce 100644
--- a/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainer.cs
+++ b/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainer.cs
@@ -525,7 +525,7 @@ namespace osu.Framework.Tests.Visual.Containers
AddStep($"scroll to {position}", () =>
{
scrollContainer.ScrollTo(position, false);
- immediateScrollPosition = scrollContainer.Current;
+ immediateScrollPosition = (float)scrollContainer.Current;
});
AddAssert($"immediately scrolled to {clampedTarget}", () => Precision.AlmostEquals(clampedTarget, immediateScrollPosition, 1));
diff --git a/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainerDoublePrecision.cs b/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainerDoublePrecision.cs
new file mode 100644
index 000000000..7b995fdfa
--- /dev/null
+++ b/osu.Framework.Tests/Visual/Containers/TestSceneScrollContainerDoublePrecision.cs
@@ -0,0 +1,149 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Testing;
+using osu.Framework.Utils;
+using osuTK;
+using osuTK.Graphics;
+
+namespace osu.Framework.Tests.Visual.Containers
+{
+ public partial class TestSceneScrollContainerDoublePrecision : ManualInputManagerTestScene
+ {
+ private const float item_height = 5000;
+ private const int item_count = 8000;
+
+ private ScrollContainer scrollContainer = null!;
+
+ [SetUp]
+ public void Setup() => Schedule(Clear);
+
+ [Test]
+ public void TestStandard()
+ {
+ AddStep("Create scroll container", () =>
+ {
+ Add(scrollContainer = new BasicScrollContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ ScrollbarVisible = true,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.7f, 0.9f),
+ });
+
+ for (int i = 0; i < item_count; i++)
+ {
+ scrollContainer.Add(new BoxWithDouble
+ {
+ Colour = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
+ RelativeSizeAxes = Axes.X,
+ Height = item_height,
+ Y = i * item_height,
+ });
+ }
+ });
+
+ scrollIntoView(item_count - 2);
+ scrollIntoView(item_count - 1);
+ }
+
+ [Test]
+ public void TestDoublePrecision()
+ {
+ AddStep("Create scroll container", () =>
+ {
+ Add(scrollContainer = new DoubleScrollContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ ScrollbarVisible = true,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.7f, 0.9f),
+ });
+
+ for (int i = 0; i < item_count; i++)
+ {
+ scrollContainer.Add(new BoxWithDouble
+ {
+ Colour = new Color4(RNG.NextSingle(1), RNG.NextSingle(1), RNG.NextSingle(1), 1),
+ RelativeSizeAxes = Axes.X,
+ Height = item_height,
+ DoubleLocation = i * item_height,
+ });
+ }
+ });
+
+ scrollIntoView(item_count - 2);
+ scrollIntoView(item_count - 1);
+ }
+
+ private void scrollIntoView(int index)
+ {
+ AddStep($"scroll {index} into view", () => scrollContainer.ScrollIntoView(scrollContainer.ChildrenOfType().Skip(index).First()));
+ AddUntilStep($"{index} is visible", () => !scrollContainer.ChildrenOfType().Skip(index).First().IsMaskedAway);
+ }
+
+ public partial class DoubleScrollContainer : BasicScrollContainer
+ {
+ private readonly Container layoutContent;
+
+ public override void Add(Drawable drawable)
+ {
+ if (drawable is not BoxWithDouble boxWithDouble)
+ throw new InvalidOperationException();
+
+ Add(boxWithDouble);
+ }
+
+ public void Add(BoxWithDouble drawable)
+ {
+ if (drawable is not BoxWithDouble boxWithDouble)
+ throw new InvalidOperationException();
+
+ layoutContent.Height = (float)Math.Max(layoutContent.Height, boxWithDouble.DoubleLocation + boxWithDouble.DrawHeight);
+ layoutContent.Add(drawable);
+ }
+
+ public DoubleScrollContainer()
+ {
+ // Managing our own custom layout within ScrollContent causes feedback with internal ScrollContainer calculations,
+ // so we must maintain one level of separation from ScrollContent.
+ base.Add(layoutContent = new Container
+ {
+ RelativeSizeAxes = Axes.X,
+ });
+ }
+
+ public override double GetChildPosInContent(Drawable d, Vector2 offset)
+ {
+ if (d is not BoxWithDouble boxWithDouble)
+ return base.GetChildPosInContent(d, offset);
+
+ return boxWithDouble.DoubleLocation + offset.X;
+ }
+
+ protected override void ApplyCurrentToContent()
+ {
+ Debug.Assert(ScrollDirection == Direction.Vertical);
+
+ double scrollableExtent = -Current + ScrollableExtent * ScrollContent.RelativeAnchorPosition.Y;
+
+ foreach (var d in layoutContent)
+ d.Y = (float)(d.DoubleLocation + scrollableExtent);
+ }
+ }
+
+ public partial class BoxWithDouble : Box
+ {
+ public double DoubleLocation { get; set; }
+ }
+ }
+}
diff --git a/osu.Framework.Tests/Visual/Drawables/TestSceneDelayedLoadWrapper.cs b/osu.Framework.Tests/Visual/Drawables/TestSceneDelayedLoadWrapper.cs
index c031f144c..123e47ed8 100644
--- a/osu.Framework.Tests/Visual/Drawables/TestSceneDelayedLoadWrapper.cs
+++ b/osu.Framework.Tests/Visual/Drawables/TestSceneDelayedLoadWrapper.cs
@@ -101,8 +101,10 @@ namespace osu.Framework.Tests.Visual.Drawables
AddUntilStep("repeating schedulers removed", () => !scroll.Scheduler.HasPendingTasks);
}
+ // Fails once in a blue moon due to loose (but maybe-not-loose-enough) timing requirements. If we break things, it will fail every time so this is fine.
[TestCase(false)]
[TestCase(true)]
+ [FlakyTest]
public void TestManyChildrenFunction(bool instant)
{
AddStep("create children", () =>
diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs
index 9b907e175..8b7de51eb 100644
--- a/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs
+++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneRearrangeableListContainer.cs
@@ -467,7 +467,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
private partial class TestRearrangeableList : BasicRearrangeableListContainer
{
- public float ScrollPosition => ScrollContainer.Current;
+ public float ScrollPosition => (float)ScrollContainer.Current;
public new IReadOnlyDictionary> ItemMap => base.ItemMap;
diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs
index 1242f984f..c59193f54 100644
--- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs
+++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBox.cs
@@ -95,7 +95,15 @@ namespace osu.Framework.Tests.Visual.UserInterface
textBoxes.Add(new CustomTextBox
{
- Text = @"Custom textbox",
+ PlaceholderText = "Custom textbox",
+ Size = new Vector2(500, 30),
+ TabbableContentContainer = textBoxes
+ });
+
+ textBoxes.Add(new BasicTextBox
+ {
+ InputProperties = new TextInputProperties(TextInputType.Text, AutoCapitalisation: true),
+ Text = "Auto-capitalised textbox",
Size = new Vector2(500, 30),
TabbableContentContainer = textBoxes
});
@@ -122,8 +130,9 @@ namespace osu.Framework.Tests.Visual.UserInterface
TabbableContentContainer = otherTextBoxes
});
- otherTextBoxes.Add(new BasicPasswordTextBox
+ otherTextBoxes.Add(new BasicTextBox
{
+ InputProperties = new TextInputProperties(TextInputType.Password),
PlaceholderText = @"Password textbox",
Text = "Secret ;)",
Size = new Vector2(500, 30),
@@ -169,12 +178,13 @@ namespace osu.Framework.Tests.Visual.UserInterface
[Test]
public void TestNumbersOnly()
{
- NumberTextBox numbers = null;
+ BasicTextBox numbers = null;
AddStep("add number textbox", () =>
{
- textBoxes.Add(numbers = new NumberTextBox
+ textBoxes.Add(numbers = new BasicTextBox
{
+ InputProperties = new TextInputProperties(TextInputType.Number),
PlaceholderText = @"Only numbers",
Size = new Vector2(500, 30),
TabbableContentContainer = textBoxes
@@ -800,6 +810,40 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddAssert("nothing selected", () => textBox.SelectedText == string.Empty);
}
+ [Test]
+ public void TestTextChangedDuringDoubleClickDrag()
+ {
+ InsertableTextBox textBox = null;
+
+ AddStep("add textbox", () =>
+ {
+ textBoxes.Add(textBox = new InsertableTextBox
+ {
+ Size = new Vector2(300, 40),
+ Text = "initial text",
+ });
+ });
+
+ AddStep("click on textbox", () =>
+ {
+ InputManager.MoveMouseTo(textBox);
+ InputManager.Click(MouseButton.Left);
+ });
+
+ AddStep("set text", () => textBox.Text = "aaaaaaaaaaaaaaaaaaaa");
+
+ AddStep("select word", () =>
+ {
+ InputManager.Click(MouseButton.Left);
+ InputManager.PressButton(MouseButton.Left);
+ });
+
+ AddStep("insert text", () => textBox.InsertString("a"));
+ AddAssert("text overwritten", () => textBox.Text == "a");
+ AddStep("start drag", () => InputManager.MoveMouseTo(textBox, new Vector2(-50, 0)));
+ AddStep("end drag", () => InputManager.ReleaseButton(MouseButton.Left));
+ }
+
[Test]
public void TestSelectAll()
{
@@ -1042,13 +1086,6 @@ namespace osu.Framework.Tests.Visual.UserInterface
public new void InsertString(string text) => base.InsertString(text);
}
- private partial class NumberTextBox : BasicTextBox
- {
- protected override bool CanAddCharacter(char character) => char.IsAsciiDigit(character);
-
- protected override bool AllowIme => false;
- }
-
private partial class CustomTextBox : BasicTextBox
{
protected override Drawable GetDrawableCharacter(char c) => new ScalingText(c, FontSize);
diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs
index 643924de6..d3211bfd6 100644
--- a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs
+++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextBoxEvents.cs
@@ -206,7 +206,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0);
AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0);
- AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() && textInput.EnsureActivatedQueue.Count == 0);
+ AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() != default && textInput.EnsureActivatedQueue.Count == 0);
AddStep("click deselection", () =>
{
@@ -217,7 +217,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0);
AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0);
- AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() && textInput.EnsureActivatedQueue.Count == 0);
+ AddAssert("text input ensure activated", () => textInput.EnsureActivatedQueue.Dequeue() != default && textInput.EnsureActivatedQueue.Count == 0);
AddStep("click-drag selection", () =>
{
@@ -500,7 +500,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddStep("add second textbox", () => textInputContainer.Add(secondTextBox = new EventQueuesTextBox
{
- ImeAllowed = allowIme,
+ InputProperties = new TextInputProperties(TextInputType.Text, allowIme),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
CommitOnFocusLost = true,
@@ -517,7 +517,7 @@ namespace osu.Framework.Tests.Visual.UserInterface
AddAssert("text input not deactivated", () => textInput.DeactivationQueue.Count == 0);
AddAssert("text input not activated again", () => textInput.ActivationQueue.Count == 0);
- AddAssert($"text input ensure activated {(allowIme ? "with" : "without")} IME", () => textInput.EnsureActivatedQueue.Dequeue() == allowIme && textInput.EnsureActivatedQueue.Count == 0);
+ AddAssert($"text input ensure activated {(allowIme ? "with" : "without")} IME", () => textInput.EnsureActivatedQueue.Dequeue().AllowIme == allowIme && textInput.EnsureActivatedQueue.Count == 0);
AddStep("commit text", () => InputManager.Key(Key.Enter));
AddAssert("text input deactivated", () => textInput.DeactivationQueue.Dequeue());
@@ -574,10 +574,6 @@ namespace osu.Framework.Tests.Visual.UserInterface
public partial class EventQueuesTextBox : TestSceneTextBox.InsertableTextBox
{
- public bool ImeAllowed { get; set; } = true;
-
- protected override bool AllowIme => ImeAllowed;
-
public readonly Queue InputErrorQueue = new Queue();
public readonly Queue UserConsumedTextQueue = new Queue();
public readonly Queue UserRemovedTextQueue = new Queue();
diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneTextInputProperties.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextInputProperties.cs
new file mode 100644
index 000000000..3c45176d7
--- /dev/null
+++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneTextInputProperties.cs
@@ -0,0 +1,68 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input;
+using osu.Framework.Testing;
+using osuTK;
+
+namespace osu.Framework.Tests.Visual.UserInterface
+{
+ public partial class TestSceneTextInputProperties : FrameworkTestScene
+ {
+ [SetUpSteps]
+ public void SetUpSteps()
+ {
+ AddStep("Create text boxes", () =>
+ {
+ FillFlowContainer flow;
+ Child = new BasicScrollContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = flow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Width = 0.9f,
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Spacing = new Vector2(20, 13),
+ }
+ };
+
+ foreach (var textInputType in Enum.GetValues())
+ {
+ flow.Add(new BasicTextBox
+ {
+ TabbableContentContainer = flow,
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Width = 0.45f,
+ PlaceholderText = $"{textInputType} (allow IME)",
+ InputProperties = new TextInputProperties
+ {
+ Type = textInputType,
+ AllowIme = true
+ },
+ });
+ flow.Add(new BasicTextBox
+ {
+ TabbableContentContainer = flow,
+ RelativeSizeAxes = Axes.X,
+ Height = 40,
+ Width = 0.45f,
+ PlaceholderText = $"{textInputType} (no IME)",
+ InputProperties = new TextInputProperties
+ {
+ Type = textInputType,
+ AllowIme = false
+ },
+ });
+ }
+ });
+ }
+ }
+}
diff --git a/osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs b/osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs
index 45253e55d..174cf2193 100644
--- a/osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs
+++ b/osu.Framework.iOS/Graphics/Textures/IOSTextureLoaderStore.cs
@@ -2,22 +2,16 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Diagnostics;
using System.IO;
-using System.Runtime.InteropServices;
-using Accelerate;
-using CoreGraphics;
using Foundation;
-using ObjCRuntime;
-using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
+using osu.Framework.Platform.Apple;
using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Advanced;
using UIKit;
namespace osu.Framework.iOS.Graphics.Textures
{
- public class IOSTextureLoaderStore : TextureLoaderStore
+ internal class IOSTextureLoaderStore : AppleTextureLoaderStore
{
public IOSTextureLoaderStore(IResourceStore store)
: base(store)
@@ -26,81 +20,21 @@ namespace osu.Framework.iOS.Graphics.Textures
protected override unsafe Image ImageFromStream(Stream stream)
{
- using (var nativeData = NSData.FromStream(stream))
+ using (new NSAutoreleasePool())
{
- if (nativeData == null)
+ int length = (int)(stream.Length - stream.Position);
+ var nativeData = NSMutableData.FromLength(length);
+
+ var bytesSpan = new Span(nativeData.MutableBytes.ToPointer(), length);
+ stream.ReadExactly(bytesSpan);
+
+ using var uiImage = UIImage.LoadFromData(nativeData);
+ if (uiImage == null)
throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");
- using (var uiImage = UIImage.LoadFromData(nativeData))
- {
- if (uiImage == null) throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");
-
- int width = (int)uiImage.Size.Width;
- int height = (int)uiImage.Size.Height;
-
- var format = new vImage_CGImageFormat
- {
- BitsPerComponent = 8,
- BitsPerPixel = 32,
- ColorSpace = CGColorSpace.CreateDeviceRGB().Handle,
- // notably, iOS generally uses premultiplied alpha when rendering image to pixels via CGBitmapContext or otherwise,
- // but vImage offers using straight alpha directly without any conversion from our side (by specifying Last instead of PremultipliedLast).
- BitmapInfo = (CGBitmapFlags)CGImageAlphaInfo.Last,
- Decode = null,
- RenderingIntent = CGColorRenderingIntent.Default,
- };
-
- vImageBuffer accelerateImage = default;
-
- // perform initial call to retrieve preferred alignment and bytes-per-row values for the given image dimensions.
- nuint alignment = (nuint)vImageBuffer_Init(&accelerateImage, (uint)height, (uint)width, 32, vImageFlags.NoAllocate);
- Debug.Assert(alignment > 0);
-
- // allocate aligned memory region to contain image pixel data.
- int bytesPerRow = accelerateImage.BytesPerRow;
- int bytesCount = bytesPerRow * accelerateImage.Height;
- accelerateImage.Data = (IntPtr)NativeMemory.AlignedAlloc((nuint)bytesCount, alignment);
-
- var result = vImageBuffer_InitWithCGImage(&accelerateImage, &format, null, uiImage.CGImage!.Handle, vImageFlags.NoAllocate);
- Debug.Assert(result == vImageError.NoError);
-
- var image = new Image(width, height);
- byte* data = (byte*)accelerateImage.Data;
-
- for (int i = 0; i < height; i++)
- {
- var imageRow = image.DangerousGetPixelRowMemory(i);
- var dataRow = new ReadOnlySpan(&data[bytesPerRow * i], width);
- dataRow.CopyTo(imageRow.Span);
- }
-
- NativeMemory.AlignedFree(accelerateImage.Data.ToPointer());
- return image;
- }
+ var cgImage = new Platform.Apple.Native.CGImage(uiImage.CGImage!.Handle);
+ return ImageFromCGImage(cgImage);
}
}
-
- #region Accelerate API
-
- [DllImport(Constants.AccelerateLibrary)]
- private static extern unsafe vImageError vImageBuffer_Init(vImageBuffer* buf, uint height, uint width, uint pixelBits, vImageFlags flags);
-
- [DllImport(Constants.AccelerateLibrary)]
- private static extern unsafe vImageError vImageBuffer_InitWithCGImage(vImageBuffer* buf, vImage_CGImageFormat* format, nfloat* backgroundColour, NativeHandle image, vImageFlags flags);
-
- // ReSharper disable once InconsistentNaming
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct vImage_CGImageFormat
- {
- public uint BitsPerComponent;
- public uint BitsPerPixel;
- public NativeHandle ColorSpace;
- public CGBitmapFlags BitmapInfo;
- public uint Version;
- public nfloat* Decode;
- public CGColorRenderingIntent RenderingIntent;
- }
-
- #endregion
}
}
diff --git a/osu.Framework.iOS/IOSFilePresenter.cs b/osu.Framework.iOS/IOSFilePresenter.cs
index b697e897e..e69e88ade 100644
--- a/osu.Framework.iOS/IOSFilePresenter.cs
+++ b/osu.Framework.iOS/IOSFilePresenter.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
+using CoreGraphics;
using Foundation;
using UIKit;
using UniformTypeIdentifiers;
@@ -26,16 +27,24 @@ namespace osu.Framework.iOS
if (documentInteraction.PresentPreview(true))
return true;
+ // Since file menus on iPadOS appear in a popover-like style, UIDocumentInteractionController requires specifying
+ // a rectangle in the present functions to display the menu as a popover around that rectangle.
+ // Ultimately, we want to be given a rectangle by the game so the popover doesn't look out of place,
+ // but for the time being, specify CGRectZero to make the popover display at the top left.
var gameView = window.ViewController.View!;
- return documentInteraction.PresentOpenInMenu(gameView.Bounds, gameView, true);
+ return documentInteraction.PresentOpenInMenu(CGRect.Empty, gameView, true);
}
public bool PresentFile(string filename)
{
setupViewController(filename);
+ // Since file menus on iPadOS appear in a popover-like style, UIDocumentInteractionController requires specifying
+ // a rectangle in the present functions to display the menu as a popover around that rectangle.
+ // Ultimately, we want to be given a rectangle by the game so the popover doesn't look out of place,
+ // but for the time being, specify CGRectZero to make the popover display at the top left.
var gameView = window.ViewController.View!;
- return documentInteraction.PresentOptionsMenu(gameView.Bounds, gameView, true);
+ return documentInteraction.PresentOptionsMenu(CGRect.Empty, gameView, true);
}
private void setupViewController(string filename)
diff --git a/osu.Framework.iOS/IOSGameHost.cs b/osu.Framework.iOS/IOSGameHost.cs
index 94b95ff14..91d860816 100644
--- a/osu.Framework.iOS/IOSGameHost.cs
+++ b/osu.Framework.iOS/IOSGameHost.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using Foundation;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
@@ -11,6 +12,8 @@ using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.Input.Bindings;
+using osu.Framework.Input.Handlers;
+using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.IO.Stores;
using osu.Framework.iOS.Graphics.Textures;
using osu.Framework.iOS.Graphics.Video;
@@ -85,6 +88,20 @@ namespace osu.Framework.iOS
public override VideoDecoder CreateVideoDecoder(Stream stream)
=> new IOSVideoDecoder(Renderer, stream);
+ protected override IEnumerable CreateAvailableInputHandlers()
+ {
+ var handlers = base.CreateAvailableInputHandlers();
+
+ foreach (var h in handlers.OfType())
+ {
+ // Similar to macOS, "relative mode" is also broken on iOS.
+ h.UseRelativeMode.Value = false;
+ h.UseRelativeMode.Default = false;
+ }
+
+ return handlers;
+ }
+
public override ISystemFileSelector? CreateSystemFileSelector(string[] allowedExtensions)
{
IOSFileSelector? selector = null;
diff --git a/osu.Framework/Game.cs b/osu.Framework/Game.cs
index 1f7a8e827..30d048699 100644
--- a/osu.Framework/Game.cs
+++ b/osu.Framework/Game.cs
@@ -128,6 +128,20 @@ namespace osu.Framework
protected sealed override void AddInternal(Drawable drawable) => throw new InvalidOperationException($"Use {nameof(Add)} or {nameof(Content)} instead.");
+ ///
+ /// The earliest point of entry during starting execution of a game.
+ /// This should be used to set up any low level tasks such as exception handling.
+ ///
+ ///
+ /// At this point in execution, only and are guaranteed to be valid for use.
+ /// They are provided as and respectively for convenience.
+ ///
+ /// The default game storage.
+ /// The default cache storage.
+ public virtual void SetupLogging(Storage gameStorage, Storage cacheStorage)
+ {
+ }
+
///
/// As Load is run post host creation, you can override this method to alter properties of the host before it makes itself visible to the user.
///
diff --git a/osu.Framework/Graphics/Containers/ScrollContainer.cs b/osu.Framework/Graphics/Containers/ScrollContainer.cs
index dbd89fed1..eb2f2de27 100644
--- a/osu.Framework/Graphics/Containers/ScrollContainer.cs
+++ b/osu.Framework/Graphics/Containers/ScrollContainer.cs
@@ -118,7 +118,7 @@ namespace osu.Framework.Graphics.Containers
///
/// The current scroll position.
///
- public float Current { get; private set; }
+ public double Current { get; private set; }
///
/// The target scroll position which is exponentially approached by current via a rate of distance decay.
@@ -126,12 +126,12 @@ namespace osu.Framework.Graphics.Containers
///
/// When not animating scroll position, this will always be equal to .
///
- public float Target { get; private set; }
+ public double Target { get; private set; }
///
/// The maximum distance that can be scrolled in the scroll direction.
///
- public float ScrollableExtent => Math.Max(AvailableContent - DisplayableContent, 0);
+ public double ScrollableExtent => Math.Max(AvailableContent - DisplayableContent, 0);
///
/// The maximum distance that the scrollbar can move in the scroll direction.
@@ -139,14 +139,14 @@ namespace osu.Framework.Graphics.Containers
///
/// May not be accurate to actual display of scrollbar if or are overridden.
///
- protected float ScrollbarMovementExtent => Math.Max(DisplayableContent - Scrollbar.DrawSize[ScrollDim], 0);
+ protected double ScrollbarMovementExtent => Math.Max(DisplayableContent - Scrollbar.DrawSize[ScrollDim], 0);
///
/// Clamp a value to the available scroll range.
///
/// The value to clamp.
/// An extension value beyond the normal extent.
- protected float Clamp(float position, float extension = 0) => Math.Max(Math.Min(position, ScrollableExtent + extension), -extension);
+ protected double Clamp(double position, double extension = 0) => Math.Max(Math.Min(position, ScrollableExtent + extension), -extension);
protected override Container Content => ScrollContent;
@@ -345,8 +345,8 @@ namespace osu.Framework.Graphics.Containers
Vector2 childDelta = ToLocalSpace(e.ScreenSpaceMousePosition) - ToLocalSpace(e.ScreenSpaceLastMousePosition);
- float scrollOffset = -childDelta[ScrollDim];
- float clampedScrollOffset = Clamp(Target + scrollOffset) - Clamp(Target);
+ double scrollOffset = -childDelta[ScrollDim];
+ double clampedScrollOffset = Clamp(Target + scrollOffset) - Clamp(Target);
// If we are dragging past the extent of the scrollable area, half the offset
// such that the user can feel it.
@@ -418,13 +418,13 @@ namespace osu.Framework.Graphics.Containers
/// Immediately offsets the current and target scroll position.
///
/// The scroll offset.
- public void OffsetScrollPosition(float offset)
+ public virtual void OffsetScrollPosition(double offset)
{
Target += offset;
Current += offset;
}
- private void scrollByOffset(float value, bool animated, double distanceDecay = float.PositiveInfinity) =>
+ private void scrollByOffset(double value, bool animated, double distanceDecay = float.PositiveInfinity) =>
OnUserScroll(Target + value, animated, distanceDecay);
///
@@ -454,7 +454,7 @@ namespace osu.Framework.Graphics.Containers
///
/// The amount by which we should scroll.
/// Whether to animate the movement.
- public void ScrollBy(float offset, bool animated = true) => scrollTo(Target + offset, animated);
+ public void ScrollBy(double offset, bool animated = true) => scrollTo(Target + offset, animated);
///
/// Handle a scroll to an absolute position from a user input.
@@ -462,7 +462,7 @@ namespace osu.Framework.Graphics.Containers
/// The position to scroll to.
/// Whether to animate the movement.
/// Controls the rate with which the target position is approached after jumping to a specific location. Default is .
- protected virtual void OnUserScroll(float value, bool animated = true, double? distanceDecay = null) =>
+ protected virtual void OnUserScroll(double value, bool animated = true, double? distanceDecay = null) =>
ScrollTo(value, animated, distanceDecay);
///
@@ -471,9 +471,9 @@ namespace osu.Framework.Graphics.Containers
/// The position to scroll to.
/// Whether to animate the movement.
/// Controls the rate with which the target position is approached after jumping to a specific location. Default is .
- public void ScrollTo(float value, bool animated = true, double? distanceDecay = null) => scrollTo(value, animated, distanceDecay ?? DistanceDecayJump);
+ public void ScrollTo(double value, bool animated = true, double? distanceDecay = null) => scrollTo(value, animated, distanceDecay ?? DistanceDecayJump);
- private void scrollTo(float value, bool animated, double distanceDecay = float.PositiveInfinity)
+ private void scrollTo(double value, bool animated, double distanceDecay = double.PositiveInfinity)
{
Target = Clamp(value, ClampExtension);
@@ -497,11 +497,11 @@ namespace osu.Framework.Graphics.Containers
/// Whether to animate the movement.
public void ScrollIntoView(Drawable d, bool animated = true)
{
- float childPos0 = GetChildPosInContent(d);
- float childPos1 = GetChildPosInContent(d, d.DrawSize);
+ double childPos0 = GetChildPosInContent(d);
+ double childPos1 = GetChildPosInContent(d, d.DrawSize);
- float minPos = Math.Min(childPos0, childPos1);
- float maxPos = Math.Max(childPos0, childPos1);
+ double minPos = Math.Min(childPos0, childPos1);
+ double maxPos = Math.Max(childPos0, childPos1);
if (minPos < Current || (minPos > Current && d.DrawSize[ScrollDim] > DisplayableContent))
ScrollTo(minPos, animated);
@@ -515,14 +515,14 @@ namespace osu.Framework.Graphics.Containers
/// The child to get the position from.
/// Positional offset in the child's space.
/// The position of the child.
- public float GetChildPosInContent(Drawable d, Vector2 offset) => d.ToSpaceOfOtherDrawable(offset, ScrollContent)[ScrollDim];
+ public virtual double GetChildPosInContent(Drawable d, Vector2 offset) => d.ToSpaceOfOtherDrawable(offset, ScrollContent)[ScrollDim];
///
/// Determines the position of a child in the content.
///
/// The child to get the position from.
/// The position of the child.
- public float GetChildPosInContent(Drawable d) => GetChildPosInContent(d, Vector2.Zero);
+ public double GetChildPosInContent(Drawable d) => GetChildPosInContent(d, Vector2.Zero);
private void updatePosition()
{
@@ -544,15 +544,15 @@ namespace osu.Framework.Graphics.Containers
localDistanceDecay = distance_decay_clamping * 2;
// Lastly, we gradually nudge the target towards valid bounds.
- Target = (float)Interpolation.Lerp(Clamp(Target), Target, Math.Exp(-distance_decay_clamping * Time.Elapsed));
+ Target = Interpolation.Lerp(Clamp(Target), Target, Math.Exp(-distance_decay_clamping * Time.Elapsed));
- float clampedTarget = Clamp(Target);
+ double clampedTarget = Clamp(Target);
if (Precision.AlmostEquals(clampedTarget, Target))
Target = clampedTarget;
}
// Exponential interpolation between the target and our current scroll position.
- Current = (float)Interpolation.Lerp(Target, Current, Math.Exp(-localDistanceDecay * Time.Elapsed));
+ Current = Interpolation.Lerp(Target, Current, Math.Exp(-localDistanceDecay * Time.Elapsed));
// This prevents us from entering the de-normalized range of floating point numbers when approaching target closely.
if (Precision.AlmostEquals(Current, Target))
@@ -578,15 +578,27 @@ namespace osu.Framework.Graphics.Containers
}
if (ScrollDirection == Direction.Horizontal)
- {
Scrollbar.X = ToScrollbarPosition(Current);
- ScrollContent.X = -Current + ScrollableExtent * ScrollContent.RelativeAnchorPosition.X;
- }
else
- {
Scrollbar.Y = ToScrollbarPosition(Current);
- ScrollContent.Y = -Current + ScrollableExtent * ScrollContent.RelativeAnchorPosition.Y;
- }
+
+ ApplyCurrentToContent();
+ }
+
+ ///
+ /// This is the final internal step of updating the scroll container, which takes
+ /// and applies it to in order to
+ /// correctly offset children.
+ ///
+ /// Overriding this method can be used to inhibit this default behaviour, to for instance
+ /// redirect the positioning to another container or change the way it is applied.
+ ///
+ protected virtual void ApplyCurrentToContent()
+ {
+ if (ScrollDirection == Direction.Horizontal)
+ ScrollContent.X = (float)(-Current + (ScrollableExtent * ScrollContent.RelativeAnchorPosition.X));
+ else
+ ScrollContent.Y = (float)(-Current + (ScrollableExtent * ScrollContent.RelativeAnchorPosition.Y));
}
///
@@ -594,12 +606,12 @@ namespace osu.Framework.Graphics.Containers
///
/// The absolute scroll position (e.g. ).
/// The scrollbar position.
- protected virtual float ToScrollbarPosition(float scrollPosition)
+ protected virtual float ToScrollbarPosition(double scrollPosition)
{
if (Precision.AlmostEquals(0, ScrollableExtent))
return 0;
- return ScrollbarMovementExtent * (scrollPosition / ScrollableExtent);
+ return (float)(ScrollbarMovementExtent * (scrollPosition / ScrollableExtent));
}
///
@@ -612,7 +624,7 @@ namespace osu.Framework.Graphics.Containers
if (Precision.AlmostEquals(0, ScrollbarMovementExtent))
return 0;
- return ScrollableExtent * (scrollbarPosition / ScrollbarMovementExtent);
+ return (float)(ScrollableExtent * (scrollbarPosition / ScrollbarMovementExtent));
}
///
diff --git a/osu.Framework/Graphics/Containers/SearchContainer.cs b/osu.Framework/Graphics/Containers/SearchContainer.cs
index 47edd3dd6..3f8ad7f87 100644
--- a/osu.Framework/Graphics/Containers/SearchContainer.cs
+++ b/osu.Framework/Graphics/Containers/SearchContainer.cs
@@ -120,7 +120,7 @@ namespace osu.Framework.Graphics.Containers
private void performFilter()
{
- string[] terms = (searchTerm ?? string.Empty).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ string[] terms = (searchTerm ?? string.Empty).Split(' ', StringSplitOptions.RemoveEmptyEntries);
matchSubTree(this, terms, terms.Length > 0, allowNonContiguousMatching);
}
diff --git a/osu.Framework/Graphics/DrawableExtensions.cs b/osu.Framework/Graphics/DrawableExtensions.cs
index ec61b220a..be6194ea4 100644
--- a/osu.Framework/Graphics/DrawableExtensions.cs
+++ b/osu.Framework/Graphics/DrawableExtensions.cs
@@ -49,7 +49,7 @@ namespace osu.Framework.Graphics
/// The to be checked.
/// The root to be checked against.
/// Whether the drawable was rooted.
- internal static bool IsRootedAt(this Drawable? drawable, Drawable root)
+ public static bool IsRootedAt(this Drawable? drawable, Drawable root)
{
if (drawable == root) return true;
diff --git a/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs b/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs
deleted file mode 100644
index b0791317b..000000000
--- a/osu.Framework/Graphics/UserInterface/BasicPasswordTextBox.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-using osu.Framework.Input;
-
-namespace osu.Framework.Graphics.UserInterface
-{
- public partial class BasicPasswordTextBox : BasicTextBox, ISuppressKeyEventLogging
- {
- protected virtual char MaskCharacter => '*';
-
- protected override bool AllowClipboardExport => false;
-
- protected override bool AllowWordNavigation => false;
-
- protected override bool AllowIme => false;
-
- protected override Drawable AddCharacterToFlow(char c) => base.AddCharacterToFlow(MaskCharacter);
- }
-}
diff --git a/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs b/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs
index c5e3f9d2c..a23df45f9 100644
--- a/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs
+++ b/osu.Framework/Graphics/UserInterface/DropdownSearchBar.cs
@@ -192,20 +192,20 @@ namespace osu.Framework.Graphics.UserInterface
platformSource.OnImeResult += TriggerImeResult;
}
- protected override void ActivateTextInput(bool allowIme)
+ protected override void ActivateTextInput(TextInputProperties properties)
{
- base.ActivateTextInput(allowIme);
+ base.ActivateTextInput(properties);
if (allowTextInput)
- platformSource.Activate(allowIme, imeRectangle ?? RectangleF.Empty);
+ platformSource.Activate(properties, imeRectangle ?? RectangleF.Empty);
}
- protected override void EnsureTextInputActivated(bool allowIme)
+ protected override void EnsureTextInputActivated(TextInputProperties properties)
{
- base.EnsureTextInputActivated(allowIme);
+ base.EnsureTextInputActivated(properties);
if (allowTextInput)
- platformSource.EnsureActivated(allowIme, imeRectangle);
+ platformSource.EnsureActivated(properties, imeRectangle);
}
protected override void DeactivateTextInput()
diff --git a/osu.Framework/Graphics/UserInterface/TextBox.cs b/osu.Framework/Graphics/UserInterface/TextBox.cs
index e825299cd..888f2eeb1 100644
--- a/osu.Framework/Graphics/UserInterface/TextBox.cs
+++ b/osu.Framework/Graphics/UserInterface/TextBox.cs
@@ -29,13 +29,18 @@ using osuTK.Input;
namespace osu.Framework.Graphics.UserInterface
{
- public abstract partial class TextBox : TabbableContainer, IHasCurrentValue, IKeyBindingHandler
+ public abstract partial class TextBox : TabbableContainer, IHasCurrentValue, IKeyBindingHandler, ICanSuppressKeyEventLogging
{
protected FillFlowContainer TextFlow { get; private set; }
protected Container TextContainer { get; private set; }
public override bool HandleNonPositionalInput => HasFocus;
+ ///
+ /// A character displayed whenever the type of text input set by is hidden.
+ ///
+ protected virtual char MaskCharacter => '*';
+
///
/// Padding to be used within the TextContainer. Requires special handling due to the sideways scrolling of text content.
///
@@ -50,12 +55,14 @@ namespace osu.Framework.Graphics.UserInterface
///
/// Whether clipboard copying functionality is allowed.
///
- protected virtual bool AllowClipboardExport => true;
+ protected virtual bool AllowClipboardExport => !InputProperties.Type.IsPassword();
///
/// Whether seeking to word boundaries is allowed.
///
- protected virtual bool AllowWordNavigation => true;
+ protected virtual bool AllowWordNavigation => !InputProperties.Type.IsPassword();
+
+ bool ICanSuppressKeyEventLogging.SuppressKeyEventLogging => InputProperties.Type.IsPassword();
///
/// Represents the left/right selection coordinates of the word double clicked on when dragging.
@@ -67,18 +74,14 @@ namespace osu.Framework.Graphics.UserInterface
///
public virtual bool HandleLeftRightArrows => true;
- ///
- /// Whether to allow IME input when this text box has input focus.
- ///
- ///
- /// This is just a hint to the native implementation, some might respect this,
- /// while others will ignore and always have the IME (dis)allowed.
- ///
- ///
- /// Useful for situations where IME input is not wanted, such as for passwords, numbers, or romanised text.
- ///
+ [Obsolete($"Use {nameof(InputProperties)} instead.")] // can be removed 20250506
protected virtual bool AllowIme => true;
+ ///
+ /// A set of properties to consider when interacting with this .
+ ///
+ public TextInputProperties InputProperties { get; init; }
+
///
/// Check if a character can be added to this TextBox.
///
@@ -87,9 +90,14 @@ namespace osu.Framework.Graphics.UserInterface
protected virtual bool CanAddCharacter(char character) => true;
///
- /// Private helper for , additionally requiring that the character is not a control character.
+ /// Private helper for , additionally requiring that the character is not a control character and obeys .
///
- private bool canAddCharacter(char character) => !char.IsControl(character) && CanAddCharacter(character);
+ private bool canAddCharacter(char character)
+ {
+ return !char.IsControl(character)
+ && (!InputProperties.Type.IsNumerical() || char.IsAsciiDigit(character))
+ && CanAddCharacter(character);
+ }
private bool readOnly;
@@ -158,6 +166,10 @@ namespace osu.Framework.Graphics.UserInterface
protected TextBox()
{
+#pragma warning disable CS0618 // Type or member is obsolete
+ InputProperties = new TextInputProperties(TextInputType.Text, AllowIme);
+#pragma warning restore CS0618 // Type or member is obsolete
+
Masking = true;
Children = new Drawable[]
@@ -773,6 +785,7 @@ namespace osu.Framework.Graphics.UserInterface
TextFlow.ChangeChildDepth(TextFlow[i], getDepthForCharacterIndex(i));
selectionStart = selectionEnd = removeStart;
+ doubleClickWord = null;
endTextChange(beganChange);
cursorAndLayout.Invalidate();
@@ -789,6 +802,9 @@ namespace osu.Framework.Graphics.UserInterface
protected virtual Drawable AddCharacterToFlow(char c)
{
+ if (InputProperties.Type.IsPassword())
+ c = MaskCharacter;
+
// Remove all characters to the right and store them in a local list,
// such that their depth can be updated.
List charsRight = new List();
@@ -1339,7 +1355,7 @@ namespace osu.Framework.Graphics.UserInterface
protected override bool OnClick(ClickEvent e)
{
if (!ReadOnly && textInputBound)
- textInput.EnsureActivated(AllowIme);
+ textInput.EnsureActivated(InputProperties);
return !ReadOnly;
}
@@ -1366,7 +1382,7 @@ namespace osu.Framework.Graphics.UserInterface
if (textInputBound)
{
- textInput.EnsureActivated(AllowIme);
+ textInput.EnsureActivated(InputProperties);
return;
}
@@ -1374,9 +1390,9 @@ namespace osu.Framework.Graphics.UserInterface
// We don't deactivate and activate, but instead keep text input active during the focus handoff, so that virtual keyboards on phones don't flicker.
if (previous?.textInput == textInput)
- textInput.EnsureActivated(AllowIme, ScreenSpaceDrawQuad.AABBFloat);
+ textInput.EnsureActivated(InputProperties, ScreenSpaceDrawQuad.AABBFloat);
else
- textInput.Activate(AllowIme, ScreenSpaceDrawQuad.AABBFloat);
+ textInput.Activate(InputProperties, ScreenSpaceDrawQuad.AABBFloat);
textInput.OnTextInput += handleTextInput;
textInput.OnImeComposition += handleImeComposition;
diff --git a/osu.Framework/Input/ButtonEventManager.cs b/osu.Framework/Input/ButtonEventManager.cs
index b10ef23a2..ab8b5e492 100644
--- a/osu.Framework/Input/ButtonEventManager.cs
+++ b/osu.Framework/Input/ButtonEventManager.cs
@@ -133,9 +133,18 @@ namespace osu.Framework.Input
}
if (handledBy != null)
- Logger.Log($"{e} handled by {handledBy}.", LoggingTarget.Runtime, LogLevel.Debug);
+ {
+ Logger.Log(SuppressLoggingEventInformation(handledBy)
+ ? $"{e.GetType().Name} handled by {handledBy}."
+ : $"{e} handled by {handledBy}.", LoggingTarget.Runtime, LogLevel.Debug);
+ }
return handledBy;
}
+
+ ///
+ /// Whether information about the event should be suppressed from logging for the given drawable.
+ ///
+ protected virtual bool SuppressLoggingEventInformation(Drawable drawable) => false;
}
}
diff --git a/osu.Framework/Input/ISuppressKeyEventLogging.cs b/osu.Framework/Input/ICanSuppressKeyEventLogging.cs
similarity index 51%
rename from osu.Framework/Input/ISuppressKeyEventLogging.cs
rename to osu.Framework/Input/ICanSuppressKeyEventLogging.cs
index 686ab71a9..02f6f4764 100644
--- a/osu.Framework/Input/ISuppressKeyEventLogging.cs
+++ b/osu.Framework/Input/ICanSuppressKeyEventLogging.cs
@@ -4,10 +4,14 @@
namespace osu.Framework.Input
{
///
- /// Marker interface which suppresses logging of keyboard input events.
+ /// An interface which suppresses logging of keyboard input events.
/// Useful for password fields, where user input should not be logged.
///
- public interface ISuppressKeyEventLogging
+ public interface ICanSuppressKeyEventLogging
{
+ ///
+ /// Whether key event logging should be suppressed for this drawable.
+ ///
+ bool SuppressKeyEventLogging { get; }
}
}
diff --git a/osu.Framework/Input/InputManager.cs b/osu.Framework/Input/InputManager.cs
index 825bb5822..87efb4345 100644
--- a/osu.Framework/Input/InputManager.cs
+++ b/osu.Framework/Input/InputManager.cs
@@ -10,7 +10,6 @@ using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ListExtensions;
-using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
@@ -1002,37 +1001,13 @@ namespace osu.Framework.Input
{
foreach (var d in drawables)
{
- if (!d.TriggerEvent(e)) continue;
-
- if (shouldLog(e))
- {
- string detail = d is ISuppressKeyEventLogging ? e.GetType().ReadableName() : e.ToString();
- Logger.Log($"{detail} handled by {d}.", LoggingTarget.Runtime, LogLevel.Debug);
- }
-
- return true;
+ if (d.TriggerEvent(e))
+ return true;
}
return false;
}
- private bool shouldLog(UIEvent eventType)
- {
- switch (eventType)
- {
- case KeyDownEvent k:
- return !k.Repeat;
-
- case DragEvent:
- case ScrollEvent:
- case MouseMoveEvent:
- return false;
-
- default:
- return true;
- }
- }
-
///
/// Unfocus the current focused drawable if it is no longer in a valid state.
///
diff --git a/osu.Framework/Input/KeyEventManager.cs b/osu.Framework/Input/KeyEventManager.cs
index b716e7b67..d1434a3bf 100644
--- a/osu.Framework/Input/KeyEventManager.cs
+++ b/osu.Framework/Input/KeyEventManager.cs
@@ -33,5 +33,7 @@ namespace osu.Framework.Input
protected override void HandleButtonUp(InputState state, List targets) =>
PropagateButtonEvent(targets, new KeyUpEvent(state, Button));
+
+ protected override bool SuppressLoggingEventInformation(Drawable drawable) => drawable is ICanSuppressKeyEventLogging canSuppress && canSuppress.SuppressKeyEventLogging;
}
}
diff --git a/osu.Framework/Input/SDLWindowTextInput.cs b/osu.Framework/Input/SDLWindowTextInput.cs
index c0fd539f4..79fa1d848 100644
--- a/osu.Framework/Input/SDLWindowTextInput.cs
+++ b/osu.Framework/Input/SDLWindowTextInput.cs
@@ -37,16 +37,16 @@ namespace osu.Framework.Input
TriggerImeComposition(text, selectionStart, selectionLength);
}
- protected override void ActivateTextInput(bool allowIme)
+ protected override void ActivateTextInput(TextInputProperties properties)
{
window.TextInput += handleTextInput;
window.TextEditing += handleTextEditing;
- window.StartTextInput(allowIme);
+ window.StartTextInput(properties);
}
- protected override void EnsureTextInputActivated(bool allowIme)
+ protected override void EnsureTextInputActivated(TextInputProperties properties)
{
- window.StartTextInput(allowIme);
+ window.StartTextInput(properties);
}
protected override void DeactivateTextInput()
diff --git a/osu.Framework/Input/TextInputProperties.cs b/osu.Framework/Input/TextInputProperties.cs
new file mode 100644
index 000000000..1175e4141
--- /dev/null
+++ b/osu.Framework/Input/TextInputProperties.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Framework.Input
+{
+ ///
+ /// Represents a number of properties to consider during a text input session.
+ ///
+ /// The type of text being input.
+ ///
+ ///
+ /// Whether IME should be allowed during this text input session, if supported by the given text input type.
+ ///
+ ///
+ /// Note that this is just a hint to the native implementation, some might respect this,
+ /// while others will ignore and always have the IME (dis)allowed.
+ ///
+ ///
+ /// Whether text should be automatically capitalised.
+ public record struct TextInputProperties(TextInputType Type, bool AllowIme = true, bool AutoCapitalisation = false);
+}
diff --git a/osu.Framework/Input/TextInputSource.cs b/osu.Framework/Input/TextInputSource.cs
index 237d17f36..9b77568e3 100644
--- a/osu.Framework/Input/TextInputSource.cs
+++ b/osu.Framework/Input/TextInputSource.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-#nullable disable
-
using System;
using System.Threading;
using osu.Framework.Graphics.Primitives;
@@ -29,7 +27,7 @@ namespace osu.Framework.Input
/// Activates this .
/// User text input can be acquired through , and .
///
- /// Whether input using IME should be allowed.
+ /// A set of properties to consider during this text input session.
///
/// Rough location of where the text will be input, so the native implementation
/// can adjust virtual keyboards and IME popups.
@@ -37,36 +35,36 @@ namespace osu.Framework.Input
///
/// Each must be followed by a .
///
- public void Activate(bool allowIme, RectangleF imeRectangle)
+ public void Activate(TextInputProperties properties, RectangleF imeRectangle)
{
if (Interlocked.Increment(ref activationCounter) == 1)
{
SetImeRectangle(imeRectangle);
- ActivateTextInput(allowIme);
+ ActivateTextInput(properties);
}
else
// the latest consumer that activated should always take precedence in (dis)allowing IME.
- EnsureActivated(allowIme, imeRectangle);
+ EnsureActivated(properties, imeRectangle);
}
///
/// Ensures that the native implementation that retrieves user text input is activated
/// and that the user can start entering text.
///
- /// Whether input using IME should be allowed.
+ /// A set of properties to consider during this text input session.
///
/// Rough location of where the text will be input, so the native implementation
/// can adjust virtual keyboards and IME popups. Can be null to avoid changing
/// the IME rectangle.
///
- public void EnsureActivated(bool allowIme, RectangleF? imeRectangle = null)
+ public void EnsureActivated(TextInputProperties properties, RectangleF? imeRectangle = null)
{
if (activationCounter >= 1)
{
if (imeRectangle.HasValue)
SetImeRectangle(imeRectangle.Value);
- EnsureTextInputActivated(allowIme);
+ EnsureTextInputActivated(properties);
}
}
@@ -103,29 +101,29 @@ namespace osu.Framework.Input
///
/// Invoked on text input.
///
- public event Action OnTextInput;
+ public event Action? OnTextInput;
///
/// Invoked when IME composition starts or changes.
///
/// Empty string for text means that the composition has been cancelled.
- public event ImeCompositionDelegate OnImeComposition;
+ public event ImeCompositionDelegate? OnImeComposition;
///
/// Invoked when IME composition successfully completes.
///
- public event Action OnImeResult;
+ public event Action? OnImeResult;
///
/// Activates the native implementation that provides text input.
/// Should be overriden per-platform.
///
- /// Whether input using IME should be allowed.
+ /// A set of properties to consider during this text input session.
///
/// An active native implementation should call on new text input
/// and forward IME composition events through and .
///
- protected virtual void ActivateTextInput(bool allowIme)
+ protected virtual void ActivateTextInput(TextInputProperties properties)
{
}
@@ -134,7 +132,7 @@ namespace osu.Framework.Input
///
/// Only called if the native implementation has been activated with .
///
- protected virtual void EnsureTextInputActivated(bool allowIme)
+ protected virtual void EnsureTextInputActivated(TextInputProperties properties)
{
}
diff --git a/osu.Framework/Input/TextInputType.cs b/osu.Framework/Input/TextInputType.cs
new file mode 100644
index 000000000..2a1645875
--- /dev/null
+++ b/osu.Framework/Input/TextInputType.cs
@@ -0,0 +1,74 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Framework.Input
+{
+ public enum TextInputType
+ {
+ ///
+ /// Plain text, default type of text input.
+ ///
+ Text,
+
+ ///
+ /// The text input is a person's name.
+ ///
+ Name,
+
+ ///
+ /// The text input is an email address.
+ ///
+ EmailAddress,
+
+ ///
+ /// The text input is a username.
+ ///
+ Username,
+
+ ///
+ /// The text input is numerical.
+ ///
+ Number,
+
+ ///
+ /// The text input is a password hidden from the user.
+ ///
+ Password,
+
+ ///
+ /// The text input is a numerical password hidden from the user.
+ ///
+ NumericalPassword,
+ }
+
+ public static class TextInputTypeExtensions
+ {
+ public static bool IsPassword(this TextInputType type)
+ {
+ switch (type)
+ {
+ case TextInputType.Password:
+ case TextInputType.NumericalPassword:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsNumerical(this TextInputType type)
+ {
+ switch (type)
+ {
+ case TextInputType.Number:
+ case TextInputType.NumericalPassword:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ public static bool SupportsIme(this TextInputType type) => type == TextInputType.Name || type == TextInputType.Text;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/AppleTextureLoaderStore.cs b/osu.Framework/Platform/Apple/AppleTextureLoaderStore.cs
new file mode 100644
index 000000000..4359b9290
--- /dev/null
+++ b/osu.Framework/Platform/Apple/AppleTextureLoaderStore.cs
@@ -0,0 +1,69 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using osu.Framework.Graphics.Textures;
+using osu.Framework.IO.Stores;
+using osu.Framework.Platform.Apple.Native;
+using osu.Framework.Platform.Apple.Native.Accelerate;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace osu.Framework.Platform.Apple
+{
+ internal abstract class AppleTextureLoaderStore : TextureLoaderStore
+ {
+ protected AppleTextureLoaderStore(IResourceStore store)
+ : base(store)
+ {
+ }
+
+ protected unsafe Image ImageFromCGImage(CGImage cgImage)
+ where TPixel : unmanaged, IPixel
+ {
+ int width = (int)cgImage.Width;
+ int height = (int)cgImage.Height;
+
+ var format = new vImage_CGImageFormat
+ {
+ BitsPerComponent = 8,
+ BitsPerPixel = 32,
+ ColorSpace = CGColorSpace.CreateDeviceRGB(),
+ // notably, macOS & iOS generally use premultiplied alpha when rendering image to pixels via CGBitmapContext or otherwise,
+ // but vImage offers rendering as straight alpha by specifying Last instead of PremultipliedLast.
+ BitmapInfo = (CGBitmapInfo)CGImageAlphaInfo.Last,
+ Decode = null,
+ RenderingIntent = CGColorRenderingIntent.Default,
+ };
+
+ vImage_Buffer accImage = default;
+
+ // perform initial call to retrieve preferred alignment and bytes-per-row values for the given image dimensions.
+ nuint alignment = (nuint)vImage.Init(&accImage, (uint)height, (uint)width, 32, vImage_Flags.NoAllocate);
+ Debug.Assert(alignment > 0);
+
+ // allocate aligned memory region to contain image pixel data.
+ nuint bytesPerRow = accImage.BytesPerRow;
+ nuint bytesCount = bytesPerRow * accImage.Height;
+ accImage.Data = (byte*)NativeMemory.AlignedAlloc(bytesCount, alignment);
+
+ var result = vImage.InitWithCGImage(&accImage, &format, null, cgImage.Handle, vImage_Flags.NoAllocate);
+ Debug.Assert(result == vImage_Error.NoError);
+
+ var image = new Image(width, height);
+
+ for (int i = 0; i < height; i++)
+ {
+ var imageRow = image.DangerousGetPixelRowMemory(i);
+ var dataRow = new ReadOnlySpan(&accImage.Data[(int)bytesPerRow * i], width);
+ dataRow.CopyTo(imageRow.Span);
+ }
+
+ NativeMemory.AlignedFree(accImage.Data);
+ return image;
+ }
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Accelerate/vImage.cs b/osu.Framework/Platform/Apple/Native/Accelerate/vImage.cs
new file mode 100644
index 000000000..60f677221
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/Accelerate/vImage.cs
@@ -0,0 +1,19 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable InconsistentNaming
+
+namespace osu.Framework.Platform.Apple.Native.Accelerate
+{
+ internal static unsafe partial class vImage
+ {
+ [LibraryImport(Interop.LIB_ACCELERATE, EntryPoint = "vImageBuffer_Init")]
+ internal static partial vImage_Error Init(vImage_Buffer* buf, uint height, uint width, uint pixelBits, vImage_Flags flags);
+
+ [LibraryImport(Interop.LIB_ACCELERATE, EntryPoint = "vImageBuffer_InitWithCGImage")]
+ internal static partial vImage_Error InitWithCGImage(vImage_Buffer* buf, vImage_CGImageFormat* format, double* backgroundColour, IntPtr image, vImage_Flags flags);
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Buffer.cs b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Buffer.cs
new file mode 100644
index 000000000..100cec0b0
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Buffer.cs
@@ -0,0 +1,18 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// ReSharper disable InconsistentNaming
+
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native.Accelerate
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct vImage_Buffer
+ {
+ public byte* Data;
+ public nuint Height;
+ public nuint Width;
+ public nuint BytesPerRow;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Accelerate/vImage_CGImageFormat.cs b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_CGImageFormat.cs
new file mode 100644
index 000000000..87800f3f6
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_CGImageFormat.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// ReSharper disable InconsistentNaming
+
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native.Accelerate
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct vImage_CGImageFormat
+ {
+ public uint BitsPerComponent;
+ public uint BitsPerPixel;
+ public CGColorSpace ColorSpace;
+ public CGBitmapInfo BitmapInfo;
+ public uint Version;
+ public double* Decode;
+ public CGColorRenderingIntent RenderingIntent;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Error.cs b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Error.cs
new file mode 100644
index 000000000..03366b12b
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Error.cs
@@ -0,0 +1,27 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// ReSharper disable InconsistentNaming
+
+namespace osu.Framework.Platform.Apple.Native.Accelerate
+{
+ internal enum vImage_Error : long
+ {
+ OutOfPlaceOperationRequired = -21780,
+ ColorSyncIsAbsent = -21779,
+ InvalidImageFormat = -21778,
+ InvalidRowBytes = -21777,
+ InternalError = -21776,
+ UnknownFlagsBit = -21775,
+ BufferSizeMismatch = -21774,
+ InvalidParameter = -21773,
+ NullPointerArgument = -21772,
+ MemoryAllocationError = -21771,
+ InvalidOffsetY = -21770,
+ InvalidOffsetX = -21769,
+ InvalidEdgeStyle = -21768,
+ InvalidKernelSize = -21767,
+ RoiLargerThanInputBuffer = -21766,
+ NoError = 0,
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Flags.cs b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Flags.cs
new file mode 100644
index 000000000..8975dd8e1
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/Accelerate/vImage_Flags.cs
@@ -0,0 +1,22 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+// ReSharper disable InconsistentNaming
+
+namespace osu.Framework.Platform.Apple.Native.Accelerate
+{
+ internal enum vImage_Flags : uint
+ {
+ NoFlags = 0,
+ LeaveAlphaUnchanged = 1,
+ CopyInPlace = 2,
+ BackgroundColorFill = 4,
+ EdgeExtend = 8,
+ DoNotTile = 16,
+ HighQualityResampling = 32,
+ TruncateKernel = 64,
+ GetTempBufferSize = 128,
+ PrintDiagnosticsToConsole = 256,
+ NoAllocate = 512,
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGBitmapContext.cs b/osu.Framework/Platform/Apple/Native/CGBitmapContext.cs
new file mode 100644
index 000000000..909761731
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGBitmapContext.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ internal readonly partial struct CGBitmapContext
+ {
+ internal IntPtr Handle { get; }
+
+ internal CGBitmapContext(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGBitmapContextCreate")]
+ public static partial CGBitmapContext Create(IntPtr data, nuint width, nuint height, nuint bitsPerComponent, nuint bytesPerRow, CGColorSpace colorSpace, CGBitmapInfo bitmapInfo);
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGContextDrawImage")]
+ public static partial void DrawImage(CGBitmapContext context, CGRect rect, CGImage image);
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGBitmapInfo.cs b/osu.Framework/Platform/Apple/Native/CGBitmapInfo.cs
new file mode 100644
index 000000000..27601ef7e
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGBitmapInfo.cs
@@ -0,0 +1,26 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ public enum CGBitmapInfo : uint
+ {
+ None,
+ PremultipliedLast,
+ PremultipliedFirst,
+ Last,
+ First,
+ NoneSkipLast,
+ NoneSkipFirst,
+ Only,
+ AlphaInfoMask = 31,
+ FloatInfoMask = 3840,
+ FloatComponents = 256,
+ ByteOrderMask = 28672,
+ ByteOrderDefault = 0,
+ ByteOrder16Little = 4096,
+ ByteOrder32Little = 8192,
+ ByteOrder16Big = 12288,
+ ByteOrder32Big = 16384,
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGColorRenderingIntent.cs b/osu.Framework/Platform/Apple/Native/CGColorRenderingIntent.cs
new file mode 100644
index 000000000..c9f8d8f62
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGColorRenderingIntent.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ public enum CGColorRenderingIntent
+ {
+ Default,
+ AbsoluteColorimetric,
+ RelativeColorimetric,
+ Perceptual,
+ Saturation,
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGColorSpace.cs b/osu.Framework/Platform/Apple/Native/CGColorSpace.cs
new file mode 100644
index 000000000..550ff1246
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGColorSpace.cs
@@ -0,0 +1,21 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ public readonly partial struct CGColorSpace
+ {
+ internal IntPtr Handle { get; }
+
+ internal CGColorSpace(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGColorSpaceCreateDeviceRGB")]
+ internal static partial CGColorSpace CreateDeviceRGB();
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGImage.cs b/osu.Framework/Platform/Apple/Native/CGImage.cs
new file mode 100644
index 000000000..e0780e4db
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGImage.cs
@@ -0,0 +1,33 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ internal readonly partial struct CGImage
+ {
+ internal IntPtr Handle { get; }
+
+ public CGImage(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ internal nuint Width => GetWidth(this);
+ internal nuint Height => GetHeight(this);
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGImageGetWidth")]
+ internal static partial nuint GetWidth(CGImage image);
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGImageGetHeight")]
+ internal static partial nuint GetHeight(CGImage image);
+
+ [LibraryImport(Interop.LIB_CORE_GRAPHICS, EntryPoint = "CGImageRelease")]
+ internal static partial void Release(CGImage image);
+
+ [LibraryImport(Interop.LIB_CORE_FOUNDATION, EntryPoint = "CFGetRetainCount")]
+ internal static partial int GetRetainCount(CGImage image);
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGImageAlphaInfo.cs b/osu.Framework/Platform/Apple/Native/CGImageAlphaInfo.cs
new file mode 100644
index 000000000..ec8f6beba
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGImageAlphaInfo.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ internal enum CGImageAlphaInfo : uint
+ {
+ None,
+ PremultipliedLast,
+ PremultipliedFirst,
+ Last,
+ First,
+ NoneSkipLast,
+ NoneSkipFirst,
+ Only,
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGPoint.cs b/osu.Framework/Platform/Apple/Native/CGPoint.cs
new file mode 100644
index 000000000..ded075d71
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGPoint.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CGPoint
+ {
+ internal double X;
+ internal double Y;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGRect.cs b/osu.Framework/Platform/Apple/Native/CGRect.cs
new file mode 100644
index 000000000..310621c7a
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGRect.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CGRect
+ {
+ internal CGPoint Origin;
+ internal CGSize Size;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/CGSize.cs b/osu.Framework/Platform/Apple/Native/CGSize.cs
new file mode 100644
index 000000000..f6e89d7ab
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/CGSize.cs
@@ -0,0 +1,14 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Runtime.InteropServices;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct CGSize
+ {
+ internal double Width;
+ internal double Height;
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/Class.cs b/osu.Framework/Platform/Apple/Native/Class.cs
index dda3c7669..62cae2959 100644
--- a/osu.Framework/Platform/Apple/Native/Class.cs
+++ b/osu.Framework/Platform/Apple/Native/Class.cs
@@ -3,7 +3,6 @@
using System;
using System.Runtime.InteropServices;
-using osu.Framework.Platform.MacOS.Native;
namespace osu.Framework.Platform.Apple.Native
{
diff --git a/osu.Framework/Platform/Apple/Native/Interop.cs b/osu.Framework/Platform/Apple/Native/Interop.cs
index c97725dc9..da4d464bc 100644
--- a/osu.Framework/Platform/Apple/Native/Interop.cs
+++ b/osu.Framework/Platform/Apple/Native/Interop.cs
@@ -13,6 +13,7 @@ namespace osu.Framework.Platform.Apple.Native
internal const string LIB_OBJ_C = "/usr/lib/libobjc.dylib";
internal const string LIB_CORE_GRAPHICS = "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics";
internal const string LIB_ACCELERATE = "/System/Library/Frameworks/Accelerate.framework/Accelerate";
+ internal const string LIB_CORE_FOUNDATION = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
internal const int RTLD_NOW = 2;
diff --git a/osu.Framework/Platform/Apple/Native/NSArray.cs b/osu.Framework/Platform/Apple/Native/NSArray.cs
index 998734096..3b3f0af26 100644
--- a/osu.Framework/Platform/Apple/Native/NSArray.cs
+++ b/osu.Framework/Platform/Apple/Native/NSArray.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Platform.MacOS.Native;
namespace osu.Framework.Platform.Apple.Native
{
diff --git a/osu.Framework/Platform/Apple/Native/NSAutoreleasePool.cs b/osu.Framework/Platform/Apple/Native/NSAutoreleasePool.cs
new file mode 100644
index 000000000..736d0db24
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/NSAutoreleasePool.cs
@@ -0,0 +1,37 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ internal readonly struct NSAutoreleasePool : IDisposable
+ {
+ internal IntPtr Handle { get; }
+
+ internal NSAutoreleasePool(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ private static readonly IntPtr class_pointer = Class.Get("NSAutoreleasePool");
+ private static readonly IntPtr sel_alloc = Selector.Get("alloc");
+ private static readonly IntPtr sel_init = Selector.Get("init");
+ private static readonly IntPtr sel_drain = Selector.Get("drain");
+
+ public static NSAutoreleasePool Init()
+ {
+ var pool = alloc();
+ Interop.SendIntPtr(pool.Handle, sel_init);
+ return pool;
+ }
+
+ private static NSAutoreleasePool alloc() => new NSAutoreleasePool(Interop.SendIntPtr(class_pointer, sel_alloc));
+
+ public void Dispose()
+ {
+ if (Handle != IntPtr.Zero)
+ Interop.SendIntPtr(Handle, sel_drain);
+ }
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/NSData.cs b/osu.Framework/Platform/Apple/Native/NSData.cs
index 808c29f6d..dc7c18d07 100644
--- a/osu.Framework/Platform/Apple/Native/NSData.cs
+++ b/osu.Framework/Platform/Apple/Native/NSData.cs
@@ -3,16 +3,14 @@
using System;
using System.Runtime.InteropServices;
-using osu.Framework.Platform.MacOS.Native;
namespace osu.Framework.Platform.Apple.Native
{
- internal readonly struct NSData : IDisposable
+ internal readonly struct NSData
{
internal IntPtr Handle { get; }
private static readonly IntPtr class_pointer = Class.Get("NSData");
- private static readonly IntPtr sel_release = Selector.Get("release");
private static readonly IntPtr sel_data_with_bytes = Selector.Get("dataWithBytes:length:");
private static readonly IntPtr sel_bytes = Selector.Get("bytes");
private static readonly IntPtr sel_length = Selector.Get("length");
@@ -22,6 +20,8 @@ namespace osu.Framework.Platform.Apple.Native
Handle = handle;
}
+ public static implicit operator NSData(NSMutableData data) => new NSData(data.Handle);
+
internal byte[] ToBytes()
{
IntPtr pointer = Interop.SendIntPtr(Handle, sel_bytes);
@@ -32,14 +32,6 @@ namespace osu.Framework.Platform.Apple.Native
return bytes;
}
- internal void Release() => Interop.SendVoid(Handle, sel_release);
-
- public void Dispose()
- {
- if (Handle != IntPtr.Zero)
- Release();
- }
-
internal static unsafe NSData FromBytes(ReadOnlySpan bytes)
{
fixed (byte* ptr = bytes)
diff --git a/osu.Framework/Platform/Apple/Native/NSMutableData.cs b/osu.Framework/Platform/Apple/Native/NSMutableData.cs
new file mode 100644
index 000000000..dd1eed658
--- /dev/null
+++ b/osu.Framework/Platform/Apple/Native/NSMutableData.cs
@@ -0,0 +1,29 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+
+namespace osu.Framework.Platform.Apple.Native
+{
+ internal readonly struct NSMutableData
+ {
+ internal IntPtr Handle { get; }
+
+ private static readonly IntPtr class_pointer = Class.Get("NSMutableData");
+ private static readonly IntPtr sel_data_with_length = Selector.Get("dataWithLength:");
+ private static readonly IntPtr sel_mutable_bytes = Selector.Get("mutableBytes");
+
+ internal NSMutableData(IntPtr handle)
+ {
+ Handle = handle;
+ }
+
+ internal unsafe byte* MutableBytes => (byte*)Interop.SendIntPtr(Handle, sel_mutable_bytes);
+
+ internal static NSMutableData FromLength(int length)
+ {
+ IntPtr handle = Interop.SendIntPtr(class_pointer, sel_data_with_length, length);
+ return new NSMutableData(handle);
+ }
+ }
+}
diff --git a/osu.Framework/Platform/Apple/Native/NSNotificationCenter.cs b/osu.Framework/Platform/Apple/Native/NSNotificationCenter.cs
index 3459e8015..6d8ff30bc 100644
--- a/osu.Framework/Platform/Apple/Native/NSNotificationCenter.cs
+++ b/osu.Framework/Platform/Apple/Native/NSNotificationCenter.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using osu.Framework.Platform.MacOS.Native;
namespace osu.Framework.Platform.Apple.Native
{
diff --git a/osu.Framework/Platform/Apple/Native/NSString.cs b/osu.Framework/Platform/Apple/Native/NSString.cs
index e1c3ce459..902ba764d 100644
--- a/osu.Framework/Platform/Apple/Native/NSString.cs
+++ b/osu.Framework/Platform/Apple/Native/NSString.cs
@@ -3,7 +3,6 @@
using System;
using System.Runtime.InteropServices;
-using osu.Framework.Platform.MacOS.Native;
namespace osu.Framework.Platform.Apple.Native
{
diff --git a/osu.Framework/Platform/MacOS/Native/Selector.cs b/osu.Framework/Platform/Apple/Native/Selector.cs
similarity index 83%
rename from osu.Framework/Platform/MacOS/Native/Selector.cs
rename to osu.Framework/Platform/Apple/Native/Selector.cs
index e54a69bd2..3d5805c1f 100644
--- a/osu.Framework/Platform/MacOS/Native/Selector.cs
+++ b/osu.Framework/Platform/Apple/Native/Selector.cs
@@ -3,9 +3,8 @@
using System;
using System.Runtime.InteropServices;
-using osu.Framework.Platform.Apple.Native;
-namespace osu.Framework.Platform.MacOS.Native
+namespace osu.Framework.Platform.Apple.Native
{
internal static partial class Selector
{
diff --git a/osu.Framework/Platform/GameHost.cs b/osu.Framework/Platform/GameHost.cs
index 85b83b988..3d159dc63 100644
--- a/osu.Framework/Platform/GameHost.cs
+++ b/osu.Framework/Platform/GameHost.cs
@@ -741,6 +741,7 @@ namespace osu.Framework.Platform
CacheStorage = GetDefaultGameStorage().GetStorageForDirectory("cache");
SetupForRun();
+ game.SetupLogging(Storage, CacheStorage);
populateInputHandlers();
diff --git a/osu.Framework/Platform/ISDLWindow.cs b/osu.Framework/Platform/ISDLWindow.cs
index 410fe82dc..fb9339a00 100644
--- a/osu.Framework/Platform/ISDLWindow.cs
+++ b/osu.Framework/Platform/ISDLWindow.cs
@@ -36,7 +36,7 @@ namespace osu.Framework.Platform
void UpdateMousePosition(Vector2 position);
- void StartTextInput(bool allowIme);
+ void StartTextInput(TextInputProperties properties);
void StopTextInput();
void SetTextInputRect(RectangleF rectangle);
void ResetIme();
diff --git a/osu.Framework/Platform/MacOS/MacOSClipboard.cs b/osu.Framework/Platform/MacOS/MacOSClipboard.cs
index 4d6e29249..86ecbe2ee 100644
--- a/osu.Framework/Platform/MacOS/MacOSClipboard.cs
+++ b/osu.Framework/Platform/MacOS/MacOSClipboard.cs
@@ -35,9 +35,12 @@ namespace osu.Framework.Platform.MacOS
using var stream = new MemoryStream();
image.SaveAsTiff(stream);
- using var nsData = NSData.FromBytes(stream.ToArray());
- using var nsImage = NSImage.LoadFromData(nsData);
- return setToPasteboard(nsImage.Handle);
+ using (NSAutoreleasePool.Init())
+ {
+ var nsData = NSData.FromBytes(stream.ToArray());
+ using var nsImage = NSImage.LoadFromData(nsData);
+ return setToPasteboard(nsImage.Handle);
+ }
}
private IntPtr getFromPasteboard(IntPtr @class)
diff --git a/osu.Framework/Platform/MacOS/MacOSGameHost.cs b/osu.Framework/Platform/MacOS/MacOSGameHost.cs
index 67100d46b..234976426 100644
--- a/osu.Framework/Platform/MacOS/MacOSGameHost.cs
+++ b/osu.Framework/Platform/MacOS/MacOSGameHost.cs
@@ -7,10 +7,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.Handlers.Mouse;
+using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osu.Framework.Platform.MacOS.Native;
@@ -44,6 +46,9 @@ namespace osu.Framework.Platform.MacOS
protected override ReadableKeyCombinationProvider CreateReadableKeyCombinationProvider() => new MacOSReadableKeyCombinationProvider();
+ public override IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore)
+ => new MacOSTextureLoaderStore(underlyingStore);
+
protected override void Swap()
{
base.Swap();
diff --git a/osu.Framework/Platform/MacOS/MacOSTextureLoaderStore.cs b/osu.Framework/Platform/MacOS/MacOSTextureLoaderStore.cs
new file mode 100644
index 000000000..3e9a38a46
--- /dev/null
+++ b/osu.Framework/Platform/MacOS/MacOSTextureLoaderStore.cs
@@ -0,0 +1,40 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.IO;
+using osu.Framework.IO.Stores;
+using osu.Framework.Platform.Apple;
+using osu.Framework.Platform.Apple.Native;
+using osu.Framework.Platform.MacOS.Native;
+using SixLabors.ImageSharp;
+
+namespace osu.Framework.Platform.MacOS
+{
+ internal class MacOSTextureLoaderStore : AppleTextureLoaderStore
+ {
+ public MacOSTextureLoaderStore(IResourceStore store)
+ : base(store)
+ {
+ }
+
+ protected override unsafe Image ImageFromStream(Stream stream)
+ {
+ using (NSAutoreleasePool.Init())
+ {
+ int length = (int)(stream.Length - stream.Position);
+ var nativeData = NSMutableData.FromLength(length);
+
+ var bytesSpan = new Span(nativeData.MutableBytes, length);
+ stream.ReadExactly(bytesSpan);
+
+ using var nsImage = NSImage.LoadFromData(nativeData);
+ if (nsImage.Handle == IntPtr.Zero)
+ throw new ArgumentException($"{nameof(Image)} could not be created from {nameof(stream)}.");
+
+ var cgImage = nsImage.CGImage;
+ return ImageFromCGImage(cgImage);
+ }
+ }
+ }
+}
diff --git a/osu.Framework/Platform/MacOS/Native/NSImage.cs b/osu.Framework/Platform/MacOS/Native/NSImage.cs
index 6ee354f6b..1a87d09f1 100644
--- a/osu.Framework/Platform/MacOS/Native/NSImage.cs
+++ b/osu.Framework/Platform/MacOS/Native/NSImage.cs
@@ -21,6 +21,9 @@ namespace osu.Framework.Platform.MacOS.Native
private static readonly IntPtr sel_release = Selector.Get("release");
private static readonly IntPtr sel_init_with_data = Selector.Get("initWithData:");
private static readonly IntPtr sel_tiff_representation = Selector.Get("TIFFRepresentation");
+ private static readonly IntPtr sel_cg_image_for_proposed_rect = Selector.Get("CGImageForProposedRect:context:hints:");
+
+ internal CGImage CGImage => new CGImage(Interop.SendIntPtr(Handle, sel_cg_image_for_proposed_rect, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero));
internal NSData TiffRepresentation => new NSData(Interop.SendIntPtr(Handle, sel_tiff_representation));
diff --git a/osu.Framework/Platform/MacOS/SDL2MacOSWindow.cs b/osu.Framework/Platform/MacOS/SDL2MacOSWindow.cs
index bb5e4350d..ea645b5ec 100644
--- a/osu.Framework/Platform/MacOS/SDL2MacOSWindow.cs
+++ b/osu.Framework/Platform/MacOS/SDL2MacOSWindow.cs
@@ -5,9 +5,9 @@
using System;
using osu.Framework.Platform.Apple.Native;
-using osu.Framework.Platform.MacOS.Native;
using osu.Framework.Platform.SDL2;
using osuTK;
+using Selector = osu.Framework.Platform.Apple.Native.Selector;
namespace osu.Framework.Platform.MacOS
{
diff --git a/osu.Framework/Platform/MacOS/SDL3MacOSWindow.cs b/osu.Framework/Platform/MacOS/SDL3MacOSWindow.cs
index c9abb8ec9..92b18a428 100644
--- a/osu.Framework/Platform/MacOS/SDL3MacOSWindow.cs
+++ b/osu.Framework/Platform/MacOS/SDL3MacOSWindow.cs
@@ -5,9 +5,9 @@
using System;
using osu.Framework.Platform.Apple.Native;
-using osu.Framework.Platform.MacOS.Native;
using osu.Framework.Platform.SDL3;
using osuTK;
+using Selector = osu.Framework.Platform.Apple.Native.Selector;
namespace osu.Framework.Platform.MacOS
{
diff --git a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs
index 43a62dd8b..5c0088392 100644
--- a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs
+++ b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs
@@ -172,7 +172,7 @@ namespace osu.Framework.Platform.SDL2
}
}
- public virtual void StartTextInput(bool allowIme) => ScheduleCommand(SDL_StartTextInput);
+ public virtual void StartTextInput(TextInputProperties properties) => ScheduleCommand(SDL_StartTextInput);
public void StopTextInput() => ScheduleCommand(SDL_StopTextInput);
diff --git a/osu.Framework/Platform/SDL3/SDL3Extensions.cs b/osu.Framework/Platform/SDL3/SDL3Extensions.cs
index e2b77fc67..5a3538d94 100644
--- a/osu.Framework/Platform/SDL3/SDL3Extensions.cs
+++ b/osu.Framework/Platform/SDL3/SDL3Extensions.cs
@@ -1011,6 +1011,34 @@ namespace osu.Framework.Platform.SDL3
w = rectangle.Width,
};
+ public static SDL_TextInputType ToSDLTextInputType(this TextInputType type)
+ {
+ switch (type)
+ {
+ default:
+ case TextInputType.Text:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT;
+
+ case TextInputType.Name:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_NAME;
+
+ case TextInputType.EmailAddress:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_EMAIL;
+
+ case TextInputType.Username:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_USERNAME;
+
+ case TextInputType.Number:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_NUMBER;
+
+ case TextInputType.Password:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN;
+
+ case TextInputType.NumericalPassword:
+ return SDL_TextInputType.SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN;
+ }
+ }
+
public static unsafe DisplayMode ToDisplayMode(this SDL_DisplayMode mode, int displayIndex)
{
int bpp;
diff --git a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs
index 1e9af042b..91d782ef4 100644
--- a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs
+++ b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs
@@ -185,7 +185,22 @@ namespace osu.Framework.Platform.SDL3
}
}
- public virtual void StartTextInput(bool allowIme) => ScheduleCommand(() => SDL_StartTextInput(SDLWindowHandle));
+ private SDL_PropertiesID? currentTextInputProperties;
+
+ public virtual void StartTextInput(TextInputProperties properties) => ScheduleCommand(() =>
+ {
+ currentTextInputProperties ??= SDL_CreateProperties();
+
+ var props = currentTextInputProperties.Value;
+ SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_TYPE_NUMBER, (long)properties.Type.ToSDLTextInputType());
+
+ if (!properties.AutoCapitalisation)
+ SDL_SetNumberProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER, (long)SDL_Capitalization.SDL_CAPITALIZE_NONE);
+ else
+ SDL_ClearProperty(props, SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER);
+
+ SDL_StartTextInputWithProperties(SDLWindowHandle, props);
+ });
public void StopTextInput() => ScheduleCommand(() => SDL_StopTextInput(SDLWindowHandle));
@@ -196,7 +211,11 @@ namespace osu.Framework.Platform.SDL3
public virtual void ResetIme() => ScheduleCommand(() =>
{
SDL_StopTextInput(SDLWindowHandle);
- SDL_StartTextInput(SDLWindowHandle);
+
+ if (currentTextInputProperties is SDL_PropertiesID props)
+ SDL_StartTextInputWithProperties(SDLWindowHandle, props);
+ else
+ SDL_StartTextInput(SDLWindowHandle);
});
public void SetTextInputRect(RectangleF rect) => ScheduleCommand(() =>
diff --git a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs
index 17c6a8df5..21e644191 100644
--- a/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs
+++ b/osu.Framework/Platform/Windows/SDL2WindowsWindow.cs
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
+using osu.Framework.Input;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Platform.SDL2;
using osu.Framework.Platform.Windows.Native;
@@ -138,10 +139,10 @@ namespace osu.Framework.Platform.Windows
#region IME handling
- public override void StartTextInput(bool allowIme)
+ public override void StartTextInput(TextInputProperties properties)
{
- base.StartTextInput(allowIme);
- ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, allowIme));
+ base.StartTextInput(properties);
+ ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.Type.SupportsIme() && properties.AllowIme));
}
public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle));
diff --git a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs
index d04ea28ba..5147a787a 100644
--- a/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs
+++ b/osu.Framework/Platform/Windows/SDL3WindowsWindow.cs
@@ -5,6 +5,7 @@ using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
+using osu.Framework.Input;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Platform.SDL3;
using osu.Framework.Platform.Windows.Native;
@@ -92,10 +93,10 @@ namespace osu.Framework.Platform.Windows
}
}
- public override void StartTextInput(bool allowIme)
+ public override void StartTextInput(TextInputProperties properties)
{
- base.StartTextInput(allowIme);
- ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, allowIme));
+ base.StartTextInput(properties);
+ ScheduleCommand(() => Imm.SetImeAllowed(WindowHandle, properties.Type.SupportsIme() && properties.AllowIme));
}
public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle));
diff --git a/osu.Framework/Testing/Input/ManualTextInputSource.cs b/osu.Framework/Testing/Input/ManualTextInputSource.cs
index def56cb23..94b722da5 100644
--- a/osu.Framework/Testing/Input/ManualTextInputSource.cs
+++ b/osu.Framework/Testing/Input/ManualTextInputSource.cs
@@ -8,8 +8,8 @@ namespace osu.Framework.Testing.Input
{
public class ManualTextInputSource : TextInputSource
{
- public readonly Queue ActivationQueue = new Queue();
- public readonly Queue EnsureActivatedQueue = new Queue();
+ public readonly Queue ActivationQueue = new Queue();
+ public readonly Queue EnsureActivatedQueue = new Queue();
public readonly Queue DeactivationQueue = new Queue();
public void Text(string text) => TriggerTextInput(text);
@@ -32,16 +32,16 @@ namespace osu.Framework.Testing.Input
base.TriggerImeComposition(string.Empty, 0, 0);
}
- protected override void ActivateTextInput(bool allowIme)
+ protected override void ActivateTextInput(TextInputProperties properties)
{
- base.ActivateTextInput(allowIme);
- ActivationQueue.Enqueue(allowIme);
+ base.ActivateTextInput(properties);
+ ActivationQueue.Enqueue(properties);
}
- protected override void EnsureTextInputActivated(bool allowIme)
+ protected override void EnsureTextInputActivated(TextInputProperties properties)
{
- base.EnsureTextInputActivated(allowIme);
- EnsureActivatedQueue.Enqueue(allowIme);
+ base.EnsureTextInputActivated(properties);
+ EnsureActivatedQueue.Enqueue(properties);
}
protected override void DeactivateTextInput()
diff --git a/osu.Framework/osu.Framework.csproj b/osu.Framework/osu.Framework.csproj
index 32f6ff813..fd2172ef0 100644
--- a/osu.Framework/osu.Framework.csproj
+++ b/osu.Framework/osu.Framework.csproj
@@ -38,7 +38,7 @@
-
+