feat: support drag tile position for scroller layout

This commit is contained in:
DreamMaoMao 2026-05-06 11:51:13 +08:00
parent e20600b0d3
commit 7a950f54df
2 changed files with 173 additions and 61 deletions

View file

@ -1764,21 +1764,15 @@ int32_t toggle_monitor(const Arg *arg) {
return 0;
}
int32_t scroller_stack(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
Client *stack_head = NULL;
Client *source_stack_head = NULL;
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
int32_t scroller_apply_stack(Client *c, Client *target_client,
int32_t direction) {
Client *source_stack_head = NULL;
Client *stack_head = NULL;
bool is_horizontal_layout =
c->mon->pertag->ltidxs[c->mon->pertag->curtag]->id == SCROLLER ? true
: false;
Client *target_client = find_client_by_direction(c, arg, false, true);
if (target_client) {
stack_head = get_scroll_stack_head(target_client);
}
@ -1797,32 +1791,32 @@ int32_t scroller_stack(const Arg *arg) {
setmaximizescreen(c, 0);
}
if (c->prev_in_stack) {
if ((is_horizontal_layout && arg->i == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) {
if (c->prev_in_stack && direction != UNDIR) {
if ((is_horizontal_layout && direction == LEFT) ||
(!is_horizontal_layout && direction == UP)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(source_stack_head->link.prev, &c->link);
arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) {
} else if ((is_horizontal_layout && direction == RIGHT) ||
(!is_horizontal_layout && direction == DOWN)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(&source_stack_head->link, &c->link);
arrange(selmon, false, false);
}
return 0;
} else if (c->next_in_stack) {
} else if (c->next_in_stack && direction != UNDIR) {
Client *next_in_stack = c->next_in_stack;
if ((is_horizontal_layout && arg->i == LEFT) ||
(!is_horizontal_layout && arg->i == UP)) {
if ((is_horizontal_layout && direction == LEFT) ||
(!is_horizontal_layout && direction == UP)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(next_in_stack->link.prev, &c->link);
arrange(selmon, false, false);
} else if ((is_horizontal_layout && arg->i == RIGHT) ||
(!is_horizontal_layout && arg->i == DOWN)) {
} else if ((is_horizontal_layout && direction == RIGHT) ||
(!is_horizontal_layout && direction == DOWN)) {
exit_scroller_stack(c);
wl_list_remove(&c->link);
wl_list_insert(&next_in_stack->link, &c->link);
@ -1833,37 +1827,31 @@ int32_t scroller_stack(const Arg *arg) {
if (!target_client || target_client->mon != c->mon) {
return 0;
} else {
c->isglobal = target_client->isglobal = 0;
c->isunglobal = target_client->isglobal = 0;
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
}
exit_scroller_stack(c);
// Find the tail of target_client's stack
Client *stack_tail = target_client;
while (stack_tail->next_in_stack) {
stack_tail = stack_tail->next_in_stack;
}
// Add c to the stack
stack_tail->next_in_stack = c;
c->prev_in_stack = stack_tail;
c->next_in_stack = NULL;
scroller_insert_stack(c, stack_tail, false);
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(selmon, false, false);
return 0;
}
int32_t scroller_stack(const Arg *arg) {
if (!selmon)
return 0;
Client *c = selmon->sel;
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
return 0;
Client *target_client = find_client_by_direction(c, arg, false, true);
return scroller_apply_stack(c, target_client, arg->i);
}
int32_t toggle_all_floating(const Arg *arg) {
if (!selmon || !selmon->sel)
return 0;

View file

@ -822,6 +822,8 @@ static void pre_caculate_before_arrange(Monitor *m, bool want_animation,
static void client_pending_fullscreen_state(Client *c, int32_t isfullscreen);
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);
#include "data/static_keymap.h"
#include "dispatch/bind_declare.h"
@ -2064,33 +2066,155 @@ void hold_end(struct wl_listener *listener, void *data) {
event->time_msec, event->cancelled);
}
void place_drag_tile_client(Client *c) {
Client *tc = NULL;
Client *closest_client = NULL;
long min_distant = LONG_MAX;
long temp_distant;
int32_t x, y;
Client *find_closest_tiled_client(Client *c) {
Client *tc, *closest = NULL;
long min_dist = LONG_MAX;
wl_list_for_each(tc, &clients, link) {
if (tc != c && ISTILED(tc) && VISIBLEON(tc, c->mon)) {
x = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x;
y = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y;
temp_distant = x * x + y * y;
if (temp_distant < min_distant) {
min_distant = temp_distant;
closest_client = tc;
}
if (tc == c || !ISTILED(tc) || !VISIBLEON(tc, c->mon))
continue;
int32_t dx = tc->geom.x + (int32_t)(tc->geom.width / 2) - cursor->x;
int32_t dy = tc->geom.y + (int32_t)(tc->geom.height / 2) - cursor->y;
long dist = (long)dx * dx + (long)dy * dy;
if (dist < min_dist) {
min_dist = dist;
closest = tc;
}
}
if (closest_client && closest_client->link.prev != &c->link) {
wl_list_remove(&c->link);
c->link.next = &closest_client->link;
c->link.prev = closest_client->link.prev;
closest_client->link.prev->next = &c->link;
closest_client->link.prev = &c->link;
} else if (closest_client) {
exchange_two_client(c, closest_client);
return closest;
}
void scroller_insert_stack(Client *c, Client *target_client,
bool insert_before) {
Client *stack_head = NULL;
if (!target_client || target_client->mon != c->mon) {
return;
} else {
c->isglobal = target_client->isglobal = 0;
c->isunglobal = target_client->isunglobal = 0;
c->tags = target_client->tags = get_tags_first_tag(target_client->tags);
}
if (c->isfullscreen) {
setfullscreen(c, 0);
}
if (c->ismaximizescreen) {
setmaximizescreen(c, 0);
}
exit_scroller_stack(c);
stack_head = get_scroll_stack_head(target_client);
if (insert_before) {
if (target_client->prev_in_stack) {
target_client->prev_in_stack->next_in_stack = c;
}
c->prev_in_stack = target_client->prev_in_stack;
c->next_in_stack = target_client;
target_client->prev_in_stack = c;
} else {
if (target_client->next_in_stack) {
target_client->next_in_stack->prev_in_stack = c;
}
c->next_in_stack = target_client->next_in_stack;
c->prev_in_stack = target_client;
target_client->next_in_stack = c;
}
if (stack_head->ismaximizescreen) {
setmaximizescreen(stack_head, 0);
}
if (stack_head->isfullscreen) {
setfullscreen(stack_head, 0);
}
arrange(c->mon, false, false);
return;
}
void try_scroller_drop(Client *c, Client *closest, int vertical) {
double ratio_main, ratio_cross;
int32_t main_pos, cross_pos;
int32_t main_size, cross_size;
Client *stack_head = NULL;
if (vertical) {
main_pos = cursor->y;
cross_pos = cursor->x;
main_size = closest->geom.height;
cross_size = closest->geom.width;
} else {
main_pos = cursor->x;
cross_pos = cursor->y;
main_size = closest->geom.width;
cross_size = closest->geom.height;
}
ratio_main =
(double)(main_pos - (vertical ? closest->geom.y : closest->geom.x)) /
main_size;
ratio_cross =
(double)(cross_pos - (vertical ? closest->geom.x : closest->geom.y)) /
cross_size;
if (ratio_main > 0.3 && ratio_main < 0.7 &&
(ratio_cross < 0.3 || ratio_cross > 0.7)) {
setfloating(c, 0);
if (ratio_cross < 0.3) {
scroller_insert_stack(c, closest, true);
} else {
scroller_insert_stack(c, closest, false);
}
return;
}
stack_head = get_scroll_stack_head(closest);
if (ratio_main < 0.3) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
setfloating(c, 0);
}
void place_drag_tile_client(Client *c) {
Client *closest = find_closest_tiled_client(c);
if (closest && closest->mon) {
const Layout *layout =
closest->mon->pertag->ltidxs[closest->mon->pertag->curtag];
if (layout) {
if (layout->id == SCROLLER) {
try_scroller_drop(c, closest, 0);
return;
}
if (layout->id == VERTICAL_SCROLLER) {
try_scroller_drop(c, closest, 1);
return;
}
}
if (closest->link.prev != &c->link) {
wl_list_remove(&c->link);
wl_list_insert(closest->link.prev, &c->link);
} else {
exchange_two_client(c, closest);
}
}
setfloating(c, 0);
}