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:
Коммит
28577f08fc
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче