#define AvoidCompileError
using System;
using UnityEngine;
using UnityEngine.UIElements;
namespace RosettaUI.UIToolkit
{
///
/// Referring to UIElement's Popup.
///
public class ModalWindow : Window
{
private const string USSClassNameEventBlocker = "rosettaui-modal-window-event-blocker";
private const string USSClassName = "rosettaui-modal-window";
private readonly VisualElement _eventBlockerElement;
private KeyboardNavigationManipulator _navigationManipulator;
protected override VisualElement SelfRoot => _eventBlockerElement;
public ModalWindow(bool resizable = false) : base(resizable, true)
{
_eventBlockerElement = new VisualElement();
_eventBlockerElement.AddToClassList(USSClassNameEventBlocker);
_eventBlockerElement.Add(this);
AddToClassList(USSClassName);
_eventBlockerElement.RegisterCallback(OnAttachToPanel);
_eventBlockerElement.RegisterCallback(OnDetachFromPanel);
}
private void OnAttachToPanel(AttachToPanelEvent evt)
{
if (evt.destinationPanel == null)
return;
contentContainer.AddManipulator(_navigationManipulator = new KeyboardNavigationManipulator(Apply));
_eventBlockerElement.RegisterCallback(OnPointerDownOnBlocker);
//evt.destinationPanel.visualTree.RegisterCallback(OnParentResized);
//m_ScrollView.RegisterCallback(OnContainerGeometryChanged);
//RegisterCallback(OnFocusOut);
}
private void OnDetachFromPanel(DetachFromPanelEvent evt)
{
if (evt.originPanel == null)
return;
UnregisterCallback(OnAttachToPanel);
UnregisterCallback(OnDetachFromPanel);
contentContainer.RemoveManipulator(_navigationManipulator);
_eventBlockerElement.UnregisterCallback(OnPointerDownOnBlocker);
//evt.originPanel.visualTree.UnregisterCallback(OnParentResized);
}
public override void Hide()
{
base.Hide();
_eventBlockerElement.RemoveFromHierarchy();
}
private void Apply(KeyboardNavigationOperation op, EventBase sourceEvent)
{
if (Apply(op))
{
sourceEvent.StopPropagationAndFocusControllerIgnoreEvent();
}
}
private bool Apply(KeyboardNavigationOperation op)
{
/*
var selectedIndex = GetSelectedIndex();
void UpdateSelectionDown(int newIndex)
{
while (newIndex < m_Items.Count)
{
if (m_Items[newIndex].element.enabledSelf)
{
ChangeSelectedIndex(newIndex, selectedIndex);
break;
}
++newIndex;
}
}
void UpdateSelectionUp(int newIndex)
{
while (newIndex >= 0)
{
if (m_Items[newIndex].element.enabledSelf)
{
ChangeSelectedIndex(newIndex, selectedIndex);
break;
}
--newIndex;
}
}
*/
switch (op)
{
case KeyboardNavigationOperation.Cancel:
Hide();
return true;
case KeyboardNavigationOperation.Submit:
/*
var item = m_Items[selectedIndex];
if (selectedIndex >= 0 && item.element.enabledSelf)
{
item.action?.Invoke();
item.actionUserData?.Invoke(item.element.userData);
}
*/
Hide();
return true;
/*
case KeyboardNavigationOperation.Previous:
UpdateSelectionUp(selectedIndex < 0 ? m_Items.Count - 1 : selectedIndex - 1);
return true;
case KeyboardNavigationOperation.Next:
UpdateSelectionDown(selectedIndex + 1);
return true;
case KeyboardNavigationOperation.PageUp:
case KeyboardNavigationOperation.Begin:
UpdateSelectionDown(0);
return true;
case KeyboardNavigationOperation.PageDown:
case KeyboardNavigationOperation.End:
UpdateSelectionUp(m_Items.Count - 1);
return true;
*/
}
return false;
}
private void OnPointerDownOnBlocker(PointerDownEvent evt)
{
/*
m_MousePosition = m_ScrollView.WorldToLocal(evt.position);
UpdateSelection(evt.target as VisualElement);
if (evt.pointerId != PointerId.mousePointerId)
{
m_MenuContainer.panel.PreventCompatibilityMouseEvents(evt.pointerId);
}
*/
if (worldBound.Contains(evt.position)) return;
Hide();
evt.StopPropagation();
}
#if false
void OnPointerMove(PointerMoveEvent evt)
{
/*
m_MousePosition = m_ScrollView.WorldToLocal(evt.position);
UpdateSelection(evt.target as VisualElement);
if (evt.pointerId != PointerId.mousePointerId)
{
m_MenuContainer.panel.PreventCompatibilityMouseEvents(evt.pointerId);
}
*/
evt.StopPropagation();
}
void OnPointerUp(PointerUpEvent evt)
{
/*
var selectedIndex = GetSelectedIndex();
if (selectedIndex != -1)
{
var item = m_Items[selectedIndex];
item.action?.Invoke();
item.actionUserData?.Invoke(item.element.userData);
Hide();
}
if (evt.pointerId != PointerId.mousePointerId)
{
m_MenuContainer.panel.PreventCompatibilityMouseEvents(evt.pointerId);
}
evt.StopPropagation();
*/
Hide();
evt.StopPropagation();
}
void OnFocusOut(FocusOutEvent evt)
{
/*
if (!m_ScrollView.ContainsPoint(m_MousePosition))
{
Hide();
}
else
{
// Keep the focus in.
m_MenuContainer.schedule.Execute(contentContainer.Focus);
}
*/
Hide();
}
void OnParentResized(GeometryChangedEvent evt)
{
Hide();
}
#endif
}
#if AvoidCompileError
///
/// Represents an operation that the user is trying to accomplish through a specific input mechanism.
///
///
/// Tests the received callback value for against the values of this enum to implement the operation in your UI.
///
public enum KeyboardNavigationOperation
{
///
/// Default value. Indicates an uninitialized enum value.
///
None,
///
/// Selects all UI selectable elements or text.
///
SelectAll,
///
/// Cancels the current UI interaction.
///
Cancel,
///
/// Submits or concludes the current UI interaction.
///
Submit,
///
/// Selects the previous item.
///
Previous,
///
/// Selects the next item.
///
Next,
///
/// Moves the selection up one page (in a list which has scrollable area).
///
PageUp,
///
/// Moves the selection down one page (in a list which has scrollable area).
///
PageDown,
///
/// Selects the first element.
///
Begin,
///
/// Selects the last element.
///
End,
}
///
/// Provides a default implementation for translating input device specific events to higher level navigation operations as commonly possible with a keyboard.
///
public class KeyboardNavigationManipulator : Manipulator
{
private readonly Action m_Action;
///
/// Initializes and returns an instance of KeyboardNavigationManipulator, configured to invoke the specified callback.
///
/// This action is invoked when specific low level events are dispatched to the target ,
/// with a specific value of and a reference to the original low level event.
public KeyboardNavigationManipulator(Action action)
{
m_Action = action;
}
protected override void RegisterCallbacksOnTarget()
{
/*
target.RegisterCallback(OnNavigationMove);
target.RegisterCallback(OnNavigationSubmit);
target.RegisterCallback(OnNavigationCancel);
*/
target.RegisterCallback(OnKeyDown);
}
protected override void UnregisterCallbacksFromTarget()
{
/*
target.UnregisterCallback(OnNavigationMove);
target.UnregisterCallback(OnNavigationSubmit);
target.UnregisterCallback(OnNavigationCancel);
*/
target.UnregisterCallback(OnKeyDown);
}
internal void OnKeyDown(KeyDownEvent evt)
{
if (target.panel?.contextType == ContextType.Editor)
OnEditorKeyDown(evt);
else
OnRuntimeKeyDown(evt);
}
private void OnRuntimeKeyDown(KeyDownEvent evt)
{
// At the moment these actions are not mapped dynamically in the InputSystemEventSystem component.
// When that becomes the case in the future, remove the following and use corresponding Navigation events.
KeyboardNavigationOperation GetOperation()
{
switch (evt.keyCode)
{
case KeyCode.A when evt.actionKey: return KeyboardNavigationOperation.SelectAll;
case KeyCode.Home: return KeyboardNavigationOperation.Begin;
case KeyCode.End: return KeyboardNavigationOperation.End;
case KeyCode.PageUp: return KeyboardNavigationOperation.PageUp;
case KeyCode.PageDown: return KeyboardNavigationOperation.PageDown;
}
// TODO why do we want to invoke the callback in this case? Looks weird.
return KeyboardNavigationOperation.None;
}
Invoke(GetOperation(), evt);
}
private void OnEditorKeyDown(KeyDownEvent evt)
{
KeyboardNavigationOperation GetOperation()
{
switch (evt.keyCode)
{
case KeyCode.A when evt.actionKey: return KeyboardNavigationOperation.SelectAll;
case KeyCode.Escape: return KeyboardNavigationOperation.Cancel;
case KeyCode.Return:
case KeyCode.KeypadEnter: return KeyboardNavigationOperation.Submit;
case KeyCode.UpArrow: return KeyboardNavigationOperation.Previous;
case KeyCode.DownArrow: return KeyboardNavigationOperation.Next;
case KeyCode.Home: return KeyboardNavigationOperation.Begin;
case KeyCode.End: return KeyboardNavigationOperation.End;
case KeyCode.PageUp: return KeyboardNavigationOperation.PageUp;
case KeyCode.PageDown: return KeyboardNavigationOperation.PageDown;
}
return KeyboardNavigationOperation.None;
}
Invoke(GetOperation(), evt);
}
#if false
void OnNavigationCancel(NavigationCancelEvent evt)
{
Invoke(KeyboardNavigationOperation.Cancel, evt);
}
void OnNavigationSubmit(NavigationSubmitEvent evt)
{
Invoke(KeyboardNavigationOperation.Submit, evt);
}
void OnNavigationMove(NavigationMoveEvent evt)
{
switch (evt.direction)
{
case NavigationMoveEvent.Direction.Up:
Invoke(KeyboardNavigationOperation.Previous, evt);
break;
case NavigationMoveEvent.Direction.Down:
Invoke(KeyboardNavigationOperation.Next, evt);
break;
}
}
#endif
private void Invoke(KeyboardNavigationOperation operation, EventBase evt)
{
if (operation == KeyboardNavigationOperation.None)
return;
m_Action?.Invoke(operation, evt);
}
}
}
#endif