commit b610b280bbdbda3dba8f8fc3e67961f26857da43
parent 99773bbc2bcaea288c5d8b657bdff74ae69e216a
Author: lumidify <nobody@lumidify.org>
Date: Wed, 16 Aug 2023 22:11:10 +0200
Add double/triple-click; add explicit scroll event
Diffstat:
13 files changed, 394 insertions(+), 123 deletions(-)
diff --git a/src/array.h b/src/array.h
@@ -30,27 +30,34 @@
#include "util.h"
#include "memory.h"
-#define LTK_ARRAY_INIT_DECL_BASE(name, type, storage) \
-typedef struct { \
- type *buf; \
- size_t buf_size; \
- size_t len; \
-} ltk_array_##name; \
- \
-storage ltk_array_##name *ltk_array_create_##name(size_t initial_len); \
-storage type ltk_array_pop_##name(ltk_array_##name *ar); \
-storage void ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len); \
-storage void ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len); \
-storage void ltk_array_resize_##name(ltk_array_##name *ar, size_t size); \
-storage void ltk_array_destroy_##name(ltk_array_##name *ar); \
-storage void ltk_array_clear_##name(ltk_array_##name *ar); \
-storage void ltk_array_append_##name(ltk_array_##name *ar, type elem); \
-storage void ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type)); \
-storage type ltk_array_get_safe_##name(ltk_array_##name *ar, size_t index); \
-storage void ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e);
+/* FIXME: make this work on more compilers? */
+#if (defined(__GNUC__) || defined(__clang__))
+#define LTK_UNUSED_FUNC __attribute__((unused))
+#else
+#define LTK_UNUSED_FUNC
+#endif
+
+#define LTK_ARRAY_INIT_DECL_BASE(name, type, storage) \
+typedef struct { \
+ type *buf; \
+ size_t buf_size; \
+ size_t len; \
+} ltk_array_##name; \
+ \
+LTK_UNUSED_FUNC storage ltk_array_##name *ltk_array_create_##name(size_t initial_len); \
+LTK_UNUSED_FUNC storage type ltk_array_pop_##name(ltk_array_##name *ar); \
+LTK_UNUSED_FUNC storage void ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len); \
+LTK_UNUSED_FUNC storage void ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len); \
+LTK_UNUSED_FUNC storage void ltk_array_resize_##name(ltk_array_##name *ar, size_t size); \
+LTK_UNUSED_FUNC storage void ltk_array_destroy_##name(ltk_array_##name *ar); \
+LTK_UNUSED_FUNC storage void ltk_array_clear_##name(ltk_array_##name *ar); \
+LTK_UNUSED_FUNC storage void ltk_array_append_##name(ltk_array_##name *ar, type elem); \
+LTK_UNUSED_FUNC storage void ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type)); \
+LTK_UNUSED_FUNC storage type ltk_array_get_safe_##name(ltk_array_##name *ar, size_t index); \
+LTK_UNUSED_FUNC storage void ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e);
#define LTK_ARRAY_INIT_IMPL_BASE(name, type, storage) \
-storage ltk_array_##name * \
+LTK_UNUSED_FUNC storage ltk_array_##name * \
ltk_array_create_##name(size_t initial_len) { \
if (initial_len == 0) \
ltk_fatal("Array length is zero\n"); \
@@ -61,7 +68,7 @@ ltk_array_create_##name(size_t initial_len) { \
return ar; \
} \
\
-storage type \
+LTK_UNUSED_FUNC storage type \
ltk_array_pop_##name(ltk_array_##name *ar) { \
if (ar->len == 0) \
ltk_fatal("Array empty; cannot pop.\n"); \
@@ -70,7 +77,7 @@ ltk_array_pop_##name(ltk_array_##name *ar) { \
} \
\
/* FIXME: having this function in the public interface is ugly */ \
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len) { \
if (index > ar->len) \
ltk_fatal("Array index out of bounds\n"); \
@@ -82,7 +89,7 @@ ltk_array_prepare_gap_##name(ltk_array_##name *ar, size_t index, size_t len) {
(ar->len - len - index) * sizeof(type)); \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len) { \
ltk_array_prepare_gap_##name(ar, index, len); \
for (size_t i = 0; i < len; i++) { \
@@ -90,20 +97,20 @@ ltk_array_insert_##name(ltk_array_##name *ar, size_t index, type *elem, size_t l
} \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_append_##name(ltk_array_##name *ar, type elem) { \
if (ar->len == ar->buf_size) \
ltk_array_resize_##name(ar, ar->len + 1); \
ar->buf[ar->len++] = elem; \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_clear_##name(ltk_array_##name *ar) { \
ar->len = 0; \
ltk_array_resize_##name(ar, 1); \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_resize_##name(ltk_array_##name *ar, size_t len) { \
size_t new_size = ideal_array_size(ar->buf_size, len); \
if (new_size != ar->buf_size) { \
@@ -113,7 +120,7 @@ ltk_array_resize_##name(ltk_array_##name *ar, size_t len) { \
} \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_destroy_##name(ltk_array_##name *ar) { \
if (!ar) \
return; \
@@ -121,7 +128,7 @@ ltk_array_destroy_##name(ltk_array_##name *ar) { \
ltk_free(ar); \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type)) { \
if (!ar) \
return; \
@@ -131,14 +138,14 @@ ltk_array_destroy_deep_##name(ltk_array_##name *ar, void (*destroy_func)(type))
ltk_array_destroy_##name(ar); \
} \
\
-storage type \
+LTK_UNUSED_FUNC storage type \
ltk_array_get_safe_##name(ltk_array_##name *ar, size_t index) { \
if (index >= ar->len) \
ltk_fatal("Index out of bounds.\n"); \
return ar->buf[index]; \
} \
\
-storage void \
+LTK_UNUSED_FUNC storage void \
ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e) { \
if (index >= ar->len) \
ltk_fatal("Index out of bounds.\n"); \
diff --git a/src/box.c b/src/box.c
@@ -40,7 +40,7 @@ static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, uns
static int ltk_box_remove(ltk_widget *widget, ltk_widget *self, ltk_error *err);
/* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow, ltk_error *err); */
static void ltk_box_scroll(ltk_widget *self);
-static int ltk_box_mouse_press(ltk_widget *self, ltk_button_event *event);
+static int ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event);
static ltk_widget *ltk_box_get_child_at_pos(ltk_widget *self, int x, int y);
static void ltk_box_ensure_rect_shown(ltk_widget *self, ltk_rect r);
@@ -65,7 +65,8 @@ static struct ltk_widget_vtable vtable = {
.remove_child = <k_box_remove,
.key_press = NULL,
.key_release = NULL,
- .mouse_press = <k_box_mouse_press,
+ .mouse_press = NULL,
+ .mouse_scroll = <k_box_mouse_scroll,
.mouse_release = NULL,
.motion_notify = NULL,
.get_child_at_pos = <k_box_get_child_at_pos,
@@ -471,19 +472,18 @@ ltk_box_get_child_at_pos(ltk_widget *self, int x, int y) {
}
static int
-ltk_box_mouse_press(ltk_widget *self, ltk_button_event *event) {
+ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event) {
ltk_box *box = (ltk_box *)self;
- /* FIXME: combine multiple events into one for efficiency */
- if (event->button == LTK_BUTTON4 || event->button == LTK_BUTTON5) {
+ if (event->dy) {
+ /* FIXME: horizontal scrolling, etc. */
/* FIXME: configure scrollstep */
- int delta = event->button == LTK_BUTTON4 ? -15 : 15;
+ int delta = event->dy * -15;
ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0);
ltk_point glob = ltk_widget_pos_to_global(self, event->x, event->y);
ltk_window_fake_motion_event(self->window, glob.x, glob.y);
return 1;
- } else {
- return 0;
}
+ return 0;
}
/* box <box id> add <widget id> [sticky] */
diff --git a/src/entry.c b/src/entry.c
@@ -53,6 +53,8 @@ static int ltk_entry_motion_notify(ltk_widget *self, ltk_motion_event *event);
static int ltk_entry_mouse_enter(ltk_widget *self, ltk_motion_event *event);
static int ltk_entry_mouse_leave(ltk_widget *self, ltk_motion_event *event);
+/* FIXME: give entire key event, not just text */
+/* FIXME: also allow binding key release, not just press */
typedef void (*cb_func)(ltk_entry *, char *, size_t);
/* FIXME: configure mouse actions, e.g. select-word-under-pointer, move-cursor-to-pointer */
diff --git a/src/event.h b/src/event.h
@@ -12,6 +12,12 @@ typedef struct {
typedef struct {
ltk_event_type type;
int x, y;
+ int dx, dy;
+} ltk_scroll_event;
+
+typedef struct {
+ ltk_event_type type;
+ int x, y;
} ltk_motion_event;
typedef struct {
@@ -43,6 +49,7 @@ typedef struct {
typedef union {
ltk_event_type type;
ltk_button_event button;
+ ltk_scroll_event scroll;
ltk_motion_event motion;
ltk_key_event key;
ltk_configure_event configure;
@@ -52,10 +59,9 @@ typedef union {
#include "ltk.h"
-int ltk_events_pending(ltk_renderdata *renderdata);
void ltk_events_cleanup(void);
/* WARNING: Text returned in key and keyboard events must be copied before calling this function again! */
-void ltk_next_event(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event);
+int ltk_next_event(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event);
void ltk_generate_keyboard_event(ltk_renderdata *renderdata, ltk_event *event);
#endif /* LTK_EVENT_H */
diff --git a/src/event_xlib.c b/src/event_xlib.c
@@ -13,11 +13,35 @@
static char *text = NULL;
static size_t text_alloc = 0;
static char *cur_kbd = NULL;
-
-int
-ltk_events_pending(ltk_renderdata *renderdata) {
- return XPending(renderdata->dpy);
-}
+/* FIXME: support more buttons?
+ -> What even makes sense here? Mice can support a bunch
+ of buttons, but what is sensible here? Just adding
+ support for higher button numbers would cause problems
+ when adding other backends (e.g. SDL) that might do
+ things completely differently. */
+/* FIXME: support touch events? */
+/* times of last button press/release,
+ used to implement double/triple-click */
+static Time last_button_press[] = {0, 0, 0};
+static Time last_button_release[] = {0, 0, 0};
+/* positions of last press/release so double/triple-click is
+ only generated when the position is near enough */
+static struct point {
+ int x;
+ int y;
+} press_pos[] = {{0, 0}, {0, 0}, {0, 0}};
+static struct point release_pos[] = {{0, 0}, {0, 0}, {0, 0}};
+/* stores whether the last button press already was
+ a double-click to decide if a triple-click should
+ be generated (same for release) */
+static int was_2press[] = {0, 0, 0};
+static int was_2release[] = {0, 0, 0};
+/* Used to store special next event - currently just
+ used to implement double/triple-click because the
+ actual double/triple-click/release event is
+ generated in addition to the regular press/release */
+static int next_event_valid = 0;
+static ltk_button_event next_event;
void
ltk_events_cleanup(void) {
@@ -34,10 +58,6 @@ get_button(unsigned int button) {
case Button1: return LTK_BUTTONL;
case Button2: return LTK_BUTTONM;
case Button3: return LTK_BUTTONR;
- case 4: return LTK_BUTTON4;
- case 5: return LTK_BUTTON5;
- case 6: return LTK_BUTTON6;
- case 7: return LTK_BUTTON7;
default: return LTK_BUTTONL; /* FIXME: what to do here? */
}
}
@@ -154,35 +174,138 @@ ltk_generate_keyboard_event(ltk_renderdata *renderdata, ltk_event *event) {
XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
}
-void
-ltk_next_event(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event) {
+#define DISTSQ(x0, y0, x1, y1) (((x1) - (x0)) * ((x1) - (x0)) + ((y1) - (y0)) * ((y1) - (y0)))
+/* return value 0 means valid event returned,
+ 1 means no events pending,
+ 2 means event discarded (need to call again) */
+static int
+next_event_base(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event) {
+ if (next_event_valid) {
+ next_event_valid = 0;
+ *event = (ltk_event){.button = next_event};
+ return 0;
+ }
XEvent xevent;
+ if (!XPending(renderdata->dpy))
+ return 1;
XNextEvent(renderdata->dpy, &xevent);
if (renderdata->xkb_supported && xevent.type == renderdata->xkb_event_type) {
ltk_generate_keyboard_event(renderdata, event);
- return;
+ return 0;
}
*event = (ltk_event){.type = LTK_UNKNOWN_EVENT};
if (XFilterEvent(&xevent, None))
- return;
+ return 2;
+ int button = 0;
switch (xevent.type) {
case ButtonPress:
- *event = (ltk_event){.button = {
- .type = LTK_BUTTONPRESS_EVENT,
- .button = get_button(xevent.xbutton.button),
- .x = xevent.xbutton.x,
- .y = xevent.xbutton.y
- }};
+ button = xevent.xbutton.button;
+ /* FIXME: are the buttons really always defined as exactly these values? */
+ if (button >= 1 && button <= 3) {
+ if (xevent.xbutton.time - last_button_press[button] <= DOUBLECLICK_TIME &&
+ DISTSQ(press_pos[button].x, press_pos[button].y, xevent.xbutton.x, xevent.xbutton.y) <= DOUBLECLICK_DISTSQ) {
+ if (was_2press[button]) {
+ /* reset so normal press is sent again next time */
+ was_2press[button] = 0;
+ last_button_press[button] = 0;
+ next_event = (ltk_button_event){
+ .type = LTK_3BUTTONPRESS_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ };
+ } else {
+ was_2press[button] = 1;
+ last_button_press[button] = xevent.xbutton.time;
+ next_event = (ltk_button_event){
+ .type = LTK_2BUTTONPRESS_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ };
+ }
+ next_event_valid = 1;
+ } else {
+ last_button_press[button] = xevent.xbutton.time;
+ }
+ *event = (ltk_event){.button = {
+ .type = LTK_BUTTONPRESS_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ }};
+ press_pos[button].x = xevent.xbutton.x;
+ press_pos[button].y = xevent.xbutton.y;
+ } else if (button >= 4 && button <= 7) {
+ /* FIXME: compress multiple scroll events into one */
+ *event = (ltk_event){.scroll = {
+ .type = LTK_SCROLL_EVENT,
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y,
+ .dx = 0,
+ .dy = 0
+ }};
+ switch (button) {
+ case 4:
+ event->scroll.dy = 1;
+ break;
+ case 5:
+ event->scroll.dy = -1;
+ break;
+ case 6:
+ event->scroll.dx = -1;
+ break;
+ case 7:
+ event->scroll.dx = 1;
+ break;
+ }
+ } else {
+ return 2;
+ }
break;
case ButtonRelease:
- *event = (ltk_event){.button = {
- .type = LTK_BUTTONRELEASE_EVENT,
- .button = get_button(xevent.xbutton.button),
- .x = xevent.xbutton.x,
- .y = xevent.xbutton.y
- }};
+ button = xevent.xbutton.button;
+ if (button >= 1 && button <= 3) {
+ if (xevent.xbutton.time - last_button_release[button] <= DOUBLECLICK_TIME &&
+ DISTSQ(release_pos[button].x, release_pos[button].y, xevent.xbutton.x, xevent.xbutton.y) <= DOUBLECLICK_DISTSQ) {
+ if (was_2release[button]) {
+ /* reset so normal release is sent again next time */
+ was_2release[button] = 0;
+ last_button_release[button] = 0;
+ next_event = (ltk_button_event){
+ .type = LTK_3BUTTONRELEASE_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ };
+ } else {
+ was_2release[button] = 1;
+ last_button_release[button] = xevent.xbutton.time;
+ next_event = (ltk_button_event){
+ .type = LTK_2BUTTONRELEASE_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ };
+ }
+ next_event_valid = 1;
+ } else {
+ last_button_release[button] = xevent.xbutton.time;
+ }
+ *event = (ltk_event){.button = {
+ .type = LTK_BUTTONRELEASE_EVENT,
+ .button = get_button(button),
+ .x = xevent.xbutton.x,
+ .y = xevent.xbutton.y
+ }};
+ release_pos[button].x = xevent.xbutton.x;
+ release_pos[button].y = xevent.xbutton.y;
+ } else {
+ return 2;
+ }
break;
case MotionNotify:
+ /* FIXME: compress motion events */
*event = (ltk_event){.motion = {
.type = LTK_MOTION_EVENT,
.x = xevent.xmotion.x,
@@ -216,13 +339,27 @@ ltk_next_event(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event)
.w = xevent.xexpose.width,
.h = xevent.xexpose.height
}};
+ } else {
+ return 2;
}
break;
case ClientMessage:
if ((Atom)xevent.xclient.data.l[0] == renderdata->wm_delete_msg)
*event = (ltk_event){.type = LTK_WINDOWCLOSE_EVENT};
+ else
+ return 2;
break;
default:
break;
}
+ return 0;
+}
+
+int
+ltk_next_event(ltk_renderdata *renderdata, size_t lang_index, ltk_event *event) {
+ int ret = 0;
+ while ((ret = next_event_base(renderdata, lang_index, event)) == 2) {
+ /* NOP */
+ }
+ return ret;
}
diff --git a/src/eventdefs.h b/src/eventdefs.h
@@ -1,10 +1,23 @@
#ifndef LTK_EVENTDEFS_H
#define LTK_EVENTDEFS_H
+/* FIXME: add to config */
+#define DOUBLECLICK_TIME 250
+/* square of distance to make calculation simpler */
+#define DOUBLECLICK_DISTSQ 25
+/* FIXME: reduce amount of scroll/motion events */
+
typedef enum {
LTK_UNKNOWN_EVENT, /* FIXME: a bit weird */
LTK_BUTTONPRESS_EVENT,
LTK_BUTTONRELEASE_EVENT,
+ /* double-click/release */
+ LTK_2BUTTONPRESS_EVENT,
+ LTK_2BUTTONRELEASE_EVENT,
+ /* triple-click/release */
+ LTK_3BUTTONPRESS_EVENT,
+ LTK_3BUTTONRELEASE_EVENT,
+ LTK_SCROLL_EVENT,
LTK_MOTION_EVENT,
LTK_KEYPRESS_EVENT,
LTK_KEYRELEASE_EVENT,
@@ -20,11 +33,6 @@ typedef enum {
LTK_BUTTONL,
LTK_BUTTONM,
LTK_BUTTONR,
- /* FIXME: dedicated scroll event */
- LTK_BUTTON4,
- LTK_BUTTON5,
- LTK_BUTTON6,
- LTK_BUTTON7
} ltk_button_type;
/* FIXME: just steal the definitions from X when using Xlib so no conversion is necessary? */
diff --git a/src/grid.c b/src/grid.c
@@ -72,6 +72,7 @@ static struct ltk_widget_vtable vtable = {
.child_size_change = <k_grid_child_size_change,
.remove_child = <k_grid_ungrid,
.mouse_press = NULL,
+ .mouse_scroll = NULL,
.mouse_release = NULL,
.motion_notify = NULL,
.get_child_at_pos = <k_grid_get_child_at_pos,
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -450,10 +450,8 @@ ltk_mainloop(ltk_window *window) {
/* value of tv doesn't really matter anymore here because the
necessary framerate-limiting delay is already done */
wretval = select(sock_state.maxfd + 1, NULL, &wfds, NULL, &tv);
- while (ltk_events_pending(window->renderdata)) {
- ltk_next_event(window->renderdata, window->cur_kbd, &event);
+ while (!ltk_next_event(window->renderdata, window->cur_kbd, &event))
ltk_handle_event(window, &event);
- }
if (rretval > 0 || (sock_write_available && wretval > 0)) {
if (FD_ISSET(sock_state.listenfd, &rfds)) {
@@ -1222,9 +1220,16 @@ ltk_handle_event(ltk_window *window, ltk_event *event) {
ltk_window_key_release_event(window, &event->key);
break;
case LTK_BUTTONPRESS_EVENT:
+ case LTK_2BUTTONPRESS_EVENT:
+ case LTK_3BUTTONPRESS_EVENT:
ltk_window_mouse_press_event(window, &event->button);
break;
+ case LTK_SCROLL_EVENT:
+ ltk_window_mouse_scroll_event(window, &event->scroll);
+ break;
case LTK_BUTTONRELEASE_EVENT:
+ case LTK_2BUTTONRELEASE_EVENT:
+ case LTK_3BUTTONRELEASE_EVENT:
ltk_window_mouse_release_event(window, &event->button);
break;
case LTK_MOTION_EVENT:
diff --git a/src/menu.c b/src/menu.c
@@ -16,6 +16,10 @@
/* NOTE: The implementation of menus and menu entries is a collection of ugly hacks. */
+/* FIXME: parent is pressed when scroll arrows pressed */
+/* -> this is because the pressed handling checks if the widget is activatable, then goes to the parent,
+ but the child isn't geometrically in the parent here, so that's weird */
+
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -94,7 +98,7 @@ static void ltk_menu_scroll_callback(void *data);
static void stop_scrolling(ltk_menu *menu);
static ltk_widget *ltk_menu_get_child_at_pos(ltk_widget *self, int x, int y);
static int set_scroll_timer(ltk_menu *menu, int x, int y);
-static int ltk_menu_mouse_press(ltk_widget *self, ltk_button_event *event);
+static int ltk_menu_mouse_scroll(ltk_widget *self, ltk_scroll_event *event);
static void ltk_menu_hide(ltk_widget *self);
static void popup_active_menu(ltk_menuentry *e);
static void unpopup_active_entry(ltk_menuentry *e);
@@ -136,7 +140,8 @@ static ltk_widget *ltk_menuentry_get_child(ltk_widget *self);
static struct ltk_widget_vtable vtable = {
.key_press = NULL,
.key_release = NULL,
- .mouse_press = <k_menu_mouse_press,
+ .mouse_press = NULL,
+ .mouse_scroll = <k_menu_mouse_scroll,
.motion_notify = <k_menu_motion_notify,
.mouse_release = NULL,
.mouse_enter = <k_menu_mouse_enter,
@@ -646,7 +651,8 @@ ltk_menu_get_child_at_pos(ltk_widget *self, int x, int y) {
/* FIXME: make sure timers are always destroyed when widget is destroyed */
static int
set_scroll_timer(ltk_menu *menu, int x, int y) {
- if (!ltk_collide_rect(menu->widget.lrect, x, y))
+ /* this check probably isn't necessary, but whatever */
+ if (x < 0 || y < 0 || x >= menu->widget.lrect.w || y >= menu->widget.lrect.h)
return 0;
int t = 0, b = 0, l = 0,r = 0;
struct theme *theme = menu->is_submenu ? &submenu_theme : &menu_theme;
@@ -694,30 +700,20 @@ ltk_menuentry_release(ltk_widget *self) {
}
static int
-ltk_menu_mouse_press(ltk_widget *self, ltk_button_event *event) {
+ltk_menu_mouse_scroll(ltk_widget *self, ltk_scroll_event *event) {
ltk_menu *menu = (ltk_menu *)self;
- /* FIXME: configure scroll step */
ltk_point glob = ltk_widget_pos_to_global(self, event->x, event->y);
- switch (event->button) {
- case LTK_BUTTON4:
- ltk_menu_scroll(menu, 1, 0, 0, 0, 10);
- ltk_window_fake_motion_event(self->window, glob.x, glob.y);
- break;
- case LTK_BUTTON5:
- ltk_menu_scroll(menu, 0, 1, 0, 0, 10);
- ltk_window_fake_motion_event(self->window, glob.x, glob.y);
- break;
- case LTK_BUTTON6:
- ltk_menu_scroll(menu, 0, 0, 1, 0, 10);
- ltk_window_fake_motion_event(self->window, glob.x, glob.y);
- break;
- case LTK_BUTTON7:
- ltk_menu_scroll(menu, 0, 0, 0, 1, 10);
- ltk_window_fake_motion_event(self->window, glob.x, glob.y);
- break;
- default:
- return 0;
- }
+ /* FIXME: configure scroll step */
+ /* FIXME: fix the interface for ltk_menu_scroll */
+ if (event->dx > 0)
+ ltk_menu_scroll(menu, 0, 0, 0, 1, event->dx * 10);
+ else if (event->dx < 0)
+ ltk_menu_scroll(menu, 0, 0, 1, 0, -event->dx * 10);
+ if (event->dy > 0)
+ ltk_menu_scroll(menu, 1, 0, 0, 0, event->dy * 10);
+ else if (event->dy < 0)
+ ltk_menu_scroll(menu, 0, 1, 0, 0, -event->dy * 10);
+ ltk_window_fake_motion_event(self->window, glob.x, glob.y);
return 1;
}
diff --git a/src/proto_types.h b/src/proto_types.h
@@ -24,23 +24,34 @@
/* P == protocol; W == widget */
-#define LTK_PEVENT_MOUSEPRESS 0
-#define LTK_PEVENT_MOUSERELEASE 1
-#define LTK_PEVENT_MOUSEMOTION 2
-#define LTK_PEVENT_KEYPRESS 3
-#define LTK_PEVENT_KEYRELEASE 4
-#define LTK_PEVENT_CONFIGURE 5
-#define LTK_PEVENT_STATECHANGE 6
-
-#define LTK_PEVENTMASK_NONE (UINT32_C(0))
-#define LTK_PEVENTMASK_MOUSEPRESS (UINT32_C(1) << LTK_PEVENT_MOUSEPRESS)
-#define LTK_PEVENTMASK_MOUSERELEASE (UINT32_C(1) << LTK_PEVENT_MOUSERELEASE)
-#define LTK_PEVENTMASK_MOUSEMOTION (UINT32_C(1) << LTK_PEVENT_MOUSEMOTION)
-#define LTK_PEVENTMASK_KEYPRESS (UINT32_C(1) << LTK_PEVENT_KEYPRESS)
-#define LTK_PEVENTMASK_KEYRELEASE (UINT32_C(1) << LTK_PEVENT_KEYRELEASE)
-#define LTK_PEVENTMASK_CONFIGURE (UINT32_C(1) << LTK_PEVENT_CONFIGURE)
-#define LTK_PEVENTMASK_EXPOSE (UINT32_C(1) << LTK_PEVENT_EXPOSE)
-#define LTK_PEVENTMASK_STATECHANGE (UINT32_C(1) << LTK_PEVENT_STATECHANGE)
+#define LTK_PEVENT_MOUSEPRESS 0
+#define LTK_PEVENT_2MOUSEPRESS 1
+#define LTK_PEVENT_3MOUSEPRESS 2
+#define LTK_PEVENT_MOUSERELEASE 3
+#define LTK_PEVENT_2MOUSERELEASE 4
+#define LTK_PEVENT_3MOUSERELEASE 5
+#define LTK_PEVENT_MOUSEMOTION 6
+#define LTK_PEVENT_MOUSESCROLL 7
+#define LTK_PEVENT_KEYPRESS 8
+#define LTK_PEVENT_KEYRELEASE 9
+#define LTK_PEVENT_CONFIGURE 10
+#define LTK_PEVENT_STATECHANGE 11
+
+/* FIXME: standardize names - internally, buttonpress is used, here it's mousepress... */
+#define LTK_PEVENTMASK_NONE (UINT32_C(0))
+#define LTK_PEVENTMASK_MOUSEPRESS (UINT32_C(1) << LTK_PEVENT_MOUSEPRESS)
+#define LTK_PEVENTMASK_2MOUSEPRESS (UINT32_C(1) << LTK_PEVENT_2MOUSEPRESS)
+#define LTK_PEVENTMASK_3MOUSEPRESS (UINT32_C(1) << LTK_PEVENT_3MOUSEPRESS)
+#define LTK_PEVENTMASK_MOUSERELEASE (UINT32_C(1) << LTK_PEVENT_MOUSERELEASE)
+#define LTK_PEVENTMASK_2MOUSERELEASE (UINT32_C(1) << LTK_PEVENT_2MOUSERELEASE)
+#define LTK_PEVENTMASK_3MOUSERELEASE (UINT32_C(1) << LTK_PEVENT_3MOUSERELEASE)
+#define LTK_PEVENTMASK_MOUSEMOTION (UINT32_C(1) << LTK_PEVENT_MOUSEMOTION)
+#define LTK_PEVENTMASK_KEYPRESS (UINT32_C(1) << LTK_PEVENT_KEYPRESS)
+#define LTK_PEVENTMASK_KEYRELEASE (UINT32_C(1) << LTK_PEVENT_KEYRELEASE)
+#define LTK_PEVENTMASK_CONFIGURE (UINT32_C(1) << LTK_PEVENT_CONFIGURE)
+#define LTK_PEVENTMASK_EXPOSE (UINT32_C(1) << LTK_PEVENT_EXPOSE)
+#define LTK_PEVENTMASK_STATECHANGE (UINT32_C(1) << LTK_PEVENT_STATECHANGE)
+#define LTK_PEVENTMASK_MOUSESCROLL (UINT32_C(1) << LTK_PEVENT_MOUSESCROLL)
#define LTK_PWEVENT_MENUENTRY_PRESS 0
#define LTK_PWEVENTMASK_MENUENTRY_NONE (UINT32_C(0))
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -165,7 +165,7 @@ static int
ltk_scrollbar_mouse_press(ltk_widget *self, ltk_button_event *event) {
ltk_scrollbar *sc = (ltk_scrollbar *)self;
int max_pos;
- if (event->button != LTK_BUTTONL)
+ if (event->button != LTK_BUTTONL || event->type != LTK_BUTTONPRESS_EVENT)
return 0;
int ex = event->x, ey = event->y;
ltk_rect handle_rect = handle_get_rect(sc);
diff --git a/src/widget.c b/src/widget.c
@@ -469,14 +469,47 @@ is_parent(ltk_widget *parent, ltk_widget *child) {
/* FIXME: fix global and local coordinates! */
static int
-queue_mouse_event(ltk_widget *widget, char *type, uint32_t mask, int x, int y) {
+queue_mouse_event(ltk_widget *widget, ltk_event_type type, int x, int y) {
+ uint32_t mask;
+ char *typename;
+ switch (type) {
+ case LTK_MOTION_EVENT:
+ mask = LTK_PEVENTMASK_MOUSEMOTION;
+ typename = "mousemotion";
+ break;
+ case LTK_2BUTTONPRESS_EVENT:
+ mask = LTK_PEVENTMASK_2MOUSEPRESS;
+ typename = "2mousepress";
+ break;
+ case LTK_3BUTTONPRESS_EVENT:
+ mask = LTK_PEVENTMASK_3MOUSEPRESS;
+ typename = "3mousepress";
+ break;
+ case LTK_BUTTONRELEASE_EVENT:
+ mask = LTK_PEVENTMASK_MOUSERELEASE;
+ typename = "mouserelease";
+ break;
+ case LTK_2BUTTONRELEASE_EVENT:
+ mask = LTK_PEVENTMASK_2MOUSERELEASE;
+ typename = "2mouserelease";
+ break;
+ case LTK_3BUTTONRELEASE_EVENT:
+ mask = LTK_PEVENTMASK_3MOUSERELEASE;
+ typename = "3mouserelease";
+ break;
+ case LTK_BUTTONPRESS_EVENT:
+ default:
+ mask = LTK_PEVENTMASK_MOUSEPRESS;
+ typename = "mousepress";
+ break;
+ }
int lock_client = -1;
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lmask & mask) {
ltk_queue_sock_write_fmt(
widget->event_masks[i].client,
"eventl %s widget %s %d %d %d %d\n",
- widget->id, type, x, y, x, y
+ widget->id, typename, x, y, x, y
/* x - widget->rect.x, y - widget->rect.y */
);
lock_client = widget->event_masks[i].client;
@@ -484,7 +517,37 @@ queue_mouse_event(ltk_widget *widget, char *type, uint32_t mask, int x, int y) {
ltk_queue_sock_write_fmt(
widget->event_masks[i].client,
"event %s widget %s %d %d %d %d\n",
- widget->id, type, x, y, x, y
+ widget->id, typename, x, y, x, y
+ /* x - widget->rect.x, y - widget->rect.y */
+ );
+ }
+ }
+ if (lock_client >= 0) {
+ if (ltk_handle_lock_client(widget->window, lock_client))
+ return 1;
+ }
+ return 0;
+}
+
+/* FIXME: global/local coords (like above) */
+static int
+queue_scroll_event(ltk_widget *widget, int x, int y, int dx, int dy) {
+ uint32_t mask = LTK_PEVENTMASK_MOUSESCROLL;
+ int lock_client = -1;
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].lmask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "eventl %s widget %s %d %d %d %d %d %d\n",
+ widget->id, "mousescroll", x, y, x, y, dx, dy
+ /* x - widget->rect.x, y - widget->rect.y */
+ );
+ lock_client = widget->event_masks[i].client;
+ } else if (widget->event_masks[i].mask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "event %s widget %s %d %d %d %d %d %d\n",
+ widget->id, "mousescroll", x, y, x, y, dx, dy
/* x - widget->rect.x, y - widget->rect.y */
);
}
@@ -1011,6 +1074,8 @@ ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
if (check_hide && !(window->popups_num > 0 && is_parent(cur_widget, window->popups[0])))
ltk_window_unregister_all_popups(window);
+ /* FIXME: popups don't always have their children geometrically contained within parents,
+ so this won't work properly in all cases */
int first = 1;
while (cur_widget) {
int handled = 0;
@@ -1020,13 +1085,13 @@ ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
if (cur_widget->state != LTK_DISABLED) {
/* FIXME: figure out whether this makes sense - currently, all widgets (unless disabled)
get mouse press, but they are only set to pressed if they are activatable */
- if (queue_mouse_event(cur_widget, "mousepress", LTK_PEVENTMASK_MOUSEPRESS, event->x, event->y))
+ if (queue_mouse_event(cur_widget, event->type, event->x, event->y))
handled = 1;
else if (cur_widget->vtable->mouse_press)
handled = cur_widget->vtable->mouse_press(cur_widget, event);
/* set first non-disabled widget to pressed widget */
/* FIXME: use config values for all_activatable */
- if (first && event->button == LTK_BUTTONL && (cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
+ if (first && event->button == LTK_BUTTONL && event->type == LTK_BUTTONPRESS_EVENT && (cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
ltk_window_set_pressed_widget(window, cur_widget, 0);
first = 0;
}
@@ -1039,6 +1104,35 @@ ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
}
void
+ltk_window_mouse_scroll_event(ltk_window *window, ltk_scroll_event *event) {
+ /* FIXME: should it first be sent to pressed widget? */
+ ltk_widget *widget = get_hover_popup(window, event->x, event->y);
+ if (!widget)
+ widget = window->root_widget;
+ if (!widget)
+ return;
+ int orig_x = event->x, orig_y = event->y;
+ ltk_widget *cur_widget = get_widget_under_pointer(widget, event->x, event->y, &event->x, &event->y);
+ /* FIXME: same issue with popups like in mouse_press above */
+ while (cur_widget) {
+ int handled = 0;
+ ltk_point local = ltk_global_to_widget_pos(cur_widget, orig_x, orig_y);
+ event->x = local.x;
+ event->y = local.y;
+ if (cur_widget->state != LTK_DISABLED) {
+ if (queue_scroll_event(cur_widget, event->x, event->y, event->dx, event->dy))
+ handled = 1;
+ else if (cur_widget->vtable->mouse_scroll)
+ handled = cur_widget->vtable->mouse_scroll(cur_widget, event);
+ }
+ if (!handled)
+ cur_widget = cur_widget->parent;
+ else
+ break;
+ }
+}
+
+void
ltk_window_fake_motion_event(ltk_window *window, int x, int y) {
ltk_motion_event e = {.type = LTK_MOTION_EVENT, .x = x, .y = y};
ltk_window_motion_notify_event(window, &e);
@@ -1048,17 +1142,18 @@ void
ltk_window_mouse_release_event(ltk_window *window, ltk_button_event *event) {
ltk_widget *widget = window->pressed_widget;
int orig_x = event->x, orig_y = event->y;
+ /* FIXME: why does this only take pressed widget and popups into account? */
if (!widget) {
widget = get_hover_popup(window, event->x, event->y);
widget = get_widget_under_pointer(widget, event->x, event->y, &event->x, &event->y);
}
/* FIXME: loop up to top of hierarchy if not handled */
- if (widget && queue_mouse_event(widget, "mouserelease", LTK_PEVENTMASK_MOUSERELEASE, event->x, event->y)) {
+ if (widget && queue_mouse_event(widget, event->type, event->x, event->y)) {
/* NOP */
} else if (widget && widget->vtable->mouse_release) {
widget->vtable->mouse_release(widget, event);
}
- if (event->button == LTK_BUTTONL) {
+ if (event->button == LTK_BUTTONL && event->type == LTK_BUTTONRELEASE_EVENT) {
int release = 0;
if (window->pressed_widget) {
ltk_rect prect = window->pressed_widget->lrect;
@@ -1104,7 +1199,7 @@ ltk_window_motion_notify_event(ltk_window *window, ltk_motion_event *event) {
event->x = local.x;
event->y = local.y;
if (cur_widget->state != LTK_DISABLED) {
- if (queue_mouse_event(cur_widget, "mousemotion", LTK_PEVENTMASK_MOUSEMOTION, event->x, event->y))
+ if (queue_mouse_event(cur_widget, LTK_MOTION_EVENT, event->x, event->y))
handled = 1;
else if (cur_widget->vtable->motion_notify)
handled = cur_widget->vtable->motion_notify(cur_widget, event);
diff --git a/src/widget.h b/src/widget.h
@@ -119,8 +119,10 @@ struct ltk_widget {
struct ltk_widget_vtable {
int (*key_press)(struct ltk_widget *, ltk_key_event *);
int (*key_release)(struct ltk_widget *, ltk_key_event *);
+ /* press/release also receive double/triple-click/release */
int (*mouse_press)(struct ltk_widget *, ltk_button_event *);
int (*mouse_release)(struct ltk_widget *, ltk_button_event *);
+ int (*mouse_scroll)(struct ltk_widget *, ltk_scroll_event *);
int (*motion_notify)(struct ltk_widget *, ltk_motion_event *);
int (*mouse_leave)(struct ltk_widget *, ltk_motion_event *);
int (*mouse_enter)(struct ltk_widget *, ltk_motion_event *);
@@ -168,6 +170,7 @@ void ltk_widget_change_state(ltk_widget *widget, ltk_widget_state old_state);
void ltk_window_key_press_event(ltk_window *window, ltk_key_event *event);
void ltk_window_key_release_event(ltk_window *window, ltk_key_event *event);
void ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event);
+void ltk_window_mouse_scroll_event(ltk_window *window, ltk_scroll_event *event);
void ltk_window_mouse_release_event(ltk_window *window, ltk_button_event *event);
void ltk_window_motion_notify_event(ltk_window *window, ltk_motion_event *event);
void ltk_window_fake_motion_event(ltk_window *window, int x, int y);