private static void RightHorizontalSplitPopup(SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds)
        {
            // Calculate left for right aligned split
            Vector2 popupPosition = new Vector2(systemWindow.Width - popup.Widget.Width, 0);

            Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);

            popup.Widget.Height = anchorLeft.Y;

            popup.Widget.Position = popupPosition;
        }
        private static void BestPopupPosition(SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds)
        {
            // Calculate left aligned screen space position (using widgetRelativeTo.parent)
            Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);

            anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);

            Vector2 popupPosition = anchorLeft;

            var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;

            Vector2 xPosition = GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

            Vector2 screenPosition;

            screenPosition = anchorLeft + xPosition;

            // Constrain
            if (screenPosition.X + popup.Widget.Width > systemWindow.Width ||
                screenPosition.X < 0)
            {
                xPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
            }

            popupPosition += xPosition;

            Vector2 yPosition = GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

            screenPosition = anchorLeft + yPosition;

            // Constrain
            if (anchor.AltMate != null &&
                (screenPosition.Y + popup.Widget.Height > systemWindow.Height ||
                 screenPosition.Y < 0))
            {
                yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
            }

            popupPosition += yPosition;

            popup.Widget.Position = popupPosition;
        }
        public static void ShowPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds, int borderWidth, Action <SystemWindow, MatePoint, MatePoint, RectangleDouble> layoutHelper)
        {
            var hookedParents = new HashSet <GuiWidget>();

            List <IIgnoredPopupChild> ignoredWidgets = popup.Widget.Children.OfType <IIgnoredPopupChild>().ToList();

            void widget_Draw(object sender, DrawEventArgs e)
            {
                if (borderWidth > 0)
                {
                    e.Graphics2D.Render(
                        new Stroke(
                            new RoundedRect(popup.Widget.LocalBounds, 0),
                            borderWidth * 2),
                        AppContext.Theme.PopupBorderColor);
                }
            }

            void widgetRelativeTo_PositionChanged(object sender, EventArgs e)
            {
                if (anchor.Widget?.Parent != null)
                {
                    layoutHelper.Invoke(systemWindow, anchor, popup, altBounds);
                }
            }

            void CloseMenu()
            {
                popup.Widget.AfterDraw -= widget_Draw;

                popup.Widget.Close();

                anchor.Widget.Closed -= anchor_Closed;

                // Unbind callbacks on parents for position_changed if we're closing
                foreach (GuiWidget widget in hookedParents)
                {
                    widget.PositionChanged -= widgetRelativeTo_PositionChanged;
                    widget.BoundsChanged   -= widgetRelativeTo_PositionChanged;
                }

                // Long lived originating item must be unregistered
                anchor.Widget.Closed -= anchor_Closed;

                // Restore focus to originating widget on close
                if (anchor.Widget?.HasBeenClosed == false)
                {
                    anchor.Widget.Focus();
                }
            }

            void FocusChanged(object s, EventArgs e)
            {
                UiThread.RunOnIdle(() =>
                {
                    // Fired any time focus changes. Traditionally we closed the menu if we weren't focused.
                    // To accommodate children (or external widgets) having focus we also query for and consider special cases
                    bool specialChildHasFocus    = ignoredWidgets.Any(w => w.ContainsFocus || w.Focused || w.KeepMenuOpen);
                    bool descendantIsHoldingOpen = popup.Widget.Descendants <GuiWidget>().Any(w => w is IIgnoredPopupChild ignoredPopupChild &&
                                                                                              ignoredPopupChild.KeepMenuOpen);

                    // If the focused changed and we've lost focus and no special cases permit, close the menu
                    if (!popup.Widget.ContainsFocus &&
                        !specialChildHasFocus &&
                        !descendantIsHoldingOpen &&
                        !PopupWidget.DebugKeepOpen)
                    {
                        CloseMenu();
                    }
                });
            }

            void anchor_Closed(object sender, EventArgs e)
            {
                // If the owning widget closed, so should we
                CloseMenu();
            }

            foreach (var ancestor in anchor.Widget.Parents <GuiWidget>().Where(p => p != systemWindow))
            {
                if (hookedParents.Add(ancestor))
                {
                    ancestor.PositionChanged += widgetRelativeTo_PositionChanged;
                    ancestor.BoundsChanged   += widgetRelativeTo_PositionChanged;
                }
            }

            popup.Widget.ContainsFocusChanged += FocusChanged;
            popup.Widget.AfterDraw            += widget_Draw;

            widgetRelativeTo_PositionChanged(anchor.Widget, null);
            anchor.Widget.Closed += anchor_Closed;

            // When the widgets position changes, sync the popup position
            systemWindow?.AddChild(popup.Widget);

            popup.Widget.Closed += (s, e) =>
            {
                Console.WriteLine();
            };

            popup.Widget.Focus();

            popup.Widget.Invalidate();
        }
 public static void ShowRightSplitPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), int borderWidth = 1)
 {
     ShowPopup(systemWindow, anchor, popup, altBounds, borderWidth, RightHorizontalSplitPopup);
 }
 public static void ShowPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), int borderWidth = 1)
 {
     ShowPopup(systemWindow, anchor, popup, altBounds, borderWidth, BestPopupPosition);
 }
        public static void ShowPopover(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), double secondsToClose = 0)
        {
            var settingsRow          = anchor.Widget as SettingsRow;
            var sliceSettingsPopover = popup.Widget as ClickablePopover;

            var hookedWidgets = new HashSet <GuiWidget>();

            void Anchor_Closed(object sender, EventArgs e)
            {
                if (popup.Widget is IOverrideAutoClose overideAutoClose &&
                    !overideAutoClose.AllowAutoClose)
                {
                    return;
                }

                // If the owning widget closed, so should we
                popup.Widget.Close();

                foreach (var widget in hookedWidgets)
                {
                    widget.Closed -= Anchor_Closed;
                }
            }

            void widgetRelativeTo_PositionChanged(object sender, EventArgs e)
            {
                if (anchor.Widget?.Parent != null)
                {
                    // Calculate left aligned screen space position (using widgetRelativeTo.parent)
                    Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
                    anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);

                    Vector2 popupPosition = anchorLeft;

                    var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;

                    Vector2 xPosition = GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

                    Vector2 screenPosition;

                    screenPosition = anchorLeft + xPosition;

                    // Constrain
                    if (screenPosition.X + popup.Widget.Width > systemWindow.Width ||
                        screenPosition.X < 0)
                    {
                        var altXPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);

                        var altScreenPosition = anchorLeft + altXPosition;

                        // Prefer clipping on edge revealed by resize
                        if ((popup.AltMate.Right && altScreenPosition.X > -15) ||
                            (popup.AltMate.Left && altScreenPosition.X + popup.Widget.Width < systemWindow.Width))
                        {
                            xPosition = altXPosition;

                            if (settingsRow != null &&
                                sliceSettingsPopover != null)
                            {
                                sliceSettingsPopover.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Left ? ArrowDirection.Right : ArrowDirection.Left;
                            }
                        }
                    }

                    popupPosition += xPosition;

                    Vector2 yPosition = GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

                    screenPosition = anchorLeft + yPosition;

                    // Constrain
                    if (anchor.AltMate != null &&
                        (screenPosition.Y + popup.Widget.Height > systemWindow.Height ||
                         screenPosition.Y < 0))
                    {
                        yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);

                        if (settingsRow != null)
                        {
                            settingsRow.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Up ? ArrowDirection.Down: ArrowDirection.Up;
                        }
                    }

                    popup.Widget.Closed  += Anchor_Closed;
                    anchor.Widget.Closed += Anchor_Closed;
                    hookedWidgets.Add(anchor.Widget);

                    foreach (var widget in anchor.Widget.Parents <GuiWidget>())
                    {
                        widget.Closed += Anchor_Closed;
                        hookedWidgets.Add(widget);
                    }

                    popupPosition += yPosition;

                    popup.Widget.Position = popupPosition;
                }
            }

            widgetRelativeTo_PositionChanged(anchor.Widget, null);

            // When the widgets position changes, sync the popup position
            systemWindow?.AddChild(popup.Widget);

            if (secondsToClose > 0)
            {
                UiThread.RunOnIdle(() => Anchor_Closed(null, null), secondsToClose);
            }
        }
        public static void ShowPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble))
        {
            var hookedParents = new HashSet <GuiWidget>();

            List <IIgnoredPopupChild> ignoredWidgets = popup.Widget.Children.OfType <IIgnoredPopupChild>().ToList();

            void widgetRelativeTo_PositionChanged(object sender, EventArgs e)
            {
                if (anchor.Widget?.Parent != null)
                {
                    // Calculate left aligned screen space position (using widgetRelativeTo.parent)
                    Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
                    anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);

                    Vector2 popupPosition = anchorLeft;

                    var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;

                    Vector2 xPosition = GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

                    Vector2 screenPosition;

                    screenPosition = anchorLeft + xPosition;

                    // Constrain
                    if (screenPosition.X + popup.Widget.Width > systemWindow.Width ||
                        screenPosition.X < 0)
                    {
                        xPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
                    }

                    popupPosition += xPosition;

                    Vector2 yPosition = GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);

                    screenPosition = anchorLeft + yPosition;

                    // Constrain
                    if (anchor.AltMate != null &&
                        (screenPosition.Y + popup.Widget.Height > systemWindow.Height ||
                         screenPosition.Y < 0))
                    {
                        yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
                    }

                    popupPosition += yPosition;

                    popup.Widget.Position = popupPosition;
                }
            }

            void CloseMenu()
            {
                popup.Widget.Close();

                anchor.Widget.Closed -= anchor_Closed;

                // Unbind callbacks on parents for position_changed if we're closing
                foreach (GuiWidget widget in hookedParents)
                {
                    widget.PositionChanged -= widgetRelativeTo_PositionChanged;
                    widget.BoundsChanged   -= widgetRelativeTo_PositionChanged;
                }

                // Long lived originating item must be unregistered
                anchor.Widget.Closed -= anchor_Closed;

                // Restore focus to originating widget on close
                if (anchor.Widget?.HasBeenClosed == false)
                {
                    anchor.Widget.Focus();
                }
            }

            void FocusChanged(object s, EventArgs e)
            {
                UiThread.RunOnIdle(() =>
                {
                    // Fired any time focus changes. Traditionally we closed the menu if we weren't focused.
                    // To accommodate children (or external widgets) having focus we also query for and consider special cases
                    bool specialChildHasFocus    = ignoredWidgets.Any(w => w.ContainsFocus || w.Focused || w.KeepMenuOpen);
                    bool descendantIsHoldingOpen = popup.Widget.Descendants <GuiWidget>().Any(w => w is IIgnoredPopupChild ignoredPopupChild &&
                                                                                              ignoredPopupChild.KeepMenuOpen);

                    // If the focused changed and we've lost focus and no special cases permit, close the menu
                    if (!popup.Widget.ContainsFocus &&
                        !specialChildHasFocus &&
                        !descendantIsHoldingOpen)
                    {
                        CloseMenu();
                    }
                });
            }

            void anchor_Closed(object sender, EventArgs e)
            {
                // If the owning widget closed, so should we
                CloseMenu();
            }

            foreach (var ancestor in anchor.Widget.Parents <GuiWidget>().Where(p => p != systemWindow))
            {
                if (hookedParents.Add(ancestor))
                {
                    ancestor.PositionChanged += widgetRelativeTo_PositionChanged;
                    ancestor.BoundsChanged   += widgetRelativeTo_PositionChanged;
                }
            }

            popup.Widget.ContainsFocusChanged += FocusChanged;

            widgetRelativeTo_PositionChanged(anchor.Widget, null);
            anchor.Widget.Closed += anchor_Closed;

            // When the widgets position changes, sync the popup position
            systemWindow?.AddChild(popup.Widget);

            popup.Widget.Closed += (s, e) =>
            {
                Console.WriteLine();
            };

            popup.Widget.Focus();

            popup.Widget.Invalidate();
        }