diff --git a/include/twin.h b/include/twin.h index 07c2011..cc1aaf3 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 iconify; bool client_grab; bool want_focus; bool draw_queued; @@ -459,8 +460,8 @@ struct _twin_window { */ typedef enum _twin_icon { TwinIconMenu, - TwinIconMinimize, - TwinIconMaximize, + TwinIconIconify, + TwinIconRestore, TwinIconClose, TwinIconResize, } twin_icon_t; 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; } diff --git a/src/icon.c b/src/icon.c index 539c08e..30afd93 100644 --- a/src/icon.c +++ b/src/icon.c @@ -39,8 +39,8 @@ static const signed char _twin_itable[] = { 's', 'e', #define TWIN_MENU_LEN 43 - /* Minimize */ -#define TWIN_MINIMIZE_POS TWIN_MENU_POS + TWIN_MENU_LEN + /* Iconify */ +#define TWIN_ICONIFY_POS TWIN_MENU_POS + TWIN_MENU_LEN 'm', L(0), G(0.8), 'd', L(0), B(1), 'd', R(1), B(1), @@ -49,9 +49,9 @@ static const signed char _twin_itable[] = { 'w', G(0.05), 'p', 'e', -#define TWIN_MINIMIZE_LEN 17 - /* Maximize */ -#define TWIN_MAXIMIZE_POS TWIN_MINIMIZE_POS + TWIN_MINIMIZE_LEN +#define TWIN_ICONIFY_LEN 17 + /* Restore */ +#define TWIN_RESTORE_POS TWIN_ICONIFY_POS + TWIN_ICONIFY_LEN 'm', L(0), T(0), 'd', L(0), G(0.2), 'd', R(1), G(0.2), @@ -64,9 +64,9 @@ static const signed char _twin_itable[] = { 'x', 's', 'e', -#define TWIN_MAXIMIZE_LEN 28 +#define TWIN_RESTORE_LEN 28 /* Close */ -#define TWIN_CLOSE_POS TWIN_MAXIMIZE_POS + TWIN_MAXIMIZE_LEN +#define TWIN_CLOSE_POS TWIN_RESTORE_POS + TWIN_RESTORE_LEN 'm', L(0), T(0), 'd', L(0), T(0.1), 'd', G(0.4), G(0.5), @@ -103,7 +103,7 @@ static const signed char _twin_itable[] = { /* clang-format on */ const uint16_t _twin_icons[] = { - TWIN_MENU_POS, TWIN_MINIMIZE_POS, TWIN_MAXIMIZE_POS, + TWIN_MENU_POS, TWIN_ICONIFY_POS, TWIN_RESTORE_POS, TWIN_CLOSE_POS, TWIN_RESIZE_POS, }; diff --git a/src/screen.c b/src/screen.c index f8a816e..11c4274 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,11 @@ 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 iconified. */ + if (p->window->iconify && 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..a9f2fe4 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->iconify = 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->iconify; } return false; } @@ -234,8 +235,8 @@ static void twin_window_frame(twin_window_t *window) twin_fixed_t text_width; twin_fixed_t title_right; twin_fixed_t close_x; - twin_fixed_t max_x; - twin_fixed_t min_x; + twin_fixed_t restore_x; + twin_fixed_t iconify_x; twin_fixed_t resize_x; twin_fixed_t resize_y; const char *name; @@ -265,8 +266,8 @@ static void twin_window_frame(twin_window_t *window) close_x = c_right - t_arc_2 - icon_size; - max_x = close_x - bw - icon_size; - min_x = max_x - bw - icon_size; + restore_x = close_x - bw - icon_size; + iconify_x = restore_x - bw - icon_size; resize_x = twin_int_to_fixed(window->client.right); resize_y = twin_int_to_fixed(window->client.bottom); @@ -314,14 +315,14 @@ static void twin_window_frame(twin_window_t *window) twin_icon_draw(pixmap, TwinIconMenu, m); twin_matrix_identity(&m); - twin_matrix_translate(&m, min_x, icon_y); + twin_matrix_translate(&m, iconify_x, icon_y); twin_matrix_scale(&m, icon_size, icon_size); - twin_icon_draw(pixmap, TwinIconMinimize, m); + twin_icon_draw(pixmap, TwinIconIconify, m); twin_matrix_identity(&m); - twin_matrix_translate(&m, max_x, icon_y); + twin_matrix_translate(&m, restore_x, icon_y); twin_matrix_scale(&m, icon_size, icon_size); - twin_icon_draw(pixmap, TwinIconMaximize, m); + twin_icon_draw(pixmap, TwinIconRestore, m); twin_matrix_identity(&m); twin_matrix_translate(&m, close_x, icon_y); @@ -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 restore_x = close_x - bw - icon_size; + twin_fixed_t iconify_x = restore_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(iconify_x) && + local_x < twin_fixed_to_int(restore_x)) { + window->iconify = true; + twin_pixmap_damage(window->pixmap, 0, 0, window->pixmap->width, + window->pixmap->height); + } else if (local_x > twin_fixed_to_int(restore_x) && + local_x < twin_fixed_to_int(close_x)) { + window->iconify = 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 iconified window is inactive. When the box is triggered by + * TwinEventButtonDown and the window is not iconified, 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->iconify) + 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. */