committed by
GitHub
65 changed files with 710 additions and 429 deletions
@ -0,0 +1,27 @@ |
|||
namespace Avalonia.Layout; |
|||
|
|||
/// <summary>
|
|||
/// Provides access to layout information of a control.
|
|||
/// </summary>
|
|||
public static class LayoutInformation |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the available size constraint passed in the previous layout pass.
|
|||
/// </summary>
|
|||
/// <param name="control">The control.</param>
|
|||
/// <returns>Previous control measure constraint, if any.</returns>
|
|||
public static Size? GetPreviousMeasureConstraint(Layoutable control) |
|||
{ |
|||
return control.PreviousMeasure; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the control bounds used in the previous layout arrange pass.
|
|||
/// </summary>
|
|||
/// <param name="control">The control.</param>
|
|||
/// <returns>Previous control arrange bounds, if any.</returns>
|
|||
public static Rect? GetPreviousArrangeBounds(Layoutable control) |
|||
{ |
|||
return control.PreviousArrange; |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using Avalonia.Automation.Peers; |
|||
|
|||
namespace Avalonia.Controls.Automation.Peers |
|||
{ |
|||
public class SliderAutomationPeer : RangeBaseAutomationPeer |
|||
{ |
|||
public SliderAutomationPeer(Slider owner) : base(owner) |
|||
{ |
|||
} |
|||
|
|||
override protected string GetClassNameCore() |
|||
{ |
|||
return "Slider"; |
|||
} |
|||
|
|||
override protected AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.Slider; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Browser.Interop; |
|||
|
|||
namespace Avalonia.Browser; |
|||
|
|||
public class BrowserPlatformOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Defines paths where avalonia modules and service locator should be resolved.
|
|||
/// If null, default path resolved depending on the backend (browser or blazor) is used.
|
|||
/// </summary>
|
|||
public Func<string, string>? FrameworkAssetPathResolver { get; set; } |
|||
} |
|||
|
|||
public static class BrowserAppBuilder |
|||
{ |
|||
/// <summary>
|
|||
/// Configures browser backend, loads avalonia javascript modules and creates a single view lifetime from the passed <see cref="mainDivId"/> parameter.
|
|||
/// </summary>
|
|||
/// <param name="builder">Application builder.</param>
|
|||
/// <param name="mainDivId">ID of the html element where avalonia content should be rendered.</param>
|
|||
/// <param name="options">Browser backend specific options.</param>
|
|||
public static async Task StartBrowserAppAsync(this AppBuilder builder, string mainDivId, BrowserPlatformOptions? options = null) |
|||
{ |
|||
if (mainDivId is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(mainDivId)); |
|||
} |
|||
|
|||
builder = await PreSetupBrowser(builder, options); |
|||
|
|||
var lifetime = new BrowserSingleViewLifetime(); |
|||
builder |
|||
.AfterSetup(_ => |
|||
{ |
|||
lifetime.View = new AvaloniaView(mainDivId); |
|||
}) |
|||
.SetupWithLifetime(lifetime); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Loads avalonia javascript modules and configures browser backend.
|
|||
/// </summary>
|
|||
/// <param name="builder">Application builder.</param>
|
|||
/// <param name="options">Browser backend specific options.</param>
|
|||
/// <remarks>
|
|||
/// This method doesn't creates any avalonia views to be rendered. To do so create an <see cref="AvaloniaView"/> object.
|
|||
/// Alternatively, you can call <see cref="StartBrowserAppAsync"/> method instead of <see cref="SetupBrowserAppAsync"/>.
|
|||
/// </remarks>
|
|||
public static async Task SetupBrowserAppAsync(this AppBuilder builder, BrowserPlatformOptions? options = null) |
|||
{ |
|||
builder = await PreSetupBrowser(builder, options); |
|||
|
|||
builder |
|||
.SetupWithoutStarting(); |
|||
} |
|||
|
|||
internal static async Task<AppBuilder> PreSetupBrowser(AppBuilder builder, BrowserPlatformOptions? options) |
|||
{ |
|||
options ??= new BrowserPlatformOptions(); |
|||
options.FrameworkAssetPathResolver ??= fileName => $"./{fileName}"; |
|||
|
|||
AvaloniaLocator.CurrentMutable.Bind<BrowserPlatformOptions>().ToConstant(options); |
|||
|
|||
await AvaloniaModule.ImportMain(); |
|||
|
|||
if (builder.WindowingSubsystemInitializer is null) |
|||
{ |
|||
builder = builder.UseBrowser(); |
|||
} |
|||
|
|||
return builder; |
|||
} |
|||
|
|||
public static AppBuilder UseBrowser( |
|||
this AppBuilder builder) |
|||
{ |
|||
return builder |
|||
.UseWindowingSubsystem(BrowserWindowingPlatform.Register) |
|||
.UseSkia(); |
|||
} |
|||
} |
|||
@ -1,47 +1,36 @@ |
|||
using System; |
|||
using System.Diagnostics.CodeAnalysis; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using System.Runtime.Versioning; |
|||
using Avalonia.Browser; |
|||
|
|||
namespace Avalonia.Browser; |
|||
namespace Avalonia; |
|||
|
|||
public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime |
|||
internal class BrowserSingleViewLifetime : ISingleViewApplicationLifetime |
|||
{ |
|||
public AvaloniaView? View; |
|||
|
|||
public Control? MainView |
|||
{ |
|||
get => View!.Content; |
|||
set => View!.Content = value; |
|||
} |
|||
} |
|||
|
|||
public class BrowserPlatformOptions |
|||
{ |
|||
public Func<string, string> FrameworkAssetPathResolver { get; set; } = new(fileName => $"./{fileName}"); |
|||
} |
|||
|
|||
public static class WebAppBuilder |
|||
{ |
|||
public static AppBuilder SetupBrowserApp( |
|||
this AppBuilder builder, string mainDivId) |
|||
{ |
|||
var lifetime = new BrowserSingleViewLifetime(); |
|||
|
|||
return builder |
|||
.UseBrowser() |
|||
.AfterSetup(b => |
|||
{ |
|||
lifetime.View = new AvaloniaView(mainDivId); |
|||
}) |
|||
.SetupWithLifetime(lifetime); |
|||
get |
|||
{ |
|||
EnsureView(); |
|||
return View.Content; |
|||
} |
|||
set |
|||
{ |
|||
EnsureView(); |
|||
View.Content = value; |
|||
} |
|||
} |
|||
|
|||
public static AppBuilder UseBrowser( |
|||
this AppBuilder builder) |
|||
[MemberNotNull(nameof(View))] |
|||
private void EnsureView() |
|||
{ |
|||
return builder |
|||
.UseWindowingSubsystem(BrowserWindowingPlatform.Register) |
|||
.UseSkia(); |
|||
if (View is null) |
|||
{ |
|||
throw new InvalidOperationException("Browser lifetime was not initialized. Make sure AppBuilder.StartBrowserApp was called."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using OpenQA.Selenium.Appium; |
|||
using OpenQA.Selenium.Interactions; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.IntegrationTests.Appium |
|||
{ |
|||
[Collection("Default")] |
|||
public class SliderTests |
|||
{ |
|||
private readonly AppiumDriver<AppiumWebElement> _session; |
|||
|
|||
public SliderTests(TestAppFixture fixture) |
|||
{ |
|||
_session = fixture.Session; |
|||
|
|||
var tabs = _session.FindElementByAccessibilityId("MainTabs"); |
|||
var tab = tabs.FindElementByName("SliderTab"); |
|||
tab.Click(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Changes_Value_When_Clicking_Increase_Button() |
|||
{ |
|||
var slider = _session.FindElementByAccessibilityId("Slider"); |
|||
|
|||
// slider.Text gets the Slider value
|
|||
Assert.True(double.Parse(slider.Text) == 30); |
|||
|
|||
new Actions(_session).Click(slider).Perform(); |
|||
|
|||
Assert.Equal(50, Math.Round(double.Parse(slider.Text))); |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 694 B |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 694 B |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
Loading…
Reference in new issue