Merge pull request #2244 from Microsoft/petea/commentry

Fixed gaze feedback being hit testable. Started improviing comments. Made Loaded/Unloaded logic assert when stressed.
This commit is contained in:
Pete Ansell 2018-06-14 15:36:23 -07:00 коммит произвёл GitHub
Родитель d6493c7176 8977cb2c7a
Коммит 28577f08fc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 144 добавлений и 44 удалений

Просмотреть файл

@ -18,6 +18,7 @@ Popup^ GazeFeedbackPopupFactory::Get()
auto rectangle = ref new Rectangle();
rectangle->StrokeThickness = 2;
rectangle->IsHitTestVisible = false;
popup->Child = rectangle;
}

Просмотреть файл

@ -72,7 +72,7 @@ static void OnInteractionChanged(DependencyObject^ ob, DependencyPropertyChanged
{
auto element = safe_cast<FrameworkElement^>(ob);
auto interaction = safe_cast<Interaction>(args->NewValue);
GazePointerProxy::SetGazeInteraction(element, interaction);
GazePointerProxy::SetInteraction(element, interaction);
}
static void OnIsCursorVisibleChanged(DependencyObject^ ob, DependencyPropertyChangedEventArgs^ args)

Просмотреть файл

@ -11,6 +11,7 @@
#include "StateChangedEventArgs.h"
using namespace Platform;
using namespace Windows::UI::Xaml::Automation::Peers;
BEGIN_NAMESPACE_GAZE_INPUT
@ -42,7 +43,7 @@ GazePointer^ GazePointer::Instance::get()
return value;
}
void GazePointer::AddRoot(FrameworkElement^ element)
void GazePointer::AddRoot(Object^ element)
{
_roots->InsertAt(0, element);
@ -53,7 +54,7 @@ void GazePointer::AddRoot(FrameworkElement^ element)
}
}
void GazePointer::RemoveRoot(FrameworkElement^ element)
void GazePointer::RemoveRoot(Object^ element)
{
unsigned int index = 0;
if (_roots->IndexOf(element, &index))
@ -258,14 +259,46 @@ void GazePointer::SetElementStateDelay(UIElement ^element, PointerState relevant
_maxHistoryTime = 2 * max(dwellTime, repeatTime);
}
/// <summary>
/// Find the parent to inherit properties from.
/// </summary>
static UIElement^ GetInheritenceParent(UIElement^ child)
{
// The result value.
Object^ parent = nullptr;
// Get the automation peer...
auto peer = FrameworkElementAutomationPeer::FromElement(child);
if (peer != nullptr)
{
// ...if it exists, get the peer's parent...
auto peerParent = dynamic_cast<FrameworkElementAutomationPeer^>(peer->Navigate(AutomationNavigationDirection::Parent));
if (peerParent != nullptr)
{
// ...and if it has a parent, get the corresponding object.
parent = peerParent->Owner;
}
}
// If the above failed to find a parent...
if (parent == nullptr)
{
// ...use the visual parent.
parent = VisualTreeHelper::GetParent(child);
}
// Safely pun the value we found to a UIElement reference.
return dynamic_cast<UIElement^>(parent);
}
TimeSpan GazePointer::GetElementStateDelay(UIElement ^element, DependencyProperty^ property, TimeSpan defaultValue)
{
DependencyObject^ walker = element;
UIElement^ walker = element;
Object^ valueAtWalker = walker->GetValue(property);
while (GazeInput::UnsetTimeSpan.Equals(valueAtWalker) && walker != nullptr)
{
walker = VisualTreeHelper::GetParent(walker);
walker = GetInheritenceParent(walker);
if (walker != nullptr)
{
@ -346,7 +379,10 @@ GazeTargetItem^ GazePointer::GetHitTarget(Point gazePoint)
do
{
interaction = GazeInput::GetInteraction(element);
element = dynamic_cast<UIElement^>(VisualTreeHelper::GetParent(element));
if (interaction == Interaction::Inherited)
{
element = GetInheritenceParent(element);
}
} while (interaction == Interaction::Inherited && element != nullptr);
if (interaction == Interaction::Inherited)

Просмотреть файл

@ -98,11 +98,10 @@ internal:
internal:
static property GazePointer^ Instance { GazePointer^ get(); }
void OnPageUnloaded(Object^ sender, RoutedEventArgs^ args);
EventRegistrationToken _unloadedToken;
void AddRoot(FrameworkElement^ element);
void RemoveRoot(FrameworkElement^ element);
void AddRoot(Object^ element);
void RemoveRoot(Object^ element);
property bool IsDeviceAvailable { bool get() { return _deviceCount != 0; }}
@ -148,7 +147,7 @@ private:
void OnDeviceRemoved(GazeDeviceWatcherPreview^ sender, GazeDeviceWatcherRemovedPreviewEventArgs^ args);
private:
Vector<FrameworkElement^>^ _roots = ref new Vector<FrameworkElement^>();
Vector<Object^>^ _roots = ref new Vector<Object^>();
TimeSpan _eyesOffDelay;

Просмотреть файл

@ -4,36 +4,50 @@
BEGIN_NAMESPACE_GAZE_INPUT
DependencyProperty^ GazePointerProxy::GazePointerProxyProperty::get()
/// <summary>
/// The IsLoaded heuristic for testing whether a FrameworkElement is in the visual tree.
/// </summary>
static bool IsLoadedHeuristic(FrameworkElement^ element)
{
static auto value = DependencyProperty::RegisterAttached("_GazePointerProxy", GazePointerProxy::typeid, GazePointerProxy::typeid,
ref new PropertyMetadata(nullptr));
return value;
}
bool isLoaded;
GazePointerProxy::GazePointerProxy(FrameworkElement^ element)
: _element(element)
{
// element.Loaded has already happened if it is in the visual tree...
auto parent = VisualTreeHelper::GetParent(element);
if (parent != nullptr)
{
_isLoaded = true;
isLoaded = true;
}
// ...or...
else
{
// ...if the element is a dynamically created Popup that has been opened.
auto popup = dynamic_cast<Popup^>(element);
_isLoaded = popup != nullptr && popup->IsOpen;
isLoaded = popup != nullptr && popup->IsOpen;
}
element->Loaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnPageLoaded);
element->Unloaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnPageUnloaded);
return isLoaded;
}
void GazePointerProxy::SetGazeInteraction(FrameworkElement^ element, Interaction value)
DependencyProperty^ GazePointerProxy::GazePointerProxyProperty::get()
{
// The attached property registration.
static auto value = DependencyProperty::RegisterAttached("_GazePointerProxy", GazePointerProxy::typeid, GazePointerProxy::typeid,
ref new PropertyMetadata(nullptr));
return value;
}
GazePointerProxy::GazePointerProxy(FrameworkElement^ element)
{
_isLoaded = IsLoadedHeuristic(element);
// Start watching for the element to enter and leave the visual tree.
element->Loaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnLoaded);
element->Unloaded += ref new RoutedEventHandler(this, &GazePointerProxy::OnUnloaded);
}
void GazePointerProxy::SetInteraction(FrameworkElement^ element, Interaction value)
{
// Get or create a GazePointerProxy for element.
auto proxy = safe_cast<GazePointerProxy^>(element->GetValue(GazePointerProxyProperty));
if (proxy == nullptr)
{
@ -41,50 +55,66 @@ void GazePointerProxy::SetGazeInteraction(FrameworkElement^ element, Interaction
element->SetValue(GazePointerProxyProperty, proxy);
}
proxy->IsEnabled = value;
// Set the proxy's _isEnabled value.
proxy->SetIsEnabled(element, value == Interaction::Enabled);
}
void GazePointerProxy::IsEnabled::set(Interaction value)
void GazePointerProxy::SetIsEnabled(Object^ sender, bool value)
{
// If we have a new value...
if (_isEnabled != value)
{
// ...record the new value.
_isEnabled = value;
// If we are in the visual tree...
if (_isLoaded)
{
if (value == Interaction::Enabled)
// ...if we're being enabled...
if (value)
{
GazePointer::Instance->AddRoot(_element);
// ...count the element in...
GazePointer::Instance->AddRoot(sender);
}
else
{
GazePointer::Instance->RemoveRoot(_element);
// ...otherwise count the element out.
GazePointer::Instance->RemoveRoot(sender);
}
}
}
}
void GazePointerProxy::OnPageLoaded(Object^ sender, RoutedEventArgs^ args)
void GazePointerProxy::OnLoaded(Object^ sender, RoutedEventArgs^ args)
{
assert(!_isLoaded);
assert(IsLoadedHeuristic(safe_cast<FrameworkElement^>(sender)));
// Record that we are now loaded.
_isLoaded = true;
if (_isEnabled == Interaction::Enabled)
// If we were previously enabled...
if (_isEnabled)
{
GazePointer::Instance->AddRoot(_element);
// ...we can now be counted as actively enabled.
GazePointer::Instance->AddRoot(sender);
}
}
void GazePointerProxy::OnPageUnloaded(Object^ sender, RoutedEventArgs^ args)
void GazePointerProxy::OnUnloaded(Object^ sender, RoutedEventArgs^ args)
{
assert(_isLoaded);
assert(!IsLoadedHeuristic(safe_cast<FrameworkElement^>(sender)));
// Record that we have left the visual tree.
_isLoaded = false;
if (_isEnabled == Interaction::Enabled)
// If we are set as enabled...
if (_isEnabled)
{
GazePointer::Instance->RemoveRoot(_element);
// ...we no longer count as being actively enabled (because we have fallen out the visual tree).
GazePointer::Instance->RemoveRoot(sender);
}
}

Просмотреть файл

@ -4,35 +4,69 @@
BEGIN_NAMESPACE_GAZE_INPUT
/// <summary>
/// Helper class that helps track which UIElements in the visual tree are enabled.
///
/// The GazePointer is enabled when one or more UIElements in the visual tree have
/// their GazeInput.InteractionProperty value set to Enabled. Notice that there are
/// two conditions for enablement: that attached property is Enabled; that the UIElement
/// is in the visual tree.
/// </summary>
private ref class GazePointerProxy sealed
{
public:
/// <summary>
/// A private attached property for associating an instance of this class with the UIElement
/// to which it refers.
/// </summary>
static property DependencyProperty^ GazePointerProxyProperty { DependencyProperty^ get(); };
internal:
static void SetGazeInteraction(FrameworkElement^ element, Interaction value);
/// <summary>
/// Method called when the GazeInput.Interaction attached property is set to a new value.
/// </summary>
/// <param name="element">The element being set. May be null to indicate whole user interface.</params>
/// <param name="value">The interaction enablement value being set.</params>
static void SetInteraction(FrameworkElement^ element, Interaction value);
private:
/// <summary>
/// Constructor.
/// </summary>
/// <param name="element">The element proxy is attaching to.</params>
GazePointerProxy(FrameworkElement^ element);
property Interaction IsEnabled
{
Interaction get() { return _isEnabled; }
void set(Interaction value);
}
/// <summary>
/// Set the enablement of this proxy.
/// </summary>
/// <param name="sender">The object setting the enable value.</param>
/// <param name="value">The new enable value.</param>
void SetIsEnabled(Object^ sender, bool value);
void OnPageLoaded(Object^ sender, RoutedEventArgs^ args);
/// <summary>
/// The handler to be called when the corresponding element joins the visual tree.
/// </summary>
void OnLoaded(Object^ sender, RoutedEventArgs^ args);
void OnPageUnloaded(Object^ sender, RoutedEventArgs^ args);
/// <summary>
/// The handler to be called when the corresponding element leaves the visual tree.
/// </summary>
void OnUnloaded(Object^ sender, RoutedEventArgs^ args);
private:
FrameworkElement^ const _element;
/// <summary>
/// Indicator that the corresponding element is part of the visual tree.
/// </summary>
bool _isLoaded;
Interaction _isEnabled;
/// <summary>
/// Boolean representing whether gaze is enabled for the corresponding element and its subtree.
/// </summary>
bool _isEnabled;
};
END_NAMESPACE_GAZE_INPUT