URI: 
       tSomewhat fix mouse event handling - 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 c25f7ed3099b6026f5e0c169e30a8a28719d5633
   DIR parent 2ed1efbf50d122e1120e32416b71c9838d9c3880
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri, 22 Jan 2021 19:42:03 +0100
       
       Somewhat fix mouse event handling
       
       Diffstat:
         M box.c                               |      47 +++++++++++++++----------------
         M button.c                            |       5 +++--
         M grid.c                              |      36 ++++++++++++++++++++-----------
         M ltk.h                               |      10 +++++-----
         M ltkd.c                              |     125 ++++++++++++++-----------------
         M scrollbar.c                         |      25 +++++++++++++++----------
         M scrollbar.h                         |       6 ++++--
       
       7 files changed, 128 insertions(+), 126 deletions(-)
       ---
   DIR diff --git a/box.c b/box.c
       t@@ -43,10 +43,11 @@ static void ltk_box_child_size_change(ltk_box *box, ltk_widget *widget);
        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);
        /* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow, char **errstr); */
       -static void ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent));
       -static void ltk_box_mouse_press(ltk_box *box, XEvent event);
       -static void ltk_box_mouse_release(ltk_box *box, XEvent event);
       -static void ltk_box_motion_notify(ltk_box *box, XEvent event);
       +static void ltk_box_scroll(ltk_box *box);
       +static int ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent));
       +static int ltk_box_mouse_press(ltk_box *box, XEvent event);
       +static int ltk_box_mouse_release(ltk_box *box, XEvent event);
       +static int ltk_box_motion_notify(ltk_box *box, XEvent event);
        
        static int ltk_box_cmd_add(
            ltk_window *window,
       t@@ -99,7 +100,7 @@ ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient) {
                box->widget.child_size_change = &ltk_box_child_size_change;
                box->widget.remove_child = &ltk_box_remove;
        
       -        box->sc = ltk_scrollbar_create(window, orient);
       +        box->sc = ltk_scrollbar_create(window, orient, &ltk_box_scroll, box);
                box->widgets = NULL;
                box->num_alloc = 0;
                box->num_widgets = 0;
       t@@ -291,48 +292,46 @@ ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_box *box, char **errs
        }
        
        static void
       +ltk_box_scroll(ltk_box *box) {
       +        ltk_recalculate_box(box);
       +        ltk_window_invalidate_rect(box->widget.window, box->widget.rect);
       +}
       +
       +static int
        ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XEvent)) {
                int pos, start, size;
                ltk_widget *widget;
                int old_sc_pos = box->sc->cur_pos;
        
       -        /* FIXME: THIS IS A HACK! */
       -        if ((handler == &ltk_widget_motion_notify_event && box->sc->widget.state == LTK_PRESSED) ||
       -            (ltk_collide_rect(box->sc->widget.rect, event.xbutton.x, event.xbutton.y))) {
       +        if (ltk_collide_rect(box->sc->widget.rect, event.xbutton.x, event.xbutton.y)) {
                        handler(box->sc, event);
       -                if (old_sc_pos != box->sc->cur_pos) {
       -                        ltk_recalculate_box(box);
       -                        ltk_window_invalidate_rect(box->widget.window, box->widget.rect);
       -                }
       -                return;
       +                return 0;
                }
        
       -        /* FIXME: When scrolling is implemented, check only the currently visible items */
       +        /* FIXME: When scrolling is implemented properly, check only the currently visible items */
                for (size_t i = 0; i < box->num_widgets; i++) {
                        widget = box->widgets[i];
                        if (ltk_collide_rect(widget->rect, event.xbutton.x, event.xbutton.y)) {
                                handler(widget, event);
       -                        return;
       +                        return 0;
                        }
                }
       +        return 0;
        }
        
       -static void
       +static int
        ltk_box_mouse_press(ltk_box *box, XEvent event) {
       -        ltk_box_mouse_event(box, event, &ltk_widget_mouse_press_event);
       +        return ltk_box_mouse_event(box, event, &ltk_widget_mouse_press_event);
        }
        
       -static void
       +static int
        ltk_box_mouse_release(ltk_box *box, XEvent event) {
       -        ltk_box_mouse_event(box, event, &ltk_widget_mouse_release_event);
       +        return ltk_box_mouse_event(box, event, &ltk_widget_mouse_release_event);
        }
        
       -/* FIXME: Release events shouldn't happen if that widget wasn't actually
       -   pressed! Scrollbar still needs to receive motion notify while pressed
       -   even if not actually hovered over! */
       -static void
       +static int
        ltk_box_motion_notify(ltk_box *box, XEvent event) {
       -        ltk_box_mouse_event(box, event, &ltk_widget_motion_notify_event);
       +        return ltk_box_mouse_event(box, event, &ltk_widget_motion_notify_event);
        }
        
        /* box <box id> add <widget id> [sticky] */
   DIR diff --git a/button.c b/button.c
       t@@ -37,7 +37,7 @@
        #include "button.h"
        
        static void ltk_button_draw(ltk_button *button, ltk_rect clip);
       -static void ltk_button_mouse_release(ltk_button *button, XEvent event);
       +static int ltk_button_mouse_release(ltk_button *button, XEvent event);
        static ltk_button *ltk_button_create(ltk_window *window,
            const char *id, const char *text);
        static void ltk_button_destroy(ltk_button *button, int shallow);
       t@@ -192,9 +192,10 @@ ltk_button_change_state(ltk_button *button) {
                ltk_text_line_render(button->tl, fill, &theme.text_color);
        }
        
       -static void
       +static int
        ltk_button_mouse_release(ltk_button *button, XEvent event) {
                ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
       +        return 1;
        }
        
        static ltk_button *
   DIR diff --git a/grid.c b/grid.c
       t@@ -53,9 +53,9 @@ static int ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
        static int ltk_grid_ungrid(ltk_window *window, ltk_widget *widget, ltk_grid *grid, char **errstr);
        static int ltk_grid_find_nearest_column(ltk_grid *grid, int x);
        static int ltk_grid_find_nearest_row(ltk_grid *grid, int y);
       -static void ltk_grid_mouse_press(ltk_grid *grid, XEvent event);
       -static void ltk_grid_mouse_release(ltk_grid *grid, XEvent event);
       -static void ltk_grid_motion_notify(ltk_grid *grid, XEvent event);
       +static int ltk_grid_mouse_press(ltk_grid *grid, XEvent event);
       +static int ltk_grid_mouse_release(ltk_grid *grid, XEvent event);
       +static int ltk_grid_motion_notify(ltk_grid *grid, XEvent event);
        
        static int ltk_grid_cmd_add(
            ltk_window *window,
       t@@ -356,46 +356,56 @@ ltk_grid_find_nearest_row(ltk_grid *grid, int y) {
                return -1;
        }
        
       -static void
       +static int
        ltk_grid_mouse_press(ltk_grid *grid, XEvent event) {
                int x = event.xbutton.x;
                int y = event.xbutton.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
       -                return;
       +                return 0;
                ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
       -        if (ptr && ltk_collide_rect(ptr->rect, x, y))
       +        if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
                        ltk_widget_mouse_press_event(ptr, event);
       +                return 0;
       +        }
       +        return 0;
        }
        
       -static void
       +static int
        ltk_grid_mouse_release(ltk_grid *grid, XEvent event) {
                int x = event.xbutton.x;
                int y = event.xbutton.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
       -                return;
       +                return 0;
                ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
       -        if (ptr && ltk_collide_rect(ptr->rect, x, y))
       +        if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
                        ltk_widget_mouse_release_event(ptr, event);
       +                return 0;
       +        }
       +        return 0;
        }
        
       -static void
       +static int
        ltk_grid_motion_notify(ltk_grid *grid, XEvent event) {
       +        /* FIXME: Why does it check this? */
                short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
                if (pressed)
       -                return;
       +                return 0;
                int x = event.xbutton.x;
                int y = event.xbutton.y;
                int row = ltk_grid_find_nearest_row(grid, y);
                int column = ltk_grid_find_nearest_column(grid, x);
                if (row == -1 || column == -1)
       -                return;
       +                return 0;
                ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
       -        if (ptr && ltk_collide_rect(ptr->rect, x, y))
       +        if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
                        ltk_widget_motion_notify_event(ptr, event);
       +                return 0;
       +        }
       +        return 0;
        }
        
        /* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> [sticky] */
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -83,9 +83,9 @@ typedef struct ltk_widget {
        
                void (*key_press) (void *, XEvent);
                void (*key_release) (void *, XEvent);
       -        void (*mouse_press) (void *, XEvent);
       -        void (*mouse_release) (void *, XEvent);
       -        void (*motion_notify) (void *, XEvent);
       +        int (*mouse_press) (void *, XEvent);
       +        int (*mouse_release) (void *, XEvent);
       +        int (*motion_notify) (void *, XEvent);
                void (*mouse_leave) (void *, XEvent);
                void (*mouse_enter) (void *, XEvent);
        
       t@@ -160,8 +160,8 @@ void ltk_fatal_errno(const char *format, ...);
        int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col);
        void ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data);
        int ltk_collide_rect(ltk_rect rect, int x, int y);
       -void ltk_remove_active_widget(ltk_widget *widget);
       -void ltk_set_active_widget(ltk_window *window, ltk_widget *widget);
       +void ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget);
       +void ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget);
        void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window * window,
            void (*draw) (void *), void (*change_state) (void *),
            void (*destroy) (void *, int), unsigned int needs_redraw,
   DIR diff --git a/ltkd.c b/ltkd.c
       t@@ -725,42 +725,6 @@ ltk_collide_rect(ltk_rect rect, int x, int y) {
                        && (rect.y + rect.h) >= y);
        }
        
       -static void
       -ltk_widget_change_state(ltk_widget *widget) {
       -        if (widget->change_state)
       -                widget->change_state(widget);
       -        if (widget->needs_redraw)
       -                ltk_window_invalidate_rect(widget->window, widget->rect);
       -}
       -
       -void
       -ltk_window_remove_active_widget(ltk_window *window) {
       -        ltk_widget *widget = window->active_widget;
       -        if (!widget) return;
       -        while (widget) {
       -                widget->state = LTK_NORMAL;
       -                widget->active_widget = NULL;
       -                ltk_widget_change_state(widget);
       -                widget = widget->parent;
       -        }
       -        window->active_widget = NULL;
       -}
       -
       -void
       -ltk_window_set_active_widget(ltk_widget *widget) {
       -        widget->window->active_widget = widget;
       -        ltk_widget *parent = widget->parent;
       -        while (widget) {
       -                widget->state = LTK_ACTIVE;
       -                ltk_widget_change_state(widget);
       -                if (parent)
       -                        parent->active_widget = widget;
       -                widget = parent;
       -                if (parent)
       -                        parent = widget->parent;
       -        }
       -}
       -
        void
        ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
            void (*draw) (void *), void (*change_state) (void *),
       t@@ -810,32 +774,46 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
                widget->sticky = 0;
        }
        
       +static void
       +ltk_widget_change_state(ltk_widget *widget) {
       +        if (widget->change_state)
       +                widget->change_state(widget);
       +        if (widget->needs_redraw)
       +                ltk_window_invalidate_rect(widget->window, widget->rect);
       +}
       +
        void
       -ltk_widget_set_pressed(ltk_window *window, ltk_widget *widget) {
       -        ltk_widget *act = window->active_widget;
       -        ltk_widget *pre = window->pressed_widget;
       -        if (pre) {
       -                if (act) {
       -                        act->state = LTK_NORMAL;
       -                        if (act->needs_redraw)
       -                                ltk_window_invalidate_rect(window, act->rect);
       -                        if (act->change_state)
       -                                act->change_state(act);
       -                }
       -                pre->state = LTK_ACTIVE;
       -                window->active_widget = pre;
       -                if (pre->needs_redraw)
       -                        ltk_window_invalidate_rect(window, pre->rect);
       -                if (pre->change_state)
       -                        pre->change_state(pre);
       +ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
       +        if (window->active_widget == widget)
       +                return;
       +        if (window->active_widget) {
       +                window->active_widget->state = LTK_NORMAL;
       +                ltk_widget_change_state(window->active_widget);
       +        }
       +        window->active_widget = widget;
       +        if (widget) {
       +                widget->state = LTK_ACTIVE;
       +                ltk_widget_change_state(widget);
       +        }
       +}
       +
       +/* FIXME: Should pressed widget also be set as active widget? */
       +void
       +ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget) {
       +        if (window->pressed_widget == widget)
       +                return;
       +        if (window->active_widget && window->active_widget != widget) {
       +                window->active_widget->state = LTK_NORMAL;
       +                ltk_widget_change_state(window->active_widget);
       +        }
       +        if (window->pressed_widget) {
       +                window->pressed_widget->state = LTK_ACTIVE;
       +                ltk_widget_change_state(window->pressed_widget);
                }
                window->pressed_widget = widget;
                if (widget) {
                        widget->state = LTK_PRESSED;
       -                if (widget->needs_redraw)
       -                        ltk_window_invalidate_rect(widget->window, widget->rect);
       -                if (widget->change_state)
       -                        widget->change_state(widget);
       +                ltk_widget_change_state(widget);
                }
        }
        
       t@@ -843,30 +821,34 @@ void
        ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) {
                if (!widget || widget->state == LTK_DISABLED)
                        return;
       -        if (event.xbutton.button == 1)
       -                ltk_widget_set_pressed(widget->window, widget);
       +        int default_handler = 1;
                if (widget->mouse_press)
       -                widget->mouse_press(widget, event);
       +                default_handler = widget->mouse_press(widget, event);
       +        if (default_handler) {
       +                if (event.xbutton.button == 1)
       +                        ltk_window_set_pressed_widget(widget->window, widget);
       +        }
        }
        
        void
        ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
       -        if (!widget)
       -                return;
       -        /* FIXME: MAKE THIS WORK MORE CONSISTENTLY FOR OTHER MOUSE BUTTONS */
       -        ltk_widget_set_pressed(widget->window, NULL);
       -        if (widget->state == LTK_DISABLED)
       +        if (!widget || widget->state == LTK_DISABLED)
                        return;
       +        int default_handler = 1;
                if (widget->mouse_release)
       -                widget->mouse_release(widget, event);
       +                default_handler = widget->mouse_release(widget, event);
       +        if (default_handler)
       +                ltk_window_set_pressed_widget(widget->window, NULL);
        }
        
        /* FIXME: ONLY SET ACTIVE WIDGET AT BOTTOM OF HIERARCHY
           -> Don't first set parent as active widget and then child */
        void
        ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
       -        if (!widget) return;
       +        if (!widget || widget->state == LTK_DISABLED)
       +                return;
                /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */
       +        /*
                if (((widget->state == LTK_NORMAL) ||
                     (widget->state == LTK_ACTIVE && widget->window->active_widget != widget)) &&
                     !widget->window->pressed_widget) {
       t@@ -875,14 +857,17 @@ ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
                                widget->change_state(widget);
                        if (widget->mouse_enter)
                                widget->mouse_enter(widget, event);
       -                /* FIXME: do this properly */
                        ltk_window_remove_active_widget(widget->window);
                        ltk_window_set_active_widget(widget);
                }
       +        */
       +        int default_handler = 1;
                if (widget->window->pressed_widget && widget->window->pressed_widget->motion_notify)
       -                widget->window->pressed_widget->motion_notify(widget->window->pressed_widget, event);
       +                default_handler = widget->window->pressed_widget->motion_notify(widget->window->pressed_widget, event);
                else if (widget->motion_notify)
       -                widget->motion_notify(widget, event);
       +                default_handler = widget->motion_notify(widget, event);
       +        if (default_handler)
       +                ltk_window_set_active_widget(widget->window, widget);
        }
        
        static void
   DIR diff --git a/scrollbar.c b/scrollbar.c
       t@@ -36,8 +36,8 @@
        #include "scrollbar.h"
        
        static void ltk_scrollbar_draw(ltk_scrollbar *scrollbar, ltk_rect clip);
       -static void ltk_scrollbar_mouse_press(ltk_scrollbar *scrollbar, XEvent event);
       -static void ltk_scrollbar_motion_notify(ltk_scrollbar *scrollbar, XEvent event);
       +static int ltk_scrollbar_mouse_press(ltk_scrollbar *scrollbar, XEvent event);
       +static int ltk_scrollbar_motion_notify(ltk_scrollbar *scrollbar, XEvent event);
        static void ltk_scrollbar_destroy(ltk_scrollbar *scrollbar, int shallow);
        
        static struct {
       t@@ -99,7 +99,7 @@ ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size) {
                /* FIXME: some sort of error? */
                if (virtual_size <= 0)
                        return;
       -        scrollbar->cur_pos = (int)(((double)scrollbar->cur_pos / scrollbar->virtual_size) * virtual_size);
       +        scrollbar->cur_pos = ((double)scrollbar->cur_pos / scrollbar->virtual_size) * virtual_size;
                scrollbar->virtual_size = virtual_size;
        }
        
       t@@ -158,22 +158,23 @@ ltk_scrollbar_draw(ltk_scrollbar *scrollbar, ltk_rect clip) {
                    handle_x, handle_y, handle_w, handle_h);
        }
        
       -static void
       +static int
        ltk_scrollbar_mouse_press(ltk_scrollbar *scrollbar, XEvent event) {
                if (event.xbutton.button != 1)
       -                return;
       +                return 1;
                scrollbar->last_mouse_x = scrollbar->widget.rect.x;
                scrollbar->last_mouse_y = scrollbar->widget.rect.y;
                scrollbar->cur_pos = 0;
                ltk_scrollbar_motion_notify(scrollbar, event);
       +        return 1;
        }
        
       -static void
       +static int
        ltk_scrollbar_motion_notify(ltk_scrollbar *sc, XEvent event) {
                double scale;
                int delta, max_pos;
                if (sc->widget.state != LTK_PRESSED)
       -                return;
       +                return 1;
                if (sc->orient == LTK_HORIZONTAL) {
                        delta = event.xbutton.x - sc->last_mouse_x;
                        max_pos = sc->virtual_size > sc->widget.rect.w ? sc->virtual_size - sc->widget.rect.w : 0;
       t@@ -183,18 +184,20 @@ ltk_scrollbar_motion_notify(ltk_scrollbar *sc, XEvent event) {
                        max_pos = sc->virtual_size > sc->widget.rect.h ? sc->virtual_size - sc->widget.rect.h : 0;
                        scale = sc->virtual_size / (double)sc->widget.rect.h;
                }
       -        /* FIXME: This doesn't work because delta is always only one pixel or so */
       -        sc->cur_pos += (int)(scale * delta);
       +        sc->cur_pos += scale * delta;
                if (sc->cur_pos < 0)
                        sc->cur_pos = 0;
                else if (sc->cur_pos > max_pos)
                        sc->cur_pos = max_pos;
                sc->last_mouse_x = event.xbutton.x;
                sc->last_mouse_y = event.xbutton.y;
       +        if (delta > 0)
       +                sc->callback(sc->callback_data);
       +        return 1;
        }
        
        ltk_scrollbar *
       -ltk_scrollbar_create(ltk_window *window, ltk_orientation orient) {
       +ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback)(void *), void *data) {
                ltk_scrollbar *sc = malloc(sizeof(ltk_scrollbar));
                if (!sc)
                        ltk_fatal_errno("Unable to allocate memory for scrollbar.\n");
       t@@ -211,6 +214,8 @@ ltk_scrollbar_create(ltk_window *window, ltk_orientation orient) {
                        sc->widget.rect.h = theme.size;
                else
                        sc->widget.rect.w = theme.size;
       +        sc->callback = callback;
       +        sc->callback_data = data;
        
                return sc;
        }
   DIR diff --git a/scrollbar.h b/scrollbar.h
       t@@ -28,7 +28,9 @@
        
        typedef struct {
                ltk_widget widget;
       -        int cur_pos;
       +        void (*callback)(void *);
       +        void *callback_data;
       +        double cur_pos;
                int virtual_size;
                int last_mouse_x;
                int last_mouse_y;
       t@@ -38,6 +40,6 @@ typedef struct {
        void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size);
        void ltk_scrollbar_setup_theme_defaults(ltk_window *window);
        void ltk_scrollbar_ini_handler(ltk_window *window, const char *prop, const char *value);
       -ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient);
       +ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback)(void *), void *data);
        
        #endif /* _LTK_SCROLLBAR_H_ */