URI: 
       tImprove resizing; misc. changes - 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 791c8bda54d3acfcffabba04ff1be38852c38435
   DIR parent 829de8bbcb77ea2607f1ec22113222ac755256b8
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sat, 16 Jan 2021 21:37:25 +0100
       
       Improve resizing; misc. changes
       
       Diffstat:
         M box.c                               |     119 +++++++++++++++++++++++--------
         M button.c                            |       4 ++--
         M grid.c                              |      74 +++++++++++++++++++++----------
         M label.c                             |       4 ++--
         M ltk.h                               |       8 ++++++--
         M ltkd.c                              |      15 +++++++++------
         M testbox.gui                         |       2 ++
       
       7 files changed, 161 insertions(+), 65 deletions(-)
       ---
   DIR diff --git a/box.c b/box.c
       t@@ -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);
       t@@ -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;
       t@@ -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
       t@@ -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) {
       t@@ -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;
       t@@ -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;
                        }
                }
       t@@ -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;
                        }
       t@@ -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,
       t@@ -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;
                }
       t@@ -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;
       +                        }
                        }
                }
        
   DIR diff --git a/button.c b/button.c
       t@@ -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);
        
   DIR diff --git a/grid.c b/grid.c
       t@@ -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>
       t@@ -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);
       t@@ -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;
       t@@ -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);
                                        }
                                }
        
       t@@ -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,
       t@@ -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;
       t@@ -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,
       t@@ -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;
                }
       t@@ -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;
       +                        }
                        }
                }
        
   DIR diff --git a/label.c b/label.c
       t@@ -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;
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -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);
       t@@ -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;
   DIR diff --git a/ltkd.c b/ltkd.c
       t@@ -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;
        }
       t@@ -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) {
       t@@ -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;
       t@@ -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;
   DIR diff --git a/testbox.gui b/testbox.gui
       t@@ -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