Skip to content

Improve "Toggle raise and lower" functionality

Jarek Janik requested to merge yaro/kwin:improveToggleRaiseLower into master

This is a follow-up to MR 6504, aimed toward v6.3. The goal here is to avoid situations where user needs to trigger "Toggle Raise & Lower" (I'll call it TR&L from now on) twice to get what he expects.

In MR 6504 Vlad Zahorodnii has written:

For 6.2, I think that checking the unconstrained stack might be a safer option.

For 6.3, we could revise this further. However, I'd like to avoid touching topWindowOnDesktop() because it's already quite bloated and needs a cleanup. Given that only one function needs such behavior, raiseOrLowerWindow() could just walk the stack itself rather than call topWindowOnDesktop(). Once we have more users and better understanding of use-cases, one could DRY.

As a short reminder: current algorithm for TR&L:

  • lowers window when it's topmost (since 6.2 looking in "unconstrained" list)
  • raises window otherwise
  • never transfers focus

I'd like to propose something better (in terms of user experience); let's try to understand the problem thoroughly first, an example will be useful:

So, suppose someone brings you to his monitor and shows you the following window layout:

    +-------+               +-------+
    | A     |               | B     |
    |       +---------------+       |
    |       |               |       |
    +---+---+               +---+---+
        |                       |
        |                       |
        |           E           |
        |                       |
        |                       |
    +---+---+               +---+---+
    | C     |               | D     |
    |       +---------------+       |
    |       |               |       |
    +-------+               +-------+

What can you tell about the stacking order here? E is on the bottom, but you can't tell anything about how are A, B, C and D stacked relatively to each other.

Now, what do you expect to happen when you trigger TR&L on any of A, B, C or D? Obviously you expect a window in question to be lowered (because none of A, B, C and D can't be raised any further - sure, it can be moved within stacking_order list, but that doesn't change anything on the screen when you click TR&L for the first time). So - what we have basically learnt here is that it is not sufficient to operate on vertical stacking information only, we need to take into account the spatial placement of windows as well, if we want to implement TR&L correctly.

So, conceptually TR&L shall work (IMO) like this:

  • lower a window if it can't be raised (visually) any further
  • raise a window otherwise. To clarify first point - perhaps a window can be raised within stacking_order/unconstrained_stacking_order, but if that would not change anything on the screen, then it doesn't count.

The first approximation of such a condition would be:

  • window can't be raised visually if it is not obscured by any other window.

Nonetheless, there are 2 factors we need to take into account:

  1. Layers
  2. Constraints
    1. When it comes to layers - since no window can be shown above a window from a higher layer, then if window is only obscured by windows from higher layers then it can't be raised any further. In other words - we can ignore windows from higher layers when we are looking for windows that would obscure our window; and since windows from lower layers can't obviously obscure our one, then it is enough to only check windows in the layer that our window belongs to [rule 1].
    2. Constraints are more complicated. Obviously a window that is only obscured by own "child" would not be eligible for raising higher up; but suppose that there is a foreign window F that doesn't obscure "our" window A, but obscures any of its children (let such a child be: C) - in such a case A can still be raised, because C can be raised above F - which gives us a change on the screen (although the stacking position of A window itself remains visually unchanged). So this gives us the following rule: window is eligible for raising if any of its children (either direct or indirect) is obscured by an "unrelated" window [rule 2].

And that's it when it comes to the "idea". The implementation is in first commit of this MR (cff40ec2), it is pretty short (~100 lines) although the algorithm seems complicated; I've been using this patch on top of 5.27 for almost a year now without problems and I'm quite happy with how it works.

This MR lacks proper tests yet (I've got some, but they need some more work). I'd like that someone review it first and - if I'm told that this looks like something that could be merged than I'll be back with the missing tests.

Edited by Jarek Janik

Merge request reports

Loading