From d9d4d9fff10db78892fb20c6f2db3650ea70edcd Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 22 Aug 2009 09:07:23 +0200 Subject: [PATCH] Implement tabbing Use command "T" to switch to tabbing --- include/data.h | 2 +- include/layout.h | 3 ++- src/client.c | 4 +++- src/commands.c | 9 +++++-- src/handlers.c | 18 +++++++++----- src/layout.c | 62 +++++++++++++++++++++++++++++++++++------------- src/util.c | 14 +++++++---- 7 files changed, 81 insertions(+), 31 deletions(-) diff --git a/include/data.h b/include/data.h index 440c9a8f..50d8349a 100644 --- a/include/data.h +++ b/include/data.h @@ -453,7 +453,7 @@ struct Container { /* Ensure MODE_DEFAULT maps to 0 because we use calloc for * initialization later */ - enum { MODE_DEFAULT = 0, MODE_STACK } mode; + enum { MODE_DEFAULT = 0, MODE_STACK, MODE_TABBED } mode; CIRCLEQ_HEAD(client_head, Client) clients; }; diff --git a/include/layout.h b/include/layout.h index fdaacd72..bd5bbcc3 100644 --- a/include/layout.h +++ b/include/layout.h @@ -29,7 +29,8 @@ int get_unoccupied_x(Workspace *workspace); * */ void decorate_window(xcb_connection_t *conn, Client *client, - xcb_drawable_t drawable, xcb_gcontext_t gc, int offset); + xcb_drawable_t drawable, xcb_gcontext_t gc, + int offset_x, int offset_y); /** * Redecorates the given client correctly by checking if it’s in a stacking diff --git a/src/client.c b/src/client.c index a4269805..ce115614 100644 --- a/src/client.c +++ b/src/client.c @@ -38,7 +38,9 @@ void client_remove_from_container(xcb_connection_t *conn, Client *client, Contai /* If the container will be empty now and is in stacking mode, we need to unmap the stack_win */ - if (CIRCLEQ_EMPTY(&(container->clients)) && container->mode == MODE_STACK) { + if (CIRCLEQ_EMPTY(&(container->clients)) && + (container->mode == MODE_STACK || + container->mode == MODE_TABBED)) { LOG("Unmapping stack window\n"); struct Stack_Window *stack_win = &(container->stack_win); stack_win->rect.height = 0; diff --git a/src/commands.c b/src/commands.c index 802f0fd4..e4475530 100644 --- a/src/commands.c +++ b/src/commands.c @@ -886,13 +886,18 @@ void parse_command(xcb_connection_t *conn, const char *command) { } /* Is it just 's' for stacking or 'd' for default? */ - if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) { + if ((command[0] == 's' || command[0] == 'd' || command[0] == 'T') && (command[1] == '\0')) { if (last_focused != NULL && client_is_floating(last_focused)) { LOG("not switching, this is a floating client\n"); return; } LOG("Switching mode for current container\n"); - switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT)); + int new_mode = MODE_DEFAULT; + if (command[0] == 's') + new_mode = MODE_STACK; + if (command[0] == 'T') + new_mode = MODE_TABBED; + switch_layout_mode(conn, CUR_CELL, new_mode); return; } diff --git a/src/handlers.c b/src/handlers.c index 5ffd54f9..4aa46fc9 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -780,9 +780,11 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, if (client->dock) return 1; - if (client->container != NULL && client->container->mode == MODE_STACK) + if (client->container != NULL && + (client->container->mode == MODE_STACK || + client->container->mode == MODE_TABBED)) render_container(conn, client->container); - else decorate_window(conn, client, client->frame, client->titlegc, 0); + else decorate_window(conn, client, client->frame, client->titlegc, 0, 0); xcb_flush(conn); return 1; @@ -845,9 +847,11 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t if (client->dock) return 1; - if (client->container != NULL && client->container->mode == MODE_STACK) + if (client->container != NULL && + (client->container->mode == MODE_STACK || + client->container->mode == MODE_TABBED)) render_container(conn, client->container); - else decorate_window(conn, client, client->frame, client->titlegc, 0); + else decorate_window(conn, client, client->frame, client->titlegc, 0, 0); xcb_flush(conn); return 1; @@ -923,8 +927,10 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * if (client->dock) return 1; - if (client->container == NULL || client->container->mode != MODE_STACK) - decorate_window(conn, client, client->frame, client->titlegc, 0); + if (client->container == NULL || + (client->container->mode != MODE_STACK && + client->container->mode != MODE_TABBED)) + decorate_window(conn, client, client->frame, client->titlegc, 0, 0); else { uint32_t background_color; /* Distinguish if the window is currently focused… */ diff --git a/src/layout.c b/src/layout.c index 1d58097d..98429dc3 100644 --- a/src/layout.c +++ b/src/layout.c @@ -85,12 +85,14 @@ int get_unoccupied_y(Workspace *workspace, int col) { * */ void redecorate_window(xcb_connection_t *conn, Client *client) { - if (client->container != NULL && client->container->mode == MODE_STACK) { + if (client->container != NULL && + (client->container->mode == MODE_STACK || + client->container->mode == MODE_TABBED)) { render_container(conn, client->container); /* We clear the frame to generate exposure events, because the color used in drawing may be different */ xcb_clear_area(conn, true, client->frame, 0, 0, client->rect.width, client->rect.height); - } else decorate_window(conn, client, client->frame, client->titlegc, 0); + } else decorate_window(conn, client, client->frame, client->titlegc, 0, 0); xcb_flush(conn); } @@ -99,7 +101,8 @@ void redecorate_window(xcb_connection_t *conn, Client *client) { * When in stacking mode, the window decorations are drawn onto an own window. * */ -void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, xcb_gcontext_t gc, int offset) { +void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, + xcb_gcontext_t gc, int offset_x, int offset_y) { i3Font *font = load_font(conn, config.font); int decoration_height = font->height + 2 + 2; struct Colortriple *color; @@ -134,11 +137,13 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background); /* In stacking mode, we only render the rect for this specific decoration */ - if (client->container != NULL && client->container->mode == MODE_STACK) { + if (client->container != NULL && (client->container->mode == MODE_STACK || client->container->mode == MODE_TABBED)) { /* We need to use the container’s width because it is the more recent value - when in stacking mode, clients get reconfigured only on demand (the not active client is not reconfigured), so the client’s rect.width would be wrong */ - xcb_rectangle_t rect = {0, offset, client->container->width, offset + decoration_height }; + xcb_rectangle_t rect = {offset_x, offset_y, + offset_x + client->container->width, + offset_y + decoration_height }; xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect); } else { xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height}; @@ -154,12 +159,16 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw if (client->titlebar_position != TITLEBAR_OFF) { /* Draw the lines */ - xcb_draw_line(conn, drawable, gc, color->border, 0, offset, client->rect.width, offset); + xcb_draw_line(conn, drawable, gc, color->border, offset_x, offset_y, offset_x + client->rect.width, offset_y); if ((client->container == NULL || - client->container->mode != MODE_STACK || + (client->container->mode != MODE_STACK && + client->container->mode != MODE_TABBED) || CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients) == NULL)) - xcb_draw_line(conn, drawable, gc, color->border, 2, offset + font->height + 3, - client->rect.width - 3, offset + font->height + 3); + xcb_draw_line(conn, drawable, gc, color->border, + offset_x + 2, /* x */ + offset_y + font->height + 3, /* y */ + offset_x + client->rect.width - 3, /* to_x */ + offset_y + font->height + 3 /* to_y */); } /* If the client has a title, we draw it */ @@ -173,11 +182,11 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw and we don’t handle the old window name (COMPOUND_TEXT) but only _NET_WM_NAME, which is UTF-8 */ if (client->name_len == -1) - xcb_image_text_8(conn, strlen(client->name), drawable, gc, 3 /* X */, - offset + font->height /* Y = baseline of font */, client->name); + xcb_image_text_8(conn, strlen(client->name), drawable, gc, offset_x + 3 /* X */, + offset_y + font->height /* Y = baseline of font */, client->name); else - xcb_image_text_16(conn, client->name_len, drawable, gc, 3 /* X */, - offset + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name); + xcb_image_text_16(conn, client->name_len, drawable, gc, offset_x + 3 /* X */, + offset_y + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name); } } @@ -239,6 +248,7 @@ void resize_client(xcb_connection_t *conn, Client *client) { Rect *rect = &(client->child_rect); switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) { case MODE_STACK: + case MODE_TABBED: rect->x = 2; rect->y = 0; rect->width = client->rect.width - (2 + 2); @@ -350,11 +360,25 @@ void render_container(xcb_connection_t *conn, Container *container) { i3Font *font = load_font(conn, config.font); int decoration_height = (font->height + 2 + 2); struct Stack_Window *stack_win = &(container->stack_win); + /* The size for each tab (width), necessary as a separate variable + * because num_clients gets fixed to 1 in tabbed mode. */ + int size_each = (num_clients == 0 ? container->width : container->width / num_clients); /* Check if we need to remap our stack title window, it gets unmapped when the container is empty in src/handlers.c:unmap_notify() */ - if (stack_win->rect.height == 0 && num_clients > 0) + if (stack_win->rect.height == 0 && num_clients > 0) { + LOG("remapping stack win\n"); xcb_map_window(conn, stack_win->window); + } else LOG("not remapping stackwin, height = %d, num_clients = %d\n", + stack_win->rect.height, num_clients); + + if (container->mode == MODE_TABBED) { + /* By setting num_clients to 1 we force that the stack window will be only one line + * high. The rest of the code is useful in both cases. */ + LOG("tabbed mode, setting num_clients = 1\n"); + if (num_clients > 1) + num_clients = 1; + } /* Check if we need to reconfigure our stack title window */ if (update_if_necessary(&(stack_win->rect.x), container->x) | @@ -405,7 +429,7 @@ void render_container(xcb_connection_t *conn, Container *container) { } /* Check if we changed client->x or client->y by updating it. - * Note the bitwise OR instead of logical OR to force evaluation of both statements */ + * Note the bitwise OR instead of logical OR to force evaluation of all statements */ if (client->force_reconfigure | update_if_necessary(&(client->rect.x), container->x) | update_if_necessary(&(client->rect.y), container->y + (decoration_height * num_clients)) | @@ -415,8 +439,14 @@ void render_container(xcb_connection_t *conn, Container *container) { client->force_reconfigure = false; + int offset_x = 0; + int offset_y = 0; + if (container->mode == MODE_STACK) + offset_y = current_client++ * decoration_height; + else if (container->mode == MODE_TABBED) + offset_x = current_client++ * size_each; decorate_window(conn, client, stack_win->pixmap.id, stack_win->pixmap.gc, - current_client++ * decoration_height); + offset_x, offset_y); } xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc, diff --git a/src/util.c b/src/util.c index 8cb5409c..e9f5472e 100644 --- a/src/util.c +++ b/src/util.c @@ -270,7 +270,8 @@ void set_focus(xcb_connection_t *conn, Client *client, bool set_anyways) { Client *last_focused = get_last_focused_client(conn, client->container, NULL); /* In stacking containers, raise the client in respect to the one which was focused before */ - if (client->container->mode == MODE_STACK && client->container->workspace->fullscreen_client == NULL) { + if ((client->container->mode == MODE_STACK || client->container->mode == MODE_TABBED) && + client->container->workspace->fullscreen_client == NULL) { /* We need to get the client again, this time excluding the current client, because * we might have just gone into stacking mode and need to raise */ Client *last_focused = get_last_focused_client(conn, client->container, client); @@ -339,11 +340,15 @@ void leave_stack_mode(xcb_connection_t *conn, Container *container) { * */ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) { - if (mode == MODE_STACK) { + if (mode == MODE_STACK || mode == MODE_TABBED) { /* When we’re already in stacking mode, nothing has to be done */ - if (container->mode == MODE_STACK) + if ((mode == MODE_STACK && container->mode == MODE_STACK) || + (mode == MODE_TABBED && container->mode == MODE_TABBED)) return; + if (container->mode == MODE_STACK || container->mode == MODE_TABBED) + goto after_stackwin; + /* When entering stacking mode, we need to open a window on which we can draw the title bars of the clients, it has height 1 because we don’t bother here with calculating the correct height - it will be adjusted when rendering anyways. */ @@ -377,9 +382,10 @@ void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) SLIST_INSERT_HEAD(&stack_wins, stack_win, stack_windows); } else { - if (container->mode == MODE_STACK) + if (container->mode == MODE_STACK || container->mode == MODE_TABBED) leave_stack_mode(conn, container); } +after_stackwin: container->mode = mode; /* Force reconfiguration of each client */ -- 2.39.5