commit c25f7ed3099b6026f5e0c169e30a8a28719d5633
parent 2ed1efbf50d122e1120e32416b71c9838d9c3880
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(-)
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 = <k_box_child_size_change;
 	box->widget.remove_child = <k_box_remove;
 
-	box->sc = ltk_scrollbar_create(window, orient);
+	box->sc = ltk_scrollbar_create(window, orient, <k_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 == <k_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, <k_widget_mouse_press_event);
+	return ltk_box_mouse_event(box, event, <k_widget_mouse_press_event);
 }
 
-static void
+static int
 ltk_box_mouse_release(ltk_box *box, XEvent event) {
-	ltk_box_mouse_event(box, event, <k_widget_mouse_release_event);
+	return ltk_box_mouse_event(box, event, <k_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, <k_widget_motion_notify_event);
+	return ltk_box_mouse_event(box, event, <k_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_ */