mirror of
https://github.com/ZuneDev/MicrosoftIris.git
synced 2026-07-27 13:13:29 -07:00
1455 lines
52 KiB
C#
1455 lines
52 KiB
C#
// Decompiled with JetBrains decompiler
|
|
// Type: Microsoft.Iris.UI.UIClass
|
|
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
|
|
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
|
|
// Assembly location: C:\Program Files\Zune\UIX.dll
|
|
|
|
using Microsoft.Iris.Accessibility;
|
|
using Microsoft.Iris.Animations;
|
|
using Microsoft.Iris.Debug;
|
|
using Microsoft.Iris.Drawing;
|
|
using Microsoft.Iris.Input;
|
|
using Microsoft.Iris.InputHandlers;
|
|
using Microsoft.Iris.Layout;
|
|
using Microsoft.Iris.Library;
|
|
using Microsoft.Iris.Markup;
|
|
using Microsoft.Iris.Navigation;
|
|
using Microsoft.Iris.Render;
|
|
using Microsoft.Iris.RenderAPI.Drawing;
|
|
using Microsoft.Iris.Session;
|
|
using Microsoft.Iris.ViewItems;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Diagnostics;
|
|
|
|
namespace Microsoft.Iris.UI
|
|
{
|
|
public class UIClass :
|
|
Microsoft.Iris.Library.TreeNode,
|
|
ICookedInputSite,
|
|
IMarkupTypeBase,
|
|
IDisposableOwner,
|
|
INotifyObject,
|
|
ISchemaInfo
|
|
{
|
|
public const string UIStateReservedSymbolName = "UI";
|
|
private BitVector32 _bits;
|
|
private Host _ownerHost;
|
|
private int _uiIDFactory;
|
|
private ViewItem _rootItem;
|
|
private InputHandlerList _inputHandlers;
|
|
private UIClassTypeSchema _typeSchema;
|
|
private Dictionary<object, object> _storage;
|
|
private Vector<IDisposableObject> _disposables;
|
|
private MarkupListeners _listeners;
|
|
private NotifyService _notifier = new NotifyService();
|
|
private ScriptRunScheduler _scriptRunScheduler = new ScriptRunScheduler();
|
|
private static DeferredHandler s_executePendingScriptsHandler = new DeferredHandler(ExecutePendingScripts);
|
|
private static readonly FocusStateHandler s_updateMouseFocusStates = new FocusStateHandler(UpdateMouseFocusStates);
|
|
private static readonly FocusStateHandler s_updateKeyFocusStates = new FocusStateHandler(UpdateKeyFocusStates);
|
|
private static readonly DataCookie s_cursorProperty = DataCookie.ReserveSlot();
|
|
private static readonly DataCookie s_cursorOverrideProperty = DataCookie.ReserveSlot();
|
|
private static readonly DataCookie s_accProxyProperty = DataCookie.ReserveSlot();
|
|
private static readonly DataCookie s_pendingFocusRestoreProperty = DataCookie.ReserveSlot();
|
|
private static readonly DataCookie s_focusInterestTargetProperty = DataCookie.ReserveSlot();
|
|
private static readonly DataCookie s_focusInterestTargetMarginsProperty = DataCookie.ReserveSlot();
|
|
private static readonly EventCookie s_descendantKeyFocusChangedEvent = EventCookie.ReserveSlot();
|
|
private static readonly EventCookie s_descendantMouseFocusChangedEvent = EventCookie.ReserveSlot();
|
|
private EventContext _eventContext;
|
|
|
|
public UIClass(UIClassTypeSchema type)
|
|
{
|
|
_typeSchema = type;
|
|
_storage = new Dictionary<object, object>(type.TotalPropertiesAndLocalsCount);
|
|
_bits = new BitVector32();
|
|
SetBit(Bits.Flippable, true);
|
|
SetBit(Bits.CreateInterestOnFocus, true);
|
|
SetBit(Bits.KeyFocusOnMouseDown, true);
|
|
SetBit(Bits.AllowDoubleClicks, true);
|
|
SetBit(Bits.Enabled, true);
|
|
SetBit(Bits.ScriptEnabled, true);
|
|
}
|
|
|
|
public bool Initialized => GetBit(Bits.Initialized);
|
|
|
|
public void RegisterDisposable(IDisposableObject disposable)
|
|
{
|
|
if (disposable is ViewItem || disposable is InputHandler)
|
|
return;
|
|
if (_disposables == null)
|
|
_disposables = new Vector<IDisposableObject>();
|
|
_disposables.Add(disposable);
|
|
}
|
|
|
|
public bool UnregisterDisposable(ref IDisposableObject disposable)
|
|
{
|
|
if (!(disposable is ViewItem) && !(disposable is InputHandler) && _disposables != null)
|
|
{
|
|
int index = _disposables.IndexOf(disposable);
|
|
if (index != -1)
|
|
{
|
|
disposable = _disposables[index];
|
|
_disposables.RemoveAt(index);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
protected override void OnDispose()
|
|
{
|
|
_typeSchema.RunFinalEvaluates(this);
|
|
base.OnDispose();
|
|
if (Initialized)
|
|
AccessibleProxy.NotifyDestroyed(this);
|
|
SetBit(Bits.ScriptEnabled, false);
|
|
if (_listeners != null)
|
|
{
|
|
_listeners.Dispose(this);
|
|
_listeners = null;
|
|
}
|
|
_notifier.ClearListeners();
|
|
if (_rootItem != null)
|
|
{
|
|
DisposeViewItemTree(_rootItem);
|
|
_rootItem = null;
|
|
}
|
|
DisposeInputHandlers();
|
|
_storage.Clear();
|
|
if (_disposables != null)
|
|
{
|
|
for (int index = 0; index < _disposables.Count; ++index)
|
|
_disposables[index].Dispose(this);
|
|
}
|
|
RemoveEventHandlers(s_descendantMouseFocusChangedEvent);
|
|
RemoveEventHandlers(s_descendantKeyFocusChangedEvent);
|
|
}
|
|
|
|
private void DisposeInputHandlers()
|
|
{
|
|
if (_inputHandlers == null)
|
|
return;
|
|
foreach (DisposableObject inputHandler in _inputHandlers)
|
|
inputHandler.Dispose(this);
|
|
}
|
|
|
|
internal void DisposeViewItemTree(ViewItem item)
|
|
{
|
|
if (item.UI != this)
|
|
return;
|
|
ViewItem nextSibling;
|
|
for (ViewItem viewItem = (ViewItem)item.FirstChild; viewItem != null; viewItem = nextSibling)
|
|
{
|
|
nextSibling = (ViewItem)viewItem.NextSibling;
|
|
DisposeViewItemTree(viewItem);
|
|
}
|
|
item.Dispose(this);
|
|
}
|
|
|
|
internal void DestroyVisualTree(ViewItem subjectItem) => DestroyVisualTree(subjectItem, false);
|
|
|
|
internal void DestroyVisualTree(ViewItem subjectItem, bool markLayoutOutputDirty) => DestroyVisualTree(subjectItem, true, markLayoutOutputDirty);
|
|
|
|
private void DestroyVisualTree(
|
|
ViewItem subjectItem,
|
|
bool allowAnimations,
|
|
bool markLayoutOutputDirty)
|
|
{
|
|
OrphanedVisualCollection orphans = new OrphanedVisualCollection(UISession.AnimationManager);
|
|
DestroyVisualTreeWorker(subjectItem, orphans, allowAnimations, markLayoutOutputDirty);
|
|
orphans.OnLayoutApplyComplete();
|
|
}
|
|
|
|
private void DestroyVisualTreeWorker(
|
|
ViewItem subjectItem,
|
|
OrphanedVisualCollection orphans,
|
|
bool allowAnimations,
|
|
bool markLayoutOutputDirty)
|
|
{
|
|
subjectItem.RendererVisual.MouseOptions = MouseOptions.None;
|
|
foreach (ViewItem child in subjectItem.Children)
|
|
{
|
|
if (child.HasVisual)
|
|
DestroyVisualTreeWorker(child, orphans, allowAnimations, markLayoutOutputDirty);
|
|
}
|
|
if (markLayoutOutputDirty)
|
|
subjectItem.MarkLayoutOutputDirty(false);
|
|
if (allowAnimations)
|
|
AnimateDestroyedVisual(subjectItem, orphans);
|
|
if (subjectItem == subjectItem.UI.RootItem && subjectItem.UI.RootItem.HasVisual)
|
|
subjectItem.UI.RevalidateUsage(true, true);
|
|
subjectItem.OrphanVisuals(orphans);
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private static void DEBUG_VerifyVisualTreeDestroyed(ViewItem vi)
|
|
{
|
|
foreach (ViewItem child in vi.Children)
|
|
;
|
|
}
|
|
|
|
public int AllocateUIID(ViewItem item) => _uiIDFactory++;
|
|
|
|
public override bool IsRoot => Zone.RootUI == this;
|
|
|
|
public UIClass Parent => (UIClass)base.Parent;
|
|
|
|
protected override void OnZoneAttached()
|
|
{
|
|
base.OnZoneAttached();
|
|
RevalidateUsage(true, false);
|
|
bool enabledFlag = true;
|
|
UIClass parent = Parent;
|
|
if (parent != null)
|
|
enabledFlag = parent.FullyEnabled;
|
|
DeliverFullyEnabled(enabledFlag);
|
|
if (_inputHandlers != null)
|
|
{
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
inputHandler.OnZoneAttached();
|
|
}
|
|
AccessibleProxy.NotifyTreeChanged(this);
|
|
}
|
|
|
|
protected override void OnZoneDetached()
|
|
{
|
|
base.OnZoneDetached();
|
|
ChangeBit(Bits.AppFullyEnabled, false);
|
|
RevalidateUsage(true, true);
|
|
if (_inputHandlers != null)
|
|
{
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
inputHandler.OnZoneDetached();
|
|
}
|
|
if (_rootItem == null || !_rootItem.HasVisual)
|
|
return;
|
|
DestroyVisualTree(_rootItem);
|
|
}
|
|
|
|
protected override void OnChildrenChanged()
|
|
{
|
|
base.OnChildrenChanged();
|
|
if (!IsZoned)
|
|
return;
|
|
AccessibleProxy.NotifyTreeChanged(this);
|
|
}
|
|
|
|
public bool HasAccessibleProxy => GetBit(Bits.HasAccProxy);
|
|
|
|
protected virtual AccessibleProxy OnCreateAccessibleProxy(
|
|
UIClass ui,
|
|
Accessible data)
|
|
{
|
|
return new AccessibleProxy(ui, data);
|
|
}
|
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
public AccessibleProxy AccessibleProxy
|
|
{
|
|
get
|
|
{
|
|
AccessibleProxy accessibleProxy;
|
|
if (HasAccessibleProxy)
|
|
{
|
|
accessibleProxy = (AccessibleProxy)GetData(s_accProxyProperty);
|
|
}
|
|
else
|
|
{
|
|
Accessible data = null;
|
|
foreach (KeyValuePair<object, object> keyValuePair in _storage)
|
|
{
|
|
if (keyValuePair.Value is Accessible accessible)
|
|
{
|
|
data = accessible;
|
|
break;
|
|
}
|
|
}
|
|
if (data == null)
|
|
data = new Accessible();
|
|
accessibleProxy = OnCreateAccessibleProxy(this, data);
|
|
SetData(s_accProxyProperty, accessibleProxy);
|
|
SetBit(Bits.HasAccProxy, true);
|
|
}
|
|
return accessibleProxy;
|
|
}
|
|
}
|
|
|
|
internal static bool ShouldPlayAnimation(IAnimationProvider provider)
|
|
{
|
|
if (provider == null)
|
|
return false;
|
|
bool flag = true;
|
|
if (provider is NonIntensiveAnimation)
|
|
flag = false;
|
|
return AnimationSystem.Enabled || !flag;
|
|
}
|
|
|
|
private void AnimateNewVisual(
|
|
ViewItem vi,
|
|
Vector3 startPosVector,
|
|
Vector2 startSizeVector,
|
|
Vector3 startScaleVector,
|
|
Rotation startRotation)
|
|
{
|
|
vi.VisualPosition = startPosVector;
|
|
vi.VisualSize = startSizeVector;
|
|
vi.VisualScale = startScaleVector;
|
|
vi.VisualRotation = startRotation;
|
|
AnimateNewVisual(vi);
|
|
}
|
|
|
|
private void AnimateNewVisual(ViewItem vi)
|
|
{
|
|
vi.PlayShowAnimation();
|
|
if (!DirectKeyFocus)
|
|
return;
|
|
vi.PlayAnimation(AnimationEventType.GainFocus);
|
|
}
|
|
|
|
private void AnimateDestroyedVisual(ViewItem vi, OrphanedVisualCollection orphans) => vi.PlayHideAnimation(orphans);
|
|
|
|
private void AnimateVisual(
|
|
ViewItem vi,
|
|
Vector3 newPosVector,
|
|
Vector2 newSizeVector,
|
|
Vector3 newScaleVector,
|
|
Rotation newRotation)
|
|
{
|
|
Vector3 visualPosition = vi.VisualPosition;
|
|
Vector2 visualSize = vi.VisualSize;
|
|
Vector3 visualScale = vi.VisualScale;
|
|
Rotation visualRotation = vi.VisualRotation;
|
|
float visualAlpha = vi.VisualAlpha;
|
|
AnimationArgs args = new AnimationArgs(vi, visualPosition, visualSize, visualScale, visualRotation, visualAlpha, newPosVector, newSizeVector, newScaleVector, newRotation, visualAlpha);
|
|
if (visualPosition != newPosVector)
|
|
vi.ApplyAnimatableValue(AnimationEventType.Move, ref args);
|
|
if (visualSize != newSizeVector)
|
|
vi.ApplyAnimatableValue(AnimationEventType.Size, ref args);
|
|
if (visualScale != newScaleVector)
|
|
vi.ApplyAnimatableValue(AnimationEventType.Scale, ref args);
|
|
if (!(visualRotation != newRotation))
|
|
return;
|
|
vi.ApplyAnimatableValue(AnimationEventType.Rotate, ref args);
|
|
}
|
|
|
|
private void TryToPlayAnimationOnTree(ViewItem vi, AnimationEventType type)
|
|
{
|
|
if (!vi.HasVisual)
|
|
return;
|
|
vi.PlayAnimation(type);
|
|
foreach (ViewItem child in vi.Children)
|
|
{
|
|
if (child.UI == this)
|
|
TryToPlayAnimationOnTree(child, type);
|
|
}
|
|
}
|
|
|
|
internal object SaveKeyFocus()
|
|
{
|
|
if (KeyFocus)
|
|
{
|
|
UIClass keyFocusDescendant = KeyFocusDescendant;
|
|
if (keyFocusDescendant != null && keyFocusDescendant.RootItem != null)
|
|
return new UIClass.SavedFocusState(keyFocusDescendant.RootItem);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
internal void RestoreKeyFocus(object obj) => RestoreKeyFocus(obj, true);
|
|
|
|
internal void RestoreKeyFocus(object obj, bool allowDeferralFlag)
|
|
{
|
|
if (!(obj is UIClass.SavedFocusState focusState))
|
|
return;
|
|
ViewItem resultItem;
|
|
ViewItemID failedComponent;
|
|
switch (RootItem.FindChildFromPath(focusState.Path, out resultItem, out failedComponent))
|
|
{
|
|
case FindChildResult.Success:
|
|
if (resultItem == null)
|
|
break;
|
|
UIClass ui = resultItem.UI;
|
|
if (!HasDescendant(ui) || !ui.IsKeyFocusable())
|
|
break;
|
|
resultItem.NavigateInto(focusState.FocusIsDefault);
|
|
break;
|
|
case FindChildResult.PotentiallyFaultIn:
|
|
if (!allowDeferralFlag)
|
|
break;
|
|
new UIClass.DeferredKeyFocusRestoreHelper(this, focusState).FaultInChild(resultItem, failedComponent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private UIClass.DeferredKeyFocusRestoreHelper PendingFocusRestore
|
|
{
|
|
get => GetData(s_pendingFocusRestoreProperty) as UIClass.DeferredKeyFocusRestoreHelper;
|
|
set => SetData(s_pendingFocusRestoreProperty, value);
|
|
}
|
|
|
|
private static bool CheckHandled(InputInfo info, InputHandler inputHandler) => info.Handled;
|
|
|
|
public void SetAreaOfInterest(AreaOfInterestID id) => SetAreaOfInterest(id, RootItem, Inset.Zero);
|
|
|
|
public void SetAreaOfInterest(AreaOfInterestID id, ViewItem target, Inset margins) => target.SetAreaOfInterest(id, margins);
|
|
|
|
public void ClearAreaOfInterest(AreaOfInterestID id) => ClearAreaOfInterest(id, RootItem);
|
|
|
|
public void ClearAreaOfInterest(AreaOfInterestID id, ViewItem target) => target.ClearAreaOfInterest(id);
|
|
|
|
internal void UpdateMouseHandling(ViewItem childItem)
|
|
{
|
|
if (childItem == null)
|
|
{
|
|
childItem = RootItem;
|
|
if (childItem == null)
|
|
return;
|
|
}
|
|
if (!childItem.HasVisual)
|
|
return;
|
|
MouseOptions mouseOptions = MouseOptions.None;
|
|
if (!childItem.IsOffscreen && childItem.AllowMouseInput && (childItem != RootItem || IsInputBranchEnabled() && InputEnabled))
|
|
mouseOptions |= MouseOptions.Traversable;
|
|
if (childItem.MouseInteractive && MouseInteractive)
|
|
mouseOptions |= MouseOptions.Hittable;
|
|
if (childItem == RootItem)
|
|
mouseOptions |= MouseOptions.Returnable;
|
|
if (childItem.ClipMouse)
|
|
mouseOptions |= MouseOptions.ClipChildren;
|
|
childItem.RendererVisual.MouseOptions = mouseOptions;
|
|
}
|
|
|
|
internal void OnHostVisibilityChanged()
|
|
{
|
|
OnInputEnabledChanged();
|
|
AccessibleProxy.NotifyVisibilityChange(this, Visible);
|
|
}
|
|
|
|
public IList EnsureInputHandlerStorage()
|
|
{
|
|
if (_inputHandlers == null)
|
|
_inputHandlers = new InputHandlerList();
|
|
return _inputHandlers;
|
|
}
|
|
|
|
internal bool Visible => IsRoot || _ownerHost.HasVisual;
|
|
|
|
internal bool InputEnabled
|
|
{
|
|
get
|
|
{
|
|
if (IsRoot)
|
|
return true;
|
|
return Visible && _ownerHost.InputEnabled;
|
|
}
|
|
}
|
|
|
|
public bool Enabled
|
|
{
|
|
get => GetBit(Bits.Enabled);
|
|
set
|
|
{
|
|
if (!ChangeBit(Bits.Enabled, value))
|
|
return;
|
|
UpdateMouseHandling(null);
|
|
RevalidateUsage(true, !value);
|
|
FireNotification(NotificationID.Enabled);
|
|
if (!IsZoned || Parent != null && !Parent.FullyEnabled)
|
|
return;
|
|
NotifyFullyEnabledChange();
|
|
}
|
|
}
|
|
|
|
private bool HostEnabled => _ownerHost == null ? IsValid : _ownerHost.InputEnabled;
|
|
|
|
public bool FullyEnabled => GetBit(Bits.AppFullyEnabled);
|
|
|
|
public bool DirectMouseFocus => GetBit(Bits.DirectMouseFocus);
|
|
|
|
public bool MouseFocus => GetBit(Bits.MouseFocus);
|
|
|
|
public bool DirectKeyFocus => GetBit(Bits.DirectKeyFocus);
|
|
|
|
public bool KeyFocus => GetBit(Bits.KeyFocus);
|
|
|
|
public UIClass KeyFocusDescendant
|
|
{
|
|
get
|
|
{
|
|
//uiClass = (UIClass)null;
|
|
if (KeyFocus && UISession.InputManager.RawInstantaneousKeyFocus is UIClass uiClass)
|
|
{
|
|
if (uiClass.IsDisposed)
|
|
return null;
|
|
else
|
|
return uiClass;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public uint PaintOrder
|
|
{
|
|
get => _ownerHost.Layer;
|
|
set => _ownerHost.Layer = value;
|
|
}
|
|
|
|
public bool CreateInterestOnFocus
|
|
{
|
|
get => GetBit(Bits.CreateInterestOnFocus);
|
|
set
|
|
{
|
|
if (!ChangeBit(Bits.CreateInterestOnFocus, value))
|
|
return;
|
|
FireNotification(NotificationID.CreateInterestOnFocus);
|
|
}
|
|
}
|
|
|
|
public ViewItem FocusInterestTarget
|
|
{
|
|
get => (ViewItem)GetData(s_focusInterestTargetProperty);
|
|
set
|
|
{
|
|
if (FocusInterestTarget == value)
|
|
return;
|
|
SetData(s_focusInterestTargetProperty, value);
|
|
FireNotification(NotificationID.FocusInterestTarget);
|
|
}
|
|
}
|
|
|
|
public Inset FocusInterestTargetMargins
|
|
{
|
|
get
|
|
{
|
|
object data = GetData(s_focusInterestTargetMarginsProperty);
|
|
return data != null ? (Inset)data : Inset.Zero;
|
|
}
|
|
set
|
|
{
|
|
if (!(FocusInterestTargetMargins != value))
|
|
return;
|
|
SetData(s_focusInterestTargetMarginsProperty, value);
|
|
FireNotification(NotificationID.FocusInterestTargetMargins);
|
|
}
|
|
}
|
|
|
|
public CursorID Cursor
|
|
{
|
|
get
|
|
{
|
|
CursorID cursorId = CursorID.NotSpecified;
|
|
object data = GetData(s_cursorProperty);
|
|
if (data != null)
|
|
cursorId = (CursorID)data;
|
|
return cursorId;
|
|
}
|
|
set
|
|
{
|
|
if (Cursor == value)
|
|
return;
|
|
SetData(s_cursorProperty, value);
|
|
FireNotification(NotificationID.Cursor);
|
|
if (!IsZoned || OverrideCursor != CursorID.NotSpecified)
|
|
return;
|
|
Zone.UpdateCursor(this);
|
|
}
|
|
}
|
|
|
|
private CursorID OverrideCursor
|
|
{
|
|
get
|
|
{
|
|
CursorID cursorId = CursorID.NotSpecified;
|
|
object data = GetData(s_cursorOverrideProperty);
|
|
if (data != null)
|
|
cursorId = (CursorID)data;
|
|
return cursorId;
|
|
}
|
|
set
|
|
{
|
|
CursorID overrideCursor = OverrideCursor;
|
|
if (overrideCursor == value)
|
|
return;
|
|
SetData(s_cursorOverrideProperty, value);
|
|
if (!IsZoned || overrideCursor == CursorID.NotSpecified && value == Cursor)
|
|
return;
|
|
Zone.UpdateCursor(this);
|
|
}
|
|
}
|
|
|
|
public CursorID EffectiveCursor
|
|
{
|
|
get
|
|
{
|
|
CursorID cursorId = OverrideCursor;
|
|
if (cursorId == CursorID.NotSpecified)
|
|
cursorId = Cursor;
|
|
return cursorId;
|
|
}
|
|
}
|
|
|
|
public void UpdateCursor()
|
|
{
|
|
CursorID cursorId = CursorID.NotSpecified;
|
|
if (_inputHandlers != null)
|
|
{
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
{
|
|
if (inputHandler.Enabled)
|
|
{
|
|
cursorId = inputHandler.GetCursor();
|
|
if (cursorId != CursorID.NotSpecified)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
OverrideCursor = cursorId;
|
|
}
|
|
|
|
public bool KeyFocusOnMouseEnter
|
|
{
|
|
get => GetBit(Bits.KeyFocusOnMouseEnter);
|
|
set => SetBit(Bits.KeyFocusOnMouseEnter, value);
|
|
}
|
|
|
|
public bool KeyFocusOnMouseDown
|
|
{
|
|
get => GetBit(Bits.KeyFocusOnMouseDown);
|
|
set => SetBit(Bits.KeyFocusOnMouseDown, value);
|
|
}
|
|
|
|
public bool AllowDoubleClicks
|
|
{
|
|
get => GetBit(Bits.AllowDoubleClicks);
|
|
set => SetBit(Bits.AllowDoubleClicks, value);
|
|
}
|
|
|
|
internal void NotifyFullyEnabledChange() => Zone.ScheduleFullyEnabledChangeNotifications();
|
|
|
|
internal void DeliverFullyEnabled(bool enabledFlag)
|
|
{
|
|
if (!Enabled || !HostEnabled)
|
|
enabledFlag = false;
|
|
if (ChangeBit(Bits.AppFullyEnabled, enabledFlag))
|
|
FireNotification(NotificationID.FullyEnabled);
|
|
foreach (UIClass child in Children)
|
|
child.DeliverFullyEnabled(enabledFlag);
|
|
}
|
|
|
|
public void EnableRawInput(bool enableFlag)
|
|
{
|
|
if (!ChangeBit(Bits.RawInputDisabled, !enableFlag))
|
|
return;
|
|
UpdateMouseHandling(null);
|
|
RevalidateUsage(true, !enableFlag);
|
|
}
|
|
|
|
public bool IsInputBranchEnabled() => GetBit(Bits.Enabled) && !GetBit(Bits.RawInputDisabled);
|
|
|
|
public bool IsEligibleForInput() => IsEligibleForInput(out UIClass _);
|
|
|
|
public bool IsEligibleForInput(out UIClass failurePoint)
|
|
{
|
|
failurePoint = null;
|
|
UIClass uiClass = this;
|
|
while (true)
|
|
{
|
|
failurePoint = uiClass;
|
|
if (uiClass.IsInputBranchEnabled() && uiClass.InputEnabled)
|
|
{
|
|
UIClass parent = uiClass.Parent;
|
|
if (parent != null)
|
|
uiClass = parent;
|
|
else
|
|
goto label_5;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return false;
|
|
label_5:
|
|
failurePoint = null;
|
|
return IsZoned;
|
|
}
|
|
|
|
public bool MouseInteractive
|
|
{
|
|
get => GetBit(Bits.MouseInteractive);
|
|
set => SetMouseInteractive(value, false);
|
|
}
|
|
|
|
public void SetMouseInteractive(bool value, bool fromScript)
|
|
{
|
|
if (fromScript)
|
|
SetBit(Bits.MouseInteractiveSet, true);
|
|
if (!fromScript && GetBit(Bits.MouseInteractiveSet) || !ChangeBit(Bits.MouseInteractive, value))
|
|
return;
|
|
if (value && _rootItem != null && !HasMouseInteractiveContent())
|
|
_rootItem.MouseInteractive = true;
|
|
UpdateMouseHandling(null);
|
|
RevalidateUsage(false, !value);
|
|
FireNotification(NotificationID.MouseInteractive);
|
|
if (!DebugOutlines.Enabled)
|
|
return;
|
|
DebugOutlines.NotifyInteractivityChange(Host);
|
|
}
|
|
|
|
public bool IsMouseFocusable() => MouseInteractive && IsEligibleForInput();
|
|
|
|
public bool KeyInteractive
|
|
{
|
|
get => GetBit(Bits.KeyInteractive);
|
|
set
|
|
{
|
|
if (!ChangeBit(Bits.KeyInteractive, value))
|
|
return;
|
|
RevalidateUsage(false, !value);
|
|
FireNotification(NotificationID.KeyInteractive);
|
|
if (!DebugOutlines.Enabled)
|
|
return;
|
|
DebugOutlines.NotifyInteractivityChange(Host);
|
|
}
|
|
}
|
|
|
|
public event SessionInputHandler SessionInput
|
|
{
|
|
add => Zone.SessionInput += value;
|
|
remove => Zone.SessionInput -= value;
|
|
}
|
|
|
|
public bool HostActivated => Zone.Form.ActivationState;
|
|
|
|
public bool IsKeyFocusable() => KeyInteractive && IsEligibleForInput();
|
|
|
|
public UIClass FindKeyFocusableAncestor()
|
|
{
|
|
if (!IsValid)
|
|
return null;
|
|
UIClass uiClass;
|
|
for (uiClass = this; uiClass != null; uiClass = uiClass.Parent)
|
|
{
|
|
UIClass failurePoint = null;
|
|
if (uiClass.KeyInteractive && uiClass.IsEligibleForInput(out failurePoint))
|
|
return uiClass;
|
|
if (failurePoint != null)
|
|
uiClass = failurePoint;
|
|
}
|
|
return uiClass;
|
|
}
|
|
|
|
public void NavigateInto() => NavigateInto(false);
|
|
|
|
public void NavigateInto(bool isDefault) => RequestKeyFocus(isDefault ? KeyFocusReason.Default : KeyFocusReason.Other);
|
|
|
|
public void RequestKeyFocus() => RequestKeyFocus(KeyFocusReason.Other);
|
|
|
|
private void RequestKeyFocus(KeyFocusReason keyfocusReason)
|
|
{
|
|
if (!IsKeyFocusable())
|
|
return;
|
|
UISession.InputManager.Queue.RequestKeyFocus(this, keyfocusReason);
|
|
}
|
|
|
|
public bool IsValid => !IsDisposed;
|
|
|
|
public static FocusStateHandler GetFocusUpdateProc(InputDeviceType focusType)
|
|
{
|
|
switch (focusType)
|
|
{
|
|
case InputDeviceType.Keyboard:
|
|
return s_updateKeyFocusStates;
|
|
case InputDeviceType.Mouse:
|
|
return s_updateMouseFocusStates;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void RevalidateUsage(bool recursiveFlag, bool knownDisabledFlag)
|
|
{
|
|
if (!IsZoned)
|
|
return;
|
|
UISession.InputManager.RevalidateInputSiteUsage(this, recursiveFlag, knownDisabledFlag);
|
|
}
|
|
|
|
internal void OnInputEnabledChanged()
|
|
{
|
|
if (!IsZoned)
|
|
return;
|
|
RevalidateUsage(true, !InputEnabled);
|
|
UpdateMouseHandling(null);
|
|
if (!Enabled || Parent != null && !Parent.FullyEnabled)
|
|
return;
|
|
NotifyFullyEnabledChange();
|
|
}
|
|
|
|
IRawInputSite ICookedInputSite.RawInputSource
|
|
{
|
|
get
|
|
{
|
|
IRawInputSite rawInputSite = null;
|
|
if (_rootItem != null)
|
|
rawInputSite = _rootItem.RendererVisual;
|
|
return rawInputSite;
|
|
}
|
|
}
|
|
|
|
public void DeliverInput(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (stage != EventRouteStages.Unhandled)
|
|
{
|
|
switch (info.EventType)
|
|
{
|
|
case InputEventType.CommandDown:
|
|
case InputEventType.CommandUp:
|
|
case InputEventType.KeyDown:
|
|
case InputEventType.KeyUp:
|
|
case InputEventType.KeyCharacter:
|
|
case InputEventType.MouseMove:
|
|
case InputEventType.MousePrimaryUp:
|
|
case InputEventType.MouseSecondaryUp:
|
|
case InputEventType.MouseWheel:
|
|
case InputEventType.DragEnter:
|
|
case InputEventType.DragOver:
|
|
case InputEventType.DragLeave:
|
|
case InputEventType.DragDropped:
|
|
case InputEventType.DragComplete:
|
|
DeliverInputEvent(info, stage);
|
|
break;
|
|
case InputEventType.GainKeyFocus:
|
|
DeliverGainKeyFocus(info, stage);
|
|
break;
|
|
case InputEventType.LoseKeyFocus:
|
|
DeliverLoseKeyFocus(info, stage);
|
|
break;
|
|
case InputEventType.GainMouseFocus:
|
|
DeliverGainMouseFocus(info, stage);
|
|
break;
|
|
case InputEventType.LoseMouseFocus:
|
|
DeliverFocusChange(info, stage);
|
|
break;
|
|
case InputEventType.MousePrimaryDown:
|
|
case InputEventType.MouseSecondaryDown:
|
|
case InputEventType.MouseDoubleClick:
|
|
DeliverMouseButtonDown(info, stage);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(info is KeyStateInfo keyStateInfo) || keyStateInfo.Action != KeyAction.Down || (keyStateInfo.SystemKey || !KeyNavigate(keyStateInfo.Key, keyStateInfo.Modifiers)))
|
|
return;
|
|
keyStateInfo.MarkHandled();
|
|
}
|
|
}
|
|
|
|
private void DeliverInputEvent(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (info.Handled)
|
|
return;
|
|
ForwardEventToInputHandlers(info, stage);
|
|
}
|
|
|
|
private void ForwardEventToInputHandlers(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (_inputHandlers == null)
|
|
return;
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
{
|
|
inputHandler.DeliverInput(this, info, stage);
|
|
if (CheckHandled(info, inputHandler))
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void DeliverFocusChange(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (stage == EventRouteStages.Routed)
|
|
{
|
|
int num = info.Handled ? 1 : 0;
|
|
DeliverCodeNotifications(info);
|
|
}
|
|
ForwardEventToInputHandlers(info, stage);
|
|
}
|
|
|
|
private void DeliverGainKeyFocus(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (stage == EventRouteStages.Direct)
|
|
OnGainKeyFocus();
|
|
DeliverFocusChange(info, stage);
|
|
}
|
|
|
|
private void DeliverLoseKeyFocus(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (stage == EventRouteStages.Direct)
|
|
OnLoseKeyFocus();
|
|
DeliverFocusChange(info, stage);
|
|
}
|
|
|
|
private void DeliverGainMouseFocus(InputInfo info, EventRouteStages stage)
|
|
{
|
|
if (stage == EventRouteStages.Direct && GetBit(Bits.KeyFocusOnMouseEnter))
|
|
RequestKeyFocus(KeyFocusReason.MouseEnter);
|
|
DeliverFocusChange(info, stage);
|
|
}
|
|
|
|
private void DeliverMouseButtonDown(InputInfo args, EventRouteStages stage)
|
|
{
|
|
if (stage == EventRouteStages.Direct && GetBit(Bits.KeyFocusOnMouseDown))
|
|
RequestKeyFocus(KeyFocusReason.MouseDown);
|
|
DeliverInputEvent(args, stage);
|
|
}
|
|
|
|
private void OnGainKeyFocus()
|
|
{
|
|
NavigationServices.SeedDefaultFocus(RootItem);
|
|
if (CreateInterestOnFocus)
|
|
{
|
|
SetAreaOfInterest(AreaOfInterestID.Focus);
|
|
ViewItem focusInterestTarget = FocusInterestTarget;
|
|
if (focusInterestTarget != null && focusInterestTarget != RootItem)
|
|
SetAreaOfInterest(AreaOfInterestID.FocusOverride, focusInterestTarget, FocusInterestTargetMargins);
|
|
}
|
|
TryToPlayAnimationOnTree(RootItem, AnimationEventType.GainFocus);
|
|
}
|
|
|
|
private void OnLoseKeyFocus()
|
|
{
|
|
if (RootItem == null)
|
|
return;
|
|
if (CreateInterestOnFocus)
|
|
{
|
|
ClearAreaOfInterest(AreaOfInterestID.Focus);
|
|
ViewItem focusInterestTarget = FocusInterestTarget;
|
|
if (focusInterestTarget != null && focusInterestTarget != RootItem)
|
|
ClearAreaOfInterest(AreaOfInterestID.FocusOverride, focusInterestTarget);
|
|
}
|
|
TryToPlayAnimationOnTree(RootItem, AnimationEventType.LoseFocus);
|
|
}
|
|
|
|
private void OnGainDeepKeyFocus()
|
|
{
|
|
if (_inputHandlers == null)
|
|
return;
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
inputHandler.NotifyGainDeepKeyFocus();
|
|
}
|
|
|
|
private void OnLoseDeepKeyFocus()
|
|
{
|
|
if (_inputHandlers == null)
|
|
return;
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
inputHandler.NotifyLoseDeepKeyFocus();
|
|
}
|
|
|
|
private static void UpdateKeyFocusStates(
|
|
UIClass recipient,
|
|
bool deepFocusFlag,
|
|
bool directFocusFlag)
|
|
{
|
|
if (recipient.ChangeBit(Bits.KeyFocus, deepFocusFlag))
|
|
{
|
|
recipient.FireNotification(NotificationID.KeyFocus);
|
|
if (deepFocusFlag)
|
|
recipient.OnGainDeepKeyFocus();
|
|
else
|
|
recipient.OnLoseDeepKeyFocus();
|
|
}
|
|
if (!recipient.ChangeBit(Bits.DirectKeyFocus, directFocusFlag))
|
|
return;
|
|
recipient.FireNotification(NotificationID.DirectKeyFocus);
|
|
if (DebugOutlines.Enabled)
|
|
DebugOutlines.NotifyInteractivityChange(recipient.Host);
|
|
if (!directFocusFlag)
|
|
return;
|
|
AccessibleProxy.NotifyFocus(recipient);
|
|
}
|
|
|
|
public event InputEventHandler DescendentKeyFocusChange
|
|
{
|
|
add => AddEventHandler(s_descendantKeyFocusChangedEvent, value);
|
|
remove => RemoveEventHandler(s_descendantKeyFocusChangedEvent, value);
|
|
}
|
|
|
|
private static void UpdateMouseFocusStates(
|
|
UIClass recipient,
|
|
bool deepFocusFlag,
|
|
bool directFocusFlag)
|
|
{
|
|
if (recipient.ChangeBit(Bits.MouseFocus, deepFocusFlag))
|
|
recipient.FireNotification(NotificationID.MouseFocus);
|
|
if (!recipient.ChangeBit(Bits.DirectMouseFocus, directFocusFlag))
|
|
return;
|
|
recipient.FireNotification(NotificationID.DirectMouseFocus);
|
|
if (!DebugOutlines.Enabled)
|
|
return;
|
|
DebugOutlines.NotifyInteractivityChange(recipient.Host);
|
|
}
|
|
|
|
public event InputEventHandler DescendentMouseFocusChange
|
|
{
|
|
add => AddEventHandler(s_descendantMouseFocusChangedEvent, value);
|
|
remove => RemoveEventHandler(s_descendantMouseFocusChangedEvent, value);
|
|
}
|
|
|
|
private void DeliverCodeNotifications(InputInfo info)
|
|
{
|
|
EventCookie focusChangedEvent = EventCookie.NULL;
|
|
switch (info.EventType)
|
|
{
|
|
case InputEventType.GainKeyFocus:
|
|
case InputEventType.LoseKeyFocus:
|
|
focusChangedEvent = s_descendantKeyFocusChangedEvent;
|
|
break;
|
|
case InputEventType.GainMouseFocus:
|
|
case InputEventType.LoseMouseFocus:
|
|
focusChangedEvent = s_descendantMouseFocusChangedEvent;
|
|
break;
|
|
}
|
|
if (!(focusChangedEvent != EventCookie.NULL) || !(GetEventHandler(focusChangedEvent) is InputEventHandler eventHandler))
|
|
return;
|
|
eventHandler(this, info);
|
|
}
|
|
|
|
public void ApplyLayoutOutput(
|
|
ViewItem subjectItem,
|
|
bool parentFullyVisible,
|
|
bool parentOffscreen,
|
|
bool allowAnimations,
|
|
out bool visibilityChange,
|
|
out bool offscreenChange)
|
|
{
|
|
bool flag1 = subjectItem.LayoutVisible && parentFullyVisible;
|
|
bool hasVisual = subjectItem.HasVisual;
|
|
if (!flag1 && !hasVisual)
|
|
{
|
|
visibilityChange = false;
|
|
offscreenChange = false;
|
|
}
|
|
else
|
|
{
|
|
visibilityChange = flag1 != hasVisual;
|
|
bool flag2 = visibilityChange && hasVisual;
|
|
bool flag3 = visibilityChange && !hasVisual;
|
|
if (flag3)
|
|
subjectItem.CreateVisual(Zone.Session.RenderSession);
|
|
Rectangle layoutBounds = subjectItem.LayoutBounds;
|
|
Vector2 vector2 = new Vector2(layoutBounds.Width, layoutBounds.Height);
|
|
Vector3 vector3_1 = new Vector3(layoutBounds.X, layoutBounds.Y, 0.0f);
|
|
Vector3 vector3_2 = subjectItem.LayoutScale * subjectItem.Scale;
|
|
Rotation layoutRotation = subjectItem.LayoutRotation;
|
|
Vector3 oldScaleVector;
|
|
if (!hasVisual)
|
|
{
|
|
AnimateNewVisual(subjectItem, vector3_1, vector2, vector3_2, layoutRotation);
|
|
oldScaleVector = vector3_2;
|
|
}
|
|
else
|
|
{
|
|
Vector2 visualSize = subjectItem.VisualSize;
|
|
oldScaleVector = subjectItem.VisualScale;
|
|
}
|
|
bool flag4 = subjectItem.LayoutOffscreen || parentOffscreen;
|
|
offscreenChange = flag4 != subjectItem.IsOffscreen;
|
|
if (offscreenChange)
|
|
subjectItem.IsOffscreen = flag4;
|
|
if (offscreenChange || flag3)
|
|
UpdateMouseHandling(subjectItem);
|
|
bool flag5 = hasVisual;
|
|
if (flag5 && flag2)
|
|
flag5 = false;
|
|
if (flag5)
|
|
AnimateVisual(subjectItem, vector3_1, vector2, vector3_2, layoutRotation);
|
|
if (flag2)
|
|
{
|
|
if (subjectItem.IsRoot)
|
|
return;
|
|
DestroyVisualTree(subjectItem, allowAnimations, false);
|
|
}
|
|
else
|
|
{
|
|
if (!(oldScaleVector != vector3_2) && hasVisual)
|
|
return;
|
|
subjectItem.OnScaleChange(oldScaleVector, vector3_2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool FindNextFocusablePeer(
|
|
Direction searchDirection,
|
|
RectangleF startRectangleF,
|
|
out UIClass resultUI)
|
|
{
|
|
if (_rootItem != null)
|
|
return FindNextFocusablePeerWorker(_rootItem, searchDirection, startRectangleF, out resultUI);
|
|
resultUI = null;
|
|
return false;
|
|
}
|
|
|
|
private bool FindNextFocusablePeerWorker(
|
|
ViewItem startItem,
|
|
Direction searchDirection,
|
|
RectangleF startRectangleF,
|
|
out UIClass resultUI)
|
|
{
|
|
INavigationSite resultSite;
|
|
bool nextPeer = NavigationServices.FindNextPeer(startItem, searchDirection, startRectangleF, out resultSite);
|
|
resultUI = null;
|
|
if (resultSite != null && resultSite is ViewItem viewItem)
|
|
resultUI = viewItem.UI;
|
|
return nextPeer;
|
|
}
|
|
|
|
internal bool FindNextFocusableWithin(
|
|
Direction searchDirection,
|
|
RectangleF startRectangleF,
|
|
out UIClass resultUI)
|
|
{
|
|
INavigationSite resultSite;
|
|
bool nextWithin = NavigationServices.FindNextWithin(_rootItem, searchDirection, startRectangleF, out resultSite);
|
|
resultUI = null;
|
|
if (resultSite != null && resultSite is ViewItem viewItem)
|
|
resultUI = viewItem.UI;
|
|
return nextWithin;
|
|
}
|
|
|
|
internal NavigationClass GetNavigability(ViewItem subjectItem)
|
|
{
|
|
NavigationClass navigationClass = NavigationClass.None;
|
|
if (subjectItem == _rootItem && IsKeyFocusable())
|
|
navigationClass = NavigationClass.Direct;
|
|
return navigationClass;
|
|
}
|
|
|
|
public void NotifyNavigationDestination(KeyFocusReason keyFocusReason) => RequestKeyFocus(keyFocusReason);
|
|
|
|
public bool InboundKeyNavigation(
|
|
Direction searchDirection,
|
|
RectangleF startRectangleF,
|
|
bool defaultFlag)
|
|
{
|
|
UIClass resultUI;
|
|
if (!FindNextFocusableWithin(searchDirection, startRectangleF, out resultUI) || resultUI == null)
|
|
return false;
|
|
resultUI.NotifyNavigationDestination(defaultFlag ? KeyFocusReason.Default : KeyFocusReason.Other);
|
|
return true;
|
|
}
|
|
|
|
public bool NavigateDirection(Direction direction, KeyFocusReason reason)
|
|
{
|
|
UIClass resultUI;
|
|
if (!FindNextFocusablePeer(direction, RectangleF.Zero, out resultUI) || resultUI == null)
|
|
return false;
|
|
UIClass uiClass = this;
|
|
resultUI.NotifyNavigationDestination(reason);
|
|
return true;
|
|
}
|
|
|
|
private bool KeyNavigate(Keys key, InputModifiers modifiers)
|
|
{
|
|
InputHandlerModifiers modifiers1 = InputHandler.GetModifiers(modifiers);
|
|
KeyHandler.TranslateKey(ref key, ref modifiers1);
|
|
KeyFocusReason reason = KeyFocusReason.Directional;
|
|
Direction direction;
|
|
switch (key)
|
|
{
|
|
case Keys.Tab:
|
|
direction = (modifiers1 & InputHandlerModifiers.Shift) != InputHandlerModifiers.Shift ? Direction.Next : Direction.Previous;
|
|
reason = KeyFocusReason.Tab;
|
|
break;
|
|
case Keys.Left:
|
|
direction = Direction.West;
|
|
break;
|
|
case Keys.Up:
|
|
direction = Direction.North;
|
|
break;
|
|
case Keys.Right:
|
|
direction = Direction.East;
|
|
break;
|
|
case Keys.Down:
|
|
direction = Direction.South;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return NavigateDirection(direction, reason);
|
|
}
|
|
|
|
public TypeSchema TypeSchema => _typeSchema;
|
|
|
|
public void NotifyInitialized()
|
|
{
|
|
if (_inputHandlers != null)
|
|
{
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
inputHandler.NotifyUIInitialized();
|
|
}
|
|
if (_rootItem != null)
|
|
{
|
|
if (MouseInteractive)
|
|
{
|
|
if (!HasMouseInteractiveContent())
|
|
_rootItem.MouseInteractive = true;
|
|
}
|
|
else if (HasMouseInteractiveContent())
|
|
MouseInteractive = true;
|
|
}
|
|
SetBit(Bits.Initialized, true);
|
|
AccessibleProxy.NotifyCreated(this);
|
|
}
|
|
|
|
public object ReadSymbol(SymbolReference symbolRef)
|
|
{
|
|
object obj = null;
|
|
switch (symbolRef.Origin)
|
|
{
|
|
case SymbolOrigin.Properties:
|
|
case SymbolOrigin.Locals:
|
|
obj = _storage[symbolRef.Symbol];
|
|
break;
|
|
case SymbolOrigin.Input:
|
|
if (_inputHandlers != null)
|
|
{
|
|
foreach (InputHandler inputHandler in _inputHandlers)
|
|
{
|
|
if (inputHandler.Name != null && ReferenceEquals(inputHandler.Name, symbolRef.Symbol))
|
|
{
|
|
obj = inputHandler;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case SymbolOrigin.Content:
|
|
obj = FindViewItemByName(RootItem, symbolRef.Symbol);
|
|
break;
|
|
case SymbolOrigin.Reserved:
|
|
if (symbolRef.Symbol == "UI")
|
|
{
|
|
obj = this;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
public ViewItem FindViewItemByName(ViewItem item, string name)
|
|
{
|
|
if (ReferenceEquals(item.Name, name))
|
|
return item;
|
|
if (item.HideNamedChildren)
|
|
return null;
|
|
foreach (ViewItem child in item.Children)
|
|
{
|
|
if (child.UI == this)
|
|
{
|
|
ViewItem viewItemByName = FindViewItemByName(child, name);
|
|
if (viewItemByName != null)
|
|
return viewItemByName;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void WriteSymbol(SymbolReference symbolRef, object value)
|
|
{
|
|
string symbol = symbolRef.Symbol;
|
|
if (_storage.ContainsKey(symbol) && Utility.IsEqual(_storage[symbol], value))
|
|
return;
|
|
_storage[symbol] = value;
|
|
if (symbolRef.Origin != SymbolOrigin.Properties && symbolRef.Origin != SymbolOrigin.Locals)
|
|
return;
|
|
bool surfaceViaHost = symbolRef.Origin == SymbolOrigin.Properties;
|
|
FireNotification(symbol, surfaceViaHost);
|
|
}
|
|
|
|
public object GetProperty(string name) => _storage[name];
|
|
|
|
public void SetProperty(string name, object value)
|
|
{
|
|
if (_storage.ContainsKey(name) && Utility.IsEqual(_storage[name], value))
|
|
return;
|
|
_storage[name] = value;
|
|
FireNotification(name, true);
|
|
}
|
|
|
|
public Dictionary<object, object> Storage => _storage;
|
|
|
|
public MarkupListeners Listeners
|
|
{
|
|
get => _listeners;
|
|
set => _listeners = value;
|
|
}
|
|
|
|
public void ScheduleScriptRun(uint scriptId, bool ignoreErrors)
|
|
{
|
|
if (!_scriptRunScheduler.Pending)
|
|
DeferredCall.Post(DispatchPriority.Script, s_executePendingScriptsHandler, this);
|
|
_scriptRunScheduler.ScheduleRun(scriptId, ignoreErrors);
|
|
}
|
|
|
|
private static void ExecutePendingScripts(object args)
|
|
{
|
|
UIClass uiClass = (UIClass)args;
|
|
uiClass._scriptRunScheduler.Execute(uiClass);
|
|
}
|
|
|
|
public object RunScript(uint scriptId, bool ignoreErrors, ParameterContext parameterContext) => _typeSchema.Run(this, scriptId, ignoreErrors, parameterContext);
|
|
|
|
public void NotifyScriptErrors()
|
|
{
|
|
SetBit(Bits.ScriptEnabled, false);
|
|
_ownerHost.NotifyChildUIScriptErrors();
|
|
ErrorManager.ReportWarning("Script runtime failure: Scripting has been disabled for '{0}' due to runtime scripting errors", _typeSchema.Name);
|
|
}
|
|
|
|
public bool ScriptEnabled => GetBit(Bits.ScriptEnabled);
|
|
|
|
protected void FireNotification(string id) => FireNotification(id, false);
|
|
|
|
private void FireNotification(string id, bool surfaceViaHost)
|
|
{
|
|
_notifier.Fire(id);
|
|
if (!surfaceViaHost || _ownerHost == null)
|
|
return;
|
|
_ownerHost.FireChildUINotification(id);
|
|
}
|
|
|
|
void INotifyObject.AddListener(Listener listener) => _notifier.AddListener(listener);
|
|
|
|
public ViewItem ConstructNamedContent(
|
|
string contentName,
|
|
ParameterContext parameterContext)
|
|
{
|
|
return _typeSchema.ConstructNamedContent(contentName, this, parameterContext);
|
|
}
|
|
|
|
private bool GetBit(UIClass.Bits lookupBit) => _bits[(int)lookupBit];
|
|
|
|
private void SetBit(UIClass.Bits changeBit, bool value) => _bits[(int)changeBit] = value;
|
|
|
|
private bool ChangeBit(UIClass.Bits bit, bool value)
|
|
{
|
|
if (_bits[(int)bit] == value)
|
|
return false;
|
|
_bits[(int)bit] = value;
|
|
return true;
|
|
}
|
|
|
|
public ViewItem RootItem => _rootItem;
|
|
|
|
public string ID => _ownerHost.Name;
|
|
|
|
public Host Host => _ownerHost;
|
|
|
|
public void DeclareHost(Host ownerHost) => _ownerHost = ownerHost;
|
|
|
|
public bool Flippable
|
|
{
|
|
get => GetBit(Bits.Flippable);
|
|
set => SetBit(Bits.Flippable, value);
|
|
}
|
|
|
|
internal void SetRootItem(ViewItem newRootItem) => _rootItem = newRootItem;
|
|
|
|
internal bool HasMouseInteractiveContent() => _rootItem != null && HasMouseInteractiveContentWorker(_rootItem);
|
|
|
|
private bool HasMouseInteractiveContentWorker(ViewItem checkItem)
|
|
{
|
|
if (checkItem.MouseInteractive)
|
|
return true;
|
|
foreach (ViewItem child in checkItem.Children)
|
|
{
|
|
if (child.UI == this && HasMouseInteractiveContentWorker(child))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public object GetEventContext() => _eventContext != null ? _eventContext.Value : null;
|
|
|
|
internal void SetEventContext(EventContext eventContext) => _eventContext = eventContext;
|
|
|
|
public override string ToString() => _typeSchema.ToString();
|
|
|
|
private class SavedFocusState
|
|
{
|
|
private ViewItemID[] _path;
|
|
private bool _focusIsDefault;
|
|
|
|
public SavedFocusState(ViewItem item)
|
|
{
|
|
_path = item.IDPath;
|
|
_focusIsDefault = item.UISession.InputManager.RawKeyFocusIsDefault;
|
|
}
|
|
|
|
public ViewItemID[] Path => _path;
|
|
|
|
public bool FocusIsDefault => _focusIsDefault;
|
|
}
|
|
|
|
private class DeferredKeyFocusRestoreHelper
|
|
{
|
|
private UIClass _ui;
|
|
private UIClass.SavedFocusState _focusState;
|
|
private ICookedInputSite _lastFocus;
|
|
|
|
public DeferredKeyFocusRestoreHelper(UIClass ui, UIClass.SavedFocusState focusState)
|
|
{
|
|
_ui = ui;
|
|
_focusState = focusState;
|
|
_lastFocus = ui.UISession.InputManager.RawKeyFocus;
|
|
}
|
|
|
|
public void FaultInChild(ViewItem bottleneck, ViewItemID item)
|
|
{
|
|
_ui.PendingFocusRestore = this;
|
|
bottleneck.FaultInChild(item, new ChildFaultedInDelegate(ChildFaultedIn));
|
|
}
|
|
|
|
private void ChildFaultedIn(ViewItem bottleneckItem, ViewItem faultedInItem)
|
|
{
|
|
if (!_ui.IsZoned || _ui.PendingFocusRestore != this)
|
|
return;
|
|
_ui.PendingFocusRestore = null;
|
|
InputManager inputManager = _ui.UISession.InputManager;
|
|
if (!inputManager.RawKeyFocusIsDefault && inputManager.RawKeyFocus != _lastFocus || (faultedInItem == null || faultedInItem.IsDisposed))
|
|
return;
|
|
_ui.RestoreKeyFocus(_focusState);
|
|
}
|
|
}
|
|
|
|
private enum Bits
|
|
{
|
|
Initialized = 1,
|
|
IsDisposed = 2,
|
|
CreateInterestOnFocus = 4,
|
|
Flippable = 8,
|
|
KeyInteractive = 16, // 0x00000010
|
|
MouseInteractive = 32, // 0x00000020
|
|
MouseInteractiveSet = 64, // 0x00000040
|
|
Enabled = 128, // 0x00000080
|
|
AppFullyEnabled = 256, // 0x00000100
|
|
RawInputDisabled = 512, // 0x00000200
|
|
DirectKeyFocus = 1024, // 0x00000400
|
|
DirectMouseFocus = 2048, // 0x00000800
|
|
KeyFocusOnMouseEnter = 4096, // 0x00001000
|
|
KeyFocusOnMouseDown = 8192, // 0x00002000
|
|
AllowDoubleClicks = 16384, // 0x00004000
|
|
KeyFocus = 32768, // 0x00008000
|
|
MouseFocus = 65536, // 0x00010000
|
|
HasAccProxy = 131072, // 0x00020000
|
|
ScriptEnabled = 262144, // 0x00040000
|
|
HasAutomationInstanceId = 524288, // 0x00080000
|
|
}
|
|
}
|
|
}
|