URI: 
       tMake scrolling normal - 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 33cf30d1cfde0826ff45ce7b876ca2c1d8dbc9b9
   DIR parent d1f0903f23c0887590329483d8993cbefb1b6189
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun,  8 May 2022 17:10:22 +0200
       
       Make scrolling normal
       
       Diffstat:
         M src/box.c                           |      23 +++++++++++++++++++++--
         M src/button.c                        |       8 ++++++--
         M src/scrollbar.c                     |     111 +++++++++++++++++--------------
         M src/scrollbar.h                     |       3 ++-
         M src/widget.c                        |      33 +++++++++----------------------
       
       5 files changed, 100 insertions(+), 78 deletions(-)
       ---
   DIR diff --git a/src/box.c b/src/box.c
       t@@ -315,7 +315,7 @@ ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XE
                        return 0;
                }
        
       -        /* FIXME: When scrolling is implemented properly, check only the currently visible items */
       +        /* FIXME: 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)) {
       t@@ -329,7 +329,26 @@ ltk_box_mouse_event(ltk_box *box, XEvent event, void (*handler)(ltk_widget *, XE
        static int
        ltk_box_mouse_press(ltk_widget *self, XEvent event) {
                ltk_box *box = (ltk_box *)self;
       -        return ltk_box_mouse_event(box, event, &ltk_widget_mouse_press_event);
       +        /* FIXME: combine multiple events into one for efficiency */
       +        /* FIXME: fix this whole state handling */
       +        if (event.xbutton.button == 4 || event.xbutton.button == 5) {
       +                ltk_widget *widget;
       +                int default_handler = 1;
       +                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)) {
       +                                if (widget->vtable->mouse_press)
       +                                        default_handler = widget->vtable->mouse_press(widget, event);
       +                        }
       +                }
       +                if (default_handler) {
       +                        int delta = event.xbutton.button == 4 ? -15 : 15;
       +                        ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0);
       +                }
       +                return 0;
       +        } else {
       +                return ltk_box_mouse_event(box, event, &ltk_widget_mouse_press_event);
       +        }
        }
        
        static int
   DIR diff --git a/src/button.c b/src/button.c
       t@@ -189,12 +189,16 @@ ltk_button_change_state(ltk_widget *self) {
                self->dirty = 1;
        }
        
       +/* FIXME: only when pressed button was actually this one */
        static int
        ltk_button_mouse_release(ltk_widget *self, XEvent event) {
                (void)event;
                ltk_button *button = (ltk_button *)self;
       -        ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
       -        return 1;
       +        if (event.xbutton.button == 1) {
       +                ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
       +                return 1;
       +        }
       +        return 0;
        }
        
        static ltk_button *
   DIR diff --git a/src/scrollbar.c b/src/scrollbar.c
       t@@ -109,12 +109,35 @@ ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size) {
                scrollbar->virtual_size = virtual_size;
        }
        
       +static ltk_rect
       +get_handle_rect(ltk_scrollbar *sc) {
       +        ltk_rect r;
       +        ltk_rect sc_rect = sc->widget.rect;
       +        if (sc->orient == LTK_HORIZONTAL) {
       +                r.y = 0;
       +                r.h = sc_rect.h;
       +                r.x = (int)((sc->cur_pos / (double)sc->virtual_size) * sc_rect.w);
       +                if (sc->virtual_size > sc_rect.w)
       +                        r.w = (int)((sc_rect.w / (double)sc->virtual_size) * sc_rect.w);
       +                else
       +                        r.w = sc_rect.w;
       +        } else {
       +                r.x = 0;
       +                r.w = sc_rect.w;
       +                r.y = (int)((sc->cur_pos / (double)sc->virtual_size) * sc_rect.h);
       +                if (sc->virtual_size > sc_rect.h)
       +                        r.h = (int)((sc_rect.h / (double)sc->virtual_size) * sc_rect.h);
       +                else
       +                        r.h = sc_rect.h;
       +        }
       +        return r;
       +}
       +
        static void
        ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
                /* FIXME: dirty attribute */
                ltk_scrollbar *scrollbar = (ltk_scrollbar *)self;
                ltk_color *bg = NULL, *fg = NULL;
       -        int handle_x, handle_y, handle_w, handle_h;
                ltk_rect rect = scrollbar->widget.rect;
                switch (scrollbar->widget.state) {
                case LTK_NORMAL:
       t@@ -141,24 +164,7 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
                ltk_surface_fill_rect(s, bg, (ltk_rect){0, 0, rect.w, rect.h});
                /* FIXME: maybe too much calculation in draw function - move to
                   resizing function? */
       -        if (scrollbar->orient == LTK_HORIZONTAL) {
       -                handle_y = 0;
       -                handle_h = rect.h;
       -                handle_x = (int)((scrollbar->cur_pos / (double)scrollbar->virtual_size) * rect.w);
       -                if (scrollbar->virtual_size > rect.w)
       -                        handle_w = (int)((rect.w / (double)scrollbar->virtual_size) * rect.w);
       -                else
       -                        handle_w = rect.w;
       -        } else {
       -                handle_x = 0;
       -                handle_w = rect.w;
       -                handle_y = (int)((scrollbar->cur_pos / (double)scrollbar->virtual_size) * rect.h);
       -                if (scrollbar->virtual_size > rect.h)
       -                        handle_h = (int)((rect.h / (double)scrollbar->virtual_size) * rect.h);
       -                else
       -                        handle_h = rect.h;
       -        }
       -        ltk_surface_fill_rect(s, fg, (ltk_rect){handle_x, handle_y, handle_w, handle_h});
       +        ltk_surface_fill_rect(s, fg, get_handle_rect(scrollbar));
                ltk_rect clip_final = ltk_rect_intersect(clip, rect);
                ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
        }
       t@@ -167,64 +173,71 @@ static int
        ltk_scrollbar_mouse_press(ltk_widget *self, XEvent event) {
                ltk_scrollbar *sc = (ltk_scrollbar *)self;
                int max_pos;
       -        double rel_pos;
       -        if (event.xbutton.button != 1 && event.xbutton.button != 3)
       +        if (event.xbutton.button != 1)
                        return 0;
       +        int ex = event.xbutton.x, ey = event.xbutton.y;
       +        ltk_rect handle_rect = get_handle_rect(sc);
                if (sc->orient == LTK_HORIZONTAL) {
       -                rel_pos = (event.xbutton.x - sc->widget.rect.x)  / (double)sc->widget.rect.w;
       +                if (ex < handle_rect.x || ex > handle_rect.x + handle_rect.w) {
       +                        sc->cur_pos = (sc->virtual_size / (double)sc->widget.rect.w) * (ex - handle_rect.w / 2 - sc->widget.rect.x);
       +                }
                        max_pos = sc->virtual_size > sc->widget.rect.w ? sc->virtual_size - sc->widget.rect.w : 0;
                } else {
       -                rel_pos = (event.xbutton.y - sc->widget.rect.y) / (double)sc->widget.rect.h;
       +                if (ey < handle_rect.y || ey > handle_rect.y + handle_rect.h) {
       +                        sc->cur_pos = (sc->virtual_size / (double)sc->widget.rect.h) * (ey - handle_rect.h / 2 - sc->widget.rect.y);
       +                }
                        max_pos = sc->virtual_size > sc->widget.rect.h ? sc->virtual_size - sc->widget.rect.h : 0;
                }
       -        /* On right click, move the scrollbar left/up by 30% of the total size times how far
       -           away the click is from the right/bottom.
       -           On left click, move the scrollbar right/down by 30% of the total size times how far
       -           away the click is from the left/top. */
       -        if (event.xbutton.button == 1) {
       -                sc->cur_pos += rel_pos * sc->virtual_size * 0.3;
       -        } else if (event.xbutton.button == 3) {
       -                sc->cur_pos -= (1 - rel_pos) * sc->virtual_size * 0.3;
       -        }
       -                
                if (sc->cur_pos < 0)
                        sc->cur_pos = 0;
                else if (sc->cur_pos > max_pos)
                        sc->cur_pos = max_pos;
                sc->callback(sc->callback_data);
       -        return 0;
       +        sc->last_mouse_x = event.xbutton.x;
       +        sc->last_mouse_y = event.xbutton.y;
       +        return 1;
        }
        
       -/* FIXME: Make this scrollbar more "traditional" */
       -static int
       -ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
       -        (void)self;
       -        (void)event;
       +/* FIXME: also queue redraw */
       +/* FIXME: improve interface (scaled is weird) */
       +void
       +ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled) {
                ltk_scrollbar *sc = (ltk_scrollbar *)self;
       +        int max_pos;
                double scale;
       -        int delta, max_pos;
       -        if (self->state != LTK_PRESSED) {
       -                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;
                        scale = sc->virtual_size / (double)sc->widget.rect.w;
                } else {
       -                delta = event.xbutton.y - sc->last_mouse_y;
                        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;
                }
       -        sc->cur_pos += scale * delta;
       +        if (scaled)
       +                sc->cur_pos += scale * delta;
       +        else
       +                sc->cur_pos += delta;
                if (sc->cur_pos < 0)
                        sc->cur_pos = 0;
                else if (sc->cur_pos > max_pos)
                        sc->cur_pos = max_pos;
       +        sc->callback(sc->callback_data);
       +}
       +
       +static int
       +ltk_scrollbar_motion_notify(ltk_widget *self, XEvent event) {
       +        ltk_scrollbar *sc = (ltk_scrollbar *)self;
       +        int delta;
       +        if (self->state != LTK_PRESSED) {
       +                return 1;
       +        }
       +        if (sc->orient == LTK_HORIZONTAL)
       +                delta = event.xbutton.x - sc->last_mouse_x;
       +        else
       +                delta = event.xbutton.y - sc->last_mouse_y;
       +        ltk_scrollbar_scroll(self, delta, 1);
                sc->last_mouse_x = event.xbutton.x;
                sc->last_mouse_y = event.xbutton.y;
       -        if (delta > 0)
       -                sc->callback(sc->callback_data);
       -        return 1;
       +        return 0;
        }
        
        ltk_scrollbar *
   DIR diff --git a/src/scrollbar.h b/src/scrollbar.h
       t@@ -1,5 +1,5 @@
        /*
       - * Copyright (c) 2021 lumidify <nobody@lumidify.org>
       + * Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org>
         *
         * Permission to use, copy, modify, and/or distribute this software for any
         * purpose with or without fee is hereby granted, provided that the above
       t@@ -34,5 +34,6 @@ 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, void (*callback)(ltk_widget *), void *data);
       +void ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled);
        
        #endif /* _LTK_SCROLLBAR_H_ */
   DIR diff --git a/src/widget.c b/src/widget.c
       t@@ -130,39 +130,24 @@ void
        ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
                if (!widget || widget->state == LTK_DISABLED)
                        return;
       -        int default_handler = 1;
                if (widget->vtable->mouse_release)
       -                default_handler = widget->vtable->mouse_release(widget, event);
       -        if (default_handler)
       -                ltk_window_set_pressed_widget(widget->window, NULL);
       +                widget->vtable->mouse_release(widget, event);
       +        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 || 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) {
       -                widget->state = LTK_ACTIVE;
       -                if (widget->vtable->change_state)
       -                        widget->vtable->change_state(widget);
       -                if (widget->vtable->mouse_enter)
       -                        widget->vtable->mouse_enter(widget, event);
       -                ltk_window_remove_active_widget(widget->window);
       -                ltk_window_set_active_widget(widget);
       +        int set_active = 1;
       +        if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify) {
       +                widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event);
       +                set_active = 0;
       +        } else if (widget->vtable->motion_notify) {
       +                set_active = widget->vtable->motion_notify(widget, event);
                }
       -        */
       -        int default_handler = 1;
       -        if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify)
       -                default_handler = widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event);
       -        else if (widget->vtable->motion_notify)
       -                default_handler = widget->vtable->motion_notify(widget, event);
       -        if (default_handler)
       +        if (set_active)
                        ltk_window_set_active_widget(widget->window, widget);
        }