commit bf6d516dc0a01db18fb93ddb4f153a454b36700b
parent 994e235d9939788238dd2c66cdefb4cc4ade1bce
Author: lumidify <nobody@lumidify.org>
Date: Thu, 16 Jun 2022 10:47:44 +0200
Clean up mouse event handling a bit; bring back hover state
Of course, everything is still hopelessly broken.
Diffstat:
M | Makefile | | | 11 | ++++++----- |
M | src/box.c | | | 71 | +++++++++++++++++++---------------------------------------------------- |
M | src/button.c | | | 19 | ++++++++++++++----- |
M | src/grid.c | | | 59 | ++++++++++------------------------------------------------- |
M | src/label.c | | | 1 | + |
M | src/ltk.h | | | 2 | ++ |
M | src/ltkd.c | | | 73 | ++++++++++++++++++++++++++++++++++++------------------------------------- |
M | src/menu.c | | | 52 | +++++++++++++++++++++++++++++++++------------------- |
M | src/scrollbar.c | | | 32 | ++++++++++++++++---------------- |
M | src/widget.c | | | 128 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
M | src/widget.h | | | 20 | ++++++++++++-------- |
11 files changed, 241 insertions(+), 227 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,14 +7,15 @@ VERSION = -999-prealpha0
# Note: The stb backend should not be used with untrusted font files.
# FIXME: Using DEBUG here doesn't work because it somehow
# interferes with a predefined macro, at least on OpenBSD.
-DEV = 0
+DEV = 1
+SANITIZE = 0
USE_PANGO = 0
# Note: this macro magic for debugging and pango rendering seems ugly; it should probably be changed
# debug
-DEV_CFLAGS_1 = -fsanitize=address -g -Wall -Wextra -pedantic
-DEV_LDFLAGS_1 = -fsanitize=address
+DEV_CFLAGS_1 = -g -Wall -Wextra -pedantic
+SANITIZE_FLAGS_1 = -fsanitize=address
# don't include default flags when debugging so possible
# optimization flags don't interfere with it
DEV_CFLAGS_0 = $(CFLAGS)
@@ -29,8 +30,8 @@ EXTRA_CFLAGS_1 = `pkg-config --cflags pangoxft`
EXTRA_LDFLAGS_1 = `pkg-config --libs pangoxft`
EXTRA_OBJ = $(EXTRA_OBJ_$(USE_PANGO))
-EXTRA_CFLAGS = $(DEV_CFLAGS_$(DEV)) $(EXTRA_CFLAGS_$(USE_PANGO))
-EXTRA_LDFLAGS = $(DEV_LDFLAGS_$(DEV)) $(EXTRA_LDFLAGS_$(USE_PANGO))
+EXTRA_CFLAGS = $(SANITIZE_FLAGS_$(SANITIZE)) $(DEV_CFLAGS_$(DEV)) $(EXTRA_CFLAGS_$(USE_PANGO))
+EXTRA_LDFLAGS = $(SANITIZE_FLAGS_$(SANITIZE)) $(DEV_LDFLAGS_$(DEV)) $(EXTRA_LDFLAGS_$(USE_PANGO))
LTK_CFLAGS = $(EXTRA_CFLAGS) -DUSE_PANGO=$(USE_PANGO) -DDEV=$(DEV) -std=c99 `pkg-config --cflags x11 fontconfig xext` -D_POSIX_C_SOURCE=200809L
LTK_LDFLAGS = $(EXTRA_LDFLAGS) -lm `pkg-config --libs x11 fontconfig xext`
diff --git a/src/box.c b/src/box.c
@@ -40,10 +40,8 @@ static int ltk_box_add(ltk_window *window, ltk_widget *widget, ltk_box *box, uns
static int ltk_box_remove(ltk_window *window, ltk_widget *widget, ltk_widget *self, char **errstr);
/* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow, char **errstr); */
static void ltk_box_scroll(ltk_widget *self);
-static int ltk_box_mouse_event(ltk_box *box, int x, int y, ltk_event *event, void (*handler)(ltk_widget *, ltk_event *));
-static int ltk_box_mouse_press(ltk_widget *self, ltk_event *event);
-static int ltk_box_mouse_release(ltk_widget *self, ltk_event *event);
-static int ltk_box_motion_notify(ltk_widget *self, ltk_event *event);
+static int ltk_box_mouse_press(ltk_widget *self, ltk_button_event *event);
+static ltk_widget *ltk_box_get_child_at_pos(ltk_widget *self, int x, int y);
static struct ltk_widget_vtable vtable = {
.change_state = NULL,
@@ -56,8 +54,9 @@ static struct ltk_widget_vtable vtable = {
.key_press = NULL,
.key_release = NULL,
.mouse_press = <k_box_mouse_press,
- .mouse_release = <k_box_mouse_release,
- .motion_notify = <k_box_motion_notify,
+ .mouse_release = NULL,
+ .motion_notify = NULL,
+ .get_child_at_pos = <k_box_get_child_at_pos,
.mouse_leave = NULL,
.mouse_enter = NULL,
.needs_redraw = 0,
@@ -321,64 +320,32 @@ ltk_box_scroll(ltk_widget *self) {
ltk_window_invalidate_rect(box->widget.window, box->widget.rect);
}
-static int
-ltk_box_mouse_event(ltk_box *box, int x, int y, ltk_event *event, void (*handler)(ltk_widget *, ltk_event *)) {
- ltk_widget *widget;
-
- if (ltk_collide_rect(box->sc->widget.rect, x, y)) {
- handler((ltk_widget *)box->sc, event);
- return 0;
- }
-
- /* FIXME: check only the currently visible items */
+static ltk_widget *
+ltk_box_get_child_at_pos(ltk_widget *self, int x, int y) {
+ ltk_box *box = (ltk_box *)self;
+ if (ltk_collide_rect(box->sc->widget.rect, x, y))
+ return (ltk_widget *)box->sc;
for (size_t i = 0; i < box->num_widgets; i++) {
- widget = box->widgets[i];
- if (ltk_collide_rect(widget->rect, x, y)) {
- handler(widget, event);
- return 0;
- }
+ if (ltk_collide_rect(box->widgets[i]->rect, x, y))
+ return box->widgets[i];
}
- return 0;
+ return NULL;
}
static int
-ltk_box_mouse_press(ltk_widget *self, ltk_event *event) {
+ltk_box_mouse_press(ltk_widget *self, ltk_button_event *event) {
ltk_box *box = (ltk_box *)self;
/* FIXME: combine multiple events into one for efficiency */
- /* FIXME: fix this whole state handling */
- if (event->button.button == LTK_BUTTON4 || event->button.button == LTK_BUTTON5) {
- 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->button.x, event->button.y)) {
- if (widget->vtable->mouse_press)
- default_handler = widget->vtable->mouse_press(widget, event);
- }
- }
+ if (event->button == LTK_BUTTON4 || event->button == LTK_BUTTON5) {
/* FIXME: configure scrollstep */
- if (default_handler) {
- int delta = event->button.button == LTK_BUTTON4 ? -15 : 15;
- ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0);
- }
- return 0;
+ int delta = event->button == LTK_BUTTON4 ? -15 : 15;
+ ltk_scrollbar_scroll((ltk_widget *)box->sc, delta, 0);
+ return 1;
} else {
- return ltk_box_mouse_event(box, event->button.x, event->button.y, event, <k_widget_mouse_press_event);
+ return 0;
}
}
-static int
-ltk_box_mouse_release(ltk_widget *self, ltk_event *event) {
- ltk_box *box = (ltk_box *)self;
- return ltk_box_mouse_event(box, event->button.x, event->button.y, event, <k_widget_mouse_release_event);
-}
-
-static int
-ltk_box_motion_notify(ltk_widget *self, ltk_event *event) {
- ltk_box *box = (ltk_box *)self;
- return ltk_box_mouse_event(box, event->motion.x, event->motion.y, event, <k_widget_motion_notify_event);
-}
-
/* box <box id> add <widget id> [sticky] */
static int
ltk_box_cmd_add(
diff --git a/src/button.c b/src/button.c
@@ -37,7 +37,7 @@
#define MAX_BUTTON_PADDING 500
static void ltk_button_draw(ltk_widget *self, ltk_rect clip);
-static int ltk_button_mouse_release(ltk_widget *self, ltk_event *event);
+static int ltk_button_mouse_release(ltk_widget *self, ltk_button_event *event);
static ltk_button *ltk_button_create(ltk_window *window,
const char *id, char *text);
static void ltk_button_destroy(ltk_widget *self, int shallow);
@@ -53,6 +53,7 @@ static struct ltk_widget_vtable vtable = {
.mouse_leave = NULL,
.mouse_enter = NULL,
.change_state = <k_button_change_state,
+ .get_child_at_pos = NULL,
.resize = NULL,
.hide = NULL,
.draw = <k_button_draw,
@@ -75,6 +76,9 @@ static struct {
ltk_color border_pressed;
ltk_color fill_pressed;
+ ltk_color border_hover;
+ ltk_color fill_hover;
+
ltk_color border_active;
ltk_color fill_active;
@@ -84,12 +88,14 @@ static struct {
static ltk_theme_parseinfo parseinfo[] = {
{"border", THEME_COLOR, {.color = &theme.border}, {.color = "#339999"}, 0, 0, 0},
+ {"border-hover", THEME_COLOR, {.color = &theme.border_hover}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-active", THEME_COLOR, {.color = &theme.border_active}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-disabled", THEME_COLOR, {.color = &theme.border_disabled}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-pressed", THEME_COLOR, {.color = &theme.border_pressed}, {.color = "#FFFFFF"}, 0, 0, 0},
{"border-width", THEME_INT, {.i = &theme.border_width}, {.i = 2}, 0, MAX_BUTTON_BORDER_WIDTH, 0},
{"fill", THEME_COLOR, {.color = &theme.fill}, {.color = "#113355"}, 0, 0, 0},
- {"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#738194"}, 0, 0, 0},
+ {"fill-hover", THEME_COLOR, {.color = &theme.fill_hover}, {.color = "#738194"}, 0, 0, 0},
+ {"fill-active", THEME_COLOR, {.color = &theme.fill_active}, {.color = "#113355"}, 0, 0, 0},
{"fill-disabled", THEME_COLOR, {.color = &theme.fill_disabled}, {.color = "#292929"}, 0, 0, 0},
{"fill-pressed", THEME_COLOR, {.color = &theme.fill_pressed}, {.color = "#113355"}, 0, 0, 0},
{"pad", THEME_INT, {.i = &theme.pad}, {.i = 5}, 0, MAX_BUTTON_PADDING, 0},
@@ -133,6 +139,10 @@ ltk_button_redraw_surface(ltk_button *button, ltk_surface *s) {
border = &theme.border;
fill = &theme.fill;
break;
+ case LTK_HOVER:
+ border = &theme.border_hover;
+ fill = &theme.fill_hover;
+ break;
case LTK_PRESSED:
border = &theme.border_pressed;
fill = &theme.fill_pressed;
@@ -167,11 +177,10 @@ 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, ltk_event *event) {
+ltk_button_mouse_release(ltk_widget *self, ltk_button_event *event) {
ltk_button *button = (ltk_button *)self;
- if (event->button.button == LTK_BUTTONL) {
+ if (self->state == LTK_PRESSED && event->button == LTK_BUTTONL) {
ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
return 1;
}
diff --git a/src/grid.c b/src/grid.c
@@ -50,9 +50,7 @@ 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_widget *self, 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 int ltk_grid_mouse_press(ltk_widget *self, ltk_event *event);
-static int ltk_grid_mouse_release(ltk_widget *self, ltk_event *event);
-static int ltk_grid_motion_notify(ltk_widget *self, ltk_event *event);
+static ltk_widget *ltk_grid_get_child_at_pos(ltk_widget *self, int x, int y);
static struct ltk_widget_vtable vtable = {
.draw = <k_grid_draw,
@@ -62,9 +60,10 @@ static struct ltk_widget_vtable vtable = {
.change_state = NULL,
.child_size_change = <k_grid_child_size_change,
.remove_child = <k_grid_ungrid,
- .mouse_press = <k_grid_mouse_press,
- .mouse_release = <k_grid_mouse_release,
- .motion_notify = <k_grid_motion_notify,
+ .mouse_press = NULL,
+ .mouse_release = NULL,
+ .motion_notify = NULL,
+ .get_child_at_pos = <k_grid_get_child_at_pos,
.mouse_leave = NULL,
.mouse_enter = NULL,
.key_press = NULL,
@@ -383,55 +382,17 @@ ltk_grid_find_nearest_row(ltk_grid *grid, int y) {
return -1;
}
-static int
-ltk_grid_mouse_press(ltk_widget *self, ltk_event *event) {
- ltk_grid *grid = (ltk_grid *)self;
- int x = event->button.x;
- int y = event->button.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 0;
- ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
- if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
- ltk_widget_mouse_press_event(ptr, event);
- return 0;
- }
- return 0;
-}
-
-static int
-ltk_grid_mouse_release(ltk_widget *self, ltk_event *event) {
- ltk_grid *grid = (ltk_grid *)self;
- int x = event->button.x;
- int y = event->button.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 0;
- ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
- if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
- ltk_widget_mouse_release_event(ptr, event);
- return 0;
- }
- return 0;
-}
-
-static int
-ltk_grid_motion_notify(ltk_widget *self, ltk_event *event) {
+static ltk_widget *
+ltk_grid_get_child_at_pos(ltk_widget *self, int x, int y) {
ltk_grid *grid = (ltk_grid *)self;
- int x = event->motion.x;
- int y = event->motion.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 0;
ltk_widget *ptr = grid->widget_grid[row * grid->columns + column];
- if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
- ltk_widget_motion_notify_event(ptr, event);
- return 0;
- }
- return 0;
+ if (ptr && ltk_collide_rect(ptr->rect, x, y))
+ return ptr;
+ return NULL;
}
/* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> [sticky] */
diff --git a/src/label.c b/src/label.c
@@ -49,6 +49,7 @@ static struct ltk_widget_vtable vtable = {
.change_state = NULL,
.child_size_change = NULL,
.remove_child = NULL,
+ .get_child_at_pos = NULL,
.key_press = NULL,
.key_release = NULL,
.mouse_press = NULL,
diff --git a/src/ltk.h b/src/ltk.h
@@ -59,6 +59,7 @@ struct ltk_window {
ltk_text_context *text_context;
ltk_surface *surface;
ltk_widget *root_widget;
+ ltk_widget *hover_widget;
ltk_widget *active_widget;
ltk_widget *pressed_widget;
void (*other_event) (struct ltk_window *, ltk_event *event);
@@ -89,6 +90,7 @@ struct ltk_window_theme {
void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
void ltk_queue_event(ltk_window *window, ltk_userevent_type type, const char *id, const char *data);
+void ltk_window_set_hover_widget(ltk_window *window, ltk_widget *widget, ltk_motion_event *event);
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_quit(ltk_window *window);
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -674,10 +674,10 @@ void
ltk_window_unregister_all_popups(ltk_window *window) {
window->popups_locked = 1;
for (size_t i = 0; i < window->popups_num; i++) {
+ window->popups[i]->hidden = 1;
if (window->popups[i]->vtable->hide) {
window->popups[i]->vtable->hide(window->popups[i]);
}
- window->popups[i]->hidden = 1;
}
window->popups_num = 0;
/* somewhat arbitrary, but should be enough for most cases */
@@ -784,6 +784,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int
renderer_set_window_properties(window->renderdata, &window->theme->bg, &window->theme->fg, window->theme->border_width);
window->root_widget = NULL;
+ window->hover_widget = NULL;
window->active_widget = NULL;
window->pressed_widget = NULL;
@@ -818,6 +819,31 @@ ltk_destroy_window(ltk_window *window) {
ltk_free(window);
}
+/* FIXME: some widgets should not be allowed to be active or pressed (e.g. containers) */
+void
+ltk_window_set_hover_widget(ltk_window *window, ltk_widget *widget, ltk_motion_event *event) {
+ ltk_widget *old = window->hover_widget;
+ if (old == widget)
+ return;
+ if (old) {
+ /* set widget to active again if it is actually active */
+ if (old == window->active_widget)
+ old->state = LTK_ACTIVE;
+ else
+ old->state = LTK_NORMAL;
+ ltk_widget_change_state(old);
+ if (old->vtable->mouse_leave)
+ old->vtable->mouse_leave(old, event);
+ }
+ window->hover_widget = widget;
+ if (widget) {
+ widget->state = LTK_HOVER;
+ ltk_widget_change_state(widget);
+ if (widget->vtable->mouse_enter)
+ widget->vtable->mouse_enter(widget, event);
+ }
+}
+
void
ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
if (window->active_widget == widget)
@@ -833,19 +859,17 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *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);
+ if (window->hover_widget && window->hover_widget != widget) {
+ window->hover_widget->state = LTK_NORMAL;
+ ltk_widget_change_state(window->hover_widget);
+ window->hover_widget = NULL;
}
+ if (window->pressed_widget)
+ ltk_window_set_active_widget(window, window->pressed_widget);
window->pressed_widget = widget;
if (widget) {
widget->state = LTK_PRESSED;
@@ -853,46 +877,21 @@ ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget) {
}
}
-static ltk_widget *
-get_hover_popup(ltk_window *window, int x, int y) {
- for (size_t i = window->popups_num; i-- > 0;) {
- if (ltk_collide_rect(window->popups[i]->rect, x, y))
- return window->popups[i];
- }
- return NULL;
-}
-
static void
ltk_handle_event(ltk_window *window, ltk_event *event) {
- ltk_widget *hover_popup;
- ltk_widget *root_widget = window->root_widget;
switch (event->type) {
case LTK_KEYPRESS_EVENT:
break;
case LTK_KEYRELEASE_EVENT:
break;
case LTK_BUTTONPRESS_EVENT:
- hover_popup = get_hover_popup(window, event->button.x, event->button.y);
- if (hover_popup) {
- ltk_widget_mouse_press_event(hover_popup, event);
- } else if (root_widget) {
- ltk_window_unregister_all_popups(window);
- ltk_widget_mouse_press_event(root_widget, event);
- }
+ ltk_window_mouse_press_event(window, &event->button);
break;
case LTK_BUTTONRELEASE_EVENT:
- hover_popup = get_hover_popup(window, event->button.x, event->button.y);
- if (hover_popup)
- ltk_widget_mouse_release_event(hover_popup, event);
- else if (root_widget)
- ltk_widget_mouse_release_event(root_widget, event);
+ ltk_window_mouse_release_event(window, &event->button);
break;
case LTK_MOTION_EVENT:
- hover_popup = get_hover_popup(window, event->motion.x, event->motion.y);
- if (hover_popup)
- ltk_widget_motion_notify_event(hover_popup, event);
- else if (root_widget)
- ltk_widget_motion_notify_event(root_widget, event);
+ ltk_window_motion_notify_event(window, &event->motion);
break;
default:
if (window->other_event)
diff --git a/src/menu.c b/src/menu.c
@@ -86,15 +86,15 @@ static void ltk_menu_scroll_callback(void *data);
static void stop_scrolling(ltk_menu *menu);
static size_t get_entry_at_point(ltk_menu *menu, int x, int y, ltk_rect *entry_rect_ret);
static int set_scroll_timer(ltk_menu *menu, int x, int y);
-static int ltk_menu_mouse_release(ltk_widget *self, ltk_event *event);
-static int ltk_menu_mouse_press(ltk_widget *self, ltk_event *event);
+static int ltk_menu_mouse_release(ltk_widget *self, ltk_button_event *event);
+static int ltk_menu_mouse_press(ltk_widget *self, ltk_button_event *event);
static void ltk_menu_hide(ltk_widget *self);
static void popup_active_menu(ltk_menu *menu, ltk_rect r);
static void unpopup_active_entry(ltk_menu *menu);
static void handle_hover(ltk_menu *menu, int x, int y);
-static int ltk_menu_motion_notify(ltk_widget *self, ltk_event *event);
-static int ltk_menu_mouse_enter(ltk_widget *self, ltk_event *event);
-static int ltk_menu_mouse_leave(ltk_widget *self, ltk_event *event);
+static int ltk_menu_motion_notify(ltk_widget *self, ltk_motion_event *event);
+static int ltk_menu_mouse_enter(ltk_widget *self, ltk_motion_event *event);
+static int ltk_menu_mouse_leave(ltk_widget *self, ltk_motion_event *event);
static ltk_menu *ltk_menu_create(ltk_window *window, const char *id, int is_submenu);
static ltk_menuentry *insert_entry(ltk_menu *menu, size_t idx);
static void recalc_menu_size(ltk_menu *menu);
@@ -126,6 +126,7 @@ static struct ltk_widget_vtable vtable = {
.mouse_release = <k_menu_mouse_release,
.mouse_enter = <k_menu_mouse_enter,
.mouse_leave = <k_menu_mouse_leave,
+ .get_child_at_pos = NULL,
.resize = <k_menu_resize,
.change_state = <k_menu_change_state,
.hide = <k_menu_hide,
@@ -250,6 +251,14 @@ ltk_menu_change_state(ltk_widget *self) {
self->dirty = 1;
ltk_window_invalidate_rect(self->window, self->rect);
}
+ if (self->state == LTK_NORMAL && menu->active_entry < menu->num_entries) {
+ ltk_menuentry *e = &menu->entries[menu->active_entry];
+ if (!e->submenu || e->submenu->widget.hidden) {
+ menu->active_entry = SIZE_MAX;
+ self->dirty = 1;
+ ltk_window_invalidate_rect(self->window, self->rect);
+ }
+ }
}
static void
@@ -602,9 +611,9 @@ set_scroll_timer(ltk_menu *menu, int x, int y) {
}
static int
-ltk_menu_mouse_release(ltk_widget *self, ltk_event *event) {
+ltk_menu_mouse_release(ltk_widget *self, ltk_button_event *event) {
ltk_menu *menu = (ltk_menu *)self;
- size_t idx = get_entry_at_point(menu, event->button.x, event->button.y, NULL);
+ size_t idx = get_entry_at_point(menu, event->x, event->y, NULL);
if (idx < menu->num_entries && idx == menu->pressed_entry) {
ltk_window_unregister_all_popups(self->window);
/* FIXME: give menu id and entry id */
@@ -620,13 +629,13 @@ ltk_menu_mouse_release(ltk_widget *self, ltk_event *event) {
}
static int
-ltk_menu_mouse_press(ltk_widget *self, ltk_event *event) {
+ltk_menu_mouse_press(ltk_widget *self, ltk_button_event *event) {
ltk_menu *menu = (ltk_menu *)self;
size_t idx;
/* FIXME: configure scroll step */
- switch (event->button.button) {
+ switch (event->button) {
case LTK_BUTTONL:
- idx = get_entry_at_point(menu, event->button.x, event->button.y, NULL);
+ idx = get_entry_at_point(menu, event->x, event->y, NULL);
if (idx < menu->num_entries) {
menu->pressed_entry = idx;
self->dirty = 1;
@@ -634,19 +643,19 @@ ltk_menu_mouse_press(ltk_widget *self, ltk_event *event) {
break;
case LTK_BUTTON4:
ltk_menu_scroll(menu, 1, 0, 0, 0, 10);
- handle_hover(menu, event->button.x, event->button.y);
+ handle_hover(menu, event->x, event->y);
break;
case LTK_BUTTON5:
ltk_menu_scroll(menu, 0, 1, 0, 0, 10);
- handle_hover(menu, event->button.x, event->button.y);
+ handle_hover(menu, event->x, event->y);
break;
case LTK_BUTTON6:
ltk_menu_scroll(menu, 0, 0, 1, 0, 10);
- handle_hover(menu, event->button.x, event->button.y);
+ handle_hover(menu, event->x, event->y);
break;
case LTK_BUTTON7:
ltk_menu_scroll(menu, 0, 0, 0, 1, 10);
- handle_hover(menu, event->button.x, event->button.y);
+ handle_hover(menu, event->x, event->y);
break;
default:
break;
@@ -664,6 +673,11 @@ ltk_menu_hide(ltk_widget *self) {
menu->scroll_left_hover = menu->scroll_right_hover = 0;
ltk_window_unregister_popup(self->window, self);
ltk_window_invalidate_rect(self->window, self->rect);
+ /* when hiding, also update parent so it doesn't show the
+ entry as selected anymore */
+ if (self->parent && self->parent->vtable->type == LTK_MENU) {
+ ltk_menu_change_state(self->parent);
+ }
}
/* FIXME: don't require passing rect */
@@ -805,19 +819,19 @@ handle_hover(ltk_menu *menu, int x, int y) {
}
static int
-ltk_menu_motion_notify(ltk_widget *self, ltk_event *event) {
- handle_hover((ltk_menu *)self, event->motion.x, event->motion.y);
+ltk_menu_motion_notify(ltk_widget *self, ltk_motion_event *event) {
+ handle_hover((ltk_menu *)self, event->x, event->y);
return 1;
}
static int
-ltk_menu_mouse_enter(ltk_widget *self, ltk_event *event) {
- handle_hover((ltk_menu *)self, event->motion.x, event->motion.y);
+ltk_menu_mouse_enter(ltk_widget *self, ltk_motion_event *event) {
+ handle_hover((ltk_menu *)self, event->x, event->y);
return 1;
}
static int
-ltk_menu_mouse_leave(ltk_widget *self, ltk_event *event) {
+ltk_menu_mouse_leave(ltk_widget *self, ltk_motion_event *event) {
(void)event;
stop_scrolling((ltk_menu *)self);
return 1;
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -21,9 +21,6 @@
#include <string.h>
#include <stdarg.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
#include "event.h"
#include "memory.h"
#include "color.h"
@@ -37,8 +34,8 @@
#define MAX_SCROLLBAR_WIDTH 100 /* completely arbitrary */
static void ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip);
-static int ltk_scrollbar_mouse_press(ltk_widget *self, ltk_event *event);
-static int ltk_scrollbar_motion_notify(ltk_widget *self, ltk_event *event);
+static int ltk_scrollbar_mouse_press(ltk_widget *self, ltk_button_event *event);
+static int ltk_scrollbar_motion_notify(ltk_widget *self, ltk_motion_event *event);
static void ltk_scrollbar_destroy(ltk_widget *self, int shallow);
static struct ltk_widget_vtable vtable = {
@@ -50,6 +47,7 @@ static struct ltk_widget_vtable vtable = {
.mouse_press = <k_scrollbar_mouse_press,
.mouse_release = NULL,
.motion_notify = <k_scrollbar_motion_notify,
+ .get_child_at_pos = NULL,
.mouse_leave = NULL,
.mouse_enter = NULL,
.child_size_change = NULL,
@@ -135,6 +133,7 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
ltk_color *bg = NULL, *fg = NULL;
ltk_rect rect = scrollbar->widget.rect;
switch (scrollbar->widget.state) {
+ /* FIXME: proper theme for hover */
case LTK_NORMAL:
bg = &theme.bg_normal;
fg = &theme.fg_normal;
@@ -143,6 +142,7 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
bg = &theme.bg_normal;
fg = &theme.fg_pressed;
break;
+ case LTK_HOVER:
case LTK_ACTIVE:
bg = &theme.bg_normal;
fg = &theme.fg_active;
@@ -165,12 +165,12 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
}
static int
-ltk_scrollbar_mouse_press(ltk_widget *self, ltk_event *event) {
+ltk_scrollbar_mouse_press(ltk_widget *self, ltk_button_event *event) {
ltk_scrollbar *sc = (ltk_scrollbar *)self;
int max_pos;
- if (event->button.button != LTK_BUTTONL)
+ if (event->button != LTK_BUTTONL)
return 0;
- int ex = event->button.x, ey = event->button.y;
+ int ex = event->x, ey = event->y;
ltk_rect handle_rect = get_handle_rect(sc);
if (sc->orient == LTK_HORIZONTAL) {
if (ex < handle_rect.x || ex > handle_rect.x + handle_rect.w) {
@@ -188,8 +188,8 @@ ltk_scrollbar_mouse_press(ltk_widget *self, ltk_event *event) {
else if (sc->cur_pos > max_pos)
sc->cur_pos = max_pos;
sc->callback(sc->callback_data);
- sc->last_mouse_x = event->button.x;
- sc->last_mouse_y = event->button.y;
+ sc->last_mouse_x = event->x;
+ sc->last_mouse_y = event->y;
return 1;
}
@@ -219,20 +219,20 @@ ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled) {
}
static int
-ltk_scrollbar_motion_notify(ltk_widget *self, ltk_event *event) {
+ltk_scrollbar_motion_notify(ltk_widget *self, ltk_motion_event *event) {
ltk_scrollbar *sc = (ltk_scrollbar *)self;
int delta;
if (self->state != LTK_PRESSED) {
return 1;
}
if (sc->orient == LTK_HORIZONTAL)
- delta = event->button.x - sc->last_mouse_x;
+ delta = event->x - sc->last_mouse_x;
else
- delta = event->button.y - sc->last_mouse_y;
+ delta = event->y - sc->last_mouse_y;
ltk_scrollbar_scroll(self, delta, 1);
- sc->last_mouse_x = event->button.x;
- sc->last_mouse_y = event->button.y;
- return 0;
+ sc->last_mouse_x = event->x;
+ sc->last_mouse_y = event->y;
+ return 1;
}
ltk_scrollbar *
diff --git a/src/widget.c b/src/widget.c
@@ -126,55 +126,111 @@ ltk_widget_change_state(ltk_widget *widget) {
ltk_window_invalidate_rect(widget->window, widget->rect);
}
+static ltk_widget *
+get_widget_under_pointer(ltk_widget *widget, int x, int y) {
+ ltk_widget *next = NULL;
+ while (widget && widget->vtable->get_child_at_pos) {
+ next = widget->vtable->get_child_at_pos(widget, x, y);
+ if (!next)
+ break;
+ else
+ widget = next;
+ }
+ return widget;
+}
+
+static ltk_widget *
+get_hover_popup(ltk_window *window, int x, int y) {
+ for (size_t i = window->popups_num; i-- > 0;) {
+ if (ltk_collide_rect(window->popups[i]->rect, x, y))
+ return window->popups[i];
+ }
+ return NULL;
+}
+
+/* FIXME: This is still weird. */
void
-ltk_widget_mouse_press_event(ltk_widget *widget, ltk_event *event) {
- if (!widget || widget->state == LTK_DISABLED)
+ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
+ ltk_widget *widget = get_hover_popup(window, event->x, event->y);
+ if (!widget) {
+ ltk_window_unregister_all_popups(window);
+ widget = window->root_widget;
+ }
+ if (!widget)
return;
- int default_handler = 1;
- if (widget->vtable->mouse_press)
- default_handler = widget->vtable->mouse_press(widget, event);
- if (default_handler) {
- if (event->button.button == LTK_BUTTONL)
- ltk_window_set_pressed_widget(widget->window, widget);
+ ltk_widget *cur_widget = get_widget_under_pointer(widget, event->x, event->y);
+ int first = 1;
+ while (cur_widget) {
+ int handled = 0;
+ if (cur_widget->state != LTK_DISABLED) {
+ if (cur_widget->vtable->mouse_press)
+ handled = cur_widget->vtable->mouse_press(cur_widget, event);
+ /* set first non-disabled widget to pressed widget */
+ if (first && event->button == LTK_BUTTONL) {
+ ltk_window_set_pressed_widget(window, cur_widget);
+ first = 0;
+ }
+ }
+ if (!handled)
+ cur_widget = cur_widget->parent;
+ else
+ break;
}
}
void
-ltk_widget_mouse_release_event(ltk_widget *widget, ltk_event *event) {
- if (!widget || widget->state == LTK_DISABLED)
- return;
- if (widget->vtable->mouse_release)
+ltk_window_mouse_release_event(ltk_window *window, ltk_button_event *event) {
+ ltk_widget *widget = get_hover_popup(window, event->x, event->y);
+ if (!widget)
+ widget = window->pressed_widget;
+ if (widget && widget->vtable->mouse_release)
widget->vtable->mouse_release(widget, event);
- ltk_window_set_pressed_widget(widget->window, NULL);
+ if (event->button == LTK_BUTTONL) {
+ ltk_window_set_pressed_widget(window, NULL);
+ /* send motion notify to widget under pointer */
+ ltk_motion_event e = {.type = LTK_MOTION_EVENT, .x = event->x, .y = event->y};
+ ltk_window_motion_notify_event(window, &e);
+ }
}
void
-ltk_widget_motion_notify_event(ltk_widget *widget, ltk_event *event) {
- /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */
- /* FIXME: need to bring back hover state to make enter/leave work properly */
- /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
- /* (especially once keyboard navigation is added) */
- /* Also, enter/leave should probably be called for all in hierarchy */
- 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 && widget->state != LTK_DISABLED) {
- /* FIXME: because only the bottom widget of the hierarchy is stored,
- this *really* does not work properly! */
- if (widget != widget->window->active_widget) {
- if (widget->window->active_widget && widget->window->active_widget->vtable->mouse_leave) {
- widget->window->active_widget->vtable->mouse_leave(widget->window->active_widget, event);
- }
- if (widget->vtable->mouse_enter) {
- widget->vtable->mouse_enter(widget, event);
+ltk_window_motion_notify_event(ltk_window *window, ltk_motion_event *event) {
+ ltk_widget *widget = get_hover_popup(window, event->x, event->y);
+ if (!widget) {
+ widget = window->pressed_widget;
+ if (widget) {
+ if (widget->vtable->motion_notify)
+ widget->vtable->motion_notify(widget, event);
+ return;
+ }
+ widget = window->root_widget;
+ }
+ if (!widget)
+ return;
+ if (!ltk_collide_rect(widget->rect, event->x, event->y)) {
+ ltk_window_set_hover_widget(widget->window, NULL, event);
+ return;
+ }
+ ltk_widget *cur_widget = get_widget_under_pointer(widget, event->x, event->y);
+ int first = 1;
+ while (cur_widget) {
+ int handled = 0;
+ if (cur_widget->state != LTK_DISABLED) {
+ if (cur_widget->vtable->motion_notify)
+ handled = cur_widget->vtable->motion_notify(cur_widget, event);
+ /* set first non-disabled widget to hover widget */
+ /* FIXME: should enter/leave event be sent to parent
+ when moving from/to widget nested in parent? */
+ if (first) {
+ ltk_window_set_hover_widget(window, cur_widget, event);
+ first = 0;
}
}
- if (widget->vtable->motion_notify)
- set_active = widget->vtable->motion_notify(widget, event);
+ if (!handled)
+ cur_widget = cur_widget->parent;
+ else
+ break;
}
- if (set_active)
- ltk_window_set_active_widget(widget->window, widget);
}
int
diff --git a/src/widget.h b/src/widget.h
@@ -38,6 +38,7 @@ typedef enum {
typedef enum {
LTK_NORMAL,
+ LTK_HOVER,
LTK_PRESSED,
LTK_ACTIVE,
LTK_DISABLED
@@ -89,11 +90,11 @@ struct ltk_widget {
struct ltk_widget_vtable {
void (*key_press) (struct ltk_widget *, ltk_event *);
void (*key_release) (struct ltk_widget *, ltk_event *);
- int (*mouse_press) (struct ltk_widget *, ltk_event *);
- int (*mouse_release) (struct ltk_widget *, ltk_event *);
- int (*motion_notify) (struct ltk_widget *, ltk_event *);
- int (*mouse_leave) (struct ltk_widget *, ltk_event *);
- int (*mouse_enter) (struct ltk_widget *, ltk_event *);
+ int (*mouse_press) (struct ltk_widget *, ltk_button_event *);
+ int (*mouse_release) (struct ltk_widget *, ltk_button_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 *);
void (*resize) (struct ltk_widget *);
void (*hide) (struct ltk_widget *);
@@ -102,7 +103,9 @@ struct ltk_widget_vtable {
void (*destroy) (struct ltk_widget *, int);
void (*child_size_change) (struct ltk_widget *, struct ltk_widget *);
+ /* FIXME: why does this take window? */
int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **);
+ struct ltk_widget *(*get_child_at_pos)(struct ltk_widget *, int x, int y);
ltk_widget_type type;
char needs_redraw;
@@ -114,9 +117,10 @@ int ltk_widget_destroy_cmd(struct ltk_window *window, char **tokens, size_t num_
void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, struct ltk_window *window,
struct ltk_widget_vtable *vtable, int w, int h);
void ltk_widget_change_state(ltk_widget *widget);
-void ltk_widget_mouse_press_event(ltk_widget *widget, ltk_event *event);
-void ltk_widget_mouse_release_event(ltk_widget *widget, ltk_event *event);
-void ltk_widget_motion_notify_event(ltk_widget *widget, ltk_event *event);
+/* FIXME: move to separate window.h */
+void ltk_window_mouse_press_event(ltk_window *window, ltk_button_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);
int ltk_widget_id_free(const char *id);
ltk_widget *ltk_get_widget(const char *id, ltk_widget_type type, char **errstr);
void ltk_set_widget(ltk_widget *widget, const char *id);