Merge branch 'rel/3.0.0-preview' into mhawker/uniformgrid-as-grid
# Conflicts: # docs/extensions/StringExtensions.md
|
@ -1,10 +1,14 @@
|
|||
trigger:
|
||||
- master
|
||||
- rel/*
|
||||
|
||||
variables:
|
||||
BuildConfiguration: Release
|
||||
|
||||
steps:
|
||||
- task: BatchScript@1
|
||||
inputs:
|
||||
filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat"
|
||||
filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\Tools\\VsDevCmd.bat"
|
||||
arguments: -no_logo
|
||||
modifyEnvironment: true
|
||||
displayName: Setup Environment Variables
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
variables:
|
||||
BuildConfiguration: Release
|
||||
|
||||
steps:
|
||||
- task: BatchScript@1
|
||||
inputs:
|
||||
filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat"
|
||||
arguments: -no_logo
|
||||
modifyEnvironment: true
|
||||
displayName: Setup Environment Variables
|
||||
|
||||
- task: NuGetToolInstaller@0
|
||||
displayName: Use NuGet 4.6.2
|
||||
inputs:
|
||||
versionSpec: 4.6.2
|
||||
|
||||
- powershell: |
|
||||
.\build\build.ps1 -target=Build
|
||||
displayName: Build
|
||||
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Package Artifacts
|
||||
inputs:
|
||||
pathToPublish: .\bin\nupkg
|
||||
artifactType: container
|
||||
artifactName: Packages
|
||||
condition: always()
|
|
@ -19,12 +19,14 @@
|
|||
<IsDesignProject>$(MSBuildProjectName.Contains('.Design'))</IsDesignProject>
|
||||
<IsTestProject>$(MSBuildProjectName.Contains('Test'))</IsTestProject>
|
||||
<IsUwpProject Condition="'$(IsDesignProject)' != 'true'">$(MSBuildProjectName.Contains('Uwp'))</IsUwpProject>
|
||||
<IsWin32Project Condition="'$(IsDesignProject)' != 'true'">$(MSBuildProjectName.Contains('Win32'))</IsWin32Project>
|
||||
<IsSampleProject>$(MSBuildProjectName.Contains('Sample'))</IsSampleProject>
|
||||
|
||||
|
||||
<UwpMetaPackageVersion>5.4.1</UwpMetaPackageVersion>
|
||||
<DefaultTargetPlatformVersion>16299</DefaultTargetPlatformVersion>
|
||||
<UwpMetaPackageVersion>6.0.8</UwpMetaPackageVersion>
|
||||
<DefaultTargetPlatformVersion>17134</DefaultTargetPlatformVersion>
|
||||
<DefaultTargetPlatformMinVersion>14393</DefaultTargetPlatformMinVersion>
|
||||
|
||||
<PackageOutputPath>$(MSBuildThisFileDirectory)bin\nupkg</PackageOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Choose>
|
||||
|
@ -36,7 +38,7 @@
|
|||
</Choose>
|
||||
|
||||
<Choose>
|
||||
<When Condition="'$(IsUwpProject)' == 'true' and '$(IsSampleProject)' != 'true' and '$(IsDesignProject)' != 'true'">
|
||||
<When Condition="('$(IsUwpProject)' == 'true' or '$(IsWin32Project)' == 'true') and '$(IsSampleProject)' != 'true' and '$(IsDesignProject)' != 'true'">
|
||||
<PropertyGroup>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -23,4 +23,20 @@
|
|||
</When>
|
||||
</Choose>
|
||||
|
||||
<Choose>
|
||||
<When Condition="'$(IsWin32Project)' == 'true'">
|
||||
<PropertyGroup>
|
||||
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == ''">10.0.$(DefaultTargetPlatformVersion).0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="'$(TargetPlatformMinVersion)' == ''">10.0.$(DefaultTargetPlatformMinVersion).0</TargetPlatformMinVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Windows">
|
||||
<HintPath Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\$(TargetPlatformMinVersion)\Windows.winmd')">$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\$(TargetPlatformMinVersion)\Windows.winmd</HintPath>
|
||||
<HintPath Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\$(TargetPlatformVersion)\Windows.winmd')">$(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\$(TargetPlatformVersion)\Windows.winmd</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,8 @@
|
|||
<Application
|
||||
x:Class="GazeInputTest.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:GazeInputTest"
|
||||
RequestedTheme="Light">
|
||||
|
||||
</Application>
|
|
@ -0,0 +1,102 @@
|
|||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace GazeInputTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
sealed partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Suspending += OnSuspending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
Frame rootFrame = Window.Current.Content as Frame;
|
||||
|
||||
// Do not repeat app initialization when the Window already has content,
|
||||
// just ensure that the window is active
|
||||
if (rootFrame == null)
|
||||
{
|
||||
// Create a Frame to act as the navigation context and navigate to the first page
|
||||
rootFrame = new Frame();
|
||||
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
|
||||
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
|
||||
{
|
||||
//TODO: Load state from previously suspended application
|
||||
}
|
||||
|
||||
// Place the frame in the current Window
|
||||
Window.Current.Content = rootFrame;
|
||||
}
|
||||
|
||||
if (e.PrelaunchActivated == false)
|
||||
{
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
// When the navigation stack isn't restored navigate to the first page,
|
||||
// configuring the new page by passing required information as a navigation
|
||||
// parameter
|
||||
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
}
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when application execution is being suspended. Application state is saved
|
||||
/// without knowing whether the application will be terminated or resumed with the contents
|
||||
/// of memory still intact.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the suspend request.</param>
|
||||
/// <param name="e">Details about the suspend request.</param>
|
||||
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
//TODO: Save application state and stop any background activity
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
}
|
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 7.5 KiB |
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GazeInputTest</RootNamespace>
|
||||
<AssemblyName>GazeInputTest</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.17134.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
|
||||
<PackageCertificateKeyFile>GazeInputTest_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
<None Include="GazeInputTest_TemporaryKey.pfx" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>6.0.1</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj">
|
||||
<Project>{a5e98964-45b1-442d-a07a-298a3221d81e}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Input.GazeInteraction</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,48 @@
|
|||
<Page
|
||||
x:Class="GazeInputTest.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:GazeInputTest"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:g="using:Microsoft.Toolkit.Uwp.Input.GazeInteraction"
|
||||
g:GazeInput.Interaction="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ToggleButton Grid.Row="0" Grid.Column="0" x:Name="ShowCursor" Checked="ShowCursor_Toggle" Unchecked="ShowCursor_Toggle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">Show Cursor</ToggleButton>
|
||||
<Button Grid.Row="0" Grid.Column="1" x:Name="Dwell" Click="Dwell_Click" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Eyes Here">
|
||||
<g:GazeInput.GazeElement>
|
||||
<g:GazeElement StateChanged="OnStateChanged"/>
|
||||
</g:GazeInput.GazeElement>
|
||||
</Button>
|
||||
<Button Grid.Row="1" Grid.Column="1" x:Name="HowButton" Content="0: Idle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="OnLegacyInvoked" g:GazeInput.MaxDwellRepeatCount="4">
|
||||
<g:GazeInput.GazeElement>
|
||||
<g:GazeElement Invoked="OnGazeInvoked"/>
|
||||
</g:GazeInput.GazeElement>
|
||||
</Button>
|
||||
<Grid Grid.Row="1" Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="5*"/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ToggleButton Grid.Row="0" Content="Click" g:GazeInput.DwellDuration="0:0:2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<g:GazeInput.GazeElement>
|
||||
<g:GazeElement DwellProgressFeedback="OnInvokeProgress" />
|
||||
</g:GazeInput.GazeElement>
|
||||
</ToggleButton>
|
||||
<ProgressBar Grid.Row="1" x:Name="ProgressShow" Maximum="100" />
|
||||
</Grid>
|
||||
<TextBlock Grid.Row="2" Grid.ColumnSpan="2" x:Name="DeviceAvailable" Text="Device availablility not yet detected"/>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,83 @@
|
|||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
using Microsoft.Toolkit.Uwp.Input.GazeInteraction;
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace GazeInputTest
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
ShowCursor.IsChecked = GazeInput.GetIsCursorVisible(this);
|
||||
|
||||
GazeInput.IsDeviceAvailableChanged += GazeInput_IsDeviceAvailableChanged;
|
||||
GazeInput_IsDeviceAvailableChanged(null, null);
|
||||
}
|
||||
|
||||
private void GazeInput_IsDeviceAvailableChanged(object sender, object e)
|
||||
{
|
||||
DeviceAvailable.Text = GazeInput.IsDeviceAvailable ? "Eye tracker device available" : "No eye tracker device available";
|
||||
}
|
||||
|
||||
private void OnStateChanged(object sender, StateChangedEventArgs ea)
|
||||
{
|
||||
Dwell.Content = ea.PointerState.ToString();
|
||||
}
|
||||
|
||||
private void Dwell_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Dwell.Content = "Clicked";
|
||||
}
|
||||
|
||||
private void ShowCursor_Toggle(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ShowCursor.IsChecked.HasValue)
|
||||
{
|
||||
GazeInput.SetIsCursorVisible(this, ShowCursor.IsChecked.Value);
|
||||
}
|
||||
}
|
||||
|
||||
int clickCount;
|
||||
|
||||
private void OnLegacyInvoked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
clickCount++;
|
||||
HowButton.Content = string.Format("{0}: Legacy click", clickCount);
|
||||
}
|
||||
|
||||
private void OnGazeInvoked(object sender, DwellInvokedRoutedEventArgs e)
|
||||
{
|
||||
clickCount++;
|
||||
HowButton.Content = string.Format("{0}: Accessible click", clickCount);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInvokeProgress(object sender, DwellProgressEventArgs e)
|
||||
{
|
||||
if (e.State == DwellProgressState.Progressing)
|
||||
{
|
||||
ProgressShow.Value = 100.0 * e.Progress;
|
||||
}
|
||||
ProgressShow.IsIndeterminate = e.State == DwellProgressState.Complete;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
IgnorableNamespaces="uap mp">
|
||||
|
||||
<Identity
|
||||
Name="46799fa2-9a41-4ab8-817d-923ee9c0ceac"
|
||||
Publisher="CN=harishsk"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="46799fa2-9a41-4ab8-817d-923ee9c0ceac" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>GazeInputTest</DisplayName>
|
||||
<PublisherDisplayName>harishsk</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="GazeInputTest.App">
|
||||
<uap:VisualElements
|
||||
DisplayName="GazeInputTest"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png"
|
||||
Description="GazeInputTest"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<DeviceCapability Name="gazeInput" />
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,40 @@
|
|||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GazeInputTest")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GazeInputTest")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
//[assembly: AssemblyVersion("1.0.0.0")]
|
||||
//[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||
optimizer.
|
||||
|
||||
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
|
||||
|
||||
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||
|
||||
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||
|
||||
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||
<Namespace Name="DataClasses.ViewModels" Serialize="All" />
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<!--
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
|
||||
|
||||
</Application>
|
||||
</Directives>
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
<Description>This .NET standard library contains various Parsers. It is part of the UWP Community Toolkit.</Description>
|
||||
<Description>This .NET standard library contains various Parsers. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Parsers Parsing Markdown RSS</PackageTags>
|
||||
<Title>UWP Community Toolkit .NET Standard Parsers</Title>
|
||||
<Title>Windows Community Toolkit .NET Standard Parsers</Title>
|
||||
|
||||
<!-- This is a temporary workaround for https://github.com/dotnet/sdk/issues/955 -->
|
||||
<DebugType>Full</DebugType>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.4</TargetFramework>
|
||||
<Description>This .NET standard library enables access to different data sources such as Bing. It is part of the UWP Community Toolkit.</Description>
|
||||
<Description>This .NET standard library enables access to different data sources such as Bing. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Bing</PackageTags>
|
||||
<Title>UWP Community Toolkit .NET Standard Services</Title>
|
||||
<Title>Windows Community Toolkit .NET Standard Services</Title>
|
||||
|
||||
<!-- This is a temporary workaround for https://github.com/dotnet/sdk/issues/955 -->
|
||||
<DebugType>Full</DebugType>
|
||||
|
|
|
@ -133,7 +133,7 @@ namespace Microsoft.Toolkit.Services.MicrosoftGraph
|
|||
{
|
||||
if (_currentUser == null)
|
||||
{
|
||||
throw new ServiceException(new Error { Message = "No user connected", Code = "NoUserConnected", ThrowSite = "UWP Community Toolkit" });
|
||||
throw new ServiceException(new Error { Message = "No user connected", Code = "NoUserConnected", ThrowSite = "Windows Community Toolkit" });
|
||||
}
|
||||
|
||||
List<Recipient> ccRecipients = null;
|
||||
|
|
|
@ -229,7 +229,7 @@ namespace Microsoft.Toolkit.Services.OneDrive
|
|||
/// <returns>When this method completes successfully, it returns an IOneDriveStorageItem that represents the specified folder.</returns>
|
||||
public async Task<OneDriveStorageItem> RenameAsync(string desiredName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
DriveItem newOneDriveItem = new DriveItem { Name = desiredName, Description = "Item Renamed from UWP Toolkit" };
|
||||
DriveItem newOneDriveItem = new DriveItem { Name = desiredName, Description = "Item Renamed from Windows Community Toolkit" };
|
||||
var itemRenamed = await ((IDriveItemRequestBuilder)RequestBuilder).Request().UpdateAsync(newOneDriveItem, cancellationToken).ConfigureAwait(false);
|
||||
return new OneDriveStorageItem(_oneDriveProvider, RequestBuilder, itemRenamed);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<Title>UWP Community Toolkit Devices</Title>
|
||||
<Description>This library enables easier consumption of Devices / Peripherals connected to Windows device. It is part of the UWP Community Toolkit.</Description>
|
||||
<Title>Windows Community Toolkit Devices</Title>
|
||||
<Description>This library enables easier consumption of Devices / Peripherals connected to Windows device. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Devices Bluetooth BTLE Networking</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<Title>UWP Community Toolkit Developer Tools</Title>
|
||||
<Description>This library provides XAML user controls and services to help developer building their app. It is part of the UWP Community Toolkit.</Description>
|
||||
<Title>Windows Community Toolkit Developer Tools</Title>
|
||||
<Description>This library provides XAML user controls and services to help developer building their app. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Controls XAML Developer Tools Accessibility</PackageTags>
|
||||
<UseUwpMetaPackage>true</UseUwpMetaPackage>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.DeveloperTools.dll">
|
||||
<ToolboxItems VSCategory="UWP Community Toolkit" BlendCategory="UWP Community Toolkit">
|
||||
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
|
||||
<Item Type="Microsoft.Toolkit.Uwp.DeveloperTools.AlignmentGrid" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.DeveloperTools.FocusTracker" />
|
||||
</ToolboxItems>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement::Invoked event and allows
|
||||
/// the application to prevent default invocation when the user dwells on a control
|
||||
/// </summary>
|
||||
public ref class DwellInvokedRoutedEventArgs : public RoutedEventArgs
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The application should set this value to true to prevent invoking the control when the user dwells on a control
|
||||
/// </summary>
|
||||
property bool Handled;
|
||||
|
||||
internal:
|
||||
|
||||
DwellInvokedRoutedEventArgs()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,43 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellProgressState.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the GazeElement.DwellProgressFeedback event. The event is fired to inform the application of the user's progress towards completing dwelling on a control
|
||||
/// </summary>
|
||||
public ref class DwellProgressEventArgs : public RoutedEventArgs
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// An enum that reflects the current state of dwell progress
|
||||
/// </summary>
|
||||
property DwellProgressState State { DwellProgressState get() { return _state; }}
|
||||
|
||||
/// <summary>
|
||||
/// A value between 0 and 1 that reflects the fraction of progress towards completing dwell
|
||||
/// </summary>
|
||||
property double Progress { double get() { return _progress; }}
|
||||
|
||||
/// <summary>
|
||||
/// A parameter for the application to set to true if it handles the event. If this parameter is set to true, the library suppresses default animation for dwell feedback on the control
|
||||
/// </summary>
|
||||
property bool Handled;
|
||||
|
||||
internal:
|
||||
DwellProgressEventArgs(DwellProgressState state, TimeSpan elapsedDuration, TimeSpan triggerDuration)
|
||||
{
|
||||
_state = state;
|
||||
_progress = ((double)elapsedDuration.Duration) / triggerDuration.Duration;
|
||||
}
|
||||
private:
|
||||
DwellProgressState _state;
|
||||
double _progress;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// An enum that reflects the current state of progress towards dwell when a user is focused on a control
|
||||
/// </summary>
|
||||
public enum class DwellProgressState
|
||||
{
|
||||
/// <summary>
|
||||
/// User is not looking at the control
|
||||
/// </summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to focus on a control with an intent to dwell and invoke
|
||||
/// </summary>
|
||||
Progressing,
|
||||
|
||||
/// <summary>
|
||||
/// User has completed dwelling on a control
|
||||
/// </summary>
|
||||
Complete
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,89 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeCursor.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeCursor::GazeCursor()
|
||||
{
|
||||
_cursorRadius = DEFAULT_CURSOR_RADIUS;
|
||||
|
||||
_isCursorVisible = DEFAULT_CURSOR_VISIBILITY;
|
||||
|
||||
_gazePopup = ref new Popup();
|
||||
_gazePopup->IsHitTestVisible = false;
|
||||
|
||||
_gazeCanvas = ref new Canvas();
|
||||
_gazeCanvas->IsHitTestVisible = false;
|
||||
|
||||
_gazeCursor = ref new Shapes::Ellipse();
|
||||
_gazeCursor->Fill = ref new SolidColorBrush(Colors::IndianRed);
|
||||
_gazeCursor->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
|
||||
_gazeCursor->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
|
||||
_gazeCursor->Width = 2 * CursorRadius;
|
||||
_gazeCursor->Height = 2 * CursorRadius;
|
||||
_gazeCursor->IsHitTestVisible = false;
|
||||
|
||||
_origSignalCursor = ref new Shapes::Ellipse();
|
||||
_origSignalCursor->Fill = ref new SolidColorBrush(Colors::Green);
|
||||
_origSignalCursor->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
|
||||
_origSignalCursor->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
|
||||
_origSignalCursor->Width = 2 * CursorRadius;
|
||||
_origSignalCursor->Height = 2 * CursorRadius;
|
||||
_origSignalCursor->IsHitTestVisible = false;
|
||||
|
||||
_gazeRect = ref new Shapes::Rectangle();
|
||||
_gazeRect->IsHitTestVisible = false;
|
||||
|
||||
_gazeCanvas->Children->Append(_gazeCursor);
|
||||
_gazeCanvas->Children->Append(_gazeRect);
|
||||
|
||||
// TODO: Reenable this once GazeCursor is refactored correctly
|
||||
//_gazeCanvas->Children->Append(_origSignalCursor);
|
||||
|
||||
_gazePopup->Child = _gazeCanvas;
|
||||
}
|
||||
|
||||
void GazeCursor::CursorRadius::set(int value)
|
||||
{
|
||||
_cursorRadius = value;
|
||||
if (_gazeCursor != nullptr)
|
||||
{
|
||||
_gazeCursor->Width = 2 * _cursorRadius;
|
||||
_gazeCursor->Height = 2 * _cursorRadius;
|
||||
}
|
||||
}
|
||||
|
||||
void GazeCursor::IsCursorVisible::set(bool value)
|
||||
{
|
||||
_isCursorVisible = value;
|
||||
if (_gazePopup != nullptr)
|
||||
{
|
||||
_gazePopup->IsOpen = _isCursorVisible && _isGazeEntered;
|
||||
}
|
||||
}
|
||||
|
||||
void GazeCursor::IsGazeEntered::set(bool value)
|
||||
{
|
||||
_isGazeEntered = value;
|
||||
if (_gazePopup != nullptr)
|
||||
{
|
||||
_gazePopup->IsOpen = _isCursorVisible && _isGazeEntered;
|
||||
}
|
||||
}
|
||||
|
||||
void GazeCursor::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
if (settings->HasKey("GazeCursor.CursorRadius"))
|
||||
{
|
||||
CursorRadius = (int)(settings->Lookup("GazeCursor.CursorRadius"));
|
||||
}
|
||||
if (settings->HasKey("GazeCursor.CursorVisibility"))
|
||||
{
|
||||
IsCursorVisible = (bool)(settings->Lookup("GazeCursor.CursorVisibility"));
|
||||
}
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,93 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeCursor sealed
|
||||
{
|
||||
private:
|
||||
const int DEFAULT_CURSOR_RADIUS = 5;
|
||||
const bool DEFAULT_CURSOR_VISIBILITY = true;
|
||||
|
||||
public:
|
||||
static property GazeCursor^ Instance
|
||||
{
|
||||
GazeCursor^ get()
|
||||
{
|
||||
static GazeCursor^ cursor = ref new GazeCursor();
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
property int CursorRadius
|
||||
{
|
||||
int get() { return _cursorRadius; }
|
||||
void set(int value);
|
||||
}
|
||||
|
||||
property bool IsCursorVisible
|
||||
{
|
||||
bool get() { return _isCursorVisible; }
|
||||
void set(bool value);
|
||||
}
|
||||
|
||||
property bool IsGazeEntered
|
||||
{
|
||||
bool get() { return _isGazeEntered; }
|
||||
void set(bool value);
|
||||
}
|
||||
|
||||
property Point Position
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
return _cursorPosition;
|
||||
}
|
||||
|
||||
void set(Point value)
|
||||
{
|
||||
_cursorPosition = value;
|
||||
_gazeCursor->Margin = Thickness(value.X - CursorRadius, value.Y - CursorRadius, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
property Point PositionOriginal
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
return _originalCursorPosition;
|
||||
}
|
||||
|
||||
void set(Point value)
|
||||
{
|
||||
_originalCursorPosition = value;
|
||||
_origSignalCursor->Margin = Thickness(value.X - CursorRadius, value.Y - CursorRadius, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GazeCursor();
|
||||
|
||||
private:
|
||||
Popup^ _gazePopup;
|
||||
Canvas^ _gazeCanvas;
|
||||
Shapes::Ellipse^ _gazeCursor;
|
||||
Shapes::Ellipse^ _origSignalCursor;
|
||||
Shapes::Rectangle^ _gazeRect;
|
||||
Point _cursorPosition = {};
|
||||
Point _originalCursorPosition = {};
|
||||
int _cursorRadius = DEFAULT_CURSOR_RADIUS;
|
||||
bool _isCursorVisible = DEFAULT_CURSOR_VISIBILITY;
|
||||
bool _isGazeEntered;
|
||||
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,54 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellInvokedRoutedEventArgs.h"
|
||||
#include "DwellProgressEventArgs.h"
|
||||
#include "StateChangedEventArgs.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// Surrogate object attached to controls allowing subscription to per-control gaze events.
|
||||
/// </summary>
|
||||
public ref class GazeElement sealed : public DependencyObject
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired when the state of the user's gaze on a control has changed
|
||||
/// </summary>
|
||||
event EventHandler<StateChangedEventArgs^>^ StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired when the user completed dwelling on a control and the control is about to be invoked by default. This event is fired to give the application an opportunity to prevent default invocation
|
||||
/// </summary>
|
||||
event EventHandler<DwellInvokedRoutedEventArgs^>^ Invoked;
|
||||
|
||||
/// <summary>
|
||||
/// This event is fired to inform the application of the progress towards dwell
|
||||
/// </summary>
|
||||
event EventHandler<DwellProgressEventArgs^>^ DwellProgressFeedback;
|
||||
|
||||
internal:
|
||||
|
||||
void RaiseStateChanged(Object^ sender, StateChangedEventArgs^ args) { StateChanged(sender, args); }
|
||||
|
||||
void RaiseInvoked(Object^ sender, DwellInvokedRoutedEventArgs^ args)
|
||||
{
|
||||
Invoked(sender, args);
|
||||
}
|
||||
|
||||
bool RaiseProgressFeedback(Object^ sender, DwellProgressState state, TimeSpan elapsedTime, TimeSpan triggerTime)
|
||||
{
|
||||
auto args = ref new DwellProgressEventArgs(state, elapsedTime, triggerTime);
|
||||
DwellProgressFeedback(sender, args);
|
||||
return args->Handled;
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,38 @@
|
|||
#include "pch.h"
|
||||
#include "GazeFeedbackPopupFactory.h"
|
||||
|
||||
using namespace Platform::Collections;
|
||||
|
||||
static Vector<Popup^>^ s_cache = ref new Vector<Popup^>();
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
Popup^ GazeFeedbackPopupFactory::Get()
|
||||
{
|
||||
Popup^ popup;
|
||||
|
||||
if (s_cache->Size != 0)
|
||||
{
|
||||
popup = s_cache->GetAt(0);
|
||||
s_cache->RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
popup = ref new Popup();
|
||||
|
||||
auto rectangle = ref new Rectangle();
|
||||
rectangle->StrokeThickness = 2;
|
||||
|
||||
popup->Child = rectangle;
|
||||
}
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
void GazeFeedbackPopupFactory::Return(Popup^ popup)
|
||||
{
|
||||
popup->IsOpen = false;
|
||||
s_cache->Append(popup);
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeFeedbackPopupFactory
|
||||
{
|
||||
public:
|
||||
|
||||
static Popup^ Get();
|
||||
|
||||
static void Return(Popup^ popup);
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,19 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeTargetItem;
|
||||
|
||||
private ref struct GazeHistoryItem
|
||||
{
|
||||
property GazeTargetItem^ HitTarget;
|
||||
property TimeSpan Timestamp;
|
||||
property TimeSpan Duration;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,127 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeInput.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazePointer.h"
|
||||
#include "GazePointerProxy.h"
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::UI;
|
||||
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
static Brush^ s_progressBrush = ref new SolidColorBrush(Colors::Green);
|
||||
|
||||
Brush^ GazeInput::DwellFeedbackProgressBrush::get()
|
||||
{
|
||||
return s_progressBrush;
|
||||
}
|
||||
|
||||
void GazeInput::DwellFeedbackProgressBrush::set(Brush^ value)
|
||||
{
|
||||
s_progressBrush = value;
|
||||
}
|
||||
|
||||
static Brush^ s_completeBrush = ref new SolidColorBrush(Colors::Red);
|
||||
|
||||
Brush^ GazeInput::DwellFeedbackCompleteBrush::get()
|
||||
{
|
||||
return s_completeBrush;
|
||||
}
|
||||
|
||||
void GazeInput::DwellFeedbackCompleteBrush::set(Brush^ value)
|
||||
{
|
||||
s_completeBrush = value;
|
||||
}
|
||||
|
||||
TimeSpan GazeInput::UnsetTimeSpan = { -1 };
|
||||
|
||||
static void OnInteractionChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
auto element = safe_cast<FrameworkElement^>(ob);
|
||||
auto interaction = safe_cast<Interaction>(args->NewValue);
|
||||
GazePointerProxy::SetGazeInteraction(element, interaction);
|
||||
}
|
||||
|
||||
static void OnIsCursorVisibleChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
GazePointer::Instance->IsCursorVisible = safe_cast<bool>(args->NewValue);
|
||||
}
|
||||
|
||||
static void OnCursorRadiusChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)
|
||||
{
|
||||
GazePointer::Instance->CursorRadius = safe_cast<int>(args->NewValue);
|
||||
}
|
||||
|
||||
static DependencyProperty^ s_interactionProperty = DependencyProperty::RegisterAttached("Interaction", Interaction::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(Interaction::Inherited, ref new PropertyChangedCallback(&OnInteractionChanged)));
|
||||
static DependencyProperty^ s_isCursorVisibleProperty = DependencyProperty::RegisterAttached("IsCursorVisible", bool::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(true, ref new PropertyChangedCallback(&OnIsCursorVisibleChanged)));
|
||||
static DependencyProperty^ s_cursorRadiusProperty = DependencyProperty::RegisterAttached("CursorRadius", int::typeid, GazeInput::typeid,
|
||||
ref new PropertyMetadata(6, ref new PropertyChangedCallback(&OnCursorRadiusChanged)));
|
||||
static DependencyProperty^ s_gazeElementProperty = DependencyProperty::RegisterAttached("GazeElement", GazeElement::typeid, GazeInput::typeid, ref new PropertyMetadata(nullptr));
|
||||
static DependencyProperty^ s_fixationDurationProperty = DependencyProperty::RegisterAttached("FixationDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_dwellDurationProperty = DependencyProperty::RegisterAttached("DwellDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_repeatDelayDurationProperty = DependencyProperty::RegisterAttached("RepeatDelayDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_dwellRepeatDurationProperty = DependencyProperty::RegisterAttached("DwellRepeatDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_thresholdDurationProperty = DependencyProperty::RegisterAttached("ThresholdDuration", TimeSpan::typeid, GazeInput::typeid, ref new PropertyMetadata(GazeInput::UnsetTimeSpan));
|
||||
static DependencyProperty^ s_maxRepeatCountProperty = DependencyProperty::RegisterAttached("MaxDwellRepeatCount", int::typeid, GazeInput::typeid, ref new PropertyMetadata(safe_cast<Object^>(0)));
|
||||
|
||||
DependencyProperty^ GazeInput::InteractionProperty::get() { return s_interactionProperty; }
|
||||
DependencyProperty^ GazeInput::IsCursorVisibleProperty::get() { return s_isCursorVisibleProperty; }
|
||||
DependencyProperty^ GazeInput::CursorRadiusProperty::get() { return s_cursorRadiusProperty; }
|
||||
DependencyProperty^ GazeInput::GazeElementProperty::get() { return s_gazeElementProperty; }
|
||||
DependencyProperty^ GazeInput::FixationDurationProperty::get() { return s_fixationDurationProperty; }
|
||||
DependencyProperty^ GazeInput::DwellDurationProperty::get() { return s_dwellDurationProperty; }
|
||||
DependencyProperty^ GazeInput::RepeatDelayDurationProperty::get() { return s_repeatDelayDurationProperty; }
|
||||
DependencyProperty^ GazeInput::DwellRepeatDurationProperty::get() { return s_dwellRepeatDurationProperty; }
|
||||
DependencyProperty^ GazeInput::ThresholdDurationProperty::get() { return s_thresholdDurationProperty; }
|
||||
DependencyProperty^ GazeInput::MaxDwellRepeatCountProperty::get() { return s_maxRepeatCountProperty; }
|
||||
|
||||
Interaction GazeInput::GetInteraction(UIElement^ element) { return safe_cast<Interaction>(element->GetValue(s_interactionProperty)); }
|
||||
bool GazeInput::GetIsCursorVisible(UIElement^ element) { return safe_cast<bool>(element->GetValue(s_isCursorVisibleProperty)); }
|
||||
int GazeInput::GetCursorRadius(UIElement^ element) { return safe_cast<int>(element->GetValue(s_cursorRadiusProperty)); }
|
||||
GazeElement^ GazeInput::GetGazeElement(UIElement^ element) { return safe_cast<GazeElement^>(element->GetValue(s_gazeElementProperty)); }
|
||||
TimeSpan GazeInput::GetFixationDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_fixationDurationProperty)); }
|
||||
TimeSpan GazeInput::GetDwellDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_dwellDurationProperty)); }
|
||||
TimeSpan GazeInput::GetRepeatDelayDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_repeatDelayDurationProperty)); }
|
||||
TimeSpan GazeInput::GetDwellRepeatDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_dwellRepeatDurationProperty)); }
|
||||
TimeSpan GazeInput::GetThresholdDuration(UIElement^ element) { return safe_cast<TimeSpan>(element->GetValue(s_thresholdDurationProperty)); }
|
||||
int GazeInput::GetMaxDwellRepeatCount(UIElement^ element) { return safe_cast<int>(element->GetValue(s_maxRepeatCountProperty)); }
|
||||
|
||||
void GazeInput::SetInteraction(UIElement^ element, Interaction value) { element->SetValue(s_interactionProperty, value); }
|
||||
void GazeInput::SetIsCursorVisible(UIElement^ element, bool value) { element->SetValue(s_isCursorVisibleProperty, value); }
|
||||
void GazeInput::SetCursorRadius(UIElement^ element, int value) { element->SetValue(s_cursorRadiusProperty, value); }
|
||||
void GazeInput::SetGazeElement(UIElement^ element, GazeElement^ value) { element->SetValue(s_gazeElementProperty, value); }
|
||||
void GazeInput::SetFixationDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_fixationDurationProperty, span); }
|
||||
void GazeInput::SetDwellDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_dwellDurationProperty, span); }
|
||||
void GazeInput::SetRepeatDelayDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_repeatDelayDurationProperty, span); }
|
||||
void GazeInput::SetDwellRepeatDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_dwellRepeatDurationProperty, span); }
|
||||
void GazeInput::SetThresholdDuration(UIElement^ element, TimeSpan span) { element->SetValue(s_thresholdDurationProperty, span); }
|
||||
void GazeInput::SetMaxDwellRepeatCount(UIElement^ element, int value) { element->SetValue(s_maxRepeatCountProperty, value); }
|
||||
|
||||
GazePointer^ GazeInput::GetGazePointer(Page^ page)
|
||||
{
|
||||
return GazePointer::Instance;
|
||||
}
|
||||
|
||||
bool GazeInput::IsDeviceAvailable::get()
|
||||
{
|
||||
return GazePointer::Instance->IsDeviceAvailable;
|
||||
}
|
||||
|
||||
EventRegistrationToken GazeInput::IsDeviceAvailableChanged::add(EventHandler<Object^>^ handler)
|
||||
{
|
||||
return GazePointer::Instance->IsDeviceAvailableChanged += handler;
|
||||
}
|
||||
|
||||
void GazeInput::IsDeviceAvailableChanged::remove(EventRegistrationToken token)
|
||||
{
|
||||
GazePointer::Instance->IsDeviceAvailableChanged -= token;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,209 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Interaction.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeElement;
|
||||
ref class GazePointer;
|
||||
|
||||
/// <summary>
|
||||
/// Static class primarily providing access to attached properties controlling gaze behavior.
|
||||
/// </summary>
|
||||
[Windows::Foundation::Metadata::WebHostHidden]
|
||||
public ref class GazeInput sealed
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the Interaction dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ InteractionProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the IsCursorVisible dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ IsCursorVisibleProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the CursorRadius dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ CursorRadiusProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the GazeElement dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ GazeElementProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifyes the FixationDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ FixationDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the DwellDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ DwellDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the RepeatDelayDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ RepeatDelayDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the DwellRepeatDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ DwellRepeatDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the ThresholdDuration dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ ThresholdDurationProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the MaxDwellRepeatCount dependency property
|
||||
/// </summary>
|
||||
static property DependencyProperty^ MaxDwellRepeatCountProperty { DependencyProperty^ get(); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell press
|
||||
/// </summary>
|
||||
static property Brush^ DwellFeedbackProgressBrush { Brush^ get(); void set(Brush^ value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush to use when displaying the default animation for dwell complete
|
||||
/// </summary>
|
||||
static property Brush^ DwellFeedbackCompleteBrush { Brush^ get(); void set(Brush^ value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
static Interaction GetInteraction(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets Boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
static bool GetIsCursorVisible(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
static int GetCursorRadius(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
static GazeElement^ GetGazeElement(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
static TimeSpan GetFixationDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
static TimeSpan GetDwellDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional duration for the first repeat to occur.This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
static TimeSpan GetRepeatDelayDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
static TimeSpan GetDwellRepeatDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
static TimeSpan GetThresholdDuration(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
static int GetMaxDwellRepeatCount(UIElement^ element);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the status of gaze interaction over that particular XAML element.
|
||||
/// </summary>
|
||||
static void SetInteraction(UIElement^ element, Interaction value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets Boolean indicating whether cursor is shown while user is looking at the school.
|
||||
/// </summary>
|
||||
static void SetIsCursorVisible(UIElement^ element, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the size of the gaze cursor radius.
|
||||
/// </summary>
|
||||
static void SetCursorRadius(UIElement^ element, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the GazeElement associated with an UIElement.
|
||||
/// </summary>
|
||||
static void SetGazeElement(UIElement^ element, GazeElement^ value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Enter state to the Fixation state. At this point, a StateChanged event is fired with PointerState set to Fixation. This event should be used to control the earliest visual feedback the application needs to provide to the user about the gaze location. The default is 350ms.
|
||||
/// </summary>
|
||||
static void SetFixationDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration for the control to transition from the Fixation state to the Dwell state. At this point, a StateChanged event is fired with PointerState set to Dwell. The Enter and Fixation states are typicaly achieved too rapidly for the user to have much control over. In contrast Dwell is conscious event. This is the point at which the control is invoked, e.g. a button click. The application can modify this property to control when a gaze enabled UI element gets invoked after a user starts looking at it.
|
||||
/// </summary>
|
||||
static void SetDwellDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the additional duration for the first repeat to occur.This prevents inadvertent repeated invocation.
|
||||
/// </summary>
|
||||
static void SetRepeatDelayDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration of repeated dwell invocations, should the user continue to dwell on the control. The first repeat will occur after an additional delay specified by RepeatDelayDuration. Subsequent repeats happen after every period of DwellRepeatDuration. A control is invoked repeatedly only if MaxDwellRepeatCount is set to greater than zero.
|
||||
/// </summary>
|
||||
static void SetDwellRepeatDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the duration that controls when the PointerState moves to either the Enter state or the Exit state. When this duration has elapsed after the user's gaze first enters a control, the PointerState is set to Enter. And when this duration has elapsed after the user's gaze has left the control, the PointerState is set to Exit. In both cases, a StateChanged event is fired. The default is 50ms.
|
||||
/// </summary>
|
||||
static void SetThresholdDuration(UIElement^ element, TimeSpan span);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum times the control will invoked repeatedly without the user's gaze having to leave and re-enter the control. The default value is zero which disables repeated invocation of a control. Developers can set a higher value to enable repeated invocation.
|
||||
/// </summary>
|
||||
static void SetMaxDwellRepeatCount(UIElement^ element, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GazePointer object.
|
||||
/// </summary>
|
||||
static GazePointer^ GetGazePointer(Page^ page);
|
||||
|
||||
/// <summary>
|
||||
/// Reports whether a gaze input device is available, and hence whether there is any possibility of gaze events occurring in the application.
|
||||
/// </summary>
|
||||
static property bool IsDeviceAvailable { bool get(); }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered whenever IsDeviceAvailable changes value.
|
||||
/// </summary>
|
||||
static event EventHandler<Object^>^ IsDeviceAvailableChanged
|
||||
{
|
||||
EventRegistrationToken add(EventHandler<Object^>^ handler);
|
||||
void remove(EventRegistrationToken token);
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
static TimeSpan UnsetTimeSpan;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,619 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "GazePointer.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazeHistoryItem.h"
|
||||
#include "GazeTargetItem.h"
|
||||
#include "StateChangedEventArgs.h"
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazePointer^ GazePointer::Instance::get()
|
||||
{
|
||||
static auto value = ref new GazePointer();
|
||||
return value;
|
||||
}
|
||||
|
||||
void GazePointer::AddRoot(FrameworkElement^ element)
|
||||
{
|
||||
_roots->InsertAt(0, element);
|
||||
|
||||
if (_roots->Size == 1)
|
||||
{
|
||||
_isShuttingDown = false;
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::RemoveRoot(FrameworkElement^ element)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
while (index < _roots->Size && _roots->GetAt(index) != element)
|
||||
{
|
||||
index++;
|
||||
}
|
||||
if (index < _roots->Size)
|
||||
{
|
||||
_roots->RemoveAt(index);
|
||||
}
|
||||
|
||||
if (_roots->Size == 0)
|
||||
{
|
||||
_isShuttingDown = true;
|
||||
_gazeCursor->IsGazeEntered = false;
|
||||
DeinitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
GazePointer::GazePointer()
|
||||
{
|
||||
_coreDispatcher = CoreWindow::GetForCurrentThread()->Dispatcher;
|
||||
|
||||
// Default to not filtering sample data
|
||||
Filter = ref new NullFilter();
|
||||
|
||||
_gazeCursor = GazeCursor::Instance;
|
||||
|
||||
// timer that gets called back if there gaze samples haven't been received in a while
|
||||
_eyesOffTimer = ref new DispatcherTimer();
|
||||
_eyesOffTimer->Tick += ref new EventHandler<Object^>(this, &GazePointer::OnEyesOff);
|
||||
|
||||
// provide a default of GAZE_IDLE_TIME microseconds to fire eyes off
|
||||
EyesOffDelay = GAZE_IDLE_TIME;
|
||||
|
||||
InitializeHistogram();
|
||||
|
||||
_watcher = GazeInputSourcePreview::CreateWatcher();
|
||||
_watcher->Added += ref new TypedEventHandler<GazeDeviceWatcherPreview^, GazeDeviceWatcherAddedPreviewEventArgs^>(this, &GazePointer::OnDeviceAdded);
|
||||
_watcher->Removed += ref new TypedEventHandler<GazeDeviceWatcherPreview^, GazeDeviceWatcherRemovedPreviewEventArgs^>(this, &GazePointer::OnDeviceRemoved);
|
||||
_watcher->Start();
|
||||
}
|
||||
|
||||
void GazePointer::OnDeviceAdded(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherAddedPreviewEventArgs^ args)
|
||||
{
|
||||
_deviceCount++;
|
||||
|
||||
if (_deviceCount == 1)
|
||||
{
|
||||
IsDeviceAvailableChanged(nullptr, nullptr);
|
||||
|
||||
InitializeGazeInputSource();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnDeviceRemoved(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherRemovedPreviewEventArgs^ args)
|
||||
{
|
||||
_deviceCount--;
|
||||
|
||||
if (_deviceCount == 0)
|
||||
{
|
||||
IsDeviceAvailableChanged(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
GazePointer::~GazePointer()
|
||||
{
|
||||
_watcher->Added -= _deviceAddedToken;
|
||||
_watcher->Removed -= _deviceRemovedToken;
|
||||
|
||||
if (_gazeInputSource != nullptr)
|
||||
{
|
||||
_gazeInputSource->GazeEntered -= _gazeEnteredToken;
|
||||
_gazeInputSource->GazeMoved -= _gazeMovedToken;
|
||||
_gazeInputSource->GazeExited -= _gazeExitedToken;
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
_gazeCursor->LoadSettings(settings);
|
||||
Filter->LoadSettings(settings);
|
||||
|
||||
// TODO Add logic to protect against missing settings
|
||||
|
||||
if (settings->HasKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
_defaultFixation = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.FixationDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
_defaultDwell = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
_defaultDwellRepeatDelay = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellRepeatDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.RepeatDelay"))
|
||||
{
|
||||
_defaultRepeat = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.RepeatDelay")));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.ThresholdDelay"))
|
||||
{
|
||||
_defaultThreshold = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.ThresholdDelay")));
|
||||
}
|
||||
|
||||
// TODO need to set fixation and dwell for all elements
|
||||
if (settings->HasKey("GazePointer.FixationDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Fixation, TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.FixationDelay"))));
|
||||
}
|
||||
if (settings->HasKey("GazePointer.DwellDelay"))
|
||||
{
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Dwell, TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.DwellDelay"))));
|
||||
}
|
||||
|
||||
if (settings->HasKey("GazePointer.GazeIdleTime"))
|
||||
{
|
||||
EyesOffDelay = TimeSpanFromMicroseconds((int)(settings->Lookup("GazePointer.GazeIdleTime")));
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::InitializeHistogram()
|
||||
{
|
||||
_activeHitTargetTimes = ref new Vector<GazeTargetItem^>();
|
||||
|
||||
_offScreenElement = ref new UserControl();
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Fixation, _defaultFixation);
|
||||
SetElementStateDelay(_offScreenElement, PointerState::Dwell, _defaultDwell);
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION; // maintain about 3 seconds of history (in microseconds)
|
||||
_gazeHistory = ref new Vector<GazeHistoryItem^>();
|
||||
}
|
||||
|
||||
void GazePointer::InitializeGazeInputSource()
|
||||
{
|
||||
if (_gazeInputSource == nullptr && _roots->Size != 0 && _deviceCount != 0)
|
||||
{
|
||||
_gazeInputSource = GazeInputSourcePreview::GetForCurrentView();
|
||||
if (_gazeInputSource != nullptr)
|
||||
{
|
||||
_gazeEnteredToken = _gazeInputSource->GazeEntered += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeEnteredPreviewEventArgs^>(this, &GazePointer::OnGazeEntered);
|
||||
_gazeMovedToken = _gazeInputSource->GazeMoved += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeMovedPreviewEventArgs^>(this, &GazePointer::OnGazeMoved);
|
||||
_gazeExitedToken = _gazeInputSource->GazeExited += ref new TypedEventHandler<
|
||||
GazeInputSourcePreview^, GazeExitedPreviewEventArgs^>(this, &GazePointer::OnGazeExited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::DeinitializeGazeInputSource()
|
||||
{
|
||||
if (_gazeInputSource != nullptr)
|
||||
{
|
||||
_gazeInputSource->GazeEntered -= _gazeEnteredToken;
|
||||
_gazeInputSource->GazeMoved -= _gazeMovedToken;
|
||||
_gazeInputSource->GazeExited -= _gazeExitedToken;
|
||||
|
||||
_gazeInputSource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static DependencyProperty^ GetProperty(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState::Fixation: return GazeInput::FixationDurationProperty;
|
||||
case PointerState::Dwell: return GazeInput::DwellDurationProperty;
|
||||
case PointerState::DwellRepeat: return GazeInput::DwellRepeatDurationProperty;
|
||||
case PointerState::Enter: return GazeInput::ThresholdDurationProperty;
|
||||
case PointerState::Exit: return GazeInput::ThresholdDurationProperty;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetDefaultPropertyValue(PointerState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case PointerState::Fixation: return _defaultFixation;
|
||||
case PointerState::Dwell: return _defaultDwell;
|
||||
case PointerState::DwellRepeat: return _defaultRepeat;
|
||||
case PointerState::Enter: return _defaultThreshold;
|
||||
case PointerState::Exit: return _defaultThreshold;
|
||||
default: throw ref new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::SetElementStateDelay(UIElement ^element, PointerState relevantState, TimeSpan stateDelay)
|
||||
{
|
||||
auto property = GetProperty(relevantState);
|
||||
element->SetValue(property, stateDelay);
|
||||
|
||||
// fix up _maxHistoryTime in case the new param exceeds the history length we are currently tracking
|
||||
auto dwellTime = GetElementStateDelay(element, PointerState::Dwell);
|
||||
auto repeatTime = GetElementStateDelay(element, PointerState::DwellRepeat);
|
||||
_maxHistoryTime = 2 * max(dwellTime, repeatTime);
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetElementStateDelay(UIElement ^element, DependencyProperty^ property, TimeSpan defaultValue)
|
||||
{
|
||||
DependencyObject^ walker = element;
|
||||
Object^ valueAtWalker = walker->GetValue(property);
|
||||
|
||||
while (GazeInput::UnsetTimeSpan.Equals(valueAtWalker) && walker != nullptr)
|
||||
{
|
||||
walker = VisualTreeHelper::GetParent(walker);
|
||||
|
||||
if (walker != nullptr)
|
||||
{
|
||||
valueAtWalker = walker->GetValue(property);
|
||||
}
|
||||
}
|
||||
|
||||
auto ticks = GazeInput::UnsetTimeSpan.Equals(valueAtWalker) ? defaultValue : safe_cast<TimeSpan>(valueAtWalker);
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
TimeSpan GazePointer::GetElementStateDelay(UIElement ^element, PointerState pointerState)
|
||||
{
|
||||
auto property = GetProperty(pointerState);
|
||||
auto defaultValue = GetDefaultPropertyValue(pointerState);
|
||||
auto ticks = GetElementStateDelay(element, property, defaultValue);
|
||||
|
||||
switch (pointerState)
|
||||
{
|
||||
case PointerState::Dwell:
|
||||
case PointerState::DwellRepeat:
|
||||
_maxHistoryTime = max(_maxHistoryTime, 2 * ticks);
|
||||
break;
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void GazePointer::Reset()
|
||||
{
|
||||
_activeHitTargetTimes->Clear();
|
||||
_gazeHistory->Clear();
|
||||
|
||||
_maxHistoryTime = DEFAULT_MAX_HISTORY_DURATION;
|
||||
}
|
||||
|
||||
GazeTargetItem^ GazePointer::GetHitTarget(Point gazePoint)
|
||||
{
|
||||
for each (auto rootElement in _roots)
|
||||
{
|
||||
auto targets = VisualTreeHelper::FindElementsInHostCoordinates(gazePoint, rootElement, false);
|
||||
GazeTargetItem^ invokable = nullptr;
|
||||
for each (auto target in targets)
|
||||
{
|
||||
if (invokable == nullptr)
|
||||
{
|
||||
auto item = GazeTargetItem::GetOrCreate(target);
|
||||
if (item->IsInvokable)
|
||||
{
|
||||
invokable = item;
|
||||
}
|
||||
}
|
||||
|
||||
switch (GazeInput::GetInteraction(target))
|
||||
{
|
||||
case Interaction::Enabled:
|
||||
if (invokable != nullptr)
|
||||
{
|
||||
return invokable;
|
||||
}
|
||||
break;
|
||||
|
||||
case Interaction::Disabled:
|
||||
return GazeTargetItem::NonInvokable;
|
||||
}
|
||||
}
|
||||
assert(invokable == nullptr);
|
||||
}
|
||||
// TODO : Check if the location is offscreen
|
||||
return GazeTargetItem::NonInvokable;
|
||||
}
|
||||
|
||||
void GazePointer::ActivateGazeTargetItem(GazeTargetItem^ target)
|
||||
{
|
||||
unsigned int index;
|
||||
if (!_activeHitTargetTimes->IndexOf(target, &index))
|
||||
{
|
||||
_activeHitTargetTimes->Append(target);
|
||||
|
||||
// calculate the time that the first DwellRepeat needs to be fired after. this will be updated every time a DwellRepeat is
|
||||
// fired to keep track of when the next one is to be fired after that.
|
||||
auto nextStateTime = GetElementStateDelay(target->TargetElement, PointerState::Enter);
|
||||
|
||||
target->Reset(nextStateTime);
|
||||
}
|
||||
}
|
||||
|
||||
GazeTargetItem^ GazePointer::ResolveHitTarget(Point gazePoint, TimeSpan timestamp)
|
||||
{
|
||||
// TODO: The existance of a GazeTargetItem should be used to indicate that
|
||||
// the target item is invokable. The method of invokation should be stored
|
||||
// within the GazeTargetItem when it is created and not recalculated when
|
||||
// subsequently needed.
|
||||
|
||||
// create GazeHistoryItem to deal with this sample
|
||||
auto target = GetHitTarget(gazePoint);
|
||||
auto historyItem = ref new GazeHistoryItem();
|
||||
historyItem->HitTarget = target;
|
||||
historyItem->Timestamp = timestamp;
|
||||
historyItem->Duration = TimeSpanZero;
|
||||
assert(historyItem->HitTarget != nullptr);
|
||||
|
||||
// create new GazeTargetItem with a (default) total elapsed time of zero if one does not exist already.
|
||||
// this ensures that there will always be an entry for target elements in the code below.
|
||||
ActivateGazeTargetItem(target);
|
||||
target->LastTimestamp = timestamp;
|
||||
|
||||
// find elapsed time since we got the last hit target
|
||||
historyItem->Duration = timestamp - _lastTimestamp;
|
||||
if (historyItem->Duration > MAX_SINGLE_SAMPLE_DURATION)
|
||||
{
|
||||
historyItem->Duration = MAX_SINGLE_SAMPLE_DURATION;
|
||||
}
|
||||
_gazeHistory->Append(historyItem);
|
||||
|
||||
// update the time this particular hit target has accumulated
|
||||
target->DetailedTime += historyItem->Duration;
|
||||
|
||||
// drop the oldest samples from the list until we have samples only
|
||||
// within the window we are monitoring
|
||||
//
|
||||
// historyItem is the last item we just appended a few lines above.
|
||||
for (auto evOldest = _gazeHistory->GetAt(0);
|
||||
historyItem->Timestamp - evOldest->Timestamp > _maxHistoryTime;
|
||||
evOldest = _gazeHistory->GetAt(0))
|
||||
{
|
||||
_gazeHistory->RemoveAt(0);
|
||||
|
||||
// subtract the duration obtained from the oldest sample in _gazeHistory
|
||||
auto targetItem = evOldest->HitTarget;
|
||||
assert(targetItem->DetailedTime - evOldest->Duration >= TimeSpanZero);
|
||||
targetItem->DetailedTime -= evOldest->Duration;
|
||||
if (targetItem->ElementState != PointerState::PreEnter)
|
||||
{
|
||||
targetItem->OverflowTime += evOldest->Duration;
|
||||
}
|
||||
}
|
||||
|
||||
_lastTimestamp = timestamp;
|
||||
|
||||
// Return the most recent hit target
|
||||
// Intuition would tell us that we should return NOT the most recent
|
||||
// hitTarget, but the one with the most accumulated time in
|
||||
// in the maintained history. But the effect of that is that
|
||||
// the user will feel that they have clicked on the wrong thing
|
||||
// when they are looking at something else.
|
||||
// That is why we return the most recent hitTarget so that
|
||||
// when its dwell time has elapsed, it will be invoked
|
||||
return target;
|
||||
}
|
||||
|
||||
void GazePointer::GotoState(UIElement^ control, PointerState state)
|
||||
{
|
||||
Platform::String^ stateName;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PointerState::Enter:
|
||||
return;
|
||||
case PointerState::Exit:
|
||||
stateName = "Normal";
|
||||
break;
|
||||
case PointerState::Fixation:
|
||||
stateName = "Fixation";
|
||||
break;
|
||||
case PointerState::DwellRepeat:
|
||||
case PointerState::Dwell:
|
||||
stateName = "Dwell";
|
||||
break;
|
||||
default:
|
||||
//assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement proper support for visual states
|
||||
// VisualStateManager::GoToState(dynamic_cast<Control^>(control), stateName, true);
|
||||
}
|
||||
|
||||
void GazePointer::OnEyesOff(Object ^sender, Object ^ea)
|
||||
{
|
||||
_eyesOffTimer->Stop();
|
||||
|
||||
CheckIfExiting(_lastTimestamp + EyesOffDelay);
|
||||
RaiseGazePointerEvent(nullptr, PointerState::Enter, EyesOffDelay);
|
||||
}
|
||||
|
||||
void GazePointer::CheckIfExiting(TimeSpan curTimestamp)
|
||||
{
|
||||
for (unsigned int index = 0; index < _activeHitTargetTimes->Size; index++)
|
||||
{
|
||||
auto targetItem = _activeHitTargetTimes->GetAt(index);
|
||||
auto targetElement = targetItem->TargetElement;
|
||||
auto exitDelay = GetElementStateDelay(targetElement, PointerState::Exit);
|
||||
|
||||
auto idleDuration = curTimestamp - targetItem->LastTimestamp;
|
||||
if (targetItem->ElementState != PointerState::PreEnter && idleDuration > exitDelay)
|
||||
{
|
||||
targetItem->ElementState = PointerState::PreEnter;
|
||||
GotoState(targetElement, PointerState::Exit);
|
||||
RaiseGazePointerEvent(targetItem, PointerState::Exit, targetItem->ElapsedTime);
|
||||
targetItem->GiveFeedback();
|
||||
|
||||
_activeHitTargetTimes->RemoveAt(index);
|
||||
|
||||
// remove all history samples referring to deleted hit target
|
||||
for (unsigned i = 0; i < _gazeHistory->Size; )
|
||||
{
|
||||
auto hitTarget = _gazeHistory->GetAt(i)->HitTarget;
|
||||
if (hitTarget->TargetElement == targetElement)
|
||||
{
|
||||
_gazeHistory->RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// return because only one element can be exited at a time and at this point
|
||||
// we have done everything that we can do
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t *PointerStates[] = {
|
||||
L"Exit",
|
||||
L"PreEnter",
|
||||
L"Enter",
|
||||
L"Fixation",
|
||||
L"Dwell",
|
||||
L"DwellRepeat"
|
||||
};
|
||||
|
||||
void GazePointer::RaiseGazePointerEvent(GazeTargetItem^ target, PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
auto control = target != nullptr ? safe_cast<Control^>(target->TargetElement) : nullptr;
|
||||
//assert(target != _rootElement);
|
||||
auto gpea = ref new StateChangedEventArgs(control, state, elapsedTime);
|
||||
//auto buttonObj = dynamic_cast<Button ^>(target);
|
||||
//if (buttonObj && buttonObj->Content)
|
||||
//{
|
||||
// String^ buttonText = dynamic_cast<String^>(buttonObj->Content);
|
||||
// Debug::WriteLine(L"GPE: %s -> %s, %d", buttonText, PointerStates[(int)state], elapsedTime);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// Debug::WriteLine(L"GPE: 0x%08x -> %s, %d", target != nullptr ? target->GetHashCode() : 0, PointerStates[(int)state], elapsedTime);
|
||||
//}
|
||||
|
||||
auto gazeElement = target != nullptr ? GazeInput::GetGazeElement(control) : nullptr;
|
||||
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
gazeElement->RaiseStateChanged(control, gpea);
|
||||
}
|
||||
|
||||
if (state == PointerState::Dwell)
|
||||
{
|
||||
auto handled = false;
|
||||
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
auto args = ref new DwellInvokedRoutedEventArgs();
|
||||
gazeElement->RaiseInvoked(control, args);
|
||||
handled = args->Handled;
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
target->Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeEntered(GazeInputSourcePreview^ provider, GazeEnteredPreviewEventArgs^ args)
|
||||
{
|
||||
Debug::WriteLine(L"Entered at %ld", args->CurrentPoint->Timestamp);
|
||||
_gazeCursor->IsGazeEntered = true;
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeMoved(GazeInputSourcePreview^ provider, GazeMovedPreviewEventArgs^ args)
|
||||
{
|
||||
if (!_isShuttingDown)
|
||||
{
|
||||
auto intermediatePoints = args->GetIntermediatePoints();
|
||||
for each(auto point in intermediatePoints)
|
||||
{
|
||||
auto position = point->EyeGazePosition;
|
||||
if (position != nullptr)
|
||||
{
|
||||
_gazeCursor->IsGazeEntered = true;
|
||||
ProcessGazePoint(TimeSpanFromMicroseconds(point->Timestamp), position->Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug::WriteLine(L"Null position eaten at %ld", point->Timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointer::OnGazeExited(GazeInputSourcePreview^ provider, GazeExitedPreviewEventArgs^ args)
|
||||
{
|
||||
Debug::WriteLine(L"Exited at %ld", args->CurrentPoint->Timestamp);
|
||||
_gazeCursor->IsGazeEntered = false;
|
||||
}
|
||||
|
||||
void GazePointer::ProcessGazePoint(TimeSpan timestamp, Point position)
|
||||
{
|
||||
auto ea = ref new GazeFilterArgs(position, timestamp);
|
||||
|
||||
auto fa = Filter->Update(ea);
|
||||
_gazeCursor->Position = fa->Location;
|
||||
|
||||
auto targetItem = ResolveHitTarget(fa->Location, fa->Timestamp);
|
||||
assert(targetItem != nullptr);
|
||||
|
||||
//Debug::WriteLine(L"ProcessGazePoint: %llu -> [%d, %d], %llu", hitTarget->GetHashCode(), (int)fa->Location.X, (int)fa->Location.Y, fa->Timestamp);
|
||||
|
||||
// check to see if any element in _hitTargetTimes needs an exit event fired.
|
||||
// this ensures that all exit events are fired before enter event
|
||||
CheckIfExiting(fa->Timestamp);
|
||||
|
||||
PointerState nextState = static_cast<PointerState>(static_cast<int>(targetItem->ElementState) + 1);
|
||||
|
||||
Debug::WriteLine(L"%llu -> State=%d, Elapsed=%d, NextStateTime=%d", targetItem->TargetElement, targetItem->ElementState, targetItem->ElapsedTime, targetItem->NextStateTime);
|
||||
|
||||
if (targetItem->ElapsedTime > targetItem->NextStateTime)
|
||||
{
|
||||
auto prevStateTime = targetItem->NextStateTime;
|
||||
|
||||
// prevent targetItem from ever actually transitioning into the DwellRepeat state so as
|
||||
// to continuously emit the DwellRepeat event
|
||||
if (nextState != PointerState::DwellRepeat)
|
||||
{
|
||||
targetItem->ElementState = nextState;
|
||||
nextState = static_cast<PointerState>(static_cast<int>(nextState) + 1); // nextState++
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, nextState);
|
||||
|
||||
if (targetItem->ElementState == PointerState::Dwell)
|
||||
{
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, GazeInput::RepeatDelayDurationProperty, _defaultDwellRepeatDelay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the NextStateTime by one dwell period, while continuing to stay in Dwell state
|
||||
targetItem->NextStateTime += GetElementStateDelay(targetItem->TargetElement, PointerState::DwellRepeat);
|
||||
}
|
||||
|
||||
if (targetItem->ElementState == PointerState::Dwell)
|
||||
{
|
||||
targetItem->RepeatCount++;
|
||||
if (targetItem->MaxDwellRepeatCount < targetItem->RepeatCount)
|
||||
{
|
||||
targetItem->NextStateTime = TimeSpan{ MAXINT64 };
|
||||
}
|
||||
}
|
||||
|
||||
GotoState(targetItem->TargetElement, targetItem->ElementState);
|
||||
|
||||
RaiseGazePointerEvent(targetItem, targetItem->ElementState, targetItem->ElapsedTime);
|
||||
}
|
||||
|
||||
targetItem->GiveFeedback();
|
||||
|
||||
_eyesOffTimer->Start();
|
||||
_lastTimestamp = fa->Timestamp;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,177 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GazeCursor.h"
|
||||
#include "IGazeFilter.h"
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::Devices::Input::Preview;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
ref class GazeTargetItem;
|
||||
ref struct GazeHistoryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class of singleton object coordinating gaze input.
|
||||
/// </summary>
|
||||
public ref class GazePointer sealed
|
||||
{
|
||||
private:
|
||||
|
||||
// units in microseconds
|
||||
const TimeSpan DEFAULT_FIXATION_DELAY = TimeSpanFromMicroseconds(350000);
|
||||
const TimeSpan DEFAULT_DWELL_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_DWELL_REPEAT_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_REPEAT_DELAY = TimeSpanFromMicroseconds(400000);
|
||||
const TimeSpan DEFAULT_THRESHOLD_DELAY = TimeSpanFromMicroseconds(50000);
|
||||
const TimeSpan DEFAULT_MAX_HISTORY_DURATION = TimeSpanFromMicroseconds(3000000);
|
||||
const TimeSpan MAX_SINGLE_SAMPLE_DURATION = TimeSpanFromMicroseconds(100000);
|
||||
|
||||
const TimeSpan GAZE_IDLE_TIME{ 25000000 };
|
||||
|
||||
public:
|
||||
virtual ~GazePointer();
|
||||
|
||||
/// <summary>
|
||||
/// Loads a settings collection into GazePointer.
|
||||
/// </summary>
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
|
||||
internal:
|
||||
void Reset();
|
||||
void SetElementStateDelay(UIElement ^element, PointerState pointerState, TimeSpan stateDelay);
|
||||
TimeSpan GetElementStateDelay(UIElement ^element, DependencyProperty^ property, TimeSpan defaultValue);
|
||||
TimeSpan GetElementStateDelay(UIElement^ element, PointerState pointerState);
|
||||
|
||||
// Provide a configurable delay for when the EyesOffDelay event is fired
|
||||
// GOTCHA: this value requires that _eyesOffTimer is instantiated so that it
|
||||
// can update the timer interval
|
||||
property TimeSpan EyesOffDelay
|
||||
{
|
||||
TimeSpan get() { return _eyesOffDelay; }
|
||||
void set(TimeSpan value)
|
||||
{
|
||||
_eyesOffDelay = value;
|
||||
|
||||
// convert GAZE_IDLE_TIME units (microseconds) to 100-nanosecond units used
|
||||
// by TimeSpan struct
|
||||
_eyesOffTimer->Interval = EyesOffDelay;
|
||||
}
|
||||
}
|
||||
|
||||
// Pluggable filter for eye tracking sample data. This defaults to being set to the
|
||||
// NullFilter which performs no filtering of input samples.
|
||||
property IGazeFilter^ Filter;
|
||||
|
||||
property bool IsCursorVisible
|
||||
{
|
||||
bool get() { return _gazeCursor->IsCursorVisible; }
|
||||
void set(bool value) { _gazeCursor->IsCursorVisible = value; }
|
||||
}
|
||||
|
||||
property int CursorRadius
|
||||
{
|
||||
int get() { return _gazeCursor->CursorRadius; }
|
||||
void set(int value) { _gazeCursor->CursorRadius = value; }
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
static property GazePointer^ Instance { GazePointer^ get(); }
|
||||
void OnPageUnloaded(Object^ sender, RoutedEventArgs^ args);
|
||||
EventRegistrationToken _unloadedToken;
|
||||
|
||||
void AddRoot(FrameworkElement^ element);
|
||||
void RemoveRoot(FrameworkElement^ element);
|
||||
|
||||
|
||||
property bool IsDeviceAvailable { bool get() { return _deviceCount != 0; }}
|
||||
event EventHandler<Object^>^ IsDeviceAvailableChanged;
|
||||
|
||||
private:
|
||||
|
||||
GazePointer();
|
||||
|
||||
private:
|
||||
|
||||
bool _isShuttingDown;
|
||||
|
||||
TimeSpan GetDefaultPropertyValue(PointerState state);
|
||||
|
||||
void InitializeHistogram();
|
||||
void InitializeGazeInputSource();
|
||||
void DeinitializeGazeInputSource();
|
||||
|
||||
void ActivateGazeTargetItem(GazeTargetItem^ target);
|
||||
GazeTargetItem^ GetHitTarget(Point gazePoint);
|
||||
GazeTargetItem^ ResolveHitTarget(Point gazePoint, TimeSpan timestamp);
|
||||
|
||||
void CheckIfExiting(TimeSpan curTimestamp);
|
||||
void GotoState(UIElement^ control, PointerState state);
|
||||
void RaiseGazePointerEvent(GazeTargetItem^ target, PointerState state, TimeSpan elapsedTime);
|
||||
|
||||
void OnGazeEntered(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeEnteredPreviewEventArgs^ args);
|
||||
void OnGazeMoved(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeMovedPreviewEventArgs^ args);
|
||||
void OnGazeExited(
|
||||
GazeInputSourcePreview^ provider,
|
||||
GazeExitedPreviewEventArgs^ args);
|
||||
|
||||
void ProcessGazePoint(TimeSpan timestamp, Point position);
|
||||
|
||||
void OnEyesOff(Object ^sender, Object ^ea);
|
||||
|
||||
void OnDeviceAdded(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherAddedPreviewEventArgs^ args);
|
||||
void OnDeviceRemoved(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherRemovedPreviewEventArgs^ args);
|
||||
|
||||
private:
|
||||
Vector<FrameworkElement^>^ _roots = ref new Vector<FrameworkElement^>();
|
||||
|
||||
TimeSpan _eyesOffDelay;
|
||||
|
||||
GazeCursor^ _gazeCursor;
|
||||
DispatcherTimer^ _eyesOffTimer;
|
||||
|
||||
// _offScreenElement is a pseudo-element that represents the area outside
|
||||
// the screen so we can track how long the user has been looking outside
|
||||
// the screen and appropriately trigger the EyesOff event
|
||||
Control^ _offScreenElement;
|
||||
|
||||
// The value is the total time that FrameworkElement has been gazed at
|
||||
Vector<GazeTargetItem^>^ _activeHitTargetTimes;
|
||||
|
||||
// A vector to track the history of observed gaze targets
|
||||
Vector<GazeHistoryItem^>^ _gazeHistory;
|
||||
TimeSpan _maxHistoryTime;
|
||||
|
||||
// Used to determine if exit events need to be fired by adding GAZE_IDLE_TIME to the last
|
||||
// saved timestamp
|
||||
TimeSpan _lastTimestamp;
|
||||
|
||||
GazeInputSourcePreview^ _gazeInputSource;
|
||||
EventRegistrationToken _gazeEnteredToken;
|
||||
EventRegistrationToken _gazeMovedToken;
|
||||
EventRegistrationToken _gazeExitedToken;
|
||||
CoreDispatcher^ _coreDispatcher;
|
||||
|
||||
GazeDeviceWatcherPreview^ _watcher;
|
||||
int _deviceCount;
|
||||
EventRegistrationToken _deviceAddedToken;
|
||||
EventRegistrationToken _deviceRemovedToken;
|
||||
|
||||
TimeSpan _defaultFixation = DEFAULT_FIXATION_DELAY;
|
||||
TimeSpan _defaultDwell = DEFAULT_DWELL_DELAY;
|
||||
TimeSpan _defaultDwellRepeatDelay = DEFAULT_DWELL_REPEAT_DELAY;
|
||||
TimeSpan _defaultRepeat = DEFAULT_REPEAT_DELAY;
|
||||
TimeSpan _defaultThreshold = DEFAULT_THRESHOLD_DELAY;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,82 @@
|
|||
#include "pch.h"
|
||||
#include "GazePointerProxy.h"
|
||||
#include "GazePointer.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
DependencyProperty^ GazePointerProxy::GazePointerProxyProperty::get()
|
||||
{
|
||||
static auto value = DependencyProperty::RegisterAttached("_GazePointerProxy", GazePointerProxy::typeid, GazePointerProxy::typeid,
|
||||
ref new PropertyMetadata(nullptr));
|
||||
return value;
|
||||
}
|
||||
|
||||
GazePointerProxy::GazePointerProxy(FrameworkElement^ element)
|
||||
: _element(element)
|
||||
{
|
||||
_loadedToken = element->Loaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnPageLoaded);
|
||||
_unloadedToken = element->Unloaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnPageUnloaded);
|
||||
}
|
||||
|
||||
void GazePointerProxy::SetGazeInteraction(FrameworkElement^ element, Interaction value)
|
||||
{
|
||||
auto proxy = safe_cast<GazePointerProxy^>(element->GetValue(GazePointerProxyProperty));
|
||||
if (proxy == nullptr)
|
||||
{
|
||||
proxy = ref new GazePointerProxy(element);
|
||||
element->SetValue(GazePointerProxyProperty, proxy);
|
||||
}
|
||||
|
||||
proxy->IsEnabled = value;
|
||||
}
|
||||
|
||||
void GazePointerProxy::IsEnabled::set(Interaction value)
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
|
||||
if (_isLoaded)
|
||||
{
|
||||
if (value == Interaction::Enabled)
|
||||
{
|
||||
GazePointer::Instance->AddRoot(_element);
|
||||
}
|
||||
else
|
||||
{
|
||||
GazePointer::Instance->RemoveRoot(_element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointerProxy::OnPageLoaded(Object^ sender, RoutedEventArgs^ args)
|
||||
{
|
||||
_element->Loaded -= _loadedToken;
|
||||
|
||||
_isLoaded = true;
|
||||
|
||||
if (_isEnabled == Interaction::Enabled)
|
||||
{
|
||||
GazePointer::Instance->AddRoot(_element);
|
||||
}
|
||||
}
|
||||
|
||||
void GazePointerProxy::OnPageUnloaded(Object^ sender, RoutedEventArgs^ args)
|
||||
{
|
||||
if (!_isLoaded)
|
||||
{
|
||||
_element->Loaded -= _loadedToken;
|
||||
}
|
||||
_element->Unloaded -= _loadedToken;
|
||||
|
||||
_isLoaded = false;
|
||||
|
||||
if (_isEnabled == Interaction::Enabled)
|
||||
{
|
||||
GazePointer::Instance->RemoveRoot(_element);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "Interaction.h"
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazePointerProxy sealed
|
||||
{
|
||||
public:
|
||||
|
||||
static property DependencyProperty^ GazePointerProxyProperty { DependencyProperty^ get(); };
|
||||
|
||||
internal:
|
||||
|
||||
static void SetGazeInteraction(FrameworkElement^ element, Interaction value);
|
||||
|
||||
private:
|
||||
|
||||
GazePointerProxy(FrameworkElement^ element);
|
||||
|
||||
property Interaction IsEnabled
|
||||
{
|
||||
Interaction get() { return _isEnabled; }
|
||||
void set(Interaction value);
|
||||
}
|
||||
|
||||
void OnPageLoaded(Object^ sender, RoutedEventArgs^ args);
|
||||
|
||||
void OnPageUnloaded(Object^ sender, RoutedEventArgs^ args);
|
||||
|
||||
private:
|
||||
|
||||
FrameworkElement^ const _element;
|
||||
EventRegistrationToken _loadedToken;
|
||||
EventRegistrationToken _unloadedToken;
|
||||
bool _isLoaded;
|
||||
Interaction _isEnabled;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeSettingsHelper.h"
|
||||
|
||||
using namespace concurrency;
|
||||
using namespace Windows::ApplicationModel::AppService;
|
||||
using namespace Windows::Foundation;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeSettingsHelper::GazeSettingsHelper()
|
||||
{
|
||||
}
|
||||
|
||||
Windows::Foundation::IAsyncAction^ GazeSettingsHelper::RetrieveSharedSettings(ValueSet^ settings)
|
||||
{
|
||||
return create_async([settings]{
|
||||
// Setup a new app service connection
|
||||
AppServiceConnection^ connection = ref new AppServiceConnection();
|
||||
connection->AppServiceName = "com.microsoft.ectksettings";
|
||||
connection->PackageFamilyName = "Microsoft.EyeControlToolkitSettings_s9y1p3hwd5qda";
|
||||
|
||||
// open the connection
|
||||
create_task(connection->OpenAsync()).then([settings, connection](AppServiceConnectionStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case AppServiceConnectionStatus::Success:
|
||||
// The new connection opened successfully
|
||||
// Set up the inputs and send a message to the service
|
||||
create_task(connection->SendMessageAsync(ref new ValueSet())).then([settings](AppServiceResponse^ response)
|
||||
{
|
||||
switch (response->Status)
|
||||
{
|
||||
case AppServiceResponseStatus::Success:
|
||||
for each (auto item in response->Message)
|
||||
{
|
||||
settings->Insert(item->Key, item->Value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case AppServiceResponseStatus::Failure:
|
||||
case AppServiceResponseStatus::ResourceLimitsExceeded:
|
||||
case AppServiceResponseStatus::Unknown:
|
||||
break;
|
||||
}
|
||||
}); // create_task(connection->SendMessageAsync(inputs))
|
||||
break;
|
||||
|
||||
default:
|
||||
case AppServiceConnectionStatus::AppNotInstalled:
|
||||
case AppServiceConnectionStatus::AppUnavailable:
|
||||
case AppServiceConnectionStatus::AppServiceUnavailable:
|
||||
case AppServiceConnectionStatus::Unknown:
|
||||
break;
|
||||
}
|
||||
}); // create_task(connection->OpenAsync())
|
||||
}); // create_async()
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,26 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
/// <summary>
|
||||
/// A helper class to read a ValueSet and retrieve relevant settings
|
||||
/// </summary>
|
||||
public ref class GazeSettingsHelper sealed
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves settings as a ValueSet from a shared store.
|
||||
/// </summary>
|
||||
static Windows::Foundation::IAsyncAction^ RetrieveSharedSettings(ValueSet^ settings);
|
||||
|
||||
private:
|
||||
GazeSettingsHelper();
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,47 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeStats.h"
|
||||
|
||||
using namespace Platform::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
GazeStats::GazeStats(int maxHistoryLen)
|
||||
{
|
||||
_maxHistoryLen = maxHistoryLen;
|
||||
_history = ref new Vector<Point>();
|
||||
}
|
||||
|
||||
void GazeStats::Reset()
|
||||
{
|
||||
_sumX = 0;
|
||||
_sumY = 0;
|
||||
_sumSquaredX = 0;
|
||||
_sumSquaredY = 0;
|
||||
_history->Clear();
|
||||
}
|
||||
|
||||
void GazeStats::Update(float x, float y)
|
||||
{
|
||||
Point pt(x, y);
|
||||
_history->Append(pt);
|
||||
|
||||
if (_history->Size > _maxHistoryLen)
|
||||
{
|
||||
auto oldest = _history->GetAt(0);
|
||||
_history->RemoveAt(0);
|
||||
|
||||
_sumX -= oldest.X;
|
||||
_sumY -= oldest.Y;
|
||||
_sumSquaredX -= oldest.X * oldest.X;
|
||||
_sumSquaredY -= oldest.Y * oldest.Y;
|
||||
}
|
||||
_sumX += x;
|
||||
_sumY += y;
|
||||
_sumSquaredX += x * x;
|
||||
_sumSquaredY += y * y;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,55 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Platform::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeStats sealed
|
||||
{
|
||||
public:
|
||||
GazeStats(int maxHistoryLen);
|
||||
void Reset();
|
||||
void Update(float x, float y);
|
||||
|
||||
property Point Mean
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
UINT count = _history->Size;
|
||||
return Point((float)_sumX / count, (float)_sumY / count);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// StdDev = sqrt(Variance) = sqrt(E[X^2] – (E[X])^2)
|
||||
//
|
||||
property Point StandardDeviation
|
||||
{
|
||||
Point get()
|
||||
{
|
||||
UINT count = _history->Size;
|
||||
if (count < _maxHistoryLen)
|
||||
{
|
||||
return Point(0.0f, 0.0f);
|
||||
}
|
||||
double meanX = _sumX / count;
|
||||
double meanY = _sumY / count;
|
||||
float stddevX = (float)sqrt((_sumSquaredX / count) - (meanX * meanX));
|
||||
float stddevY = (float)sqrt((_sumSquaredY / count) - (meanY * meanY));
|
||||
return Point(stddevX, stddevY);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UINT _maxHistoryLen;
|
||||
double _sumX;
|
||||
double _sumY;
|
||||
double _sumSquaredX;
|
||||
double _sumSquaredY;
|
||||
Vector<Point>^ _history;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,260 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GazeTargetItem.h"
|
||||
|
||||
#include "GazeElement.h"
|
||||
#include "GazeFeedbackPopupFactory.h"
|
||||
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Provider;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
static DependencyProperty^ GazeTargetItemProperty = DependencyProperty::RegisterAttached("_GazeTargetItem", GazeTargetItem::typeid, GazeTargetItem::typeid, ref new PropertyMetadata(nullptr));
|
||||
|
||||
ref class InvokeGazeTargetItem sealed : public GazeTargetItem
|
||||
{
|
||||
private:
|
||||
|
||||
IInvokeProvider ^ _invokeProvider;
|
||||
|
||||
internal:
|
||||
|
||||
InvokeGazeTargetItem(UIElement^ element, IInvokeProvider^ invokeProvider)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
_invokeProvider = invokeProvider;
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
_invokeProvider->Invoke();
|
||||
}
|
||||
};
|
||||
|
||||
ref class ToggleGazeTargetItem sealed : public GazeTargetItem
|
||||
{
|
||||
private:
|
||||
|
||||
IToggleProvider ^ _toggleProvider;
|
||||
|
||||
internal:
|
||||
|
||||
ToggleGazeTargetItem(UIElement^ element, IToggleProvider^ toggleProvider)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
_toggleProvider = toggleProvider;
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
_toggleProvider->Toggle();
|
||||
}
|
||||
};
|
||||
|
||||
ref class SelectionGazeTargetItem sealed : public GazeTargetItem
|
||||
{
|
||||
private:
|
||||
|
||||
ISelectionItemProvider ^ _selectionItemProvider;
|
||||
|
||||
internal:
|
||||
|
||||
SelectionGazeTargetItem(UIElement^ element, ISelectionItemProvider^ selectionItemProvider)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
_selectionItemProvider = selectionItemProvider;
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
_selectionItemProvider->Select();
|
||||
}
|
||||
};
|
||||
|
||||
ref class NonInvokeGazeTargetItem sealed : GazeTargetItem
|
||||
{
|
||||
private:
|
||||
|
||||
NonInvokeGazeTargetItem()
|
||||
: GazeTargetItem(ref new Page())
|
||||
{
|
||||
}
|
||||
|
||||
internal:
|
||||
|
||||
static property GazeTargetItem^ Instance
|
||||
{
|
||||
GazeTargetItem^ get()
|
||||
{
|
||||
static GazeTargetItem^ s_gazeTargetItem = ref new NonInvokeGazeTargetItem();
|
||||
return s_gazeTargetItem;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property bool IsInvokable { bool get() override { return false; } }
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ref class ExpandCollapseGazeTargetItem sealed : GazeTargetItem
|
||||
{
|
||||
private:
|
||||
|
||||
IExpandCollapseProvider ^ _expandCollapseProvider;
|
||||
|
||||
internal:
|
||||
|
||||
ExpandCollapseGazeTargetItem(UIElement^ element, IExpandCollapseProvider^ expandCollapseProvider)
|
||||
: GazeTargetItem(element)
|
||||
{
|
||||
_expandCollapseProvider = expandCollapseProvider;
|
||||
}
|
||||
|
||||
void Invoke() override
|
||||
{
|
||||
switch (_expandCollapseProvider->ExpandCollapseState)
|
||||
{
|
||||
case ExpandCollapseState::Collapsed:
|
||||
_expandCollapseProvider->Expand();
|
||||
break;
|
||||
|
||||
case ExpandCollapseState::Expanded:
|
||||
_expandCollapseProvider->Collapse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GazeTargetItem^ GazeTargetItem::NonInvokable::get() { return NonInvokeGazeTargetItem::Instance; }
|
||||
|
||||
GazeTargetItem^ GazeTargetItem::GetOrCreate(UIElement^ element)
|
||||
{
|
||||
GazeTargetItem^ item;
|
||||
|
||||
auto value = element->ReadLocalValue(GazeTargetItemProperty);
|
||||
|
||||
if (value != DependencyProperty::UnsetValue)
|
||||
{
|
||||
item = safe_cast<GazeTargetItem^>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(element);
|
||||
|
||||
auto invokeProvider = dynamic_cast<IInvokeProvider^>(peer);
|
||||
if (invokeProvider != nullptr)
|
||||
{
|
||||
item = ref new InvokeGazeTargetItem(element, invokeProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto toggleProvider = dynamic_cast<IToggleProvider^>(peer);
|
||||
if (toggleProvider != nullptr)
|
||||
{
|
||||
item = ref new ToggleGazeTargetItem(element, toggleProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto selectionItemProvider = dynamic_cast<ISelectionItemProvider^>(peer);
|
||||
if (selectionItemProvider != nullptr)
|
||||
{
|
||||
item = ref new SelectionGazeTargetItem(element, selectionItemProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto expandCollapseProvider = dynamic_cast<IExpandCollapseProvider^>(peer);
|
||||
if (expandCollapseProvider != nullptr)
|
||||
{
|
||||
item = ref new ExpandCollapseGazeTargetItem(element, expandCollapseProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = NonInvokeGazeTargetItem::Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
element->SetValue(GazeTargetItemProperty, item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void GazeTargetItem::RaiseProgressEvent(DwellProgressState state)
|
||||
{
|
||||
// TODO: We should eliminate non-invokable controls before we arrive here!
|
||||
if (dynamic_cast<Page^>(TargetElement) != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notifiedProgressState != state || state == DwellProgressState::Progressing)
|
||||
{
|
||||
auto handled = false;
|
||||
|
||||
auto gazeElement = GazeInput::GetGazeElement(TargetElement);
|
||||
if (gazeElement != nullptr)
|
||||
{
|
||||
handled = gazeElement->RaiseProgressFeedback(TargetElement, state, ElapsedTime - _prevStateTime, _nextStateTime - _prevStateTime);
|
||||
}
|
||||
|
||||
if (!handled && state != DwellProgressState::Idle)
|
||||
{
|
||||
if (_feedbackPopup == nullptr)
|
||||
{
|
||||
_feedbackPopup = GazeFeedbackPopupFactory::Get();
|
||||
}
|
||||
|
||||
auto control = safe_cast<Control^>(TargetElement);
|
||||
|
||||
auto transform = control->TransformToVisual(_feedbackPopup);
|
||||
auto bounds = transform->TransformBounds(*ref new Rect(*ref new Point(0, 0),
|
||||
*ref new Size(safe_cast<float>(control->ActualWidth), safe_cast<float>(control->ActualHeight))));
|
||||
auto rectangle = safe_cast<Rectangle^>(_feedbackPopup->Child);
|
||||
|
||||
if (state == DwellProgressState::Progressing)
|
||||
{
|
||||
auto progress = ((double)(ElapsedTime - _prevStateTime).Duration) / (_nextStateTime - _prevStateTime).Duration;
|
||||
|
||||
if (0 <= progress && progress < 1)
|
||||
{
|
||||
rectangle->Stroke = GazeInput::DwellFeedbackProgressBrush;
|
||||
rectangle->Width = (1 - progress) * bounds.Width;
|
||||
rectangle->Height = (1 - progress) * bounds.Height;
|
||||
|
||||
_feedbackPopup->HorizontalOffset = bounds.Left + progress * bounds.Width / 2;
|
||||
_feedbackPopup->VerticalOffset = bounds.Top + progress * bounds.Height / 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rectangle->Stroke = GazeInput::DwellFeedbackCompleteBrush;
|
||||
rectangle->Width = bounds.Width;
|
||||
rectangle->Height = bounds.Height;
|
||||
|
||||
_feedbackPopup->HorizontalOffset = bounds.Left;
|
||||
_feedbackPopup->VerticalOffset = bounds.Top;
|
||||
}
|
||||
|
||||
_feedbackPopup->IsOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_feedbackPopup != nullptr)
|
||||
{
|
||||
GazeFeedbackPopupFactory::Return(_feedbackPopup);
|
||||
_feedbackPopup = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_notifiedProgressState = state;
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,101 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DwellProgressState.h"
|
||||
#include "GazeInput.h"
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
private ref class GazeTargetItem abstract
|
||||
{
|
||||
internal:
|
||||
static property GazeTargetItem^ NonInvokable{ GazeTargetItem^ get(); }
|
||||
|
||||
property TimeSpan DetailedTime;
|
||||
property TimeSpan OverflowTime;
|
||||
property TimeSpan ElapsedTime { TimeSpan get() { return DetailedTime + OverflowTime; } }
|
||||
property TimeSpan NextStateTime;
|
||||
property TimeSpan LastTimestamp;
|
||||
property PointerState ElementState;
|
||||
property UIElement^ TargetElement;
|
||||
property int RepeatCount;
|
||||
property int MaxDwellRepeatCount;
|
||||
|
||||
GazeTargetItem(UIElement^ target)
|
||||
{
|
||||
TargetElement = target;
|
||||
}
|
||||
|
||||
static GazeTargetItem^ GetOrCreate(UIElement^ element);
|
||||
|
||||
virtual void Invoke() = 0;
|
||||
|
||||
virtual property bool IsInvokable { bool get() { return true; } }
|
||||
|
||||
void Reset(TimeSpan nextStateTime)
|
||||
{
|
||||
ElementState = PointerState::PreEnter;
|
||||
DetailedTime = TimeSpanZero;
|
||||
OverflowTime = TimeSpanZero;
|
||||
NextStateTime = nextStateTime;
|
||||
RepeatCount = 0;
|
||||
MaxDwellRepeatCount = GazeInput::GetMaxDwellRepeatCount(TargetElement);
|
||||
}
|
||||
|
||||
void GiveFeedback()
|
||||
{
|
||||
if (_nextStateTime != NextStateTime)
|
||||
{
|
||||
_prevStateTime = _nextStateTime;
|
||||
_nextStateTime = NextStateTime;
|
||||
}
|
||||
|
||||
if (ElementState != _notifiedPointerState)
|
||||
{
|
||||
switch (ElementState)
|
||||
{
|
||||
case PointerState::Dwell:
|
||||
case PointerState::Fixation:
|
||||
RaiseProgressEvent(DwellProgressState::Progressing);
|
||||
break;
|
||||
|
||||
case PointerState::Exit:
|
||||
case PointerState::PreEnter:
|
||||
RaiseProgressEvent(DwellProgressState::Idle);
|
||||
break;
|
||||
}
|
||||
|
||||
_notifiedPointerState = ElementState;
|
||||
}
|
||||
else if (ElementState == PointerState::Dwell || ElementState == PointerState::Fixation)
|
||||
{
|
||||
if (RepeatCount <= MaxDwellRepeatCount)
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState::Progressing);
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseProgressEvent(DwellProgressState::Complete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void RaiseProgressEvent(DwellProgressState state);
|
||||
|
||||
PointerState _notifiedPointerState = PointerState::Exit;
|
||||
TimeSpan _prevStateTime;
|
||||
TimeSpan _nextStateTime;
|
||||
DwellProgressState _notifiedProgressState = DwellProgressState::Idle;
|
||||
Popup^ _feedbackPopup;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,66 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This struct encapsulates the location and timestamp associated with the user's gaze
|
||||
/// and is used as an input and output parameter for the IGazeFilter::Update method
|
||||
/// </summary>
|
||||
private ref struct GazeFilterArgs sealed
|
||||
{
|
||||
/// <summary>
|
||||
/// The current point in the gaze stream
|
||||
/// </summary>
|
||||
property Point Location {Point get() { return _location; }}
|
||||
|
||||
/// <summary>
|
||||
/// The timestamp associated with the current point
|
||||
/// </summary>
|
||||
property TimeSpan Timestamp {TimeSpan get() { return _timestamp; }}
|
||||
|
||||
internal:
|
||||
|
||||
GazeFilterArgs(Point location, TimeSpan timestamp)
|
||||
{
|
||||
_location = location;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Point _location;
|
||||
TimeSpan _timestamp;
|
||||
};
|
||||
|
||||
// Every filter must provide an Wpdate method which transforms sample data
|
||||
// and returns filtered output
|
||||
private interface class IGazeFilter
|
||||
{
|
||||
GazeFilterArgs^ Update(GazeFilterArgs^ args);
|
||||
void LoadSettings(ValueSet^ settings);
|
||||
};
|
||||
|
||||
|
||||
// Basic filter which performs no input filtering -- easy to
|
||||
// use as a default filter.
|
||||
private ref class NullFilter sealed : public IGazeFilter
|
||||
{
|
||||
public:
|
||||
virtual inline GazeFilterArgs^ Update(GazeFilterArgs^ args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
|
||||
virtual inline void LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,30 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This enum indicates the current state of gaze interaction.
|
||||
/// </summary>
|
||||
public enum class Interaction
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The state of gaze interaction is inherited from the nearest parent
|
||||
/// </summary>
|
||||
Inherited,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is enabled
|
||||
/// </summary>
|
||||
Enabled,
|
||||
|
||||
/// <summary>
|
||||
/// Gaze interaction is disabled
|
||||
/// </summary>
|
||||
Disabled
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<SDKReference Include="Microsoft.VCLibs, Version 14.0">
|
||||
<Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name>
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,257 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{a5e98964-45b1-442d-a07a-298a3221d81e}</ProjectGuid>
|
||||
<Keyword>WindowsRuntimeComponent</Keyword>
|
||||
<RootNamespace>Microsoft.Toolkit.Uwp.Input.GazeInteraction</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>true</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
<WindowsTargetPlatformMinVersion>10.0.17134.0</WindowsTargetPlatformMinVersion>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<ProjectName>Microsoft.Toolkit.Uwp.Input.GazeInteraction</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<OutDir>$(SolutionDir)$(MSBuildProjectName)\Output\$(Platform)\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GazeInput.h" />
|
||||
<ClInclude Include="GazeCursor.h" />
|
||||
<ClInclude Include="GazeElement.h" />
|
||||
<ClInclude Include="Interaction.h" />
|
||||
<ClInclude Include="GazeFeedbackPopupFactory.h" />
|
||||
<ClInclude Include="GazeHistoryItem.h" />
|
||||
<ClInclude Include="DwellInvokedRoutedEventArgs.h" />
|
||||
<ClInclude Include="GazePointer.h" />
|
||||
<ClInclude Include="StateChangedEventArgs.h" />
|
||||
<ClInclude Include="GazePointerProxy.h" />
|
||||
<ClInclude Include="PointerState.h" />
|
||||
<ClInclude Include="DwellProgressEventArgs.h" />
|
||||
<ClInclude Include="DwellProgressState.h" />
|
||||
<ClInclude Include="GazeSettingsHelper.h" />
|
||||
<ClInclude Include="GazeStats.h" />
|
||||
<ClInclude Include="GazeTargetItem.h" />
|
||||
<ClInclude Include="IGazeFilter.h" />
|
||||
<ClInclude Include="OneEuroFilter.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GazeInput.cpp" />
|
||||
<ClCompile Include="GazeCursor.cpp" />
|
||||
<ClCompile Include="GazeFeedbackPopupFactory.cpp" />
|
||||
<ClCompile Include="GazePointer.cpp" />
|
||||
<ClCompile Include="GazePointerProxy.cpp" />
|
||||
<ClCompile Include="GazeSettingsHelper.cpp" />
|
||||
<ClCompile Include="GazeStats.cpp" />
|
||||
<ClCompile Include="GazeTargetItem.cpp" />
|
||||
<ClCompile Include="OneEuroFilter.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>f664c808-2b14-44e2-b2c2-0e7f5c370edd</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Sources">
|
||||
<UniqueIdentifier>{3088c194-1660-4cdf-b724-1d4cd29741ef}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Headers">
|
||||
<UniqueIdentifier>{3d051054-c757-4fd1-8f31-5a2912c88d15}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GazeCursor.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazeStats.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OneEuroFilter.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazeSettingsHelper.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazeTargetItem.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazeFeedbackPopupFactory.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazePointerProxy.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazeInput.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GazePointer.cpp">
|
||||
<Filter>Sources</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeCursor.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazePointer.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeStats.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IGazeFilter.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OneEuroFilter.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeSettingsHelper.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeElement.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeHistoryItem.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeTargetItem.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeFeedbackPopupFactory.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazePointerProxy.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GazeInput.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PointerState.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StateChangedEventArgs.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DwellProgressState.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DwellInvokedRoutedEventArgs.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DwellProgressEventArgs.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Interaction.h">
|
||||
<Filter>Headers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,114 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
||||
#include "OneEuroFilter.h"
|
||||
|
||||
//
|
||||
// http://www.lifl.fr/~casiez/1euro/
|
||||
// http://www.lifl.fr/~casiez/publications/CHI2012-casiez.pdf
|
||||
//
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
OneEuroFilter::OneEuroFilter()
|
||||
{
|
||||
|
||||
_lastTimestamp = TimeSpanZero;
|
||||
Beta = ONEEUROFILTER_DEFAULT_BETA;
|
||||
Cutoff = ONEEUROFILTER_DEFAULT_CUTOFF;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
OneEuroFilter::OneEuroFilter(float cutoff, float beta)
|
||||
{
|
||||
_lastTimestamp = TimeSpanZero;
|
||||
Beta = beta;
|
||||
Cutoff = cutoff;
|
||||
VelocityCutoff = ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF;
|
||||
}
|
||||
|
||||
GazeFilterArgs^ OneEuroFilter::Update(GazeFilterArgs^ args)
|
||||
{
|
||||
if (_lastTimestamp == TimeSpanZero)
|
||||
{
|
||||
_lastTimestamp = args->Timestamp;
|
||||
_pointFilter = ref new LowpassFilter(args->Location);
|
||||
_deltaFilter = ref new LowpassFilter(Point());
|
||||
return ref new GazeFilterArgs(args->Location, args->Timestamp);
|
||||
}
|
||||
|
||||
Point gazePoint = args->Location;
|
||||
|
||||
// Reducing _beta increases lag. Increasing beta decreases lag and improves response time
|
||||
// But a really high value of beta also contributes to jitter
|
||||
float beta = Beta;
|
||||
|
||||
// This simply represents the cutoff frequency. A lower value reduces jiiter
|
||||
// and higher value increases jitter
|
||||
float cf = Cutoff;
|
||||
Point cutoff = Point(cf, cf);
|
||||
|
||||
// determine sampling frequency based on last time stamp
|
||||
float samplingFrequency = 10000000.0f / max(1, (args->Timestamp - _lastTimestamp).Duration);
|
||||
_lastTimestamp = args->Timestamp;
|
||||
|
||||
// calculate change in distance...
|
||||
Point deltaDistance;
|
||||
deltaDistance.X = gazePoint.X - _pointFilter->Previous.X;
|
||||
deltaDistance.Y = gazePoint.Y - _pointFilter->Previous.Y;
|
||||
|
||||
// ...and velocity
|
||||
Point velocity(deltaDistance.X * samplingFrequency, deltaDistance.Y * samplingFrequency);
|
||||
|
||||
// find the alpha to use for the velocity filter
|
||||
float velocityAlpha = Alpha(samplingFrequency, VelocityCutoff);
|
||||
Point velocityAlphaPoint(velocityAlpha, velocityAlpha);
|
||||
|
||||
// find the filtered velocity
|
||||
Point filteredVelocity = _deltaFilter->Update(velocity, velocityAlphaPoint);
|
||||
|
||||
// ignore sign since it will be taken care of by deltaDistance
|
||||
filteredVelocity.X = abs(filteredVelocity.X);
|
||||
filteredVelocity.Y = abs(filteredVelocity.Y);
|
||||
|
||||
// compute new cutoff to use based on velocity
|
||||
cutoff.X += beta * filteredVelocity.X;
|
||||
cutoff.Y += beta * filteredVelocity.Y;
|
||||
|
||||
// find the new alpha to use to filter the points
|
||||
Point distanceAlpha(Alpha(samplingFrequency, cutoff.X), Alpha(samplingFrequency, cutoff.Y));
|
||||
|
||||
// find the filtered point
|
||||
Point filteredPoint = _pointFilter->Update(gazePoint, distanceAlpha);
|
||||
|
||||
// compute the new args
|
||||
auto fa = ref new GazeFilterArgs(filteredPoint, args->Timestamp);
|
||||
return fa;
|
||||
}
|
||||
|
||||
float OneEuroFilter::Alpha(float rate, float cutoff)
|
||||
{
|
||||
const float PI = 3.14159265f;
|
||||
float te = 1.0f / rate;
|
||||
float tau = (float)(1.0f / (2 * PI * cutoff));
|
||||
float alpha = te / (te + tau);
|
||||
return alpha;
|
||||
}
|
||||
void OneEuroFilter::LoadSettings(ValueSet^ settings)
|
||||
{
|
||||
if (settings->HasKey("OneEuroFilter.Beta"))
|
||||
{
|
||||
Beta = (float)(settings->Lookup("OneEuroFilter.Beta"));
|
||||
}
|
||||
if (settings->HasKey("OneEuroFilter.Cutoff"))
|
||||
{
|
||||
Cutoff = (float)(settings->Lookup("OneEuroFilter.Cutoff"));
|
||||
}
|
||||
if (settings->HasKey("OneEuroFilter.VelocityCutoff"))
|
||||
{
|
||||
VelocityCutoff = (float)(settings->Lookup("OneEuroFilter.VelocityCutoff"));
|
||||
}
|
||||
}
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,65 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IGazeFilter.h"
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
const float ONEEUROFILTER_DEFAULT_BETA = 5.0f;
|
||||
const float ONEEUROFILTER_DEFAULT_CUTOFF = 0.1f;
|
||||
const float ONEEUROFILTER_DEFAULT_VELOCITY_CUTOFF = 1.0f;
|
||||
|
||||
private ref class LowpassFilter sealed
|
||||
{
|
||||
public:
|
||||
LowpassFilter()
|
||||
{
|
||||
Previous = Point(0, 0);
|
||||
}
|
||||
|
||||
LowpassFilter(Point initial)
|
||||
{
|
||||
Previous = initial;
|
||||
}
|
||||
|
||||
property Point Previous;
|
||||
|
||||
Point Update(Point point, Point alpha)
|
||||
{
|
||||
Point pt;
|
||||
pt.X = (alpha.X * point.X) + ((1 - alpha.X) * Previous.X);
|
||||
pt.Y = (alpha.Y * point.Y) + ((1 - alpha.Y) * Previous.Y);
|
||||
Previous = pt;
|
||||
return Previous;
|
||||
}
|
||||
};
|
||||
|
||||
private ref class OneEuroFilter sealed : public IGazeFilter
|
||||
{
|
||||
public:
|
||||
OneEuroFilter();
|
||||
OneEuroFilter(float cutoff, float beta);
|
||||
virtual GazeFilterArgs^ Update(GazeFilterArgs^ args);
|
||||
virtual void LoadSettings(ValueSet^ settings);
|
||||
|
||||
public:
|
||||
property float Beta;
|
||||
property float Cutoff;
|
||||
property float VelocityCutoff;
|
||||
|
||||
private:
|
||||
float Alpha(float rate, float cutoff);
|
||||
|
||||
private:
|
||||
TimeSpan _lastTimestamp;
|
||||
LowpassFilter^ _pointFilter;
|
||||
LowpassFilter^ _deltaFilter;
|
||||
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,48 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This enum reflects the states that a user's gaze through while interacting with a control using their eyes.
|
||||
/// </summary>
|
||||
public enum class PointerState
|
||||
{
|
||||
/// <summary>
|
||||
/// User's gaze is no longer on the control
|
||||
/// </summary>
|
||||
Exit = 0,
|
||||
|
||||
// The order of the following elements is important because
|
||||
// they represent states that linearly transition to their
|
||||
// immediate successors.
|
||||
|
||||
/// <summary>
|
||||
/// For internal use only
|
||||
/// </summary>
|
||||
PreEnter = 1,
|
||||
|
||||
/// <summary>
|
||||
/// User's gaze has entered a control
|
||||
/// </summary>
|
||||
Enter = 2,
|
||||
|
||||
/// <summary>
|
||||
/// User eye's are focused on the control
|
||||
/// </summary>
|
||||
Fixation = 3,
|
||||
|
||||
/// <summary>
|
||||
/// User is conciously dwelling on the control with an intent to invoke, e.g. click a button
|
||||
/// </summary>
|
||||
Dwell = 4,
|
||||
|
||||
/// <summary>
|
||||
/// User is continuing to dwell on the control for repeated invocation. (For internal use only)
|
||||
/// </summary>
|
||||
DwellRepeat = 5
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,42 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PointerState.h"
|
||||
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
||||
BEGIN_NAMESPACE_GAZE_INPUT
|
||||
|
||||
/// <summary>
|
||||
/// This parameter is passed to the StateChanged event.
|
||||
/// </summary>
|
||||
public ref struct StateChangedEventArgs sealed
|
||||
{
|
||||
/// <summary>
|
||||
/// The state of user's gaze with respect to a control
|
||||
/// </summary>
|
||||
property GazeInteraction::PointerState PointerState {GazeInteraction::PointerState get() { return _pointerState; }}
|
||||
|
||||
/// <summary>
|
||||
/// Elapsed time since the last state
|
||||
/// </summary>
|
||||
property TimeSpan ElapsedTime {TimeSpan get() { return _elapsedTime; }}
|
||||
|
||||
internal:
|
||||
|
||||
StateChangedEventArgs(UIElement^ target, GazeInteraction::PointerState state, TimeSpan elapsedTime)
|
||||
{
|
||||
_hitTarget = target;
|
||||
_pointerState = state;
|
||||
_elapsedTime = elapsedTime;
|
||||
}
|
||||
|
||||
private:
|
||||
UIElement ^ _hitTarget;
|
||||
GazeInteraction::PointerState _pointerState;
|
||||
TimeSpan _elapsedTime;
|
||||
};
|
||||
|
||||
END_NAMESPACE_GAZE_INPUT
|
|
@ -0,0 +1,4 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#include "pch.h"
|
|
@ -0,0 +1,54 @@
|
|||
//Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
|
||||
//See LICENSE in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
|
||||
//#define _USE_MATH_DEFINES
|
||||
//#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
//#include <Windows.Input.Gaze.h>
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Devices::Enumeration;
|
||||
using namespace Windows::Devices::HumanInterfaceDevice;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Media;
|
||||
using namespace Windows::UI::Xaml::Shapes;
|
||||
|
||||
#include <strsafe.h>
|
||||
private class Debug
|
||||
{
|
||||
public:
|
||||
static void WriteLine(wchar_t *format, ...)
|
||||
{
|
||||
wchar_t message[1024];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
StringCchVPrintf(message, 1024, format, args);
|
||||
OutputDebugString(message);
|
||||
OutputDebugString(L"\n");
|
||||
}
|
||||
};
|
||||
|
||||
inline static TimeSpan operator + (const TimeSpan& lhs, const TimeSpan& rhs) { return TimeSpan{ lhs.Duration + rhs.Duration }; }
|
||||
inline static TimeSpan operator - (const TimeSpan& lhs, const TimeSpan& rhs) { return TimeSpan{ lhs.Duration - rhs.Duration }; }
|
||||
inline static TimeSpan operator * (int lhs, const TimeSpan& rhs) { return TimeSpan{ lhs*rhs.Duration }; }
|
||||
inline static bool operator < (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration < rhs.Duration; }
|
||||
inline static bool operator <= (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration <= rhs.Duration; }
|
||||
inline static bool operator > (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration > rhs.Duration; }
|
||||
inline static bool operator >= (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration >= rhs.Duration; }
|
||||
inline static bool operator == (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration == rhs.Duration; }
|
||||
inline static bool operator != (const TimeSpan& lhs, const TimeSpan& rhs) { return lhs.Duration != rhs.Duration; }
|
||||
|
||||
static TimeSpan TimeSpanZero{ 0 };
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(uint64 milliseconds) { return TimeSpan{ (int64)(10 * milliseconds) }; }
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(int64 milliseconds) { return TimeSpan{ 10 * milliseconds }; }
|
||||
inline static TimeSpan TimeSpanFromMicroseconds(int milliseconds) { return TimeSpan{ 10 * milliseconds }; }
|
||||
|
||||
#define BEGIN_NAMESPACE_GAZE_INPUT namespace Microsoft { namespace Toolkit { namespace Uwp { namespace Input { namespace GazeInteraction {
|
||||
#define END_NAMESPACE_GAZE_INPUT } } } } }
|
|
@ -3,9 +3,9 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<Title>UWP Community Toolkit Notifications for JavaScript</Title>
|
||||
<Title>Windows Community Toolkit Notifications for JavaScript</Title>
|
||||
<Description>Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML.
|
||||
Supports adaptive tiles and adaptive/interactive toasts for Windows 10. It is part of the UWP Community Toolkit. Supports C# and C++ UWP project types (see Microsoft.Toolkit.Uwp.Notifications). Also works with C# portable class libraries and non-UWP C# projects like server projects.</Description>
|
||||
Supports adaptive tiles and adaptive/interactive toasts for Windows 10. It is part of the Windows Community Toolkit. Supports C# and C++ UWP project types (see Microsoft.Toolkit.Uwp.Notifications). Also works with C# portable class libraries and non-UWP C# projects like server projects.</Description>
|
||||
<PackageTags>notifications win10 windows-10 tile tiles toast toasts badge xml uwp javascript</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard1.4;uap10.0;native</TargetFrameworks>
|
||||
<DefineConstants>$(DefineConstants);NETFX_CORE</DefineConstants>
|
||||
<Title>UWP Community Toolkit Notifications</Title>
|
||||
<Title>Windows Community Toolkit Notifications</Title>
|
||||
<Description>Generate tile, toast, and badge notifications for Windows 10 via code, with the help of IntelliSense, instead of directly using XML.
|
||||
Supports adaptive tiles and adaptive/interactive toasts for Windows 10. It is part of the UWP Community Toolkit. Supports C# and C++ UWP project types (see Microsoft.Toolkit.Uwp.Notifications.JavaScript for the JS version). Also works with C# portable class libraries and non-UWP C# projects like server projects.</Description>
|
||||
Supports adaptive tiles and adaptive/interactive toasts for Windows 10. It is part of the Windows Community Toolkit. Supports C# and C++ UWP project types (see Microsoft.Toolkit.Uwp.Notifications.JavaScript for the JS version). Also works with C# portable class libraries and non-UWP C# projects like server projects.</Description>
|
||||
<PackageTags>notifications win10 windows 10 tile tiles toast toasts badge xml uwp c# csharp c++</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0' or '$(TargetFramework)' == 'native' ">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="5.4.1" />
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'native' ">
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.Controls
|
|||
_printHelper.OnPrintSucceeded += PrintHelper_OnPrintSucceeded;
|
||||
_printHelper.OnPrintCanceled += PrintHelper_OnPrintCanceled;
|
||||
|
||||
await _printHelper.ShowPrintUIAsync("UWP Community Toolkit Sample App");
|
||||
await _printHelper.ShowPrintUIAsync("Windows Community Toolkit Sample App");
|
||||
}
|
||||
|
||||
private void ReleasePrintHelper()
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<AssemblyName>Microsoft.Toolkit.Uwp.SampleApp</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.14393.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
|
@ -76,7 +76,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
|
@ -105,7 +105,7 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
|
@ -119,7 +119,7 @@
|
|||
<Version>4.1.6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.4.1</Version>
|
||||
<Version>6.0.8</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Monaco.Editor">
|
||||
<Version>0.5.0-alpha</Version>
|
||||
|
@ -309,6 +309,8 @@
|
|||
<Content Include="SamplePages\Expander\Expander.png" />
|
||||
<Content Include="SamplePages\DropShadowPanel\Trex.png" />
|
||||
<Content Include="SamplePages\DropShadowPanel\Unicorn.png" />
|
||||
<Content Include="SamplePages\GazeInteraction\GazeInteraction.png" />
|
||||
<Content Include="SamplePages\GazeTracing\GazeTracing.png" />
|
||||
<Content Include="SamplePages\GridSplitter\GridSplitter.png" />
|
||||
<Content Include="SamplePages\HamburgerMenu\HamburgerMenu.png" />
|
||||
<Content Include="SamplePages\AlignmentGrid\AlignmentGrid.png" />
|
||||
|
@ -496,9 +498,17 @@
|
|||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\LayoutTransformControl\LayoutTransformControlXaml.bind" />
|
||||
<Content Include="SamplePages\GazeTracing\GazeTracingXaml.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\PrintHelper\PrintHelperXaml.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\GazeInteraction\GazeInteractionXaml.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\GazeInteraction\GazeInteractionCode.bind" />
|
||||
<Content Include="SamplePages\GazeTracing\GazeTracingCode.bind" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.xaml.cs">
|
||||
|
@ -568,6 +578,12 @@
|
|||
<DependentUpon>BackdropBlurBrushPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\FrameworkElementExtensions\FrameworkElementExtensionsPage.xaml.cs" />
|
||||
<Compile Include="SamplePages\GazeInteraction\GazeInteractionPage.xaml.cs">
|
||||
<DependentUpon>GazeInteractionPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\GazeTracing\GazeTracingPage.xaml.cs">
|
||||
<DependentUpon>GazeTracingPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\ImageBlendBrush\ImageBlendBrushPage.xaml.cs">
|
||||
<DependentUpon>ImageBlendBrushPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -924,6 +940,14 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\GazeInteraction\GazeInteractionPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\GazeTracing\GazeTracingPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\HeaderedContentControl\HeaderedContentControlPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -1250,6 +1274,10 @@
|
|||
<Project>{b1e850ff-dde6-44d5-a830-34250e97a687}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Connectivity</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj">
|
||||
<Project>{a5e98964-45b1-442d-a07a-298a3221d81e}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.Input.GazeInteraction</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp.DeveloperTools\Microsoft.Toolkit.Uwp.DeveloperTools.csproj">
|
||||
<Project>{e7697922-9555-4cfb-aee0-c5f4d657e559}</Project>
|
||||
<Name>Microsoft.Toolkit.Uwp.DeveloperTools</Name>
|
||||
|
|
|
@ -23,6 +23,7 @@ using System.Text;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.Uwp.Helpers;
|
||||
using Microsoft.Toolkit.Uwp.Input.GazeInteraction;
|
||||
using Microsoft.Toolkit.Uwp.SampleApp.Models;
|
||||
using Microsoft.Toolkit.Uwp.UI.Animations;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
|
@ -652,6 +653,28 @@ namespace Microsoft.Toolkit.Uwp.SampleApp
|
|||
}
|
||||
}
|
||||
|
||||
// Search in Microsoft.Toolkit.Uwp.Input.GazeInteraction
|
||||
|
||||
var gazeType = Interaction.Enabled;
|
||||
|
||||
assembly = gazeType.GetType().GetTypeInfo().Assembly;
|
||||
|
||||
foreach (var typeInfo in assembly.ExportedTypes)
|
||||
|
||||
{
|
||||
|
||||
if (typeInfo.Name == typeName)
|
||||
|
||||
{
|
||||
|
||||
return typeInfo;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="Microsoft.Toolkit.Uwp.SampleApp.App">
|
||||
<uap:VisualElements DisplayName="UWP Community Toolkit Sample App" Square150x150Logo="Assets\UWPCommunityToolkitSampleAppMedTile.png" Square44x44Logo="Assets\UWPCommunityToolkitSampleAppAppList.png" Description="This app demonstrates the controls, services and APIs available in the UWP Community Toolkit. The toolkit is available as NuGet package for your C# or VB.NET projects. Source code of the toolkit and the sample app is available on GitHub." BackgroundColor="#3750D1">
|
||||
<uap:VisualElements DisplayName="Windows Community Toolkit Sample App" Square150x150Logo="Assets\UWPCommunityToolkitSampleAppMedTile.png" Square44x44Logo="Assets\UWPCommunityToolkitSampleAppAppList.png" Description="This app demonstrates the controls, services and APIs available in the Windows Community Toolkit. The toolkit is available as NuGet package for your C# or VB.NET projects. Source code of the toolkit and the sample app is available on GitHub." BackgroundColor="#3750D1">
|
||||
<uap:LockScreen Notification="badge" BadgeLogo="Assets\UWPCommunityToolkitSampleAppBadgeLogo.png" />
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\UWPCommunityToolkitSampleAppWideTile.png" Square310x310Logo="Assets\UWPCommunityToolkitSampleAppLargeTile.png" Square71x71Logo="Assets\UWPCommunityToolkitSampleAppSmallTile.png">
|
||||
<uap:ShowNameOnTiles>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<Extensions>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="uwpct">
|
||||
<uap:DisplayName>UWP Community Toolkit</uap:DisplayName>
|
||||
<uap:DisplayName>Windows Community Toolkit</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
<Extension Category="windows.backgroundTasks" EntryPoint="Microsoft.Toolkit.Uwp.Samples.BackgroundTasks.TestBackgroundTask">
|
||||
|
@ -45,5 +45,6 @@
|
|||
<Capability Name="privateNetworkClientServer" />
|
||||
<DeviceCapability Name="location" />
|
||||
<DeviceCapability Name="bluetooth" />
|
||||
<DeviceCapability Name="gazeInput" />
|
||||
</Capabilities>
|
||||
</Package>
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GazeInteraction/GazeInteraction.png
Normal file
После Ширина: | Высота: | Размер: 21 KiB |
|
@ -0,0 +1,51 @@
|
|||
|
||||
// You can handle State changes in the StateChanged handler declared in the XAML markup for the element
|
||||
|
||||
private int dwellCount = 0;
|
||||
|
||||
private void GazeButtonControl_StateChanged(object sender, GazePointerEventArgs ea)
|
||||
{
|
||||
|
||||
if (ea.PointerState == GazePointerState.Enter)
|
||||
{
|
||||
// do something
|
||||
}
|
||||
if (ea.PointerState == GazePointerState.Fixation)
|
||||
{
|
||||
// do something
|
||||
}
|
||||
if (ea.PointerState == GazePointerState.Dwell)
|
||||
{
|
||||
if (dwellCount == 0)
|
||||
{
|
||||
// do something
|
||||
|
||||
dwellCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do something
|
||||
|
||||
dwellCount += 1;
|
||||
}
|
||||
}
|
||||
if (ea.PointerState == GazePointerState.Exit)
|
||||
{
|
||||
// do something
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// You can respond to dwell progress in the ProgressFeedback handler
|
||||
|
||||
private void OnProgressFeedback(object sender, GazeProgressEventArgs e)
|
||||
{
|
||||
// do something
|
||||
}
|
||||
|
||||
private void OnGazeInvoked(object sender, GazeInvokedRoutedEventArgs e)
|
||||
{
|
||||
// do something
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
<Page
|
||||
x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.GazeInteractionPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.Common"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:g="using:Microsoft.Toolkit.Uwp.Input.GazeInteraction"
|
||||
g:GazeInput.Interaction="Enabled"
|
||||
g:GazeInput.IsCursorVisible="True"
|
||||
g:GazeInput.CursorRadius="20"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<local:TimeSpanConverter x:Key="ConvertTimeToMilliSecs"/>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" ></RowDefinition>
|
||||
<RowDefinition Height="1*" ></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid x:Name="XamlRoot"/>
|
||||
|
||||
<Grid Grid.Row="1" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50" ></RowDefinition>
|
||||
<RowDefinition Height="50" ></RowDefinition>
|
||||
<RowDefinition Height="50" ></RowDefinition>
|
||||
<RowDefinition Height="1*" ></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="1" Grid.Row="1">
|
||||
<Rectangle x:Name="EnterRec" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FFB6EC89" Visibility="Collapsed"/>
|
||||
<TextBlock Text="ENTER" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="2" Grid.Row="1">
|
||||
<Rectangle x:Name="FixationRec" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FF6ACE17" Visibility="Collapsed"/>
|
||||
<TextBlock Text="FIXATION" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="3" Grid.Row="1">
|
||||
<Rectangle x:Name="DwellRec" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FF1790CE" Visibility="Collapsed"/>
|
||||
<TextBlock Text="DWELL" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="4" Grid.Row="1">
|
||||
<TextBlock Text="Repeat
Delay" FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Center" TextAlignment="Center"/>
|
||||
</Grid>
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="5" Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle x:Name="RepeatRec" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FF175ACE" Visibility="Collapsed" Grid.ColumnSpan="4" />
|
||||
<TextBlock Text="REPEAT" FontSize="20" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="1" />
|
||||
<TextBlock x:Name="DwellCountText" Text="10" FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Column="2" Visibility="Collapsed" TextAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid BorderBrush="Black" HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Column="6" Grid.Row="1">
|
||||
<Rectangle x:Name="ExitRec" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#FFCE1F17" Visibility="Collapsed"/>
|
||||
<TextBlock Text="EXIT" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
|
||||
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="1" BorderBrush="Black" BorderThickness="1,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.ThresholdDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="2" BorderBrush="Black" BorderThickness="1,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.FixationDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="3" BorderBrush="Black" BorderThickness="1,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="19*"/>
|
||||
<ColumnDefinition Width="62*"/>
|
||||
<ColumnDefinition Width="19*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.DwellDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="4" BorderBrush="Black" BorderThickness="1,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.RepeatDelayDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="5" BorderBrush="Black" BorderThickness="1,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.DwellRepeatDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Stretch" Height="50" VerticalAlignment="Stretch" Width="100" Grid.Row="2" Grid.Column="6" BorderBrush="Black" BorderThickness="1,0,1,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center"/>
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding PropertyDescriptor.Expando.ThresholdDuration.Value, Converter={StaticResource ConvertTimeToMilliSecs}}" VerticalAlignment="Center" Grid.Column="1" Margin="5,0"/>
|
||||
<Rectangle Height="1" Stroke="Black" VerticalAlignment="Center" Grid.Column="2"/>
|
||||
</Grid>
|
||||
|
||||
<ProgressBar Visibility="Collapsed" BorderBrush="Black" BorderThickness="1" Background="White" x:Name="DwellProgressBar" Height="10" Minimum="0" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="6"/>
|
||||
|
||||
<TextBlock x:Name="WarningText" Text="Please attach eye tracking device" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="6" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" Foreground="MediumBlue" Visibility="Collapsed" />
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,138 @@
|
|||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
using System;
|
||||
using Microsoft.Toolkit.Uwp.Input.GazeInteraction;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Shapes;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class GazeInteractionPage : IXamlRenderListener
|
||||
{
|
||||
private GazeElement gazeButtonControl;
|
||||
|
||||
private int dwellCount = 0;
|
||||
|
||||
public GazeInteractionPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
GazeInput.IsDeviceAvailableChanged += GazeInput_IsDeviceAvailableChanged;
|
||||
|
||||
WarnUserToPlugInDevice();
|
||||
|
||||
var buttonControl = control.FindChildByName("TargetButton") as Button;
|
||||
|
||||
if (buttonControl != null)
|
||||
{
|
||||
gazeButtonControl = GazeInput.GetGazeElement(buttonControl);
|
||||
|
||||
if (gazeButtonControl == null)
|
||||
{
|
||||
gazeButtonControl = new GazeElement();
|
||||
GazeInput.SetGazeElement(buttonControl, gazeButtonControl);
|
||||
}
|
||||
|
||||
if (gazeButtonControl != null)
|
||||
{
|
||||
gazeButtonControl.DwellProgressFeedback += OnProgressFeedback;
|
||||
gazeButtonControl.Invoked += OnGazeInvoked;
|
||||
gazeButtonControl.StateChanged += GazeButtonControl_StateChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GazeInput_IsDeviceAvailableChanged(object sender, object e)
|
||||
{
|
||||
WarnUserToPlugInDevice();
|
||||
}
|
||||
|
||||
private void WarnUserToPlugInDevice()
|
||||
{
|
||||
if (GazeInput.IsDeviceAvailable)
|
||||
{
|
||||
WarningText.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else
|
||||
{
|
||||
WarningText.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGazeInvoked(object sender, DwellInvokedRoutedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void OnProgressFeedback(object sender, DwellProgressEventArgs e)
|
||||
{
|
||||
DwellProgressBar.Maximum = 1.0;
|
||||
DwellProgressBar.Value = e.Progress;
|
||||
if (e.State == DwellProgressState.Complete)
|
||||
{
|
||||
DwellProgressBar.Value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void GazeButtonControl_StateChanged(object sender, StateChangedEventArgs ea)
|
||||
{
|
||||
if (ea.PointerState == PointerState.Enter)
|
||||
{
|
||||
EnterRec.Visibility = Visibility.Visible;
|
||||
DwellCountText.Visibility = Visibility.Collapsed;
|
||||
DwellCountText.Text = string.Empty;
|
||||
dwellCount = 0;
|
||||
ExitRec.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (ea.PointerState == PointerState.Fixation)
|
||||
{
|
||||
FixationRec.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
if (ea.PointerState == PointerState.Dwell)
|
||||
{
|
||||
if (dwellCount == 0)
|
||||
{
|
||||
DwellRec.Visibility = Visibility.Visible;
|
||||
dwellCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RepeatRec.Visibility = Visibility.Visible;
|
||||
DwellCountText.Text = dwellCount.ToString();
|
||||
DwellCountText.Visibility = Visibility.Visible;
|
||||
dwellCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ea.PointerState == PointerState.Exit)
|
||||
{
|
||||
ExitRec.Visibility = Visibility.Visible;
|
||||
|
||||
EnterRec.Visibility = Visibility.Collapsed;
|
||||
FixationRec.Visibility = Visibility.Collapsed;
|
||||
DwellRec.Visibility = Visibility.Collapsed;
|
||||
RepeatRec.Visibility = Visibility.Collapsed;
|
||||
DwellProgressBar.Value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
|
||||
xmlns:g="using:Microsoft.Toolkit.Uwp.Input.GazeInteraction"
|
||||
g:GazeInput.Interaction="@[Interaction:Enum:Interaction.Enabled]"
|
||||
g:GazeInput.IsCursorVisible="@[IsCursorVisible:Bool:True]"
|
||||
g:GazeInput.CursorRadius="@[CursorRadius:Slider:5:5-50]@"
|
||||
>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Button x:Name="TargetButton" HorizontalAlignment="Center" BorderBrush="#7FFFFFFF"
|
||||
|
||||
g:GazeInput.ThresholdDuration="@[ThresholdDuration:TimeSpan:50:1-5000]@"
|
||||
g:GazeInput.FixationDuration="@[FixationDuration:TimeSpan:350:1-5000]@"
|
||||
g:GazeInput.DwellDuration="@[DwellDuration:TimeSpan:400:1-5000]@"
|
||||
g:GazeInput.RepeatDelayDuration="@[RepeatDelayDuration:TimeSpan:400:1-5000]@"
|
||||
g:GazeInput.DwellRepeatDuration="@[DwellRepeatDuration:TimeSpan:400:1-5000]@"
|
||||
g:GazeInput.MaxDwellRepeatCount="@[MaxDwellRepeatCount:Slider:0:0-20]@"
|
||||
|
||||
Width="@[ButtonSize:Slider:100:10-300]"
|
||||
Height="@[ButtonSize]"
|
||||
/>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
После Ширина: | Высота: | Размер: 22 KiB |
|
@ -0,0 +1,35 @@
|
|||
using Windows.Devices.Input.Preview;
|
||||
|
||||
public sealed partial class GazeTracingPage : IXamlRenderListener
|
||||
{
|
||||
private GazeInputSourcePreview gazeInputSourcePreview;
|
||||
|
||||
public ObservableCollection<Point> GazeHistory { get; set; } = new ObservableCollection<Point>();
|
||||
|
||||
public int MaxGazeHistorySize { get; set; }
|
||||
public GazeTracingPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
DataContext = this;
|
||||
|
||||
MaxGazeHistorySize = 100;
|
||||
|
||||
gazeInputSourcePreview = GazeInputSourcePreview.GetForCurrentView();
|
||||
gazeInputSourcePreview.GazeMoved += GazeInputSourcePreview_GazeMoved;
|
||||
}
|
||||
|
||||
private void GazeInputSourcePreview_GazeMoved(GazeInputSourcePreview sender, GazeMovedPreviewEventArgs args)
|
||||
{
|
||||
var point = args.CurrentPoint.EyeGazePosition;
|
||||
if (!point.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GazeHistory.Add(point.Value);
|
||||
if (GazeHistory.Count > MaxGazeHistorySize)
|
||||
{
|
||||
GazeHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<Page
|
||||
x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.GazeTracingPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="XamlRoot">
|
||||
<Grid.Resources>
|
||||
<DataTemplate x:Key="ItemsControlItemTemplate1">
|
||||
<Ellipse Fill="#FF1111FC" HorizontalAlignment="Left" Height="5" Stroke="Black" VerticalAlignment="Top" Width="5" RenderTransformOrigin="0.5,0.5">
|
||||
<Ellipse.RenderTransform>
|
||||
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
|
||||
</Ellipse.RenderTransform>
|
||||
</Ellipse>
|
||||
|
||||
</DataTemplate>
|
||||
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
|
||||
<Canvas ></Canvas>
|
||||
</ItemsPanelTemplate>
|
||||
</Grid.Resources>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<StackPanel>
|
||||
<RichTextBlock Margin="20,10,20,20" Foreground="{StaticResource Brush-Grey-03}">
|
||||
<Paragraph>Use the controls below to select various visuals, then see how gaze tracing works. For example, try tracing the Square or Circle. Then, try to trace the same shape with gaze on the blank page.</Paragraph>
|
||||
</RichTextBlock>
|
||||
<Pivot HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
|
||||
<PivotItem Header="Blank" />
|
||||
<PivotItem Header="Square">
|
||||
<Rectangle Width="500" Height="500" Stroke="Black" StrokeThickness="5" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Circle">
|
||||
<Ellipse Width="500" Height="500" Stroke="Black" StrokeThickness="5" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Owl">
|
||||
<Image Width="500" Height="500" Source="/Assets/Photos/Owl.jpg" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Big Four">
|
||||
<Image Width="500" Height="500" Source="/Assets/Photos/BigFourSummerHeat.jpg" />
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</StackPanel>
|
||||
<ItemsControl ItemTemplate="{StaticResource ItemsControlItemTemplate1}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding GazeHistory}" ItemsPanel="{StaticResource ItemsPanelTemplate1}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,90 @@
|
|||
// ******************************************************************
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
|
||||
// ******************************************************************
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.Devices.Input.Preview;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class GazeTracingPage : IXamlRenderListener
|
||||
{
|
||||
private GazeInputSourcePreview gazeInputSourcePreview;
|
||||
private Frame rootFrame;
|
||||
|
||||
public ObservableCollection<Point> GazeHistory { get; set; } = new ObservableCollection<Point>();
|
||||
|
||||
public int TracePointDiameter { get; set; }
|
||||
|
||||
public int MaxGazeHistorySize { get; set; }
|
||||
|
||||
public bool ShowIntermediatePoints { get; set; }
|
||||
|
||||
public GazeTracingPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
DataContext = this;
|
||||
|
||||
ShowIntermediatePoints = false;
|
||||
MaxGazeHistorySize = 100;
|
||||
|
||||
rootFrame = Window.Current.Content as Frame;
|
||||
gazeInputSourcePreview = GazeInputSourcePreview.GetForCurrentView();
|
||||
gazeInputSourcePreview.GazeMoved += GazeInputSourcePreview_GazeMoved;
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
if (control.FindChildByName("Points") is ItemsControl itemsControl)
|
||||
{
|
||||
itemsControl.ItemsSource = GazeHistory;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGazeHistory(GazePointPreview pt)
|
||||
{
|
||||
if (!pt.EyeGazePosition.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var transform = rootFrame.TransformToVisual(this);
|
||||
var point = transform.TransformPoint(pt.EyeGazePosition.Value);
|
||||
GazeHistory.Add(point);
|
||||
if (GazeHistory.Count > MaxGazeHistorySize)
|
||||
{
|
||||
GazeHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void GazeInputSourcePreview_GazeMoved(GazeInputSourcePreview sender, GazeMovedPreviewEventArgs args)
|
||||
{
|
||||
if (!ShowIntermediatePoints)
|
||||
{
|
||||
UpdateGazeHistory(args.CurrentPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
var points = args.GetIntermediatePoints();
|
||||
foreach (var pt in points)
|
||||
{
|
||||
UpdateGazeHistory(pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<Page
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="Page"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<DataTemplate x:Key="ItemsControlItemTemplate1">
|
||||
<Ellipse Fill="#FF1111FC" HorizontalAlignment="Left" Width="@[Size:Slider:5:5-50]" Height="@[Size:Slider:5:5-50]" Stroke="Black" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" DataContext="{Binding DataContext, ElementName=Page, Mode=OneWay}">
|
||||
<Ellipse.RenderTransform>
|
||||
<TranslateTransform X="{Binding DataContext.X, RelativeSource={RelativeSource TemplatedParent}}" Y="{Binding DataContext.Y, RelativeSource={RelativeSource TemplatedParent}}"/>
|
||||
</Ellipse.RenderTransform>
|
||||
</Ellipse>
|
||||
|
||||
</DataTemplate>
|
||||
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
|
||||
<Canvas ></Canvas>
|
||||
</ItemsPanelTemplate>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<StackPanel>
|
||||
<RichTextBlock Margin="20,10,20,20" Foreground="{StaticResource Brush-Grey-03}">
|
||||
<Paragraph>Use the controls below to select various visuals, then see how gaze tracing works. For example, try tracing the Square or Circle. Then, try to trace the same shape with gaze on the blank page.</Paragraph>
|
||||
</RichTextBlock>
|
||||
<Pivot HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
|
||||
<PivotItem Header="Blank" />
|
||||
<PivotItem Header="Square">
|
||||
<Rectangle Width="500" Height="500" Stroke="Black" StrokeThickness="5" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Circle">
|
||||
<Ellipse Width="500" Height="500" Stroke="Black" StrokeThickness="5" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Owl">
|
||||
<Image Width="500" Height="500" Source="/Assets/Photos/Owl.jpg" />
|
||||
</PivotItem>
|
||||
<PivotItem Header="Big Four">
|
||||
<Image Width="500" Height="500" Source="/Assets/Photos/BigFourSummerHeat.jpg" />
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</StackPanel>
|
||||
<ItemsControl x:Name="Points" ItemTemplate="{StaticResource ItemsControlItemTemplate1}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding GazeHistory}" ItemsPanel="{StaticResource ItemsPanelTemplate1}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -9,7 +9,7 @@
|
|||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<controls:HeaderedTextBlock
|
||||
Header="@[Header:String:Name]"
|
||||
Text="@[Text:String:UWP Toolkit]"
|
||||
Text="@[Text:String:Windows Community Toolkit]"
|
||||
Orientation="@[Orientation:Enum:Orientation.Vertical]"
|
||||
Margin="20,10,0,0" />
|
||||
</Grid>
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
<TextBox x:Name="ShareText"
|
||||
Margin="0,0,0,12"
|
||||
Header="Share"
|
||||
Text="Check out the UWP Community Toolkit! https://aka.ms/uwpct" />
|
||||
Text="Check out the Windows Community Toolkit! https://aka.ms/uwpct" />
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button x:Name="ShareButton"
|
||||
Margin="0,0,12,0"
|
||||
|
|
|
@ -250,7 +250,7 @@ Sometimes you need to preserve indentation, too. In those cases, you can create
|
|||
System.out.println("Hello world!");
|
||||
}
|
||||
|
||||
Starting with UWP Community Toolkit v1.4, you can also use GitHub code notification by creating a block surrounded by 3x\` (3 backticks). This can also be used with Language Identifiers on the entering backticks, such as:
|
||||
Starting with Windows Community Toolkit v1.4, you can also use GitHub code notification by creating a block surrounded by 3x\` (3 backticks). This can also be used with Language Identifiers on the entering backticks, such as:
|
||||
|
||||
\`\`\`csharp
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@
|
|||
Text="Print" />
|
||||
<MenuFlyoutSeparator />
|
||||
<MenuFlyoutSubItem Text="Recent Files">
|
||||
<MenuFlyoutItem Text="UWP ToolKit GridSplitter" />
|
||||
<MenuFlyoutItem Text="UWP ToolKit WrapPanel" />
|
||||
<MenuFlyoutItem Text="Windows Community ToolKit GridSplitter" />
|
||||
<MenuFlyoutItem Text="Windows Community ToolKit WrapPanel" />
|
||||
</MenuFlyoutSubItem>
|
||||
<MenuFlyoutItem controls:Menu.InputGestureText="Ctrl+Shift+E"
|
||||
Command="{StaticResource GenericCommand}"
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
await MicrosoftGraphService.Instance.User.Message.SendEmailAsync(subject, content, BodyType.Text, toRecipients, ccRecipients, Importance.Normal);
|
||||
|
||||
// Sending a second message in html format
|
||||
// await MicrosoftGraphService.Instance.User.Message.SendEmailAsync("Introducing the UWP Community Toolkit", GetHtmlMessage(), BodyType.Html, toRecipients, ccRecipients);
|
||||
// await MicrosoftGraphService.Instance.User.Message.SendEmailAsync("Introducing the Windows Community Toolkit", GetHtmlMessage(), BodyType.Html, toRecipients, ccRecipients);
|
||||
}
|
||||
catch (ServiceException ex)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,7 @@ printHelper.OnPrintFailed += PrintHelper_OnPrintFailed;
|
|||
printHelper.OnPrintSucceeded += PrintHelper_OnPrintSucceeded;
|
||||
|
||||
// Start printing process
|
||||
await printHelper.ShowPrintUIAsync("UWP Community Toolkit Sample App");
|
||||
await printHelper.ShowPrintUIAsync("Windows Community Toolkit Sample App");
|
||||
|
||||
// Event handlers
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
printHelperOptions.AddDisplayOption(StandardPrintTaskOptions.Orientation);
|
||||
}
|
||||
|
||||
await _printHelper.ShowPrintUIAsync("UWP Community Toolkit Sample App", printHelperOptions);
|
||||
await _printHelper.ShowPrintUIAsync("Windows Community Toolkit Sample App", printHelperOptions);
|
||||
}
|
||||
|
||||
private async void DirectPrint_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
|
@ -136,7 +136,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
printHelperOptions.AddDisplayOption(StandardPrintTaskOptions.Orientation);
|
||||
}
|
||||
|
||||
await _printHelper.ShowPrintUIAsync("UWP Community Toolkit Sample App", printHelperOptions, true);
|
||||
await _printHelper.ShowPrintUIAsync("Windows Community Toolkit Sample App", printHelperOptions, true);
|
||||
}
|
||||
|
||||
private async void CustomPrint_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
|
@ -163,7 +163,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
|
||||
|
||||
// Static header
|
||||
var header = new TextBlock { Text = "UWP Community Toolkit Sample App - Print Helper - Custom Print", Margin = new Thickness(0, 0, 0, 20) };
|
||||
var header = new TextBlock { Text = "Windows Community Toolkit Sample App - Print Helper - Custom Print", Margin = new Thickness(0, 0, 0, 20) };
|
||||
Grid.SetRow(header, 0);
|
||||
grid.Children.Add(header);
|
||||
|
||||
|
@ -195,7 +195,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
printHelperOptions.AddDisplayOption(StandardPrintTaskOptions.Orientation);
|
||||
}
|
||||
|
||||
_printHelper.ShowPrintUIAsync("UWP Community Toolkit Sample App", printHelperOptions);
|
||||
_printHelper.ShowPrintUIAsync("Windows Community Toolkit Sample App", printHelperOptions);
|
||||
}
|
||||
|
||||
private void ReleasePrintHelper()
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
{
|
||||
public sealed partial class PullToRefreshListViewPage : IXamlRenderListener
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
private readonly ObservableCollection<Item> _items;
|
||||
|
||||
public PullToRefreshListViewPage()
|
||||
|
@ -50,5 +51,6 @@ namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
|||
{
|
||||
AddItems();
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
Background="White"
|
||||
OverscrollLimit="@[OverscrollLimit:DoubleSlider:0.4:0-1]"
|
||||
PullThreshold="@[PullThreshold:Slider:100:50-300]"
|
||||
IsPullToRefreshWithMouseEnabled="@[IsPullToRefreshWithMouseEnabled:Bool:True]">
|
||||
IsPullToRefreshWithMouseEnabled="@[IsPullToRefreshWithMouseEnabled:Bool:True]"
|
||||
UseRefreshContainerWhenPossible="@[UseRefreshContainerWhenPossible:Bool:True]">
|
||||
<controls:PullToRefreshListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock AutomationProperties.Name="{Binding Title}"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<TextBlock x:Name="CurrentTheme" />
|
||||
|
||||
<TextBlock
|
||||
Text="*This sample doesn't work since the UWP Community Toolkit requests the Light Theme at the current time."
|
||||
Text="*This sample doesn't work since the Windows Community Toolkit requests the Light Theme at the current time."
|
||||
FontStyle="Italic"
|
||||
Margin="0,10,0,0" />
|
||||
</StackPanel>
|
||||
|
|
|
@ -130,11 +130,13 @@
|
|||
{
|
||||
"Name": "PullToRefreshListView",
|
||||
"Type": "PullToRefreshListViewPage",
|
||||
"About": "PullToRefreshListView is derived from the built in ListView in the Universal Windows Platform. It enables the popular Pull To Refresh pattern.",
|
||||
"About": "The PullToRefreshListView will be removed in a future major release. Please use the RefreshContainer control available in the 1803 version of Windows.",
|
||||
"CodeUrl": "https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/PullToRefreshListView",
|
||||
"CodeFile": "PullToRefreshListViewCode.bind",
|
||||
"XamlCodeFile": "PullToRefreshListViewXaml.bind",
|
||||
"Icon": "/SamplePages/PullToRefreshListView/PullToRefreshListView.png",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "The PullToRefreshListView will be removed in a future major release. Please use the RefreshContainer control available in the 1803 version of Windows.",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/Microsoft/UWPCommunityToolkit/master/docs/controls/PullToRefreshListview.md"
|
||||
},
|
||||
{
|
||||
|
@ -870,5 +872,31 @@
|
|||
"Icon": "/SamplePages/Analytics/Analytics.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Gaze",
|
||||
"Icon": "Icons/DeveloperTools.png",
|
||||
"Samples": [
|
||||
{
|
||||
"Name": "GazeInteraction",
|
||||
"Type": "GazeInteractionPage",
|
||||
"About": "Demonstrate the properties and events of the GazePointer library",
|
||||
"XamlCodeFile": "GazeInteractionXaml.bind",
|
||||
"CodeFile": "GazeInteractionCode.bind",
|
||||
"Icon": "/SamplePages/GazeInteraction/GazeInteraction.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/Microsoft/UWPCommunityToolkit/master/docs/gaze/Gaze.md",
|
||||
"ApiCheck": "Windows.Devices.Input.Preview.GazeInputSourcePreview"
|
||||
},
|
||||
{
|
||||
"Name": "GazeTracing",
|
||||
"Type": "GazeTracingPage",
|
||||
"About": "Shows how to use the Windows 10 API for eye trackers",
|
||||
"CodeUrl": "https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.Input.Gaze/",
|
||||
"XamlCodeFile": "GazeTracingXaml.bind",
|
||||
"CodeFile": "GazeTracingCode.bind",
|
||||
"Icon": "/SamplePages/GazeTracing/GazeTracing.png",
|
||||
"ApiCheck": "Windows.Devices.Input.Preview.GazeInputSourcePreview"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -3,7 +3,7 @@
|
|||
"new-samples": [ "StaggeredPanel", "RadialGradientBrush", "Markdown Parser", "NavigationViewStyles", "StringExtensions" ],
|
||||
"resources": [
|
||||
{
|
||||
"title": "UWP Community Toolkit",
|
||||
"title": "Toolkit",
|
||||
"links": [
|
||||
{
|
||||
"title": "GitHub Repository",
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<AssemblyName>Microsoft.Toolkit.Uwp.Samples.BackgroundTasks</AssemblyName>
|
||||
<DefaultLanguage>fr-FR</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
|
||||
<TargetPlatformVersion>10.0.17134.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.14393.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
|
@ -117,7 +117,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.4.1</Version>
|
||||
<Version>6.0.8</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<Title>UWP Community Toolkit Services</Title>
|
||||
<Description>This library enables access to different data sources like Bing, Facebook, LinkedIn, MicrosoftGraph, OneDrive and Twitter. It is part of the UWP Community Toolkit.</Description>
|
||||
<Title>Windows Community Toolkit Services</Title>
|
||||
<Description>This library enables access to different data sources like Bing, Facebook, LinkedIn, MicrosoftGraph, OneDrive and Twitter. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Twitter OAuth Facebook Bing MicrosoftGraph LinkedIn OneDrive</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace Microsoft.Toolkit.Uwp.Services.OneDrive.Platform
|
|||
{
|
||||
if (content.Size > OneDriveUploadConstants.SimpleUploadMaxSize)
|
||||
{
|
||||
throw new ServiceException(new Error { Message = "The file size cannot exceed 4MB, use UploadFileAsync instead ", Code = "MaxSizeExceeded", ThrowSite = "UWP Community Toolkit" });
|
||||
throw new ServiceException(new Error { Message = "The file size cannot exceed 4MB, use UploadFileAsync instead ", Code = "MaxSizeExceeded", ThrowSite = "Windows Community Toolkit" });
|
||||
}
|
||||
|
||||
streamContent = content.AsStreamForRead();
|
||||
|
@ -193,7 +193,7 @@ namespace Microsoft.Toolkit.Uwp.Services.OneDrive.Platform
|
|||
var response = await _service.Provider.GraphProvider.HttpProvider.SendAsync(request).ConfigureAwait(false);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new ServiceException(new Error { Message = "Could not create an UploadSession", Code = "NoUploadSession", ThrowSite = "UWP Community Toolkit" });
|
||||
throw new ServiceException(new Error { Message = "Could not create an UploadSession", Code = "NoUploadSession", ThrowSite = "Windows Community Toolkit" });
|
||||
}
|
||||
|
||||
_oneDriveStorageFolder.IsUploadCompleted = false;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<Title>UWP Community Toolkit Animations</Title>
|
||||
<Description>This library provides helpers and services on top of Windows Composition and XAML storyboards. It is part of the UWP Community Toolkit.</Description>
|
||||
<Title>Windows Community Toolkit Animations</Title>
|
||||
<Description>This library provides helpers and services on top of Windows Composition and XAML storyboards. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Animations Composition</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -26,6 +26,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls.Design.Common
|
|||
/// <summary>
|
||||
/// Basic Controls category.
|
||||
/// </summary>
|
||||
public const string Toolkit = "UWP Community Toolkit";
|
||||
public const string Toolkit = "Windows Community Toolkit";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ using System.Windows;
|
|||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("UWP Community Toolkit Controls (Design)")]
|
||||
[assembly: AssemblyDescription("Design time support for UWP Community Toolkit Controls")]
|
||||
[assembly: AssemblyTitle("Windows Community Toolkit Controls (Design)")]
|
||||
[assembly: AssemblyDescription("Design time support for Windows Community Toolkit Controls")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("UWP Community Toolkit")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2017")]
|
||||
[assembly: AssemblyProduct("Windows Community Toolkit")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0</TargetFramework>
|
||||
<Title>UWP Community Toolkit Controls</Title>
|
||||
<Description>This library provides XAML user controls. It is part of the UWP Community Toolkit.</Description>
|
||||
<Title>Windows Community Toolkit Controls</Title>
|
||||
<Description>This library provides XAML user controls. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Controls XAML Hamburger Range WrapPanel Adaptive Markdown</PackageTags>
|
||||
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -14,6 +14,7 @@ using System;
|
|||
using System.Windows.Input;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
@ -33,6 +34,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
[TemplatePart(Name = PartIndicatorTransform, Type = typeof(CompositeTransform))]
|
||||
[TemplatePart(Name = PartDefaultIndicatorContent, Type = typeof(TextBlock))]
|
||||
[TemplatePart(Name = PullAndReleaseIndicatorContent, Type = typeof(ContentPresenter))]
|
||||
[Obsolete("The PullToRefreshListView will be removed in a future major release. Please use the RefreshContainer control available in the 1803 version of Windows")]
|
||||
public class PullToRefreshListView : ListView
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -95,6 +97,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
public static readonly DependencyProperty IsPullToRefreshWithMouseEnabledProperty =
|
||||
DependencyProperty.Register(nameof(IsPullToRefreshWithMouseEnabled), typeof(bool), typeof(PullToRefreshListView), new PropertyMetadata(false));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="UseRefreshContainerWhenPossible"/> dependency property
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty UseRefreshContainerWhenPossibleProperty =
|
||||
DependencyProperty.Register(nameof(UseRefreshContainerWhenPossible), typeof(bool), typeof(PullToRefreshListView), new PropertyMetadata(false, OnUseRefreshContainerWhenPossibleChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether <see cref="RefreshContainer"/> is supported
|
||||
/// </summary>
|
||||
public static bool IsRefreshContainerSupported { get; } = ApiInformation.IsTypePresent("Windows.UI.Xaml.Controls.RefreshContainer");
|
||||
|
||||
private const string PartRoot = "Root";
|
||||
private const string PartScroller = "ScrollViewer";
|
||||
private const string PartContentTransform = "ContentTransform";
|
||||
|
@ -103,6 +116,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
private const string PartIndicatorTransform = "RefreshIndicatorTransform";
|
||||
private const string PartDefaultIndicatorContent = "DefaultIndicatorContent";
|
||||
private const string PullAndReleaseIndicatorContent = "PullAndReleaseIndicatorContent";
|
||||
private const string PartRefreshContainer = "RefreshContainer";
|
||||
|
||||
private Border _root;
|
||||
private Border _refreshIndicatorBorder;
|
||||
|
@ -123,6 +137,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
private double _overscrollMultiplier;
|
||||
private bool _isManipulatingWithMouse;
|
||||
private double _startingVerticalOffset;
|
||||
private ControlTemplate _previousTemplateUsed;
|
||||
private RefreshContainer _refreshContainer;
|
||||
|
||||
private bool UsingRefreshContainer => IsRefreshContainerSupported && UseRefreshContainerWhenPossible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the HamburgerMenu should use the NavigationView when possible (Fall Creators Update and above)
|
||||
/// When set to true and the device supports NavigationView, the HamburgerMenu will use a template based on NavigationView
|
||||
/// </summary>
|
||||
public bool UseRefreshContainerWhenPossible
|
||||
{
|
||||
get { return (bool)GetValue(UseRefreshContainerWhenPossibleProperty); }
|
||||
set { SetValue(UseRefreshContainerWhenPossibleProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the user has requested content to be refreshed
|
||||
|
@ -185,6 +213,23 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
_root.ManipulationCompleted -= Scroller_ManipulationCompleted;
|
||||
}
|
||||
|
||||
_root = null;
|
||||
_refreshIndicatorBorder = null;
|
||||
_refreshIndicatorTransform = null;
|
||||
_scroller = null;
|
||||
_contentTransform = null;
|
||||
_headerTransform = null;
|
||||
_footerTransform = null;
|
||||
_scrollerContent = null;
|
||||
_defaultIndicatorContent = null;
|
||||
_pullAndReleaseIndicatorContent = null;
|
||||
_scrollerVerticalScrollBar = null;
|
||||
|
||||
if (UsingRefreshContainer)
|
||||
{
|
||||
OnApplyRefreshContainerTemplate();
|
||||
}
|
||||
|
||||
_root = GetTemplateChild(PartRoot) as Border;
|
||||
_scroller = GetTemplateChild(PartScroller) as ScrollViewer;
|
||||
_scrollerContent = GetTemplateChild(PartScrollerContent) as ItemsPresenter;
|
||||
|
@ -225,6 +270,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
base.OnApplyTemplate();
|
||||
}
|
||||
|
||||
private void OnApplyRefreshContainerTemplate()
|
||||
{
|
||||
if (_refreshContainer != null)
|
||||
{
|
||||
_refreshContainer.RefreshRequested -= RefreshContainer_RefreshRequested;
|
||||
}
|
||||
|
||||
_refreshContainer = GetTemplateChild(PartRefreshContainer) as RefreshContainer;
|
||||
|
||||
if (_refreshContainer != null)
|
||||
{
|
||||
_refreshContainer.RefreshRequested += RefreshContainer_RefreshRequested;
|
||||
}
|
||||
}
|
||||
|
||||
private void Scroller_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
|
||||
{
|
||||
// Other input are already managed by the scroll viewer
|
||||
|
@ -629,6 +689,18 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
}
|
||||
}
|
||||
|
||||
private void RefreshContainer_RefreshRequested(object sender, RefreshRequestedEventArgs args)
|
||||
{
|
||||
using (var deferral = args.GetDeferral())
|
||||
{
|
||||
RefreshRequested?.Invoke(this, EventArgs.Empty);
|
||||
if (RefreshCommand != null && RefreshCommand.CanExecute(null))
|
||||
{
|
||||
RefreshCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Overscroll Limit. Value between 0 and 1 where 1 is the height of the control. Default is 0.3
|
||||
/// </summary>
|
||||
|
@ -663,6 +735,30 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
d.SetValue(ReleaseToRefreshLabelProperty, e.NewValue);
|
||||
}
|
||||
|
||||
private static void OnUseRefreshContainerWhenPossibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var list = d as PullToRefreshListView;
|
||||
if (list == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (list.UseRefreshContainerWhenPossible && IsRefreshContainerSupported)
|
||||
{
|
||||
ResourceDictionary dict = new ResourceDictionary();
|
||||
dict.Source = new System.Uri("ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/PullToRefreshListView/PullToRefreshListViewRefreshContainerTemplate.xaml");
|
||||
list._previousTemplateUsed = list.Template;
|
||||
list.Template = dict["PullToRefreshListViewRefreshContainerTemplate"] as ControlTemplate;
|
||||
}
|
||||
else if (!list.UseRefreshContainerWhenPossible &&
|
||||
e.OldValue is bool oldValue &&
|
||||
oldValue &&
|
||||
list._previousTemplateUsed != null)
|
||||
{
|
||||
list.Template = list._previousTemplateUsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the PullThreshold in pixels for when Refresh should be Requested. Default is 100
|
||||
/// </summary>
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/PullToRefreshListView/PullToRefreshListViewTemplate.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="local:PullToRefreshListView">
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="TabNavigation" Value="Once" />
|
||||
|
@ -33,54 +37,6 @@
|
|||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:PullToRefreshListView">
|
||||
<Border x:Name="Root"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid>
|
||||
<Border x:Name="RefreshIndicator"
|
||||
VerticalAlignment="Top">
|
||||
<Grid>
|
||||
<ContentPresenter x:Name="PullAndReleaseIndicatorContent"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="20" />
|
||||
<ContentPresenter Content="{TemplateBinding RefreshIndicatorContent}" />
|
||||
</Grid>
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform x:Name="RefreshIndicatorTransform" />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<ScrollViewer x:Name="ScrollViewer"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
|
||||
TabNavigation="{TemplateBinding TabNavigation}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
|
||||
<ItemsPresenter x:Name="ItemsPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Footer="{TemplateBinding Footer}"
|
||||
FooterTemplate="{TemplateBinding FooterTemplate}"
|
||||
FooterTransitions="{TemplateBinding FooterTransitions}"
|
||||
Header="{TemplateBinding Header}"
|
||||
HeaderTemplate="{TemplateBinding HeaderTemplate}"
|
||||
HeaderTransitions="{TemplateBinding HeaderTransitions}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template" Value="{StaticResource PullToRefreshListViewTemplate}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<ControlTemplate x:Key="PullToRefreshListViewRefreshContainerTemplate" TargetType="local:PullToRefreshListView">
|
||||
<Border x:Name="Root"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid>
|
||||
<RefreshContainer x:Name="RefreshContainer">
|
||||
<ScrollViewer x:Name="ScrollViewer"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
|
||||
TabNavigation="{TemplateBinding TabNavigation}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
|
||||
<ItemsPresenter x:Name="ItemsPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Footer="{TemplateBinding Footer}"
|
||||
FooterTemplate="{TemplateBinding FooterTemplate}"
|
||||
FooterTransitions="{TemplateBinding FooterTransitions}"
|
||||
Header="{TemplateBinding Header}"
|
||||
HeaderTemplate="{TemplateBinding HeaderTemplate}"
|
||||
HeaderTransitions="{TemplateBinding HeaderTransitions}" />
|
||||
</ScrollViewer>
|
||||
</RefreshContainer>
|
||||
</Grid>
|
||||
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</ResourceDictionary>
|
|
@ -0,0 +1,50 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<ControlTemplate x:Key="PullToRefreshListViewTemplate" TargetType="local:PullToRefreshListView">
|
||||
<Border x:Name="Root"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid>
|
||||
<Border x:Name="RefreshIndicator"
|
||||
VerticalAlignment="Top">
|
||||
<Grid>
|
||||
<ContentPresenter x:Name="PullAndReleaseIndicatorContent"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="20" />
|
||||
<ContentPresenter Content="{TemplateBinding RefreshIndicatorContent}" />
|
||||
</Grid>
|
||||
<Border.RenderTransform>
|
||||
<CompositeTransform x:Name="RefreshIndicatorTransform" />
|
||||
</Border.RenderTransform>
|
||||
</Border>
|
||||
<ScrollViewer x:Name="ScrollViewer"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
|
||||
TabNavigation="{TemplateBinding TabNavigation}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
|
||||
<ItemsPresenter x:Name="ItemsPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Footer="{TemplateBinding Footer}"
|
||||
FooterTemplate="{TemplateBinding FooterTemplate}"
|
||||
FooterTransitions="{TemplateBinding FooterTransitions}"
|
||||
Header="{TemplateBinding Header}"
|
||||
HeaderTemplate="{TemplateBinding HeaderTemplate}"
|
||||
HeaderTransitions="{TemplateBinding HeaderTransitions}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</ResourceDictionary>
|
|
@ -452,7 +452,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
_commandContainer = GetTemplateChild(PartCommandContainer) as Grid;
|
||||
if (_commandContainer != null)
|
||||
{
|
||||
_commandContainer.Background = LeftBackground as SolidColorBrush;
|
||||
_commandContainer.Background = LeftBackground as Brush;
|
||||
_commandContainer.Clip = new RectangleGeometry();
|
||||
_commandContainerTransform = new CompositeTransform();
|
||||
_commandContainer.Clip.Transform = _commandContainerTransform;
|
||||
|
@ -631,7 +631,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
// If swiping from left to right, show left command panel.
|
||||
_rightCommandPanel.Opacity = 0;
|
||||
|
||||
_commandContainer.Background = LeftBackground as SolidColorBrush;
|
||||
_commandContainer.Background = LeftBackground as Brush;
|
||||
_commandContainer.Opacity = 1;
|
||||
_leftCommandPanel.Opacity = 1;
|
||||
|
||||
|
@ -672,7 +672,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
// If swiping from right to left, show right command panel.
|
||||
_leftCommandPanel.Opacity = 0;
|
||||
|
||||
_commandContainer.Background = RightBackground as SolidColorBrush;
|
||||
_commandContainer.Background = RightBackground as Brush;
|
||||
_commandContainer.Opacity = 1;
|
||||
_rightCommandPanel.Opacity = 1;
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
<Style TargetType="controls:SlidableListItem">
|
||||
<Setter Property="LeftForeground" Value="White" />
|
||||
<Setter Property="RightForeground" Value="White" />
|
||||
<Setter Property="LeftBackground" Value="LightGray" />
|
||||
<Setter Property="RightBackground" Value="DarkGray" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="ActivationWidth" Value="80" />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<FileList>
|
||||
<File Reference="Microsoft.Toolkit.Uwp.UI.Controls.dll">
|
||||
<ToolboxItems VSCategory="UWP Community Toolkit" BlendCategory="UWP Community Toolkit">
|
||||
<ToolboxItems VSCategory="Windows Community Toolkit" BlendCategory="Windows Community Toolkit">
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.AdaptiveGridView" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.BladeItem" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.BladeView" />
|
||||
|
|