Make containers containing exactly one window behave like default containers

Starting from this commit, a borderless window will always be
borderless if it is the only window in a container. For example,
you can have Firefox borderless in a tabbed container and as soon
as the download manager or a viewer gets opened, the container
will be rendered like a normal tabbed container.

This solves the user-interface dilemma of borderless/1-px-border
windows inside stacked/tabbed containers, at least for this special
case. Thanks to Merovius for this suggestion.
This commit is contained in:
Michael Stapelberg 2009-12-22 23:40:06 +01:00
parent 75ac464c0d
commit 848d9c1b01
5 changed files with 106 additions and 28 deletions

View File

@ -3,7 +3,7 @@
* *
* i3 - an improved dynamic tiling window manager * i3 - an improved dynamic tiling window manager
* *
* (c) 2009 Michael Stapelberg and contributors * © 2009 Michael Stapelberg and contributors
* *
* See file LICENSE for license information. * See file LICENSE for license information.
* *

26
include/container.h Normal file
View File

@ -0,0 +1,26 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
*/
#include "data.h"
#ifndef _CONTAINER_H
#define _CONTAINER_H
/**
* Returns the mode of the given container (or MODE_DEFAULT if a NULL pointer
* was passed in order to save a few explicit checks in other places). If
* for_frame was set to true, the special case of having exactly one client
* in a container is handled so that MODE_DEFAULT is returned. For some parts
* of the rendering, this is interesting, other parts need the real mode.
*
*/
int container_mode(Container *con, bool for_frame);
#endif

43
src/container.c Normal file
View File

@ -0,0 +1,43 @@
/*
* vim:ts=8:expandtab
*
* i3 - an improved dynamic tiling window manager
*
* © 2009 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
*/
#include "data.h"
#include "log.h"
/*
* Returns the mode of the given container (or MODE_DEFAULT if a NULL pointer
* was passed in order to save a few explicit checks in other places). If
* for_frame was set to true, the special case of having exactly one client
* in a container is handled so that MODE_DEFAULT is returned. For some parts
* of the rendering, this is interesting, other parts need the real mode.
*
*/
int container_mode(Container *con, bool for_frame) {
int num_clients = 0;
Client *client;
if (con == NULL || con->mode == MODE_DEFAULT)
return MODE_DEFAULT;
if (!for_frame)
return con->mode;
CIRCLEQ_FOREACH(client, &(con->clients), clients)
num_clients++;
/* If the container contains only one client, mode is irrelevant */
if (num_clients == 1) {
DLOG("mode to default\n");
return MODE_DEFAULT;
}
return con->mode;
}

View File

@ -37,6 +37,7 @@
#include "floating.h" #include "floating.h"
#include "workspace.h" #include "workspace.h"
#include "log.h" #include "log.h"
#include "container.h"
/* After mapping/unmapping windows, a notify event is generated. However, we dont want it, /* After mapping/unmapping windows, a notify event is generated. However, we dont want it,
since itd trigger an infinite loop of switching between the different windows when since itd trigger an infinite loop of switching between the different windows when
@ -605,9 +606,8 @@ 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 && int mode = container_mode(client->container, true);
(client->container->mode == MODE_STACK || if (mode == MODE_STACK || mode == MODE_TABBED)
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, 0); else decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
xcb_flush(conn); xcb_flush(conn);
@ -752,9 +752,7 @@ 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 || if (container_mode(client->container, true) == MODE_DEFAULT)
(client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED))
decorate_window(conn, client, client->frame, client->titlegc, 0, 0); decorate_window(conn, client, client->frame, client->titlegc, 0, 0);
else { else {
uint32_t background_color; uint32_t background_color;
@ -779,7 +777,7 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
/* Draw a black background */ /* Draw a black background */
xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
if (client->titlebar_position == TITLEBAR_OFF) { if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
xcb_rectangle_t crect = {1, 0, client->rect.width - (1 + 1), client->rect.height - 1}; xcb_rectangle_t crect = {1, 0, client->rect.width - (1 + 1), client->rect.height - 1};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect); xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
} else { } else {

View File

@ -29,6 +29,7 @@
#include "handlers.h" #include "handlers.h"
#include "workspace.h" #include "workspace.h"
#include "log.h" #include "log.h"
#include "container.h"
/* /*
* Updates *destination with new_value and returns true if it was changed or false * Updates *destination with new_value and returns true if it was changed or false
@ -141,16 +142,15 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
- Draw two lines in a lighter color - Draw two lines in a lighter color
- Draw the windows title - Draw the windows title
*/ */
int mode = container_mode(client->container, true);
/* Draw a rectangle in background color around the window */ /* Draw a rectangle in background color around the window */
if (client->borderless && (client->container == NULL || if (client->borderless && mode == MODE_DEFAULT)
(client->container->mode != MODE_STACK &&
client->container->mode != MODE_TABBED)))
xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
else xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, color->background); else 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 || client->container->mode == MODE_TABBED)) { if (mode == MODE_STACK || 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 */
@ -165,7 +165,10 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
/* Draw the inner background to have a black frame around clients (such as mplayer) /* Draw the inner background to have a black frame around clients (such as mplayer)
which cannot be resized exactly in our frames and therefore are centered */ which cannot be resized exactly in our frames and therefore are centered */
xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000")); xcb_change_gc_single(conn, client->titlegc, XCB_GC_FOREGROUND, get_colorpixel(conn, "#000000"));
if (client->titlebar_position == TITLEBAR_OFF) { if (client->titlebar_position == TITLEBAR_OFF && client->borderless) {
xcb_rectangle_t crect = {0, 0, client->rect.width, client->rect.height};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
} else if (client->titlebar_position == TITLEBAR_OFF && !client->borderless) {
xcb_rectangle_t crect = {1, 1, client->rect.width - (1 + 1), client->rect.height - (1 + 1)}; xcb_rectangle_t crect = {1, 1, client->rect.width - (1 + 1), client->rect.height - (1 + 1)};
xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect); xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &crect);
} else { } else {
@ -175,13 +178,13 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
} }
} }
mode = container_mode(client->container, false);
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, offset_x, offset_y, offset_x + client->rect.width, offset_y); xcb_draw_line(conn, drawable, gc, color->border, offset_x, offset_y, offset_x + client->rect.width, offset_y);
if ((client->container == NULL || if (mode == MODE_DEFAULT ||
(client->container->mode != MODE_STACK && CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients) == NULL)
client->container->mode != MODE_TABBED) ||
CIRCLEQ_NEXT_OR_NULL(&(client->container->clients), client, clients) == NULL))
xcb_draw_line(conn, drawable, gc, color->border, xcb_draw_line(conn, drawable, gc, color->border,
offset_x + 2, /* x */ offset_x + 2, /* x */
offset_y + font->height + 3, /* y */ offset_y + font->height + 3, /* y */
@ -191,9 +194,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
/* If the client has a title, we draw it */ /* If the client has a title, we draw it */
if (client->name != NULL && if (client->name != NULL &&
((client->container != NULL && (mode != MODE_DEFAULT || client->titlebar_position != TITLEBAR_OFF)) {
client->container->mode != MODE_DEFAULT) ||
client->titlebar_position != TITLEBAR_OFF)) {
/* Draw the font */ /* Draw the font */
uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
uint32_t values[] = { color->text, color->background, font->id }; uint32_t values[] = { color->text, color->background, font->id };
@ -267,7 +268,7 @@ void resize_client(xcb_connection_t *conn, Client *client) {
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT; XCB_CONFIG_WINDOW_HEIGHT;
Rect *rect = &(client->child_rect); Rect *rect = &(client->child_rect);
switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) { switch (container_mode(client->container, true)) {
case MODE_STACK: case MODE_STACK:
case MODE_TABBED: case MODE_TABBED:
rect->x = 2; rect->x = 2;
@ -409,7 +410,7 @@ void render_container(xcb_connection_t *conn, Container *container) {
/* 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 > 1) {
DLOG("remapping stack win\n"); DLOG("remapping stack win\n");
xcb_map_window(conn, stack_win->window); xcb_map_window(conn, stack_win->window);
} else DLOG("not remapping stackwin, height = %d, num_clients = %d\n", } else DLOG("not remapping stackwin, height = %d, num_clients = %d\n",
@ -429,11 +430,21 @@ void render_container(xcb_connection_t *conn, Container *container) {
stack_lines = min(num_clients, container->stack_limit_value); stack_lines = min(num_clients, container->stack_limit_value);
} }
int height = decoration_height * stack_lines;
if (num_clients == 1) {
height = 0;
stack_win->rect.height = 0;
xcb_unmap_window(conn, stack_win->window);
DLOG("Just one client, setting height to %d\n", height);
}
/* 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 (height > 0 && (
update_if_necessary(&(stack_win->rect.y), container->y) | update_if_necessary(&(stack_win->rect.x), container->x) |
update_if_necessary(&(stack_win->rect.width), container->width) | update_if_necessary(&(stack_win->rect.y), container->y) |
update_if_necessary(&(stack_win->rect.height), decoration_height * stack_lines)) { update_if_necessary(&(stack_win->rect.width), container->width) |
update_if_necessary(&(stack_win->rect.height), height))) {
/* Configuration can happen in two slightly different ways: /* Configuration can happen in two slightly different ways:
@ -497,9 +508,9 @@ void render_container(xcb_connection_t *conn, Container *container) {
* Note the bitwise OR instead of logical OR to force evaluation of all 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 * stack_lines)) | update_if_necessary(&(client->rect.y), container->y + height) |
update_if_necessary(&(client->rect.width), container->width) | update_if_necessary(&(client->rect.width), container->width) |
update_if_necessary(&(client->rect.height), container->height - (decoration_height * stack_lines))) update_if_necessary(&(client->rect.height), container->height - height))
resize_client(conn, client); resize_client(conn, client);
client->force_reconfigure = false; client->force_reconfigure = false;