Skip to content

Commit

Permalink
Add command: Move shuffle [flags] direction(s)
Browse files Browse the repository at this point in the history
Extend the Move command to be able to shuffle the window to the closest
window (it is not currently touching) in a given direction. The flags
can be used to make it consider either windows or icons, and which layer(s)
the windows must be on. Closes #540
  • Loading branch information
somiaj authored and ThomasAdam committed Jun 23, 2021
1 parent 7360272 commit c3e8d23
Show file tree
Hide file tree
Showing 3 changed files with 295 additions and 40 deletions.
125 changes: 87 additions & 38 deletions doc/fvwm3/fvwm3.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4052,27 +4052,22 @@ AddToFunc lower-to-bottom
+ I Lower
....

*Move* [[screen _screen_] [w | m]_x_[p | w] ... [w | m]_y_[p | w] ... [Warp]] | [pointer] | [ewmhiwa]::
Allows the user to move a window. If called from somewhere in a window
or its border, then that window is moved. If called from the root
window then the user is allowed to select the target window. By
default, the EWMH working area is honoured.
+
If the literal option _screen_ followed by a _screen_ argument is
specified, the coordinates are interpreted as relative to the given
screen. The width and height of the screen are used for the
calculations instead of the display dimensions. The _screen_ as
interpreted as in the *MoveToScreen* command. If the optional argument
_Warp_ is specified the pointer is warped with the window. If the
single argument _pointer_ is given, the top left corner of the window
is moved to the pointer position before starting the operation; this
is mainly intended for internal use by modules like *FvwmPager*. If
the optional argument _ewmhiwa_ is given, then the window position
will ignore the working area (such as ignoring any values set via
*EwmhBaseStruts*).
+
The operation can be aborted with
+
*Move* [_options_]::
Allows the user to move a window. If called from somewhere in a
window or its border, then that window is moved. If called from
the root window, then the user is allowed to select the target
window. Move can be called with various options to either start
an interactive move, specify the position to move, or a direction.
+
Calling *Move* by itself with no options starts an interactive move.
When moving a window interactively, the window may snap to other windows
and screen boundaries, configurable via the *SnapAttraction* style. Holding
down _Alt_ whilst moving the window will disable snap attraction during
the move. Moving a window to the edge of the screen can be used to drag the
window to other pages, see *EdgeScroll*, and the *EdgeMoveDelay* style
for more information.
+
The interactive move operation can be aborted with Escape
or any mouse button not set to place the window. By default mouse
button 2 is set to cancel the move operation. To change this you may
use the *Mouse* command with special context 'P' for Placement.
Expand All @@ -4081,18 +4076,75 @@ The window condition _PlacedByButton_ can be used to check if a
specific button was pressed to place the window (see *Current*
command).
+
If the optional arguments _x_ and _y_ are provided, then the window is
moved immediately without user interaction. Each argument can specify
an absolute or relative position from either the left/top or
right/bottom of the screen. By default, the numeric value given is
interpreted as a percentage of the screen width/height, but a trailing
'_p_' changes the interpretation to mean pixels, while a trailing
'_w_' means precent of the window width/height. To move the window
relative to its current position, add the '_w_' (for "window") prefix
before the _x_ and/or _y_ value. To move the window to a position
relative to the current location of the pointer, add the '_m_' (for
"mouse") prefix. To leave either coordinate unchanged, "_keep_" can be
specified in place of _x_ or _y_.
If the single argument _pointer_ is given, the top left corner of the
window is moved to the pointer position before starting an interactive
move; this is mainly intended for internal use by modules like *FvwmPager*.
+

> *Move* pointer

+
To move a window in a given direction until it hits another window, icon,
or screen boundary use:
+

> *Move* shuffle [Warp] [snap _type_] [layers _min_ _max_] _direction_(s)

+
The _direction_ can be _North_/_N_/_Up_/_U_, _East_/_E_/_Right_/_R_,
_South_/_S_/_Down_/_D_, or _West_/_W_/_Left_/_L_. The window will move
in the given direction until it hits another window, the *EwmhBaseStruts*,
or the screen boundary. If multiple _direction_(s) are given, the window
will move the directions in the order of the sequence stated.
+
The literal option _Warp_ will warp the mouse pointer to the window.
If the literal option _snap_ followed by a snap _type_ of _windows_,
_icons_, or _same_ is given, then the window will only stop if it hits
another window, icon, or the same type. If the literal option _layers_
followed by a _min_ layer and _max_ layer is given, then only windows on
the layers between _min_ and _max_ layers will stop the window. For example:
+

....
# Shuffle the window Right.
Move shuffle Right
# Shuffle Up, only consider windows on Layer 3.
Move shuffle layers 3 3 Up
# Shuffle Left then Up
Move shuffle Left Up
# Shuffle Up then Left (may not be same position as above)
Move shuffle Up Left
....

+
*Move* can be used to moved a window to a specified position:
+

> *Move* [screen _S_] \[w | m]_x_[p | w] \[w | m]_y_[p | w] [Warp] [ewmhiwa]

+
This will move the window to the _x_ and _y_ position (see below).
By default, the EWMH working area is honoured. If he trailing option
_ewmhiwa_ is given, then the window position will ignore the working
area (such as ignoring any values set via *EwmhBaseStruts*). If the
option _Warp_ is given then the pointer is warped to the window.
+
If the literal option _screen_ followed by a RandR screen name _S_ is
specified, the coordinates are interpreted as relative to the given
screen. The width and height of the screen are used for the
calculations instead of the display dimensions. The _screen_ is
interpreted as in the *MoveToScreen* command.
+
The positional arguments _x_ and _y_ can specify an absolute or relative
position from either the left/top or right/bottom of the screen. By default,
the numeric value given is interpreted as a percentage of the screen
width/height, but a trailing '_p_' changes the interpretation to mean pixels,
while a trailing '_w_' means percent of the window width/height. To move the
window relative to its current position, add the '_w_' (for "window") prefix
before the _x_ and/or _y_ value. To move the window to a position relative to
the current location of the pointer, add the '_m_' (for "mouse") prefix. To
leave either coordinate unchanged, "_keep_" can be specified in place of
_x_ or _y_.
+
For advanced uses, the arguments _x_ and _y_ can be used multiple
times, but without the prefix '_m_' or '_w_'. (See complex examples
Expand Down Expand Up @@ -4136,16 +4188,13 @@ Move 40p w-10p
Move m+0 m+0
# Move window to center of screen (50% of screen
# poition minus 50% of widow size).
# position minus 50% of widow size).
Move 50-50w 50-50w
....

+
Note: In order to obtain moving windows which do not snap to screen,
with interactive move, hold down _Alt_ whilst moving the window to
disable snap attraction if it's defined.
+
See also the *AnimatedMove* command.
+

*MoveToDesk* [prev | _arg1_ [_arg2_] [_min_ _max_]]::
Moves the selected window to another desktop. The arguments are the
Expand Down
4 changes: 2 additions & 2 deletions fvwm/geometry.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,10 +1408,10 @@ void get_page_offset_check_visible(
{
get_page_offset(ret_page_x, ret_page_y, fw);
}

#if 0
fprintf(stderr, "%s: MON: %s {page_x: %d, page_y: %d}\n",
__func__, fw->m->si->name, *ret_page_x, *ret_page_y);

#endif
return;
}

Expand Down
206 changes: 206 additions & 0 deletions fvwm/move_resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,204 @@ static int ParsePositionArgumentSuffix(
return n;
}

/* Functions to shuffle windows to the closest boundary. */
static void grow_bound_to_next_monitor(
FvwmWindow *fw, rectangle *bound, rectangle win_r, direction_t dir)
{
int page_x, page_y;
struct monitor *m;

get_page_offset_check_visible(&page_x, &page_y, fw);
bound->x -= page_x;
bound->y -= page_y;
win_r.x -= page_x;
win_r.y -= page_y;

TAILQ_FOREACH(m, &monitor_q, entry)
{
if (fw->m == m)
continue;

if (dir == DIR_N && m->si->y + m->si->h == fw->m->si->y &&
win_r.x < m->si->x + m->si->w &&
win_r.x + win_r.width > m->si->x)
{
bound->y = m->si->y + m->ewmhc.BaseStrut.top;
bound->height = win_r.y + win_r.height - bound->y;
}
else if (dir == DIR_E && m->si->x == fw->m->si->x +
fw->m->si->w && win_r.y < m->si->y + m->si->h &&
win_r.y + win_r.height > m->si->y)
{
bound->width = m->si->x + m->si->w - bound->x -
m->ewmhc.BaseStrut.right;
}
else if (dir == DIR_S && m->si->y == fw->m->si->y +
fw->m->si->h && win_r.x < m->si->x + m->si->w &&
win_r.x + win_r.width > m->si->x)
{
bound->height = m->si->y + m->si->h - bound->y -
m->ewmhc.BaseStrut.bottom;
}
else if (dir == DIR_W && m->si->x + m->si->w == fw->m->si->x &&
win_r.y < m->si->y + m->si->h &&
win_r.y + win_r.height > m->si->y)
{
bound->x = m->si->x + m->ewmhc.BaseStrut.left;
bound->width = win_r.x + win_r.width - bound->x;
}
}
bound->x += page_x;
bound->y += page_y;
}
static void shuffle_win_to_closest(
FvwmWindow *fw, char **action, int *pFinalX, int *pFinalY, Bool *fWarp)
{
direction_t dir;
rectangle cwin, bound;
char *naction, *token = NULL;
int page_x, page_y, n;
int snap = SNAP_NONE;
int layers[2] = { -1, -1 };

cwin = fw->g.frame;
get_page_offset_check_visible(&page_x, &page_y, fw);

token = PeekToken(*action, &naction);
/* Get flags */
while (token)
{
if (StrEquals(token, "snap"))
{
*action = naction;
token = PeekToken(*action, &naction);
if (StrEquals(token, "windows"))
snap = SNAP_WINDOWS;
else if (StrEquals(token, "icons"))
snap = SNAP_ICONS;
else if (StrEquals(token, "same"))
snap = SNAP_SAME;
}
else if (StrEquals(token, "layers"))
{
*action = naction;
n = GetIntegerArguments(*action, &naction, layers, 2);
if (n != 2)
{
layers[0] = -1;
layers[1] = -1;
}
}
else if (StrEquals(token, "Warp") && fWarp != NULL)
{
*fWarp = true;
}
else
{
break;
}
*action = naction;
token = PeekToken(*action, &naction);

}

/* Get direction(s) */
while (token)
{
dir = gravity_parse_dir_argument(
*action, &naction, DIR_NONE);

switch (dir)
{
case DIR_N:
bound.x = cwin.x;
bound.y = fw->m->si->y + page_y;
if (cwin.y - bound.y > fw->m->ewmhc.BaseStrut.top)
bound.y += fw->m->ewmhc.BaseStrut.top;
bound.width = cwin.width;
bound.height = cwin.y + cwin.height - bound.y;
if (cwin.y <= bound.y)
{
grow_bound_to_next_monitor(
fw, &bound, cwin, DIR_N);
}
grow_to_closest_type(fw, &cwin, bound, layers,
snap, false);
cwin.height = fw->g.frame.height;
break;
case DIR_E:
bound.x = cwin.x;
bound.y = cwin.y;
bound.width = fw->m->si->x + fw->m->si->w -
bound.x + page_x;
if (bound.x + bound.width - cwin.x - cwin.width >
fw->m->ewmhc.BaseStrut.right)
{
bound.width -= fw->m->ewmhc.BaseStrut.right;
}
bound.height = cwin.height;
if (cwin.x + cwin.width >= bound.x + bound.width)
{
grow_bound_to_next_monitor(
fw, &bound, cwin, DIR_E);
}
grow_to_closest_type(fw, &cwin, bound, layers,
snap, false);
cwin.x = cwin.x + cwin.width - fw->g.frame.width;
cwin.width = fw->g.frame.width;
break;
case DIR_S:
bound.x = cwin.x;
bound.y = cwin.y;
bound.width = cwin.width;
bound.height = fw->m->si->y + fw->m->si->h -
bound.y + page_y;
if (bound.y + bound.height - cwin.y - cwin.height >
fw->m->ewmhc.BaseStrut.bottom)
{
bound.height -= fw->m->ewmhc.BaseStrut.bottom;
}
if (cwin.y + cwin.height >= bound.y + bound.height)
{
grow_bound_to_next_monitor(
fw, &bound, cwin, DIR_S);
}
grow_to_closest_type(fw, &cwin, bound, layers,
snap, false);
cwin.y = cwin.y + cwin.height - fw->g.frame.height;
cwin.height = fw->g.frame.height;
break;
case DIR_W:
bound.x = fw->m->si->x + page_x;
if (cwin.x - bound.x > fw->m->ewmhc.BaseStrut.left)
bound.x += fw->m->ewmhc.BaseStrut.left;
bound.y = cwin.y;
bound.width = cwin.y + cwin.width - bound.x;
bound.height = cwin.height;
bound.height = cwin.y + cwin.height - bound.y;
if (cwin.x <= bound.x)
{
grow_bound_to_next_monitor(
fw, &bound, cwin, DIR_W);
}
grow_to_closest_type(fw, &cwin, bound, layers,
snap, false);
cwin.width = fw->g.frame.width;
break;
case DIR_NONE:
/* No direction found, need to move to next token */
token = PeekToken(*action, &naction);
break;
default:
break;
}
*action = naction;
token = PeekToken(*action, &naction);
}
*pFinalX = cwin.x;
*pFinalY = cwin.y;
}

static int __get_shift(int val, float factor)
{
int shift;
Expand Down Expand Up @@ -460,6 +658,14 @@ int GetMoveArguments(FvwmWindow *fw,
free(s1);
return 0;
}
if (s1 && StrEquals(s1, "shuffle"))
{
free(s1);
shuffle_win_to_closest(fw, &action, pFinalX, pFinalY, fWarp);
*paction = action;
return 2;

}
if (s1 && StrEquals(s1, "screen"))
{
fscreen_scr_arg parg;
Expand Down

0 comments on commit c3e8d23

Please sign in to comment.