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 c25f7ed3099b6026f5e0c169e30a8a28719d5633
parent 2ed1efbf50d122e1120e32416b71c9838d9c3880
Author: lumidify <nobody@lumidify.org>
Date:   Fri, 22 Jan 2021 19:42:03 +0100

Somewhat fix mouse event handling

Diffstat:
Mbox.c | 47+++++++++++++++++++++++------------------------
Mbutton.c | 5+++--
Mgrid.c | 36+++++++++++++++++++++++-------------
Mltk.h | 10+++++-----
Mltkd.c | 125+++++++++++++++++++++++++++++++++++--------------------------------------------
Mscrollbar.c | 25+++++++++++++++----------
Mscrollbar.h | 6++++--
7 files changed, 128 insertions(+), 126 deletions(-)

diff --git a/box.c b/box.c @@ -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, @@ -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; @@ -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] */ diff --git a/button.c b/button.c @@ -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); @@ -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 * diff --git a/grid.c b/grid.c @@ -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, @@ -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] */ diff --git a/ltk.h b/ltk.h @@ -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); @@ -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, diff --git a/ltkd.c b/ltkd.c @@ -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 *), @@ -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); } } @@ -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) { @@ -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 diff --git a/scrollbar.c b/scrollbar.c @@ -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 { @@ -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; } @@ -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; @@ -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"); @@ -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; } diff --git a/scrollbar.h b/scrollbar.h @@ -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; @@ -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_ */