Implement tabbing

Use command "T" to switch to tabbing
This commit is contained in:
Michael Stapelberg 2009-08-22 09:07:23 +02:00
parent f5c0bfd181
commit d9d4d9fff1
7 changed files with 81 additions and 31 deletions

View File

@ -453,7 +453,7 @@ struct Container {
/* Ensure MODE_DEFAULT maps to 0 because we use calloc for /* Ensure MODE_DEFAULT maps to 0 because we use calloc for
* initialization later */ * initialization later */
enum { MODE_DEFAULT = 0, MODE_STACK } mode; enum { MODE_DEFAULT = 0, MODE_STACK, MODE_TABBED } mode;
CIRCLEQ_HEAD(client_head, Client) clients; CIRCLEQ_HEAD(client_head, Client) clients;
}; };

View File

@ -29,7 +29,8 @@ int get_unoccupied_x(Workspace *workspace);
* *
*/ */
void decorate_window(xcb_connection_t *conn, Client *client, 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 its in a stacking * Redecorates the given client correctly by checking if its in a stacking

View File

@ -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 /* If the container will be empty now and is in stacking mode, we need to
unmap the stack_win */ 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"); LOG("Unmapping stack window\n");
struct Stack_Window *stack_win = &(container->stack_win); struct Stack_Window *stack_win = &(container->stack_win);
stack_win->rect.height = 0; stack_win->rect.height = 0;

View File

@ -886,13 +886,18 @@ void parse_command(xcb_connection_t *conn, const char *command) {
} }
/* Is it just 's' for stacking or 'd' for default? */ /* 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)) { if (last_focused != NULL && client_is_floating(last_focused)) {
LOG("not switching, this is a floating client\n"); LOG("not switching, this is a floating client\n");
return; return;
} }
LOG("Switching mode for current container\n"); 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; return;
} }

View File

@ -780,9 +780,11 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
if (client->dock) if (client->dock)
return 1; 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); 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); xcb_flush(conn);
return 1; return 1;
@ -845,9 +847,11 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t
if (client->dock) if (client->dock)
return 1; 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); 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); xcb_flush(conn);
return 1; return 1;
@ -923,8 +927,10 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
if (client->dock) if (client->dock)
return 1; return 1;
if (client->container == NULL || client->container->mode != MODE_STACK) if (client->container == NULL ||
decorate_window(conn, client, client->frame, client->titlegc, 0); (client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED))
decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
else { else {
uint32_t background_color; uint32_t background_color;
/* Distinguish if the window is currently focused… */ /* Distinguish if the window is currently focused… */

View File

@ -85,12 +85,14 @@ int get_unoccupied_y(Workspace *workspace, int col) {
* *
*/ */
void redecorate_window(xcb_connection_t *conn, Client *client) { 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); render_container(conn, client->container);
/* We clear the frame to generate exposure events, because the color used /* We clear the frame to generate exposure events, because the color used
in drawing may be different */ in drawing may be different */
xcb_clear_area(conn, true, client->frame, 0, 0, client->rect.width, client->rect.height); 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); 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. * 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); i3Font *font = load_font(conn, config.font);
int decoration_height = font->height + 2 + 2; int decoration_height = font->height + 2 + 2;
struct Colortriple *color; 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); xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background);
/* In stacking mode, we only render the rect for this specific decoration */ /* 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 containers width because it is the more recent value - when /* We need to use the containers width because it is the more recent value - when
in stacking mode, clients get reconfigured only on demand (the not active client in stacking mode, clients get reconfigured only on demand (the not active client
is not reconfigured), so the clients rect.width would be wrong */ is not reconfigured), so the clients 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); xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
} else { } else {
xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height}; 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) { if (client->titlebar_position != TITLEBAR_OFF) {
/* Draw the lines */ /* 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 || 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)) CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients) == NULL))
xcb_draw_line(conn, drawable, gc, color->border, 2, offset + font->height + 3, xcb_draw_line(conn, drawable, gc, color->border,
client->rect.width - 3, offset + font->height + 3); 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 */ /* 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 dont handle the old window name (COMPOUND_TEXT) but only _NET_WM_NAME, which and we dont handle the old window name (COMPOUND_TEXT) but only _NET_WM_NAME, which
is UTF-8 */ is UTF-8 */
if (client->name_len == -1) if (client->name_len == -1)
xcb_image_text_8(conn, strlen(client->name), drawable, gc, 3 /* X */, xcb_image_text_8(conn, strlen(client->name), drawable, gc, offset_x + 3 /* X */,
offset + font->height /* Y = baseline of font */, client->name); offset_y + font->height /* Y = baseline of font */, client->name);
else else
xcb_image_text_16(conn, client->name_len, drawable, gc, 3 /* X */, xcb_image_text_16(conn, client->name_len, drawable, gc, offset_x + 3 /* X */,
offset + font->height /* Y = baseline of font */, (xcb_char2b_t*)client->name); 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); Rect *rect = &(client->child_rect);
switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) { switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) {
case MODE_STACK: case MODE_STACK:
case MODE_TABBED:
rect->x = 2; rect->x = 2;
rect->y = 0; rect->y = 0;
rect->width = client->rect.width - (2 + 2); 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); i3Font *font = load_font(conn, config.font);
int decoration_height = (font->height + 2 + 2); int decoration_height = (font->height + 2 + 2);
struct Stack_Window *stack_win = &(container->stack_win); 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 /* Check if we need to remap our stack title window, it gets unmapped when the container
is empty in src/handlers.c:unmap_notify() */ 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); 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 */ /* Check if we need to reconfigure our stack title window */
if (update_if_necessary(&(stack_win->rect.x), container->x) | 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. /* 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 | if (client->force_reconfigure |
update_if_necessary(&(client->rect.x), container->x) | update_if_necessary(&(client->rect.x), container->x) |
update_if_necessary(&(client->rect.y), container->y + (decoration_height * num_clients)) | 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; 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, 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, xcb_copy_area(conn, stack_win->pixmap.id, stack_win->window, stack_win->pixmap.gc,

View File

@ -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); 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 */ /* 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 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 */ * we might have just gone into stacking mode and need to raise */
Client *last_focused = get_last_focused_client(conn, client->container, client); 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) { void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) {
if (mode == MODE_STACK) { if (mode == MODE_STACK || mode == MODE_TABBED) {
/* When were already in stacking mode, nothing has to be done */ /* When were 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; 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 /* 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 dont bother here with title bars of the clients, it has height 1 because we dont bother here with
calculating the correct height - it will be adjusted when rendering anyways. */ 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); SLIST_INSERT_HEAD(&stack_wins, stack_win, stack_windows);
} else { } else {
if (container->mode == MODE_STACK) if (container->mode == MODE_STACK || container->mode == MODE_TABBED)
leave_stack_mode(conn, container); leave_stack_mode(conn, container);
} }
after_stackwin:
container->mode = mode; container->mode = mode;
/* Force reconfiguration of each client */ /* Force reconfiguration of each client */