ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 657c2bb9354fc1f5d1e224c5db412ce0c4da447f
parent 997414b6693f3a4bc08c43347baa4e829d8e6cca
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---
MLICENSE | 3++-
MMakefile | 4+++-
Asrc/.gitignore | 4++++
Msrc/box.c | 21+++++++++++----------
Msrc/button.c | 57++++++++++++++++-----------------------------------------
Msrc/draw.c | 7++++++-
Asrc/graphics.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/graphics.h | 38++++++++++++++++++++++++++++++++++++++
Msrc/grid.c | 14+++++++-------
Msrc/label.c | 37++++++++++++++++++++++---------------
Msrc/ltkd.c | 7+++----
Msrc/scrollbar.c | 7+++++--
Msrc/widget.c | 45+++++++++++++++++++++++++++++++++++----------
Msrc/widget.h | 14++++++++++----
15 files changed, 219 insertions(+), 99 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,5 +1,2 @@ -ltkd -ltkc -ltk.sock *.o *.core diff --git a/LICENSE b/LICENSE @@ -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 diff --git a/Makefile b/Makefile @@ -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 @@ -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) diff --git a/src/.gitignore b/src/.gitignore @@ -0,0 +1,4 @@ +ltkd +ltkc +*.o +*.core diff --git a/src/box.c b/src/box.c @@ -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( @@ -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; @@ -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; @@ -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; } } @@ -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; } diff --git a/src/button.c b/src/button.c @@ -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); @@ -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 { @@ -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 @@ -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); @@ -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 @@ -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 @@ -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); @@ -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); diff --git a/src/draw.c b/src/draw.c @@ -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( @@ -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); diff --git a/src/graphics.c b/src/graphics.c @@ -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 + ); +} diff --git a/src/graphics.h b/src/graphics.h @@ -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_ */ diff --git a/src/grid.c b/src/grid.c @@ -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( @@ -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; @@ -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; diff --git a/src/label.c b/src/label.c @@ -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 { @@ -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 * @@ -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); @@ -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; } diff --git a/src/ltkd.c b/src/ltkd.c @@ -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; } @@ -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) { diff --git a/src/scrollbar.c b/src/scrollbar.c @@ -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 { @@ -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; diff --git a/src/widget.c b/src/widget.c @@ -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); } @@ -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; } diff --git a/src/widget.h b/src/widget.h @@ -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 { @@ -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); @@ -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_ */