Skip to content

✨ Option to make Next/Previous display movement follow clockwise order #1000

Description

@Caffeine19

Bug Description

These are my display arrangements and the order I would expect when moving a window to the next / previous display in a clockwise direction, as illustrated below:

Image

In this layout, I expect the window to move between displays following the clockwise order shown by the numbered arrows.

Actual behavior

However, this is what actually happens:

Image

The window traversal order does not follow the physical clockwise arrangement of the displays and instead jumps between displays in an unintuitive order.

Affected Scope

User interface

Steps to Reproduce

  1. I think you may need at least 4 displays for test.
  2. Arrange displays in two lines.
  3. Move window to next display/previous display.

Reproducibility

Always

Expected vs Actual Behavior

Expected behavior

When moving a window to the next or previous display, the window should follow the physical display layout in a clockwise order.

Actual behavior

When moving a window to the next or previous display, the window moves between displays in a non-clockwise and unintuitive order.

Screen Recordings / Screenshots

No response

Severity

Critical (major functionality broken)

macOS Version

macOS 15.7.3

Loop Version

Version 🧪 1.4.1 (1673)

Did You Try the Development Build?

Yes

Additional Context

No response

Final Checks

  • My issue is written in English.
  • My issue title is descriptive.
  • This is a single bug (multiple bugs should be reported individually).
  • I have looked to see if this is a duplicate of another bug report.
  • I can help with further investigation.
  • I can help with developing a fix for this issue.

Activity

Caffeine19 commented on Jan 12, 2026

@Caffeine19
Author

I just tried Rectangle, and it also didn’t follow the clockwise order.
So maybe this order is provided by the system?

Caffeine19 commented on Jan 12, 2026

@Caffeine19
Author

Just find the code:
https://github.com/MrKai77/Loop/blob/8e82a047225289042faef2ce197bf78e06903be5/Loop/Utilities/ScreenUtility.swift#L96

   /// Sorts all NSScreens in an order such that the next/previous screen are in positional order.
    private static func getOrderedScreens() -> [NSScreen] {
        NSScreen.screens.sorted { screen1, screen2 in
            if screen2.frame.maxY <= screen1.frame.minY {
                return true
            }

            if screen1.frame.maxY <= screen2.frame.minY {
                return false
            }

            return screen1.frame.minX < screen2.frame.minX
        }
    }

Caffeine19 commented on Jan 12, 2026

@Caffeine19
Author

Or add another feature to let users manually sort their displays?

mrkai77 commented on Jan 12, 2026

@mrkai77
Owner

The behavior you’re seeing is actually intended; the screen switching roughly follows a “vertically stacked Z” shape. That said, I agree that sorting screens in a clockwise direction could be interesting. I'm thinking that it might make more sense to expose an advanced option that lets them choose how “Next/Previous Screen” determines the next screen: using a Z-shaped order, clockwise, or counter-clockwise.

Caffeine19 commented on Jan 12, 2026

@Caffeine19
Author

It would be great to have this feature. But I just wonder, if a user has 9 displays and they are arranged in a 3x3 layout, how would clockwise ordering work? I mean, how would the center display be handled?

mrkai77 commented on Jan 12, 2026

@mrkai77
Owner

I’d expect it would cycle through the outer displays clockwise first, then move on to the inner displays, again in a clockwise order :)

Caffeine19 commented on Jan 13, 2026

@Caffeine19
Author

By the way, I asked AI (please don't get mad for this) last night and this is his answer:

private static func getOrderedScreensClockwise() -> [NSScreen] {
    let screens = NSScreen.screens

    // 1. Compute the center point of each screen
    let centers = screens.map { screen -> CGPoint in
        CGPoint(
            x: screen.frame.midX,
            y: screen.frame.midY
        )
    }

    // 2. Compute the geometric center of the entire display layout
    let centerX = centers.map(\.x).reduce(0, +) / CGFloat(centers.count)
    let centerY = centers.map(\.y).reduce(0, +) / CGFloat(centers.count)
    let layoutCenter = CGPoint(x: centerX, y: centerY)

    // 3. Sort screens by their angular position around the layout center (clockwise)
    return screens.sorted { s1, s2 in
        let a1 = atan2(
            s1.frame.midY - layoutCenter.y,
            s1.frame.midX - layoutCenter.x
        )
        let a2 = atan2(
            s2.frame.midY - layoutCenter.y,
            s2.frame.midX - layoutCenter.x
        )

        // atan2 returns angles in counterclockwise order; reversing it yields clockwise traversal
        return a1 > a2
    }
}

I think it's a little complex, and I don't like math. Tried to test locally today, but the build failed because I don't have a developer account, probably.

Caffeine19 commented on Jan 21, 2026

@Caffeine19
Author

By the way, I asked AI (please don't get mad for this) last night and this is his answer:

private static func getOrderedScreensClockwise() -> [NSScreen] {
let screens = NSScreen.screens

// 1. Compute the center point of each screen
let centers = screens.map { screen -> CGPoint in
    CGPoint(
        x: screen.frame.midX,
        y: screen.frame.midY
    )
}

// 2. Compute the geometric center of the entire display layout
let centerX = centers.map(\.x).reduce(0, +) / CGFloat(centers.count)
let centerY = centers.map(\.y).reduce(0, +) / CGFloat(centers.count)
let layoutCenter = CGPoint(x: centerX, y: centerY)

// 3. Sort screens by their angular position around the layout center (clockwise)
return screens.sorted { s1, s2 in
    let a1 = atan2(
        s1.frame.midY - layoutCenter.y,
        s1.frame.midX - layoutCenter.x
    )
    let a2 = atan2(
        s2.frame.midY - layoutCenter.y,
        s2.frame.midX - layoutCenter.x
    )

    // atan2 returns angles in counterclockwise order; reversing it yields clockwise traversal
    return a1 > a2
}

}
I think it's a little complex, and I don't like math. Tried to test locally today, but the build failed because I don't have a developer account, probably.

Test locally, works great 😊

Caffeine19 commented on Jan 22, 2026

@Caffeine19
Author

@mrkai77
Should we add an option to let users choose between the previous “Z” order and the new clockwise order, or should we simply make the clockwise order the default?
If we add an option, should it be placed under Settings > Behavior > Display Order? We might also need two icons to represent the different orders.

mrkai77 commented on Jan 22, 2026

@mrkai77
Owner

Yes, it makes sense to expose this as an option for users while keeping the current behavior as the default. I think it would fit well in the Advanced tab, since it’s likely not something most users will need to change.

As for the code you attached above, it would work, but the main caveat is that it won’t handle doubly-layered (or higher-ordered) screen layouts, since it relies purely on angles rather than processing the outermost and then innermost layouts clockwise 😉

changed the title [-]🐞 Next/Previous display movement does not follow the clockwise order[/-] [+]✨ Option to make Next/Previous display movement follow clockwise order[/+] on Feb 5, 2026

mrkai77 commented on Feb 19, 2026

@mrkai77
Owner

Hey @Caffeine19! I haven’t had a chance to fully test this on my end yet either, but here’s a build that adds an option to navigate screens in a clockwise order:
https://github.com/MrKai77/Loop/actions/runs/22194367772/artifacts/5577914708

If you’re able to try it out, I’d really appreciate hearing whether it works as expected! You can find the setting under the Advanced tab :)

Caffeine19 commented on Feb 20, 2026

@Caffeine19
Author

Hey @Caffeine19! I haven’t had a chance to fully test this on my end yet either, but here’s a build that adds an option to navigate screens in a clockwise order: https://github.com/MrKai77/Loop/actions/runs/22194367772/artifacts/5577914708

If you’re able to try it out, I’d really appreciate hearing whether it works as expected! You can find the setting under the Advanced tab :)

Thanks, bro. I’ll try it later. Sorry, I’m on holiday and can’t get four displays to test. 😘

Caffeine19 commented on Feb 24, 2026

@Caffeine19
Author

Hey @Caffeine19! I haven’t had a chance to fully test this on my end yet either, but here’s a build that adds an option to navigate screens in a clockwise order: https://github.com/MrKai77/Loop/actions/runs/22194367772/artifacts/5577914708

If you’re able to try it out, I’d really appreciate hearing whether it works as expected! You can find the setting under the Advanced tab :)

Happy New Year 😘

Just tested with the new "Clockwise" option:

Image

It works great in this display layout:

Image

But still moves in Z-pattern in this display layout, which I actually is:

Image

mrkai77 commented on Oct 5, 2026

@mrkai77
Owner

Hey @Caffeine19! This is now merged in #1156 and will be in the next development build. You can switch to development builds from Loop’s About tab. Once you’ve updated, go to Advanced > Next/previous screen order and choose Clockwise :)

About the layout you mentioned last time, where it still moved in a Z pattern: the clockwise order is now worked out differently. Loop traces the outline of your screen arrangement clockwise and visits each screen the first time the outline reaches it. This should handle two-row layouts much better, including rows that are a few pixels out of line or that mix screen sizes. Please let us know if it still doesn’t follow the order you’d expect!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions