예제 #1
0
        bool MoveWindow(IntPtr window, Direction moveDir, MoveType moveType)
        {
            if (window == null)
            {
                return(false);
            }

            // Get minimized/maximized state
            WINDOWPLACEMENT placement;

            GetWindowPlacement(window, out placement);
            bool isMaximized = placement.showCmd == ShowCmd.Maximize;
            bool isMinimized = placement.showCmd == ShowCmd.Minimize;

            // Get window rect
            RECT windowRect, clientRect;

            GetWindowRect(window, out windowRect);
            GetClientRect(window, out clientRect);

            // Find closest rect to current position
            Section closestSection = null;
            float   closestOverlap = -1f;
            RECT    testRect       = isMinimized ? placement.rcNormalPosition : windowRect;

            foreach (var section in Sections)
            {
                float overlap = RectangleOverlapRatio(testRect, section.rect);

                if (overlap > closestOverlap ||
                    (overlap == closestOverlap && (
                         (moveDir == Direction.Left && section.rect.Left < closestSection.rect.Left) ||
                         (moveDir == Direction.Up && section.rect.Top < closestSection.rect.Top) ||
                         (moveDir == Direction.Right && section.rect.Right > closestSection.rect.Right) ||
                         (moveDir == Direction.Down && section.rect.Bottom > closestSection.rect.Bottom))))
                {
                    closestSection = section;
                    closestOverlap = overlap;
                }
            }

            Section nextSection = null;

            // IF window is not aligned with the closest section THEN fill that section
            bool fillClosest = !isMaximized && !isMinimized && moveType != MoveType.Extend && closestOverlap < .97f;

            if (fillClosest)
            {
                nextSection = closestSection;
            }
            else
            {
                // Window will be moving to a new section

                // Check for maximize
                if (moveDir == Direction.Up && !isMaximized && !isMinimized && (closestSection.rect.Top == closestSection.layout.top || RectangleOverlapRatio(testRect.AsRectangle(), closestSection.layout.screen.WorkingArea) > .9f))
                {
                    return(ShowWindowAsync(window, (int)ShowCmd.Maximize));
                }

                // Check for minimize
                if (moveDir == Direction.Down && !isMaximized && !isMinimized && closestSection.rect.Bottom == closestSection.layout.bottom)
                {
                    return(ShowWindowAsync(window, (int)ShowCmd.Minimize));
                }

                // Check for restore
                if ((moveDir == Direction.Up && isMinimized) || (moveDir == Direction.Down && isMaximized))
                {
                    return(ShowWindowAsync(window, (int)ShowCmd.Restore));
                }

                // Cache bounds of closestSection
                var closestBounds = closestSection.rect;
                if (isMaximized)
                {
                    // Treat maximized as a 0-height rect at top of working area
                    closestBounds        = new RECT(closestSection.layout.screen.WorkingArea);
                    closestBounds.Bottom = closestBounds.Top;
                }
                else if (isMinimized)
                {
                    // Treat minimized as a 0-height rect at bottom of working area
                    closestBounds     = new RECT(closestSection.layout.screen.WorkingArea);
                    closestBounds.Top = closestBounds.Bottom;
                }

                // Find best section
                Section bestSection        = null;
                int     bestOverlap        = int.MinValue;
                int     bestOffAxisOverlap = int.MinValue;

                foreach (var testSection in Sections)
                {
                    testRect = testSection.rect;

                    // Skip section where window is currently placed
                    if (testRect.AsRectangle() == closestBounds.AsRectangle())
                    {
                        continue;
                    }

                    bool isSameScreen = testSection.layout.screen == closestSection.layout.screen;

                    // Moving Up while maximized or Down while minimized means window MUST change screens
                    bool requireDifferentScreen = (isMaximized && moveDir == Direction.Up) || (isMinimized && moveDir == Direction.Down);
                    if (requireDifferentScreen && isSameScreen)
                    {
                        continue;
                    }

                    // Moving Left/Right while Maximized OR Minimized MUST stay on the same screen
                    bool requireSameScreen = (moveDir == Direction.Left || moveDir == Direction.Right) && (isMaximized || isMinimized);
                    if (requireSameScreen && !isSameScreen)
                    {
                        continue;
                    }

                    // TestRect must be in direction of moveDir. UNLESS we're in a requireSameScreen special case
                    if (!requireSameScreen)
                    {
                        if (moveDir == Direction.Up && testRect.Bottom > closestBounds.Top)
                        {
                            continue;
                        }
                        if (moveDir == Direction.Down && testRect.Top < closestBounds.Top)
                        {
                            continue;
                        }
                        if (moveDir == Direction.Right && testRect.Left < closestBounds.Right)
                        {
                            continue;
                        }
                        if (moveDir == Direction.Left && testRect.Right > closestBounds.Left)
                        {
                            continue;
                        }
                    }

                    int horizOverlap = OverlapAmount(closestBounds.Left, closestBounds.Right, testRect.Left, testRect.Right);
                    int vertOverlap  = OverlapAmount(closestBounds.Top, closestBounds.Bottom, testRect.Top, testRect.Bottom);

                    // Determine overlap along primary test axis and off axis.
                    bool vertDir        = moveDir == Direction.Up || moveDir == Direction.Down;
                    int  axisOverlap    = vertDir ? horizOverlap : vertOverlap;
                    int  offAxisOverlap = Clamp(vertDir ? vertOverlap : horizOverlap, int.MinValue, 0);

                    // We want the most overlap for our main axis
                    if (axisOverlap < bestOverlap)
                    {
                        continue;
                    }

                    // Tie breaker for same axisOverlap (happens all the time with grids)
                    if (bestSection != null && axisOverlap == bestOverlap)
                    {
                        var bestRect = bestSection.rect;

                        // Closest for up/down
                        if (moveDir == Direction.Up && bestRect.Bottom > testRect.Bottom)
                        {
                            continue;
                        }
                        if (moveDir == Direction.Down && bestRect.Top < testRect.Top)
                        {
                            continue;
                        }

                        if (isMaximized || isMinimized)
                        {
                            // Most left/right for left/right
                            if (moveDir == Direction.Left && bestRect.Left < testRect.Left)
                            {
                                continue;
                            }
                            if (moveDir == Direction.Right && bestRect.Right > testRect.Right)
                            {
                                continue;
                            }
                        }
                        else
                        {
                            // Closest for left/right
                            if (moveDir == Direction.Left && bestRect.Left > testRect.Left)
                            {
                                continue;
                            }
                            if (moveDir == Direction.Right && bestRect.Right < testRect.Right)
                            {
                                continue;
                            }
                        }
                    }

                    // And we want the closestOverlap on offAxis (which is probably negative)
                    if (offAxisOverlap < bestOffAxisOverlap)
                    {
                        continue;
                    }

                    bestSection        = testSection;
                    bestOverlap        = axisOverlap;
                    bestOffAxisOverlap = offAxisOverlap;
                }

                if (bestSection == null)
                {
                    return(false);
                }

                nextSection = bestSection;
            }


            // Move to the nextSection
            var nextRect = nextSection.rect;

            // Expact rect to account for border padding
            GetWindowRect(window, out windowRect);
            GetClientRect(window, out clientRect);
            int  pad        = (windowRect.Width - clientRect.Width) / 2 + nextSection.layout.adjustSize;
            int  xPos       = nextRect.Left - pad;
            int  yPos       = nextRect.Top;
            int  width      = nextRect.Width + 2 * pad;
            int  height     = nextRect.Height + pad;
            RECT screenRect = new RECT(xPos, yPos, xPos + width, yPos + height);

            // Check for Extend operation
            if (moveType == MoveType.Extend && nextSection.layout.screen == closestSection.layout.screen && !isMinimized && !isMaximized)
            {
                screenRect = windowRect.Extended(screenRect);
            }

            // Convert screen coords workspace coords
            var  screenBounds    = nextSection.layout.screen.Bounds;
            var  workspaceBounds = nextSection.layout.screen.WorkingArea;
            RECT workspaceRect   = screenRect;


            // Apply workspace offset
            int xOffset = workspaceBounds.Left - screenBounds.Left;
            int yOffset = workspaceBounds.Top - screenBounds.Top;

            workspaceRect.Left   -= xOffset;
            workspaceRect.Right  -= xOffset;
            workspaceRect.Top    -= yOffset;
            workspaceRect.Bottom -= yOffset;

            placement.rcNormalPosition = workspaceRect;
            placement.showCmd          = ShowCmd.Normal;
            bool result = SetWindowPlacement(window, ref placement);

            // Windows can't handle moving to a monitor with a different scale factor.
            // So see if SetWindowPlacement failed to the put window where we said to put it
            // If it did fail, then use SetWindowPos to set pos/size
            // Note: We don't use SetWindowPos all the time because SetWindowPos on a maximized window
            // either pops (ShowCmd.Normal) or we lose the maximize flag which screws up everything :(
            GetWindowRect(window, out windowRect);
            if ((windowRect.Width != screenRect.Width || windowRect.Height != screenRect.Height))
            {
                // Recalculate pad and associated values
                GetWindowRect(window, out windowRect);
                GetClientRect(window, out clientRect);
                pad        = (windowRect.Width - clientRect.Width) / 2 + nextSection.layout.adjustSize;
                xPos       = nextRect.Left - pad;
                yPos       = nextRect.Top;
                width      = nextRect.Width + 2 * pad;
                height     = nextRect.Height + pad;
                screenRect = new RECT(xPos, yPos, xPos + width, yPos + height);


                // Force window to normal (in-case it was minimized or maximized)
                ShowWindow(window, (int)ShowCmd.Normal);

                // Move window to correct position. Do not change size. Do not redraw.
                SetWindowPos(window, new IntPtr(), screenRect.Left, screenRect.Top, screenRect.Width, screenRect.Height, (uint)SWP_FLAGS.NO_SIZE | (uint)SWP_FLAGS.NO_REDRAW);

                // Change Window size. Do not move. Redraw.
                SetWindowPos(window, new IntPtr(), screenRect.Left, screenRect.Top, screenRect.Width, screenRect.Height, (uint)SWP_FLAGS.NO_MOVE);
            }

            return(true);
        }