diff --git a/assets/config.conf b/assets/config.conf index 3a126f11..bd65466b 100644 --- a/assets/config.conf +++ b/assets/config.conf @@ -98,6 +98,7 @@ scratchpad_height_ratio=0.9 borderpx=4 rootcolor=0x201b14ff bordercolor=0x444444ff +dropcolor=0x8FBA7C55 focuscolor=0xc9b890ff maximizescreencolor=0x89aa61ff urgentcolor=0xad401fff diff --git a/docs/visuals/theming.md b/docs/visuals/theming.md index 8137eb4e..03750e4e 100644 --- a/docs/visuals/theming.md +++ b/docs/visuals/theming.md @@ -26,6 +26,9 @@ rootcolor=0x323232ff # Inactive window border bordercolor=0x444444ff +# Drop shadow when dragging windows +dropcolor=0x8FBA7C55 + # Active window border focuscolor=0xc66b25ff diff --git a/src/animation/client.h b/src/animation/client.h index eeb094bb..1574324a 100644 --- a/src/animation/client.h +++ b/src/animation/client.h @@ -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) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 56892ec9..291730c5 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -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; diff --git a/src/mango.c b/src/mango.c index 2f726303..466178d1 100644 --- a/src/mango.c +++ b/src/mango.c @@ -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;