commit 33cf30d1cfde0826ff45ce7b876ca2c1d8dbc9b9
parent d1f0903f23c0887590329483d8993cbefb1b6189
Author: lumidify <nobody@lumidify.org>
Date:   Sun,  8 May 2022 17:10:22 +0200
Make scrolling normal
Diffstat:
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, <k_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, <k_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);
 }