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 791c8bda54d3acfcffabba04ff1be38852c38435
parent 829de8bbcb77ea2607f1ec22113222ac755256b8
Author: lumidify <nobody@lumidify.org>
Date:   Sat, 16 Jan 2021 21:37:25 +0100

Improve resizing; misc. changes

Diffstat:
Mbox.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mbutton.c | 4++--
Mgrid.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mlabel.c | 4++--
Mltk.h | 8++++++--
Mltkd.c | 15+++++++++------
Mtestbox.gui | 2++
7 files changed, 161 insertions(+), 65 deletions(-)

diff --git a/box.c b/box.c @@ -38,6 +38,7 @@ static void ltk_box_draw(ltk_box *box); static ltk_box *ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient); static void ltk_box_destroy(ltk_box *box, int shallow); static void ltk_recalculate_box(ltk_box *box); +static void ltk_box_child_size_change(ltk_box *box, ltk_widget *widget); /* FIXME: Why is sticky unsigned short? */ static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, unsigned short sticky, char **errstr); static int ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_box *box, char **errstr); @@ -91,6 +92,7 @@ ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient) { box->widget.mouse_release = &ltk_box_mouse_release; box->widget.motion_notify = &ltk_box_motion_notify; box->widget.resize = &ltk_recalculate_box; + box->widget.child_size_change = &ltk_box_child_size_change; box->widget.remove_child = &ltk_box_remove; box->sc = NULL; @@ -117,7 +119,6 @@ ltk_box_destroy(ltk_box *box, int shallow) { free(box); } -/* FIXME: Allow no sticky value to be centered in middle */ /* FIXME: Need some sort of "visible rect" so widgets don't draw outside */ /* FIXME: Make this function name more consistent */ static void @@ -128,26 +129,70 @@ ltk_recalculate_box(ltk_box *box) { ptr = box->widgets[i]; if (box->orient == LTK_HORIZONTAL) { ptr->rect.x = cur_pos; - cur_pos += ptr->rect.w; if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) ptr->rect.h = box->widget.rect.h; if (ptr->sticky & LTK_STICKY_TOP) ptr->rect.y = box->widget.rect.y; else if (ptr->sticky & LTK_STICKY_BOTTOM) ptr->rect.y = box->widget.rect.y + box->widget.rect.h - ptr->rect.h; + else + ptr->rect.y = box->widget.rect.y + (box->widget.rect.h - ptr->rect.h) / 2; + if (ptr->resize) + ptr->resize(ptr); + cur_pos += ptr->rect.w; } else { ptr->rect.y = cur_pos; - cur_pos += ptr->rect.h; if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT) ptr->rect.w = box->widget.rect.w; if (ptr->sticky & LTK_STICKY_LEFT) ptr->rect.x = box->widget.rect.x; else if (ptr->sticky & LTK_STICKY_RIGHT) ptr->rect.x = box->widget.rect.x + box->widget.rect.w - ptr->rect.w; + else + ptr->rect.x = box->widget.rect.x + (box->widget.rect.w - ptr->rect.w) / 2; + if (ptr->resize) + ptr->resize(ptr); + cur_pos += ptr->rect.h; } } } +/* FIXME: This entire resizing thing is a bit weird. For instance, if a label + in a vertical box increases its height because its width has been decreased + and it is forced to wrap, should that just change the rect or also the + ideal size? Ideal size wouldn't really make sense here, but then the box + might be forced to add a scrollbar even though the parent widget would + actually give it more space if it knew that it needed it. */ + +static void +ltk_box_child_size_change(ltk_box *box, ltk_widget *widget) { + short size_changed = 0; + /* This is always reset here - if it needs to be changed, + the resize function called by the last child_size_change + function will fix it */ + /* Note: This seems a bit weird, but if each widget set its rect itself, + that would also lead to weird things. For instance, if a butten is + added to after a box after being ungridded, and its rect was changed + by the grid (e.g. because of a column weight), who should reset the + rect if it doesn't have sticky set? Of course, the resize function + could also set all widgets even if they don't have any sticky + settings, but there'd probably be some catch as well. */ + widget->rect.w = widget->ideal_w; + widget->rect.h = widget->ideal_h; + if (box->orient == LTK_HORIZONTAL && widget->ideal_h > box->widget.ideal_h) { + box->widget.ideal_h = widget->ideal_h; + size_changed = 1; + } else if (box->orient == LTK_VERTICAL && widget->ideal_w > box->widget.ideal_h) { + box->widget.ideal_w = widget->ideal_w; + size_changed = 1; + } + + if (size_changed && box->widget.parent && box->widget.parent->child_size_change) + box->widget.parent->child_size_change(box->widget.parent, box); + else + ltk_recalculate_box(box); +} + static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, unsigned short sticky, char **errstr) { if (widget->parent) { @@ -164,9 +209,13 @@ ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, unsigned short } box->widgets[box->num_widgets++] = widget; + if (box->orient == LTK_HORIZONTAL) + box->widget.ideal_w += widget->ideal_w; + else + box->widget.ideal_h += widget->ideal_h; widget->parent = box; widget->sticky = sticky; - ltk_recalculate_box(box); + ltk_box_child_size_change(box, widget); ltk_window_invalidate_rect(window, box->widget.rect); return 0; @@ -186,6 +235,25 @@ ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_box *box, char **errs (box->num_widgets - i - 1) * sizeof(ltk_widget *)); box->num_widgets--; ltk_window_invalidate_rect(window, box->widget.rect); + /* search for new ideal width/height */ + /* FIXME: make this all a bit nicer and break the lines better */ + if (box->orient == LTK_HORIZONTAL && widget->ideal_h == box->widget.ideal_h) { + box->widget.ideal_h = 0; + for (size_t j = 0; j < box->num_widgets; j++) { + if (box->widgets[j]->ideal_h > box->widget.ideal_h) + box->widget.ideal_h = box->widgets[j]->ideal_h; + } + if (box->widget.parent && box->widget.parent->resize) + box->widget.parent->resize(box->widget.parent); + } else if (box->orient == LTK_VERTICAL && widget->ideal_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 > box->widget.ideal_w) + box->widget.ideal_w = box->widgets[j]->ideal_w; + } + if (box->widget.parent && box->widget.parent->resize) + box->widget.parent->resize(box->widget.parent); + } return 0; } } @@ -204,18 +272,10 @@ ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XE handler(box->sc, event); */ + /* FIXME: When scrolling is implemented, check only the currently visible items */ for (size_t i = 0; i < box->num_widgets; i++) { widget = box->widgets[i]; - if (box->orient == LTK_HORIZONTAL) { - pos = event.xbutton.x; - start = widget->rect.x; - size = widget->rect.w; - } else { - pos = event.xbutton.y; - start = widget->rect.y; - size = widget->rect.h; - } - if (pos >= start && pos <= start + size) { + if (ltk_collide_rect(widget->rect, event.xbutton.x, event.xbutton.y)) { handler(widget, event); return; } @@ -243,7 +303,7 @@ ltk_box_motion_notify(ltk_box *box, XEvent event) { ltk_box_mouse_event(box, event, &ltk_widget_motion_notify_event); } -/* box <box id> add <widget id> <sticky> */ +/* box <box id> add <widget id> [sticky] */ static int ltk_box_cmd_add( ltk_window *window, @@ -255,7 +315,7 @@ ltk_box_cmd_add( ltk_widget *widget; int sticky = 0; - if (num_tokens != 5) { + if (num_tokens != 4 && num_tokens != 5) { *errstr = "Invalid number of arguments.\n"; return 1; } @@ -266,19 +326,20 @@ ltk_box_cmd_add( return 1; } - /* FIXME: Allow default value (just no sticky at all) */ - for (c = tokens[4]; *c != '\0'; c++) { - if (*c == 'n') { - sticky |= LTK_STICKY_TOP; - } else if (*c == 's') { - sticky |= LTK_STICKY_BOTTOM; - } else if (*c == 'e') { - sticky |= LTK_STICKY_RIGHT; - } else if (*c == 'w') { - sticky |= LTK_STICKY_LEFT; - } else if (*c != 'u') { - *errstr = "Invalid sticky specification.\n"; - return 1; + if (num_tokens == 5) { + for (c = tokens[4]; *c != '\0'; c++) { + if (*c == 'n') { + sticky |= LTK_STICKY_TOP; + } else if (*c == 's') { + sticky |= LTK_STICKY_BOTTOM; + } else if (*c == 'e') { + sticky |= LTK_STICKY_RIGHT; + } else if (*c == 'w') { + sticky |= LTK_STICKY_LEFT; + } else { + *errstr = "Invalid sticky specification.\n"; + return 1; + } } } diff --git a/button.c b/button.c @@ -213,8 +213,8 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) { button->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1); int text_w, text_h; ltk_text_line_get_size(button->tl, &text_w, &text_h); - button->widget.rect.w = text_w + theme.border_width * 2 + theme.pad * 2; - button->widget.rect.h = text_h + theme.border_width * 2 + theme.pad * 2; + 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; /* render text */ ltk_button_change_state(button); diff --git a/grid.c b/grid.c @@ -21,8 +21,12 @@ * SOFTWARE. */ -/* TODO: remove_widget function that also adjusts static width */ -/* TODO: widget size request */ +/* TODO: make ungrid function also adjust static row/column width/height + -> also, how should the grid deal with a widget spanning over multiple + rows/columns with static size - if all are static, it could just + divide the widget size (it would complicate things, though), but + what should happen if some rows/columns under the span do have a + positive weight? */ #include <stdio.h> #include <stdlib.h> @@ -43,6 +47,7 @@ static ltk_grid *ltk_grid_create(ltk_window *window, const char *id, int rows, int columns); static void ltk_grid_destroy(ltk_grid *grid, int shallow); static void ltk_recalculate_grid(ltk_grid *grid); +static void ltk_grid_child_size_change(ltk_grid *grid, ltk_widget *widget); static int ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid, int row, int column, int row_span, int column_span, unsigned short sticky, char **errstr); static int ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid, char **errstr); @@ -113,6 +118,7 @@ ltk_grid_create(ltk_window *window, const char *id, int rows, int columns) { grid->widget.mouse_release = &ltk_grid_mouse_release; grid->widget.motion_notify = &ltk_grid_motion_notify; grid->widget.resize = &ltk_recalculate_grid; + grid->widget.child_size_change = &ltk_grid_child_size_change; grid->widget.remove_child = &ltk_grid_ungrid; grid->rows = rows; @@ -239,7 +245,7 @@ ltk_recalculate_grid(ltk_grid *grid) { } if (orig_width != ptr->rect.w || orig_height != ptr->rect.h) { if (ptr->resize) { - ptr->resize(ptr, orig_width, orig_height); + ptr->resize(ptr); } } @@ -262,6 +268,30 @@ ltk_recalculate_grid(ltk_grid *grid) { } } +/* FIXME: Maybe add debug stuff to check that grid is actually parent of widget */ +static void +ltk_grid_child_size_change(ltk_grid *grid, ltk_widget *widget) { + short size_changed = 0; + widget->rect.w = widget->ideal_w; + widget->rect.h = widget->ideal_h; + if (grid->column_weights[widget->column] == 0 && + widget->rect.w > grid->column_widths[widget->column]) { + grid->widget.ideal_w += widget->rect.w - grid->column_widths[widget->column]; + grid->column_widths[widget->column] = widget->rect.w; + size_changed = 1; + } + if (grid->row_weights[widget->row] == 0 && + widget->rect.h > grid->row_heights[widget->row]) { + grid->widget.ideal_h += widget->rect.h - grid->row_heights[widget->row]; + grid->row_heights[widget->row] = widget->rect.h; + size_changed = 1; + } + if (size_changed && grid->widget.parent && grid->widget.parent->child_size_change) + grid->widget.parent->child_size_change(grid->widget.parent, grid); + else + ltk_recalculate_grid(grid); +} + /* FIXME: Check if widget already exists at position */ static int ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid, @@ -275,19 +305,13 @@ ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid, widget->column = column; widget->row_span = row_span; widget->column_span = column_span; - if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column]) { - grid->column_widths[column] = widget->rect.w; - } - if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) { - grid->row_heights[row] = widget->rect.h; - } for (int i = row; i < row + row_span; i++) { for (int j = column; j < column + column_span; j++) { grid->widget_grid[i * grid->columns + j] = widget; } } widget->parent = grid; - ltk_recalculate_grid(grid); + ltk_grid_child_size_change(grid, widget); ltk_window_invalidate_rect(window, grid->widget.rect); return 0; @@ -374,7 +398,7 @@ ltk_grid_motion_notify(ltk_grid *grid, XEvent event) { ltk_widget_motion_notify_event(ptr, event); } -/* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> <sticky> */ +/* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> [sticky] */ static int ltk_grid_cmd_add( ltk_window *window, @@ -387,7 +411,7 @@ ltk_grid_cmd_add( const char *errstr_num; int row, column, row_span, column_span, sticky = 0; - if (num_tokens != 9) { + if (num_tokens != 8 && num_tokens != 9) { *errstr = "Invalid number of arguments.\n"; return 1; } @@ -418,18 +442,20 @@ ltk_grid_cmd_add( return 1; } - for (c = tokens[8]; *c != '\0'; c++) { - if (*c == 'n') { - sticky |= LTK_STICKY_TOP; - } else if (*c == 's') { - sticky |= LTK_STICKY_BOTTOM; - } else if (*c == 'e') { - sticky |= LTK_STICKY_RIGHT; - } else if (*c == 'w') { - sticky |= LTK_STICKY_LEFT; - } else if (*c != 'u') { - *errstr = "Invalid sticky specification.\n"; - return 1; + if (num_tokens == 9) { + for (c = tokens[8]; *c != '\0'; c++) { + if (*c == 'n') { + sticky |= LTK_STICKY_TOP; + } else if (*c == 's') { + sticky |= LTK_STICKY_BOTTOM; + } else if (*c == 'e') { + sticky |= LTK_STICKY_RIGHT; + } else if (*c == 'w') { + sticky |= LTK_STICKY_LEFT; + } else { + *errstr = "Invalid sticky specification.\n"; + return 1; + } } } diff --git a/label.c b/label.c @@ -92,8 +92,8 @@ ltk_label_create(ltk_window *window, const char *id, const char *text) { label->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1); int text_w, text_h; ltk_text_line_get_size(label->tl, &text_w, &text_h); - label->widget.rect.w = text_w + theme.pad * 2; - label->widget.rect.h = text_h + theme.pad * 2; + 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); return label; diff --git a/ltk.h b/ltk.h @@ -70,12 +70,15 @@ typedef enum { typedef struct ltk_window ltk_window; typedef struct ltk_widget { - ltk_rect rect; ltk_window *window; struct ltk_widget *active_widget; struct ltk_widget *parent; char *id; + ltk_rect rect; + unsigned int ideal_w; + unsigned int ideal_h; + void (*key_press) (void *, XEvent); void (*key_release) (void *, XEvent); void (*mouse_press) (void *, XEvent); @@ -84,11 +87,12 @@ typedef struct ltk_widget { void (*mouse_leave) (void *, XEvent); void (*mouse_enter) (void *, XEvent); - void (*resize) (void *, int, int); + void (*resize) (void *); void (*draw) (void *); void (*change_state) (void *); void (*destroy) (void *, int); + void (*child_size_change) (struct ltk_widget *, struct ltk_widget *); int (*remove_child) (ltk_window *, void *, void *, char **); ltk_widget_type type; diff --git a/ltkd.c b/ltkd.c @@ -397,12 +397,11 @@ ltk_set_root_widget_cmd( widget = ltk_get_widget(tokens[1], LTK_WIDGET, errstr); if (!widget) return 1; window->root_widget = widget; - int w = widget->rect.w; - int h = widget->rect.h; + ltk_window_invalidate_rect(window, widget->rect); widget->rect.w = window->rect.w; widget->rect.h = window->rect.h; if (widget->resize) - widget->resize(widget, w, h); + widget->resize(widget); return 0; } @@ -548,10 +547,11 @@ ltk_window_other_event(ltk_window *window, XEvent event) { if (orig_w != w || orig_h != h) { window->rect.w = w; window->rect.h = h; + ltk_window_invalidate_rect(window, window->rect); if (ptr && ptr->resize) { ptr->rect.w = w; ptr->rect.h = h; - ptr->resize(ptr, orig_w, orig_h); + ptr->resize(ptr); } } } else if (event.type == Expose && event.xexpose.count == 0) { @@ -767,6 +767,7 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, widget->draw = draw; widget->change_state = change_state; widget->destroy = destroy; + widget->child_size_change = NULL; widget->remove_child = NULL; widget->needs_redraw = needs_redraw; @@ -774,8 +775,10 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, widget->row = 0; widget->rect.x = 0; widget->rect.y = 0; - widget->rect.w = 100; - widget->rect.h = 100; + widget->rect.w = 0; + widget->rect.h = 0; + widget->ideal_w = 0; + widget->ideal_h = 0; widget->row = 0; widget->column = 0; diff --git a/testbox.gui b/testbox.gui @@ -2,5 +2,7 @@ box box1 create vertical set-root-widget box1 button btn1 create "I'm a button!" button btn2 create "I'm also a button!" +button btn3 create "I'm another boring button." box box1 add btn1 ew box box1 add btn2 e +box box1 add btn3