// In order for tests to run without an EditorWindow but still be able to send
 // events, we sometimes need to force the event type. IMGUI::GetEventType() (native) will
 // return the event type as Ignore if the proper views haven't yet been
 // initialized. This (falsely) breaks tests that rely on the event type. So for tests, we
 // just ensure the event type is what we originally set it to when we sent it.
 internal static EventBase CreateEvent(Event systemEvent, EventType eventType)
 {
     switch (eventType)
     {
         case EventType.MouseMove:
             return PointerMoveEvent.GetPooled(systemEvent);
         case EventType.MouseDrag:
             return PointerMoveEvent.GetPooled(systemEvent);
         case EventType.MouseDown:
             // If some buttons are already down, we generate PointerMove/MouseDown events.
             // Otherwise we generate PointerDown/MouseDown events.
             // See W3C pointer events recommendation: https://www.w3.org/TR/pointerevents2
             if (PointerDeviceState.GetPressedButtons(PointerId.mousePointerId) != 0)
             {
                 return PointerMoveEvent.GetPooled(systemEvent);
             }
             else
             {
                 return PointerDownEvent.GetPooled(systemEvent);
             }
         case EventType.MouseUp:
             // If more buttons are still down, we generate PointerMove/MouseUp events.
             // Otherwise we generate PointerUp/MouseUp events.
             // See W3C pointer events recommendation: https://www.w3.org/TR/pointerevents2
             if (PointerDeviceState.HasAdditionalPressedButtons(PointerId.mousePointerId, systemEvent.button))
             {
                 return PointerMoveEvent.GetPooled(systemEvent);
             }
             else
             {
                 return PointerUpEvent.GetPooled(systemEvent);
             }
         case EventType.ContextClick:
             return ContextClickEvent.GetPooled(systemEvent);
         case EventType.MouseEnterWindow:
             return MouseEnterWindowEvent.GetPooled(systemEvent);
         case EventType.MouseLeaveWindow:
             return MouseLeaveWindowEvent.GetPooled(systemEvent);
         case EventType.ScrollWheel:
             return WheelEvent.GetPooled(systemEvent);
         case EventType.KeyDown:
             return KeyDownEvent.GetPooled(systemEvent);
         case EventType.KeyUp:
             return KeyUpEvent.GetPooled(systemEvent);
         case EventType.DragUpdated:
             return DragUpdatedEvent.GetPooled(systemEvent);
         case EventType.DragPerform:
             return DragPerformEvent.GetPooled(systemEvent);
         case EventType.DragExited:
             return DragExitedEvent.GetPooled(systemEvent);
         case EventType.ValidateCommand:
             return ValidateCommandEvent.GetPooled(systemEvent);
         case EventType.ExecuteCommand:
             return ExecuteCommandEvent.GetPooled(systemEvent);
         default:// Layout, Ignore, Used
             return IMGUIEvent.GetPooled(systemEvent);
     }
 }
Beispiel #2
0
        // In order for tests to run without an EditorWindow but still be able to send
        // events, we sometimes need to force the event type. IMGUI::GetEventType() (native) will
        // return the event type as Ignore if the proper views haven't yet been
        // initialized. This (falsely) breaks tests that rely on the event type. So for tests, we
        // just ensure the event type is what we originally set it to when we sent it.
        internal static EventBase CreateEvent(Event systemEvent, EventType eventType)
        {
            switch (eventType)
            {
            case EventType.MouseMove:
                return(MouseMoveEvent.GetPooled(systemEvent));

            case EventType.MouseDrag:
                return(MouseMoveEvent.GetPooled(systemEvent));

            case EventType.MouseDown:
                return(MouseDownEvent.GetPooled(systemEvent));

            case EventType.MouseUp:
                return(MouseUpEvent.GetPooled(systemEvent));

            case EventType.ContextClick:
                return(ContextClickEvent.GetPooled(systemEvent));

            case EventType.MouseEnterWindow:
                return(MouseEnterWindowEvent.GetPooled(systemEvent));

            case EventType.MouseLeaveWindow:
                return(MouseLeaveWindowEvent.GetPooled(systemEvent));

            case EventType.ScrollWheel:
                return(WheelEvent.GetPooled(systemEvent));

            case EventType.KeyDown:
                return(KeyDownEvent.GetPooled(systemEvent));

            case EventType.KeyUp:
                return(KeyUpEvent.GetPooled(systemEvent));

            case EventType.DragUpdated:
                return(DragUpdatedEvent.GetPooled(systemEvent));

            case EventType.DragPerform:
                return(DragPerformEvent.GetPooled(systemEvent));

            case EventType.DragExited:
                return(DragExitedEvent.GetPooled(systemEvent));

            case EventType.ValidateCommand:
                return(ValidateCommandEvent.GetPooled(systemEvent));

            case EventType.ExecuteCommand:
                return(ExecuteCommandEvent.GetPooled(systemEvent));

            default:    // Layout, Ignore, Used
                return(IMGUIEvent.GetPooled(systemEvent));
            }
        }
        private void DoOnGUI(Event evt, Matrix4x4 parentTransform, Rect clippingRect, bool isComputingLayout, Rect layoutSize, Action onGUIHandler, bool canAffectFocus = true)
        {
            // Extra checks are needed here because client code might have changed the IMGUIContainer
            // since we enter HandleIMGUIEvent()
            if (onGUIHandler == null ||
                panel == null)
            {
                return;
            }

            // Save the GUIClip count to make sanity checks after calling the OnGUI handler
            int guiClipCount = GUIClip.Internal_GetCount();

            SaveGlobals();

            // Save a copy of the container size.
            var previousMeasuredWidth  = layoutMeasuredWidth;
            var previousMeasuredHeight = layoutMeasuredHeight;

            UIElementsUtility.BeginContainerGUI(cache, evt, this);

            // For the IMGUI, we need to update the GUI.color with the actual play mode tint ...
            // In fact, this is taken from EditorGUIUtility.ResetGUIState().
            // Here, the play mode tint is either white (no tint, or not in play mode) or the right color (if in play mode)
            GUI.color = UIElementsUtility.editorPlayModeTintColor;
            // From now on, Event.current is either evt or a copy of evt.
            // Since Event.current may change while being processed, we do not rely on evt below but use Event.current instead.

            if (Event.current.type != EventType.Layout)
            {
                if (lostFocus)
                {
                    if (focusController != null)
                    {
                        // We dont want to clear the GUIUtility.keyboardControl if another IMGUIContainer
                        // just set it in the if (receivedFocus) block below. So we only clear it if own it.
                        if (GUIUtility.OwnsId(GUIUtility.keyboardControl))
                        {
                            GUIUtility.keyboardControl           = 0;
                            focusController.imguiKeyboardControl = 0;
                        }
                    }
                    lostFocus = false;
                }

                if (receivedFocus)
                {
                    if (hasFocusableControls)
                    {
                        if (focusChangeDirection != FocusChangeDirection.unspecified && focusChangeDirection != FocusChangeDirection.none)
                        {
                            // We got here by tabbing.

                            // We assume we are using the VisualElementFocusRing.
                            if (focusChangeDirection == VisualElementFocusChangeDirection.left)
                            {
                                GUIUtility.SetKeyboardControlToLastControlId();
                            }
                            else if (focusChangeDirection == VisualElementFocusChangeDirection.right)
                            {
                                GUIUtility.SetKeyboardControlToFirstControlId();
                            }
                        }
                        else if (GUIUtility.keyboardControl == 0)
                        {
                            // Since GUIUtility.keyboardControl == 0, we got focused in some other way than by clicking inside us
                            // (for example it could be by clicking in an element that delegates focus to us).
                            // Give GUIUtility.keyboardControl to our first control.
                            GUIUtility.SetKeyboardControlToFirstControlId();
                        }
                    }

                    receivedFocus        = false;
                    focusChangeDirection = FocusChangeDirection.unspecified;
                    if (focusController != null)
                    {
                        if (focusController.imguiKeyboardControl != GUIUtility.keyboardControl)
                        {
                            newKeyboardFocusControlID = GUIUtility.keyboardControl;
                        }

                        focusController.imguiKeyboardControl = GUIUtility.keyboardControl;
                    }
                }
                // We intentionally don't send the NewKeyboardFocus command here since it creates an issue with the AutomatedWindow
                // newKeyboardFocusControlID = GUIUtility.keyboardControl;
            }

            EventType originalEventType = Event.current.type;

            bool isExitGUIException = false;

            try
            {
                using (new GUIClip.ParentClipScope(parentTransform, clippingRect))
                {
                    onGUIHandler();
                }
            }
            catch (Exception exception)
            {
                // only for layout events: we always intercept any exceptions to not interrupt event processing
                if (originalEventType == EventType.Layout)
                {
                    isExitGUIException = GUIUtility.IsExitGUIException(exception);
                    if (!isExitGUIException)
                    {
                        Debug.LogException(exception);
                    }
                }
                else
                {
                    // rethrow event if not in layout
                    throw;
                }
            }
            finally
            {
                if (Event.current.type != EventType.Layout && canAffectFocus)
                {
                    int currentKeyboardFocus = GUIUtility.keyboardControl;
                    int result = GUIUtility.CheckForTabEvent(Event.current);
                    if (focusController != null)
                    {
                        if (result < 0)
                        {
                            // If CheckForTabEvent returns -1 or -2, we have reach the end/beginning of its control list.
                            // We should switch the focus to the next VisualElement.
                            Focusable currentFocusedElement = focusController.GetLeafFocusedElement();
                            Focusable nextFocusedElement    = null;
                            using (KeyDownEvent e = KeyDownEvent.GetPooled('\t', KeyCode.Tab, result == -1 ? EventModifiers.None : EventModifiers.Shift))
                            {
                                nextFocusedElement = focusController.SwitchFocusOnEvent(e);
                            }

                            if (currentFocusedElement == this)
                            {
                                if (nextFocusedElement == this)
                                {
                                    // We will still have the focus. We should cycle around our controls.
                                    if (result == -2)
                                    {
                                        GUIUtility.SetKeyboardControlToLastControlId();
                                    }
                                    else if (result == -1)
                                    {
                                        GUIUtility.SetKeyboardControlToFirstControlId();
                                    }

                                    newKeyboardFocusControlID            = GUIUtility.keyboardControl;
                                    focusController.imguiKeyboardControl = GUIUtility.keyboardControl;
                                }
                                else
                                {
                                    // We will lose the focus. Set the focused element ID to 0 until next
                                    // IMGUIContainer have a chance to set it to its own control.
                                    // Doing this will ensure we draw ourselves without any focused control.
                                    GUIUtility.keyboardControl           = 0;
                                    focusController.imguiKeyboardControl = 0;
                                }
                            }
                        }
                        else if (result > 0)
                        {
                            // A positive result indicates that the focused control has changed to one of our elements; result holds the control id.
                            focusController.imguiKeyboardControl = GUIUtility.keyboardControl;
                            newKeyboardFocusControlID            = GUIUtility.keyboardControl;
                        }
                        else if (result == 0)
                        {
                            // This means the event is not a tab. Synchronize our focus info with IMGUI.

                            if (originalEventType == EventType.MouseDown && !focusOnlyIfHasFocusableControls)
                            {
                                focusController.SyncIMGUIFocus(GUIUtility.keyboardControl, this, true);
                            }
                            else if ((currentKeyboardFocus != GUIUtility.keyboardControl) || (originalEventType == EventType.MouseDown))
                            {
                                focusController.SyncIMGUIFocus(GUIUtility.keyboardControl, this, false);
                            }
                            else if (GUIUtility.keyboardControl != focusController.imguiKeyboardControl)
                            {
                                // Here we want to resynchronize our internal state ...
                                newKeyboardFocusControlID = GUIUtility.keyboardControl;

                                if (focusController.GetLeafFocusedElement() == this)
                                {
                                    // In this case, the focused element is the right one in the Focus Controller... we are just updating the internal imguiKeyboardControl
                                    focusController.imguiKeyboardControl = GUIUtility.keyboardControl;
                                }
                                else
                                {
                                    // In this case, the focused element is NOT the right one in the Focus Controller... we also have to refocus...
                                    focusController.SyncIMGUIFocus(GUIUtility.keyboardControl, this, false);
                                }
                            }
                        }
                    }
                    // Cache the fact that we have focusable controls or not.
                    hasFocusableControls = GUIUtility.HasFocusableControls();
                }
            }

            // This will copy Event.current into evt.
            UIElementsUtility.EndContainerGUI(evt, layoutSize);
            RestoreGlobals();

            // See if the container size has changed. This is to make absolutely sure the VisualElement resizes
            // if the IMGUI content resizes.
            if (!isComputingLayout && evt.type == EventType.Layout &&
                (!Mathf.Approximately(previousMeasuredWidth, layoutMeasuredWidth) || !Mathf.Approximately(previousMeasuredHeight, layoutMeasuredHeight)))
            {
                IncrementVersion(VersionChangeType.Layout);
            }

            if (!isExitGUIException)
            {
                // This is the same logic as GUIClipState::EndOnGUI
                if (evt.type != EventType.Ignore && evt.type != EventType.Used)
                {
                    int currentCount = GUIClip.Internal_GetCount();
                    if (currentCount > guiClipCount)
                    {
                        Debug.LogError("GUI Error: You are pushing more GUIClips than you are popping. Make sure they are balanced.");
                    }
                    else if (currentCount < guiClipCount)
                    {
                        Debug.LogError("GUI Error: You are popping more GUIClips than you are pushing. Make sure they are balanced.");
                    }
                }
            }

            // Clear extraneous GUIClips
            while (GUIClip.Internal_GetCount() > guiClipCount)
            {
                GUIClip.Internal_Pop();
            }

            if (evt.type == EventType.Used)
            {
                IncrementVersion(VersionChangeType.Repaint);
            }
        }
Beispiel #4
0
        // In order for tests to run without an EditorWindow but still be able to send
        // events, we sometimes need to force the event type. IMGUI::GetEventType() (native) will
        // return the event type as Ignore if the proper views haven't yet been
        // initialized. This (falsely) breaks tests that rely on the event type. So for tests, we
        // just ensure the event type is what we originally set it to when we sent it.
        internal static EventBase CreateEvent(Event systemEvent, EventType eventType)
        {
            switch (eventType)
            {
            case EventType.MouseMove:
                return(PointerMoveEvent.GetPooled(systemEvent));

            case EventType.MouseDrag:
                return(PointerMoveEvent.GetPooled(systemEvent));

            case EventType.MouseDown:
                // If some buttons are already down, we generate PointerMove/MouseDown events.
                // Otherwise we generate PointerDown/MouseDown events.
                // See W3C pointer events recommendation: https://www.w3.org/TR/pointerevents2
                // Note: sometimes systemEvent.button is already pressed (systemEvent is processed multiple times).
                if (PointerDeviceState.HasAdditionalPressedButtons(PointerId.mousePointerId, systemEvent.button))
                {
                    return(PointerMoveEvent.GetPooled(systemEvent));
                }
                else
                {
                    return(PointerDownEvent.GetPooled(systemEvent));
                }

            case EventType.MouseUp:
                // If more buttons are still down, we generate PointerMove/MouseUp events.
                // Otherwise we generate PointerUp/MouseUp events.
                // See W3C pointer events recommendation: https://www.w3.org/TR/pointerevents2
                if (PointerDeviceState.HasAdditionalPressedButtons(PointerId.mousePointerId, systemEvent.button))
                {
                    return(PointerMoveEvent.GetPooled(systemEvent));
                }
                else
                {
                    return(PointerUpEvent.GetPooled(systemEvent));
                }

            case EventType.ContextClick:
                return(ContextClickEvent.GetPooled(systemEvent));

            case EventType.MouseEnterWindow:
                return(MouseEnterWindowEvent.GetPooled(systemEvent));

            case EventType.MouseLeaveWindow:
                return(MouseLeaveWindowEvent.GetPooled(systemEvent));

            case EventType.ScrollWheel:
                return(WheelEvent.GetPooled(systemEvent));

            case EventType.KeyDown:
                return(KeyDownEvent.GetPooled(systemEvent));

            case EventType.KeyUp:
                return(KeyUpEvent.GetPooled(systemEvent));

#if UNITY_EDITOR
            case EventType.DragUpdated:
                return(DragUpdatedEvent.GetPooled(systemEvent));

            case EventType.DragPerform:
                return(DragPerformEvent.GetPooled(systemEvent));

            case EventType.DragExited:
                return(DragExitedEvent.GetPooled(systemEvent));
#endif
            case EventType.ValidateCommand:
                return(ValidateCommandEvent.GetPooled(systemEvent));

            case EventType.ExecuteCommand:
                return(ExecuteCommandEvent.GetPooled(systemEvent));

            default:    // Layout, Ignore, Used
                return(IMGUIEvent.GetPooled(systemEvent));
            }
        }