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 33cf30d1cfde0826ff45ce7b876ca2c1d8dbc9b9
parent d1f0903f23c0887590329483d8993cbefb1b6189
Author: lumidify <nobody@lumidify.org>
Date:   Sun,  8 May 2022 17:10:22 +0200

Make scrolling normal

Diffstat:
Msrc/box.c | 23+++++++++++++++++++++--
Msrc/button.c | 8++++++--
Msrc/scrollbar.c | 111++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/scrollbar.h | 3++-
Msrc/widget.c | 33+++++++++------------------------
5 files changed, 100 insertions(+), 78 deletions(-)

diff --git a/src/box.c b/src/box.c @@ -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)) { @@ -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 diff --git a/src/button.c b/src/button.c @@ -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 * diff --git a/src/scrollbar.c b/src/scrollbar.c @@ -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: @@ -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); } @@ -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 * diff --git a/src/scrollbar.h b/src/scrollbar.h @@ -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 @@ -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_ */ diff --git a/src/widget.c b/src/widget.c @@ -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); }