diff --git a/docs/bindings/keys.md b/docs/bindings/keys.md index b90fd0f65..5fc1f936f 100644 --- a/docs/bindings/keys.md +++ b/docs/bindings/keys.md @@ -120,6 +120,7 @@ bindr=Super,Super_L,spawn,rofi -show run | `focuslast` | - | Focus the previously active window. | | `exchange_client` | `left/right/up/down` | Swap window with neighbor in direction. | | `exchange_stack_client` | `next/prev` | Exchange window position in stack. | +| `move_client` | `left/right/up/down` | Move window to direction. | | `zoom` | - | Swap focused window with Master. | ### Group diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 0db72390a..73382cd95 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1216,6 +1216,9 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value, } else if (strcmp(func_name, "exchange_client") == 0) { func = exchange_client; (*arg).i = parse_direction(arg_value); + } else if (strcmp(func_name, "move_client") == 0) { + func = move_client; + (*arg).i = parse_direction(arg_value); } else if (strcmp(func_name, "exchange_stack_client") == 0) { func = exchange_stack_client; (*arg).i = parse_circle_direction(arg_value); diff --git a/src/dispatch/bind_declare.h b/src/dispatch/bind_declare.h index 7242bf1b7..a2277a5d6 100644 --- a/src/dispatch/bind_declare.h +++ b/src/dispatch/bind_declare.h @@ -37,6 +37,7 @@ void setmfact(const Arg *arg); void quit(const Arg *arg); void moveresize(const Arg *arg); void exchange_client(const Arg *arg); +void move_client(const Arg *arg); void exchange_stack_client(const Arg *arg); void killclient(const Arg *arg); void toggleglobal(const Arg *arg); diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index ab54852e9..ec3cb2ed0 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -111,6 +111,38 @@ void exchange_client(const Arg *arg) { return; } +void move_client(const Arg *arg) { + if (!selmon) + return; + + Client *c = arg->tc ? arg->tc : selmon->sel; + + if (!c || c->isfloating) { + return; + } else if ((c->isfullscreen || c->ismaximizescreen) && + !is_scroller_layout(c->mon)) { + return; + } + + Client *tc = direction_select(arg); + tc = get_focused_stack_client(tc, arg->tc); + + uint32_t layout_id = + (!tc ? dirtomon(arg->i)->pertag->ltidxs[c->mon->pertag->curtag]->id + : tc->mon->pertag->ltidxs[c->mon->pertag->curtag]->id); + + if (layout_id == DWINDLE) { + dwindle_move_client(c, tc, config.dwindle_split_ratio, arg->i, false); + arrange(c->mon, false, false); + } else { + move_two_client(c, tc, arg->i); + } + + warp_cursor(c); + + return; +} + void exchange_stack_client(const Arg *arg) { if (!selmon) return; diff --git a/src/layout/dwindle.h b/src/layout/dwindle.h index f08652fb4..56763035b 100644 --- a/src/layout/dwindle.h +++ b/src/layout/dwindle.h @@ -316,16 +316,77 @@ static void dwindle_assign(DwindleNode *node, int32_t ax, int32_t ay, } } -static void dwindle_move_client(DwindleNode **root, Client *c, Client *target, - float ratio, int32_t dir) { - if (!c || !target || c == target) +static void dwindle_move_client(Client *c1, Client *c2, float ratio, + int32_t dir, bool lock) { + + if (!c1 || c1 == c2) + return; + + Monitor *c_mon = c1->mon; + Monitor *t_mon = ((c2 == NULL) ? dirtomon(dir) : c2->mon); + + uint32_t move_dir = + (t_mon->m.x > c_mon->m.x) * RIGHT | (t_mon->m.x < c_mon->m.x) * LEFT | + (t_mon->m.y > c_mon->m.y) * UP | (t_mon->m.y < c_mon->m.y) * DOWN; + + if ((!config.exchange_cross_monitor || move_dir != dir) && c_mon != t_mon) { + return; + } + + if (c_mon != t_mon) { + c1->mon = t_mon; + t_mon->sel = c1; + selmon = t_mon; + + arrange(c_mon, false, false); + arrange(t_mon, false, false); + } + + DwindleNode **root; + DwindleNode *c_leaf; + DwindleNode *t_leaf; + + uint32_t tag = t_mon->pertag->curtag; + root = &t_mon->pertag->dwindle_root[tag]; + c_leaf = dwindle_find_leaf(*root, c1); + t_leaf = dwindle_find_leaf(*root, c2); + + if (!c_leaf) { + return; + } + + if (c_leaf->parent && c_leaf->parent == t_leaf->parent) { + if ((move_dir == RIGHT || move_dir == UP) || t_mon == c_mon) { + DwindleNode *p = c_leaf->parent; + DwindleNode *tmp = p->first; + p->first = p->second; + p->second = tmp; + } + return; - if (!dwindle_find_leaf(*root, c) || !dwindle_find_leaf(*root, target)) + } + + if (!c2) return; - dwindle_remove(root, c); - bool as_first = (dir == UP || dir == LEFT); + bool split_h = (dir == LEFT || dir == RIGHT); - dwindle_insert(root, c, target, ratio, as_first, split_h, true); + + bool as_first; + if (dir == LEFT || dir == RIGHT) { + int cy = c1->geom.y + c1->geom.height / 2; + int ty = c2->geom.y + c2->geom.height / 2; + as_first = (cy < ty); + } else { + as_first = (dir == UP); + } + + if (t_mon == c_mon || (move_dir == RIGHT || move_dir == LEFT)) { + dwindle_remove(root, c1); + dwindle_insert(root, c1, c2, ratio, as_first, split_h, lock); + } else { + dwindle_remove(root, c2); + dwindle_insert(root, c2, c1, ratio, as_first, split_h, lock); + } } static void dwindle_swap_clients(Client *c1, Client *c2) { @@ -677,4 +738,4 @@ void dwindle(Monitor *m) { void cleanup_monitor_dwindle(Monitor *m) { for (uint32_t t = 0; t < (uint32_t)config.tag_num + 1; t++) dwindle_free_tree(m->pertag->dwindle_root[t]); -} \ No newline at end of file +} diff --git a/src/mango.c b/src/mango.c index b8b1e1e6c..3de8bf895 100644 --- a/src/mango.c +++ b/src/mango.c @@ -797,6 +797,7 @@ static void motionrelative(struct wl_listener *listener, void *data); static void reset_foreign_tolevel(Client *c, Monitor *oldmon, Monitor *newmon); static void add_foreign_topleve(Client *c); static void exchange_two_client(Client *c1, Client *c2); +static void move_two_client(Client *c1, Client *c2, int32_t dir); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int32_t test); @@ -970,8 +971,8 @@ static void client_pending_maximized_state(Client *c, int32_t ismaximized); static void client_pending_minimized_state(Client *c, int32_t isminimized); static void scroller_insert_stack(Client *c, Client *target_client, bool insert_before); -static void dwindle_move_client(DwindleNode **root, Client *c, Client *target, - float ratio, int32_t dir); +static void dwindle_move_client(Client *c1, Client *c2, float ratio, + int32_t dir, bool lock); static void dwindle_resize_client_step(Monitor *m, Client *c, int32_t dx, int32_t dy); static void dwindle_resize_client(Monitor *m, Client *c); @@ -6123,6 +6124,55 @@ void exchange_two_client(Client *c1, Client *c2) { finish_exchange_arrange_and_focus(c1, c2, m1, m2); } +static void move_two_client(Client *c1, Client *c2, int32_t dir) { + + if (!c1 || c1 == c2) { + return; + } + + Monitor *c_mon = c1->mon; + Monitor *t_mon = ((c2 == NULL) ? dirtomon(dir) : c2->mon); + + uint32_t move_dir = + (t_mon->m.x > c_mon->m.x) * RIGHT | (t_mon->m.x < c_mon->m.x) * LEFT | + (t_mon->m.y > c_mon->m.y) * UP | (t_mon->m.y < c_mon->m.y) * DOWN; + + if ((!config.exchange_cross_monitor || move_dir != dir) && c_mon != t_mon) { + return; + } + + if (c_mon != t_mon) { + c1->mon = t_mon; + t_mon->sel = c1; + selmon = t_mon; + + arrange(c_mon, false, false); + arrange(t_mon, false, false); + } + + if (c2 != NULL) { + + const Layout *layout = c2->mon->pertag->ltidxs[c2->mon->pertag->curtag]; + + if (layout->id == SCROLLER) { + exchange_two_scroller_clients(c1, c2); + return; + } + + wl_list_remove(&c1->link); + + if (((dir == LEFT || dir == UP) && c_mon == t_mon) || + ((move_dir == RIGHT || move_dir == UP) && c_mon != t_mon)) { + wl_list_insert(c2->link.prev, &c1->link); + } else { + wl_list_insert(&c2->link, &c1->link); + } + } + + arrange(c_mon, false, false); + arrange(t_mon, false, false); +} + void set_activation_env() { if (!getenv("DBUS_SESSION_BUS_ADDRESS")) { mango_error(true, WLR_INFO,