URI: 
       tAdd graphics helper functions - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 657c2bb9354fc1f5d1e224c5db412ce0c4da447f
   DIR parent 997414b6693f3a4bc08c43347baa4e829d8e6cca
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat, 27 Feb 2021 21:26:15 +0100
       
       Add graphics helper functions
       
       These aren't used everywhere yet, and everything is still a bit buggy,
       but that will hopefully change eventually.
       
       Diffstat:
         M .gitignore                          |       3 ---
         M LICENSE                             |       3 ++-
         M Makefile                            |       4 +++-
         A src/.gitignore                      |       4 ++++
         M src/box.c                           |      21 +++++++++++----------
         M src/button.c                        |      57 +++++++++----------------------
         M src/draw.c                          |       7 ++++++-
         A src/graphics.c                      |      57 +++++++++++++++++++++++++++++++
         A src/graphics.h                      |      38 +++++++++++++++++++++++++++++++
         M src/grid.c                          |      14 +++++++-------
         M src/label.c                         |      37 ++++++++++++++++++-------------
         M src/ltkd.c                          |       7 +++----
         M src/scrollbar.c                     |       7 +++++--
         M src/widget.c                        |      45 ++++++++++++++++++++++++-------
         M src/widget.h                        |      14 ++++++++++----
       
       15 files changed, 219 insertions(+), 99 deletions(-)
       ---
   DIR diff --git a/.gitignore b/.gitignore
       t@@ -1,5 +1,2 @@
       -ltkd
       -ltkc
       -ltk.sock
        *.o
        *.core
   DIR diff --git a/LICENSE b/LICENSE
       t@@ -1,4 +1,5 @@
       -See khash.h, ini.*, stb_truetype.*, and strtonum.c for third-party licenses.
       +See src/khash.h, src/ini.*, src/stb_truetype.*, and src/strtonum.c
       +for third-party licenses.
        
        ISC License
        
   DIR diff --git a/Makefile b/Makefile
       t@@ -47,6 +47,7 @@ OBJ = \
                src/button.o \
                src/label.o \
                src/draw.o \
       +        src/graphics.o \
                $(EXTRA_OBJ)
        
        # Note: This could be improved so a change in a header only causes the .c files
       t@@ -68,7 +69,8 @@ HDR = \
                src/scrollbar.h \
                src/stb_truetype.h \
                src/text.h \
       -        src/util.h
       +        src/util.h \
       +        src/graphics.h
        
        CFLAGS += $(EXTRA_CFLAGS)
        LDFLAGS += $(EXTRA_LDFLAGS)
   DIR diff --git a/src/.gitignore b/src/.gitignore
       t@@ -0,0 +1,4 @@
       +ltkd
       +ltkc
       +*.o
       +*.core
   DIR diff --git a/src/box.c b/src/box.c
       t@@ -55,7 +55,10 @@ static struct ltk_widget_vtable vtable = {
                .remove_child = &ltk_box_remove,
                .mouse_press = &ltk_box_mouse_press,
                .mouse_release = &ltk_box_mouse_release,
       -        .motion_notify = &ltk_box_motion_notify
       +        .motion_notify = &ltk_box_motion_notify,
       +        .needs_redraw = 0,
       +        .needs_pixmap = 0,
       +        .type = LTK_BOX
        };
        
        static int ltk_box_cmd_add(
       t@@ -99,7 +102,7 @@ static ltk_box *
        ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient) {
                ltk_box *box = ltk_malloc(sizeof(ltk_box));
        
       -        ltk_fill_widget_defaults(&box->widget, id, window, &vtable, 0, LTK_BOX);
       +        ltk_fill_widget_defaults(&box->widget, id, window, &vtable, 0, 0);
        
                box->sc = ltk_scrollbar_create(window, orient, &ltk_box_scroll, box);
                box->widgets = NULL;
       t@@ -150,8 +153,7 @@ ltk_recalculate_box(ltk_widget *self) {
                                        ptr->rect.y = box->widget.rect.y + box->widget.rect.h - ptr->rect.h - sc_rect->h;
                                else
                                        ptr->rect.y = box->widget.rect.y + (box->widget.rect.h - ptr->rect.h) / 2;
       -                        if (ptr->vtable->resize)
       -                                ptr->vtable->resize(ptr);
       +                        ltk_widget_resize(ptr);
                                cur_pos += ptr->rect.w;
                        } else {
                                ptr->rect.y = cur_pos - box->sc->cur_pos;
       t@@ -163,8 +165,7 @@ ltk_recalculate_box(ltk_widget *self) {
                                        ptr->rect.x = box->widget.rect.x + box->widget.rect.w - ptr->rect.w - sc_rect->w;
                                else
                                        ptr->rect.x = box->widget.rect.x + (box->widget.rect.w - ptr->rect.w) / 2;
       -                        if (ptr->vtable->resize)
       -                                ptr->vtable->resize(ptr);
       +                        ltk_widget_resize(ptr);
                                cur_pos += ptr->rect.h;
                        }
                }
       t@@ -278,16 +279,16 @@ ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_widget *self, char **
                                                if (box->widgets[j]->ideal_h + sc_h > box->widget.ideal_h)
                                                        box->widget.ideal_h = box->widgets[j]->ideal_h + sc_h;
                                        }
       -                                if (box->widget.parent && box->widget.parent->vtable->resize)
       -                                        box->widget.parent->vtable->resize(box->widget.parent);
       +                                if (box->widget.parent)
       +                                        ltk_widget_resize(box->widget.parent);
                                } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w == box->widget.ideal_w) {
                                        box->widget.ideal_w = 0;
                                        for (size_t j = 0; j < box->num_widgets; j++) {
                                                if (box->widgets[j]->ideal_w + sc_w > box->widget.ideal_w)
                                                        box->widget.ideal_w = box->widgets[j]->ideal_w + sc_w;
                                        }
       -                                if (box->widget.parent && box->widget.parent->vtable->resize)
       -                                        box->widget.parent->vtable->resize(box->widget.parent);
       +                                if (box->widget.parent)
       +                                        ltk_widget_resize(box->widget.parent);
                                }
                                return 0;
                        }
   DIR diff --git a/src/button.c b/src/button.c
       t@@ -31,6 +31,7 @@
        #include "util.h"
        #include "text.h"
        #include "button.h"
       +#include "graphics.h"
        
        static void ltk_button_draw(ltk_widget *self, ltk_rect clip);
        static int ltk_button_mouse_release(ltk_widget *self, XEvent event);
       t@@ -38,14 +39,16 @@ static ltk_button *ltk_button_create(ltk_window *window,
            const char *id, const char *text);
        static void ltk_button_destroy(ltk_widget *self, int shallow);
        static void ltk_button_change_state(ltk_widget *self);
       -static void ltk_button_resize(ltk_widget *self);
       +static void ltk_button_redraw_pixmap(ltk_button *button);
        
        static struct ltk_widget_vtable vtable = {
            .mouse_release = &ltk_button_mouse_release,
            .change_state = &ltk_button_change_state,
            .draw = &ltk_button_draw,
            .destroy = &ltk_button_destroy,
       -    .resize = &ltk_button_resize
       +    .type = LTK_BUTTON,
       +    .needs_redraw = 1,
       +    .needs_pixmap = 1
        };
        
        static struct {
       t@@ -136,12 +139,12 @@ ltk_button_draw(ltk_widget *self, ltk_rect clip) {
                ltk_window *window = button->widget.window;
                ltk_rect rect = button->widget.rect;
                ltk_rect clip_final = ltk_rect_intersect(clip, rect);
       +        if (self->dirty)
       +                ltk_button_redraw_pixmap(button);
                /* no idea why it would be less than 0, but whatever */
                if (clip_final.w <= 0 || clip_final.h <= 0)
                        return;
       -        XCopyArea(window->dpy, button->pixmap, window->xwindow, window->gc,
       -            clip_final.x - rect.x, clip_final.y - rect.y,
       -            clip_final.w, clip_final.h, clip_final.x, clip_final.y);
       +        ltk_copy_clipped(self, clip_final);
        }
        
        static void
       t@@ -171,15 +174,10 @@ ltk_button_redraw_pixmap(ltk_button *button) {
                default:
                        ltk_fatal("No style found for button!\n");
                }
       -        XSetForeground(window->dpy, window->gc, fill->xcolor.pixel);
       -        XFillRectangle(window->dpy, button->pixmap, window->gc, 0, 0, rect.w, rect.h);
       -        /* FIXME: Why did I do this? */
       -        if (bw < 1) return;
       -        XSetForeground(window->dpy, window->gc, border->xcolor.pixel);
       -        XSetLineAttributes(window->dpy, window->gc, bw, LineSolid,
       -            CapButt, JoinMiter);
       -        XDrawRectangle(window->dpy, button->pixmap, window->gc,
       -            bw / 2, bw / 2, rect.w - bw, rect.h - bw);
       +        rect.x = 0;
       +        rect.y = 0;
       +        ltk_fill_widget_rect(&button->widget, fill, rect);
       +        ltk_draw_widget_rect(&button->widget, border, rect, bw);
        
                int text_w, text_h;
                ltk_text_line_get_size(button->tl, &text_w, &text_h);
       t@@ -187,29 +185,8 @@ ltk_button_redraw_pixmap(ltk_button *button) {
                int text_y = (rect.h - text_h) / 2;
                /* FIXME: Remove clipping rect from text line - this is just used here as a dummy
                   because it is completely ignored */
       -        ltk_text_line_draw(button->tl, button->pixmap, window->gc, text_x, text_y, rect);
       -}
       -
       -/* FIXME: Make this amortised constant; make it generic for all widgets */
       -static void
       -ltk_button_resize(ltk_widget *self) {
       -        ltk_button *button = (ltk_button *)self;
       -        Window win;
       -        int x, y;
       -        unsigned int w, h, bw, d;
       -        unsigned int new_w, new_h;
       -        ltk_window *window = button->widget.window;
       -        ltk_rect rect = button->widget.rect;
       -        XGetGeometry(window->dpy, button->pixmap, &win, &x, &y, &w, &h, &bw, &d);
       -
       -        new_w = (int)w < rect.w ? rect.w : (int)w;
       -        new_h = (int)h < rect.h ? rect.h : (int)h;
       -        if (new_w < w && new_h < h)
       -                return;
       -        XFreePixmap(window->dpy, button->pixmap);
       -        button->pixmap = XCreatePixmap(window->dpy, window->xwindow,
       -            new_w, new_h, window->depth);
       -        ltk_button_redraw_pixmap(button);
       +        ltk_text_line_draw(button->tl, button->widget.pixmap, window->gc, text_x, text_y, rect);
       +        button->widget.dirty = 0;
        }
        
        static void
       t@@ -233,7 +210,7 @@ ltk_button_change_state(ltk_widget *self) {
                        ltk_fatal("No style found for button!\n");
                }
                ltk_text_line_render(button->tl, fill, &theme.text_color);
       -        ltk_button_redraw_pixmap(button);
       +        self->dirty = 1;
        }
        
        static int
       t@@ -249,7 +226,6 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) {
                char *text_copy;
                ltk_button *button = ltk_malloc(sizeof(ltk_button));
        
       -        ltk_fill_widget_defaults(&button->widget, id, window, &vtable, 1, LTK_BUTTON);
                uint16_t font_size = window->theme.font_size;
                text_copy = ltk_strdup(text);
                button->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1);
       t@@ -257,8 +233,7 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) {
                ltk_text_line_get_size(button->tl, &text_w, &text_h);
                button->widget.ideal_w = text_w + theme.border_width * 2 + theme.pad * 2;
                button->widget.ideal_h = text_h + theme.border_width * 2 + theme.pad * 2;
       -        button->pixmap = XCreatePixmap(window->dpy, window->xwindow,
       -            button->widget.ideal_w, button->widget.ideal_h, window->depth);
       +        ltk_fill_widget_defaults(&button->widget, id, window, &vtable, button->widget.ideal_w, button->widget.ideal_h);
                /* render text */
                ltk_button_change_state((ltk_widget *)button);
        
   DIR diff --git a/src/draw.c b/src/draw.c
       t@@ -45,6 +45,11 @@ static struct ltk_widget_vtable vtable = {
                .draw = &ltk_draw_draw,
                .resize = &ltk_draw_resize,
                .destroy = &ltk_draw_destroy,
       +        .type = LTK_DRAW,
       +        .needs_redraw = 1,
       +        /* FIXME: use the widget pixmap here and store the drawn stuff
       +           logically as paths, not just on the pixmap */
       +        .needs_pixmap = 0
        };
        
        static int ltk_draw_cmd_clear(
       t@@ -87,7 +92,7 @@ static ltk_draw *
        ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *color) {
                ltk_draw *draw = ltk_malloc(sizeof(ltk_draw));
        
       -        ltk_fill_widget_defaults(&draw->widget, id, window, &vtable, 1, LTK_DRAW);
       +        ltk_fill_widget_defaults(&draw->widget, id, window, &vtable, w, h);
                draw->widget.rect.w = w;
                draw->widget.rect.h = h;
                draw->pix = XCreatePixmap(window->dpy, window->xwindow, w, h, window->depth);
   DIR diff --git a/src/graphics.c b/src/graphics.c
       t@@ -0,0 +1,57 @@
       +/*
       + * Copyright (c) 2021 lumidify <nobody@lumidify.org>
       + *
       + * Permission to use, copy, modify, and distribute this software for any
       + * purpose with or without fee is hereby granted, provided that the above
       + * copyright notice and this permission notice appear in all copies.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       + */
       +
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +
       +#include "color.h"
       +#include "rect.h"
       +#include "widget.h"
       +#include "ltk.h"
       +
       +void
       +ltk_fill_widget_rect(ltk_widget *widget, ltk_color *color, ltk_rect rect) {
       +        ltk_window *win = widget->window;
       +        XSetForeground(win->dpy, win->gc, color->xcolor.pixel);
       +        XFillRectangle(win->dpy, widget->pixmap, win->gc, rect.x, rect.y, rect.w, rect.h);
       +}
       +
       +void
       +ltk_draw_widget_rect(ltk_widget *widget, ltk_color *color, ltk_rect rect, int border_width) {
       +        ltk_window *win = widget->window;
       +        if (border_width <= 0)
       +                return;
       +        XSetForeground(win->dpy, win->gc, color->xcolor.pixel);
       +        XSetLineAttributes(win->dpy, win->gc, border_width, LineSolid, CapButt, JoinMiter);
       +        XDrawRectangle(
       +            win->dpy, widget->pixmap, win->gc,
       +            border_width / 2, border_width / 2,
       +            rect.w - border_width, rect.h - border_width
       +        );
       +}
       +
       +void
       +ltk_copy_clipped(ltk_widget *widget, ltk_rect clip) {
       +        ltk_window *win = widget->window;
       +        ltk_rect clip_final = ltk_rect_intersect(clip, widget->rect);
       +        if (clip_final.w <= 0 || clip_final.h <= 0)
       +                return;
       +        XCopyArea(
       +            win->dpy, widget->pixmap, win->xwindow, win->gc,
       +            clip_final.x - widget->rect.x, clip_final.y - widget->rect.y,
       +            clip_final.w, clip_final.h, clip_final.x, clip_final.y
       +        );
       +}
   DIR diff --git a/src/graphics.h b/src/graphics.h
       t@@ -0,0 +1,38 @@
       +/*
       + * Copyright (c) 2021 lumidify <nobody@lumidify.org>
       + *
       + * Permission to use, copy, modify, and distribute this software for any
       + * purpose with or without fee is hereby granted, provided that the above
       + * copyright notice and this permission notice appear in all copies.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       + */
       +
       +#ifndef _LTK_GRAPHICS_H_
       +#define _LTK_GRAPHICS_H_
       +
       +/* Requires: "color.h", "rect.h", "widget.h" */
       +
       +/* FIXME: Is it faster to take ltk_color* or ltk_color? */
       +
       +/* Fill `rect` with `color` on `widget`'s pixmap.
       + * `rect` is relative to `widget`'s rect. */
       +void ltk_fill_widget_rect(ltk_widget *widget, ltk_color *color, ltk_rect rect);
       +
       +/* Draw `rect` with `color` and `border_width` on `widget`'s pixmap.
       + * `rect` is relative to `widget`'s rect. */
       +void ltk_draw_widget_rect(ltk_widget *widget, ltk_color *color, ltk_rect rect, int border_width);
       +
       +/* Copy the part of `widget`'s pixmap covered by the intersection of `clip`
       + * and `widget`'s rect to the window `widget` is contained within.
       + * `clip` is absolute, i.e. relative to the coordinates of the window,
       + * not of `widget`. */
       +void ltk_copy_clipped(ltk_widget *widget, ltk_rect clip);
       +
       +#endif /* _LTK_GRAPHICS_H_ */
   DIR diff --git a/src/grid.c b/src/grid.c
       t@@ -63,7 +63,10 @@ static struct ltk_widget_vtable vtable = {
                .remove_child = &ltk_grid_ungrid,
                .mouse_press = &ltk_grid_mouse_press,
                .mouse_release = &ltk_grid_mouse_release,
       -        .motion_notify = &ltk_grid_motion_notify
       +        .motion_notify = &ltk_grid_motion_notify,
       +        .type = LTK_GRID,
       +        .needs_redraw = 0,
       +        .needs_pixmap = 0
        };
        
        static int ltk_grid_cmd_add(
       t@@ -120,7 +123,7 @@ static ltk_grid *
        ltk_grid_create(ltk_window *window, const char *id, int rows, int columns) {
                ltk_grid *grid = ltk_malloc(sizeof(ltk_grid));
        
       -        ltk_fill_widget_defaults(&grid->widget, id, window, &vtable, 0, LTK_GRID);
       +        ltk_fill_widget_defaults(&grid->widget, id, window, &vtable, 0, 0);
        
                grid->rows = rows;
                grid->columns = columns;
       t@@ -246,11 +249,8 @@ ltk_recalculate_grid(ltk_widget *self) {
                                if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) {
                                        ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i];
                                }
       -                        if (orig_width != ptr->rect.w || orig_height != ptr->rect.h) {
       -                                if (ptr->vtable->resize) {
       -                                        ptr->vtable->resize(ptr);
       -                                }
       -                        }
       +                        if (orig_width != ptr->rect.w || orig_height != ptr->rect.h)
       +                                ltk_widget_resize(ptr);
        
                                if (ptr->sticky & LTK_STICKY_RIGHT) {
                                        ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w;
   DIR diff --git a/src/label.c b/src/label.c
       t@@ -31,15 +31,20 @@
        #include "util.h"
        #include "text.h"
        #include "label.h"
       +#include "graphics.h"
        
        static void ltk_label_draw(ltk_widget *self, ltk_rect clip);
        static ltk_label *ltk_label_create(ltk_window *window,
            const char *id, const char *text);
        static void ltk_label_destroy(ltk_widget *self, int shallow);
       +static void ltk_label_redraw_pixmap(ltk_label *label);
        
        static struct ltk_widget_vtable vtable = {
                .draw = &ltk_label_draw,
       -        .destroy = &ltk_label_destroy
       +        .destroy = &ltk_label_destroy,
       +        .type = LTK_LABEL,
       +        .needs_redraw = 1,
       +        .needs_pixmap = 1
        };
        
        static struct {
       t@@ -78,19 +83,26 @@ ltk_label_draw(ltk_widget *self, ltk_rect clip) {
                ltk_window *window = label->widget.window;
                ltk_rect rect = label->widget.rect;
                ltk_rect clip_final = ltk_rect_intersect(clip, rect);
       +        if (self->dirty)
       +                ltk_label_redraw_pixmap(label);
                /* no idea why it would be less than 0, but whatever */
                if (clip_final.w <= 0 || clip_final.h <= 0)
                        return;
       -        XCopyArea(window->dpy, label->text_pixmap, window->xwindow, window->gc,
       -            clip_final.x - rect.x, clip_final.y - rect.y,
       -            clip_final.w, clip_final.h, clip_final.x, clip_final.y);
       -        /*
       +        ltk_copy_clipped(self, clip_final);
       +}
       +
       +static void
       +ltk_label_redraw_pixmap(ltk_label *label) {
       +        ltk_rect r = label->widget.rect;
       +        r.x = 0;
       +        r.y = 0;
       +        ltk_fill_widget_rect(&label->widget, &theme.bg_color, r);
       +
                int text_w, text_h;
                ltk_text_line_get_size(label->tl, &text_w, &text_h);
       -        int text_x = rect.x + (rect.w - text_w) / 2;
       -        int text_y = rect.y + (rect.h - text_h) / 2;
       -        ltk_text_line_draw(label->tl, window->gc, text_x, text_y, clip);
       -        */
       +        int text_x = (r.w - text_w) / 2;
       +        int text_y = (r.h - text_h) / 2;
       +        ltk_text_line_draw(label->tl, label->widget.pixmap, label->widget.window->gc, text_x, text_y, r);
        }
        
        static ltk_label *
       t@@ -98,7 +110,6 @@ ltk_label_create(ltk_window *window, const char *id, const char *text) {
                char *text_copy;
                ltk_label *label = ltk_malloc(sizeof(ltk_label));
        
       -        ltk_fill_widget_defaults(&label->widget, id, window, &vtable, 1, LTK_LABEL);
                uint16_t font_size = window->theme.font_size;
                text_copy = ltk_strdup(text);
                label->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1);
       t@@ -107,11 +118,7 @@ ltk_label_create(ltk_window *window, const char *id, const char *text) {
                label->widget.ideal_w = text_w + theme.pad * 2;
                label->widget.ideal_h = text_h + theme.pad * 2;
                ltk_text_line_render(label->tl, &window->theme.bg, &theme.text_color);
       -        label->text_pixmap = XCreatePixmap(window->dpy, window->xwindow,
       -            label->widget.ideal_w, label->widget.ideal_h, window->depth);
       -        XSetForeground(window->dpy, window->gc, theme.bg_color.xcolor.pixel);
       -        XFillRectangle(window->dpy, label->text_pixmap, window->gc, 0, 0, label->widget.ideal_w, label->widget.ideal_h);
       -        ltk_text_line_draw(label->tl, label->text_pixmap, window->gc, theme.pad, theme.pad, label->widget.rect);
       +        ltk_fill_widget_defaults(&label->widget, id, window, &vtable, label->widget.ideal_w, label->widget.ideal_h);
        
                return label;
        }
   DIR diff --git a/src/ltkd.c b/src/ltkd.c
       t@@ -420,8 +420,7 @@ ltk_set_root_widget_cmd(
                ltk_window_invalidate_rect(window, widget->rect);
                widget->rect.w = window->rect.w;
                widget->rect.h = window->rect.h;
       -        if (widget->vtable->resize)
       -                widget->vtable->resize(widget);
       +        ltk_widget_resize(widget);
        
                return 0;
        }
       t@@ -494,10 +493,10 @@ ltk_window_other_event(ltk_window *window, XEvent event) {
                                window->rect.w = w;
                                window->rect.h = h;
                                ltk_window_invalidate_rect(window, window->rect);
       -                        if (ptr && ptr->vtable->resize) {
       +                        if (ptr) {
                                        ptr->rect.w = w;
                                        ptr->rect.h = h;
       -                                ptr->vtable->resize(ptr);
       +                                ltk_widget_resize(ptr);
                                }
                        }
                } else if (event.type == Expose && event.xexpose.count == 0) {
   DIR diff --git a/src/scrollbar.c b/src/scrollbar.c
       t@@ -40,7 +40,10 @@ static struct ltk_widget_vtable vtable = {
                .draw = &ltk_scrollbar_draw,
                .mouse_press = &ltk_scrollbar_mouse_press,
                .motion_notify = &ltk_scrollbar_motion_notify,
       -        .destroy = &ltk_scrollbar_destroy
       +        .destroy = &ltk_scrollbar_destroy,
       +        .type = LTK_UNKNOWN, /* FIXME */
       +        .needs_redraw = 1,
       +        .needs_pixmap = 1
        };
        
        static struct {
       t@@ -230,7 +233,7 @@ ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
        ltk_scrollbar *
        ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback)(ltk_widget *), void *data) {
                ltk_scrollbar *sc = ltk_malloc(sizeof(ltk_scrollbar));
       -        ltk_fill_widget_defaults((ltk_widget *)sc, NULL, window, &vtable, 1, LTK_UNKNOWN);
       +        ltk_fill_widget_defaults((ltk_widget *)sc, NULL, window, &vtable, 1, 1); /* FIXME: proper size */
                sc->last_mouse_x = sc->last_mouse_y = 0;
                /* This cannot be 0 because that leads to divide-by-zero */
                sc->virtual_size = 1;
   DIR diff --git a/src/widget.c b/src/widget.c
       t@@ -59,41 +59,66 @@ ltk_widgets_cleanup() {
        
        void
        ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
       -    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type) {
       +    struct ltk_widget_vtable *vtable, int w, int h) {
                if (id)
                        widget->id = ltk_strdup(id);
                else
                        widget->id = NULL;
                widget->window = window;
       -        widget->active_widget = NULL;
                widget->parent = NULL;
       -        widget->type = type;
       +
       +        widget->pix_w = w;
       +        widget->pix_h = h;
       +        if (vtable->needs_pixmap)
       +                widget->pixmap = XCreatePixmap(window->dpy, window->xwindow, w, h, window->depth);
        
                /* FIXME: possibly check that draw and destroy aren't NULL */
                widget->vtable = vtable;
        
       -        widget->needs_redraw = needs_redraw;
                widget->state = LTK_NORMAL;
                widget->row = 0;
                widget->rect.x = 0;
                widget->rect.y = 0;
       -        widget->rect.w = 0;
       -        widget->rect.h = 0;
       -        widget->ideal_w = 0;
       -        widget->ideal_h = 0;
       +        widget->rect.w = w;
       +        widget->rect.h = h;
        
                widget->row = 0;
                widget->column = 0;
                widget->row_span = 0;
                widget->column_span = 0;
                widget->sticky = 0;
       +        widget->dirty = 1;
       +}
       +
       +/* FIXME: Make this properly amortised constant */
       +/* FIXME: Maybe pass the new width as arg here?
       +   That would make a bit more sense */
       +void
       +ltk_widget_resize(ltk_widget *widget) {
       +        if (widget->vtable->resize)
       +                widget->vtable->resize(widget);
       +        if (!widget->vtable->needs_pixmap)
       +                return;
       +        int new_w, new_h;
       +        ltk_window *w = widget->window;
       +        ltk_rect r = widget->rect;
       +        int pw = widget->pix_w;
       +        int ph = widget->pix_h;
       +
       +        new_w = pw < r.w ? r.w : pw;
       +        new_h = ph < r.h ? r.h : ph;
       +        if (new_w == pw && new_h == ph)
       +                return;
       +        XFreePixmap(w->dpy, widget->pixmap);
       +        widget->pixmap = XCreatePixmap(w->dpy, w->xwindow, new_w, new_h, w->depth);
       +        widget->dirty = 1;
        }
        
        void
        ltk_widget_change_state(ltk_widget *widget) {
                if (widget->vtable->change_state)
                        widget->vtable->change_state(widget);
       -        if (widget->needs_redraw)
       +        if (widget->vtable->needs_redraw)
                        ltk_window_invalidate_rect(widget->window, widget->rect);
        }
        
       t@@ -170,7 +195,7 @@ ltk_get_widget(const char *id, ltk_widget_type type, char **errstr) {
                        return NULL;
                }
                widget = kh_value(widget_hash, k);
       -        if (type != LTK_WIDGET && widget->type != type) {
       +        if (type != LTK_WIDGET && widget->vtable->type != type) {
                        *errstr = "Widget with given ID has wrong type.\n";
                        return NULL;
                }
   DIR diff --git a/src/widget.h b/src/widget.h
       t@@ -55,24 +55,25 @@ struct ltk_widget_vtable;
        
        typedef struct ltk_widget {
                struct ltk_window *window;
       -        struct ltk_widget *active_widget;
                struct ltk_widget *parent;
                char *id;
        
                struct ltk_widget_vtable *vtable;
       +        Pixmap pixmap;
       +        int pix_w;
       +        int pix_h;
        
                ltk_rect rect;
                unsigned int ideal_w;
                unsigned int ideal_h;
        
       -        ltk_widget_type type;
                ltk_widget_state state;
                unsigned int sticky;
                unsigned short row;
                unsigned short column;
                unsigned short row_span;
                unsigned short column_span;
       -        unsigned char needs_redraw;
       +        char dirty;
        } ltk_widget;
        
        struct ltk_widget_vtable {
       t@@ -92,11 +93,15 @@ struct ltk_widget_vtable {
        
                void (*child_size_change) (struct ltk_widget *, struct ltk_widget *);
                int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **);
       +
       +        ltk_widget_type type;
       +        char needs_redraw;
       +        char needs_pixmap;
        };
        
        int ltk_widget_destroy(struct ltk_window *window, char **tokens, size_t num_tokens, char **errstr);
        void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, struct ltk_window *window,
       -    struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type);
       +    struct ltk_widget_vtable *vtable, int w, int h);
        void ltk_widget_change_state(ltk_widget *widget);
        void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event);
        void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event);
       t@@ -107,5 +112,6 @@ void ltk_set_widget(ltk_widget *widget, const char *id);
        void ltk_remove_widget(const char *id);
        void ltk_widgets_cleanup();
        void ltk_widgets_init();
       +void ltk_widget_resize(ltk_widget *widget);
        
        #endif /* _LTK_WIDGET_H_ */