From 0b046ced779151353e28e5d0d7ca602c724dafe8 Mon Sep 17 00:00:00 2001 From: Wei-Hsin Yeh Date: Mon, 16 Jun 2025 20:38:16 +0800 Subject: [PATCH 1/2] Avoid rendering shadows on the window frame To prevent twin_stack_blur() from blurring shadows over the window frame, twin_shadow_border() draws a black border beginning at the top Y position of the window's client area, offset by 'CONFIG_SHADOW_BLUR / 2 + 1'. Signed-off-by: Wei-Hsin Yeh --- src/draw-common.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/draw-common.c b/src/draw-common.c index 4c84e1d..eb4d9bf 100644 --- a/src/draw-common.c +++ b/src/draw-common.c @@ -163,12 +163,17 @@ void twin_shadow_border(twin_pixmap_t *shadow, offset = min(offset_x, offset_y); switch (shadow->window->style) { + /* + * Draw a black border starting from the top y position of the window's + * client area plus CONFIG_SHADOW_BLUR / 2 + 1, to prevent twin_stack_blur() + * from blurring shadows over the window frame. + */ case TwinWindowApplication: - y_start = TWIN_TITLE_HEIGHT; + y_start = TWIN_TITLE_HEIGHT + CONFIG_SHADOW_BLUR / 2 + 1; break; case TwinWindowPlain: default: - y_start = 0; + y_start = CONFIG_SHADOW_BLUR / 2 + 1; break; } From c19cbbd72babf90974ee0160e01b6cba7ec4f0c1 Mon Sep 17 00:00:00 2001 From: Wei-Hsin Yeh Date: Mon, 16 Jun 2025 20:58:22 +0800 Subject: [PATCH 2/2] Add minimize and maximize features on window frame Add a condition in twin_window_dispatch() to handle clicks on the minimize and maximize buttons located on the window frame. The positions of these buttons are calculated for click detection. A minimized window is inactive, even if it remains at the top of the screen. A window is considered active when it is the topmost window and is not minimized. When the window is active, its title bar turns blue. Signed-off-by: Wei-Hsin Yeh --- include/twin.h | 1 + src/screen.c | 9 +++++++ src/window.c | 64 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/twin.h b/include/twin.h index 07c2011..1e312f2 100644 --- a/include/twin.h +++ b/include/twin.h @@ -443,6 +443,7 @@ struct _twin_window { twin_rect_t client; twin_rect_t damage; bool active; + bool minimize; bool client_grab; bool want_focus; bool draw_queued; diff --git a/src/screen.c b/src/screen.c index f8a816e..580d0f4 100644 --- a/src/screen.c +++ b/src/screen.c @@ -9,6 +9,9 @@ #include "twin_private.h" +#define TWIN_BW 0 +#define TWIN_TITLE_HEIGHT 20 + twin_screen_t *twin_screen_create(twin_coord_t width, twin_coord_t height, twin_put_begin_t put_begin, @@ -135,6 +138,12 @@ static void twin_screen_span_pixmap(twin_screen_t maybe_unused *screen, return; if (p->y + p->height <= y) return; + + /* Skip drawing the window's client area if the window is minimized. */ + if (p->window->minimize && + y >= p->y + TWIN_BW + TWIN_TITLE_HEIGHT + TWIN_BW) + return; + /* bounds check in x */ p_left = left; if (p_left < p->x) diff --git a/src/window.c b/src/window.c index 3222975..47a15b4 100644 --- a/src/window.c +++ b/src/window.c @@ -36,6 +36,7 @@ twin_window_t *twin_window_create(twin_screen_t *screen, window->screen = screen; window->style = style; window->active = false; + window->minimize = false; switch (window->style) { case TwinWindowApplication: left = TWIN_BW; @@ -179,7 +180,7 @@ bool twin_window_valid_range(twin_window_t *window, y < window->pixmap->y + window->pixmap->height - offset_y) { if (y < window->pixmap->y + (window->client.top)) return !twin_pixmap_transparent(window->pixmap, x, y); - return true; + return !window->minimize; } return false; } @@ -494,21 +495,66 @@ bool twin_window_dispatch(twin_window_t *window, twin_event_t *event) twin_event_t ev = *event; bool delegate = true; + twin_fixed_t bw = twin_int_to_fixed(TWIN_TITLE_BW); + twin_fixed_t t_h = twin_int_to_fixed(window->client.top) - bw; + twin_fixed_t t_arc_2 = t_h * 2 / 3; + twin_fixed_t c_right = twin_int_to_fixed(window->client.right) - bw / 2; + twin_fixed_t name_height = t_h - bw - bw / 2; + twin_fixed_t icon_size = name_height * 8 / 10; + twin_fixed_t menu_x = t_arc_2; + twin_fixed_t text_x = menu_x + icon_size + bw; + twin_fixed_t text_width; + twin_fixed_t title_right; + twin_path_t *path = twin_path_create(); + const char *name = window->name; + + text_width = twin_width_utf8(path, name); + twin_path_destroy(path); + title_right = (text_x + text_width + bw + icon_size + bw + icon_size + bw + + icon_size + t_arc_2); + + if (title_right < c_right) + c_right = title_right; + + twin_fixed_t close_x = c_right - t_arc_2 - icon_size; + twin_fixed_t max_x = close_x - bw - icon_size; + twin_fixed_t min_x = max_x - bw - icon_size; + int local_x, local_y; + switch (ev.kind) { case TwinEventButtonDown: + local_y = ev.u.pointer.screen_y - window->pixmap->y; + if (local_y >= 0 && local_y <= TWIN_BW + TWIN_TITLE_HEIGHT + TWIN_BW) { + local_x = ev.u.pointer.screen_x - window->pixmap->x; + if (local_x > twin_fixed_to_int(min_x) && + local_x < twin_fixed_to_int(max_x)) { + window->minimize = true; + twin_pixmap_damage(window->pixmap, 0, 0, window->pixmap->width, + window->pixmap->height); + } else if (local_x > twin_fixed_to_int(max_x) && + local_x < twin_fixed_to_int(close_x)) { + window->minimize = false; + twin_pixmap_damage(window->pixmap, 0, 0, window->pixmap->width, + window->pixmap->height); + } + } case TwinEventActivate: /* Set window active. */ /* - * When the box is trigger by TwinEventButtonDown, its window's title - * bar needs to change color and be put onto the toppest layer. + * A minimized window is inactive. When the box is triggered by + * TwinEventButtonDown and the window is not minimized, it becomes + * active. For a window to be considered active, it must be the topmost + * window on the screen. The window's title bar turns blue to indicate + * the active state. */ - if (!window->active) { + if (window->minimize) + window->active = false; + else window->active = true; - twin_window_frame(window); - if (window != window->screen->top->window) { - window->screen->top->window->active = false; - twin_window_frame(window->screen->top->window); - } + twin_window_frame(window); + if (window != window->screen->top->window) { + window->screen->top->window->active = false; + twin_window_frame(window->screen->top->window); } #if defined(CONFIG_DROP_SHADOW) /* Handle drop shadow. */