feat: add rect indicator for drag tile to tile

This commit is contained in:
DreamMaoMao 2026-05-06 14:10:08 +08:00
parent 7a950f54df
commit 9a00d8ce09
5 changed files with 189 additions and 49 deletions

View file

@ -98,6 +98,7 @@ scratchpad_height_ratio=0.9
borderpx=4
rootcolor=0x201b14ff
bordercolor=0x444444ff
dropcolor=0x8FBA7C55
focuscolor=0xc9b890ff
maximizescreencolor=0x89aa61ff
urgentcolor=0xad401fff

View file

@ -26,6 +26,9 @@ rootcolor=0x323232ff
# Inactive window border
bordercolor=0x444444ff
# Drop shadow when dragging windows
dropcolor=0x8FBA7C55
# Active window border
focuscolor=0xc66b25ff

View file

@ -404,6 +404,90 @@ struct ivec2 clip_to_hide(Client *c, struct wlr_box *clip_box) {
return offset;
}
void client_set_drop_area(Client *c) {
bool first_draw = false;
int32_t drop_direction = UNDIR;
if (!c->enable_drop_area_draw && !c->droparea->node.enabled) {
return;
}
if (!c->enable_drop_area_draw && c->droparea->node.enabled) {
wlr_scene_node_lower_to_bottom(&c->droparea->node);
wlr_scene_node_set_enabled(&c->droparea->node, false);
return;
} else if (c->enable_drop_area_draw && !c->droparea->node.enabled) {
wlr_scene_node_raise_to_top(&c->droparea->node);
wlr_scene_node_set_enabled(&c->droparea->node, true);
first_draw = true;
}
int32_t bw = (int32_t)c->bw;
int32_t client_width = c->geom.width - 2 * bw;
int32_t client_height = c->geom.height - 2 * bw;
// 光标在窗口客户区内的相对坐标
double rel_x = cursor->x - c->geom.x - bw;
double rel_y = cursor->y - c->geom.y - bw;
struct wlr_box drop_box;
// 左边缘x 在 0~30% 客户区宽度,且 y 在 30%~70% 客户区高度
if (rel_x < client_width * 0.3 && rel_y > client_height * 0.3 &&
rel_y < client_height * 0.7) {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width * 0.3;
drop_box.height = client_height;
drop_direction = LEFT;
}
// 右边缘x 在 70%~100% 客户区宽度,且 y 在 30%~70% 客户区高度
else if (rel_x > client_width * 0.7 && rel_y > client_height * 0.3 &&
rel_y < client_height * 0.7) {
drop_box.x = bw + client_width * 0.7;
drop_box.y = bw;
drop_box.width = client_width * 0.3;
drop_box.height = client_height;
drop_direction = RIGHT;
}
// 上边缘x 在 30%~70% 客户区宽度,且 y 在 0~30% 客户区高度
else if (rel_x > client_width * 0.3 && rel_x < client_width * 0.7 &&
rel_y < client_height * 0.3) {
drop_box.x = bw;
drop_box.y = bw;
drop_box.width = client_width;
drop_box.height = client_height * 0.3;
drop_direction = UP;
}
// 下边缘x 在 30%~70% 客户区宽度,且 y 在 70%~100% 客户区高度
else if (rel_x > client_width * 0.3 && rel_x < client_width * 0.7 &&
rel_y > client_height * 0.7) {
drop_box.x = bw;
drop_box.y = bw + client_height * 0.7;
drop_box.width = client_width;
drop_box.height = client_height * 0.3;
drop_direction = DOWN;
}
// 中心(或其他角落):显示在中间区域
else {
drop_box.x = bw + client_width * 0.3;
drop_box.y = bw + client_height * 0.3;
drop_box.width = client_width * 0.4;
drop_box.height = client_height * 0.4;
drop_direction = UNDIR;
}
if (!first_draw && c->drop_direction == drop_direction) {
return;
} else {
c->drop_direction = drop_direction;
}
wlr_scene_node_set_position(&c->droparea->node, drop_box.x, drop_box.y);
wlr_scene_rect_set_size(c->droparea, drop_box.width, drop_box.height);
}
void client_apply_clip(Client *c, float factor) {
if (c->iskilling || !client_surface(c)->mapped)

View file

@ -294,6 +294,7 @@ typedef struct {
float scratchpad_height_ratio;
float rootcolor[4];
float bordercolor[4];
float dropcolor[4];
float focuscolor[4];
float maximizescreencolor[4];
float urgentcolor[4];
@ -1672,6 +1673,17 @@ bool parse_option(Config *config, char *key, char *value) {
} else {
convert_hex_to_rgba(config->bordercolor, color);
}
} else if (strcmp(key, "dropcolor") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid dropcolor "
"format: %s\n",
value);
return false;
} else {
convert_hex_to_rgba(config->dropcolor, color);
}
} else if (strcmp(key, "focuscolor") == 0) {
int64_t color = parse_color(value);
if (color == -1) {
@ -3310,6 +3322,10 @@ void set_value_default() {
config.bordercolor[1] = 0x44 / 255.0f;
config.bordercolor[2] = 0x44 / 255.0f;
config.bordercolor[3] = 1.0f;
config.dropcolor[0] = 0x8f / 255.0f;
config.dropcolor[1] = 0xba / 255.0f;
config.dropcolor[2] = 0x7c / 255.0f;
config.dropcolor[3] = 0.5f;
config.focuscolor[0] = 0xc6 / 255.0f;
config.focuscolor[1] = 0x6b / 255.0f;
config.focuscolor[2] = 0x25 / 255.0f;

View file

@ -311,6 +311,7 @@ struct Client {
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
struct wlr_scene_rect *shield;
struct wlr_scene_rect *droparea;
struct wlr_scene_tree *scene_surface;
struct wlr_scene_tree *image_capture_tree;
@ -422,6 +423,8 @@ struct Client {
bool isfocusing;
struct Client *next_in_stack;
struct Client *prev_in_stack;
bool enable_drop_area_draw;
int32_t drop_direction;
};
typedef struct {
@ -886,7 +889,7 @@ static KeyboardGroup *kb_group;
static struct wl_list inputdevices;
static struct wl_list keyboard_shortcut_inhibitors;
static uint32_t cursor_mode;
static Client *grabc;
static Client *grabc, *dropc;
static int32_t rzcorner;
static int32_t grabcx, grabcy; /* client-relative */
static int32_t drag_begin_cursorx, drag_begin_cursory; /* client-relative */
@ -2141,49 +2144,41 @@ void scroller_insert_stack(Client *c, Client *target_client,
}
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;
Client *stack_head = get_scroll_stack_head(closest);
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) {
if (closest->drop_direction == LEFT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
} else {
return;
} else if (closest->drop_direction == RIGHT) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == UP) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == DOWN) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
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);
if (closest->drop_direction == UP) {
setfloating(c, 0);
scroller_insert_stack(c, closest, true);
return;
} else if (closest->drop_direction == DOWN) {
setfloating(c, 0);
scroller_insert_stack(c, closest, false);
return;
} else if (closest->drop_direction == LEFT) {
wl_list_remove(&c->link);
wl_list_insert(stack_head->link.prev, &c->link);
} else if (closest->drop_direction == RIGHT) {
wl_list_remove(&c->link);
wl_list_insert(&stack_head->link, &c->link);
}
}
setfloating(c, 0);
@ -2196,22 +2191,27 @@ void place_drag_tile_client(Client *c) {
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->drop_direction == UNDIR) {
exchange_two_client(c, closest);
setfloating(c, 0);
return;
}
if (closest->link.prev != &c->link) {
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->drop_direction == LEFT || closest->drop_direction == UP) {
wl_list_remove(&c->link);
wl_list_insert(closest->link.prev, &c->link);
} else {
exchange_two_client(c, closest);
wl_list_remove(&c->link);
wl_list_insert(&closest->link, &c->link);
}
}
@ -2336,6 +2336,11 @@ buttonpress(struct wl_listener *listener, void *data) {
apply_window_snap(tmpc);
}
tmpc->drag_to_tile = false;
if (dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = NULL;
}
return;
} else {
cursor_mode = CurNormal;
@ -4107,6 +4112,8 @@ void locksession(struct wl_listener *listener, void *data) {
}
void init_client_properties(Client *c) {
c->drop_direction = UNDIR;
c->enable_drop_area_draw = false;
c->isfocusing = false;
c->isfloating = 0;
c->isfakefullscreen = 0;
@ -4268,6 +4275,12 @@ mapnotify(struct wl_listener *listener, void *data) {
: config.bordercolor);
c->border[i]->node.data = c;
}
// extra node
c->droparea = wlr_scene_rect_create(c->scene, 0, 0, config.dropcolor);
wlr_scene_node_lower_to_bottom(&c->droparea->node);
wlr_scene_node_set_position(&c->droparea->node, 0, 0);
wlr_scene_node_set_enabled(&c->droparea->node, false);
c->shield = wlr_scene_rect_create(c->scene_surface, 0, 0,
(float[4]){0, 0, 0, 0xff});
@ -4446,6 +4459,7 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
double dy, double dx_unaccel, double dy_unaccel) {
double sx = 0, sy = 0, sx_confined, sy_confined;
Client *c = NULL, *w = NULL;
Client *closet_drop_client = NULL;
LayerSurface *l = NULL;
struct wlr_surface *surface = NULL;
struct wlr_pointer_constraint_v1 *constraint;
@ -4517,6 +4531,24 @@ void motionnotify(uint32_t time, struct wlr_input_device *device, double dx,
.y = (int32_t)round(cursor->y) - grabcy,
.width = grabc->geom.width,
.height = grabc->geom.height};
if (config.drag_tile_to_tile && grabc->drag_to_tile) {
closet_drop_client = find_closest_tiled_client(grabc);
if (closet_drop_client && dropc && closet_drop_client != dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = closet_drop_client;
dropc->enable_drop_area_draw = true;
client_set_drop_area(dropc);
} else if (closet_drop_client) {
dropc = closet_drop_client;
dropc->enable_drop_area_draw = true;
client_set_drop_area(dropc);
} else if (dropc) {
dropc->enable_drop_area_draw = false;
client_set_drop_area(dropc);
dropc = NULL;
}
}
resize(grabc, grabc->float_geom, 1);
return;
} else if (cursor_mode == CurResize) {
@ -6217,6 +6249,10 @@ void unmapnotify(struct wl_listener *listener, void *data) {
grabc = NULL;
}
if (c == dropc) {
dropc = NULL;
}
wl_list_for_each(m, &mons, link) {
if (!m->wlr_output->enabled) {
continue;