Merge branch 'master' into dev/jela/nav-view-itemsource
This commit is contained in:
Коммит
00da42300c
|
@ -580,16 +580,16 @@
|
|||
<Methods>
|
||||
<Member
|
||||
fullName="System.Void Windows.UI.Xaml.RoutedEvent..ctor(System.String name)"
|
||||
reason="Not part of the UWP API." />
|
||||
reason="Not part of the WinUI API." />
|
||||
<Member
|
||||
fullName="Windows.Foundation.Point Windows.UI.Xaml.Input.DoubleTappedRoutedEventArgs.GetPosition()"
|
||||
reason="Not part of the UWP API." />
|
||||
reason="Not part of the WinUI API." />
|
||||
<Member
|
||||
fullName="Windows.Foundation.Point Windows.UI.Xaml.Input.TappedRoutedEventArgs.GetPosition()"
|
||||
reason="Not part of the UWP API." />
|
||||
reason="Not part of the WinUI API." />
|
||||
<Member
|
||||
fullName="System.Void Windows.UI.ViewManagement.ApplicationViewTitleBar..ctor()"
|
||||
reason="Parameter-less ctor does not exist in UWP" />
|
||||
reason="Parameter-less ctor does not exist in WinUI" />
|
||||
|
||||
<Member
|
||||
fullName="CoreGraphics.CGSize Windows.UI.Xaml.Controls.VirtualizingPanelLayout.GetItemSizeForIndexPath(Foundation.NSIndexPath indexPath)"
|
||||
|
@ -597,6 +597,16 @@
|
|||
<Member
|
||||
fullName="CoreGraphics.CGSize Windows.UI.Xaml.Controls.ListViewBaseSource.GetItemSize(UIKit.UICollectionView collectionView, Foundation.NSIndexPath indexPath)"
|
||||
reason="Made internal, shouldn't normally be called by consumer code"/>
|
||||
|
||||
<Member
|
||||
fullName="System.Boolean Windows.UI.Xaml.Input.RightTappedRoutedEventArgs.get_Handled()"
|
||||
reason="Auto property"/>
|
||||
<Member
|
||||
fullName="System.Void Windows.UI.Xaml.Input.RightTappedRoutedEventArgs.set_Handled(System.Boolean value)"
|
||||
reason="Auto property"/>
|
||||
<Member
|
||||
fullName="System.Void Windows.UI.Input.RightTappedEventArgs..ctor()"
|
||||
reason="Not part of the WinUI API."/>
|
||||
</Methods>
|
||||
</IgnoreSet>
|
||||
</IgnoreSets>
|
||||
|
|
|
@ -45,13 +45,15 @@ jobs:
|
|||
msbuildLocationMethod: version
|
||||
msbuildVersion: latest
|
||||
msbuildArchitecture: x86
|
||||
msbuildArguments: /r /p:CheckExclusions=True "/p:CombinedConfiguration=$(CombinedConfiguration)" /nodeReuse:true /detailedsummary /m:16 /nr:false /bl:$(build.artifactstagingdirectory)\build.binlog
|
||||
msbuildArguments: /r /p:CheckExclusions=True "/p:CombinedConfiguration=$(CombinedConfiguration)" /nodeReuse:true /detailedsummary /m /nr:false /bl:$(build.artifactstagingdirectory)\build.binlog
|
||||
clean: false
|
||||
maximumCpuCount: true
|
||||
restoreNugetPackages: false
|
||||
logProjectEvents: false
|
||||
createLogFile: false
|
||||
|
||||
|
||||
- task: VisualStudioTestPlatformInstaller@1
|
||||
|
||||
- task: VSTest@2
|
||||
inputs:
|
||||
testAssemblyVer2: |
|
||||
|
|
|
@ -38,6 +38,7 @@ else
|
|||
namespace = 'SamplesApp.UITests.Windows_UI_Xaml_Media.Animation_Tests' or \
|
||||
namespace = 'SamplesApp.UITests.Windows_UI_Xaml_Controls.ControlTests' or \
|
||||
namespace = 'SamplesApp.UITests.Windows_UI_Xaml_Controls.TextBlockTests' or \
|
||||
namespace = 'SamplesApp.UITests.Microsoft_UI_Xaml_Controls.NumberBoxTests' or \
|
||||
namespace = 'SamplesApp.UITests.Windows_UI_Xaml_Controls.ImageTests'
|
||||
"
|
||||
fi
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
- Add support for `Popup.LightDismissOverlayMode`, as well as `DatePicker.LightDismissOverlayMode` and `Flyout.LightDismissOverlayMode`
|
||||
- `TransformToVisual` now returns a real transform to convert coordinates between views (was only returning a translate transform to offset the origin of controls)
|
||||
- Multiple pointers at same time on screen (a.k.a. Multi-touch) are now supported
|
||||
- Add support for WinUI 2.3 [`NumberBox`](https://docs.microsoft.com/en-us/uwp/api/microsoft.ui.xaml.controls.numberbox?view=winui-2.3)
|
||||
- Add support of the `UIElement.RightTapped` event
|
||||
|
||||
### Breaking changes
|
||||
-
|
||||
|
@ -34,6 +36,7 @@
|
|||
- [Android] Fix Image margin calculation on fixed size
|
||||
- [Android] Native views weren't clipped correctly
|
||||
- [iOS] #2361 ListView would measure children with infinite width
|
||||
- [iOS] Fix crash when using ComboBox template with native Picker and changing ItemsSource to null after SelectedItem was set
|
||||
- [#2398] Fully qualify the `MethodName` value for `CreateFromStringAttribute' if it's not fully qualified it the code
|
||||
- [WASM] Fix bug where changing a property could remove the required clipping on a view
|
||||
- [Android] Fix unconstrained Image loading issue when contained in a ContentControl template
|
||||
|
|
|
@ -239,6 +239,9 @@ As those events are tightly coupled to the native events, Uno has to make some c
|
|||
* On Firefox, pressed pointers are reported as fingers. This means you will receive events with `PointerDeviceType == Pen` only for hovering
|
||||
(i.e. `Pointer<Enter|Move|Exit>` - note that, as of 2019-11-28, once pressed `PointerMove` will be flagged as "touch")
|
||||
and you won't be able to track the barrel button nor the eraser. (cf. https://bugzilla.mozilla.org/show_bug.cgi?id=1449660)
|
||||
* On WASM, if you touch the screen with the pen **then** you press the barrel button (still while touching the screen), the pointer events will
|
||||
have the `IsRightButtonPressed` set (in addition of the `IsBarrelButtonPressed`). On WinUI and Android you get this flag only if the barrel
|
||||
button was pressed at the moment where you touched the screen, otherwise you will have the `IsLeftButtonPressed` and the `IsBarrelButtonPressed`.
|
||||
|
||||
### Pointer capture
|
||||
|
||||
|
|
|
@ -154,6 +154,9 @@
|
|||
- ViewBox
|
||||
- PersonPicture
|
||||
|
||||
## WinUI Specific Controls (Pre 3.0)
|
||||
- [NumberBox](https://docs.microsoft.com/en-us/uwp/api/microsoft.ui.xaml.controls.numberbox?view=winui-2.3)
|
||||
|
||||
### Non-UI features
|
||||
|
||||
- Windows.UI.Storage (StorageFile, StorageFolder, Settings)
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using SamplesApp.UITests.TestFramework;
|
||||
using Uno.UITest.Helpers;
|
||||
using Uno.UITest.Helpers.Queries;
|
||||
|
||||
namespace SamplesApp.UITests.Microsoft_UI_Xaml_Controls.NumberBoxTests
|
||||
{
|
||||
public class NumberBoxTests : SampleControlUITestBase
|
||||
{
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void UpDownTest()
|
||||
{
|
||||
Run("UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests.MUX_Test");
|
||||
|
||||
var numBox = _app.Marked("TestNumberBox");
|
||||
Assert.AreEqual(0, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
numBox.SetDependencyPropertyValue("SpinButtonPlacementMode", "Inline");
|
||||
|
||||
var upButton = numBox.Descendant().Marked("UpSpinButton");
|
||||
var downButton = numBox.Descendant().Marked("DownSpinButton");
|
||||
|
||||
Console.WriteLine("Assert that up button increases value by 1");
|
||||
upButton.Tap();
|
||||
Assert.AreEqual(1, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.WriteLine("Assert that down button decreases value by 1");
|
||||
downButton.Tap();
|
||||
Assert.AreEqual(0, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.WriteLine("Change SmallChange value to 5");
|
||||
var smallChangeNumBox = _app.Marked("SmallChangeNumberBox");
|
||||
smallChangeNumBox.SetDependencyPropertyValue("Text", "5");
|
||||
|
||||
Console.WriteLine("Assert that up button increases value by 5");
|
||||
upButton.Tap();
|
||||
Assert.AreEqual(5, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
_app.Tap("MinCheckBox");
|
||||
_app.Tap("MaxCheckBox");
|
||||
|
||||
numBox.SetDependencyPropertyValue("Value", "100");
|
||||
_app.Tap("WrapCheckBox");
|
||||
|
||||
Console.WriteLine("Assert that when wrapping is on, and value is at max, clicking the up button wraps to the min value.");
|
||||
upButton.Tap();
|
||||
Assert.AreEqual(0, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.WriteLine("Assert that when wrapping is on, clicking the down button wraps to the max value.");
|
||||
downButton.Tap();
|
||||
Assert.AreEqual(100, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.WriteLine("Assert that incrementing after typing in a value validates the text first.");
|
||||
numBox.ClearText();
|
||||
numBox.EnterText("50");
|
||||
_app.PressEnter();
|
||||
upButton.Tap();
|
||||
Assert.AreEqual(55, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void MinMaxTest()
|
||||
{
|
||||
Run("UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests.MUX_Test");
|
||||
|
||||
_app.Tap("MinCheckBox");
|
||||
_app.Tap("MaxCheckBox");
|
||||
|
||||
var numBox = _app.Marked("TestNumberBox");
|
||||
Assert.AreEqual(0, numBox.GetDependencyPropertyValue<double>("Minimum"));
|
||||
Assert.AreEqual(100, numBox.GetDependencyPropertyValue<double>("Maximum"));
|
||||
|
||||
numBox.SetDependencyPropertyValue("Value", "10");
|
||||
|
||||
Console.Write("Assert that setting the value to -1 changes the value to 0");
|
||||
numBox.SetDependencyPropertyValue("Value", "-1");
|
||||
Assert.AreEqual(0, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.Write("Assert that typing '123' in the NumberBox changes the value to 100");
|
||||
numBox.ClearText();
|
||||
numBox.EnterText("123");
|
||||
_app.PressEnter();
|
||||
|
||||
Assert.AreEqual(100, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.Write("Changing Max to 90; Assert value also changes to 90");
|
||||
var maxBox = _app.Marked("MaxNumberBox");
|
||||
maxBox.SetDependencyPropertyValue("Value", "90");
|
||||
Assert.AreEqual(90, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.Write("Assert that setting the minimum above the maximum changes the maximum");
|
||||
var minBox = _app.Marked("MinNumberBox");
|
||||
minBox.SetDependencyPropertyValue("Value", "200");
|
||||
Assert.AreEqual(200, numBox.GetDependencyPropertyValue<double>("Minimum"));
|
||||
Assert.AreEqual(200, numBox.GetDependencyPropertyValue<double>("Maximum"));
|
||||
Assert.AreEqual(200, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
|
||||
Console.Write("Assert that setting the maximum below the minimum changes the minimum");
|
||||
maxBox.SetDependencyPropertyValue("Value", "150");
|
||||
Assert.AreEqual(150, numBox.GetDependencyPropertyValue<double>("Minimum"));
|
||||
Assert.AreEqual(150, numBox.GetDependencyPropertyValue<double>("Maximum"));
|
||||
Assert.AreEqual(150, numBox.GetDependencyPropertyValue<double>("Value"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void UpDownEnabledTest()
|
||||
{
|
||||
Run("UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests.MUX_Test");
|
||||
|
||||
var numBox = _app.Marked("TestNumberBox");
|
||||
|
||||
numBox.SetDependencyPropertyValue("SpinButtonPlacementMode", "Inline");
|
||||
|
||||
var upButton = numBox.Descendant().Marked("UpSpinButton");
|
||||
var downButton = numBox.Descendant().Marked("DownSpinButton");
|
||||
|
||||
_app.Tap("MinCheckBox");
|
||||
_app.Tap("MaxCheckBox");
|
||||
|
||||
Console.WriteLine("Assert that when Value is at Minimum, the down spin button is disabled.");
|
||||
Assert.IsTrue(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsFalse(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
|
||||
Console.WriteLine("Assert that when Value is at Maximum, the up spin button is disabled.");
|
||||
numBox.SetDependencyPropertyValue("Value", "100");
|
||||
|
||||
Assert.IsFalse(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsTrue(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
|
||||
Console.WriteLine("Assert that when wrapping is enabled, spin buttons are enabled.");
|
||||
_app.Tap("WrapCheckBox");
|
||||
Assert.IsTrue(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsTrue(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
_app.Tap("WrapCheckBox");
|
||||
|
||||
Console.WriteLine("Assert that when Maximum is updated the up button is updated also.");
|
||||
var maxBox = _app.Marked("MaxNumberBox");
|
||||
maxBox.SetDependencyPropertyValue("Value", "200");
|
||||
|
||||
Assert.IsTrue(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsTrue(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
|
||||
Console.WriteLine("Assert that spin buttons are disabled if value is NaN.");
|
||||
numBox.SetDependencyPropertyValue("Value", "NaN");
|
||||
|
||||
Assert.IsFalse(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsFalse(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
|
||||
numBox.SetDependencyPropertyValue("ValidationMode", "Disabled");
|
||||
|
||||
Console.WriteLine("Assert that when validation is off, spin buttons are enabled");
|
||||
numBox.SetDependencyPropertyValue("Value", "0");
|
||||
|
||||
Assert.IsTrue(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsTrue(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
|
||||
Console.WriteLine("...except in the NaN case");
|
||||
numBox.SetDependencyPropertyValue("Value", "NaN");
|
||||
|
||||
Assert.IsFalse(upButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
Assert.IsFalse(downButton.GetDependencyPropertyValue<bool>("IsEnabled"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void BasicExpressionTest()
|
||||
{
|
||||
Run("UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests.MUX_Test");
|
||||
|
||||
var numBox = _app.Marked("TestNumberBox");
|
||||
|
||||
_app.EnterText(numBox, "5 + 3");
|
||||
Assert.AreEqual("0", numBox.GetText());
|
||||
|
||||
_app.Tap("ExpressionCheckBox");
|
||||
|
||||
int numErrors = 0;
|
||||
const double resetValue = double.NaN;
|
||||
|
||||
Dictionary<string, double> expressions = new Dictionary<string, double>
|
||||
{
|
||||
// Valid expressions. None of these should evaluate to the reset value.
|
||||
{ "5", 5 },
|
||||
{ "-358", -358 },
|
||||
{ "12.34", 12.34 },
|
||||
{ "5 + 3", 8 },
|
||||
{ "12345 + 67 + 890", 13302 },
|
||||
{ "000 + 0011", 11 },
|
||||
{ "5 - 3 + 2", 4 },
|
||||
{ "3 + 2 - 5", 0 },
|
||||
{ "9 - 2 * 6 / 4", 6 },
|
||||
{ "9 - -7", 16 },
|
||||
{ "9-3*2", 3 }, // no spaces
|
||||
{ " 10 * 6 ", 60 }, // extra spaces
|
||||
{ "10 /( 2 + 3 )", 2 },
|
||||
{ "5 * -40", -200 },
|
||||
{ "(1 - 4) / (2 + 1)", -1 },
|
||||
{ "3 * ((4 + 8) / 2)", 18 },
|
||||
{ "23 * ((0 - 48) / 8)", -138 },
|
||||
{ "((74-71)*2)^3", 216 },
|
||||
{ "2 - 2 ^ 3", -6 },
|
||||
{ "2 ^ 2 ^ 2 / 2 + 9", 17 },
|
||||
{ "5 ^ -2", 0.04 },
|
||||
{ "5.09 + 14.333", 19.423 },
|
||||
{ "2.5 * 0.35", 0.875 },
|
||||
{ "-2 - 5", -7 }, // begins with negative number
|
||||
{ "(10)", 10 }, // number in parens
|
||||
{ "(-9)", -9 }, // negative number in parens
|
||||
{ "0^0", 1 }, // who knew?
|
||||
|
||||
// These should not parse, which means they will reset back to the previous value.
|
||||
{ "5x + 3y", resetValue }, // invalid chars
|
||||
{ "5 + (3", resetValue }, // mismatched parens
|
||||
{ "9 + (2 + 3))", resetValue },
|
||||
{ "(2 + 3)(1 + 5)", resetValue }, // missing operator
|
||||
{ "9 + + 7", resetValue }, // extra operators
|
||||
{ "9 - * 7", resetValue },
|
||||
{ "9 - - 7", resetValue },
|
||||
{ "+9", resetValue },
|
||||
{ "1 / 0", resetValue }, // divide by zero
|
||||
|
||||
// These don't currently work, but maybe should.
|
||||
{ "-(3 + 5)", resetValue }, // negative sign in front of parens -- should be -8
|
||||
};
|
||||
|
||||
foreach (KeyValuePair<string, double> pair in expressions)
|
||||
{
|
||||
numBox.ClearText();
|
||||
numBox.EnterText(pair.Key);
|
||||
_app.PressEnter();
|
||||
|
||||
var value = numBox.GetDependencyPropertyValue<double>("Value");
|
||||
string output = "Expression '" + pair.Key + "' - expected: " + pair.Value + ", actual: " + value;
|
||||
if (Math.Abs(pair.Value - value) > 0.00001)
|
||||
{
|
||||
numErrors++;
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(0, numErrors);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using SamplesApp.UITests.TestFramework;
|
||||
using Uno.UITest.Helpers;
|
||||
using Uno.UITest.Helpers.Queries;
|
||||
using Uno.UITests.Helpers;
|
||||
|
||||
namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
||||
{
|
||||
[Ignore("DoubleTapCoordinates is not implemented yet https://github.com/unoplatform/Uno.UITest/issues/29")]
|
||||
public class DoubleTapped_Tests : SampleControlUITestBase
|
||||
{
|
||||
private const string _xamlTestPage = "UITests.Shared.Windows_UI_Input.GestureRecognizerTests.DoubleTappedTests";
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_Basic()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "Basic_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Double tap the target
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DoubleTapCoordinates(target.X + tapX, target.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastDoubleTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Browser, Platform.iOS)] // Disabled on Android: The test engine is not able to find "Transformed_Target"
|
||||
public void When_Transformed()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string parentName = "Transformed_Parent";
|
||||
const string targetName = "Transformed_Target";
|
||||
|
||||
var parent = _app.WaitForElement(parentName).Single().Rect;
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
|
||||
// Double tap the target
|
||||
_app.DoubleTapCoordinates(parent.Right - target.Width, parent.Bottom - 3);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastDoubleTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_InScroll()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "InScroll_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Scroll to make the target visible
|
||||
var scroll = _app.WaitForElement("InScroll_ScrollViewer").Single().Rect;
|
||||
_app.DragCoordinates(scroll.Right - 3, scroll.Bottom - 3, 0, 0);
|
||||
|
||||
// Double tap the target
|
||||
var target = _app.WaitForElement(targetName).Single();
|
||||
_app.DoubleTapCoordinates(target.Rect.X + tapX, target.Rect.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastDoubleTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_InListViewWithItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.DoubleTapCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastDoubleTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "Item_1" // We were not able to scroll on WASM!
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_InListViewWithoutItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithoutItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.DoubleTapCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastDoubleTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "Item_1" // We were not able to scroll on WASM!
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,14 +11,14 @@ using Uno.UITest.Helpers.Queries;
|
|||
|
||||
namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
||||
{
|
||||
public class Tapped_Tests : SampleControlUITestBase
|
||||
public class GestureEventsCommons_Tests : SampleControlUITestBase
|
||||
{
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android)] // Fixed in another commit coming soon
|
||||
public void When_Tapped_Then_ArgsLocationIsValid()
|
||||
{
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.TappedTest");
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.GestureEventsCommons");
|
||||
|
||||
var root = _app.WaitForElement("WhenTappedThenArgsLocationIsValid_Root").Single();
|
||||
var target = _app.WaitForElement("WhenTappedThenArgsLocationIsValid_Target").Single();
|
||||
|
@ -39,7 +39,7 @@ namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
|||
[AutoRetry]
|
||||
public void When_ChildHandlesPointers()
|
||||
{
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.TappedTest");
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.GestureEventsCommons");
|
||||
|
||||
var target = _app.WaitForElement("WhenChildHandlesPointers_Target").Single();
|
||||
|
||||
|
@ -47,14 +47,14 @@ namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
|||
_app.TapCoordinates(target.Rect.X + tapX, target.Rect.Y + tapY);
|
||||
|
||||
var result = _app.Marked("WhenChildHandlesPointers_Result").GetDependencyPropertyValue<string>("Text");
|
||||
result.Should().Be("Tapped");
|
||||
result.Should().Be("Yes");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_MultipleTappedRecognizer()
|
||||
{
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.TappedTest");
|
||||
Run("UITests.Shared.Windows_UI_Input.GestureRecognizerTests.GestureEventsCommons");
|
||||
|
||||
var target = _app.WaitForElement("WhenMultipleTappedRecognizer_Target").Single();
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Uno.UITest.Helpers.Queries;
|
||||
|
||||
namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
||||
{
|
||||
internal class GestureResult
|
||||
{
|
||||
public static GestureResult Get(QueryEx textBlock)
|
||||
=> Parse(textBlock.GetDependencyPropertyValue<string>("Text"));
|
||||
|
||||
public static GestureResult Parse(string text)
|
||||
{
|
||||
var regex = new Regex(@"(?<elt>[\w_]+)@(?<x>[\d\.]+),(?<y>[\d\.]+)");
|
||||
var result = regex.Match(text);
|
||||
if (!result.Success)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(text), $"Cannot parse '{text}'.");
|
||||
}
|
||||
|
||||
return new GestureResult(
|
||||
result.Groups["elt"].Value,
|
||||
float.Parse(result.Groups["x"].Value, CultureInfo.InvariantCulture),
|
||||
float.Parse(result.Groups["y"].Value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private GestureResult(string element, float x, float y)
|
||||
{
|
||||
Element = element;
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public string Element { get; }
|
||||
|
||||
public float X { get; }
|
||||
|
||||
public float Y { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using SamplesApp.UITests.TestFramework;
|
||||
using Uno.UITest.Helpers;
|
||||
using Uno.UITest.Helpers.Queries;
|
||||
using Uno.UITests.Helpers;
|
||||
|
||||
namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
||||
{
|
||||
public class RightTapped_Tests : SampleControlUITestBase
|
||||
{
|
||||
private const string _xamlTestPage = "UITests.Shared.Windows_UI_Input.GestureRecognizerTests.RightTappedTests";
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot test right button click on WASM yet
|
||||
public void When_Basic()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "Basic_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Tap and hold the target
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.TouchAndHoldCoordinates(target.X + tapX, target.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastRightTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.iOS)] // We cannot test right button click on WASM yet + Disabled on Android: The test engine is not able to find "Transformed_Target"
|
||||
public void When_Transformed()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string parentName = "Transformed_Parent";
|
||||
const string targetName = "Transformed_Target";
|
||||
|
||||
var parent = _app.WaitForElement(parentName).Single().Rect;
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
|
||||
// Tap and hold the target
|
||||
_app.TouchAndHoldCoordinates(parent.Right - target.Width, parent.Bottom - 3);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastRightTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot test right button click on WASM yet
|
||||
public void When_InScroll()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "InScroll_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Scroll to make the target visible
|
||||
var scroll = _app.WaitForElement("InScroll_ScrollViewer").Single().Rect;
|
||||
_app.DragCoordinates(scroll.Right - 3, scroll.Bottom - 3, 0, 0);
|
||||
|
||||
// Tap and hold the target
|
||||
var target = _app.WaitForElement(targetName).Single();
|
||||
_app.TouchAndHoldCoordinates(target.Rect.X + tapX, target.Rect.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastRightTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot test right button click on WASM yet
|
||||
public void When_InListViewWithItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.TouchAndHoldCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastRightTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "none" // Long press not supported with mouse
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot test right button click on WASM yet
|
||||
public void When_InListViewWithoutItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithoutItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.TouchAndHoldCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastRightTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "none" // Long press not supported with mouse
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using SamplesApp.UITests.TestFramework;
|
||||
using Uno.UITest.Helpers;
|
||||
using Uno.UITest.Helpers.Queries;
|
||||
using Uno.UITests.Helpers;
|
||||
|
||||
namespace SamplesApp.UITests.Windows_UI_Xaml_Input
|
||||
{
|
||||
public class Tapped_Tests : SampleControlUITestBase
|
||||
{
|
||||
private const string _xamlTestPage = "UITests.Shared.Windows_UI_Input.GestureRecognizerTests.TappedTest";
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_Basic()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "Basic_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Tap the target
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.TapCoordinates(target.X + tapX, target.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.iOS)] // Disabled on Android: The test engine is not able to find "Transformed_Target"
|
||||
public void When_Transformed()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string parentName = "Transformed_Parent";
|
||||
const string targetName = "Transformed_Target";
|
||||
|
||||
var parent = _app.WaitForElement(parentName).Single().Rect;
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
|
||||
// Tap the target
|
||||
_app.TapCoordinates(parent.Right - target.Width, parent.Bottom - 3);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot scroll to reach the target on WASM yet
|
||||
public void When_InScroll()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "InScroll_Target";
|
||||
const int tapX = 10, tapY = 10;
|
||||
|
||||
// Scroll to make the target visible
|
||||
var scroll = _app.WaitForElement("InScroll_ScrollViewer").Single().Rect;
|
||||
_app.DragCoordinates(scroll.Right - 3, scroll.Bottom - 3, 0, 0);
|
||||
|
||||
// Tap the target
|
||||
var target = _app.WaitForElement(targetName).Single();
|
||||
_app.TapCoordinates(target.Rect.X + tapX, target.Rect.Y + tapY);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastTapped"));
|
||||
result.Element.Should().Be(targetName);
|
||||
((int)result.X).Should().Be(tapX);
|
||||
((int)result.Y).Should().Be(tapY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
[ActivePlatforms(Platform.Android, Platform.iOS)] // We cannot scroll to reach the target on WASM yet
|
||||
public void When_InListViewWithItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.TapCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "Item_1" // We were not able to scroll on WASM!
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[AutoRetry]
|
||||
public void When_InListViewWithoutItemClick()
|
||||
{
|
||||
Run(_xamlTestPage);
|
||||
|
||||
const string targetName = "ListViewWithoutItemClick";
|
||||
|
||||
// Scroll a bit in the ListView
|
||||
var target = _app.WaitForElement(targetName).Single().Rect;
|
||||
_app.DragCoordinates(target.CenterX, target.Bottom - 3, target.CenterX, target.Y + 3);
|
||||
|
||||
// Tap and hold an item
|
||||
_app.TapCoordinates(target.CenterX, target.CenterY - 5);
|
||||
|
||||
var result = GestureResult.Get(_app.Marked("LastTapped"));
|
||||
var expectedItem = AppInitializer.GetLocalPlatform() == Platform.Browser
|
||||
? "Item_1" // We were not able to scroll on WASM!
|
||||
: "Item_3";
|
||||
result.Element.Should().Be(expectedItem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,7 +98,7 @@
|
|||
<Version>6.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.UI.Xaml">
|
||||
<Version>2.1.190218001-prerelease</Version>
|
||||
<Version>2.1.190606001</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.11.4-develop" />
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
<UserControl x:Class="UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests.MUX_Test"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests"
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:contract5Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:xamarin="http://uno.ui/xamarin"
|
||||
mc:Ignorable="d xamarin"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<xamarin:Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Orientation="Vertical"
|
||||
contract5Present:Spacing="4"
|
||||
Background="{ThemeResource SystemControlBackgroundBaseLowBrush}"
|
||||
Padding="12">
|
||||
|
||||
<ComboBox x:Name="ValidationComboBox"
|
||||
AutomationProperties.Name="ValidationComboBox"
|
||||
Header="Validation Mode"
|
||||
SelectedIndex="0"
|
||||
SelectionChanged="Validation_Changed">
|
||||
<ComboBoxItem Content="InvalidInputOverwritten" />
|
||||
<ComboBoxItem Content="Disabled" />
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox x:Name="SpinModeComboBox"
|
||||
AutomationProperties.Name="SpinModeComboBox"
|
||||
SelectedIndex="0"
|
||||
SelectionChanged="SpinMode_Changed"
|
||||
Header="SpinButtonMode">
|
||||
<ComboBoxItem Content="Hidden" />
|
||||
<ComboBoxItem Content="Compact" />
|
||||
<ComboBoxItem Content="Inline" />
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox x:Name="EnabledCheckBox"
|
||||
AutomationProperties.Name="EnabledCheckBox"
|
||||
IsChecked="True"
|
||||
Content="Enabled" />
|
||||
|
||||
<CheckBox x:Name="ExpressionCheckBox"
|
||||
AutomationProperties.Name="ExpressionCheckBox"
|
||||
IsChecked="False"
|
||||
Content="Accepts Expression" />
|
||||
|
||||
<CheckBox x:Name="WrapCheckBox"
|
||||
AutomationProperties.Name="WrapCheckBox"
|
||||
IsChecked="False"
|
||||
Content="Wrap Enabled" />
|
||||
|
||||
<!-- Set Text instead of Value to verify that it works -->
|
||||
<controls:NumberBox x:Name="SmallChangeNumberBox"
|
||||
AutomationProperties.Name="SmallChangeNumberBox"
|
||||
Text="1"
|
||||
Header="SmallChange" />
|
||||
<controls:NumberBox x:Name="LargeChangeNumberBox"
|
||||
AutomationProperties.Name="LargeChangeNumberBox"
|
||||
Text="10"
|
||||
Header="LargeChange" />
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox x:Name="MinCheckBox"
|
||||
AutomationProperties.Name="MinCheckBox"
|
||||
IsChecked="False"
|
||||
Checked="MinCheckBox_CheckChanged"
|
||||
Unchecked="MinCheckBox_CheckChanged"
|
||||
MinWidth="32"
|
||||
VerticalAlignment="Bottom" />
|
||||
<controls:NumberBox x:Name="MinNumberBox"
|
||||
AutomationProperties.Name="MinNumberBox"
|
||||
Header="Minimum"
|
||||
Value="0"
|
||||
IsEnabled="False"
|
||||
ValueChanged="MinValueChanged" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox x:Name="MaxCheckBox"
|
||||
AutomationProperties.Name="MaxCheckBox"
|
||||
IsChecked="False"
|
||||
Checked="MaxCheckBox_CheckChanged"
|
||||
Unchecked="MaxCheckBox_CheckChanged"
|
||||
MinWidth="32"
|
||||
VerticalAlignment="Bottom" />
|
||||
<!-- Verify that setting Value overrides setting Text -->
|
||||
<controls:NumberBox x:Name="MaxNumberBox"
|
||||
AutomationProperties.Name="MaxNumberBox"
|
||||
Header="Maximum"
|
||||
Value="100"
|
||||
Text="50"
|
||||
IsEnabled="False"
|
||||
ValueChanged="MaxValueChanged" />
|
||||
</StackPanel>
|
||||
|
||||
<Button x:Name="CustomFormatterButton"
|
||||
AutomationProperties.Name="CustomFormatterButton"
|
||||
Content="Custom Formatter"
|
||||
Click="CustomFormatterButton_Click"
|
||||
Margin="0,4,0,0" />
|
||||
|
||||
<Button x:Name="SetTextButton"
|
||||
AutomationProperties.Name="SetTextButton"
|
||||
Content="Set text to 15"
|
||||
Click="SetTextButton_Click"
|
||||
Margin="0,4,0,0" />
|
||||
|
||||
<Button x:Name="SetValueButton"
|
||||
AutomationProperties.Name="SetValueButton"
|
||||
Content="Set value to 42"
|
||||
Click="SetValueButton_Click"
|
||||
Margin="0,4,0,0" />
|
||||
|
||||
<Button x:Name="SetValueNaNButton"
|
||||
AutomationProperties.Name="SetValueNaNButton"
|
||||
Content="Set value to NaN"
|
||||
Click="SetNaNButton_Click"
|
||||
Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
contract5Present:Spacing="4">
|
||||
<controls:NumberBox x:Name="TestNumberBox"
|
||||
AutomationProperties.Name="TestNumberBox"
|
||||
Header="NumberBox"
|
||||
PlaceholderText="Text"
|
||||
ValueChanged="NumberBoxValueChanged"
|
||||
SmallChange="{x:Bind SmallChangeNumberBox.Value, Mode=OneWay}"
|
||||
LargeChange="{x:Bind LargeChangeNumberBox.Value, Mode=OneWay}"
|
||||
AcceptsExpression="{x:Bind ExpressionCheckBox.IsChecked.Value, Mode=OneWay}"
|
||||
IsWrapEnabled="{x:Bind WrapCheckBox.IsChecked.Value, Mode=OneWay}"
|
||||
IsEnabled="{x:Bind EnabledCheckBox.IsChecked.Value, Mode=OneWay}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Value:"
|
||||
Margin="0,0,5,0" />
|
||||
<TextBlock x:Name="NewValueTextBox"
|
||||
AutomationProperties.Name="NewValueTextBox"
|
||||
Text="0" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Old Value:"
|
||||
Margin="0,0,5,0" />
|
||||
<TextBlock x:Name="OldValueTextBox"
|
||||
AutomationProperties.Name="OldValueTextBox" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Text:"
|
||||
Margin="0,0,5,0" />
|
||||
<TextBlock x:Name="TextTextBox"
|
||||
AutomationProperties.Name="TextTextBox"
|
||||
Text="0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</xamarin:Grid>
|
||||
|
||||
</UserControl>
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Uno.UI.Samples.Controls;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.Globalization.NumberFormatting;
|
||||
|
||||
// Condition to be removed when UWP will be updated to 18362+
|
||||
#if HAS_UNO
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
#endif
|
||||
|
||||
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace UITests.Shared.Microsoft_UI_Xaml_Controls.NumberBoxTests
|
||||
{
|
||||
[SampleControlInfo("NumberBox", "MUX_Test")]
|
||||
public sealed partial class MUX_Test : UserControl
|
||||
{
|
||||
public MUX_Test()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
#if HAS_UNO
|
||||
TestNumberBox.RegisterPropertyChangedCallback(NumberBox.TextProperty, new DependencyPropertyChangedCallback(TextPropertyChanged));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAS_UNO
|
||||
private void SpinMode_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TestNumberBox != null)
|
||||
{
|
||||
if (SpinModeComboBox.SelectedIndex == 0)
|
||||
{
|
||||
TestNumberBox.SpinButtonPlacementMode = NumberBoxSpinButtonPlacementMode.Hidden;
|
||||
}
|
||||
else if (SpinModeComboBox.SelectedIndex == 1)
|
||||
{
|
||||
TestNumberBox.SpinButtonPlacementMode = NumberBoxSpinButtonPlacementMode.Compact;
|
||||
}
|
||||
else if (SpinModeComboBox.SelectedIndex == 2)
|
||||
{
|
||||
TestNumberBox.SpinButtonPlacementMode = NumberBoxSpinButtonPlacementMode.Inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Validation_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TestNumberBox != null)
|
||||
{
|
||||
if (ValidationComboBox.SelectedIndex == 0)
|
||||
{
|
||||
TestNumberBox.ValidationMode = NumberBoxValidationMode.InvalidInputOverwritten;
|
||||
}
|
||||
else if (ValidationComboBox.SelectedIndex == 1)
|
||||
{
|
||||
TestNumberBox.ValidationMode = NumberBoxValidationMode.Disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MinCheckBox_CheckChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MinNumberBox.IsEnabled = (bool)MinCheckBox.IsChecked;
|
||||
MinValueChanged(null, null);
|
||||
}
|
||||
|
||||
private void MaxCheckBox_CheckChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MaxNumberBox.IsEnabled = (bool)MaxCheckBox.IsChecked;
|
||||
MaxValueChanged(null, null);
|
||||
}
|
||||
|
||||
private void MinValueChanged(object sender, object e)
|
||||
{
|
||||
if (TestNumberBox != null)
|
||||
{
|
||||
TestNumberBox.Minimum = (bool)MinCheckBox.IsChecked ? MinNumberBox.Value : double.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void MaxValueChanged(object sender, object e)
|
||||
{
|
||||
if (TestNumberBox != null)
|
||||
{
|
||||
TestNumberBox.Maximum = (bool)MaxCheckBox.IsChecked ? MaxNumberBox.Value : double.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void NumberBoxValueChanged(object sender, NumberBoxValueChangedEventArgs e)
|
||||
{
|
||||
if (TestNumberBox != null && NewValueTextBox != null && OldValueTextBox != null)
|
||||
{
|
||||
NewValueTextBox.Text = e.NewValue.ToString();
|
||||
OldValueTextBox.Text = e.OldValue.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void CustomFormatterButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
List<string> languages = new List<string>() { "fr-FR" };
|
||||
DecimalFormatter formatter = new DecimalFormatter(languages, "FR");
|
||||
formatter.IntegerDigits = 1;
|
||||
formatter.FractionDigits = 2;
|
||||
TestNumberBox.NumberFormatter = formatter;
|
||||
}
|
||||
|
||||
private void SetTextButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TestNumberBox.Text = "15";
|
||||
}
|
||||
|
||||
private void SetValueButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TestNumberBox.Value = 42;
|
||||
}
|
||||
|
||||
private void SetNaNButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TestNumberBox.Value = Double.NaN;
|
||||
}
|
||||
|
||||
private void TextPropertyChanged(DependencyObject o, DependencyProperty p)
|
||||
{
|
||||
TextTextBox.Text = TestNumberBox.Text;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -13,6 +13,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Microsoft_UI_Xaml_Controls\NumberBoxTests\MUX_Test.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Resources\StaticResource\StaticResource_Simple.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -125,6 +129,14 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\DoubleTappedTests.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\GestureEventsCommons.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\ManipulationEvents.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -137,6 +149,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\RightTappedTests.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\TappedTest.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -661,6 +677,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\RepeatButton\RepeatButton_Automated.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\ScrollViewerTests\ScrollViewer_Simple.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -2890,6 +2910,9 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\BindableBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\UWPViewHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Helpers\ViewModelBase.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft_UI_Xaml_Controls\NumberBoxTests\MUX_Test.xaml.cs">
|
||||
<DependentUpon>MUX_Test.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Toolkit\Elevation.xaml.cs">
|
||||
<DependentUpon>Elevation.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -2960,6 +2983,12 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI.Xaml_Automation\AutomationProperties_Name.xaml.cs">
|
||||
<DependentUpon>AutomationProperties_Name.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\DoubleTappedTests.xaml.cs">
|
||||
<DependentUpon>DoubleTappedTests.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\GestureEventsCommons.xaml.cs">
|
||||
<DependentUpon>GestureEventsCommons.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\ManipulationEvents.xaml.cs">
|
||||
<DependentUpon>ManipulationEvents.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -2969,6 +2998,9 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\Manipulation_WhenInScrollViewer.xaml.cs">
|
||||
<DependentUpon>Manipulation_WhenInScrollViewer.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\RightTappedTests.xaml.cs">
|
||||
<DependentUpon>$fileinputname$.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Input\GestureRecognizerTests\TappedTest.xaml.cs">
|
||||
<DependentUpon>TappedTest.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -3246,6 +3278,9 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\Popup\Popup_Overlay_On.xaml.cs">
|
||||
<DependentUpon>Popup_Overlay_On.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\RepeatButton\RepeatButton_Automated.xaml.cs">
|
||||
<DependentUpon>RepeatButton_Automated.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Windows_UI_Xaml_Controls\TextBlockControl\TextBlock_Foreground_While_Collapsed.xaml.cs">
|
||||
<DependentUpon>TextBlock_Foreground_While_Collapsed.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
<Page
|
||||
x:Class="UITests.Shared.Windows_UI_Input.GestureRecognizerTests.DoubleTappedTests"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.Shared.Windows_UI_Input.GestureRecognizerTests"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Text="Double on each zones. Last double tapped:"
|
||||
TextWrapping="Wrap"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="2"/>
|
||||
<TextBlock
|
||||
x:Name="LastDoubleTapped"
|
||||
Text="__none__"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"/>
|
||||
|
||||
<TextBlock
|
||||
Text="Basic"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
x:Name="Basic_Target"
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FF0018"
|
||||
DoubleTapped="TargetDoubleTapped" />
|
||||
|
||||
<TextBlock
|
||||
Text="With transformation"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFA52C"
|
||||
x:Name="Transformed_Parent">
|
||||
<Border
|
||||
x:Name="Transformed_Target"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Background="#33000000"
|
||||
DoubleTapped="TargetDoubleTapped">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform Rotation="45" TranslateX="100" TranslateY="100" />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In ScrollViewer"
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFFF41">
|
||||
<ScrollViewer
|
||||
VerticalScrollMode="Enabled"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
HorizontalScrollMode="Enabled"
|
||||
HorizontalScrollBarVisibility="Visible"
|
||||
x:Name="InScroll_ScrollViewer">
|
||||
<Grid
|
||||
Width="300"
|
||||
Height="300">
|
||||
<Border
|
||||
x:Name="InScroll_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#33000000"
|
||||
DoubleTapped="TargetDoubleTapped" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In button"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#008018">
|
||||
<Border
|
||||
x:Name="InButton_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#33000000"
|
||||
DoubleTapped="TargetDoubleTapped" />
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Text="ListView"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#0000F9">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="True"
|
||||
SelectionMode="Single"
|
||||
x:Name="ListViewWithItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
DoubleTapped="ItemDoubleTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
|
||||
|
||||
<TextBlock
|
||||
Text="ListView item click disabled"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#86007D">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="False"
|
||||
SelectionMode="None"
|
||||
x:Name="ListViewWithoutItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
DoubleTapped="ItemDoubleTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Uno.UI;
|
||||
using Uno.UI.Samples.Controls;
|
||||
|
||||
namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
||||
{
|
||||
[SampleControlInfo("Gesture recognizer")]
|
||||
public sealed partial class DoubleTappedTests : Page
|
||||
{
|
||||
public DoubleTappedTests()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void TargetDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
LastDoubleTapped.Text = $"{target.Name}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
|
||||
private void ItemDoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
LastDoubleTapped.Text = $"Item_{target.DataContext}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
<Page
|
||||
x:Class="UITests.Shared.Windows_UI_Input.GestureRecognizerTests.GestureEventsCommons"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.Shared.Windows_UI_Input.GestureRecognizerTests"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<StackPanel>
|
||||
<Border
|
||||
x:Name="WhenTappedThenArgsLocationIsValid_Root"
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FF0018"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test event args location" />
|
||||
<Border
|
||||
x:Name="WhenTappedThenArgsLocationIsValid_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
Tapped="WhenTappedThenArgsLocationIsValid_OnTargetTapped"
|
||||
Background="#FF0018">
|
||||
<TextBlock Text="Touch target" />
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Rel. to test root (physical px): " />
|
||||
<TextBlock Name="WhenTappedThenArgsLocationIsValid_Result_RelativeToRoot" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Rel. to target (physical px): " />
|
||||
<TextBlock Name="WhenTappedThenArgsLocationIsValid_Result_RelativeToTarget" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FFA52C"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test when child handles all pointer events" />
|
||||
<Border
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="#FFA52C"
|
||||
Tapped="WhenChildHandlesPointers_OnParentTapped">
|
||||
<Border
|
||||
x:Name="WhenChildHandlesPointers_Target"
|
||||
Width="70"
|
||||
Height="70"
|
||||
Background="#66FFFFFF"
|
||||
PointerEntered="HandlePointerEvent"
|
||||
PointerPressed="HandlePointerEvent"
|
||||
PointerMoved="HandlePointerEvent"
|
||||
PointerReleased="HandlePointerEvent"
|
||||
PointerExited="HandlePointerEvent"
|
||||
PointerCanceled="HandlePointerEvent"
|
||||
PointerCaptureLost="HandlePointerEvent"
|
||||
PointerWheelChanged="HandlePointerEvent">
|
||||
<TextBlock Text="Touch target"/>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on parent: " />
|
||||
<TextBlock x:Name="WhenChildHandlesPointers_Result" Text="__no__" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FFFF41"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test parent and child listen tapped event" />
|
||||
<Border
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="#FFFF41"
|
||||
Tapped="WhenMultipleTappedRecognizer_OnParentTapped">
|
||||
<Border
|
||||
x:Name="WhenMultipleTappedRecognizer_Target"
|
||||
Width="70"
|
||||
Height="70"
|
||||
Background="#66FFFFFF"
|
||||
Tapped="WhenMultipleTappedRecognizer_OnTargetTapped">
|
||||
<TextBlock Text="Touch target"/>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on parent: " />
|
||||
<TextBlock x:Name="WhenMultipleTappedRecognizer_Result_Parent" Text="0" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on target: " />
|
||||
<TextBlock x:Name="WhenMultipleTappedRecognizer_Result_Target" Text="0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#008018"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test when parent capture pointer" />
|
||||
<Border
|
||||
x:Name="WhenParentCapturesPointer_Parent"
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="#008018"
|
||||
PointerPressed="WhenParentCapturesPointer_OnParentPointerPressed"
|
||||
Tapped="WhenParentCapturesPointer_OnParentTapped">
|
||||
<Border
|
||||
x:Name="WhenParentCapturesPointer_Target"
|
||||
Width="70"
|
||||
Height="70"
|
||||
Background="#66FFFFFF"
|
||||
Tapped="WhenParentCapturesPointer_OnTargetTapped">
|
||||
<TextBlock Text="Touch target"/>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Captured by parent: " />
|
||||
<TextBlock x:Name="WhenParentCapturesPointer_Result_Captured" Text="__false__" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on parent: " />
|
||||
<TextBlock x:Name="WhenParentCapturesPointer_Result_Parent" Text="__no__" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on target: " />
|
||||
<TextBlock x:Name="WhenParentCapturesPointer_Result_Target" Text="__no__" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Page>
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Uno.UI;
|
||||
using Uno.UI.Samples.Controls;
|
||||
|
||||
namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
||||
{
|
||||
[SampleControlInfo("Gesture recognizer")]
|
||||
public sealed partial class GestureEventsCommons : Page
|
||||
{
|
||||
public GestureEventsCommons()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void WhenTappedThenArgsLocationIsValid_OnTargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var relativeToRoot = e.GetPosition(WhenTappedThenArgsLocationIsValid_Root).LogicalToPhysicalPixels();
|
||||
var relativeToTarget = e.GetPosition(WhenTappedThenArgsLocationIsValid_Target).LogicalToPhysicalPixels();
|
||||
|
||||
WhenTappedThenArgsLocationIsValid_Result_RelativeToRoot.Text = $"({(int)relativeToRoot.X:D},{(int)relativeToRoot.Y:D})";
|
||||
WhenTappedThenArgsLocationIsValid_Result_RelativeToTarget.Text = $"({(int)relativeToTarget.X:D},{(int)relativeToTarget.Y:D})";
|
||||
}
|
||||
|
||||
private void HandlePointerEvent(object sender, PointerRoutedEventArgs e)
|
||||
=> e.Handled = true;
|
||||
|
||||
private void WhenChildHandlesPointers_OnParentTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenChildHandlesPointers_Result.Text = "Yes";
|
||||
|
||||
private void WhenMultipleTappedRecognizer_OnParentTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenMultipleTappedRecognizer_Result_Parent.Text = int.TryParse(WhenMultipleTappedRecognizer_Result_Parent.Text, out var count)
|
||||
? (count + 1).ToString()
|
||||
: "1";
|
||||
|
||||
private void WhenMultipleTappedRecognizer_OnTargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenMultipleTappedRecognizer_Result_Target.Text = int.TryParse(WhenMultipleTappedRecognizer_Result_Target.Text, out var count)
|
||||
? (count + 1).ToString()
|
||||
: "1";
|
||||
|
||||
private void WhenParentCapturesPointer_OnParentPointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
=> WhenParentCapturesPointer_Result_Captured.Text = ((UIElement)sender).CapturePointer(e.Pointer).ToString();
|
||||
|
||||
private void WhenParentCapturesPointer_OnParentTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenParentCapturesPointer_Result_Parent.Text = "Yes";
|
||||
|
||||
private void WhenParentCapturesPointer_OnTargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenParentCapturesPointer_Result_Target.Text = "Yes";
|
||||
}
|
||||
}
|
|
@ -342,6 +342,18 @@
|
|||
Content="Handle event"
|
||||
Margin="20, 0"
|
||||
Visibility="{Binding ElementName=_gestureDoubleTapped, Path=IsOn}"/>
|
||||
<ToggleSwitch
|
||||
x:Name="_gestureRightTapped"
|
||||
Header="Right tapped"
|
||||
OnContent="Subscribed to right tap"
|
||||
OffContent="Right tap disabled"
|
||||
Toggled="OnConfigChanged"
|
||||
IsOn="False" />
|
||||
<CheckBox
|
||||
x:Name="_gestureRightTappedHandle"
|
||||
Content="Handle event"
|
||||
Margin="20, 0"
|
||||
Visibility="{Binding ElementName=_gestureRightTapped, Path=IsOn}"/>
|
||||
|
||||
|
||||
<Slider
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizer
|
|||
private readonly ManipulationCompletedEventHandler _logManipulationCompleted;
|
||||
private readonly TappedEventHandler _logTapped;
|
||||
private readonly DoubleTappedEventHandler _logDoubleTapped;
|
||||
private readonly RightTappedEventHandler _logRightTapped;
|
||||
|
||||
private bool _isReady;
|
||||
|
||||
|
@ -77,6 +78,7 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizer
|
|||
_logManipulationCompleted = new ManipulationCompletedEventHandler(CreateHandler(ManipulationCompletedEvent, "Manip completed", _manipCompletedHandle));
|
||||
_logTapped = new TappedEventHandler(CreateHandler(TappedEvent, "Tapped", _gestureTappedHandle));
|
||||
_logDoubleTapped = new DoubleTappedEventHandler(CreateHandler(DoubleTappedEvent, "DoubleTapped", _gestureDoubleTappedHandle));
|
||||
_logRightTapped = new RightTappedEventHandler(CreateHandler(RightTappedEvent, "RightTapped", _gestureRightTappedHandle));
|
||||
|
||||
_log.ItemsSource = _eventLog;
|
||||
_pointerType.ItemsSource = Enum.GetNames(typeof(PointerDeviceType));
|
||||
|
@ -162,6 +164,7 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizer
|
|||
target.RemoveHandler(ManipulationCompletedEvent, _logManipulationCompleted);
|
||||
target.RemoveHandler(TappedEvent, _logTapped);
|
||||
target.RemoveHandler(DoubleTappedEvent, _logDoubleTapped);
|
||||
target.RemoveHandler(RightTappedEvent, _logRightTapped);
|
||||
|
||||
if (allEvents || _ptEntered.IsOn)
|
||||
target.AddHandler(PointerEnteredEvent, _logPointerEntered, handledToo);
|
||||
|
@ -191,6 +194,8 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizer
|
|||
target.AddHandler(TappedEvent, _logTapped, handledToo);
|
||||
if (allEvents || _gestureDoubleTapped.IsOn)
|
||||
target.AddHandler(DoubleTappedEvent, _logDoubleTapped, handledToo);
|
||||
if (allEvents || _gestureRightTapped.IsOn)
|
||||
target.AddHandler(RightTappedEvent, _logRightTapped, handledToo);
|
||||
}
|
||||
|
||||
private (EventValidity, string error) Validate(object snd, RoutedEvent evt, RoutedEventArgs args)
|
||||
|
@ -329,6 +334,8 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizer
|
|||
return $"{Src(tapped)} | hd={tapped.Handled} | position={F(tapped.GetPosition(Sender as UIElement))}";
|
||||
case DoubleTappedRoutedEventArgs doubleTapped:
|
||||
return $"{Src(doubleTapped)} | hd={doubleTapped.Handled} | position={F(doubleTapped.GetPosition(Sender as UIElement))}";
|
||||
case RightTappedRoutedEventArgs rightTapped:
|
||||
return $"{Src(rightTapped)} | hd={rightTapped.Handled} | position={F(rightTapped.GetPosition(Sender as UIElement))}";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
<Page
|
||||
x:Class="UITests.Shared.Windows_UI_Input.GestureRecognizerTests.RightTappedTests"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.Shared.Windows_UI_Input.GestureRecognizerTests"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Text="Long press (touch), right click (mouse), use barrel button (pen) on each zones. Last right tapped:"
|
||||
TextWrapping="Wrap"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="2"/>
|
||||
<TextBlock
|
||||
x:Name="LastRightTapped"
|
||||
Text="__none__"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"/>
|
||||
|
||||
<TextBlock
|
||||
Text="Basic"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
x:Name="Basic_Target"
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FF0018"
|
||||
RightTapped="TargetRightTapped" />
|
||||
|
||||
<TextBlock
|
||||
Text="With transformation"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFA52C"
|
||||
x:Name="Transformed_Parent">
|
||||
<Border
|
||||
x:Name="Transformed_Target"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Background="#33000000"
|
||||
RightTapped="TargetRightTapped">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform Rotation="45" TranslateX="100" TranslateY="100" />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In ScrollViewer"
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFFF41">
|
||||
<ScrollViewer
|
||||
VerticalScrollMode="Enabled"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
HorizontalScrollMode="Enabled"
|
||||
HorizontalScrollBarVisibility="Visible"
|
||||
x:Name="InScroll_ScrollViewer">
|
||||
<Grid
|
||||
Width="300"
|
||||
Height="300">
|
||||
<Border
|
||||
x:Name="InScroll_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#33000000"
|
||||
RightTapped="TargetRightTapped" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In button"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#008018">
|
||||
<Border
|
||||
x:Name="InButton_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#33000000"
|
||||
RightTapped="TargetRightTapped" />
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Text="ListView"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#0000F9">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="True"
|
||||
SelectionMode="Single"
|
||||
x:Name="ListViewWithItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
RightTapped="ItemRightTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
|
||||
|
||||
<TextBlock
|
||||
Text="ListView item click disabled"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#86007D">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="False"
|
||||
SelectionMode="None"
|
||||
x:Name="ListViewWithoutItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
RightTapped="ItemRightTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Uno.UI;
|
||||
using Uno.UI.Samples.Controls;
|
||||
|
||||
namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
||||
{
|
||||
[SampleControlInfo("Gesture recognizer")]
|
||||
public sealed partial class RightTappedTests : Page
|
||||
{
|
||||
public RightTappedTests()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void TargetRightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||
{
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
LastRightTapped.Text = $"{target.Name}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
|
||||
private void ItemRightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||
{
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
LastRightTapped.Text = $"Item_{target.DataContext}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,99 +8,200 @@
|
|||
mc:Ignorable="d"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Text="Double on each zones. Last double tapped:"
|
||||
TextWrapping="Wrap"
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="2"/>
|
||||
<TextBlock
|
||||
x:Name="LastTapped"
|
||||
Text="__none__"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"/>
|
||||
|
||||
<TextBlock
|
||||
Text="Basic"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
x:Name="WhenTappedThenArgsLocationIsValid_Root"
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FF0018"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test event args location" />
|
||||
<Border
|
||||
x:Name="WhenTappedThenArgsLocationIsValid_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
Tapped="WhenTappedThenArgsLocationIsValid_OnTargetTapped"
|
||||
Background="#FF0018">
|
||||
<TextBlock Text="Touch target" />
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Relative to test root (physical px): " />
|
||||
<TextBlock Name="WhenTappedThenArgsLocationIsValid_Result_RelativeToRoot" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Relative to target (physical px): " />
|
||||
<TextBlock Name="WhenTappedThenArgsLocationIsValid_Result_RelativeToTarget" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
x:Name="Basic_Target"
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FF0018"
|
||||
Tapped="TargetTapped" />
|
||||
|
||||
<TextBlock
|
||||
Text="With transformation"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFA52C"
|
||||
x:Name="Transformed_Parent">
|
||||
<Border
|
||||
x:Name="Transformed_Target"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
Width="50"
|
||||
Height="50"
|
||||
Background="#33000000"
|
||||
Tapped="TargetTapped">
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform Rotation="45" TranslateX="100" TranslateY="100" />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In ScrollViewer"
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FFA52C"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test when child handles all pointer events" />
|
||||
<Border
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="#FFA52C"
|
||||
Tapped="WhenChildHandlesPointers_OnParentTapped">
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#FFFF41">
|
||||
<ScrollViewer
|
||||
VerticalScrollMode="Enabled"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
HorizontalScrollMode="Enabled"
|
||||
HorizontalScrollBarVisibility="Visible"
|
||||
x:Name="InScroll_ScrollViewer">
|
||||
<Grid
|
||||
Width="300"
|
||||
Height="300">
|
||||
<Border
|
||||
x:Name="WhenChildHandlesPointers_Target"
|
||||
Width="70"
|
||||
Height="70"
|
||||
Background="#66FFFFFF"
|
||||
PointerEntered="HandlePointerEvent"
|
||||
PointerPressed="HandlePointerEvent"
|
||||
PointerMoved="HandlePointerEvent"
|
||||
PointerReleased="HandlePointerEvent"
|
||||
PointerExited="HandlePointerEvent"
|
||||
PointerCanceled="HandlePointerEvent"
|
||||
PointerCaptureLost="HandlePointerEvent"
|
||||
PointerWheelChanged="HandlePointerEvent">
|
||||
<TextBlock Text="Touch target"/>
|
||||
</Border>
|
||||
</Border>
|
||||
<TextBlock x:Name="WhenChildHandlesPointers_Result" />
|
||||
</StackPanel>
|
||||
x:Name="InScroll_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="#33000000"
|
||||
Tapped="TargetTapped" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Text="In button"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Button
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#008018">
|
||||
<Border
|
||||
x:Name="InButton_Target"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="#33000000"
|
||||
Tapped="TargetTapped" />
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Text="ListView"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Width="250"
|
||||
BorderThickness="3"
|
||||
BorderBrush="#FFFF41"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Test duplicated tapped event" />
|
||||
<Border
|
||||
Width="100"
|
||||
Height="100"
|
||||
Background="#FFFF41"
|
||||
Tapped="WhenMultipleTappedRecognizer_OnParentTapped">
|
||||
<Border
|
||||
x:Name="WhenMultipleTappedRecognizer_Target"
|
||||
Width="70"
|
||||
Height="70"
|
||||
Background="#66FFFFFF"
|
||||
Tapped="WhenMultipleTappedRecognizer_OnTargetTapped">
|
||||
<TextBlock Text="Touch target"/>
|
||||
</Border>
|
||||
</Border>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on parent: " />
|
||||
<TextBlock x:Name="WhenMultipleTappedRecognizer_Result_Parent" Text="0" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Tapped on target: " />
|
||||
<TextBlock x:Name="WhenMultipleTappedRecognizer_Result_Target" Text="0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#0000F9">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="True"
|
||||
SelectionMode="Single"
|
||||
x:Name="ListViewWithItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
Tapped="ItemTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<TextBlock
|
||||
Text="ListView item click disabled"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Center"/>
|
||||
<Border
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="150"
|
||||
Height="150"
|
||||
Background="#86007D">
|
||||
<ListView
|
||||
ItemsSource="0123456789abcdef"
|
||||
IsItemClickEnabled="False"
|
||||
SelectionMode="None"
|
||||
x:Name="ListViewWithoutItemClick">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Height="50"
|
||||
Width="125"
|
||||
Background="#11000000"
|
||||
BorderBrush="#33000000"
|
||||
BorderThickness="3"
|
||||
Margin="3"
|
||||
Tapped="ItemTapped">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Uno.UI;
|
||||
|
@ -15,29 +16,20 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
|||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void WhenTappedThenArgsLocationIsValid_OnTargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
private void TargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var relativeToRoot = e.GetPosition(WhenTappedThenArgsLocationIsValid_Root).LogicalToPhysicalPixels();
|
||||
var relativeToTarget = e.GetPosition(WhenTappedThenArgsLocationIsValid_Target).LogicalToPhysicalPixels();
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
WhenTappedThenArgsLocationIsValid_Result_RelativeToRoot.Text = $"({(int)relativeToRoot.X:D},{(int)relativeToRoot.Y:D})";
|
||||
WhenTappedThenArgsLocationIsValid_Result_RelativeToTarget.Text = $"({(int)relativeToTarget.X:D},{(int)relativeToTarget.Y:D})";
|
||||
LastTapped.Text = $"{target.Name}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
|
||||
private void HandlePointerEvent(object sender, PointerRoutedEventArgs e)
|
||||
=> e.Handled = true;
|
||||
private void ItemTapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var target = (FrameworkElement)sender;
|
||||
var position = e.GetPosition(target).LogicalToPhysicalPixels();
|
||||
|
||||
private void WhenChildHandlesPointers_OnParentTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenChildHandlesPointers_Result.Text = "Tapped";
|
||||
|
||||
private void WhenMultipleTappedRecognizer_OnParentTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenMultipleTappedRecognizer_Result_Parent.Text = int.TryParse(WhenMultipleTappedRecognizer_Result_Parent.Text, out var count)
|
||||
? (count + 1).ToString()
|
||||
: "1";
|
||||
|
||||
private void WhenMultipleTappedRecognizer_OnTargetTapped(object sender, TappedRoutedEventArgs e)
|
||||
=> WhenMultipleTappedRecognizer_Result_Target.Text = int.TryParse(WhenMultipleTappedRecognizer_Result_Target.Text, out var count)
|
||||
? (count + 1).ToString()
|
||||
: "1";
|
||||
LastTapped.Text = $"Item_{target.DataContext}@{position.X:F2},{position.Y:F2}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</Grid.RowDefinitions>
|
||||
|
||||
<Border
|
||||
x:Name="_Parent"
|
||||
x:Name="TheParent"
|
||||
BorderThickness="10"
|
||||
BorderBrush="DeepSkyBlue"
|
||||
Background="Red"
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
|||
private void OnParentPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var parentRelToTarget = e.GetCurrentPoint(Target).Position;
|
||||
var parentRelToParent = e.GetCurrentPoint(_Parent).Position;
|
||||
var parentRelToParent = e.GetCurrentPoint(TheParent).Position;
|
||||
|
||||
ParentRelToTarget.Text = F(parentRelToTarget);
|
||||
ParentRelToParent.Text = F(parentRelToParent);
|
||||
|
@ -29,7 +29,7 @@ namespace UITests.Shared.Windows_UI_Input.GestureRecognizerTests
|
|||
private void OnTargetPointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var targetRelToTarget = e.GetCurrentPoint(Target).Position;
|
||||
var targetRelToParent = e.GetCurrentPoint(_Parent).Position;
|
||||
var targetRelToParent = e.GetCurrentPoint(TheParent).Position;
|
||||
|
||||
TargetRelToTarget.Text = F(targetRelToTarget);
|
||||
TargetRelToParent.Text = F(targetRelToParent);
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<UserControl x:Class="UITests.Shared.Windows_UI_Xaml_Controls.RepeatButton_Automated"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:UITests.Shared.Windows_UI_Xaml_Controls.RepeatButton"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<RepeatButton x:Uid="repeat01"
|
||||
Click="OnClicked"
|
||||
Content="Click me" />
|
||||
<TextBlock x:Name="counter"
|
||||
Text="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Uno.UI.Samples.Controls;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace UITests.Shared.Windows_UI_Xaml_Controls
|
||||
{
|
||||
[SampleControlInfo("RepeatButton")]
|
||||
public sealed partial class RepeatButton_Automated : UserControl
|
||||
{
|
||||
private int clickCount;
|
||||
|
||||
public RepeatButton_Automated()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnClicked(object sender, object args)
|
||||
{
|
||||
clickCount++;
|
||||
|
||||
counter.Text = clickCount.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,13 +20,15 @@ namespace Windows.Foundation.Metadata
|
|||
"Uno"
|
||||
};
|
||||
|
||||
private static bool IsImplementedByUno(MemberInfo member) => (member?.GetCustomAttributes(typeof(Uno.NotImplementedAttribute), false)?.Length ?? -1) == 0;
|
||||
|
||||
public static bool IsTypePresent(string typeName)
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (!_isTypePresent.TryGetValue(typeName, out var result))
|
||||
{
|
||||
_isTypePresent[typeName] = result = GetValidType(typeName)?.GetCustomAttributes(typeof(Uno.NotImplementedAttribute), false)?.Length == 0;
|
||||
_isTypePresent[typeName] = result = IsImplementedByUno(GetValidType(typeName));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -43,19 +45,21 @@ namespace Windows.Foundation.Metadata
|
|||
.Any() ?? false;
|
||||
|
||||
public static bool IsEventPresent(string typeName, string eventName)
|
||||
=> GetValidType(typeName)
|
||||
?.GetEvent(eventName) != null;
|
||||
=> IsImplementedByUno(
|
||||
GetValidType(typeName)
|
||||
?.GetEvent(eventName));
|
||||
|
||||
public static bool IsPropertyPresent(string typeName, string propertyName)
|
||||
=> GetValidType(typeName)
|
||||
?.GetProperty(propertyName) != null;
|
||||
=> IsImplementedByUno(
|
||||
GetValidType(typeName)
|
||||
?.GetProperty(propertyName));
|
||||
|
||||
public static bool IsReadOnlyPropertyPresent(string typeName, string propertyName)
|
||||
{
|
||||
var property = GetValidType(typeName)
|
||||
?.GetProperty(propertyName);
|
||||
|
||||
if(property != null)
|
||||
if(IsImplementedByUno(property))
|
||||
{
|
||||
return property.GetMethod != null && property.SetMethod == null;
|
||||
}
|
||||
|
@ -68,7 +72,7 @@ namespace Windows.Foundation.Metadata
|
|||
var property = GetValidType(typeName)
|
||||
?.GetProperty(propertyName);
|
||||
|
||||
if (property != null)
|
||||
if (IsImplementedByUno(property))
|
||||
{
|
||||
return property.GetMethod != null && property.SetMethod != null;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ import android.view.ViewParent;
|
|||
/* internal */ final class UnoMotionHelper {
|
||||
private static final String LOGTAG = "UnoViewGroup";
|
||||
|
||||
// Stylus when barrel is pressed when touching the screen
|
||||
private static final int STYLUS_WITH_BARREL_DOWN = 211;
|
||||
private static final int STYLUS_WITH_BARREL_MOVE = 213;
|
||||
private static final int STYLUS_WITH_BARREL_UP = 212;
|
||||
|
||||
/**
|
||||
* The singleton instance of the helper
|
||||
*/
|
||||
|
@ -76,14 +81,14 @@ import android.view.ViewParent;
|
|||
private View _currentMotionOriginalSource;
|
||||
|
||||
// To trace the pointer events (dispatchTouchEvent and dispatchGenericMotionEvent),
|
||||
// uncomment this and then uncomment logs in the method itself.
|
||||
// uncomment this and then uncomment logs in the method itself (Replace all "// Log" by "Log").
|
||||
/*
|
||||
private static String _indent = "";
|
||||
public boolean dispatchMotionEvent(Uno.UI.MotionTargetAdapter adapter, MotionEvent event)
|
||||
{
|
||||
final ViewGroup view = adapter.asViewGroup();
|
||||
final String originalIndent = _indent;
|
||||
Log.i(LOGTAG, _indent + " + " + view.toString() + "(" + System.identityHashCode(this) + ") " +
|
||||
Log.i(LOGTAG, _indent + " + " + view.toString() + "(" + System.identityHashCode(view) + ") " +
|
||||
"[evt: " + String.format("%.2f", event.getX()) + "," + String.format("%.2f", event.getY()) + " | size: " + view.getWidth() + "x" + view.getHeight() + " | scroll: x="+ view.getScrollX() + " y=" + view.getScrollY() + "]");
|
||||
_indent += " | ";
|
||||
Log.i(LOGTAG, _indent + event.toString());
|
||||
|
@ -137,7 +142,7 @@ import android.view.ViewParent;
|
|||
_currentMotionOriginalSource = null;
|
||||
|
||||
final boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
|
||||
final boolean isBeginningOfSequence = isDown || event.getAction() == MotionEvent.ACTION_HOVER_ENTER;
|
||||
final boolean isBeginningOfSequence = isDown || event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == STYLUS_WITH_BARREL_DOWN;
|
||||
if (isBeginningOfSequence) {
|
||||
// When we receive a pointer DOWN, we have to reset the down -> move -> up sequence,
|
||||
// so the dispatch will re-evaluate the _customDispatchTouchTarget;
|
||||
|
@ -164,7 +169,7 @@ import android.view.ViewParent;
|
|||
// (!dispatch.getIsTargetCachingSupported() || isDown), BUT if we do this, we may miss some HOVER_EXIT
|
||||
// So we prefer to not follow the UWP behavior (PointerEnter/Exit are raised only when entering/leaving
|
||||
// non clipped content) and get all the events.
|
||||
// Log.i(LOGTAG, _indent + "BLOCKED (not in view due to clipping)");
|
||||
// Log.i(LOGTAG, _indent + "BLOCKED (not in view due to clipping, or invalid dispatch comming from custom)");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -325,6 +330,8 @@ import android.view.ViewParent;
|
|||
|
||||
final boolean handled = dispatchStaticTransformedMotionEvent(adapter, currentTarget, true, event);
|
||||
if (handled || adapter.getIsStrongSequence()) {
|
||||
// Log.i(LOGTAG, _indent + "Custom dispatched to current target " + currentTarget.toString() + " [handled: " + handled + " | isStrongSequence: " + adapter.getIsStrongSequence() + "]");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -337,16 +344,23 @@ import android.view.ViewParent;
|
|||
|
||||
// Same check as native "canViewReceivePointerEvents"
|
||||
if (child == currentTarget || child.getVisibility() != View.VISIBLE || child.getAnimation() != null) {
|
||||
// Log.i(LOGTAG, _indent + "Custom dispatch ignored child #" + i + " (" + child.toString() + ") [isCurrent: " + (child == currentTarget) + " | visibility: " + child.getVisibility() + " | isAnimating: " + (child.getAnimation() != null) + "]");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dispatchStaticTransformedMotionEvent(adapter, child, false, event)) {
|
||||
// Log.i(LOGTAG, _indent + "Custom dispatch is setting child #" + i + " (" + child.toString() + ") as current target.");
|
||||
|
||||
adapter.setCurrentTarget(child); // (try to) cache the child for future events
|
||||
return true; // Stop at the first child which is able to handle the event
|
||||
} else {
|
||||
// Log.i(LOGTAG, _indent + "Custom dispatch tried child #" + i + " (" + child.toString() + ") but dispatch return false.");
|
||||
}
|
||||
}
|
||||
|
||||
// No target found ...
|
||||
// Log.i(LOGTAG, _indent + "Custom dispatch was not able to find a target child, current is being cleared.");
|
||||
adapter.setCurrentTarget(null);
|
||||
return false;
|
||||
}
|
||||
|
@ -400,7 +414,7 @@ import android.view.ViewParent;
|
|||
// with an implicit capture (i.e. down->move->up), OR the pointer is over the target.
|
||||
// In both cases, we have to dispatch the motion event to it, and propagates its handling result.
|
||||
|
||||
// Log.i(LOGTAG, _indent + "Dispatching to child " + child.toString() + " [evt: " + String.format("%.2f", transformedEvent.getX()) + "," + String.format("%.2f", transformedEvent.getY()) + " | isSequenceContinuation: " + isSequenceContinuation + " | inView: " + isMotionInView(transformedEvent, child) + "]");
|
||||
// Log.i(LOGTAG, _indent + "Dispatching to child " + child.toString() + " [evt: " + String.format("%.2f", transformedEvent.getX()) + "," + String.format("%.2f", transformedEvent.getY()) + " | isSequenceContinuation: " + isSequenceContinuation + " | isStrongSequence: " + adapter.getIsStrongSequence() + " | inView: " + isMotionInView(transformedEvent, child) + "]");
|
||||
|
||||
return adapter.dispatchToChild(child, transformedEvent);
|
||||
} else if (isSequenceContinuation) {
|
||||
|
@ -454,6 +468,9 @@ import android.view.ViewParent;
|
|||
case MotionEvent.ACTION_HOVER_ENTER:
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_HOVER_EXIT:
|
||||
case STYLUS_WITH_BARREL_DOWN:
|
||||
case STYLUS_WITH_BARREL_MOVE:
|
||||
case STYLUS_WITH_BARREL_UP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -157,6 +157,9 @@
|
|||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
</None>
|
||||
<None Update="Windows_UI_Xaml_Data\xBindTests\Controls\Binding_Nullable.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
<None Update="Windows_UI_Xaml_Data\xBindTests\Controls\Binding_Control.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</None>
|
||||
|
|
|
@ -11,6 +11,7 @@ using Windows.UI.Input;
|
|||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Uno.Disposables;
|
||||
using Point = Windows.Foundation.Point;
|
||||
using static Uno.UI.Tests.Windows_UI_Input.GestureRecognizerTestExtensions;
|
||||
|
||||
|
@ -188,6 +189,108 @@ namespace Uno.UI.Tests.Windows_UI_Input
|
|||
taps.Should().BeEquivalentTo(Tap(25, 25));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
taps.Should().BeEmpty();
|
||||
|
||||
sut.CanBeDoubleTap(GetPoint(28, 28)).Should().BeFalse();
|
||||
sut.ProcessUpEvent(27, 27);
|
||||
taps.Should().BeEquivalentTo(RightTap(25, 25));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped_Duration()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
taps.Should().BeEmpty();
|
||||
|
||||
sut.CanBeDoubleTap(GetPoint(28, 28)).Should().BeFalse();
|
||||
sut.ProcessUpEvent(27, 27, ts: long.MaxValue); // No matter the duration for mouse
|
||||
taps.Should().BeEquivalentTo(RightTap(25, 25));
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped_Delta_X()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
sut.ProcessUpEvent(25 + GestureRecognizer.TapMaxXDelta + 1, 25); // Moved too far
|
||||
|
||||
taps.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped_Delta_X_Back_Over()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
sut.ProcessMoveEvent(25 + GestureRecognizer.TapMaxXDelta + 1, 25); // Moved too far
|
||||
sut.ProcessUpEvent(25, 25); // Release over
|
||||
|
||||
taps.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped_Delta_Y()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
sut.ProcessUpEvent(25, 25 + GestureRecognizer.TapMaxXDelta + 1); // Moved too far
|
||||
|
||||
taps.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RightTapped_Delta_Y_Back_Over()
|
||||
{
|
||||
var sut = new GestureRecognizer { GestureSettings = GestureSettings.RightTap };
|
||||
var taps = new List<RightTappedEventArgs>();
|
||||
sut.RightTapped += (snd, e) => taps.Add(e);
|
||||
|
||||
using (MouseRight())
|
||||
{
|
||||
sut.ProcessDownEvent(25, 25);
|
||||
sut.ProcessMoveEvent(25, 25 + GestureRecognizer.TapMaxXDelta + 1); // Moved too far
|
||||
sut.ProcessUpEvent(25, 25); // Release over
|
||||
|
||||
taps.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Manipulation_Begin()
|
||||
{
|
||||
|
@ -1005,6 +1108,57 @@ namespace Uno.UI.Tests.Windows_UI_Input
|
|||
{
|
||||
private static long _frameId = 0;
|
||||
|
||||
public static PointerDevice MousePointer { get; } = new PointerDevice(PointerDeviceType.Mouse);
|
||||
public static PointerDevice PenPointer { get; } = new PointerDevice(PointerDeviceType.Pen);
|
||||
public static PointerDevice TouchPointer { get; } = new PointerDevice(PointerDeviceType.Touch);
|
||||
|
||||
public static PointerPointProperties LeftButton { get; } = new PointerPointProperties
|
||||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = true,
|
||||
IsLeftButtonPressed = true
|
||||
};
|
||||
|
||||
public static PointerPointProperties RightButton { get; } = new PointerPointProperties
|
||||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = true,
|
||||
IsRightButtonPressed = true
|
||||
};
|
||||
|
||||
public static PointerPointProperties LeftBarrelButton { get; } = new PointerPointProperties
|
||||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = true,
|
||||
IsLeftButtonPressed = true,
|
||||
IsBarrelButtonPressed = true
|
||||
};
|
||||
|
||||
public static PointerPointProperties RightBarrelButton { get; } = new PointerPointProperties
|
||||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = true,
|
||||
IsRightButtonPressed = true,
|
||||
IsBarrelButtonPressed = true
|
||||
};
|
||||
|
||||
private static readonly AsyncLocal<(PointerDevice device, PointerPointProperties properties)> _currentPointer = new AsyncLocal<(PointerDevice device, PointerPointProperties properties)>();
|
||||
public static IDisposable Pointer(PointerDevice device, PointerPointProperties properties)
|
||||
{
|
||||
_currentPointer.Value = (device, properties);
|
||||
|
||||
return Disposable.Create(() =>
|
||||
{
|
||||
_currentPointer.Value = default;
|
||||
});
|
||||
}
|
||||
|
||||
public static IDisposable Mouse() => Pointer(MousePointer, LeftButton);
|
||||
public static IDisposable MouseRight() => Pointer(MousePointer, RightButton);
|
||||
public static IDisposable Pen() => Pointer(PenPointer, LeftButton);
|
||||
public static IDisposable PenWithBarrel() => Pointer(PenPointer, RightBarrelButton);
|
||||
public static IDisposable Touch() => Pointer(TouchPointer, LeftButton);
|
||||
|
||||
public static PointerPoint GetPoint(
|
||||
double x,
|
||||
double y,
|
||||
|
@ -1014,23 +1168,24 @@ namespace Uno.UI.Tests.Windows_UI_Input
|
|||
bool? isInContact = true,
|
||||
PointerPointProperties properties = null)
|
||||
{
|
||||
var currentPointer = _currentPointer.Value;
|
||||
var frameId = (uint)Interlocked.Increment(ref _frameId);
|
||||
id = id ?? 1;
|
||||
ts = ts ?? frameId;
|
||||
var pointer = new PointerDevice(device ?? PointerDeviceType.Touch);
|
||||
var pointer = device.HasValue
|
||||
? new PointerDevice(device.Value)
|
||||
: (currentPointer.device ?? new PointerDevice(PointerDeviceType.Touch));
|
||||
var location = new Windows.Foundation.Point(x, y);
|
||||
properties = properties ?? new PointerPointProperties
|
||||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = true,
|
||||
IsLeftButtonPressed = true,
|
||||
};
|
||||
properties = properties ?? currentPointer.properties ?? LeftButton;
|
||||
|
||||
return new PointerPoint(frameId, ts.Value, pointer, id.Value, location, location, isInContact.GetValueOrDefault(), properties);
|
||||
}
|
||||
|
||||
public static TappedEventArgs Tap(double x, double y, uint tapCount = 1, PointerDeviceType? device = null)
|
||||
=> new TappedEventArgs(device ?? PointerDeviceType.Touch, new Point(x, y), tapCount);
|
||||
=> new TappedEventArgs(device ?? _currentPointer.Value.device?.PointerDeviceType ?? PointerDeviceType.Touch, new Point(x, y), tapCount);
|
||||
|
||||
public static RightTappedEventArgs RightTap(double x, double y, PointerDeviceType? device = null)
|
||||
=> new RightTappedEventArgs(device ?? _currentPointer.Value.device?.PointerDeviceType ?? PointerDeviceType.Touch, new Point(x, y));
|
||||
|
||||
public static void ProcessDownEvent(
|
||||
this GestureRecognizer sut,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<Page x:Class="Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests.Controls.Binding_Nullable"
|
||||
xmlns:sys="using:System"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<CheckBox x:Name="myCheckBox"
|
||||
x:FieldModifier="public" />
|
||||
<TextBlock x:Name="_NullableBinding"
|
||||
x:FieldModifier="public"
|
||||
Text="{x:Bind myCheckBox.IsChecked.Value, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Binding_Nullable : Page
|
||||
{
|
||||
public Binding_Nullable()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -159,5 +159,56 @@ namespace Uno.UI.Tests.Windows_UI_Xaml_Data.xBindTests
|
|||
Assert.AreEqual(4, SUT.AddIntCallCount);
|
||||
Assert.AreEqual(4, SUT.OffsetCallCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void When_NullableProperty()
|
||||
{
|
||||
var SUT = new Binding_Nullable();
|
||||
|
||||
Assert.IsNull(SUT._NullableBinding.Text);
|
||||
|
||||
SUT.ForceLoaded();
|
||||
|
||||
Assert.AreEqual("False", SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = true;
|
||||
|
||||
Assert.AreEqual("True", SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = false;
|
||||
|
||||
Assert.AreEqual("False", SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = null;
|
||||
|
||||
Assert.AreEqual("False", SUT._NullableBinding.Text);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void When_NullableProperty_And_ThreeState()
|
||||
{
|
||||
var SUT = new Binding_Nullable();
|
||||
SUT.myCheckBox.IsThreeState = true;
|
||||
SUT.myCheckBox.IsChecked = null;
|
||||
|
||||
Assert.IsNull(SUT._NullableBinding.Text);
|
||||
|
||||
SUT.ForceLoaded();
|
||||
|
||||
Assert.IsNull(SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = true;
|
||||
|
||||
Assert.AreEqual("True", SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = false;
|
||||
|
||||
Assert.AreEqual("False", SUT._NullableBinding.Text);
|
||||
|
||||
SUT.myCheckBox.IsChecked = null;
|
||||
|
||||
Assert.AreEqual("False", SUT._NullableBinding.Text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ declare namespace Uno.UI {
|
|||
*/
|
||||
setStyle(elementId: number, styles: {
|
||||
[name: string]: string;
|
||||
}, setAsArranged?: boolean, clipToBounds?: boolean): string;
|
||||
}): string;
|
||||
/**
|
||||
* Set the CSS style of a html element.
|
||||
*
|
||||
|
@ -267,18 +267,13 @@ declare namespace Uno.UI {
|
|||
*
|
||||
*/
|
||||
setStyleDoubleNative(pParams: number): boolean;
|
||||
setArrangeProperties(elementId: number, clipToBounds: boolean): string;
|
||||
/**
|
||||
* Set the CSS style of a html element.
|
||||
*
|
||||
* To remove a value, set it to empty string.
|
||||
* @param styles A dictionary of styles to apply on html element.
|
||||
* Remove the CSS style of a html element.
|
||||
*/
|
||||
resetStyle(elementId: number, names: string[]): string;
|
||||
/**
|
||||
* Set the CSS style of a html element.
|
||||
*
|
||||
* To remove a value, set it to empty string.
|
||||
* @param styles A dictionary of styles to apply on html element.
|
||||
* Remove the CSS style of a html element.
|
||||
*/
|
||||
resetStyleNative(pParams: number): boolean;
|
||||
private resetStyleInternal;
|
||||
|
@ -695,10 +690,8 @@ declare class WindowManagerSetStyleDoubleParams {
|
|||
}
|
||||
declare class WindowManagerSetStylesParams {
|
||||
HtmlId: number;
|
||||
SetAsArranged: boolean;
|
||||
Pairs_Length: number;
|
||||
Pairs: Array<string>;
|
||||
ClipToBounds: boolean;
|
||||
static unmarshal(pData: number): WindowManagerSetStylesParams;
|
||||
}
|
||||
declare class WindowManagerSetXUidParams {
|
||||
|
|
|
@ -641,19 +641,13 @@ var Uno;
|
|||
* To remove a value, set it to empty string.
|
||||
* @param styles A dictionary of styles to apply on html element.
|
||||
*/
|
||||
setStyle(elementId, styles, setAsArranged = false, clipToBounds) {
|
||||
setStyle(elementId, styles) {
|
||||
const element = this.getView(elementId);
|
||||
for (const style in styles) {
|
||||
if (styles.hasOwnProperty(style)) {
|
||||
element.style.setProperty(style, styles[style]);
|
||||
}
|
||||
}
|
||||
if (setAsArranged) {
|
||||
this.setAsArranged(element);
|
||||
}
|
||||
if (typeof clipToBounds === "boolean") {
|
||||
this.setClipToBounds(element, clipToBounds);
|
||||
}
|
||||
return "ok";
|
||||
}
|
||||
/**
|
||||
|
@ -672,10 +666,6 @@ var Uno;
|
|||
const value = pairs[i + 1];
|
||||
elementStyle.setProperty(key, value);
|
||||
}
|
||||
if (params.SetAsArranged) {
|
||||
this.setAsArranged(element);
|
||||
}
|
||||
this.setClipToBounds(element, params.ClipToBounds);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
|
@ -688,21 +678,21 @@ var Uno;
|
|||
element.style.setProperty(params.Name, String(params.Value));
|
||||
return true;
|
||||
}
|
||||
setArrangeProperties(elementId, clipToBounds) {
|
||||
const element = this.getView(elementId);
|
||||
this.setAsArranged(element);
|
||||
this.setClipToBounds(element, clipToBounds);
|
||||
return "ok";
|
||||
}
|
||||
/**
|
||||
* Set the CSS style of a html element.
|
||||
*
|
||||
* To remove a value, set it to empty string.
|
||||
* @param styles A dictionary of styles to apply on html element.
|
||||
* Remove the CSS style of a html element.
|
||||
*/
|
||||
resetStyle(elementId, names) {
|
||||
this.resetStyleInternal(elementId, names);
|
||||
return "ok";
|
||||
}
|
||||
/**
|
||||
* Set the CSS style of a html element.
|
||||
*
|
||||
* To remove a value, set it to empty string.
|
||||
* @param styles A dictionary of styles to apply on html element.
|
||||
* Remove the CSS style of a html element.
|
||||
*/
|
||||
resetStyleNative(pParams) {
|
||||
const params = WindowManagerResetStyleParams.unmarshal(pParams);
|
||||
|
@ -781,7 +771,7 @@ var Uno;
|
|||
const element = this.getView(params.HtmlId);
|
||||
var style = element.style;
|
||||
style.transform = `matrix(${params.M11},${params.M12},${params.M21},${params.M22},${params.M31},${params.M32})`;
|
||||
element.classList.remove(WindowManager.unoUnarrangedClassName);
|
||||
this.setAsArranged(element);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
|
@ -1000,7 +990,7 @@ var Uno;
|
|||
}
|
||||
src = src.parentElement;
|
||||
}
|
||||
return `${evt.pointerId};${evt.clientX};${evt.clientY};${(evt.ctrlKey ? "1" : "0")};${(evt.shiftKey ? "1" : "0")};${evt.buttons};${evt.button};${evt.pointerType};${srcHandle};${evt.timeStamp}`;
|
||||
return `${evt.pointerId};${evt.clientX};${evt.clientY};${(evt.ctrlKey ? "1" : "0")};${(evt.shiftKey ? "1" : "0")};${evt.buttons};${evt.button};${evt.pointerType};${srcHandle};${evt.timeStamp};${evt.pressure}`;
|
||||
}
|
||||
/**
|
||||
* keyboard event extractor to be used with registerEventOnView
|
||||
|
@ -2214,13 +2204,10 @@ class WindowManagerSetStylesParams {
|
|||
ret.HtmlId = Number(Module.getValue(pData + 0, "*"));
|
||||
}
|
||||
{
|
||||
ret.SetAsArranged = Boolean(Module.getValue(pData + 4, "i32"));
|
||||
ret.Pairs_Length = Number(Module.getValue(pData + 4, "i32"));
|
||||
}
|
||||
{
|
||||
ret.Pairs_Length = Number(Module.getValue(pData + 8, "i32"));
|
||||
}
|
||||
{
|
||||
var pArray = Module.getValue(pData + 12, "*");
|
||||
var pArray = Module.getValue(pData + 8, "*");
|
||||
if (pArray !== 0) {
|
||||
ret.Pairs = new Array();
|
||||
for (var i = 0; i < ret.Pairs_Length; i++) {
|
||||
|
@ -2237,9 +2224,6 @@ class WindowManagerSetStylesParams {
|
|||
ret.Pairs = null;
|
||||
}
|
||||
}
|
||||
{
|
||||
ret.ClipToBounds = Boolean(Module.getValue(pData + 16, "i32"));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -891,7 +891,7 @@
|
|||
src = src.parentElement;
|
||||
}
|
||||
|
||||
return `${evt.pointerId};${evt.clientX};${evt.clientY};${(evt.ctrlKey ? "1" : "0")};${(evt.shiftKey ? "1" : "0")};${evt.buttons};${evt.button};${evt.pointerType};${srcHandle};${evt.timeStamp}`;
|
||||
return `${evt.pointerId};${evt.clientX};${evt.clientY};${(evt.ctrlKey ? "1" : "0")};${(evt.shiftKey ? "1" : "0")};${evt.buttons};${evt.button};${evt.pointerType};${srcHandle};${evt.timeStamp};${evt.pressure}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,10 +3,8 @@ class WindowManagerSetStylesParams
|
|||
{
|
||||
/* Pack=4 */
|
||||
HtmlId : number;
|
||||
SetAsArranged : boolean;
|
||||
Pairs_Length : number;
|
||||
Pairs : Array<string>;
|
||||
ClipToBounds : boolean;
|
||||
public static unmarshal(pData:number) : WindowManagerSetStylesParams
|
||||
{
|
||||
let ret = new WindowManagerSetStylesParams();
|
||||
|
@ -16,15 +14,11 @@ class WindowManagerSetStylesParams
|
|||
}
|
||||
|
||||
{
|
||||
ret.SetAsArranged = Boolean(Module.getValue(pData + 4, "i32"));
|
||||
ret.Pairs_Length = Number(Module.getValue(pData + 4, "i32"));
|
||||
}
|
||||
|
||||
{
|
||||
ret.Pairs_Length = Number(Module.getValue(pData + 8, "i32"));
|
||||
}
|
||||
|
||||
{
|
||||
var pArray = Module.getValue(pData + 12, "*");
|
||||
var pArray = Module.getValue(pData + 8, "*");
|
||||
if(pArray !== 0)
|
||||
{
|
||||
ret.Pairs = new Array<string>();
|
||||
|
@ -48,10 +42,6 @@ class WindowManagerSetStylesParams
|
|||
ret.Pairs = null;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret.ClipToBounds = Boolean(Module.getValue(pData + 16, "i32"));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,6 +306,14 @@ namespace Uno.UI.DataBinding
|
|||
return attachedPropertyGetter.ReturnType;
|
||||
}
|
||||
|
||||
if(type.IsPrimitive && property == "Value")
|
||||
{
|
||||
// This case is trying assuming that Value for a primitive is used for the case
|
||||
// of a Nullable primitive.
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
_log.ErrorFormat("The [{0}] property getter does not exist on type [{1}]", property, type);
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -7,59 +7,6 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
#endif
|
||||
public partial class ButtonBase : global::Windows.UI.Xaml.Controls.ContentControl
|
||||
{
|
||||
// Skipping already declared property CommandParameter
|
||||
// Skipping already declared property Command
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.UI.Xaml.Controls.ClickMode ClickMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return (global::Windows.UI.Xaml.Controls.ClickMode)this.GetValue(ClickModeProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.SetValue(ClickModeProperty, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Skipping already declared property IsPointerOver
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public bool IsPressed
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)this.GetValue(IsPressedProperty);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.DependencyProperty ClickModeProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"ClickMode", typeof(global::Windows.UI.Xaml.Controls.ClickMode),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.ButtonBase),
|
||||
new FrameworkPropertyMetadata(default(global::Windows.UI.Xaml.Controls.ClickMode)));
|
||||
#endif
|
||||
// Skipping already declared property CommandParameterProperty
|
||||
// Skipping already declared property CommandProperty
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.DependencyProperty IsPointerOverProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"IsPointerOver", typeof(bool),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.ButtonBase),
|
||||
new FrameworkPropertyMetadata(default(bool)));
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.DependencyProperty IsPressedProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"IsPressed", typeof(bool),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.ButtonBase),
|
||||
new FrameworkPropertyMetadata(default(bool)));
|
||||
#endif
|
||||
// Skipping already declared method Windows.UI.Xaml.Controls.Primitives.ButtonBase.ButtonBase()
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.ButtonBase.ButtonBase()
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.ButtonBase.ClickMode.get
|
||||
|
|
|
@ -2,68 +2,4 @@
|
|||
#pragma warning disable 114 // new keyword hiding
|
||||
namespace Windows.UI.Xaml.Controls.Primitives
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
#endif
|
||||
public partial class RepeatButton : global::Windows.UI.Xaml.Controls.Primitives.ButtonBase
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public int Interval
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)this.GetValue(IntervalProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.SetValue(IntervalProperty, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public int Delay
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)this.GetValue(DelayProperty);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.SetValue(DelayProperty, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.DependencyProperty DelayProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"Delay", typeof(int),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.RepeatButton),
|
||||
new FrameworkPropertyMetadata(default(int)));
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.DependencyProperty IntervalProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"Interval", typeof(int),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.RepeatButton),
|
||||
new FrameworkPropertyMetadata(default(int)));
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public RepeatButton() : base()
|
||||
{
|
||||
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.Controls.Primitives.RepeatButton", "RepeatButton.RepeatButton()");
|
||||
}
|
||||
#endif
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.RepeatButton()
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.Delay.get
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.Delay.set
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.Interval.get
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.Interval.set
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.DelayProperty.get
|
||||
// Forced skipping of method Windows.UI.Xaml.Controls.Primitives.RepeatButton.IntervalProperty.get
|
||||
}
|
||||
}
|
||||
|
|
|
@ -567,7 +567,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.UI.Xaml.Controls.Control", "void Control.OnHolding(HoldingRoutedEventArgs e)");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
protected virtual void OnRightTapped( global::Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e)
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Windows.UI.Xaml.Input
|
|||
#endif
|
||||
public partial class KeyRoutedEventArgs : global::Windows.UI.Xaml.RoutedEventArgs
|
||||
{
|
||||
#if false || false || NET461 || false || false
|
||||
#if false || false || false || false || false
|
||||
[global::Uno.NotImplemented]
|
||||
public bool Handled
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ namespace Windows.UI.Xaml.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if false || false || NET461 || false || false
|
||||
#if false || false || false || false || false
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.System.VirtualKey Key
|
||||
{
|
||||
|
@ -43,16 +43,6 @@ namespace Windows.UI.Xaml.Input
|
|||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.System.VirtualKey OriginalKey
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member VirtualKey KeyRoutedEventArgs.OriginalKey is not implemented in Uno.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public string DeviceId
|
||||
{
|
||||
get
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#pragma warning disable 114 // new keyword hiding
|
||||
namespace Windows.UI.Xaml.Input
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
public delegate void RightTappedEventHandler(object @sender, global::Windows.UI.Xaml.Input.RightTappedRoutedEventArgs @e);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
#pragma warning disable 114 // new keyword hiding
|
||||
namespace Windows.UI.Xaml.Input
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
#endif
|
||||
public partial class RightTappedRoutedEventArgs : global::Windows.UI.Xaml.RoutedEventArgs
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public bool Handled
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ namespace Windows.UI.Xaml.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.Devices.Input.PointerDeviceType PointerDeviceType
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace Windows.UI.Xaml.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public RightTappedRoutedEventArgs() : base()
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ namespace Windows.UI.Xaml.Input
|
|||
// Forced skipping of method Windows.UI.Xaml.Input.RightTappedRoutedEventArgs.PointerDeviceType.get
|
||||
// Forced skipping of method Windows.UI.Xaml.Input.RightTappedRoutedEventArgs.Handled.get
|
||||
// Forced skipping of method Windows.UI.Xaml.Input.RightTappedRoutedEventArgs.Handled.set
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.Foundation.Point GetPosition( global::Windows.UI.Xaml.UIElement relativeTo)
|
||||
{
|
||||
|
|
|
@ -715,7 +715,7 @@ namespace Windows.UI.Xaml
|
|||
typeof(global::Windows.UI.Xaml.UIElement),
|
||||
new FrameworkPropertyMetadata(default(global::Windows.UI.Xaml.Media.Projection)));
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public static global::Windows.UI.Xaml.RoutedEvent RightTappedEvent
|
||||
{
|
||||
|
@ -1627,7 +1627,7 @@ namespace Windows.UI.Xaml
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public event global::Windows.UI.Xaml.Input.RightTappedEventHandler RightTapped
|
||||
{
|
||||
|
|
|
@ -308,7 +308,7 @@ namespace Uno.UI.Helpers.WinUI
|
|||
}
|
||||
|
||||
static bool? s_isThemeShadowAvailable;
|
||||
bool IsThemeShadowAvailable()
|
||||
static public bool IsThemeShadowAvailable()
|
||||
{
|
||||
if (s_isThemeShadowAvailable == null)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
internal struct MathToken
|
||||
{
|
||||
public MathToken(MathTokenType t, char c)
|
||||
{
|
||||
Type = t;
|
||||
Char = c;
|
||||
Value = double.NaN;
|
||||
}
|
||||
|
||||
public MathToken(MathTokenType t, double d)
|
||||
{
|
||||
Type = t;
|
||||
Char = '\0';
|
||||
Value = d;
|
||||
}
|
||||
|
||||
public MathTokenType Type { get; }
|
||||
|
||||
public char Char { get; }
|
||||
|
||||
public double Value { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
internal enum MathTokenType
|
||||
{
|
||||
Numeric,
|
||||
Operator,
|
||||
Parenthesis,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
#pragma warning disable 109
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Uno.UI.Helpers.WinUI;
|
||||
using Windows.Globalization.NumberFormatting;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
|
||||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public partial class NumberBox : Control
|
||||
{
|
||||
public double Minimum
|
||||
{
|
||||
get => (double)GetValue(MinimumProperty);
|
||||
set => SetValue(MinimumProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MinimumProperty =
|
||||
DependencyProperty.Register(
|
||||
name: nameof(Minimum),
|
||||
propertyType: typeof(double),
|
||||
ownerType: typeof(NumberBox),
|
||||
typeMetadata: new PropertyMetadata(
|
||||
defaultValue: -double.MaxValue,
|
||||
propertyChangedCallback: (s, e) => (s as NumberBox)?.OnMinimumPropertyChanged(e)));
|
||||
|
||||
|
||||
public double Maximum
|
||||
{
|
||||
get => (double)GetValue(MaximumProperty);
|
||||
set => SetValue(MaximumProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MaximumProperty =
|
||||
DependencyProperty.Register("Maximum", typeof(double), typeof(NumberBox), new PropertyMetadata(double.MaxValue, (s, e) => (s as NumberBox)?.OnMaximumPropertyChanged(e)));
|
||||
|
||||
public double Value
|
||||
{
|
||||
get => (double)GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ValueProperty =
|
||||
DependencyProperty.Register("Value", typeof(double), typeof(NumberBox), new PropertyMetadata(0.0, (s, e) => (s as NumberBox)?.OnValuePropertyChanged(e)));
|
||||
|
||||
|
||||
|
||||
public double SmallChange
|
||||
{
|
||||
get => (double)GetValue(SmallChangeProperty);
|
||||
set => SetValue(SmallChangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SmallChangeProperty =
|
||||
DependencyProperty.Register("SmallChange", typeof(double), typeof(NumberBox), new PropertyMetadata(1.0, (s, e) => (s as NumberBox)?.OnSmallChangePropertyChanged(e)));
|
||||
|
||||
public double LargeChange
|
||||
{
|
||||
get => (double)GetValue(LargeChangeProperty);
|
||||
set => SetValue(LargeChangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LargeChangeProperty =
|
||||
DependencyProperty.Register("LargeChange", typeof(double), typeof(NumberBox), new PropertyMetadata(10.0));
|
||||
|
||||
public string Text
|
||||
{
|
||||
get => (string)GetValue(TextProperty);
|
||||
set => SetValue(TextProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TextProperty =
|
||||
DependencyProperty.Register("Text", typeof(string), typeof(NumberBox), new PropertyMetadata("", (s, e) => (s as NumberBox)?.OnTextPropertyChanged(e)));
|
||||
|
||||
// TextBox properties
|
||||
|
||||
public object Header
|
||||
{
|
||||
get => (object)GetValue(HeaderProperty);
|
||||
set => SetValue(HeaderProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HeaderProperty =
|
||||
DependencyProperty.Register("Header", typeof(object), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public DataTemplate HeaderTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(HeaderTemplateProperty);
|
||||
set => SetValue(HeaderTemplateProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HeaderTemplateProperty =
|
||||
DependencyProperty.Register("HeaderTemplate", typeof(DataTemplate), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public string PlaceholderText
|
||||
{
|
||||
get => (string)GetValue(PlaceholderTextProperty);
|
||||
set => SetValue(PlaceholderTextProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PlaceholderTextProperty =
|
||||
DependencyProperty.Register("PlaceholderText", typeof(string), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public FlyoutBase SelectionFlyout
|
||||
{
|
||||
get => (FlyoutBase)GetValue(SelectionFlyoutProperty);
|
||||
set => SetValue(SelectionFlyoutProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectionFlyoutProperty =
|
||||
DependencyProperty.Register("SelectionFlyout", typeof(FlyoutBase), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public SolidColorBrush SelectionHighlightColor
|
||||
{
|
||||
get => (SolidColorBrush)GetValue(SelectionHighlightColorProperty);
|
||||
set => SetValue(SelectionHighlightColorProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectionHighlightColorProperty =
|
||||
DependencyProperty.Register("SelectionHighlightColor", typeof(SolidColorBrush), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public TextReadingOrder TextReadingOrder
|
||||
{
|
||||
get => (TextReadingOrder)GetValue(TextReadingOrderProperty);
|
||||
set => SetValue(TextReadingOrderProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TextReadingOrderProperty =
|
||||
DependencyProperty.Register("TextReadingOrder", typeof(TextReadingOrder), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public bool PreventKeyboardDisplayOnProgrammaticFocus
|
||||
{
|
||||
get => (bool)GetValue(PreventKeyboardDisplayOnProgrammaticFocusProperty);
|
||||
set => SetValue(PreventKeyboardDisplayOnProgrammaticFocusProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PreventKeyboardDisplayOnProgrammaticFocusProperty =
|
||||
DependencyProperty.Register("PreventKeyboardDisplayOnProgrammaticFocus", typeof(bool), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public new object Description
|
||||
{
|
||||
get => (object)GetValue(DescriptionProperty);
|
||||
set => SetValue(DescriptionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DescriptionProperty =
|
||||
DependencyProperty.Register("Description", typeof(object), typeof(NumberBox), new PropertyMetadata(null));
|
||||
|
||||
public NumberBoxValidationMode ValidationMode
|
||||
{
|
||||
get => (NumberBoxValidationMode)GetValue(ValidationModeProperty);
|
||||
set => SetValue(ValidationModeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ValidationModeProperty =
|
||||
DependencyProperty.Register("ValidationMode", typeof(NumberBoxValidationMode), typeof(NumberBox), new PropertyMetadata(NumberBoxValidationMode.InvalidInputOverwritten, (s, e) => (s as NumberBox)?.OnValidationModePropertyChanged(e)));
|
||||
|
||||
public NumberBoxSpinButtonPlacementMode SpinButtonPlacementMode
|
||||
{
|
||||
get => (NumberBoxSpinButtonPlacementMode)GetValue(SpinButtonPlacementModeProperty);
|
||||
set => SetValue(SpinButtonPlacementModeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SpinButtonPlacementModeProperty =
|
||||
DependencyProperty.Register("SpinButtonPlacementMode", typeof(NumberBoxSpinButtonPlacementMode), typeof(NumberBox), new PropertyMetadata(NumberBoxSpinButtonPlacementMode.Hidden, (s, e) => (s as NumberBox)?.OnSpinButtonPlacementModePropertyChanged(e)));
|
||||
|
||||
public bool IsWrapEnabled
|
||||
{
|
||||
get => (bool)GetValue(IsWrapEnabledProperty);
|
||||
set => SetValue(IsWrapEnabledProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsWrapEnabledProperty =
|
||||
DependencyProperty.Register("IsWrapEnabled", typeof(bool), typeof(NumberBox), new PropertyMetadata(false, (s, e) => (s as NumberBox)?.OnIsWrapEnabledPropertyChanged(e)));
|
||||
|
||||
public bool AcceptsExpression
|
||||
{
|
||||
get => (bool)GetValue(AcceptsExpressionProperty);
|
||||
set => SetValue(AcceptsExpressionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty AcceptsExpressionProperty =
|
||||
DependencyProperty.Register("AcceptsExpression", typeof(bool), typeof(NumberBox), new PropertyMetadata(false /* ,UNO TODO (s, e) => (s as NumberBox)?.OnAcceptsExpressionPropertyChanged(e)*/));
|
||||
|
||||
public INumberFormatter2 NumberFormatter
|
||||
{
|
||||
get => (INumberFormatter2)GetValue(NumberFormatterProperty);
|
||||
set => SetValue(NumberFormatterProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty NumberFormatterProperty =
|
||||
DependencyProperty.Register("NumberFormatter", typeof(INumberFormatter2), typeof(NumberBox), new PropertyMetadata(null, (s, e) => (s as NumberBox)?.OnNumberFormatterPropertyChanged(e)));
|
||||
|
||||
public event Windows.Foundation.TypedEventHandler<NumberBox, NumberBoxValueChangedEventArgs> ValueChanged;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Uno.UI.Helpers.WinUI;
|
||||
using Windows.Globalization.NumberFormatting;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.Foundation.Metadata;
|
||||
using System.Globalization;
|
||||
using Uno.Disposables;
|
||||
|
||||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public partial class NumberBox : Control
|
||||
{
|
||||
bool m_valueUpdating = false;
|
||||
bool m_textUpdating = false;
|
||||
|
||||
SignificantDigitsNumberRounder m_displayRounder = new SignificantDigitsNumberRounder();
|
||||
|
||||
TextBox m_textBox;
|
||||
Windows.UI.Xaml.Controls.Primitives.Popup m_popup;
|
||||
|
||||
SerialDisposable _eventSubscriptions = new SerialDisposable();
|
||||
|
||||
static string c_numberBoxDownButtonName = "DownSpinButton";
|
||||
static string c_numberBoxUpButtonName = "UpSpinButton";
|
||||
static string c_numberBoxTextBoxName = "InputBox";
|
||||
// UNO TODO static string c_numberBoxPopupButtonName= "PopupButton";
|
||||
static string c_numberBoxPopupName = "UpDownPopup";
|
||||
static string c_numberBoxPopupDownButtonName = "PopupDownSpinButton";
|
||||
static string c_numberBoxPopupUpButtonName = "PopupUpSpinButton";
|
||||
// UNO TODO static string c_numberBoxPopupContentRootName= "PopupContentRoot";
|
||||
|
||||
// UNO TODO static double c_popupShadowDepth = 16.0;
|
||||
// UNO TODO static string c_numberBoxPopupShadowDepthName= "NumberBoxPopupShadowDepth";
|
||||
|
||||
// Shockingly, there is no standard function for trimming strings.
|
||||
const string c_whitespace = " \n\r\t\f\v";
|
||||
|
||||
private static string trim(string s)
|
||||
{
|
||||
IEnumerable<(char c, int i)> GetNonWhiteSpace()
|
||||
=> s.Select((c, i) => (c, i)).Where(p => !c_whitespace.Contains(p.c.ToString()));
|
||||
|
||||
var start = GetNonWhiteSpace().FirstOrDefault();
|
||||
var end = GetNonWhiteSpace().LastOrDefault();
|
||||
return (start.c == '\0' || end.c == '\0') ? "" : s.Substring(start.i, end.i - start.i + 1);
|
||||
}
|
||||
|
||||
public NumberBox()
|
||||
{
|
||||
// Default values for the number formatter
|
||||
var formatter = new DecimalFormatter();
|
||||
formatter.IntegerDigits = 1;
|
||||
formatter.FractionDigits = 0;
|
||||
NumberFormatter = formatter;
|
||||
|
||||
PointerWheelChanged += OnNumberBoxScroll;
|
||||
|
||||
GotFocus += OnNumberBoxGotFocus;
|
||||
LostFocus += OnNumberBoxLostFocus;
|
||||
|
||||
Loaded += (s, e) => InitializeTemplate();
|
||||
Unloaded += (s, e) => DisposeRegistrations();
|
||||
|
||||
DefaultStyleKey = this;
|
||||
}
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new NumberBoxAutomationPeer(this);
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
InitializeTemplate();
|
||||
}
|
||||
|
||||
private void InitializeTemplate()
|
||||
{
|
||||
_eventSubscriptions.Disposable = null;
|
||||
|
||||
var registrations = new CompositeDisposable();
|
||||
|
||||
var spinDownName = ResourceAccessor.GetLocalizedStringResource("NumberBoxDownSpinButtonName");
|
||||
var spinUpName = ResourceAccessor.GetLocalizedStringResource("NumberBoxUpSpinButtonName");
|
||||
|
||||
if (this.GetTemplateChild(c_numberBoxDownButtonName) is RepeatButton spinDown)
|
||||
{
|
||||
spinDown.Click += OnSpinDownClick;
|
||||
registrations.Add(() => spinDown.Click -= OnSpinDownClick);
|
||||
|
||||
// Do localization for the down button
|
||||
if (string.IsNullOrEmpty(AutomationProperties.GetName(spinDown)))
|
||||
{
|
||||
AutomationProperties.SetName(spinDown, spinDownName);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetTemplateChild(c_numberBoxUpButtonName) is RepeatButton spinUp)
|
||||
{
|
||||
spinUp.Click += OnSpinUpClick;
|
||||
registrations.Add(() => spinUp.Click -= OnSpinUpClick);
|
||||
|
||||
// Do localization for the up button
|
||||
if (string.IsNullOrEmpty(AutomationProperties.GetName(spinUp)))
|
||||
{
|
||||
AutomationProperties.SetName(spinUp, spinUpName);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetTemplateChild(c_numberBoxTextBoxName) is TextBox textBox)
|
||||
{
|
||||
textBox.KeyDown += OnNumberBoxKeyDown;
|
||||
registrations.Add(() => textBox.KeyDown -= OnNumberBoxKeyDown);
|
||||
textBox.KeyUp += OnNumberBoxKeyUp;
|
||||
registrations.Add(() => textBox.KeyUp -= OnNumberBoxKeyUp);
|
||||
|
||||
m_textBox = textBox;
|
||||
}
|
||||
|
||||
m_popup = GetTemplateChild(c_numberBoxPopupName) as Windows.UI.Xaml.Controls.Primitives.Popup;
|
||||
|
||||
if (SharedHelpers.IsThemeShadowAvailable())
|
||||
{
|
||||
// UNO TODO
|
||||
//if (GetTemplateChildT(c_numberBoxPopupContentRootName) is UIElement popupRoot)
|
||||
//{
|
||||
// if (!popupRoot.Shadow())
|
||||
// {
|
||||
// popupRoot.Shadow(ThemeShadow{});
|
||||
// auto&& translation = popupRoot.Translation();
|
||||
|
||||
// const double shadowDepth = unbox_value<double>(SharedHelpers.FindResource(c_numberBoxPopupShadowDepthName, Application.Current().Resources(), box_value(c_popupShadowDepth)));
|
||||
|
||||
// popupRoot.Translation({ translation.x, translation.y, (float)shadowDepth });
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
if (GetTemplateChild(c_numberBoxPopupDownButtonName) is RepeatButton popupSpinDown)
|
||||
{
|
||||
popupSpinDown.Click += OnSpinDownClick;
|
||||
registrations.Add(() => popupSpinDown.Click -= OnSpinDownClick);
|
||||
}
|
||||
|
||||
if (GetTemplateChild(c_numberBoxPopupUpButtonName) is RepeatButton popupSpinUp)
|
||||
{
|
||||
popupSpinUp.Click += OnSpinUpClick;
|
||||
registrations.Add(() => popupSpinUp.Click -= OnSpinUpClick);
|
||||
}
|
||||
|
||||
// .NET rounds to 12 significant digits when displaying doubles, so we will do the same.
|
||||
m_displayRounder.SignificantDigits = 12;
|
||||
|
||||
UpdateSpinButtonPlacement();
|
||||
UpdateSpinButtonEnabled();
|
||||
|
||||
if (ReadLocalValue(ValueProperty) == DependencyProperty.UnsetValue
|
||||
&& ReadLocalValue(TextProperty) != DependencyProperty.UnsetValue)
|
||||
{
|
||||
// If Text has been set, but Value hasn't, update Value based on Text.
|
||||
UpdateValueToText();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateTextToValue();
|
||||
}
|
||||
|
||||
_eventSubscriptions.Disposable = registrations;
|
||||
}
|
||||
|
||||
private void DisposeRegistrations()
|
||||
{
|
||||
_eventSubscriptions.Disposable = null;
|
||||
}
|
||||
|
||||
private void OnValuePropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
// This handler may change Value; don't send extra events in that case.
|
||||
if (!m_valueUpdating)
|
||||
{
|
||||
var oldValue = (double)args.OldValue;
|
||||
|
||||
try
|
||||
{
|
||||
m_valueUpdating = true;
|
||||
|
||||
CoerceValue();
|
||||
|
||||
var newValue = (double)Value;
|
||||
if (newValue != oldValue && !(double.IsNaN(newValue) && double.IsNaN(oldValue)))
|
||||
{
|
||||
// Fire ValueChanged event
|
||||
var valueChangedArgs = new NumberBoxValueChangedEventArgs(oldValue, newValue);
|
||||
ValueChanged?.Invoke(this, valueChangedArgs);
|
||||
|
||||
// Fire value property change for UIA
|
||||
if (FrameworkElementAutomationPeer.FromElement(this) is NumberBoxAutomationPeer peer)
|
||||
{
|
||||
peer.RaiseValueChangedEvent(oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTextToValue();
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_valueUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMinimumPropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
CoerceMaximum();
|
||||
CoerceValue();
|
||||
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
|
||||
private void OnMaximumPropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
CoerceMinimum();
|
||||
CoerceValue();
|
||||
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
|
||||
private void OnSmallChangePropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
|
||||
private void OnIsWrapEnabledPropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
|
||||
private void OnNumberFormatterPropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
// Update text with new formatting
|
||||
UpdateTextToValue();
|
||||
}
|
||||
|
||||
private void ValidateNumberFormatter(INumberFormatter2 value)
|
||||
{
|
||||
// NumberFormatter also needs to be an INumberParser
|
||||
if (!(value is INumberParser))
|
||||
{
|
||||
throw new ArgumentException(nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSpinButtonPlacementModePropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
UpdateSpinButtonPlacement();
|
||||
}
|
||||
|
||||
private void OnTextPropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (!m_textUpdating)
|
||||
{
|
||||
UpdateValueToText();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValueToText()
|
||||
{
|
||||
if (m_textBox != null)
|
||||
{
|
||||
m_textBox.Text = Text;
|
||||
ValidateInput();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidationModePropertyChanged(DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
ValidateInput();
|
||||
UpdateSpinButtonEnabled();
|
||||
}
|
||||
|
||||
private void OnNumberBoxGotFocus(object sender, RoutedEventArgs args)
|
||||
{
|
||||
// When the control receives focus, select the text
|
||||
if (m_textBox != null)
|
||||
{
|
||||
m_textBox.SelectAll();
|
||||
}
|
||||
|
||||
if (SpinButtonPlacementMode == NumberBoxSpinButtonPlacementMode.Compact)
|
||||
{
|
||||
if (m_popup != null)
|
||||
{
|
||||
m_popup.IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNumberBoxLostFocus(object sender, RoutedEventArgs args)
|
||||
{
|
||||
ValidateInput();
|
||||
|
||||
if (m_popup != null)
|
||||
{
|
||||
m_popup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CoerceMinimum()
|
||||
{
|
||||
var max = Maximum;
|
||||
if (Minimum > max)
|
||||
{
|
||||
Minimum = max;
|
||||
}
|
||||
}
|
||||
|
||||
private void CoerceMaximum()
|
||||
{
|
||||
var min = Minimum;
|
||||
if (Maximum < min)
|
||||
{
|
||||
Maximum = min;
|
||||
}
|
||||
}
|
||||
|
||||
private void CoerceValue()
|
||||
{
|
||||
// Validate that the value is in bounds
|
||||
var value = Value;
|
||||
if (!double.IsNaN(value) && !IsInBounds(value) && ValidationMode == NumberBoxValidationMode.InvalidInputOverwritten)
|
||||
{
|
||||
// Coerce value to be within range
|
||||
var max = Maximum;
|
||||
if (value > max)
|
||||
{
|
||||
Value = max;
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = Minimum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateInput()
|
||||
{
|
||||
// Validate the content of the inner textbox
|
||||
if (m_textBox != null)
|
||||
{
|
||||
var text = trim(m_textBox.Text);
|
||||
|
||||
// Handles empty TextBox case, set text to current value
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
Value = double.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Setting NumberFormatter to something that isn't an INumberParser will throw an exception, so this should be safe
|
||||
var numberParser = NumberFormatter as INumberParser;
|
||||
|
||||
var value = AcceptsExpression
|
||||
? NumberBoxParser.Compute(text, numberParser)
|
||||
: ApiInformation.IsTypePresent(numberParser?.GetType().FullName)
|
||||
? numberParser.ParseDouble(text)
|
||||
: double.TryParse(text, out var v)
|
||||
? (double?)v
|
||||
: null;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (ValidationMode == NumberBoxValidationMode.InvalidInputOverwritten)
|
||||
{
|
||||
// Override text to current value
|
||||
UpdateTextToValue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.Value == Value)
|
||||
{
|
||||
// Even if the value hasn't changed, we still want to update the text (e.g. Value is 3, user types 1 + 2, we want to replace the text with 3)
|
||||
UpdateTextToValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSpinDownClick(object sender, RoutedEventArgs args)
|
||||
{
|
||||
StepValue(-SmallChange);
|
||||
}
|
||||
|
||||
private void OnSpinUpClick(object sender, RoutedEventArgs args)
|
||||
{
|
||||
StepValue(SmallChange);
|
||||
}
|
||||
|
||||
private void OnNumberBoxKeyDown(object sender, KeyRoutedEventArgs args)
|
||||
{
|
||||
// Handle these on key down so that we get repeat behavior.
|
||||
switch (args.OriginalKey)
|
||||
{
|
||||
case VirtualKey.Up:
|
||||
StepValue(SmallChange);
|
||||
args.Handled = true;
|
||||
break;
|
||||
|
||||
case VirtualKey.Down:
|
||||
StepValue(-SmallChange);
|
||||
args.Handled = true;
|
||||
break;
|
||||
|
||||
case VirtualKey.PageUp:
|
||||
StepValue(LargeChange);
|
||||
args.Handled = true;
|
||||
break;
|
||||
|
||||
case VirtualKey.PageDown:
|
||||
StepValue(-LargeChange);
|
||||
args.Handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNumberBoxKeyUp(object sender, KeyRoutedEventArgs args)
|
||||
{
|
||||
switch (args.OriginalKey)
|
||||
{
|
||||
case VirtualKey.Enter:
|
||||
case VirtualKey.GamepadA:
|
||||
ValidateInput();
|
||||
args.Handled = true;
|
||||
break;
|
||||
|
||||
case VirtualKey.Escape:
|
||||
case VirtualKey.GamepadB:
|
||||
UpdateTextToValue();
|
||||
args.Handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNumberBoxScroll(object sender, PointerRoutedEventArgs args)
|
||||
{
|
||||
if (m_textBox != null)
|
||||
{
|
||||
if (m_textBox.FocusState != FocusState.Unfocused)
|
||||
{
|
||||
var delta = args.GetCurrentPoint(this).Properties.MouseWheelDelta;
|
||||
if (delta > 0)
|
||||
{
|
||||
StepValue(SmallChange);
|
||||
}
|
||||
else if (delta < 0)
|
||||
{
|
||||
StepValue(-SmallChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StepValue(double change)
|
||||
{
|
||||
// Before adjusting the value, validate the contents of the textbox so we don't override it.
|
||||
ValidateInput();
|
||||
|
||||
var newVal = Value;
|
||||
if (!double.IsNaN(newVal))
|
||||
{
|
||||
newVal += change;
|
||||
|
||||
if (IsWrapEnabled)
|
||||
{
|
||||
var max = Maximum;
|
||||
var min = Minimum;
|
||||
|
||||
if (newVal > max)
|
||||
{
|
||||
newVal = min;
|
||||
}
|
||||
else if (newVal < min)
|
||||
{
|
||||
newVal = max;
|
||||
}
|
||||
}
|
||||
|
||||
Value = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
// Updates TextBox.Text with the formatted Value
|
||||
private void UpdateTextToValue()
|
||||
{
|
||||
if (m_textBox != null)
|
||||
{
|
||||
string newText = "";
|
||||
|
||||
var value = Value;
|
||||
if (!double.IsNaN(value))
|
||||
{
|
||||
// Rounding the value here will prevent displaying digits caused by floating point imprecision.
|
||||
var roundedValue = m_displayRounder.RoundDouble(value);
|
||||
|
||||
if (ApiInformation.IsTypePresent(NumberFormatter.GetType().FullName))
|
||||
{
|
||||
newText = NumberFormatter.FormatDouble(roundedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
newText = roundedValue.ToString($"0." + new string('#', (int)m_displayRounder.SignificantDigits), CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
|
||||
m_textBox.Text = newText;
|
||||
|
||||
try
|
||||
{
|
||||
m_textUpdating = true;
|
||||
Text = newText;
|
||||
|
||||
// This places the caret at the end of the text.
|
||||
m_textBox.Select(newText.Length, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_textUpdating = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpinButtonPlacement()
|
||||
{
|
||||
var spinButtonMode = SpinButtonPlacementMode;
|
||||
|
||||
if (spinButtonMode == NumberBoxSpinButtonPlacementMode.Inline)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "SpinButtonsVisible", false);
|
||||
}
|
||||
else if (spinButtonMode == NumberBoxSpinButtonPlacementMode.Compact)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "SpinButtonsPopup", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "SpinButtonsCollapsed", false);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpinButtonEnabled()
|
||||
{
|
||||
var value = Value;
|
||||
bool isUpButtonEnabled = false;
|
||||
bool isDownButtonEnabled = false;
|
||||
|
||||
if (!double.IsNaN(value))
|
||||
{
|
||||
if (IsWrapEnabled || ValidationMode != NumberBoxValidationMode.InvalidInputOverwritten)
|
||||
{
|
||||
// If wrapping is enabled, or invalid values are allowed, then the buttons should be enabled
|
||||
isUpButtonEnabled = true;
|
||||
isDownButtonEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value < Maximum)
|
||||
{
|
||||
isUpButtonEnabled = true;
|
||||
}
|
||||
if (value > Minimum)
|
||||
{
|
||||
isDownButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VisualStateManager.GoToState(this, isUpButtonEnabled ? "UpSpinButtonEnabled" : "UpSpinButtonDisabled", false);
|
||||
VisualStateManager.GoToState(this, isDownButtonEnabled ? "DownSpinButtonEnabled" : "DownSpinButtonDisabled", false);
|
||||
}
|
||||
|
||||
private bool IsInBounds(double value)
|
||||
{
|
||||
return (value >= Minimum && value <= Maximum);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,455 @@
|
|||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:contract5Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent"
|
||||
xmlns:contract6Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent"
|
||||
xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent">
|
||||
|
||||
<Style TargetType="local:NumberBox">
|
||||
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}" />
|
||||
<!--UNO TODO <contract7Present:Setter Property="SelectionFlyout" Value="{StaticResource TextControlCommandBarSelectionFlyout}" /> -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:NumberBox">
|
||||
<Grid>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="SpinButtonStates">
|
||||
<VisualState x:Name="SpinButtonsCollapsed" />
|
||||
|
||||
<VisualState x:Name="SpinButtonsVisible">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DownSpinButton.Visibility" Value="Visible" />
|
||||
<Setter Target="UpSpinButton.Visibility" Value="Visible" />
|
||||
<!--UNO TODO <contract7Present:Setter Target="InputBox.CornerRadius" Value="{Binding Source={ThemeResource ControlCornerRadius}, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />-->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="SpinButtonsPopup">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="InputBox.Style" Value="{StaticResource NumberBoxTextBoxStyle}" />
|
||||
<!--UNO TODO <contract7Present:Setter Target="InputBox.CornerRadius" Value="{Binding Source={ThemeResource ControlCornerRadius}}" />-->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="UpSpinButtonEnabledStates">
|
||||
<VisualState x:Name="UpSpinButtonEnabled" />
|
||||
<VisualState x:Name="UpSpinButtonDisabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="UpSpinButton.IsEnabled" Value="False" />
|
||||
<Setter Target="PopupUpSpinButton.IsEnabled" Value="False" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DownSpinButtonEnabledStates">
|
||||
<VisualState x:Name="DownSpinButtonEnabled" />
|
||||
<VisualState x:Name="DownSpinButtonDisabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DownSpinButton.IsEnabled" Value="False" />
|
||||
<Setter Target="PopupDownSpinButton.IsEnabled" Value="False" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="TextControlBorderBrush"/>
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="TextControlBorderBrush"/>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="TextControlBorderBrush"/>
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="TextControlBorderBrush"/>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox x:Name="InputBox"
|
||||
Grid.Column="0"
|
||||
Header="{TemplateBinding Header}"
|
||||
HeaderTemplate="{TemplateBinding HeaderTemplate}"
|
||||
PlaceholderText="{TemplateBinding PlaceholderText}"
|
||||
SelectionHighlightColor="{TemplateBinding SelectionHighlightColor}"
|
||||
TextReadingOrder="{TemplateBinding TextReadingOrder}"
|
||||
PreventKeyboardDisplayOnProgrammaticFocus="{TemplateBinding PreventKeyboardDisplayOnProgrammaticFocus}"
|
||||
/>
|
||||
<!--
|
||||
UNO TODO
|
||||
contract7Present:SelectionFlyout="{TemplateBinding SelectionFlyout}"
|
||||
contract7Present:Description="{TemplateBinding Description}"-->
|
||||
|
||||
<Popup x:Name="UpDownPopup"
|
||||
Grid.Column="1"
|
||||
VerticalOffset="{ThemeResource NumberBoxPopupVerticalOffset}"
|
||||
HorizontalOffset="{ThemeResource NumberBoxPopupHorizonalOffset}"
|
||||
HorizontalAlignment="Left">
|
||||
|
||||
<Grid x:Name="PopupContentRoot"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
>
|
||||
<!-- UNO TODO contract7Present:CornerRadius="{ThemeResource OverlayCornerRadius}"-->
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<RepeatButton x:Name="PopupUpSpinButton"
|
||||
Style="{StaticResource NumberBoxPopupSpinButtonStyle}"
|
||||
Grid.Row="0"
|
||||
Content="" />
|
||||
<!-- UNO TODO Content=""-->
|
||||
|
||||
<RepeatButton x:Name="PopupDownSpinButton"
|
||||
Style="{StaticResource NumberBoxPopupSpinButtonStyle}"
|
||||
Grid.Row="1"
|
||||
Content=""/>
|
||||
<!-- UNO TODO Content=""-->
|
||||
</Grid>
|
||||
</Popup>
|
||||
|
||||
<RepeatButton x:Name="UpSpinButton"
|
||||
Grid.Column="1"
|
||||
Visibility="Collapsed"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
Content=""
|
||||
Style="{StaticResource NumberBoxSpinButtonStyle}"
|
||||
/>
|
||||
<!--Content=""-->
|
||||
<!-- UNO TODO contract7Present:CornerRadius="0"-->
|
||||
|
||||
<RepeatButton x:Name="DownSpinButton"
|
||||
Grid.Column="2"
|
||||
Visibility="Collapsed"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
Content=""
|
||||
Style="{StaticResource NumberBoxSpinButtonStyle}" />
|
||||
<!--Content=""-->
|
||||
<!-- UNO TODO contract7Present:CornerRadius="{Binding Source={ThemeResource ControlCornerRadius}, Converter={StaticResource RightCornerRadiusFilterConverter}}"-->
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Name="NumberBoxSpinButtonStyle" TargetType="RepeatButton">
|
||||
<Style.Setters>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="MinWidth" Value="34"/>
|
||||
<Setter Property="Height" Value="{ThemeResource TextControlThemeMinHeight}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Background" Value="{ThemeResource TextControlBackground}"/>
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource NumberBoxSpinButtonBorderThickness}"/>
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}"/>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style x:Name="NumberBoxPopupSpinButtonStyle" TargetType="RepeatButton">
|
||||
<Style.Setters>
|
||||
<Setter Property="AutomationProperties.AccessibilityView" Value="Raw"/>
|
||||
<Setter Property="IsTabStop" Value="False"/>
|
||||
<Setter Property="Width" Value="40"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="Background" Value="{ThemeResource TextControlBackground}"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FontFamily" Value="{ThemeResource SymbolThemeFontFamily}"/>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
|
||||
<Style x:Name="NumberBoxTextBoxStyle" TargetType="TextBox">
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextControlForeground}" />
|
||||
<Setter Property="Background" Value="{ThemeResource TextControlBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}" />
|
||||
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
|
||||
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}" />
|
||||
<contract6Present:Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
|
||||
<!--
|
||||
UNO TODO
|
||||
<contract7Present:Setter Property="ContextFlyout" Value="{StaticResource TextControlCommandBarContextFlyout}" />
|
||||
<contract7Present:Setter Property="SelectionFlyout" Value="{StaticResource TextControlCommandBarSelectionFlyout}" />
|
||||
-->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TextBox">
|
||||
<Grid>
|
||||
|
||||
<Grid.Resources>
|
||||
<Style x:Name="DeleteButtonStyle" TargetType="Button">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid x:Name="ButtonLayoutGrid"
|
||||
BorderBrush="{ThemeResource TextControlButtonBorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Background="{ThemeResource TextControlButtonBackground}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="ButtonLayoutGrid"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<TextBlock x:Name="GlyphElement"
|
||||
Foreground="{ThemeResource TextControlButtonForeground}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontStyle="Normal"
|
||||
FontSize="12"
|
||||
Text=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlHeaderForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<contract5Present:ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundDisabled}}" />
|
||||
</contract5Present:ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<contract5Present:ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundPointerOver}}" />
|
||||
</contract5Present:ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Focused">
|
||||
|
||||
<Storyboard>
|
||||
<contract5Present:ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundFocused}}" />
|
||||
</contract5Present:ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundFocused}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushFocused}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundFocused}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="RequestedTheme">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="Light" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="ButtonStates">
|
||||
<VisualState x:Name="ButtonVisible">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0">
|
||||
<DiscreteObjectKeyFrame.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</DiscreteObjectKeyFrame.Value>
|
||||
</DiscreteObjectKeyFrame>
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="ButtonCollapsed" />
|
||||
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
FontWeight="Normal"
|
||||
Foreground="{ThemeResource TextControlHeaderForeground}"
|
||||
Margin="{ThemeResource TextBoxTopHeaderMargin}"
|
||||
TextWrapping="Wrap"
|
||||
VerticalAlignment="Top"
|
||||
Visibility="Collapsed"
|
||||
x:DeferLoadStrategy="Lazy" />
|
||||
<Border x:Name="BorderElement"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.RowSpan="1"
|
||||
Grid.ColumnSpan="3"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
MinWidth="{ThemeResource TextControlThemeMinWidth}"
|
||||
MinHeight="{ThemeResource TextControlThemeMinHeight}" />
|
||||
<ScrollViewer x:Name="ContentElement"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
|
||||
Margin="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
IsTabStop="False"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
ZoomMode="Disabled" />
|
||||
<TextBlock x:Name="PlaceholderTextContentPresenter"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="{TemplateBinding BorderThickness}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Text="{TemplateBinding PlaceholderText}"
|
||||
TextAlignment="{TemplateBinding TextAlignment}"
|
||||
TextWrapping="{TemplateBinding TextWrapping}"
|
||||
IsHitTestVisible="False" />
|
||||
<Button x:Name="DeleteButton"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource DeleteButtonStyle}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Margin="{ThemeResource HelperButtonThemePadding}"
|
||||
IsTabStop="False"
|
||||
Visibility="Collapsed"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
MinWidth="34"
|
||||
VerticalAlignment="Stretch" />
|
||||
<ContentPresenter x:Name="DescriptionPresenter"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Content="{TemplateBinding Description}"
|
||||
Foreground="{ThemeResource SystemControlDescriptionTextForegroundBrush}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
x:Load="False"/>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource NumberBoxPopupIndicatorMargin}"
|
||||
Foreground="{ThemeResource NumberBoxPopupIndicatorForeground}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
AutomationProperties.AccessibilityView="Raw" />
|
||||
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI.Xaml.Automation;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Automation.Provider;
|
||||
|
||||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public class NumberBoxAutomationPeer : AutomationPeer, IRangeValueProvider
|
||||
{
|
||||
private NumberBox _owner;
|
||||
|
||||
internal NumberBoxAutomationPeer(NumberBox owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
// IAutomationPeerOverrides
|
||||
protected override object GetPatternCore(PatternInterface patternInterface)
|
||||
{
|
||||
if (patternInterface == PatternInterface.RangeValue)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return base.GetPatternCore(patternInterface);
|
||||
}
|
||||
|
||||
protected override AutomationControlType GetAutomationControlTypeCore()
|
||||
{
|
||||
return AutomationControlType.Spinner;
|
||||
}
|
||||
|
||||
NumberBox GetImpl()
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
|
||||
// IRangeValueProvider
|
||||
public double Minimum => GetImpl().Minimum;
|
||||
|
||||
public double Maximum => GetImpl().Maximum;
|
||||
|
||||
public double Value => GetImpl().Value;
|
||||
|
||||
public double SmallChange => GetImpl().SmallChange;
|
||||
|
||||
public double LargeChange => GetImpl().LargeChange;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void SetValue(double value) => GetImpl().Value = value;
|
||||
|
||||
internal void RaiseValueChangedEvent(double oldValue, double newValue)
|
||||
{
|
||||
if (ApiInformation.IsPropertyPresent("Windows.UI.Xaml.Automation.RangeValuePatternIdentifiers", nameof(RangeValuePatternIdentifiers.ValueProperty)))
|
||||
{
|
||||
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty,
|
||||
oldValue,
|
||||
newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.Globalization.NumberFormatting;
|
||||
|
||||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
internal class NumberBoxParser
|
||||
{
|
||||
static string c_numberBoxOperators = "+-*/^";
|
||||
|
||||
// Returns list of MathTokens from expression input string. If there are any parsing errors, it returns an empty vector.
|
||||
private static List<MathToken> GetTokens(string input, INumberParser numberParser)
|
||||
{
|
||||
var tokens = new List<MathToken>();
|
||||
|
||||
var expectNumber = true;
|
||||
while (input.Length > 0)
|
||||
{
|
||||
// Skip spaces
|
||||
var nextChar = input[0];
|
||||
if (nextChar != ' ')
|
||||
{
|
||||
if (expectNumber)
|
||||
{
|
||||
if (nextChar == '(')
|
||||
{
|
||||
// Open parens are also acceptable, but don't change the next expected token type.
|
||||
tokens.Add(new MathToken(MathTokenType.Parenthesis, nextChar));
|
||||
}
|
||||
else
|
||||
{
|
||||
var (value, charLength) = GetNextNumber(input, numberParser);
|
||||
|
||||
if (charLength > 0)
|
||||
{
|
||||
tokens.Add(new MathToken(MathTokenType.Numeric, value));
|
||||
|
||||
// UNO TODO: Was pointer manipulation, may be better off with Span<char>.
|
||||
input = input.Substring(charLength - 1);// advance the end of the token
|
||||
|
||||
expectNumber = false; // next token should be an operator
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error case -- next token is not a number
|
||||
return new List<MathToken>();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c_numberBoxOperators.IndexOf(nextChar) != -1)
|
||||
{
|
||||
tokens.Add(new MathToken(MathTokenType.Operator, nextChar));
|
||||
expectNumber = true; // next token should be a number
|
||||
}
|
||||
else if (nextChar == ')')
|
||||
{
|
||||
// Closed parens are also acceptable, but don't change the next expected token type.
|
||||
tokens.Add(new MathToken(MathTokenType.Parenthesis, nextChar));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error case -- could not evaluate part of the expression
|
||||
return new List<MathToken>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UNO TODO: Was pointer manipulation, may be better off with Span<char>.
|
||||
input = input.Substring(1);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Attempts to parse a number from the beginning of the given input string. Returns the character size of the matched string.
|
||||
static (double, int) GetNextNumber(string input, INumberParser numberParser)
|
||||
{
|
||||
// Attempt to parse anything before an operator or space as a number
|
||||
Regex regex = new Regex("^-?([^-+/*\\(\\)\\^\\s]+)");
|
||||
var match = regex.Match(input);
|
||||
if (match.Success)
|
||||
{
|
||||
// Might be a number
|
||||
var matchLength = match.Groups[0].Length;
|
||||
var parsedNum = ApiInformation.IsTypePresent(numberParser?.GetType().FullName)
|
||||
? numberParser.ParseDouble(input.Substring(0, matchLength))
|
||||
: double.TryParse(input.Substring(0, matchLength), out var d)
|
||||
? (double?)d
|
||||
: null;
|
||||
|
||||
if (parsedNum != null)
|
||||
{
|
||||
// Parsing was successful
|
||||
return (parsedNum.Value, matchLength);
|
||||
}
|
||||
}
|
||||
|
||||
return (double.NaN, 0);
|
||||
}
|
||||
|
||||
static int GetPrecedenceValue(char c)
|
||||
{
|
||||
int opPrecedence = 0;
|
||||
if (c == '*' || c == '/')
|
||||
{
|
||||
opPrecedence = 1;
|
||||
}
|
||||
else if (c == '^')
|
||||
{
|
||||
opPrecedence = 2;
|
||||
}
|
||||
|
||||
return opPrecedence;
|
||||
}
|
||||
|
||||
// Converts a list of tokens from infix format (e.g. "3 + 5") to postfix (e.g. "3 5 +")
|
||||
static List<MathToken> ConvertInfixToPostfix(List<MathToken> infixTokens)
|
||||
{
|
||||
List<MathToken> postfixTokens= new List<MathToken>();
|
||||
Stack<MathToken> operatorStack = new Stack<MathToken>();
|
||||
|
||||
foreach(var token in infixTokens)
|
||||
{
|
||||
if (token.Type == MathTokenType.Numeric)
|
||||
{
|
||||
postfixTokens.Add(token);
|
||||
}
|
||||
else if (token.Type == MathTokenType.Operator)
|
||||
{
|
||||
while (operatorStack.Count != 0)
|
||||
{
|
||||
var top = operatorStack.Peek();
|
||||
if (top.Type != MathTokenType.Parenthesis && (GetPrecedenceValue(top.Char) >= GetPrecedenceValue(token.Char)))
|
||||
{
|
||||
postfixTokens.Add(operatorStack.Peek());
|
||||
operatorStack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
operatorStack.Push(token);
|
||||
}
|
||||
else if (token.Type == MathTokenType.Parenthesis)
|
||||
{
|
||||
if (token.Char == '(')
|
||||
{
|
||||
operatorStack.Push(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (operatorStack.Count!=0 && operatorStack.Peek().Char != '(')
|
||||
{
|
||||
// Pop operators onto output until we reach a left paren
|
||||
postfixTokens.Add(operatorStack.Peek());
|
||||
operatorStack.Pop();
|
||||
}
|
||||
|
||||
if (operatorStack.Count == 0)
|
||||
{
|
||||
// Broken parenthesis
|
||||
return new List<MathToken>();
|
||||
}
|
||||
|
||||
// Pop left paren and discard
|
||||
operatorStack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pop all remaining operators.
|
||||
while (operatorStack.Count != 0)
|
||||
{
|
||||
if (operatorStack.Peek().Type == MathTokenType.Parenthesis)
|
||||
{
|
||||
// Broken parenthesis
|
||||
return new List<MathToken>();
|
||||
}
|
||||
|
||||
postfixTokens.Add(operatorStack.Peek());
|
||||
operatorStack.Pop();
|
||||
}
|
||||
|
||||
return postfixTokens;
|
||||
}
|
||||
|
||||
static double? ComputePostfixExpression(List<MathToken> tokens)
|
||||
{
|
||||
Stack<double> stack = new Stack<double>();
|
||||
|
||||
foreach(var token in tokens)
|
||||
{
|
||||
if (token.Type == MathTokenType.Operator)
|
||||
{
|
||||
// There has to be at least two values on the stack to apply
|
||||
if (stack.Count < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var op1 = stack.Peek();
|
||||
stack.Pop();
|
||||
|
||||
var op2 = stack.Peek();
|
||||
stack.Pop();
|
||||
|
||||
double result;
|
||||
|
||||
switch (token.Char)
|
||||
{
|
||||
case '-':
|
||||
result = op2 - op1;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
result = op1 + op2;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
result = op1 * op2;
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (op1 == 0)
|
||||
{
|
||||
// divide by zero
|
||||
return double.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = op2 / op1;
|
||||
}
|
||||
break;
|
||||
|
||||
case '^':
|
||||
result = Math.Pow(op2, op1);
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
stack.Push(result);
|
||||
}
|
||||
else if (token.Type == MathTokenType.Numeric)
|
||||
{
|
||||
stack.Push(token.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is more than one number on the stack, we didn't have enough operations, which is also an error.
|
||||
if (stack.Count != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return stack.Peek();
|
||||
}
|
||||
|
||||
public static double? Compute(string expr, INumberParser numberParser)
|
||||
{
|
||||
// Tokenize the input string
|
||||
var tokens = GetTokens(expr, numberParser);
|
||||
if (tokens.Count > 0)
|
||||
{
|
||||
// Rearrange to postfix notation
|
||||
var postfixTokens = ConvertInfixToPostfix(tokens);
|
||||
if (postfixTokens.Count > 0)
|
||||
{
|
||||
// Compute expression
|
||||
return ComputePostfixExpression(postfixTokens);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public enum NumberBoxSpinButtonPlacementMode
|
||||
{
|
||||
Hidden,
|
||||
Compact,
|
||||
Inline
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public enum NumberBoxValidationMode
|
||||
{
|
||||
InvalidInputOverwritten,
|
||||
Disabled
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.UI.Xaml.Controls
|
||||
{
|
||||
public class NumberBoxValueChangedEventArgs
|
||||
{
|
||||
internal NumberBoxValueChangedEventArgs(double oldValue, double newValue)
|
||||
{
|
||||
OldValue = oldValue;
|
||||
NewValue = newValue;
|
||||
}
|
||||
|
||||
public double OldValue { get; }
|
||||
|
||||
public double NewValue { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. See LICENSE in the project root for license information. -->
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="NumberBoxPopupIndicatorForeground" ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="NumberBoxPopupIndicatorForeground" ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<StaticResource x:Key="NumberBoxPopupIndicatorForeground" ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Thickness x:Key="NumberBoxSpinButtonBorderThickness">0,1,1,1</Thickness>
|
||||
<Thickness x:Key="NumberBoxIconMargin">10,0,0,0</Thickness>
|
||||
|
||||
<x:Double x:Key="NumberBoxPopupHorizonalOffset">-20</x:Double>
|
||||
<x:Double x:Key="NumberBoxPopupVerticalOffset">8</x:Double>
|
||||
<x:Double x:Key="NumberBoxPopupShadowDepth">16</x:Double>
|
||||
|
||||
<Thickness x:Key="NumberBoxPopupIndicatorMargin">0,0,8,0</Thickness>
|
||||
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,128 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NumberBoxDownSpinButtonName" xml:space="preserve">
|
||||
<value>Decrease</value>
|
||||
<comment>Automation name for the down button</comment>
|
||||
</data>
|
||||
<data name="NumberBoxUpSpinButtonName" xml:space="preserve">
|
||||
<value>Increase</value>
|
||||
<comment>Automation name for the up button</comment>
|
||||
</data>
|
||||
</root>
|
|
@ -4,6 +4,7 @@
|
|||
<#
|
||||
AddClass("Windows.UI.Xaml.Controls", "TextBox", hasCommonStates: true, hasCommonOverState: true, hasCommonFocusedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "Button", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "RepeatButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "ToggleButton", hasCommonStates: true, hasCommonCheckedState: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "CheckBox", hasCommonStates: true, hasCombinedCheckedState: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "RadioButton", hasCommonStates: true, hasCheckedStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<#
|
||||
AddClass("Windows.UI.Xaml.Controls", "TextBox", hasCommonStates: true, hasCommonOverState: true, hasCommonFocusedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "Button", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "RepeatButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "ToggleButton", hasCommonStates: true, hasCommonCheckedState: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "RadioButton", hasCommonStates: true, hasCheckedStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "HyperlinkButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<#
|
||||
AddClass("Windows.UI.Xaml.Controls", "TextBox", hasCommonStates: true, hasCommonOverState: true, hasCommonFocusedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "Button", hasCommonStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "RepeatButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "ToggleButton", hasCommonStates: true, hasCommonCheckedState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "RadioButton", hasCommonStates: true, hasCheckedStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "HyperlinkButton", hasCommonStates: true, hasCommonPressedState: true);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<#
|
||||
AddClass("Windows.UI.Xaml.Controls", "TextBox", hasCommonStates: true, hasCommonOverState: true, hasCommonFocusedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "Button", hasCommonStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "RepeatButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "ToggleButton", hasCommonStates: true, hasCommonCheckedState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "RadioButton", hasCommonStates: true, hasCheckedStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "HyperlinkButton", hasCommonStates: true, hasCommonPressedState: true);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<#
|
||||
AddClass("Windows.UI.Xaml.Controls", "TextBox", hasCommonStates: true, hasCommonFocusedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "Button", hasCommonStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "RepeatButton", hasCommonStates: true, hasCommonOverState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls.Primitives", "ToggleButton", hasCommonStates: true, hasCommonCheckedState: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "RadioButton", hasCommonStates: true, hasCheckedStates: true, hasCommonPressedState: true);
|
||||
AddClass("Windows.UI.Xaml.Controls", "HyperlinkButton", hasCommonStates: true, hasCommonPressedState: true);
|
||||
|
|
|
@ -59,8 +59,6 @@ namespace Windows.UI.Xaml.Controls
|
|||
}
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
{
|
||||
return new ButtonAutomationPeer(this);
|
||||
}
|
||||
=> new ButtonAutomationPeer(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,6 +232,11 @@ namespace Windows.UI.Xaml.Controls
|
|||
DoubleTapped += OnDoubleTappedHandler;
|
||||
}
|
||||
|
||||
if (implementedEvents.HasFlag(RoutedEventFlag.RightTapped))
|
||||
{
|
||||
RightTapped += OnRightTappedHandler;
|
||||
}
|
||||
|
||||
if (implementedEvents.HasFlag(RoutedEventFlag.KeyDown))
|
||||
{
|
||||
KeyDown += OnKeyDownHandler;
|
||||
|
@ -755,6 +760,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
protected virtual void OnManipulationCompleted(ManipulationCompletedRoutedEventArgs e) { }
|
||||
protected virtual void OnTapped(TappedRoutedEventArgs e) { }
|
||||
protected virtual void OnDoubleTapped(DoubleTappedRoutedEventArgs e) { }
|
||||
protected virtual void OnRightTapped(RightTappedRoutedEventArgs e) { }
|
||||
protected virtual void OnKeyDown(KeyRoutedEventArgs args) { }
|
||||
protected virtual void OnKeyUp(KeyRoutedEventArgs args) { }
|
||||
protected virtual void OnGotFocus(RoutedEventArgs e) { }
|
||||
|
@ -802,6 +808,9 @@ namespace Windows.UI.Xaml.Controls
|
|||
private static readonly DoubleTappedEventHandler OnDoubleTappedHandler =
|
||||
(object sender, DoubleTappedRoutedEventArgs args) => ((Control)sender).OnDoubleTapped(args);
|
||||
|
||||
private static readonly RightTappedEventHandler OnRightTappedHandler =
|
||||
(object sender, RightTappedRoutedEventArgs args) => ((Control)sender).OnRightTapped(args);
|
||||
|
||||
private static readonly KeyEventHandler OnKeyDownHandler =
|
||||
(object sender, KeyRoutedEventArgs args) => ((Control)sender).OnKeyDown(args);
|
||||
|
||||
|
@ -820,6 +829,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
private static readonly Type[] _pointerArgsType = new[] { typeof(PointerRoutedEventArgs) };
|
||||
private static readonly Type[] _tappedArgsType = new[] { typeof(TappedRoutedEventArgs) };
|
||||
private static readonly Type[] _doubleTappedArgsType = new[] { typeof(DoubleTappedRoutedEventArgs) };
|
||||
private static readonly Type[] _rightTappedArgsType = new[] { typeof(RightTappedRoutedEventArgs) };
|
||||
private static readonly Type[] _keyArgsType = new[] { typeof(KeyRoutedEventArgs) };
|
||||
private static readonly Type[] _routedArgsType = new[] { typeof(RoutedEventArgs) };
|
||||
private static readonly Type[] _manipStartingArgsType = new[] { typeof(ManipulationStartingRoutedEventArgs) };
|
||||
|
@ -916,6 +926,11 @@ namespace Windows.UI.Xaml.Controls
|
|||
result |= RoutedEventFlag.DoubleTapped;
|
||||
}
|
||||
|
||||
if (GetIsEventOverrideImplemented(type, nameof(OnRightTapped), _rightTappedArgsType))
|
||||
{
|
||||
result |= RoutedEventFlag.RightTapped;
|
||||
}
|
||||
|
||||
if (GetIsEventOverrideImplemented(type, nameof(OnKeyDown), _keyArgsType))
|
||||
{
|
||||
result |= RoutedEventFlag.KeyDown;
|
||||
|
|
|
@ -117,7 +117,10 @@ namespace Windows.UI.Xaml.Controls
|
|||
return;
|
||||
}
|
||||
|
||||
Select(row, component: 0, animated: true);
|
||||
if (Items.Length > 0) // If we have no Items, we don't need to call UIPickerView.Select(). Skipping the call avoids a native crash under certain narrow circumstances. (https://github.com/unoplatform/private/issues/115)
|
||||
{
|
||||
Select(row, component: 0, animated: true);
|
||||
}
|
||||
}
|
||||
else if (newSelectedItem != null && Items[0] == null)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,8 @@ using Windows.UI.Input;
|
|||
using Windows.UI.Xaml.Input;
|
||||
using Uno.Extensions.Specialized;
|
||||
using Uno.Logging;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
#if XAMARIN_IOS
|
||||
using View = UIKit.UIView;
|
||||
#elif __MACOS__
|
||||
|
@ -47,6 +49,9 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
public ButtonBase()
|
||||
{
|
||||
InitializeProperties();
|
||||
|
||||
Unloaded += (s, e) =>
|
||||
IsPressed = false;
|
||||
}
|
||||
|
||||
public new bool IsPointerOver
|
||||
|
@ -80,10 +85,11 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
#endregion
|
||||
|
||||
#region CommandParameter
|
||||
public static global::Windows.UI.Xaml.DependencyProperty CommandParameterProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
"CommandParameter", typeof(object),
|
||||
typeof(global::Windows.UI.Xaml.Controls.Primitives.ButtonBase),
|
||||
public static DependencyProperty CommandParameterProperty { get; } =
|
||||
DependencyProperty.Register(
|
||||
"CommandParameter",
|
||||
typeof(object),
|
||||
typeof(Controls.Primitives.ButtonBase),
|
||||
new FrameworkPropertyMetadata(default(object), OnCommandParameterChanged));
|
||||
|
||||
public object CommandParameter
|
||||
|
@ -98,6 +104,39 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
}
|
||||
#endregion
|
||||
|
||||
public ClickMode ClickMode
|
||||
{
|
||||
get => (ClickMode)this.GetValue(ClickModeProperty);
|
||||
set => this.SetValue(ClickModeProperty, value);
|
||||
}
|
||||
|
||||
public new bool IsPressed
|
||||
{
|
||||
get => (bool)GetValue(IsPressedProperty);
|
||||
internal set => SetValue(IsPressedProperty, value);
|
||||
}
|
||||
|
||||
public static DependencyProperty ClickModeProperty { get; } =
|
||||
DependencyProperty.Register(
|
||||
name: nameof(ClickMode),
|
||||
propertyType: typeof(ClickMode),
|
||||
ownerType: typeof(Controls.Primitives.ButtonBase),
|
||||
typeMetadata: new FrameworkPropertyMetadata(ClickMode.Release));
|
||||
|
||||
public static DependencyProperty IsPointerOverProperty { get; } =
|
||||
DependencyProperty.Register(
|
||||
name: nameof(IsPointerOver),
|
||||
propertyType: typeof(bool),
|
||||
ownerType: typeof(Controls.Primitives.ButtonBase),
|
||||
typeMetadata: new FrameworkPropertyMetadata(default(bool)));
|
||||
|
||||
public static DependencyProperty IsPressedProperty { get; } =
|
||||
DependencyProperty.Register(
|
||||
name: nameof(IsPressed),
|
||||
propertyType: typeof(bool),
|
||||
ownerType: typeof(Controls.Primitives.ButtonBase),
|
||||
typeMetadata: new FrameworkPropertyMetadata(default(bool)));
|
||||
|
||||
partial void RegisterEvents();
|
||||
|
||||
private void OnCommandChanged(ICommand newCommand)
|
||||
|
@ -191,6 +230,8 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
var handle = args.GetCurrentPoint(this).Properties.IsLeftButtonPressed && CapturePointer(args.Pointer);
|
||||
args.Handled = handle;
|
||||
|
||||
IsPressed = true;
|
||||
|
||||
if (handle && mode == ClickMode.Press)
|
||||
{
|
||||
RaiseClick(args);
|
||||
|
@ -224,6 +265,8 @@ namespace Windows.UI.Xaml.Controls.Primitives
|
|||
}
|
||||
}
|
||||
|
||||
IsPressed = false;
|
||||
|
||||
// This should be automatically done by the pointers due to release, but if for any reason
|
||||
// the state is invalid, this makes sure to not keep invalid capture longer than needed.
|
||||
// Note: This must be done ** after ** the click event (UWP raise CaptureLost event after)
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
using System;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml.Automation.Peers;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace Windows.UI.Xaml.Controls.Primitives
|
||||
{
|
||||
public partial class RepeatButton : ButtonBase
|
||||
{
|
||||
private bool _keyboardCausingRepeat;
|
||||
private bool _pointerCausingRepeat;
|
||||
private DispatcherTimer _timer;
|
||||
|
||||
public int Interval
|
||||
{
|
||||
get => (int)GetValue(IntervalProperty);
|
||||
set => SetValue(IntervalProperty, value);
|
||||
}
|
||||
|
||||
public static DependencyProperty IntervalProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
name: nameof(Interval),
|
||||
propertyType: typeof(int),
|
||||
ownerType: typeof(RepeatButton),
|
||||
typeMetadata: new FrameworkPropertyMetadata(250, propertyChangedCallback: (s, e) => (s as RepeatButton)?.OnIntervalChanged(e)));
|
||||
|
||||
public int Delay
|
||||
{
|
||||
get => (int)this.GetValue(DelayProperty);
|
||||
set => this.SetValue(DelayProperty, value);
|
||||
}
|
||||
|
||||
public static DependencyProperty DelayProperty { get; } =
|
||||
Windows.UI.Xaml.DependencyProperty.Register(
|
||||
name: nameof(Delay),
|
||||
propertyType: typeof(int),
|
||||
ownerType: typeof(RepeatButton),
|
||||
typeMetadata: new FrameworkPropertyMetadata(250, propertyChangedCallback: (s, e) => (s as RepeatButton)?.OnDelayChanged(e)));
|
||||
|
||||
public RepeatButton() : base()
|
||||
{
|
||||
InitializeVisualStates();
|
||||
|
||||
ClickMode = ClickMode.Press;
|
||||
|
||||
DefaultStyleKey = typeof(RepeatButton);
|
||||
}
|
||||
|
||||
protected override AutomationPeer OnCreateAutomationPeer()
|
||||
=> new RepeatButtonAutomationPeer(this);
|
||||
|
||||
private void OnIntervalChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is int newValue)
|
||||
{
|
||||
if (newValue <= 0)
|
||||
{
|
||||
throw new ArgumentException($"Interval cannot be positive", DelayProperty.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDelayChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue is int newValue)
|
||||
{
|
||||
if (newValue < 0)
|
||||
{
|
||||
throw new ArgumentException($"Delay cannot be negative", DelayProperty.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnIsEnabledChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
base.OnIsEnabledChanged(oldValue, newValue);
|
||||
|
||||
_keyboardCausingRepeat = false;
|
||||
_pointerCausingRepeat = false;
|
||||
|
||||
UpdateRepeatState();
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyRoutedEventArgs args)
|
||||
{
|
||||
base.OnKeyDown(args);
|
||||
|
||||
if(args.Key == VirtualKey.Space
|
||||
&& ClickMode != ClickMode.Hover)
|
||||
{
|
||||
_keyboardCausingRepeat = true;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyRoutedEventArgs args)
|
||||
{
|
||||
base.OnKeyUp(args);
|
||||
|
||||
if (args.Key == VirtualKey.Space
|
||||
& ClickMode != ClickMode.Hover)
|
||||
{
|
||||
_keyboardCausingRepeat = false;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLostFocus(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLostFocus(e);
|
||||
|
||||
if (ClickMode != ClickMode.Hover)
|
||||
{
|
||||
_keyboardCausingRepeat = false;
|
||||
_pointerCausingRepeat = false;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerEntered(PointerRoutedEventArgs args)
|
||||
{
|
||||
base.OnPointerEntered(args);
|
||||
|
||||
if (ClickMode == ClickMode.Hover)
|
||||
{
|
||||
_pointerCausingRepeat = true;
|
||||
}
|
||||
|
||||
UpdateRepeatState();
|
||||
}
|
||||
|
||||
protected override void OnPointerMoved(PointerRoutedEventArgs args)
|
||||
{
|
||||
base.OnPointerMoved(args);
|
||||
}
|
||||
|
||||
protected override void OnPointerExited(PointerRoutedEventArgs args)
|
||||
{
|
||||
base.OnPointerExited(args);
|
||||
|
||||
if (ClickMode == ClickMode.Hover)
|
||||
{
|
||||
_pointerCausingRepeat = false;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerPressed(PointerRoutedEventArgs args)
|
||||
{
|
||||
if (args.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnPointerPressed(args);
|
||||
|
||||
var pointerPoint = args.GetCurrentPoint(this);
|
||||
var isLeftButtonPressed = pointerPoint.Properties.IsLeftButtonPressed;
|
||||
|
||||
if (isLeftButtonPressed)
|
||||
{
|
||||
if (ClickMode != ClickMode.Hover)
|
||||
{
|
||||
_pointerCausingRepeat = true;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerReleased(PointerRoutedEventArgs args)
|
||||
{
|
||||
if (args.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnPointerReleased(args);
|
||||
|
||||
if (ClickMode != ClickMode.Hover)
|
||||
{
|
||||
_pointerCausingRepeat = false;
|
||||
UpdateRepeatState();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartTimer()
|
||||
{
|
||||
|
||||
if (_timer == null)
|
||||
{
|
||||
_timer = new DispatcherTimer();
|
||||
_timer.Tick += TimerCallback;
|
||||
}
|
||||
|
||||
if (!_timer.IsEnabled)
|
||||
{
|
||||
_timer.Interval = TimeSpan.FromMilliseconds(Delay);
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopTimer()
|
||||
{
|
||||
_timer?.Stop();
|
||||
}
|
||||
|
||||
private void UpdateRepeatState()
|
||||
{
|
||||
if((_pointerCausingRepeat && IsPointerOver) || _keyboardCausingRepeat)
|
||||
{
|
||||
StartTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void TimerCallback(object sender, object state)
|
||||
{
|
||||
var interval = TimeSpan.FromMilliseconds(Interval);
|
||||
|
||||
if (_timer.Interval != interval)
|
||||
{
|
||||
_timer.Interval = interval;
|
||||
}
|
||||
|
||||
var isPressed = IsPressed;
|
||||
|
||||
if ((isPressed && IsPointerOver) || (isPressed && _keyboardCausingRepeat))
|
||||
{
|
||||
RaiseClick();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@ namespace Windows.UI.Xaml.Controls
|
|||
private ContentPresenter _headerContentPresenter;
|
||||
private Thumb _horizontalThumb, _verticalThumb;
|
||||
private FrameworkElement _horizontalTemplate, _verticalTemplate;
|
||||
private bool _duringDrag;
|
||||
private bool _isDragging; // between DragStart and DragCompleted
|
||||
private bool _isInDragDelta; // is reacting to a DragDelta
|
||||
private Rectangle _horizontalDecreaseRect;
|
||||
private Rectangle _horizontalTrackRect;
|
||||
private Rectangle _verticalDecreaseRect;
|
||||
|
@ -154,7 +155,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
{
|
||||
ApplyValueToSlide();
|
||||
|
||||
IsPointerPressed = false;
|
||||
_isDragging = false;
|
||||
UpdateCommonState();
|
||||
}
|
||||
|
||||
|
@ -162,7 +163,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
{
|
||||
try
|
||||
{
|
||||
_duringDrag = true;
|
||||
_isInDragDelta = true;
|
||||
|
||||
if (Orientation == Orientation.Horizontal)
|
||||
{
|
||||
|
@ -183,7 +184,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
}
|
||||
finally
|
||||
{
|
||||
_duringDrag = false;
|
||||
_isInDragDelta = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,10 +192,6 @@ namespace Windows.UI.Xaml.Controls
|
|||
{
|
||||
if (HasXamlTemplate)
|
||||
{
|
||||
// Disable parent scrolling on Android
|
||||
#if XAMARIN_ANDROID
|
||||
this.RequestDisallowInterceptTouchEvent(true);
|
||||
#endif
|
||||
if (Orientation == Orientation.Horizontal)
|
||||
{
|
||||
_horizontalInitial = GetSanitizedDimension(_horizontalDecreaseRect.Width);
|
||||
|
@ -204,7 +201,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
_verticalInitial = GetSanitizedDimension(_verticalDecreaseRect.Height);
|
||||
}
|
||||
|
||||
IsPointerPressed = true;
|
||||
_isDragging = true;
|
||||
UpdateCommonState();
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +212,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
{
|
||||
VisualStateManager.GoToState(this, "Disabled", useTransitions);
|
||||
}
|
||||
else if (IsPointerPressed)
|
||||
else if (_isDragging)
|
||||
{
|
||||
VisualStateManager.GoToState(this, "Pressed", useTransitions);
|
||||
}
|
||||
|
@ -277,19 +274,19 @@ namespace Windows.UI.Xaml.Controls
|
|||
// The _decreaseRect's height/width is updated, which in turn pushes or pulls the Thumb to its correct position
|
||||
if (Orientation == Orientation.Horizontal)
|
||||
{
|
||||
if (_horizontalThumb != null && _horizontalDecreaseRect != null)
|
||||
{
|
||||
var maxWidth = ActualWidth - _horizontalThumb.ActualWidth;
|
||||
_horizontalDecreaseRect.Width = (float)((Value - Minimum) / (Maximum - Minimum)) * maxWidth;
|
||||
}
|
||||
if (_horizontalThumb != null && _horizontalDecreaseRect != null)
|
||||
{
|
||||
var maxWidth = ActualWidth - _horizontalThumb.ActualWidth;
|
||||
_horizontalDecreaseRect.Width = (float)((Value - Minimum) / (Maximum - Minimum)) * maxWidth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_verticalThumb != null && _verticalDecreaseRect != null)
|
||||
{
|
||||
var maxHeight = ActualHeight - _verticalThumb.ActualHeight;
|
||||
_verticalDecreaseRect.Height = (float)((Value - Minimum) / (Maximum - Minimum)) * maxHeight;
|
||||
}
|
||||
if (_verticalThumb != null && _verticalDecreaseRect != null)
|
||||
{
|
||||
var maxHeight = ActualHeight - _verticalThumb.ActualHeight;
|
||||
_verticalDecreaseRect.Height = (float)((Value - Minimum) / (Maximum - Minimum)) * maxHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,7 +308,7 @@ namespace Windows.UI.Xaml.Controls
|
|||
{
|
||||
base.OnValueChanged(oldValue, newValue);
|
||||
|
||||
if (!_duringDrag && HasXamlTemplate)
|
||||
if (!_isInDragDelta && HasXamlTemplate)
|
||||
{
|
||||
ApplyValueToSlide();
|
||||
}
|
||||
|
@ -319,21 +316,24 @@ namespace Windows.UI.Xaml.Controls
|
|||
|
||||
private void SubscribeSliderContainerPressed()
|
||||
{
|
||||
if (_sliderContainer != null && IsTrackerEnabled)
|
||||
// This allows the user to start sliding by clicking anywhere on the slider
|
||||
// In that case, the Thumb won't be able to capture the pointer and instead we will replicate
|
||||
// its behavior and "push" to it the drag events (on which we are already subscribed).
|
||||
|
||||
var container = _sliderContainer;
|
||||
if (container != null && IsTrackerEnabled)
|
||||
{
|
||||
_sliderContainerSubscription.Disposable = null;
|
||||
|
||||
_sliderContainer.PointerPressed += OnSliderContainerPressed;
|
||||
_sliderContainer.PointerMoved += OnSliderContainerMoved;
|
||||
_sliderContainer.PointerReleased += OnSliderContainerReleased;
|
||||
_sliderContainer.PointerCanceled += OnSliderContainerCanceled;
|
||||
container.PointerPressed += OnSliderContainerPressed;
|
||||
container.PointerMoved += OnSliderContainerMoved;
|
||||
container.PointerCaptureLost += OnSliderContainerCaptureLost;
|
||||
|
||||
_sliderContainerSubscription.Disposable = Disposable.Create(() =>
|
||||
{
|
||||
_sliderContainer.PointerPressed -= OnSliderContainerPressed;
|
||||
_sliderContainer.PointerMoved -= OnSliderContainerMoved;
|
||||
_sliderContainer.PointerReleased -= OnSliderContainerReleased;
|
||||
_sliderContainer.PointerCanceled -= OnSliderContainerCanceled;
|
||||
container.PointerPressed -= OnSliderContainerPressed;
|
||||
container.PointerMoved -= OnSliderContainerMoved;
|
||||
container.PointerCaptureLost -= OnSliderContainerCaptureLost;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -341,60 +341,36 @@ namespace Windows.UI.Xaml.Controls
|
|||
private void OnSliderContainerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var container = sender as FrameworkElement;
|
||||
var point = e.GetCurrentPoint(container).Position;
|
||||
if (container.CapturePointer(e.Pointer))
|
||||
{
|
||||
var point = e.GetCurrentPoint(container).Position;
|
||||
var newOffset = Orientation == Orientation.Horizontal
|
||||
? point.X / container.ActualWidth
|
||||
: 1 - (point.Y / container.ActualHeight);
|
||||
|
||||
var newOffset = Orientation == Orientation.Horizontal ?
|
||||
point.X / container.ActualWidth :
|
||||
1 - (point.Y / container.ActualHeight);
|
||||
|
||||
ApplySlideToValue(newOffset);
|
||||
|
||||
Thumb?.StartDrag(point);
|
||||
|
||||
// This captures downstream events outside the slider's bounds.
|
||||
container.CapturePointer(e.Pointer);
|
||||
|
||||
// This is currently obligatory on Android to be able to receive downstream touch events (eg PointerMoved etc).
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnSliderContainerCanceled(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
OnDragCompleted(sender, null);
|
||||
|
||||
var container = sender as FrameworkElement;
|
||||
container.ReleasePointerCapture(e.Pointer);
|
||||
}
|
||||
|
||||
private void OnSliderContainerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var container = sender as FrameworkElement;
|
||||
var point = e.GetCurrentPoint(container).Position;
|
||||
|
||||
ApplyValueToSlide();
|
||||
|
||||
Thumb?.CompleteDrag(point);
|
||||
|
||||
container.ReleasePointerCapture(e.Pointer);
|
||||
|
||||
e.Handled = true;
|
||||
ApplySlideToValue(newOffset);
|
||||
Thumb?.StartDrag(point);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSliderContainerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var container = sender as FrameworkElement;
|
||||
#if __WASM__
|
||||
if (!container.IsCaptured(e.Pointer))
|
||||
if (container.IsCaptured(e.Pointer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
var point = e.GetCurrentPoint(container).Position;
|
||||
|
||||
Thumb?.DeltaDrag(point);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSliderContainerCaptureLost(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var container = sender as FrameworkElement;
|
||||
var point = e.GetCurrentPoint(container).Position;
|
||||
|
||||
Thumb?.DeltaDrag(point);
|
||||
|
||||
e.Handled = true;
|
||||
ApplyValueToSlide();
|
||||
Thumb?.CompleteDrag(point);
|
||||
}
|
||||
|
||||
#region IsTrackerEnabled DependencyProperty
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#if !NET461
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Windows.System;
|
||||
|
@ -14,6 +12,7 @@ namespace Windows.UI.Xaml.Input
|
|||
: base(originalSource)
|
||||
{
|
||||
Key = key;
|
||||
OriginalKey = key;
|
||||
}
|
||||
|
||||
public bool Handled { get; set; }
|
||||
|
@ -21,6 +20,6 @@ namespace Windows.UI.Xaml.Input
|
|||
|
||||
//TODO
|
||||
//public CorePhysicalKeyStatus KeyStatus { get; }
|
||||
public global::Windows.System.VirtualKey OriginalKey { get; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,12 +15,28 @@ namespace Windows.UI.Xaml.Input
|
|||
{
|
||||
partial class PointerRoutedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The stylus is pressed while holding the barrel button
|
||||
/// </summary>
|
||||
internal const MotionEventActions StylusWithBarrelDown = (MotionEventActions)211;
|
||||
/// <summary>
|
||||
/// The stylus is moved after having been pressed while holding the barrel button
|
||||
/// </summary>
|
||||
internal const MotionEventActions StylusWithBarrelMove = (MotionEventActions)213;
|
||||
/// <summary>
|
||||
/// The stylus is released after having been pressed while holding the barrel button
|
||||
/// </summary>
|
||||
internal const MotionEventActions StylusWithBarrelUp = (MotionEventActions)212;
|
||||
|
||||
private const int _pointerIdsCount = (int)MotionEventActions.PointerIndexMask >> (int)MotionEventActions.PointerIndexShift; // 0xff
|
||||
private const int _pointerIdsShift = 31 - (int)MotionEventActions.PointerIndexShift; // 23
|
||||
|
||||
private readonly MotionEvent _nativeEvent;
|
||||
private readonly int _pointerIndex;
|
||||
private readonly UIElement _receiver;
|
||||
private readonly PointerPointProperties _properties;
|
||||
|
||||
internal bool HasPressedButton => _properties.HasPressedButton;
|
||||
|
||||
internal PointerRoutedEventArgs(MotionEvent nativeEvent, int pointerIndex, UIElement originalSource, UIElement receiver) : this()
|
||||
{
|
||||
|
@ -32,30 +48,32 @@ namespace Windows.UI.Xaml.Input
|
|||
// and that usually the deviceId is [0, something_not_too_big_hopefully_less_than_0x00ffffff].
|
||||
// If deviceId is greater than 0x00ffffff, we might have a conflict but only in case of multi touch
|
||||
// and with a high variation of deviceId. We assume that's safe enough.
|
||||
|
||||
// Note: Make sure to use the GetPointerId in order to make sure to keep the same id while: down_1 / down_2 / up_1 / up_2
|
||||
// otherwise up_2 will be with the id of 1
|
||||
// otherwise up_2 will be with the id of 1
|
||||
var pointerId = ((uint)nativeEvent.GetPointerId(pointerIndex) & _pointerIdsCount) << _pointerIdsShift | (uint)nativeEvent.DeviceId;
|
||||
var type = nativeEvent.GetToolType(pointerIndex).ToPointerDeviceType();
|
||||
var isInContact = IsInContact(type, nativeEvent, pointerIndex);
|
||||
var nativePointerAction = nativeEvent.Action;
|
||||
var nativePointerButtons = nativeEvent.ButtonState;
|
||||
var nativePointerType = nativeEvent.GetToolType(_pointerIndex);
|
||||
var pointerType = nativePointerType.ToPointerDeviceType();
|
||||
var isInContact = IsInContact(nativeEvent, pointerType, nativePointerAction, nativePointerButtons);
|
||||
var keys = nativeEvent.MetaState.ToVirtualKeyModifiers();
|
||||
|
||||
FrameId = (uint)_nativeEvent.EventTime;
|
||||
Pointer = new Pointer(pointerId, type, isInContact, isInRange: true);
|
||||
Pointer = new Pointer(pointerId, pointerType, isInContact, isInRange: true);
|
||||
KeyModifiers = keys;
|
||||
OriginalSource = originalSource;
|
||||
CanBubbleNatively = true;
|
||||
|
||||
_properties = GetProperties(nativePointerType, nativePointerAction, nativePointerButtons); // Last: we need the Pointer property to be set!
|
||||
}
|
||||
|
||||
public PointerPoint GetCurrentPoint(UIElement relativeTo)
|
||||
{
|
||||
var timestamp = ToTimeStamp(_nativeEvent.EventTime);
|
||||
var device = PointerDevice.For(Pointer.PointerDeviceType);
|
||||
|
||||
var (rawPosition, position) = GetPositions(relativeTo);
|
||||
var properties = GetProperties();
|
||||
|
||||
return new PointerPoint(FrameId, timestamp, device, Pointer.PointerId, rawPosition, position, Pointer.IsInContact, properties);
|
||||
return new PointerPoint(FrameId, timestamp, device, Pointer.PointerId, rawPosition, position, Pointer.IsInContact, _properties);
|
||||
}
|
||||
|
||||
private (Point raw, Point relative) GetPositions(UIElement relativeTo)
|
||||
|
@ -99,7 +117,7 @@ namespace Windows.UI.Xaml.Input
|
|||
return (raw, relative);
|
||||
}
|
||||
|
||||
private PointerPointProperties GetProperties()
|
||||
private PointerPointProperties GetProperties(MotionEventToolType type, MotionEventActions action, MotionEventButtonState buttons)
|
||||
{
|
||||
var props = new PointerPointProperties
|
||||
{
|
||||
|
@ -107,8 +125,6 @@ namespace Windows.UI.Xaml.Input
|
|||
IsInRange = Pointer.IsInRange
|
||||
};
|
||||
|
||||
var type = _nativeEvent.GetToolType(_pointerIndex);
|
||||
var action = _nativeEvent.Action;
|
||||
var isDown = action.HasFlag(MotionEventActions.Down) || action.HasFlag(MotionEventActions.PointerDown);
|
||||
var isUp = action.HasFlag(MotionEventActions.Up) || action.HasFlag(MotionEventActions.PointerUp);
|
||||
var updates = _none;
|
||||
|
@ -117,20 +133,40 @@ namespace Windows.UI.Xaml.Input
|
|||
case MotionEventToolType.Finger:
|
||||
props.IsLeftButtonPressed = Pointer.IsInContact;
|
||||
updates = isDown ? _fingerDownUpdates : isUp ? _fingerUpUpdates : _none;
|
||||
// Pressure = .5f => Keeps default as UWP returns .5 for fingers.
|
||||
break;
|
||||
|
||||
case MotionEventToolType.Mouse:
|
||||
props.IsLeftButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Primary);
|
||||
props.IsMiddleButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Tertiary);
|
||||
props.IsRightButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.Secondary);
|
||||
props.IsLeftButtonPressed = buttons.HasFlag(MotionEventButtonState.Primary);
|
||||
props.IsMiddleButtonPressed = buttons.HasFlag(MotionEventButtonState.Tertiary);
|
||||
props.IsRightButtonPressed = buttons.HasFlag(MotionEventButtonState.Secondary);
|
||||
updates = isDown ? _mouseDownUpdates : isUp ? _mouseUpUpdates : _none;
|
||||
// Pressure = .5f => Keeps default as UWP returns .5 for Mouse no matter is button is pressed or not (Android return 1.0 while pressing a button, but 0 otherwise).
|
||||
break;
|
||||
|
||||
// Note: On UWP, if you touch screen while already holding the barrel button, you will get a right + barrel,
|
||||
// ** BUT ** if you touch screen and THEN press the barrel button props will be left + barrel until released.
|
||||
// On Android this distinction seems to be flagged by the "1101 ****" action flag (i.e. "StylusWithBarrel***" actions),
|
||||
// so here we set the Is<Left|Right>ButtonPressed based on the action and we don't try to link it to the barrel button state.
|
||||
case MotionEventToolType.Stylus when action == StylusWithBarrelDown:
|
||||
case MotionEventToolType.Stylus when action == StylusWithBarrelMove:
|
||||
case MotionEventToolType.Stylus when action == StylusWithBarrelUp:
|
||||
// Note: We still validate the "IsButtonPressed(StylusPrimary)" as the user might release the button while pressed.
|
||||
// In that case we will still receive moves and up with the "StylusWithBarrel***" actions.
|
||||
props.IsBarrelButtonPressed = buttons.HasFlag(MotionEventButtonState.StylusPrimary);
|
||||
props.IsRightButtonPressed = Pointer.IsInContact;
|
||||
props.Pressure = Math.Min(1f, _nativeEvent.GetPressure(_pointerIndex)); // Might exceed 1.0 on Android
|
||||
break;
|
||||
case MotionEventToolType.Stylus:
|
||||
props.IsBarrelButtonPressed = _nativeEvent.IsButtonPressed(MotionEventButtonState.StylusPrimary);
|
||||
props.IsLeftButtonPressed = Pointer.IsInContact && !props.IsBarrelButtonPressed;
|
||||
props.IsBarrelButtonPressed = buttons.HasFlag(MotionEventButtonState.StylusPrimary);
|
||||
props.IsLeftButtonPressed = Pointer.IsInContact;
|
||||
props.Pressure = Math.Min(1f, _nativeEvent.GetPressure(_pointerIndex)); // Might exceed 1.0 on Android
|
||||
break;
|
||||
case MotionEventToolType.Eraser:
|
||||
props.IsEraser = true;
|
||||
props.Pressure = Math.Min(1f, _nativeEvent.GetPressure(_pointerIndex)); // Might exceed 1.0 on Android
|
||||
break;
|
||||
|
||||
case MotionEventToolType.Unknown: // used by Xamarin.UITest
|
||||
props.IsLeftButtonPressed = true;
|
||||
break;
|
||||
|
@ -191,23 +227,25 @@ namespace Windows.UI.Xaml.Input
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsInContact(PointerDeviceType type, MotionEvent nativeEvent, int pointerIndex)
|
||||
private static bool IsInContact(MotionEvent nativeEvent, PointerDeviceType pointerType, MotionEventActions action, MotionEventButtonState buttons)
|
||||
{
|
||||
switch (type)
|
||||
switch (pointerType)
|
||||
{
|
||||
case PointerDeviceType.Mouse:
|
||||
return nativeEvent.ButtonState != 0;
|
||||
// For mouse, we cannot only rely on action: We will get a "HoverExit" when we press the left button.
|
||||
return buttons != 0;
|
||||
|
||||
case PointerDeviceType.Pen:
|
||||
return nativeEvent.GetAxisValue(Axis.Distance, pointerIndex) == 0;
|
||||
return nativeEvent.GetAxisValue(Axis.Distance, nativeEvent.ActionIndex) == 0;
|
||||
|
||||
default:
|
||||
case PointerDeviceType.Touch:
|
||||
return nativeEvent.Action.HasFlag(MotionEventActions.Down)
|
||||
|| nativeEvent.Action.HasFlag(MotionEventActions.PointerDown)
|
||||
|| nativeEvent.Action.HasFlag(MotionEventActions.Move);
|
||||
// WARNING: MotionEventActions.Down == 0, so action.HasFlag(MotionEventActions.Up) is always true!
|
||||
return !action.HasFlag(MotionEventActions.Up)
|
||||
&& !action.HasFlag(MotionEventActions.PointerUp)
|
||||
&& !action.HasFlag(MotionEventActions.Cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ namespace Windows.UI.Xaml.Input
|
|||
{
|
||||
IsPrimary = true,
|
||||
IsInRange = Pointer.IsInRange,
|
||||
IsLeftButtonPressed = Pointer.IsInContact
|
||||
IsLeftButtonPressed = Pointer.IsInContact,
|
||||
Pressure = (float)(_nativeTouch.Force / _nativeTouch.MaximumPossibleForce)
|
||||
};
|
||||
|
||||
#region Misc static helpers
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Windows.UI.Xaml.Input
|
|||
private readonly Point _absolutePosition;
|
||||
private readonly WindowManagerInterop.HtmlPointerButtonsState _buttons;
|
||||
private readonly WindowManagerInterop.HtmlPointerButtonUpdate _buttonUpdate;
|
||||
private readonly double _pressure;
|
||||
|
||||
internal PointerRoutedEventArgs(
|
||||
double timestamp,
|
||||
|
@ -24,6 +25,7 @@ namespace Windows.UI.Xaml.Input
|
|||
WindowManagerInterop.HtmlPointerButtonsState buttons,
|
||||
WindowManagerInterop.HtmlPointerButtonUpdate buttonUpdate,
|
||||
VirtualKeyModifiers keys,
|
||||
double pressure,
|
||||
UIElement source,
|
||||
bool canBubbleNatively)
|
||||
: this()
|
||||
|
@ -32,6 +34,7 @@ namespace Windows.UI.Xaml.Input
|
|||
_absolutePosition = absolutePosition;
|
||||
_buttons = buttons;
|
||||
_buttonUpdate = buttonUpdate;
|
||||
_pressure = pressure;
|
||||
|
||||
FrameId = ToFrameId(timestamp);
|
||||
Pointer = new Pointer(pointerId, pointerType, isInContact, isInRange: true);
|
||||
|
@ -63,22 +66,29 @@ namespace Windows.UI.Xaml.Input
|
|||
|
||||
props.IsLeftButtonPressed = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.Left);
|
||||
props.IsMiddleButtonPressed = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.Middle);
|
||||
if (_buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.Right))
|
||||
{
|
||||
switch (Pointer.PointerDeviceType)
|
||||
{
|
||||
case PointerDeviceType.Mouse:
|
||||
props.IsMiddleButtonPressed = true;
|
||||
break;
|
||||
case PointerDeviceType.Pen:
|
||||
props.IsBarrelButtonPressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
props.IsRightButtonPressed = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.Right);
|
||||
props.IsXButton1Pressed = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.X1);
|
||||
props.IsXButton2Pressed = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.X2);
|
||||
props.IsEraser = _buttons.HasFlag(WindowManagerInterop.HtmlPointerButtonsState.Eraser);
|
||||
|
||||
switch (Pointer.PointerDeviceType)
|
||||
{
|
||||
// For touch and mouse, we keep the default pressure of .5, as WinUI
|
||||
|
||||
case PointerDeviceType.Pen:
|
||||
// !!! WARNING !!! Here we have a slight different behavior compared to WinUI:
|
||||
// On WinUI we will get IsRightButtonPressed (with IsBarrelButtonPressed) only if the user is pressing
|
||||
// the barrel button when pen goes "in contact" (i.e. touches the screen), otherwise we will get
|
||||
// IsLeftButtonPressed and IsBarrelButtonPressed.
|
||||
// Here we set IsRightButtonPressed as soon as the barrel button was pressed, no matter
|
||||
// if the pen was already in contact or not.
|
||||
// This is acceptable since the UIElement pressed state is **per pointer** (not buttons of pointer)
|
||||
// and GestureRecognizer always checks that pressed buttons didn't changed for a single gesture.
|
||||
props.IsBarrelButtonPressed = props.IsRightButtonPressed;
|
||||
props.Pressure = (float)_pressure;
|
||||
break;
|
||||
}
|
||||
|
||||
props.PointerUpdateKind = ToUpdateKind(_buttonUpdate, props);
|
||||
|
||||
return props;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
namespace Windows.UI.Xaml.Input
|
||||
{
|
||||
public delegate void RightTappedEventHandler(object sender, RightTappedRoutedEventArgs e);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using Windows.Devices.Input;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Input;
|
||||
using Uno.UI.Xaml.Input;
|
||||
|
||||
namespace Windows.UI.Xaml.Input
|
||||
{
|
||||
public partial class RightTappedRoutedEventArgs : RoutedEventArgs, ICancellableRoutedEventArgs
|
||||
{
|
||||
private readonly UIElement _originalSource;
|
||||
private readonly Point _position;
|
||||
|
||||
public RightTappedRoutedEventArgs() { }
|
||||
internal RightTappedRoutedEventArgs(UIElement originalSource, RightTappedEventArgs args)
|
||||
: base(originalSource)
|
||||
{
|
||||
_originalSource = originalSource;
|
||||
_position = args.Position;
|
||||
PointerDeviceType = args.PointerDeviceType;
|
||||
}
|
||||
|
||||
public bool Handled { get; set; }
|
||||
|
||||
public PointerDeviceType PointerDeviceType { get; }
|
||||
|
||||
public Point GetPosition(UIElement relativeTo)
|
||||
{
|
||||
if (_originalSource == null)
|
||||
{
|
||||
return default; // Required for the default public ctor ...
|
||||
}
|
||||
else if (relativeTo == _originalSource)
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _originalSource.GetPosition(_position, relativeTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ namespace Uno.UI.Xaml
|
|||
// Gestures
|
||||
Tapped = 1UL << 48,
|
||||
DoubleTapped = 1UL << 49,
|
||||
// RightTapped = 1UL << 50, => Reserved for future usage
|
||||
RightTapped = 1UL << 50,
|
||||
// Holding = 1UL << 51, => Reserved for future usage
|
||||
|
||||
// Context menu
|
||||
|
@ -98,8 +98,10 @@ namespace Uno.UI.Xaml
|
|||
| RoutedEventFlag.ManipulationCompleted;
|
||||
|
||||
private const RoutedEventFlag _isGesture = // 0b0000_0000_0001_1111___0000_0000_0000_0000___0000_0000_0000_0000___0000_0000_0000_0000
|
||||
RoutedEventFlag.Tapped
|
||||
| RoutedEventFlag.DoubleTapped;
|
||||
RoutedEventFlag.Tapped
|
||||
| RoutedEventFlag.DoubleTapped
|
||||
| RoutedEventFlag.RightTapped;
|
||||
//| RoutedEventFlag.Holding;
|
||||
|
||||
private const RoutedEventFlag _isContextMenu = (RoutedEventFlag)0b0011_0000_0000_0000___0000_0000_0000_0000___0000_0000_0000_0000___0000_0000_0000_0000;
|
||||
// RoutedEventFlag.ContextRequested
|
||||
|
|
|
@ -7977,6 +7977,99 @@
|
|||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Default style for Windows.UI.Xaml.Controls.Primitives.RepeatButton -->
|
||||
<Style x:Name="XamlDefaultRepeatButton" TargetType="RepeatButton">
|
||||
<Setter Property="Background" Value="{ThemeResource RepeatButtonBackground}" />
|
||||
<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource RepeatButtonForeground}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource RepeatButtonBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
|
||||
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-3" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="RepeatButton">
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Background="{TemplateBinding Background}"
|
||||
BackgroundSizing="{TemplateBinding BackgroundSizing}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBackgroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBorderBrushPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonForegroundPointerOver}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBackgroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBorderBrushPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonForegroundPressed}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBackgroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonBorderBrushDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource RepeatButtonForegroundDisabled}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
</VisualStateGroup>
|
||||
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</ContentPresenter>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="RepeatButton" BasedOn="{StaticResource XamlDefaultRepeatButton}" />
|
||||
|
||||
<win:AcrylicBrush x:Key="SystemControlChromeMediumLowAcrylicElementMediumBrush" BackgroundSource="Backdrop" TintColor="{StaticResource SystemChromeAltHighColor}" TintOpacity="0.6" FallbackColor="{StaticResource SystemChromeMediumLowColor}" />
|
||||
<xamarin:SolidColorBrush x:Key="SystemControlChromeMediumLowAcrylicElementMediumBrush" Color="{StaticResource SystemChromeMediumLowColor}" />
|
||||
<StaticResource x:Key="NavigationViewDefaultPaneBackground" ResourceKey="SystemControlChromeMediumLowAcrylicElementMediumBrush" />
|
||||
|
|
|
@ -196,7 +196,7 @@
|
|||
<Thickness x:Key="PivotNavButtonMargin">0,6,0,0</Thickness>
|
||||
<Thickness x:Key="PivotPortraitThemePadding">12,14,0,13</Thickness>
|
||||
<Thickness x:Key="ProgressBarBorderThemeThickness">0</Thickness>
|
||||
<Thickness x:Key="RepeatButtonBorderThemeThickness">0</Thickness>
|
||||
<Thickness x:Key="RepeatButtonBorderThemeThickness">2</Thickness>
|
||||
<Thickness x:Key="ScrollBarPanningBorderThemeThickness">1</Thickness>
|
||||
<Thickness x:Key="SearchBoxQuerySuggestionThemeMargin">12,11,8,13</Thickness>
|
||||
<Thickness x:Key="SearchBoxResultSuggestionThemeMargin">12,11,8,13</Thickness>
|
||||
|
@ -746,6 +746,15 @@
|
|||
<SolidColorBrush x:Key="ScrollBarThumbPressedBorderThemeBrush" Color="#ED555555" />
|
||||
<SolidColorBrush x:Key="ScrollBarTrackBackgroundThemeBrush" Color="#59D5D5D5" />
|
||||
<SolidColorBrush x:Key="ScrollBarTrackBorderThemeBrush" Color="#59D5D5D5" />
|
||||
<SolidColorBrush x:Key="RepeatButtonBorderThemeBrush" Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonDisabledBackgroundThemeBrush" Color="Transparent" />
|
||||
<SolidColorBrush x:Key="RepeatButtonDisabledBorderThemeBrush" Color="#66FFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonDisabledForegroundThemeBrush" Color="#66FFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonForegroundThemeBrush" Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonPointerOverBackgroundThemeBrush" Color="#21FFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonPointerOverForegroundThemeBrush" Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonPressedBackgroundThemeBrush" Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="RepeatButtonPressedForegroundThemeBrush" Color="#FF000000" />
|
||||
<SolidColorBrush x:Key="SearchBoxBackgroundThemeBrush" Color="#CCFFFFFF" />
|
||||
<SolidColorBrush x:Key="SearchBoxBorderThemeBrush" Color="#FF2A2A2A" />
|
||||
<SolidColorBrush x:Key="SearchBoxDisabledBackgroundThemeBrush" Color="Transparent" />
|
||||
|
@ -989,6 +998,20 @@
|
|||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
|
||||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
|
||||
|
||||
<!-- Resources for Windows.UI.Xaml.Controls.Primitives.RepeatButton -->
|
||||
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
|
||||
|
||||
<!-- Resources for AccentButtonStyle -->
|
||||
<StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
<StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
|
@ -1199,7 +1222,7 @@
|
|||
<Thickness x:Key="PivotNavButtonMargin">0,6,0,0</Thickness>
|
||||
<Thickness x:Key="PivotPortraitThemePadding">12,14,0,13</Thickness>
|
||||
<Thickness x:Key="ProgressBarBorderThemeThickness">0</Thickness>
|
||||
<Thickness x:Key="RepeatButtonBorderThemeThickness">0</Thickness>
|
||||
<Thickness x:Key="RepeatButtonBorderThemeThickness">2</Thickness>
|
||||
<Thickness x:Key="ScrollBarPanningBorderThemeThickness">1</Thickness>
|
||||
<Thickness x:Key="SearchBoxQuerySuggestionThemeMargin">12,11,8,13</Thickness>
|
||||
<Thickness x:Key="SearchBoxResultSuggestionThemeMargin">12,11,8,13</Thickness>
|
||||
|
@ -1987,6 +2010,20 @@
|
|||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
|
||||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
|
||||
|
||||
<!-- Resources for Windows.UI.Xaml.Controls.Primitives.RepeatButton -->
|
||||
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
|
||||
|
||||
<!-- Resources for AccentButtonStyle -->
|
||||
<StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
<StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
|
@ -2983,6 +3020,20 @@
|
|||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushPointerOver" ResourceKey="SystemControlTransparentBrush" />
|
||||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushPressed" ResourceKey="SystemControlTransparentBrush" />
|
||||
<xamarin:StaticResource x:Key="HyperlinkButtonBorderBrushDisabled" ResourceKey="SystemControlTransparentBrush" />
|
||||
|
||||
<!-- Resources for Windows.UI.Xaml.Controls.Primitives.RepeatButton -->
|
||||
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
|
||||
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
|
||||
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
|
||||
|
||||
<!-- Resources for AccentButtonStyle -->
|
||||
<StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
|
@ -3013,4 +3064,65 @@
|
|||
<StaticResource x:Key="TimePickerLightDismissOverlayBackground" ResourceKey="SystemControlPageBackgroundMediumAltMediumBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!-- Control resources that are customizable but do not vary based on theme -->
|
||||
<Thickness x:Key="TextBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="TextBoxLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="AutoSuggestBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="AutoSuggestBoxLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="PasswordBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="PasswordBoxLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="RichEditBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="RichEditBoxLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="TimePickerTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="TimePickerLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="DatePickerTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="DatePickerLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="CalendarDatePickerTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="CalendarDatePickerLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="SliderTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="SliderLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="ComboBoxTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="ComboBoxLeftHeaderMargin">0,5,32,0</Thickness>
|
||||
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="ToggleSwitchLeftHeaderMargin">0,6,32,0</Thickness>
|
||||
<Thickness x:Key="ListBoxItemPadding">12,9,12,12</Thickness>
|
||||
<Thickness x:Key="TimePickerFlyoutPresenterItemPadding">0,3,0,6</Thickness>
|
||||
<Thickness x:Key="DatePickerFlyoutPresenterItemPadding">0,3,0,6</Thickness>
|
||||
<Thickness x:Key="DatePickerFlyoutPresenterMonthPadding">9,3,0,6</Thickness>
|
||||
|
||||
<Thickness x:Key="ToggleSwitchHeaderMargin">0,0,0,4</Thickness>
|
||||
<Thickness x:Key="ButtonPadding">8,4,8,5</Thickness>
|
||||
<Thickness x:Key="HyperlinkButtonPadding">0,6,0,7</Thickness>
|
||||
<x:Double x:Key="TimePickerFlyoutPresenterHighlightHeight">40</x:Double>
|
||||
<x:Double x:Key="TimePickerFlyoutPresenterAcceptDismissHostGridHeight">41</x:Double>
|
||||
<x:Double x:Key="TimePickerFlyoutPresenterItemHeight">40</x:Double>
|
||||
<x:Double x:Key="DatePickerFlyoutPresenterHighlightHeight">40</x:Double>
|
||||
<x:Double x:Key="DatePickerFlyoutPresenterAcceptDismissHostGridHeight">41</x:Double>
|
||||
<x:Double x:Key="DatePickerFlyoutPresenterItemHeight">40</x:Double>
|
||||
|
||||
<x:Double x:Key="TreeViewItemMinHeight">32</x:Double>
|
||||
<x:Double x:Key="TreeViewItemContentHeight">32</x:Double>
|
||||
<x:Double x:Key="SliderPreContentMargin">15</x:Double>
|
||||
<x:Double x:Key="SliderPostContentMargin">15</x:Double>
|
||||
<x:Double x:Key="SliderHorizontalHeight">32</x:Double>
|
||||
<x:Double x:Key="SliderVerticalWidth">32</x:Double>
|
||||
<x:Double x:Key="ToggleSwitchPreContentMargin">6</x:Double>
|
||||
<x:Double x:Key="ToggleSwitchPostContentMargin">6</x:Double>
|
||||
<x:Double x:Key="DatePickerThemeMinWidth">296</x:Double>
|
||||
<x:Double x:Key="DatePickerThemeMaxWidth">456</x:Double>
|
||||
<x:Double x:Key="TimePickerThemeMinWidth">242</x:Double>
|
||||
<x:Double x:Key="TimePickerThemeMaxWidth">456</x:Double>
|
||||
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double>
|
||||
<x:Double x:Key="AutoSuggestBoxLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="CalendarDatePickerLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="ComboBoxLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="DatePickerLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="PasswordBoxLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="RichEditBoxLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="SliderLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="TextBoxLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="TimePickerLeftHeaderMaxWidth">296</x:Double>
|
||||
<x:Double x:Key="ToggleSwitchLeftHeaderMaxWidth">296</x:Double>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace Windows.UI.Xaml
|
|||
var args = new PointerRoutedEventArgs(nativeEvent, pointerIndex, srcElement, this);
|
||||
var argsAction = MotionEventActions.Move;
|
||||
|
||||
handled |= OnNativeMotionEvent(args, argsAction, isInView);
|
||||
handled |= OnNativeMotionEvent(nativeEvent, args, argsAction, isInView);
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
@ -94,11 +94,11 @@ namespace Windows.UI.Xaml
|
|||
var args = new PointerRoutedEventArgs(nativeEvent, nativeEvent.ActionIndex, srcElement, this);
|
||||
var argsAction = actionMasked;
|
||||
|
||||
return OnNativeMotionEvent(args, argsAction, isInView);
|
||||
return OnNativeMotionEvent(nativeEvent, args, argsAction, isInView);
|
||||
}
|
||||
}
|
||||
|
||||
private bool OnNativeMotionEvent(PointerRoutedEventArgs args, MotionEventActions action, bool isInView)
|
||||
private bool OnNativeMotionEvent(MotionEvent nativeEvent, PointerRoutedEventArgs args, MotionEventActions action, bool isInView)
|
||||
{
|
||||
// Warning: MotionEvent of other kinds are filtered out in native code (UnoMotionHelper.java)
|
||||
switch (action)
|
||||
|
@ -117,16 +117,27 @@ namespace Windows.UI.Xaml
|
|||
case MotionEventActions.Down when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
|
||||
case MotionEventActions.PointerDown when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
|
||||
return OnNativePointerEnter(args) | OnNativePointerDown(args);
|
||||
case PointerRoutedEventArgs.StylusWithBarrelDown:
|
||||
case MotionEventActions.Down:
|
||||
case MotionEventActions.PointerDown:
|
||||
return OnNativePointerDown(args);
|
||||
case MotionEventActions.Up when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
|
||||
case MotionEventActions.PointerUp when args.Pointer.PointerDeviceType == PointerDeviceType.Touch:
|
||||
return OnNativePointerUp(args) | OnNativePointerExited(args);
|
||||
case PointerRoutedEventArgs.StylusWithBarrelUp:
|
||||
case MotionEventActions.Up:
|
||||
case MotionEventActions.PointerUp:
|
||||
return OnNativePointerUp(args);
|
||||
|
||||
// We get ACTION_DOWN and ACTION_UP only for "left" button, and instead we get a HOVER_MOVE when pressing/releasing the right button of the mouse.
|
||||
// So on each POINTER_MOVE we make sure to update the pressed state if it does not match.
|
||||
// Note: We can also have HOVER_MOVE with barrel button pressed, so we make sure to "PointerDown" only for Mouse.
|
||||
case MotionEventActions.HoverMove when args.Pointer.PointerDeviceType == PointerDeviceType.Mouse && args.HasPressedButton && !IsPressed(args.Pointer):
|
||||
return OnNativePointerDown(args) | OnNativePointerMoveWithOverCheck(args, isInView);
|
||||
case MotionEventActions.HoverMove when !args.HasPressedButton && IsPressed(args.Pointer):
|
||||
return OnNativePointerUp(args) | OnNativePointerMoveWithOverCheck(args, isInView);
|
||||
|
||||
case PointerRoutedEventArgs.StylusWithBarrelMove:
|
||||
case MotionEventActions.Move:
|
||||
case MotionEventActions.HoverMove:
|
||||
// Note: We use the OnNativePointerMove**WithOverCheck** in order to update the over state in case of press -> move out -> release
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
@ -132,7 +133,7 @@ namespace Windows.UI.Xaml
|
|||
if (sender is UIElement elt && !elt.IsHitTestVisibleCoalesced)
|
||||
{
|
||||
elt.Release(PointerCaptureKind.Any);
|
||||
elt.SetPressed(null, false, muteEvent: true);
|
||||
elt.ClearPressed();
|
||||
elt.SetOver(null, false, muteEvent: true);
|
||||
}
|
||||
};
|
||||
|
@ -142,7 +143,7 @@ namespace Windows.UI.Xaml
|
|||
if (sender is UIElement elt)
|
||||
{
|
||||
elt.Release(PointerCaptureKind.Any);
|
||||
elt.SetPressed(null, false, muteEvent: true);
|
||||
elt.ClearPressed();
|
||||
elt.SetOver(null, false, muteEvent: true);
|
||||
}
|
||||
};
|
||||
|
@ -221,6 +222,12 @@ namespace Windows.UI.Xaml
|
|||
that.SafeRaiseEvent(DoubleTappedEvent, new DoubleTappedRoutedEventArgs(that, args));
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly TypedEventHandler<GestureRecognizer, RightTappedEventArgs> OnRecognizerRightTapped = (sender, args) =>
|
||||
{
|
||||
var that = (UIElement)sender.Owner;
|
||||
that.SafeRaiseEvent(RightTappedEvent, new RightTappedRoutedEventArgs(that, args));
|
||||
};
|
||||
#endregion
|
||||
|
||||
private bool _isGestureCompleted;
|
||||
|
@ -235,6 +242,7 @@ namespace Windows.UI.Xaml
|
|||
recognizer.ManipulationInertiaStarting += OnRecognizerManipulationInertiaStarting;
|
||||
recognizer.ManipulationCompleted += OnRecognizerManipulationCompleted;
|
||||
recognizer.Tapped += OnRecognizerTapped;
|
||||
recognizer.RightTapped += OnRecognizerRightTapped;
|
||||
|
||||
// Allow partial parts to subscribe to pointer events (WASM)
|
||||
OnGestureRecognizerInitialized(recognizer);
|
||||
|
@ -319,6 +327,10 @@ namespace Windows.UI.Xaml
|
|||
{
|
||||
_gestures.Value.GestureSettings |= GestureSettings.DoubleTap;
|
||||
}
|
||||
else if (routedEvent == RightTappedEvent)
|
||||
{
|
||||
_gestures.Value.GestureSettings |= GestureSettings.RightTap;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -673,6 +685,8 @@ namespace Windows.UI.Xaml
|
|||
#endregion
|
||||
|
||||
#region Pointer pressed state (Updated by the partial API OnNative***)
|
||||
private readonly HashSet<uint> _pressedPointers = new HashSet<uint>();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a pointer was pressed while over the element (i.e. PressedState).
|
||||
/// Note: The pressed state will remain true even if the pointer exits the control (while pressed)
|
||||
|
@ -684,7 +698,7 @@ namespace Windows.UI.Xaml
|
|||
/// So it means that this flag will be maintained only if you subscribe at least to one pointer event
|
||||
/// (or override one of the OnPointer*** methods).
|
||||
/// </remarks>
|
||||
internal bool IsPointerPressed { get; set; } // TODO: 'Set' should be private, but we need to update all controls that are setting
|
||||
internal bool IsPointerPressed => _pressedPointers.Count != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a pointer was pressed while over the element (i.e. PressedState)
|
||||
|
@ -697,30 +711,49 @@ namespace Windows.UI.Xaml
|
|||
/// So it means that this method will give valid state only if you subscribe at least to one pointer event
|
||||
/// (or override one of the OnPointer*** methods).
|
||||
/// </remarks>
|
||||
internal bool IsPressed(Pointer pointer) => IsPointerPressed;
|
||||
/// <remarks>
|
||||
/// Note that on UWP the "pressed" state is managed **PER POINTER**, and not per pressed button on the given pointer.
|
||||
/// It means that with a mouse if you follow this sequence : press left => press right => release right => release left,
|
||||
/// you will get only one 'PointerPressed' and one 'PointerReleased'.
|
||||
/// Same thing if you release left first (press left => press right => release left => release right), and for the pen's barrel button.
|
||||
/// </remarks>
|
||||
internal bool IsPressed(Pointer pointer) => _pressedPointers.Contains(pointer.PointerId);
|
||||
|
||||
private bool SetPressed(PointerRoutedEventArgs args, bool isPressed, bool muteEvent = false)
|
||||
{
|
||||
var wasPressed = IsPointerPressed;
|
||||
IsPointerPressed = isPressed;
|
||||
|
||||
if (muteEvent
|
||||
|| wasPressed == isPressed) // nothing changed
|
||||
var wasPressed = IsPressed(args.Pointer);
|
||||
if (wasPressed == isPressed) // nothing changed
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPressed) // Pressed
|
||||
{
|
||||
_pressedPointers.Add(args.Pointer.PointerId);
|
||||
|
||||
if (muteEvent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
args.Handled = false;
|
||||
return RaisePointerEvent(PointerPressedEvent, args);
|
||||
}
|
||||
else // Released
|
||||
{
|
||||
_pressedPointers.Remove(args.Pointer.PointerId);
|
||||
|
||||
if (muteEvent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
args.Handled = false;
|
||||
return RaisePointerEvent(PointerReleasedEvent, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearPressed() => _pressedPointers.Clear();
|
||||
#endregion
|
||||
|
||||
#region Pointer capture state (Updated by the partial API OnNative***)
|
||||
|
|
|
@ -109,7 +109,7 @@ namespace Windows.UI.Xaml
|
|||
private static PointerRoutedEventArgs PayloadToPointerArgs(object snd, string payload, bool isInContact, bool canBubble = true)
|
||||
{
|
||||
var parts = payload?.Split(';');
|
||||
if (parts?.Length != 10)
|
||||
if (parts?.Length != 11)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ namespace Windows.UI.Xaml
|
|||
var typeStr = parts[7];
|
||||
var srcHandle = int.Parse(parts[8], CultureInfo.InvariantCulture);
|
||||
var timestamp = double.Parse(parts[9], CultureInfo.InvariantCulture);
|
||||
var pressure = double.Parse(parts[10], CultureInfo.InvariantCulture);
|
||||
|
||||
var src = GetElementFromHandle(srcHandle) ?? (UIElement)snd;
|
||||
var position = new Point(x, y);
|
||||
|
@ -141,6 +142,7 @@ namespace Windows.UI.Xaml
|
|||
(WindowManagerInterop.HtmlPointerButtonsState)buttons,
|
||||
(WindowManagerInterop.HtmlPointerButtonUpdate)buttonUpdate,
|
||||
keyModifiers,
|
||||
pressure,
|
||||
src,
|
||||
canBubble);
|
||||
}
|
||||
|
|
|
@ -105,6 +105,8 @@ namespace Windows.UI.Xaml
|
|||
|
||||
public static RoutedEvent DoubleTappedEvent { get; } = new RoutedEvent(RoutedEventFlag.DoubleTapped);
|
||||
|
||||
public static RoutedEvent RightTappedEvent { get; } = new RoutedEvent(RoutedEventFlag.RightTapped);
|
||||
|
||||
public static RoutedEvent KeyDownEvent { get; } = new RoutedEvent(RoutedEventFlag.KeyDown);
|
||||
|
||||
public static RoutedEvent KeyUpEvent { get; } = new RoutedEvent(RoutedEventFlag.KeyUp);
|
||||
|
@ -297,6 +299,12 @@ namespace Windows.UI.Xaml
|
|||
remove => RemoveHandler(DoubleTappedEvent, value);
|
||||
}
|
||||
|
||||
public event RightTappedEventHandler RightTapped
|
||||
{
|
||||
add => AddHandler(RightTappedEvent, value, false);
|
||||
remove => RemoveHandler(RightTappedEvent, value);
|
||||
}
|
||||
|
||||
#if __MACOS__
|
||||
public new event KeyEventHandler KeyDown
|
||||
#else
|
||||
|
@ -661,6 +669,9 @@ namespace Windows.UI.Xaml
|
|||
case DoubleTappedEventHandler doubleTappedEventHandler:
|
||||
doubleTappedEventHandler(this, (DoubleTappedRoutedEventArgs)args);
|
||||
break;
|
||||
case RightTappedEventHandler rightTappedEventHandler:
|
||||
rightTappedEventHandler(this, (RightTappedRoutedEventArgs)args);
|
||||
break;
|
||||
case KeyEventHandler keyEventHandler:
|
||||
keyEventHandler(this, (KeyRoutedEventArgs)args);
|
||||
break;
|
||||
|
|
|
@ -244,7 +244,7 @@ namespace Windows.UI.Xaml
|
|||
|
||||
if (evt.IsTouchInView(this))
|
||||
{
|
||||
IsPointerPressed = true;
|
||||
_pressedPointers.Add(0);
|
||||
IsPointerOver = true;
|
||||
|
||||
// evt.AllTouches raises a invalid selector exception
|
||||
|
@ -317,7 +317,7 @@ namespace Windows.UI.Xaml
|
|||
base.MouseUp(evt);
|
||||
}
|
||||
|
||||
IsPointerPressed = false;
|
||||
ClearPressed();
|
||||
IsPointerOver = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -2,93 +2,5 @@
|
|||
#pragma warning disable 114 // new keyword hiding
|
||||
namespace Windows.Globalization.NumberFormatting
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
#endif
|
||||
public partial class SignificantDigitsNumberRounder : global::Windows.Globalization.NumberFormatting.INumberRounder
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public uint SignificantDigits
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member uint SignificantDigitsNumberRounder.SignificantDigits is not implemented in Uno.");
|
||||
}
|
||||
set
|
||||
{
|
||||
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder", "uint SignificantDigitsNumberRounder.SignificantDigits");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.Globalization.NumberFormatting.RoundingAlgorithm RoundingAlgorithm
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member RoundingAlgorithm SignificantDigitsNumberRounder.RoundingAlgorithm is not implemented in Uno.");
|
||||
}
|
||||
set
|
||||
{
|
||||
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder", "RoundingAlgorithm SignificantDigitsNumberRounder.RoundingAlgorithm");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public SignificantDigitsNumberRounder()
|
||||
{
|
||||
global::Windows.Foundation.Metadata.ApiInformation.TryRaiseNotImplemented("Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder", "SignificantDigitsNumberRounder.SignificantDigitsNumberRounder()");
|
||||
}
|
||||
#endif
|
||||
// Forced skipping of method Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder.SignificantDigitsNumberRounder()
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public int RoundInt32( int value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member int SignificantDigitsNumberRounder.RoundInt32(int value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public uint RoundUInt32( uint value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member uint SignificantDigitsNumberRounder.RoundUInt32(uint value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public long RoundInt64( long value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member long SignificantDigitsNumberRounder.RoundInt64(long value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public ulong RoundUInt64( ulong value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member ulong SignificantDigitsNumberRounder.RoundUInt64(ulong value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public float RoundSingle( float value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member float SignificantDigitsNumberRounder.RoundSingle(float value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
[global::Uno.NotImplemented]
|
||||
public double RoundDouble( double value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member double SignificantDigitsNumberRounder.RoundDouble(double value) is not implemented in Uno.");
|
||||
}
|
||||
#endif
|
||||
// Forced skipping of method Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder.RoundingAlgorithm.get
|
||||
// Forced skipping of method Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder.RoundingAlgorithm.set
|
||||
// Forced skipping of method Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder.SignificantDigits.get
|
||||
// Forced skipping of method Windows.Globalization.NumberFormatting.SignificantDigitsNumberRounder.SignificantDigits.set
|
||||
// Processing: Windows.Globalization.NumberFormatting.INumberRounder
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -467,7 +467,7 @@ namespace Windows.UI.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public event global::Windows.Foundation.TypedEventHandler<global::Windows.UI.Input.GestureRecognizer, global::Windows.UI.Input.RightTappedEventArgs> RightTapped
|
||||
{
|
||||
|
|
|
@ -177,7 +177,7 @@ namespace Windows.UI.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public float Pressure
|
||||
{
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
#pragma warning disable 114 // new keyword hiding
|
||||
namespace Windows.UI.Input
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
#endif
|
||||
public partial class RightTappedEventArgs
|
||||
{
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.Devices.Input.PointerDeviceType PointerDeviceType
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ namespace Windows.UI.Input
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __MACOS__
|
||||
#if false
|
||||
[global::Uno.NotImplemented]
|
||||
public global::Windows.Foundation.Point Position
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using Uno;
|
||||
|
||||
namespace Windows.Globalization.NumberFormatting
|
||||
{
|
||||
public partial class SignificantDigitsNumberRounder : INumberRounder
|
||||
{
|
||||
public uint SignificantDigits { get; set; } = 0;
|
||||
|
||||
[NotImplemented]
|
||||
public RoundingAlgorithm RoundingAlgorithm { get; set; } = RoundingAlgorithm.RoundHalfUp;
|
||||
|
||||
public SignificantDigitsNumberRounder()
|
||||
{
|
||||
}
|
||||
|
||||
[NotImplemented]
|
||||
public int RoundInt32( int value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member int SignificantDigitsNumberRounder.RoundInt32(int value) is not implemented in Uno.");
|
||||
}
|
||||
|
||||
[NotImplemented]
|
||||
public uint RoundUInt32( uint value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member uint SignificantDigitsNumberRounder.RoundUInt32(uint value) is not implemented in Uno.");
|
||||
}
|
||||
|
||||
[NotImplemented]
|
||||
public long RoundInt64( long value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member long SignificantDigitsNumberRounder.RoundInt64(long value) is not implemented in Uno.");
|
||||
}
|
||||
|
||||
[NotImplemented]
|
||||
public ulong RoundUInt64( ulong value)
|
||||
{
|
||||
throw new global::System.NotImplementedException("The member ulong SignificantDigitsNumberRounder.RoundUInt64(ulong value) is not implemented in Uno.");
|
||||
}
|
||||
|
||||
public float RoundSingle( float value)
|
||||
{
|
||||
return (float)Math.Round(value, (int)SignificantDigits, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
public double RoundDouble( double value)
|
||||
{
|
||||
return Math.Round(value, (int)SignificantDigits, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Windows.Devices.Input;
|
||||
using Windows.Foundation;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Uno.Extensions;
|
||||
|
@ -72,19 +73,16 @@ namespace Windows.UI.Input
|
|||
|
||||
internal void ProcessMoveEvents(IList<PointerPoint> value, bool isRelevant)
|
||||
{
|
||||
if (isRelevant)
|
||||
foreach (var point in value)
|
||||
{
|
||||
foreach (var point in value)
|
||||
if (_activePointers.TryGetValue(point.PointerId, out var points))
|
||||
{
|
||||
if (_activePointers.TryGetValue(point.PointerId, out var points))
|
||||
{
|
||||
points.Add(point);
|
||||
}
|
||||
else if (_log.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
// info: We might get some PointerMove for mouse even if not pressed!
|
||||
_log.Info("Received a 'Move' for a pointer which was not considered as down. Ignoring event.");
|
||||
}
|
||||
points.Add(point);
|
||||
}
|
||||
else if (_log.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
// info: We might get some PointerMove for mouse even if not pressed!
|
||||
_log.Info("Received a 'Move' for a pointer which was not considered as down. Ignoring event.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,18 +101,14 @@ namespace Windows.UI.Input
|
|||
if (_activePointers.Remove(value.PointerId, out var points))
|
||||
{
|
||||
#endif
|
||||
|
||||
// Note: At this point we MAY be IsActive == false, which is the expected behavior (same as UWP)
|
||||
// even if we will fire some events now.
|
||||
|
||||
if (isRelevant)
|
||||
{
|
||||
// We need to process only events that are bubbling natively to this control (i.e. isIrrelevant == false),
|
||||
// if they are bubbling in managed it means that they where handled a child control,
|
||||
// so we should not use them for gesture recognition.
|
||||
// We need to process only events that are bubbling natively to this control (i.e. isIrrelevant == false),
|
||||
// if they are bubbling in managed it means that they where handled a child control,
|
||||
// so we should not use them for gesture recognition.
|
||||
|
||||
Recognize(points, pointerUp: value);
|
||||
}
|
||||
Recognize(points, pointerUp: value);
|
||||
|
||||
_manipulation?.Remove(value);
|
||||
}
|
||||
|
@ -154,7 +148,8 @@ namespace Windows.UI.Input
|
|||
return;
|
||||
}
|
||||
|
||||
var recognized = TryRecognizeTap(points, pointerUp);
|
||||
var recognized = TryRecognizeRightTap(points, pointerUp) // We check right tap first as for touch a right tap is a press and hold of the finger :)
|
||||
|| TryRecognizeTap(points, pointerUp);
|
||||
|
||||
if (!recognized && _log.IsEnabled(LogLevel.Information))
|
||||
{
|
||||
|
@ -177,47 +172,79 @@ namespace Windows.UI.Input
|
|||
internal Manipulation PendingManipulation => _manipulation;
|
||||
#endregion
|
||||
|
||||
#region Tap (includes DoubleTap)
|
||||
#region Tap (includes DoubleTap and RightTap)
|
||||
internal const ulong MultiTapMaxDelayTicks = TimeSpan.TicksPerMillisecond * 1000;
|
||||
internal const ulong HoldMinDelayTicks = TimeSpan.TicksPerMillisecond * 800;
|
||||
internal const float HoldMinPressure = .75f;
|
||||
internal const int TapMaxXDelta = 10;
|
||||
internal const int TapMaxYDelta = 10;
|
||||
|
||||
public event TypedEventHandler<GestureRecognizer, TappedEventArgs> Tapped;
|
||||
private (ulong id, ulong ts, Point position) _lastSingleTap;
|
||||
|
||||
private (ulong, ulong, Point) _lastSingleTap;
|
||||
public event TypedEventHandler<GestureRecognizer, TappedEventArgs> Tapped;
|
||||
public event TypedEventHandler<GestureRecognizer, RightTappedEventArgs> RightTapped;
|
||||
|
||||
public bool CanBeDoubleTap(PointerPoint value)
|
||||
{
|
||||
if (!_gestureSettings.HasFlag(GestureSettings.DoubleTap))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var (lastTapId, lastTapTs, lastTapLocation) = _lastSingleTap;
|
||||
if (lastTapTs == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentId = GetPointerIdentifier(value);
|
||||
var currentTs = value.Timestamp;
|
||||
var currentPosition = value.Position;
|
||||
|
||||
return lastTapId == currentId
|
||||
&& currentTs - lastTapTs <= MultiTapMaxDelayTicks
|
||||
&& !IsOutOfTapRange(lastTapLocation, currentPosition);
|
||||
}
|
||||
=> _gestureSettings.HasFlag(GestureSettings.DoubleTap) && IsMultiTapGesture(_lastSingleTap, value);
|
||||
|
||||
private bool TryRecognizeTap(List<PointerPoint> points, PointerPoint pointerUp = null)
|
||||
{
|
||||
if (pointerUp == null)
|
||||
if (IsTapGesture(LeftButton, points, pointerUp, out var start, out _))
|
||||
{
|
||||
// pointerUp is not null here!
|
||||
|
||||
_lastSingleTap = (start.id, pointerUp.Timestamp, pointerUp.Position);
|
||||
Tapped?.Invoke(this, new TappedEventArgs(start.point.PointerDevice.PointerDeviceType, start.point.Position, tapCount: 1));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var start = points[0]; // down
|
||||
var end = pointerUp;
|
||||
var startIdentifier = GetPointerIdentifier(start);
|
||||
private bool TryRecognizeMultiTap(PointerPoint pointerDown)
|
||||
{
|
||||
if (_gestureSettings.HasFlag(GestureSettings.DoubleTap) && IsMultiTapGesture(_lastSingleTap, pointerDown))
|
||||
{
|
||||
_lastSingleTap = default; // The Recognizer supports only double tap, even on UWP
|
||||
Tapped?.Invoke(this, new TappedEventArgs(pointerDown.PointerDevice.PointerDeviceType, pointerDown.Position, tapCount: 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRecognizeRightTap(List<PointerPoint> points, PointerPoint pointerUp = null)
|
||||
{
|
||||
if (_gestureSettings.HasFlag(GestureSettings.RightTap) && IsRightTapGesture(points, pointerUp, out var start))
|
||||
{
|
||||
RightTapped?.Invoke(this, new RightTappedEventArgs(start.point.PointerDevice.PointerDeviceType, start.point.Position));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Actual Tap gestures recognition (static)
|
||||
// The beginning of a Tap gesture is: 1 down -> * moves close to the down with same buttons pressed
|
||||
private static bool IsBeginningTapGesture(CheckButton isExpectedButton, List<PointerPoint> points, out (PointerPoint point, ulong id) start, out bool isForceTouch)
|
||||
{
|
||||
var startPoint = points[0]; // down
|
||||
start = (startPoint, GetPointerIdentifier(startPoint));
|
||||
isForceTouch = false;
|
||||
|
||||
if (!isExpectedButton(start.point)) // We validate only the start as for other points we validate the full pointer identifier
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate tap gesture
|
||||
// Note: There is no limit for the duration of the tap!
|
||||
|
@ -228,46 +255,136 @@ namespace Windows.UI.Input
|
|||
|
||||
if (
|
||||
// The pointer changed (left vs right click)
|
||||
pointIdentifier != startIdentifier
|
||||
pointIdentifier != start.id
|
||||
|
||||
// Pointer moved to far
|
||||
|| IsOutOfTapRange(point.Position, start.Position)
|
||||
|| IsOutOfTapRange(point.Position, start.point.Position)
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
isForceTouch |= point.Properties.Pressure >= HoldMinPressure;
|
||||
}
|
||||
// For the pointer up, we check only the distance, as it's expected that the IsLeftButtonPressed changed!
|
||||
if (IsOutOfTapRange(end.Position, start.Position))
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// A Tap gesture is: 1 down -> * moves close to the down with same buttons pressed -> 1 up
|
||||
private static bool IsTapGesture(CheckButton isExpectedButton, List<PointerPoint> points, PointerPoint pointerUp, out (PointerPoint point, ulong id) start, out bool isForceTouch)
|
||||
{
|
||||
if (pointerUp == null) // no tap if no up!
|
||||
{
|
||||
start = default;
|
||||
isForceTouch = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that all the intermediates points are valid
|
||||
if (!IsBeginningTapGesture(isExpectedButton, points, out start, out isForceTouch))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_lastSingleTap = (startIdentifier, end.Timestamp, end.Position);
|
||||
Tapped?.Invoke(this, new TappedEventArgs(start.PointerDevice.PointerDeviceType, start.Position, tapCount: 1));
|
||||
// For the pointer up, we check only the distance, as it's expected that the pressed button changed!
|
||||
if (IsOutOfTapRange(pointerUp.Position, start.point.Position))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsMultiTapGesture((ulong id, ulong ts, Point position) previousTap, PointerPoint down)
|
||||
{
|
||||
if (previousTap.ts == 0) // i.s. no previous tap to compare with
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentId = GetPointerIdentifier(down);
|
||||
var currentTs = down.Timestamp;
|
||||
var currentPosition = down.Position;
|
||||
|
||||
return previousTap.id == currentId
|
||||
&& currentTs - previousTap.ts <= MultiTapMaxDelayTicks
|
||||
&& !IsOutOfTapRange(previousTap.position, currentPosition);
|
||||
}
|
||||
|
||||
private static bool IsRightTapGesture(List<PointerPoint> points, PointerPoint pointerUp, out (PointerPoint point, ulong id) start)
|
||||
{
|
||||
switch (points[0].PointerDevice.PointerDeviceType)
|
||||
{
|
||||
case PointerDeviceType.Touch:
|
||||
var isLeftTap = IsTapGesture(LeftButton, points, pointerUp, out start, out var isForceTouch);
|
||||
if (isLeftTap && pointerUp.Timestamp - start.point.Timestamp > HoldMinDelayTicks)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#if __IOS__
|
||||
if (Uno.WinRTFeatureConfiguration.GestureRecognizer.InterpretForceTouchAsRightTap
|
||||
&& isLeftTap
|
||||
&& isForceTouch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
|
||||
case PointerDeviceType.Pen:
|
||||
if (IsTapGesture(BarrelButton, points, pointerUp, out start, out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some pens does not have a barrel button, so we also allow long press (and anyway it's the UWP behavior)
|
||||
if (IsTapGesture(LeftButton, points, pointerUp, out start, out _)
|
||||
&& pointerUp.Timestamp - start.point.Timestamp > HoldMinDelayTicks)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case PointerDeviceType.Mouse:
|
||||
if (IsTapGesture(RightButton, points, pointerUp, out start, out _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#if __ANDROID__
|
||||
// On Android, usually the right button is mapped to back navigation. So, unlike UWP,
|
||||
// we also allow a long press with the left button to be more user friendly.
|
||||
if (Uno.WinRTFeatureConfiguration.GestureRecognizer.InterpretMouseLeftLongPressAsRightTap
|
||||
&& IsTapGesture(LeftButton, points, pointerUp, out start, out _)
|
||||
&& pointerUp.Timestamp - start.point.Timestamp > HoldMinDelayTicks)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
|
||||
default:
|
||||
start = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Tap helpers
|
||||
private delegate bool CheckButton(PointerPoint point);
|
||||
|
||||
private static readonly CheckButton LeftButton = (PointerPoint point) => point.Properties.IsLeftButtonPressed;
|
||||
private static readonly CheckButton RightButton = (PointerPoint point) => point.Properties.IsRightButtonPressed;
|
||||
private static readonly CheckButton BarrelButton = (PointerPoint point) => point.Properties.IsBarrelButtonPressed;
|
||||
|
||||
private static bool IsOutOfTapRange(Point p1, Point p2)
|
||||
=> Math.Abs(p1.X - p2.X) > TapMaxXDelta
|
||||
|| Math.Abs(p1.Y - p2.Y) > TapMaxYDelta;
|
||||
|
||||
private bool TryRecognizeMultiTap(PointerPoint pointerDown)
|
||||
{
|
||||
if (!CanBeDoubleTap(pointerDown))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_lastSingleTap = default; // The Recognizer supports only double tap, even on UWP
|
||||
Tapped?.Invoke(this, new TappedEventArgs(pointerDown.PointerDevice.PointerDeviceType, pointerDown.Position, tapCount: 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private ulong GetPointerIdentifier(PointerPoint point)
|
||||
#endregion
|
||||
|
||||
private static ulong GetPointerIdentifier(PointerPoint point)
|
||||
{
|
||||
// For mouse, the PointerId is the same, no matter the button pressed.
|
||||
// The only thing that changes are flags in the properties.
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Windows.UI.Input
|
|||
[global::Uno.NotImplemented] // The GestureRecognizer won't raise this event
|
||||
HoldWithMouse = 8U,
|
||||
/// <summary>Enable support for a right-tap interaction. The RightTapped event is raised when the contact is lifted or the mouse button released.</summary>
|
||||
[global::Uno.NotImplemented] // The GestureRecognizer won't raise this event
|
||||
RightTap = 16U,
|
||||
/// <summary>Enable support for the slide or swipe gesture with a mouse or pen/stylus (single contact). The Dragging event is raised when either gesture is detected.This gesture can be used for text selection, selecting or rearranging objects, or scrolling and panning.</summary>
|
||||
[global::Uno.NotImplemented] // The GestureRecognizer won't raise this event
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace Windows.UI.Input
|
|||
{
|
||||
}
|
||||
|
||||
internal bool HasPressedButton => IsLeftButtonPressed || IsMiddleButtonPressed || IsRightButtonPressed || IsXButton1Pressed || IsXButton2Pressed || IsBarrelButtonPressed;
|
||||
|
||||
public bool IsPrimary { get; internal set; }
|
||||
|
||||
public bool IsInRange { get; internal set; }
|
||||
|
@ -28,6 +30,8 @@ namespace Windows.UI.Input
|
|||
|
||||
public bool IsEraser { get; internal set; }
|
||||
|
||||
public float Pressure { get; internal set; } = 0.5f; // According to the doc, the default value is .5
|
||||
|
||||
public PointerUpdateKind PointerUpdateKind { get; internal set; }
|
||||
|
||||
[global::Uno.NotImplemented]
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
using Windows.Devices.Input;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Windows.UI.Input
|
||||
{
|
||||
public partial class RightTappedEventArgs
|
||||
{
|
||||
internal RightTappedEventArgs(PointerDeviceType type, Point position)
|
||||
{
|
||||
PointerDeviceType = type;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public PointerDeviceType PointerDeviceType { get; }
|
||||
|
||||
public Point Position { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
|
||||
namespace Uno
|
||||
{
|
||||
public static class WinRTFeatureConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by tests cleanup to restore the default configuration for other tests!
|
||||
/// </summary>
|
||||
internal static void RestoreDefaults()
|
||||
{
|
||||
GestureRecognizer.RestoreDefaults();
|
||||
}
|
||||
|
||||
public static class GestureRecognizer
|
||||
{
|
||||
internal static void RestoreDefaults()
|
||||
{
|
||||
#if __ANDROID__
|
||||
InterpretMouseLeftLongPressAsRightTap = _defaultInterpretMouseLeftLongPressAsRightTap;
|
||||
#elif __IOS__
|
||||
InterpretForceTouchAsRightTap = _defaultInterpretForceTouchAsRightTap;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __ANDROID__
|
||||
private const bool _defaultInterpretMouseLeftLongPressAsRightTap = true;
|
||||
/// <summary>
|
||||
/// Determines if unlike UWP, long press on the left button of a mouse should be interpreted as a right tap.
|
||||
/// This is useful as the right button is commonly used by Android devices for back navigation.
|
||||
/// Using a long press with left button will be more intuitive for Android's users.
|
||||
/// Note that a long press on the right button is usually not used for back navigation, and will always be interpreted
|
||||
/// as a right tap no matter the value of this flag.
|
||||
/// </summary>
|
||||
[DefaultValue(_defaultInterpretMouseLeftLongPressAsRightTap)]
|
||||
public static bool InterpretMouseLeftLongPressAsRightTap { get; set; } = _defaultInterpretMouseLeftLongPressAsRightTap;
|
||||
#endif
|
||||
#if __IOS__
|
||||
private const bool _defaultInterpretForceTouchAsRightTap = true;
|
||||
/// <summary>
|
||||
/// Determines if force touch (a.k.a. 3D touch) should be interpreted as a right tap.
|
||||
/// Note that a long press will always be interpreted as a right tap no matter the value of this flag.
|
||||
/// </summary>
|
||||
[DefaultValue(_defaultInterpretForceTouchAsRightTap)]
|
||||
public static bool InterpretForceTouchAsRightTap { get; set; } = _defaultInterpretForceTouchAsRightTap;
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче