]> git.sur5r.net Git - i3/i3/commitdiff
Implement tabbing
authorMichael Stapelberg <michael@stapelberg.de>
Sat, 22 Aug 2009 07:07:23 +0000 (09:07 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 22 Aug 2009 07:07:23 +0000 (09:07 +0200)
Use command "T" to switch to tabbing

include/data.h
include/layout.h
src/client.c
src/commands.c
src/handlers.c
src/layout.c
src/util.c

index 440c9a8f70c4ebdb590d2745895dad676d672220..50d8349ad02c48b59cb964fc9fac77bd72ad22c9 100644 (file)
@@ -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;
 };
 
index fdaacd72fbaf1c44e7efbdb1ebd1f6e16168af61..bd5bbcc345a59972fa8fc8797428653dd0eceaae 100644 (file)
@@ -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
index a42698055c63f1849b6965abf4fe942183dbd957..ce11561414f232cdd89e4c4359b1de901d40342b 100644 (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
            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;
index 802f0fd41436b1fa8b4a407fffc6a8d49bcbfefe..e44755306d5d8d24b684b22f3f1707aa73025376 100644 (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? */
-        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;
         }
 
index 5ffd54f943f4cb98bbaf6a248694184bdce1f7ad..4aa46fc90bdb8fca066bb757364a19a88d755002 100644 (file)
@@ -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… */
index 1d58097d9d4133ae6807a30a50b175de20cff242..98429dc383a25045923cacddb2b39d5357da7f82 100644 (file)
@@ -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,
index 8cb5409cd8d7a7dd29fe0ff7353137414ae96ff9..e9f5472e7ec1e1a4c0cc4e5a14817ac0fb5b0b20 100644 (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);
 
                 /* 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 */