mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-06-13 14:33:08 -04:00
feat: no resize client when in overview
This commit is contained in:
parent
f3d0c18d19
commit
fef4e9047b
6 changed files with 238 additions and 125 deletions
|
|
@ -58,6 +58,14 @@ int32_t is_special_animation_rule(Client *c) {
|
|||
}
|
||||
}
|
||||
|
||||
void set_overview_enter_animation(Client *c) {
|
||||
struct wlr_box geo = c->geom;
|
||||
c->animainit_geom.width = geo.width * 1.2;
|
||||
c->animainit_geom.height = geo.height * 1.2;
|
||||
c->animainit_geom.x = geo.x + (geo.width - c->animainit_geom.width) / 2;
|
||||
c->animainit_geom.y = geo.y + (geo.height - c->animainit_geom.height) / 2;
|
||||
}
|
||||
|
||||
void set_client_open_animation(Client *c, struct wlr_box geo) {
|
||||
int32_t slide_direction;
|
||||
int32_t horizontal, horizontal_value;
|
||||
|
|
@ -213,6 +221,27 @@ void scene_buffer_apply_effect(struct wlr_scene_buffer *buffer, int32_t sx,
|
|||
return;
|
||||
}
|
||||
|
||||
void scene_buffer_apply_overview_effect(struct wlr_scene_buffer *buffer,
|
||||
int32_t sx, int32_t sy, void *data) {
|
||||
BufferData *buffer_data = (BufferData *)data;
|
||||
|
||||
struct wlr_scene_surface *scene_surface =
|
||||
wlr_scene_surface_try_from_buffer(buffer);
|
||||
|
||||
if (scene_surface == NULL)
|
||||
return;
|
||||
|
||||
struct wlr_surface *surface = scene_surface->surface;
|
||||
|
||||
if (buffer_data->width > 0 && buffer_data->height > 0) {
|
||||
wlr_scene_buffer_set_dest_size(buffer, buffer_data->width,
|
||||
buffer_data->height);
|
||||
}
|
||||
|
||||
if (wlr_xdg_popup_try_from_wlr_surface(surface) != NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
void buffer_set_effect(Client *c, BufferData data) {
|
||||
|
||||
if (!c || c->iskilling)
|
||||
|
|
@ -226,8 +255,13 @@ void buffer_set_effect(Client *c, BufferData data) {
|
|||
if (c == grabc)
|
||||
data.should_scale = false;
|
||||
|
||||
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
|
||||
scene_buffer_apply_effect, &data);
|
||||
if (c->mon->isoverview) {
|
||||
wlr_scene_node_for_each_buffer(
|
||||
&c->scene_surface->node, scene_buffer_apply_overview_effect, &data);
|
||||
} else {
|
||||
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
|
||||
scene_buffer_apply_effect, &data);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_shield(Client *c, struct wlr_box clip_box) {
|
||||
|
|
@ -727,7 +761,18 @@ void client_apply_clip(Client *c, float factor) {
|
|||
|
||||
// 应用窗口表面剪切
|
||||
apply_shield(c, clip_box);
|
||||
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
|
||||
if (!c->mon->isoverview) {
|
||||
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip_box);
|
||||
} else {
|
||||
struct wlr_box ov_surface_box = c->geom;
|
||||
ov_surface_box.x = 0;
|
||||
ov_surface_box.y = 0;
|
||||
ov_surface_box.width = c->mon->m.width - 2 * c->bw;
|
||||
ov_surface_box.height = c->mon->m.height - 2 * c->bw;
|
||||
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node,
|
||||
&ov_surface_box);
|
||||
wlr_scene_node_set_enabled(&c->scene_surface->node, true);
|
||||
}
|
||||
|
||||
// 获取剪切后的表面的实际大小用于计算缩放
|
||||
int32_t acutal_surface_width = geometry.width - offset.x - offset.width;
|
||||
|
|
@ -837,6 +882,10 @@ void client_animation_next_tick(Client *c) {
|
|||
? (double)passed_time / (double)c->animation.duration
|
||||
: 1.0;
|
||||
|
||||
if (c->mon->isoverview && animation_passed >= 1.0) {
|
||||
animation_passed = 1.0;
|
||||
}
|
||||
|
||||
int32_t type = c->animation.action == NONE ? MOVE : c->animation.action;
|
||||
double factor = find_animation_curve_at(animation_passed, type);
|
||||
|
||||
|
|
@ -866,7 +915,10 @@ void client_animation_next_tick(Client *c) {
|
|||
|
||||
client_apply_clip(c, factor);
|
||||
|
||||
if (animation_passed >= 1.0) {
|
||||
if (c->mon->isoverview && animation_passed >= 1.0) {
|
||||
c->animation.overining = false;
|
||||
return;
|
||||
} else if (animation_passed >= 1.0) {
|
||||
|
||||
// clear the open action state
|
||||
// To prevent him from being mistaken that
|
||||
|
|
@ -1093,8 +1145,11 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
|
|||
c->animation.begin_fade_in = false;
|
||||
}
|
||||
|
||||
if (c->animation.action == OPEN && !c->animation.tagining &&
|
||||
!c->animation.tagouting && wlr_box_equal(&c->geom, &c->current)) {
|
||||
if (c->animation.overining) {
|
||||
c->animation.action = OVERVIEW;
|
||||
} else if (c->animation.action == OPEN && !c->animation.tagining &&
|
||||
!c->animation.tagouting &&
|
||||
wlr_box_equal(&c->geom, &c->current)) {
|
||||
c->animation.action = c->animation.action;
|
||||
} else if (c->animation.tagouting) {
|
||||
c->animation.duration = config.animation_duration_tag;
|
||||
|
|
@ -1133,8 +1188,9 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
|
|||
}
|
||||
|
||||
// c->geom 是真实的窗口大小和位置,跟过度的动画无关,用于计算布局
|
||||
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
|
||||
c->geom.height - 2 * c->bw);
|
||||
if (!c->mon->isoverview)
|
||||
c->configure_serial = client_set_size(c, c->geom.width - 2 * c->bw,
|
||||
c->geom.height - 2 * c->bw);
|
||||
|
||||
if (c->configure_serial != 0) {
|
||||
c->mon->resizing_count_pending++;
|
||||
|
|
@ -1177,6 +1233,11 @@ void resize(Client *c, struct wlr_box geo, int32_t interact) {
|
|||
c->animainit_geom = c->geom;
|
||||
}
|
||||
|
||||
if (c->mon->isoverview && c != c->mon->sel &&
|
||||
c->animation.action == OVERVIEW) {
|
||||
set_overview_enter_animation(c);
|
||||
}
|
||||
|
||||
// 开始应用动画设置
|
||||
client_set_pending_state(c);
|
||||
|
||||
|
|
|
|||
|
|
@ -1744,8 +1744,10 @@ int32_t toggleoverview(const Arg *arg) {
|
|||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c && c->mon == selmon && !client_is_unmanaged(c) &&
|
||||
!client_is_x11_popup(c) && !c->isunglobal)
|
||||
!client_is_x11_popup(c) && !c->isunglobal) {
|
||||
c->animation.overining = true;
|
||||
overview_backup(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,19 @@ void get_layout_abbr(char *abbr, const char *full_name) {
|
|||
}
|
||||
}
|
||||
|
||||
Client *xytoclient(double x, double y) {
|
||||
Client *c = NULL, *tmp = NULL;
|
||||
wl_list_for_each_safe(c, tmp, &clients, link) {
|
||||
if (VISIBLEON(c, c->mon) && c->animation.current.x <= x &&
|
||||
c->animation.current.y <= y &&
|
||||
c->animation.current.x + c->animation.current.width >= x &&
|
||||
c->animation.current.y + c->animation.current.height >= y) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
||||
LayerSurface **pl, double *nx, double *ny) {
|
||||
struct wlr_scene_node *node, *pnode;
|
||||
|
|
@ -84,6 +97,7 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
|||
Client *c = NULL;
|
||||
LayerSurface *l = NULL;
|
||||
int32_t layer;
|
||||
Client *ovc = NULL;
|
||||
|
||||
for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
|
||||
|
||||
|
|
@ -130,4 +144,12 @@ void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc,
|
|||
*pc = c;
|
||||
if (pl)
|
||||
*pl = l;
|
||||
|
||||
if (selmon && selmon->isoverview && !l) {
|
||||
ovc = xytoclient(x, y);
|
||||
if (pc)
|
||||
*pc = ovc;
|
||||
if (psurface && ovc)
|
||||
*psurface = client_surface(ovc);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +1,3 @@
|
|||
|
||||
// 网格布局窗口大小和位置计算
|
||||
void overview(Monitor *m) {
|
||||
int32_t i, n;
|
||||
int32_t cx, cy, cw, ch;
|
||||
int32_t dx;
|
||||
int32_t cols, rows, overcols;
|
||||
Client *c = NULL;
|
||||
n = 0;
|
||||
int32_t target_gappo =
|
||||
enablegaps ? m->isoverview ? config.overviewgappo : config.gappoh : 0;
|
||||
int32_t target_gappi =
|
||||
enablegaps ? m->isoverview ? config.overviewgappi : config.gappih : 0;
|
||||
float single_width_ratio = m->isoverview ? 0.7 : 0.9;
|
||||
float single_height_ratio = m->isoverview ? 0.8 : 0.9;
|
||||
|
||||
n = m->isoverview ? m->visible_clients : m->visible_tiling_clients;
|
||||
|
||||
if (n == 0) {
|
||||
return; // 没有需要处理的客户端,直接返回
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
|
||||
cw = (m->w.width - 2 * target_gappo) * single_width_ratio;
|
||||
ch = (m->w.height - 2 * target_gappo) * single_height_ratio;
|
||||
c->geom.x = m->w.x + (m->w.width - cw) / 2;
|
||||
c->geom.y = m->w.y + (m->w.height - ch) / 2;
|
||||
c->geom.width = cw;
|
||||
c->geom.height = ch;
|
||||
resize(c, c->geom, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 2) {
|
||||
cw = (m->w.width - 2 * target_gappo - target_gappi) / 2;
|
||||
ch = (m->w.height - 2 * target_gappo) * 0.65;
|
||||
i = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
|
||||
if (i == 0) {
|
||||
c->geom.x = m->w.x + target_gappo;
|
||||
c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
|
||||
c->geom.width = cw;
|
||||
c->geom.height = ch;
|
||||
resize(c, c->geom, 0);
|
||||
} else if (i == 1) {
|
||||
c->geom.x = m->w.x + cw + target_gappo + target_gappi;
|
||||
c->geom.y = m->w.y + (m->w.height - ch) / 2 + target_gappo;
|
||||
c->geom.width = cw;
|
||||
c->geom.height = ch;
|
||||
resize(c, c->geom, 0);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算列数和行数
|
||||
for (cols = 0; cols <= n / 2; cols++) {
|
||||
if (cols * cols >= n) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
rows = (cols && (cols - 1) * cols >= n) ? cols - 1 : cols;
|
||||
|
||||
// 计算每个客户端的高度和宽度
|
||||
ch = (m->w.height - 2 * target_gappo - (rows - 1) * target_gappi) / rows;
|
||||
cw = (m->w.width - 2 * target_gappo - (cols - 1) * target_gappi) / cols;
|
||||
|
||||
// 处理多余的列
|
||||
overcols = n % cols;
|
||||
if (overcols) {
|
||||
dx = (m->w.width - overcols * cw - (overcols - 1) * target_gappi) / 2 -
|
||||
target_gappo;
|
||||
}
|
||||
|
||||
// 调整每个客户端的位置和大小
|
||||
i = 0;
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
|
||||
cx = m->w.x + (i % cols) * (cw + target_gappi);
|
||||
cy = m->w.y + (i / cols) * (ch + target_gappi);
|
||||
if (overcols && i >= n - overcols) {
|
||||
cx += dx;
|
||||
}
|
||||
c->geom.x = cx + target_gappo;
|
||||
c->geom.y = cy + target_gappo;
|
||||
c->geom.width = cw;
|
||||
c->geom.height = ch;
|
||||
resize(c, c->geom, 0);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deck(Monitor *m) {
|
||||
int32_t mw, my;
|
||||
int32_t i, n = 0;
|
||||
|
|
|
|||
138
src/layout/overview.h
Normal file
138
src/layout/overview.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// 紧凑型自适应行高概览布局 (Mission Control / GNOME 风格)
|
||||
void overview(Monitor *m) {
|
||||
int32_t target_gappo =
|
||||
enablegaps ? (m->isoverview ? config.overviewgappo : config.gappoh) : 0;
|
||||
int32_t target_gappi =
|
||||
enablegaps ? (m->isoverview ? config.overviewgappi : config.gappih) : 0;
|
||||
|
||||
int n = m->isoverview ? m->visible_clients : m->visible_tiling_clients;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
// 收集有效客户端,并提取它们原始的宽高比
|
||||
Client *c_arr[n];
|
||||
float aspects[n];
|
||||
int actual_n = 0;
|
||||
Client *c = NULL;
|
||||
|
||||
wl_list_for_each(c, &clients, link) {
|
||||
if (c->mon != m)
|
||||
continue;
|
||||
if (VISIBLEON(c, m) && !c->isunglobal &&
|
||||
((m->isoverview && !client_is_x11_popup(c)) || ISTILED(c))) {
|
||||
|
||||
c_arr[actual_n] = c;
|
||||
float aspect = 1.0f;
|
||||
if (c->overview_backup_geom.height > 0 &&
|
||||
c->overview_backup_geom.width > 0) {
|
||||
aspect = (float)c->overview_backup_geom.width /
|
||||
c->overview_backup_geom.height;
|
||||
}
|
||||
// 限制极端宽高比,防止某个 1px 宽度的异常窗口毁掉整个布局的数学计算
|
||||
if (aspect < 0.2f)
|
||||
aspect = 0.2f;
|
||||
if (aspect > 5.0f)
|
||||
aspect = 5.0f;
|
||||
|
||||
aspects[actual_n] = aspect;
|
||||
actual_n++;
|
||||
}
|
||||
}
|
||||
n = actual_n;
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
// 动态决定行数与列数分配 (例如 7 个窗口分发为 3, 2, 2)
|
||||
int cols = 1;
|
||||
while (cols * cols < n)
|
||||
cols++;
|
||||
int rows = (n + cols - 1) / cols;
|
||||
|
||||
int items_per_row[rows];
|
||||
int remaining = n;
|
||||
for (int r = 0; r < rows; r++) {
|
||||
// 使用向上取整的除法,让首行尽可能多排布,视觉重心更稳
|
||||
int count = (remaining + rows - r - 1) / (rows - r);
|
||||
items_per_row[r] = count;
|
||||
remaining -= count;
|
||||
}
|
||||
|
||||
// 计算整个布局允许的最大可用区域 (留出四周安全边距)
|
||||
float max_avail_w = m->w.width - 2 * target_gappo;
|
||||
float max_avail_h = m->w.height - 2 * target_gappo;
|
||||
if (max_avail_w < 10)
|
||||
max_avail_w = 10;
|
||||
if (max_avail_h < 10)
|
||||
max_avail_h = 10;
|
||||
|
||||
// 计算能够满足所有限制条件的 "最大统一行高"
|
||||
float A_sum[rows]; // 每行窗口宽高比之和
|
||||
float h_max_w = 999999.0f; // 受宽度限制推导出的行高上限
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
A_sum[r] = 0;
|
||||
int start_idx = 0;
|
||||
for (int i = 0; i < r; i++)
|
||||
start_idx += items_per_row[i];
|
||||
|
||||
for (int i = 0; i < items_per_row[r]; i++) {
|
||||
A_sum[r] += aspects[start_idx + i];
|
||||
}
|
||||
|
||||
// 这行所有的内部间隙总和
|
||||
float gap_x_total = (items_per_row[r] - 1) * target_gappi;
|
||||
// 保证最宽的一行也不会超出 max_avail_w
|
||||
float h_limit = (max_avail_w - gap_x_total) / A_sum[r];
|
||||
if (h_limit < h_max_w)
|
||||
h_max_w = h_limit;
|
||||
}
|
||||
|
||||
float gap_y_total = (rows - 1) * target_gappi;
|
||||
// 保证总行高不会超出 max_avail_h
|
||||
float h_max_h = (max_avail_h - gap_y_total) / rows;
|
||||
|
||||
// 最终采用的行高是水平和垂直双向限制中最严苛的一个
|
||||
float row_height = h_max_w < h_max_h ? h_max_w : h_max_h;
|
||||
|
||||
// 应用坐标并进行防撕裂渲染
|
||||
float total_layout_height = rows * row_height + gap_y_total;
|
||||
// 计算全局起点 Y,确保整个概览在屏幕垂直居中
|
||||
float start_y = m->w.y + (m->w.height - total_layout_height) / 2.0f;
|
||||
|
||||
int current_idx = 0;
|
||||
float current_y = start_y;
|
||||
|
||||
for (int r = 0; r < rows; r++) {
|
||||
// 根据当前确定的行高,反推这行真实的像素宽度
|
||||
float row_width =
|
||||
row_height * A_sum[r] + (items_per_row[r] - 1) * target_gappi;
|
||||
// 让当前这一排窗口在屏幕水平居中 (不满列数的行会自动居中对齐)
|
||||
float current_x = m->w.x + (m->w.width - row_width) / 2.0f;
|
||||
|
||||
for (int i = 0; i < items_per_row[r]; i++) {
|
||||
Client *client = c_arr[current_idx];
|
||||
float aspect = aspects[current_idx];
|
||||
float client_width = row_height * aspect;
|
||||
|
||||
// 【关键防错位】累加 float 坐标,最后写入 client
|
||||
// 时再强转+0.5四舍五入。
|
||||
// 避免每次计算内部小间隙都丢弃浮点精度,导致最后多出或少出 1px
|
||||
// 缝隙。
|
||||
struct wlr_box client_geom;
|
||||
client_geom.x = (int)(current_x + 0.5f);
|
||||
client_geom.y = (int)(current_y + 0.5f);
|
||||
|
||||
float next_x = current_x + client_width;
|
||||
client_geom.width = (int)(next_x + 0.5f) - client_geom.x;
|
||||
|
||||
float next_y = current_y + row_height;
|
||||
client_geom.height = (int)(next_y + 0.5f) - client_geom.y;
|
||||
|
||||
resize(client, client_geom, 0);
|
||||
|
||||
current_x = next_x + target_gappi;
|
||||
current_idx++;
|
||||
}
|
||||
current_y += row_height + target_gappi;
|
||||
}
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ enum {
|
|||
}; /* EWMH atoms */
|
||||
#endif
|
||||
enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */
|
||||
enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT };
|
||||
enum { NONE, OPEN, MOVE, CLOSE, TAG, FOCUS, OPAFADEIN, OPAFADEOUT, OVERVIEW };
|
||||
enum { UNFOLD, FOLD, INVALIDFOLD };
|
||||
enum { PREV, NEXT };
|
||||
enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED };
|
||||
|
|
@ -276,6 +276,7 @@ struct dwl_animation {
|
|||
bool tagouting;
|
||||
bool begin_fade_in;
|
||||
bool tag_from_rule;
|
||||
bool overining;
|
||||
uint32_t time_started;
|
||||
uint32_t duration;
|
||||
struct wlr_box initial;
|
||||
|
|
@ -1099,6 +1100,7 @@ static struct wl_event_source *sync_keymap;
|
|||
#include "layout/arrange.h"
|
||||
#include "layout/dwindle.h"
|
||||
#include "layout/horizontal.h"
|
||||
#include "layout/overview.h"
|
||||
#include "layout/scroll.h"
|
||||
#include "layout/vertical.h"
|
||||
|
||||
|
|
@ -4322,6 +4324,7 @@ mapnotify(struct wl_listener *listener, void *data) {
|
|||
// init client geom
|
||||
c->geom.width += 2 * c->bw;
|
||||
c->geom.height += 2 * c->bw;
|
||||
c->overview_backup_geom = c->geom;
|
||||
|
||||
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
|
||||
.app_id = client_get_appid(c),
|
||||
|
|
@ -4807,7 +4810,8 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
|
|||
|
||||
if (config.sloppyfocus && !start_drag_window && c && time && c->scene &&
|
||||
c->scene->node.enabled && !c->animation.tagining &&
|
||||
(surface != seat->pointer_state.focused_surface) &&
|
||||
(surface != seat->pointer_state.focused_surface ||
|
||||
(selmon && selmon->isoverview && selmon->sel != c)) &&
|
||||
!client_is_unmanaged(c) && VISIBLEON(c, c->mon))
|
||||
focusclient(c, 0);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue