diff --git a/src/dispatch/bind_define.h b/src/dispatch/bind_define.h index 7eb0664c..76903f88 100644 --- a/src/dispatch/bind_define.h +++ b/src/dispatch/bind_define.h @@ -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; diff --git a/src/mango.c b/src/mango.c index 6ffdee50..2f726303 100644 --- a/src/mango.c +++ b/src/mango.c @@ -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); }