-
Notifications
You must be signed in to change notification settings - Fork 770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add reverse layouts #4246
Add reverse layouts #4246
Conversation
5becf3f
to
920549b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your PR.
On first impression, this seems to be well thought-out and of high quality. Of course we will need to go through some iterations and I will probably try to run this on my machine as well.
Some first thoughts:
- How does this affect
floating disable
. I.e. when a floating window has to be moved in a "reverse" split, does it get positioned on the correct place? - How does this affect moving? I.e. with
H [ Vr [ A B ] C ]
what happens if you move C left?
Good question! I haven't done any testing with floats. I'll take a look.
This should be unaffected as moving (in EDIT: On a closer look, there are cases where |
a381bb1
to
12020d9
Compare
I've gone over the floating disable code, done some minor tweaking, and added a few testcases. Should work as expected now.
I've extended the move code a bit to better accommodate reverse layouts. A couple of testcases where added for good measure. PTAL! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To reduce the number of ternaries in multiple parts of the code, WDYT about adding some helper functions in src/util.c
?
position_t position_maybe_reverse(position_t position, layout_fill_t order) {
if (order == LF_DEFAULT) {
return position;
} else {
return position == BEFORE ? AFTER : BEFORE;
}
}
direction_t direction_maybe_reverse(direction_t direction, layout_fill_t order) {
if (order == LF_DEFAULT) {
return direction;
}
switch (direction) {
case D_LEFT:
return D_RIGHT;
case D_RIGHT:
return D_LEFT;
case D_UP:
return D_DOWN;
case D_DOWN:
return D_UP;
}
assert(false);
}
Thanks for taking a look. To be honest, I'm having some trouble figuring out how your suggestions are supposed to improve the code. Let me start with The way i3 windowing works is, it builds a tree of containers where the containers on the leaves of the tree are windows. i3 then takes this tree and arranges the windows on the screen based on their position within the tree. With this model in mind, there are two ways I can implement reverse layouts here. I can either 1) change how containers get placed into the tree, or 2) change how windows get arranged on the screen. Option 2 is also called layout. I have implemented option number 1 in this PR, since changing window layouting sounded harder to implement and more difficult to test. Now, remember that despite having implemented option 1, the way reverse layouts are exposed to the users is as "layout fill order" and as the word "layout" alludes to, these config options and commands describe implementation option 2 above. This means that every time a reverse layout is relevant for tree manipulation, I need to translate the layout fill order option to the appropriate tree operation. This seems to be the idea behind All of this is a long-winded way of saying that the suggested signature of tree_position_t position_maybe_reverse(layout_position_t position, layout_fill_t order) but that would require me creating new types and I don't think that's worth it for the few times this function is relevant. Sorry, I also don't see where I could use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, I think you overestimated the applicability of my comment, it's just a low level abstraction to remove some of the complexity from conditionals in the new code. I've highlighted some lines where it seems that you can use it.
By all means, if you think that it does not make the code more readable, you don't need to integrate them in your implementation.
12020d9
to
7e54950
Compare
I've factored out the complicated assignment, though I wasn't able to find a way to further simplify it, the suggested functions didn't really help. I think the other assignments are fine as they are. |
The layout fill order allows to configure how Cons are attached to a new layout. The value 'default' keeps the traditional order, filling the layout from left to right, or top lo bottom depending on horizontal or vertical orientation. 'reverse' in turn means to fill a layout from right to left, or bottom to top. This commit does not yet add the behaviour described above, but merely extends data structures to accommodate for this new option. We only store a single fill order per Con and not any additional values for the workspace layout and last split layout as those variables are necessary to deal with the default layout which does not apply to the fill order.
The tree representation was updated to support the new reverse layouts in the form of 'Vr[]' (N.B. the 'r' suffix). cmp_tree() needs to be updated to work with those extended strings.
This enables the layout fill order to be saved using i3-save-tree and restored using attach_layout.
This adds support to specify the default layout fill order for workspaces. workspace_layout receives an additional optional argument to set layout fill order to reverse.
Calling 'layout <layout> reverse' allows to change the current layout to a new layout with reverse fill order.
Allows splitting the current con into all directions by creating new horizontal or vertical splits with corresponding layout fill order. This is accomplished by changing tree_split() to accept a direction_t instead of just orientation_t. Also convert 543-layout-reverse.t to use subtests to improve structure and output readability.
With the newly introduced reverse layouts, new windows can appear on the opposite side of where the split indicator used to be drawn. This updates the drawing code to draw the indicator closer to new windows.
7e54950
to
6e3e1eb
Compare
I just rebased and would like to request that the maintainer take a look again. Is there anything major that needs changing? I've put quite a bit of effort into coming up with sensible test cases, so at this point I'm pretty confident that this PR does what it says on the tin. Of course, we can discuss if it is the right tin in the first place ;) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
I will try to use it a bit tonight/tomorrow and hopefully provide more helpful feedback ;)
This was a relatively complicated assignment repeated twice. Factor it out into its own function.
6e3e1eb
to
e4c68b0
Compare
e4c68b0
to
88ea3a4
Compare
@orestisfl Did you have time to try it out? @wlhlm just pinged me about this on reddit regarding the new feature policy, and I think given the work put into this already we should continue here unchanged. |
@Airblader I agree. I just installed the patch. Sorry for the delays. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your hard work!
I think it's time to write the user documentation.
@@ -1565,6 +1571,8 @@ void cmd_layout(I3_CMD, const char *layout_str) { | |||
|
|||
DLOG("matching: %p / %s\n", current->con, current->con->name); | |||
con_set_layout(current->con, layout); | |||
if (reverse != NULL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use curly braces for new code (here and everywhere)
* bottom-to-top. 'toggle' inverts the setting depending on its previous value. | ||
* | ||
*/ | ||
void con_set_layout_fill_order(Con *con, const char *fill_order) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a huge fan of this function being in con.c
since it takes a char *
as an argument. I would make it a static
inside commands.c
.
parent = con->parent; | ||
DLOG("con_set_fill_order(%p, %s), parent = %p\n", con, fill_order, parent); | ||
|
||
if (strcasecmp(fill_order, "default") == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also make the function accept NULL
?
if (strcasecmp(fill_order, "default") == 0) { | |
if (fill_order == NULL || strcasecmp(fill_order, "default") == 0) { |
Any further update on this PR? @wlhlm are you still with us 🙏 ? Tbh this glaring missing feature of I3 is the ONLY thing that keeps me (and I'm very sure many others) from switching from Bspwm over to I3. Can some good soul make the extra effort to push this through the finish line? |
I would feel absolutely happy (privileged even!) to help get this PR across the line. Given it seems @wlhlm may not be available/around the project as of late, I've merged the great work in his right-to-left-layouts branch into my fork and have brought it up to date with the latest i3:next. @orestisfl, I've also implemented your requested changes in #4246 (review). Please see next...jtaala:right-to-left-layouts for @wlhlm's work (and my small changes implementing @orestisfl's requested changes) diff'd with latest i3:next. I've been testing @wlhlm's work in my fork and have found no issues thus far, and I've also discovered that I really like right-to-left/bottom-to-top behaviour much better (just my preference). Given the hard work of @wlhlm and the quality of his work, plus the evident usefulness of the feature - I would be happy to help write the documentation now to get it into i3:next. @orestisfl, @Airblader, (and @wlhlm if you're around) - would you be happy with me picking up and progressing this? Would likely mean once done, I would need to create a new PR (fully referencing this one of course). Please see new PR with my (small) changes and documentation added here #4794. |
This is an implementation of #1767 (allow splitting to the left/top) and addresses the underlying desire to have layouts that are filled in from right to left and bottom to top.
I have dubbed this the "layout fill order" which has two values "default", and "reverse", where the former denotes the old behavior, where new windows get added on the right in horizontal layouts and on the bottom in vertical layouts. The latter reverses this order: windows get added to the left in horizontal layouts and to the top in vertical layouts.
I have had a tough time coming up with an actual name for this functionality, not sure if "layout fill order" is intuitive for everybody, so I'm open to suggestions.
Closes #1767.
Implementation
The discussion in #1767 suggested adding additional layouts that are populated in reverse (by extending
layout_t
), but I think that begs a larger refactoring as in many places, the normal layout is equivalent with its reverse counterpart (let's sayL_SPLITH
andL_SPLITH_REVERSE
), requiring layout checks to be doubled fromlayout == L_SPLITH
tolayout == L_SPLITH || layout == L_SPLITH_REVERSE
.To me, the way how a layout is populated with new windows is orthogonal to the layout itself. On a lower level, all the fill order does, is affect whether a new child node gets prepended or appended to the list of existing child nodes of a parent container, regardless of the actual layout itself. My solution is to store this in an additional property side-by-side with the layout. This let's me keep existing layout checks intact and only requires updating the few places that care about the fill order (mainly
con_attach()
).User-facing changes
split
was extended to accept the argumentsleft
,right
,up
, anddown
split right
andsplit down
are aliases forsplit h
andsplit v
respectivelysplit left
creates a horizontal split with reverse fill order so that the next window gets attached to the leftsplit up
is similar, but creates a vertical reverse splitworkspace_layout <layout>
now accepts an additional argumentreverse
to set the default fill order for workspaces to reverselayout <default|stacked|stacking|tabbed|splitv|splith>
received an optional argumentreverse
layout splith reverse
changes the layout to splith with a reversed fill order (new window will get inserted before focused window instead of after).layout fill_order [default|reverse|toggle]
allows changing the fill order independent of the layoutVr[]
for a vertical split with reverse fill order.TODO
Currently, documentation is missing completely as that makes only sense to add when the maintainers are happy with the implementation.