commit 46ddc040dfd9d76a78a5f7b4d1d080e141c7dd2f
parent 9b2b4596605e6594a2538f6b3bce3ffd9e90c652
Author: lumidify <nobody@lumidify.org>
Date: Wed, 13 May 2026 13:18:27 +0200
Store widgets in central array and use IDs in most public functions
Diffstat:
44 files changed, 2015 insertions(+), 1661 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -4,7 +4,7 @@ src/ltk/ctrlsel.*, and src/ltk/macros.h for third-party licenses.
ISC License
The Lumidify ToolKit (LTK)
-Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+Copyright (c) 2016-2026 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
diff --git a/examples/ltk/test.c b/examples/ltk/test.c
@@ -45,75 +45,75 @@ main(int argc, char *argv[]) {
(void)argv;
ltk_init();
- ltk_window *window = ltk_window_create("Hi", 0, 0, 500, 500);
- ltk_grid *grid = ltk_grid_create(window, 5, 2);
+ ltk_widget_id window = ltk_window_create("Hi", 0, 0, 500, 500);
+ ltk_widget_id grid = ltk_grid_create(window, 5, 2);
ltk_grid_set_column_weight(grid, 0, 1);
ltk_grid_set_column_weight(grid, 1, 1);
ltk_grid_set_row_weight(grid, 4, 1);
- ltk_button *button = ltk_button_create(window, "I'm a button!");
- ltk_button *button1 = ltk_button_create(window, "I'm also a button!");
- ltk_label *label = ltk_label_create(window, "I'm a label!");
+ ltk_widget_id button = ltk_button_create(window, "I'm a button!");
+ ltk_widget_id button1 = ltk_button_create(window, "I'm also a button!");
+ ltk_widget_id label = ltk_label_create(window, "I'm a label!");
ltk_image *img = ltk_image_create_from_path("test.jpg");
if (!img) {
fprintf(stderr, "Unable to load image.\n");
return 1;
}
- ltk_image_widget *iw = ltk_image_widget_create(window, img);
- ltk_entry *entry = ltk_entry_create(window, "");
- ltk_menu *menu = ltk_menu_create(window);
- ltk_menuentry *e1 = ltk_menuentry_create(window, "Hi");
- ltk_menuentry *e2 = ltk_menuentry_create(window, "I'm a submenu");
+ ltk_widget_id iw = ltk_image_widget_create(window, img);
+ ltk_widget_id entry = ltk_entry_create(window, "");
+ ltk_widget_id menu = ltk_menu_create(window);
+ ltk_widget_id e1 = ltk_menuentry_create(window, "Hi");
+ ltk_widget_id e2 = ltk_menuentry_create(window, "I'm a submenu");
ltk_menu_add_entry(menu, e1);
ltk_menu_add_entry(menu, e2);
- ltk_menu *submenu = ltk_submenu_create(window);
- ltk_menuentry *e3 = ltk_menuentry_create(window, "Menu Entry 1");
- ltk_menuentry *e4 = ltk_menuentry_create(window, "Quit");
+ ltk_widget_id submenu = ltk_submenu_create(window);
+ ltk_widget_id e3 = ltk_menuentry_create(window, "Menu Entry 1");
+ ltk_widget_id e4 = ltk_menuentry_create(window, "Quit");
ltk_menu_add_entry(submenu, e3);
ltk_menu_add_entry(submenu, e4);
ltk_menuentry_attach_submenu(e2, submenu);
- ltk_box *box = ltk_box_create(window, LTK_VERTICAL);
- ltk_button *btn1 = ltk_button_create(window, "Bla1");
- ltk_button *btn2 = ltk_button_create(window, "Bla2");
- ltk_button *btn3 = ltk_button_create(window, "Bla3");
- ltk_button *btn4 = ltk_button_create(window, "Bla4");
- ltk_button *btn5 = ltk_button_create(window, "Bla5");
- ltk_box_add(box, LTK_CAST_WIDGET(btn1), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(btn2), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(btn3), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(btn4), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(btn5), LTK_STICKY_LEFT);
+ ltk_widget_id box = ltk_box_create(window, LTK_VERTICAL);
+ ltk_widget_id btn1 = ltk_button_create(window, "Bla1");
+ ltk_widget_id btn2 = ltk_button_create(window, "Bla2");
+ ltk_widget_id btn3 = ltk_button_create(window, "Bla3");
+ ltk_widget_id btn4 = ltk_button_create(window, "Bla4");
+ ltk_widget_id btn5 = ltk_button_create(window, "Bla5");
+ ltk_box_add(box, btn1, LTK_STICKY_LEFT);
+ ltk_box_add(box, btn2, LTK_STICKY_LEFT);
+ ltk_box_add(box, btn3, LTK_STICKY_LEFT);
+ ltk_box_add(box, btn4, LTK_STICKY_LEFT);
+ ltk_box_add(box, btn5, LTK_STICKY_LEFT);
- ltk_checkbutton *cbtn1 = ltk_checkbutton_create(window, "Checkbutton1", 0);
- ltk_checkbutton *cbtn2 = ltk_checkbutton_create(window, "Checkbutton2", 1);
- ltk_box_add(box, LTK_CAST_WIDGET(cbtn1), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(cbtn2), LTK_STICKY_LEFT);
+ ltk_widget_id cbtn1 = ltk_checkbutton_create(window, "Checkbutton1", 0);
+ ltk_widget_id cbtn2 = ltk_checkbutton_create(window, "Checkbutton2", 1);
+ ltk_box_add(box, cbtn1, LTK_STICKY_LEFT);
+ ltk_box_add(box, cbtn2, LTK_STICKY_LEFT);
- ltk_radiobutton *rbtn1 = ltk_radiobutton_create(window, "Radiobutton1", 0, NULL);
- ltk_radiobutton *rbtn2 = ltk_radiobutton_create(window, "Radiobutton2", 1, rbtn1);
- ltk_box_add(box, LTK_CAST_WIDGET(rbtn1), LTK_STICKY_LEFT);
- ltk_box_add(box, LTK_CAST_WIDGET(rbtn2), LTK_STICKY_LEFT);
+ ltk_widget_id rbtn1 = ltk_radiobutton_create(window, "Radiobutton1", 0, LTK_WIDGET_ID_NONE);
+ ltk_widget_id rbtn2 = ltk_radiobutton_create(window, "Radiobutton2", 1, rbtn1);
+ ltk_box_add(box, rbtn1, LTK_STICKY_LEFT);
+ ltk_box_add(box, rbtn2, LTK_STICKY_LEFT);
- ltk_combobox *combo = ltk_combobox_create(window);
+ ltk_widget_id combo = ltk_combobox_create(window);
ltk_combobox_add_option(combo, "Option 1");
ltk_combobox_add_option(combo, "Option 2");
ltk_combobox_add_option(combo, "Option 3");
ltk_combobox_add_option(combo, "Option 4");
- ltk_grid_add(grid, LTK_CAST_WIDGET(menu), 0, 0, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(combo), 0, 1, 1, 1, LTK_STICKY_LEFT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(button), 1, 0, 1, 1, LTK_STICKY_LEFT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(button1), 1, 1, 1, 1, LTK_STICKY_RIGHT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(label), 2, 0, 1, 1, LTK_STICKY_RIGHT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(iw), 2, 1, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT|LTK_STICKY_PRESERVE_ASPECT_RATIO);
- ltk_grid_add(grid, LTK_CAST_WIDGET(entry), 3, 0, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT);
- ltk_grid_add(grid, LTK_CAST_WIDGET(box), 4, 0, 1, 2, LTK_STICKY_LEFT|LTK_STICKY_RIGHT|LTK_STICKY_TOP|LTK_STICKY_BOTTOM);
- ltk_window_set_root_widget(window, LTK_CAST_WIDGET(grid));
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(button), LTK_BUTTON_SIGNAL_PRESSED, &quit, LTK_ARG_VOID);
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(e4), LTK_MENUENTRY_SIGNAL_PRESSED, &quit, LTK_ARG_VOID);
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(button1), LTK_BUTTON_SIGNAL_PRESSED, &printstuff, LTK_MAKE_ARG_INT(5));
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(window), LTK_WINDOW_SIGNAL_CLOSE, &quit, LTK_ARG_VOID);
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(button1), LTK_WIDGET_SIGNAL_CHANGE_STATE, &printstate, LTK_ARG_VOID);
+ ltk_grid_add(grid, menu, 0, 0, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT);
+ ltk_grid_add(grid, combo, 0, 1, 1, 1, LTK_STICKY_LEFT);
+ ltk_grid_add(grid, button, 1, 0, 1, 1, LTK_STICKY_LEFT);
+ ltk_grid_add(grid, button1, 1, 1, 1, 1, LTK_STICKY_RIGHT);
+ ltk_grid_add(grid, label, 2, 0, 1, 1, LTK_STICKY_RIGHT);
+ ltk_grid_add(grid, iw, 2, 1, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT|LTK_STICKY_PRESERVE_ASPECT_RATIO);
+ ltk_grid_add(grid, entry, 3, 0, 1, 1, LTK_STICKY_LEFT|LTK_STICKY_RIGHT);
+ ltk_grid_add(grid, box, 4, 0, 1, 2, LTK_STICKY_LEFT|LTK_STICKY_RIGHT|LTK_STICKY_TOP|LTK_STICKY_BOTTOM);
+ ltk_window_set_root_widget(window, grid);
+ ltk_widget_register_signal_handler(button, LTK_BUTTON_SIGNAL_PRESSED, &quit, LTK_ARG_VOID);
+ ltk_widget_register_signal_handler(e4, LTK_MENUENTRY_SIGNAL_PRESSED, &quit, LTK_ARG_VOID);
+ ltk_widget_register_signal_handler(button1, LTK_BUTTON_SIGNAL_PRESSED, &printstuff, LTK_MAKE_ARG_INT(5));
+ ltk_widget_register_signal_handler(window, LTK_WINDOW_SIGNAL_CLOSE, &quit, LTK_ARG_VOID);
+ ltk_widget_register_signal_handler(button1, LTK_WIDGET_SIGNAL_CHANGE_STATE, &printstate, LTK_ARG_VOID);
ltk_mainloop();
return 0;
diff --git a/src/ltk/array.h b/src/ltk/array.h
@@ -1,5 +1,7 @@
+/* FIXME: oops, I guess I left the old MIT license here - not sure if I should just
+ leave it as is or update it to ISC... */
/*
- * Copyright (c) 2020-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2020-2026 lumidify <nobody@lumidify.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -47,20 +49,24 @@ typedef struct { \
size_t len; \
} ltk_array_##name;
-#define LTK_ARRAY_INIT_FUNC_DECL_BASE(name, type, storage) \
-LTK_UNUSED_FUNC storage ltk_array_##name *ltk_array_create_##name(size_t initial_capacity); \
-LTK_UNUSED_FUNC storage type ltk_array_pop_##name(ltk_array_##name *ar); \
-LTK_UNUSED_FUNC storage size_t ltk_array_remove_if_##name( \
- ltk_array_##name *ar, int (*callback)(type *, void *), void *data \
-); \
-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); \
+#define LTK_ARRAY_INIT_FUNC_DECL_BASE(name, type, storage) \
+LTK_UNUSED_FUNC storage ltk_array_##name *ltk_array_create_##name(size_t initial_capacity); \
+LTK_UNUSED_FUNC storage type ltk_array_pop_##name(ltk_array_##name *ar); \
+LTK_UNUSED_FUNC storage size_t ltk_array_remove_if_##name( \
+ ltk_array_##name *ar, int (*callback)(type *, void *), void *data \
+); \
+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); \
+LTK_UNUSED_FUNC storage void ltk_array_insert_multiple_##name(ltk_array_##name *ar, size_t index, type *elem, size_t len); \
+LTK_UNUSED_FUNC storage void ltk_array_delete_##name(ltk_array_##name *ar, size_t index, size_t len); \
+LTK_UNUSED_FUNC storage void ltk_array_resize_##name(ltk_array_##name *ar, size_t size, type default_val); \
+LTK_UNUSED_FUNC storage void ltk_array_change_capacity_##name(ltk_array_##name *ar, size_t size); \
+LTK_UNUSED_FUNC storage void ltk_array_len_to_capacity_##name(ltk_array_##name *ar, type default_val); \
+LTK_UNUSED_FUNC storage void ltk_array_destroy_##name(ltk_array_##name *ar); \
+LTK_UNUSED_FUNC storage void ltk_array_reset_##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) \
@@ -102,7 +108,7 @@ 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"); \
- ltk_array_resize_##name(ar, ar->len + len); \
+ ltk_array_change_capacity_##name(ar, ar->len + len); \
ar->len += len; \
if (ar->len - len == index) \
return; \
@@ -111,7 +117,13 @@ 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_array_insert_##name(ltk_array_##name *ar, size_t index, type elem) { \
+ ltk_array_prepare_gap_##name(ar, index, 1); \
+ ar->buf[index] = elem; \
+} \
+ \
+LTK_UNUSED_FUNC storage void \
+ltk_array_insert_multiple_##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++) { \
ar->buf[index + i] = elem[i]; \
@@ -131,18 +143,27 @@ ltk_array_delete_##name(ltk_array_##name *ar, size_t index, size_t len) { \
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); \
+ ltk_array_change_capacity_##name(ar, ar->len + 1); \
ar->buf[ar->len++] = elem; \
} \
\
LTK_UNUSED_FUNC storage void \
-ltk_array_clear_##name(ltk_array_##name *ar) { \
+ltk_array_reset_##name(ltk_array_##name *ar) { \
ar->len = 0; \
- ltk_array_resize_##name(ar, 1); \
+ ltk_array_change_capacity_##name(ar, 1); \
} \
\
LTK_UNUSED_FUNC storage void \
-ltk_array_resize_##name(ltk_array_##name *ar, size_t len) { \
+ltk_array_resize_##name(ltk_array_##name *ar, size_t len, type default_val) { \
+ ltk_array_change_capacity_##name(ar, len); \
+ for (size_t i = ar->len; i < len; i++) { \
+ ar->buf[i] = default_val; \
+ } \
+ ar->len = len; \
+} \
+ \
+LTK_UNUSED_FUNC storage void \
+ltk_array_change_capacity_##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) { \
ar->buf = ltk_reallocarray(ar->buf, new_size, sizeof(type)); \
@@ -152,6 +173,14 @@ ltk_array_resize_##name(ltk_array_##name *ar, size_t len) { \
} \
\
LTK_UNUSED_FUNC storage void \
+ltk_array_len_to_capacity_##name(ltk_array_##name *ar, type default_val) { \
+ for (size_t i = ar->len; i < ar->buf_size; i++) { \
+ ar->buf[i] = default_val; \
+ } \
+ ar->len = ar->buf_size; \
+} \
+ \
+LTK_UNUSED_FUNC storage void \
ltk_array_destroy_##name(ltk_array_##name *ar) { \
if (!ar) \
return; \
@@ -183,15 +212,20 @@ ltk_array_set_safe_##name(ltk_array_##name *ar, size_t index, type e) { \
ar->buf[index] = e; \
}
+/* FIXME: resize and change_capacity should maybe also take destroy_func optionally if size/capacity is decreased */
#define ltk_array(name) ltk_array_##name
#define ltk_array_create(name, initial_capacity) ltk_array_create_##name(initial_capacity)
#define ltk_array_pop(name, ar) ltk_array_pop_##name(ar)
#define ltk_array_remove_if(name, ar, callback, data) ltk_array_remove_if_##name(ar, callback, data)
-#define ltk_array_insert(name, ar, index, elem, len) ltk_array_insert_##name(ar, index, elem, len)
+#define ltk_array_insert(name, ar, index, elem) ltk_array_insert_##name(ar, index, elem)
+#define ltk_array_insert_multiple(name, ar, index, elem, len) ltk_array_insert_multiple_##name(ar, index, elem, len)
#define ltk_array_delete(name, ar, index, len) ltk_array_delete_##name(ar, index, len)
-#define ltk_array_resize(name, ar, size) ltk_array_resize_##name(ar, size)
+#define ltk_array_resize(name, ar, size, default_val) ltk_array_resize_##name(ar, size, default_val)
+#define ltk_array_change_capacity(name, ar, size) ltk_array_change_capacity_##name(ar, size)
+#define ltk_array_len_to_capacity(name, ar, default_val) ltk_array_len_to_capacity_##name(ar, default_val)
#define ltk_array_destroy(name, ar) ltk_array_destroy_##name(ar)
-#define ltk_array_clear(name, ar) ltk_array_clear_##name(ar)
+#define ltk_array_reset(name, ar) ltk_array_reset_##name(ar)
+#define ltk_array_clear(ar) ((ar)->len = 0)
#define ltk_array_append(name, ar, elem) ltk_array_append_##name(ar, elem)
#define ltk_array_destroy_deep(name, ar, destroy_func) ltk_array_destroy_deep_##name(ar, destroy_func)
#define ltk_array_len(ar) ((ar)->len)
diff --git a/src/ltk/box.c b/src/ltk/box.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -26,28 +26,29 @@
#include "rect.h"
#include "scrollbar.h"
#include "widget.h"
+#include "ltk.h"
static void ltk_box_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip);
static void ltk_box_destroy(ltk_widget *self, int shallow);
static void ltk_recalculate_box(ltk_widget *self);
-static void ltk_box_child_size_change(ltk_widget *self, ltk_widget *widget);
-static int ltk_box_remove_child(ltk_widget *self, ltk_widget *widget);
+static void ltk_box_child_size_change(ltk_widget *self, ltk_widget_id childid);
+static int ltk_box_remove_child(ltk_widget *self, ltk_widget_id widgetid);
/* static int ltk_box_clear(ltk_window *window, ltk_box *box, int shallow); */
static int ltk_box_scroll_cb(ltk_widget *self, ltk_callback_arglist args, ltk_callback_arg data);
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 ltk_widget_id 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);
-static ltk_widget *ltk_box_prev_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_box_next_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_box_first_child(ltk_widget *self);
-static ltk_widget *ltk_box_last_child(ltk_widget *self);
+static ltk_widget_id ltk_box_prev_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_box_next_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_box_first_child(ltk_widget *self);
+static ltk_widget_id ltk_box_last_child(ltk_widget *self);
-static ltk_widget *ltk_box_nearest_child(ltk_widget *self, ltk_rect rect);
-static ltk_widget *ltk_box_nearest_child_left(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_box_nearest_child_right(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_box_nearest_child_above(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_box_nearest_child_below(ltk_widget *self, ltk_widget *widget);
+static ltk_widget_id ltk_box_nearest_child(ltk_widget *self, ltk_rect rect);
+static ltk_widget_id ltk_box_nearest_child_left(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_box_nearest_child_right(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_box_nearest_child_above(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_box_nearest_child_below(ltk_widget *self, ltk_widget_id childid);
static void ltk_box_recalc_ideal_size(ltk_widget *self);
@@ -87,47 +88,48 @@ static struct ltk_widget_vtable vtable = {
static void
ltk_box_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) {
ltk_box *box = LTK_CAST_BOX(self);
- ltk_widget *ptr;
/* FIXME: clip out scrollbar */
ltk_rect real_clip = ltk_rect_intersect((ltk_rect){0, 0, self->lrect.w, self->lrect.h}, clip);
- for (size_t i = 0; i < box->num_widgets; i++) {
- ptr = box->widgets[i];
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget *ptr = ltk_get_widget_from_id(ltk_array_get(box->widgets, i));
/* FIXME: Maybe continue immediately if widget is
obviously outside of clipping rect */
ltk_widget_draw(ptr, s, x + ptr->lrect.x, y + ptr->lrect.y, ltk_rect_relative(ptr->lrect, real_clip));
}
+ ltk_widget *scrollbar = ltk_get_widget_from_id(box->scrollbar);
ltk_widget_draw(
- LTK_CAST_WIDGET(box->sc), s,
- x + box->sc->widget.lrect.x,
- y + box->sc->widget.lrect.y,
- ltk_rect_relative(box->sc->widget.lrect, real_clip)
+ scrollbar, s,
+ x + scrollbar->lrect.x,
+ y + scrollbar->lrect.y,
+ ltk_rect_relative(scrollbar->lrect, real_clip)
);
}
-ltk_box *
-ltk_box_create(ltk_window *window, ltk_orientation orient) {
+ltk_widget_id
+ltk_box_create(ltk_widget_id windowid, ltk_orientation orient) {
ltk_box *box = ltk_malloc(sizeof(ltk_box));
ltk_widget *self = LTK_CAST_WIDGET(box);
- ltk_fill_widget_defaults(self, window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(self, windowid, &vtable, 0, 0);
- box->sc = ltk_scrollbar_create(window, orient);
- box->sc->widget.parent = self;
+ box->scrollbar = ltk_scrollbar_create(windowid, orient);
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ scwidget->parent = id;
+ /* FIXM: separate callback function that takes widget ID that can be used safely externally
+ (e.g. if an independent scrollbar should be associated with the box) */
ltk_widget_register_signal_handler(
- LTK_CAST_WIDGET(box->sc), LTK_SCROLLBAR_SIGNAL_SCROLL,
- <k_box_scroll_cb, LTK_MAKE_ARG_WIDGET(self)
+ box->scrollbar, LTK_SCROLLBAR_SIGNAL_SCROLL,
+ <k_box_scroll_cb, LTK_MAKE_ARG_WIDGET_ID(self->id)
);
- box->widgets = NULL;
- box->num_alloc = 0;
- box->num_widgets = 0;
+ box->widgets = ltk_array_create(widget_id, 1);
box->orient = orient;
if (orient == LTK_HORIZONTAL)
- box->widget.ideal_h = box->sc->widget.ideal_h;
+ self->ideal_h = scwidget->ideal_h;
else
- box->widget.ideal_w = box->sc->widget.ideal_w;
+ self->ideal_w = scwidget->ideal_w;
ltk_recalculate_box(self);
- return box;
+ return id;
}
static void
@@ -146,22 +148,22 @@ ltk_box_ensure_rect_shown(ltk_widget *self, ltk_rect r) {
delta = r.y;
}
if (delta)
- ltk_scrollbar_scroll(LTK_CAST_WIDGET(box->sc), delta, 0);
+ ltk_scrollbar_scroll(box->scrollbar, delta, 0);
}
static void
ltk_box_destroy(ltk_widget *self, int shallow) {
ltk_box *box = LTK_CAST_BOX(self);
- ltk_widget *ptr;
- for (size_t i = 0; i < box->num_widgets; i++) {
- ptr = box->widgets[i];
- ptr->parent = NULL;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget *ptr = ltk_get_widget_from_id(ltk_array_get(box->widgets, i));
+ ptr->parent = LTK_WIDGET_ID_NONE;
if (!shallow)
ltk_widget_destroy(ptr, shallow);
}
- ltk_free(box->widgets);
- box->sc->widget.parent = NULL;
- ltk_widget_destroy(LTK_CAST_WIDGET(box->sc), 0);
+ ltk_array_destroy(widget_id, box->widgets);
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ scwidget->parent = LTK_WIDGET_ID_NONE;
+ ltk_widget_destroy(scwidget, 0);
ltk_free(box);
}
@@ -173,65 +175,65 @@ ltk_box_destroy(ltk_widget *self, int shallow) {
static void
ltk_recalculate_box(ltk_widget *self) {
ltk_box *box = LTK_CAST_BOX(self);
- ltk_widget *ptr;
- ltk_rect *sc_rect = &box->sc->widget.lrect;
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ ltk_scrollbar *sc = LTK_CAST_SCROLLBAR(scwidget);
+ ltk_rect *sc_rect = &scwidget->lrect;
int cur_pos = 0;
if (box->orient == LTK_HORIZONTAL)
- sc_rect->h = box->sc->widget.ideal_h;
+ sc_rect->h = scwidget->ideal_h;
else
- sc_rect->w = box->sc->widget.ideal_w;
- for (size_t i = 0; i < box->num_widgets; i++) {
- ptr = box->widgets[i];
+ sc_rect->w = scwidget->ideal_w;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget *ptr = ltk_get_widget_from_id(ltk_array_get(box->widgets, i));
ptr->lrect.w = ptr->ideal_w;
ptr->lrect.h = ptr->ideal_h;
if (box->orient == LTK_HORIZONTAL) {
- ptr->lrect.x = cur_pos - box->sc->cur_pos;
+ ptr->lrect.x = cur_pos - sc->cur_pos;
if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM)
- ptr->lrect.h = box->widget.lrect.h - sc_rect->h;
+ ptr->lrect.h = self->lrect.h - sc_rect->h;
if (ptr->sticky & LTK_STICKY_TOP)
ptr->lrect.y = 0;
else if (ptr->sticky & LTK_STICKY_BOTTOM)
- ptr->lrect.y = box->widget.lrect.h - ptr->lrect.h - sc_rect->h;
+ ptr->lrect.y = self->lrect.h - ptr->lrect.h - sc_rect->h;
else
- ptr->lrect.y = (box->widget.lrect.h - ptr->lrect.h) / 2;
+ ptr->lrect.y = (self->lrect.h - ptr->lrect.h) / 2;
cur_pos += ptr->lrect.w;
} else {
- ptr->lrect.y = cur_pos - box->sc->cur_pos;
+ ptr->lrect.y = cur_pos - sc->cur_pos;
if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT)
- ptr->lrect.w = box->widget.lrect.w - sc_rect->w;
+ ptr->lrect.w = self->lrect.w - sc_rect->w;
if (ptr->sticky & LTK_STICKY_LEFT)
ptr->lrect.x = 0;
else if (ptr->sticky & LTK_STICKY_RIGHT)
- ptr->lrect.x = box->widget.lrect.w - ptr->lrect.w - sc_rect->w;
+ ptr->lrect.x = self->lrect.w - ptr->lrect.w - sc_rect->w;
else
- ptr->lrect.x = (box->widget.lrect.w - ptr->lrect.w) / 2;
+ ptr->lrect.x = (self->lrect.w - ptr->lrect.w) / 2;
cur_pos += ptr->lrect.h;
}
ptr->crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, ptr->lrect);
ltk_widget_resize(ptr);
}
- ltk_scrollbar_set_virtual_size(box->sc, cur_pos);
+ ltk_scrollbar_set_virtual_size(box->scrollbar, cur_pos);
if (box->orient == LTK_HORIZONTAL) {
sc_rect->x = 0;
- sc_rect->y = box->widget.lrect.h - sc_rect->h;
- sc_rect->w = box->widget.lrect.w;
+ sc_rect->y = self->lrect.h - sc_rect->h;
+ sc_rect->w = self->lrect.w;
} else {
- sc_rect->x = box->widget.lrect.w - sc_rect->w;
+ sc_rect->x = self->lrect.w - sc_rect->w;
sc_rect->y = 0;
- sc_rect->h = box->widget.lrect.h;
+ sc_rect->h = self->lrect.h;
}
- *sc_rect = ltk_rect_intersect(*sc_rect, (ltk_rect){0, 0, box->widget.lrect.w, box->widget.lrect.h});
- box->sc->widget.crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, *sc_rect);
- ltk_widget_resize(LTK_CAST_WIDGET(box->sc));
+ *sc_rect = ltk_rect_intersect(*sc_rect, (ltk_rect){0, 0, self->lrect.w, self->lrect.h});
+ scwidget->crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, *sc_rect);
+ ltk_widget_resize(scwidget);
}
static void
ltk_box_recalc_ideal_size(ltk_widget *self) {
ltk_box *box = LTK_CAST_BOX(self);
- ltk_widget *ptr;
self->ideal_w = self->ideal_h = 0;
- for (size_t i = 0; i < box->num_widgets; i++) {
- ptr = box->widgets[i];
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget *ptr = ltk_get_widget_from_id(ltk_array_get(box->widgets, i));
ltk_widget_recalc_ideal_size(ptr);
if (box->orient == LTK_HORIZONTAL && ptr->ideal_h > self->ideal_h) {
self->ideal_h = ptr->ideal_h;
@@ -241,11 +243,12 @@ ltk_box_recalc_ideal_size(ltk_widget *self) {
self->ideal_h += ptr->ideal_h;
}
}
- ltk_widget_recalc_ideal_size(LTK_CAST_WIDGET(box->sc));
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ ltk_widget_recalc_ideal_size(scwidget);
if (box->orient == LTK_HORIZONTAL)
- self->ideal_h += box->sc->widget.ideal_h;
+ self->ideal_h += scwidget->ideal_h;
else if (box->orient == LTK_VERTICAL)
- self->ideal_w += box->sc->widget.ideal_w;
+ self->ideal_w += scwidget->ideal_w;
}
/* FIXME: This entire resizing thing is a bit weird. For instance, if a label
@@ -254,10 +257,14 @@ ltk_box_recalc_ideal_size(ltk_widget *self) {
ideal size? Ideal size wouldn't really make sense here, but then the box
might be forced to add a scrollbar even though the parent widget would
actually give it more space if it knew that it needed it. */
+/* In the case with the label, there would need to be a way to say what the ideal
+ height is, given the current width, but I guess that eventually brings us to
+ a constraint solver, which I'd like to avoid... */
static void
-ltk_box_child_size_change(ltk_widget *self, ltk_widget *widget) {
+ltk_box_child_size_change(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
+ ltk_widget *child = ltk_get_widget_from_id(childid);
short size_changed = 0;
/* This is always reset here - if it needs to be changed,
the resize function called by the last child_size_change
@@ -270,217 +277,231 @@ ltk_box_child_size_change(ltk_widget *self, ltk_widget *widget) {
could also set all widgets even if they don't have any sticky
settings, but there'd probably be some catch as well. */
/* FIXME: the same comment as in grid.c applies */
- int orig_w = widget->lrect.w;
- int orig_h = widget->lrect.h;
- widget->lrect.w = widget->ideal_w;
- widget->lrect.h = widget->ideal_h;
- int sc_w = box->sc->widget.lrect.w;
- int sc_h = box->sc->widget.lrect.h;
- if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h > box->widget.ideal_h) {
- box->widget.ideal_h = widget->ideal_h + sc_h;
+ int orig_w = child->lrect.w;
+ int orig_h = child->lrect.h;
+ child->lrect.w = child->ideal_w;
+ child->lrect.h = child->ideal_h;
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ int sc_w = scwidget->lrect.w;
+ int sc_h = scwidget->lrect.h;
+ if (box->orient == LTK_HORIZONTAL && child->ideal_h + sc_h > self->ideal_h) {
+ self->ideal_h = child->ideal_h + sc_h;
size_changed = 1;
- } else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w > box->widget.ideal_h) {
- box->widget.ideal_w = widget->ideal_w + sc_w;
+ } else if (box->orient == LTK_VERTICAL && child->ideal_w + sc_w > self->ideal_h) {
+ self->ideal_w = child->ideal_w + sc_w;
size_changed = 1;
}
- if (size_changed && box->widget.parent && box->widget.parent->vtable->child_size_change)
- box->widget.parent->vtable->child_size_change(box->widget.parent, (ltk_widget *)box);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (size_changed && parent && parent->vtable->child_size_change)
+ parent->vtable->child_size_change(parent, self->id);
else
- ltk_recalculate_box(LTK_CAST_WIDGET(box));
- if (orig_w != widget->lrect.w || orig_h != widget->lrect.h)
- ltk_widget_resize(widget);
+ ltk_recalculate_box(self);
+ if (orig_w != child->lrect.w || orig_h != child->lrect.h)
+ ltk_widget_resize(child);
}
int
-ltk_box_add(ltk_box *box, ltk_widget *widget, ltk_sticky_mask sticky) {
- if (widget->parent)
+ltk_box_add(ltk_widget_id boxid, ltk_widget_id widgetid, ltk_sticky_mask sticky) {
+ ltk_widget *self = ltk_get_widget_from_id(boxid);
+ ltk_box *box = LTK_CAST_BOX(self);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ if (!LTK_WIDGET_ID_IS_NONE(widget->parent))
return 1;
- if (box->num_widgets >= box->num_alloc) {
- size_t new_size = box->num_alloc > 0 ? box->num_alloc * 2 : 4;
- ltk_widget **new = ltk_realloc(box->widgets, new_size * sizeof(ltk_widget *));
- box->num_alloc = new_size;
- box->widgets = new;
- }
ltk_widget_recalc_ideal_size(widget);
- int sc_w = box->sc->widget.lrect.w;
- int sc_h = box->sc->widget.lrect.h;
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ int sc_w = scwidget->lrect.w;
+ int sc_h = scwidget->lrect.h;
- box->widgets[box->num_widgets++] = widget;
+ ltk_array_append(widget_id, box->widgets, widgetid);
if (box->orient == LTK_HORIZONTAL) {
- box->widget.ideal_w += widget->ideal_w;
- if (widget->ideal_h + sc_h > box->widget.ideal_h)
- box->widget.ideal_h = widget->ideal_h + sc_h;
+ self->ideal_w += widget->ideal_w;
+ if (widget->ideal_h + sc_h > self->ideal_h)
+ self->ideal_h = widget->ideal_h + sc_h;
} else {
- box->widget.ideal_h += widget->ideal_h;
- if (widget->ideal_w + sc_w > box->widget.ideal_w)
- box->widget.ideal_w = widget->ideal_w + sc_w;
+ self->ideal_h += widget->ideal_h;
+ if (widget->ideal_w + sc_w > self->ideal_w)
+ self->ideal_w = widget->ideal_w + sc_w;
}
- widget->parent = LTK_CAST_WIDGET(box);
+ widget->parent = self->id;
widget->sticky = sticky;
- ltk_box_child_size_change(LTK_CAST_WIDGET(box), widget);
- ltk_window_invalidate_widget_rect(box->widget.window, LTK_CAST_WIDGET(box));
+ ltk_box_child_size_change(self, widgetid);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
return 0;
}
int
-ltk_box_remove_index(ltk_box *box, size_t index) {
- if (index >= box->num_widgets)
+ltk_box_remove_index(ltk_widget_id boxid, size_t index) {
+ ltk_widget *self = ltk_get_widget_from_id(boxid);
+ ltk_box *box = LTK_CAST_BOX(self);
+ if (index >= ltk_array_len(box->widgets))
return 1;
- ltk_widget *self = LTK_CAST_WIDGET(box);
- ltk_widget *widget = box->widgets[index];
- int sc_w = box->sc->widget.lrect.w;
- int sc_h = box->sc->widget.lrect.h;
- if (index < box->num_widgets - 1)
- memmove(box->widgets + index, box->widgets + index + 1,
- (box->num_widgets - index - 1) * sizeof(ltk_widget *));
- box->num_widgets--;
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_widget *widget = ltk_get_widget_from_id(ltk_array_get(box->widgets, index));
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ int sc_w = scwidget->lrect.w;
+ int sc_h = scwidget->lrect.h;
+ ltk_array_delete(widget_id, box->widgets, index, 1);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
/* search for new ideal width/height */
/* FIXME: make this all a bit nicer and break the lines better */
/* FIXME: other part of ideal size not updated */
if (box->orient == LTK_HORIZONTAL && widget->ideal_h + sc_h == self->ideal_h) {
self->ideal_h = 0;
- for (size_t j = 0; j < box->num_widgets; j++) {
- if (box->widgets[j]->ideal_h + sc_h > self->ideal_h)
- self->ideal_h = box->widgets[j]->ideal_h + sc_h;
+ for (size_t j = 0; j < ltk_array_len(box->widgets); j++) {
+ ltk_widget *w = ltk_get_widget_from_id(ltk_array_get(box->widgets, j));
+ if (w->ideal_h + sc_h > self->ideal_h)
+ self->ideal_h = w->ideal_h + sc_h;
}
- if (self->parent)
- ltk_widget_resize(self->parent);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent)
+ ltk_widget_resize(parent);
} else if (box->orient == LTK_VERTICAL && widget->ideal_w + sc_w == self->ideal_w) {
self->ideal_w = 0;
- for (size_t j = 0; j < box->num_widgets; j++) {
- if (box->widgets[j]->ideal_w + sc_w > self->ideal_w)
- self->ideal_w = box->widgets[j]->ideal_w + sc_w;
+ for (size_t j = 0; j < ltk_array_len(box->widgets); j++) {
+ ltk_widget *w = ltk_get_widget_from_id(ltk_array_get(box->widgets, j));
+ if (w->ideal_w + sc_w > self->ideal_w)
+ self->ideal_w = w->ideal_w + sc_w;
}
- if (self->parent)
- ltk_widget_resize(self->parent);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent)
+ ltk_widget_resize(parent);
}
return 0;
}
int
-ltk_box_remove(ltk_box *box, ltk_widget *widget) {
- if (widget->parent != LTK_CAST_WIDGET(box))
+ltk_box_remove(ltk_widget_id boxid, ltk_widget_id widgetid) {
+ ltk_widget *self = ltk_get_widget_from_id(boxid);
+ ltk_box *box = LTK_CAST_BOX(self);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ if (!LTK_WIDGET_ID_EQUAL(widget->parent, self->id))
return 1;
- widget->parent = NULL;
- for (size_t i = 0; i < box->num_widgets; i++) {
- if (box->widgets[i] == widget) {
- return ltk_box_remove_index(box, i);
- }
+ widget->parent = LTK_WIDGET_ID_NONE;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget_id id = ltk_array_get(box->widgets, i);
+ if (LTK_WIDGET_ID_EQUAL(id, widgetid))
+ return ltk_box_remove_index(boxid, i);
}
return 1;
}
static int
-ltk_box_remove_child(ltk_widget *self, ltk_widget *widget) {
- return ltk_box_remove(LTK_CAST_BOX(self), widget);
+ltk_box_remove_child(ltk_widget *self, ltk_widget_id widgetid) {
+ return ltk_box_remove(self->id, widgetid);
}
/* FIXME: maybe come up with a more efficient method */
-static ltk_widget *
+static ltk_widget_id
ltk_box_nearest_child(ltk_widget *self, ltk_rect rect) {
ltk_box *box = LTK_CAST_BOX(self);
- ltk_widget *minw = NULL;
+ ltk_widget_id minw = LTK_WIDGET_ID_NONE;
int min_dist = INT_MAX;
- for (size_t i = 0; i < box->num_widgets; i++) {
- ltk_rect r = box->widgets[i]->lrect;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget_id id = ltk_array_get(box->widgets, i);
+ ltk_widget *widget = ltk_get_widget_from_id(id);
+ ltk_rect r = widget->lrect;
int dist = ltk_rect_fakedist(rect, r);
if (dist < min_dist) {
min_dist = dist;
- minw = box->widgets[i];
+ minw = id;
}
}
return minw;
}
-static ltk_widget *
-ltk_box_nearest_child_left(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_box_nearest_child_left(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
if (box->orient == LTK_VERTICAL)
- return NULL;
- return ltk_box_prev_child(self, widget);
+ return LTK_WIDGET_ID_NONE;
+ return ltk_box_prev_child(self, childid);
}
-static ltk_widget *
-ltk_box_nearest_child_right(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_box_nearest_child_right(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
if (box->orient == LTK_VERTICAL)
- return NULL;
- return ltk_box_next_child(self, widget);
+ return LTK_WIDGET_ID_NONE;
+ return ltk_box_next_child(self, childid);
}
-static ltk_widget *
-ltk_box_nearest_child_above(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_box_nearest_child_above(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
if (box->orient == LTK_HORIZONTAL)
- return NULL;
- return ltk_box_prev_child(self, widget);
+ return LTK_WIDGET_ID_NONE;
+ return ltk_box_prev_child(self, childid);
}
-static ltk_widget *
-ltk_box_nearest_child_below(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_box_nearest_child_below(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
if (box->orient == LTK_HORIZONTAL)
- return NULL;
- return ltk_box_next_child(self, widget);
+ return LTK_WIDGET_ID_NONE;
+ return ltk_box_next_child(self, childid);
}
-static ltk_widget *
-ltk_box_prev_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_box_prev_child(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
- for (size_t i = box->num_widgets; i-- > 0;) {
- if (box->widgets[i] == child)
- return i > 0 ? box->widgets[i-1] : NULL;
+ for (size_t i = ltk_array_len(box->widgets); i-- > 0;) {
+ ltk_widget_id id = ltk_array_get(box->widgets, i);
+ if (LTK_WIDGET_ID_EQUAL(id, childid))
+ return i > 0 ? ltk_array_get(box->widgets, i - 1) : LTK_WIDGET_ID_NONE;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_box_next_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_box_next_child(ltk_widget *self, ltk_widget_id childid) {
ltk_box *box = LTK_CAST_BOX(self);
- for (size_t i = 0; i < box->num_widgets; i++) {
- if (box->widgets[i] == child)
- return i < box->num_widgets - 1 ? box->widgets[i+1] : NULL;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget_id id = ltk_array_get(box->widgets, i);
+ if (LTK_WIDGET_ID_EQUAL(id, childid))
+ return i < ltk_array_len(box->widgets) - 1 ? ltk_array_get(box->widgets, i + 1) : LTK_WIDGET_ID_NONE;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_box_first_child(ltk_widget *self) {
ltk_box *box = LTK_CAST_BOX(self);
- return box->num_widgets > 0 ? box->widgets[0] : NULL;
+ return ltk_array_len(box->widgets) > 0 ? ltk_array_get(box->widgets, 0) : LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_box_last_child(ltk_widget *self) {
ltk_box *box = LTK_CAST_BOX(self);
- return box->num_widgets > 0 ? box->widgets[box->num_widgets-1] : NULL;
+ size_t len = ltk_array_len(box->widgets);
+ return len > 0 ? ltk_array_get(box->widgets, len - 1) : LTK_WIDGET_ID_NONE;
}
static int
ltk_box_scroll_cb(ltk_widget *self, ltk_callback_arglist args, ltk_callback_arg data) {
(void)self;
(void)args;
- ltk_widget *boxw = LTK_CAST_ARG_WIDGET(data);
+ ltk_widget_id boxid = LTK_CAST_ARG_WIDGET_ID(data);
+ ltk_widget *boxw = ltk_get_widget_from_id(boxid);
ltk_recalculate_box(boxw);
- ltk_window_invalidate_widget_rect(boxw->window, boxw);
+ ltk_window_invalidate_widget_rect(boxw->window, boxw->id);
return 1;
}
-static ltk_widget *
+static ltk_widget_id
ltk_box_get_child_at_pos(ltk_widget *self, int x, int y) {
ltk_box *box = LTK_CAST_BOX(self);
- if (ltk_collide_rect(box->sc->widget.crect, x, y))
- return (ltk_widget *)box->sc;
- for (size_t i = 0; i < box->num_widgets; i++) {
- if (ltk_collide_rect(box->widgets[i]->crect, x, y))
- return box->widgets[i];
+ ltk_widget *scwidget = ltk_get_widget_from_id(box->scrollbar);
+ if (ltk_collide_rect(scwidget->crect, x, y))
+ return box->scrollbar;
+ for (size_t i = 0; i < ltk_array_len(box->widgets); i++) {
+ ltk_widget *widget = ltk_get_widget_from_id(ltk_array_get(box->widgets, i));
+ if (ltk_collide_rect(widget->crect, x, y))
+ return widget->id;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
static int
@@ -490,7 +511,7 @@ ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event) {
/* FIXME: horizontal scrolling, etc. */
/* FIXME: configure scrollstep */
int delta = event->dy * -15;
- ltk_scrollbar_scroll(LTK_CAST_WIDGET(box->sc), delta, 0);
+ ltk_scrollbar_scroll(box->scrollbar, 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;
diff --git a/src/ltk/box.h b/src/ltk/box.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -24,20 +24,20 @@
#define LTK_BOX_SIGNAL_INVALID -1
+/* FIXME: allow to associate any scrollbar with box (maybe even multiple?) */
+/* -> can be done using signals without any special handling? */
typedef struct {
ltk_widget widget;
- ltk_scrollbar *sc;
- ltk_widget *pressed_widget;
- ltk_widget *active_widget;
- ltk_widget **widgets;
- size_t num_alloc;
- size_t num_widgets;
+ ltk_widget_id scrollbar;
+ ltk_widget_id pressed_widget;
+ ltk_widget_id active_widget;
+ ltk_array(widget_id) *widgets;
ltk_orientation orient;
} ltk_box;
-ltk_box *ltk_box_create(ltk_window *window, ltk_orientation orient);
-int ltk_box_add(ltk_box *box, ltk_widget *widget, ltk_sticky_mask sticky);
-int ltk_box_remove(ltk_box *box, ltk_widget *widget);
-int ltk_box_remove_index(ltk_box *box, size_t index);
+ltk_widget_id ltk_box_create(ltk_widget_id windowid, ltk_orientation orient);
+int ltk_box_add(ltk_widget_id boxid, ltk_widget_id widgetid, ltk_sticky_mask sticky);
+int ltk_box_remove(ltk_widget_id boxid, ltk_widget_id widgetid);
+int ltk_box_remove_index(ltk_widget_id boxid, size_t index);
#endif /* LTK_BOX_H */
diff --git a/src/ltk/button.c b/src/ltk/button.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -155,18 +155,19 @@ ltk_button_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_rect
static int
ltk_button_release(ltk_widget *self) {
- ltk_widget_emit_signal(self, LTK_BUTTON_SIGNAL_PRESSED, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_BUTTON_SIGNAL_PRESSED, LTK_EMPTY_ARGLIST);
return 1;
}
static void
recalc_ideal_size(ltk_button *button) {
+ ltk_widget *self = LTK_CAST_WIDGET(button);
int text_w, text_h;
ltk_text_line_get_size(button->tl, &text_w, &text_h);
- int bw = ltk_size_to_pixel(theme.border_width, LTK_CAST_WIDGET(button)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(button)->last_dpi);
- button->widget.ideal_w = text_w + bw * 2 + pad * 2;
- button->widget.ideal_h = text_h + bw * 2 + pad * 2;
+ int bw = ltk_size_to_pixel(theme.border_width, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ self->ideal_w = text_w + bw * 2 + pad * 2;
+ self->ideal_h = text_h + bw * 2 + pad * 2;
}
static void
@@ -177,28 +178,24 @@ ltk_button_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_size(button);
}
-ltk_button *
-ltk_button_create(ltk_window *window, const char *text) {
+ltk_widget_id
+ltk_button_create(ltk_widget_id windowid, const char *text) {
ltk_button *button = ltk_malloc(sizeof(ltk_button));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(button), windowid, &vtable, 0, 0);
button->tl = ltk_text_line_create_const_text_default(
- theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1
+ theme.font, ltk_size_to_pixel(theme.font_size, LTK_CAST_WIDGET(button)->last_dpi), text, -1
);
recalc_ideal_size(button);
- button->widget.dirty = 1;
+ LTK_CAST_WIDGET(button)->dirty = 1;
- return button;
+ return id;
}
static void
ltk_button_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_button *button = LTK_CAST_BUTTON(self);
- if (!button) {
- ltk_warn("Tried to destroy NULL button.\n");
- return;
- }
ltk_text_line_destroy(button->tl);
ltk_free(button);
}
diff --git a/src/ltk/button.h b/src/ltk/button.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -29,6 +29,6 @@ typedef struct {
ltk_text_line *tl;
} ltk_button;
-ltk_button *ltk_button_create(ltk_window *window, const char *text);
+ltk_widget_id ltk_button_create(ltk_widget_id windowid, const char *text);
#endif /* LTK_BUTTON_H */
diff --git a/src/ltk/checkbutton.c b/src/ltk/checkbutton.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -206,33 +206,37 @@ static int
ltk_checkbutton_release(ltk_widget *self) {
ltk_checkbutton *button = LTK_CAST_CHECKBUTTON(self);
button->checked = !button->checked;
- ltk_widget_emit_signal(self, LTK_CHECKBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_CHECKBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
return 1;
}
int
-ltk_checkbutton_get_checked(ltk_checkbutton *button) {
+ltk_checkbutton_get_checked(ltk_widget_id buttonid) {
+ ltk_widget *self = ltk_get_widget_from_id(buttonid);
+ ltk_checkbutton *button = LTK_CAST_CHECKBUTTON(self);
return button->checked;
}
void
-ltk_checkbutton_set_checked(ltk_checkbutton *button, int checked) {
+ltk_checkbutton_set_checked(ltk_widget_id buttonid, int checked) {
+ ltk_widget *self = ltk_get_widget_from_id(buttonid);
+ ltk_checkbutton *button = LTK_CAST_CHECKBUTTON(self);
button->checked = checked;
- ltk_widget *self = LTK_CAST_WIDGET(button);
- ltk_window_invalidate_widget_rect(self->window, self);
- ltk_widget_emit_signal(self, LTK_CHECKBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
+ ltk_widget_emit_signal(self->id, LTK_CHECKBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
}
#define MAX(a, b) ((a) > (b) ? (a) : (b))
static void
recalc_ideal_size(ltk_checkbutton *button) {
+ ltk_widget *self = LTK_CAST_WIDGET(button);
int text_w, text_h;
ltk_text_line_get_size(button->tl, &text_w, &text_h);
- int box_size = ltk_size_to_pixel(theme.box_size, LTK_CAST_WIDGET(button)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(button)->last_dpi);
- button->widget.ideal_w = text_w + pad * 3 + box_size;
- button->widget.ideal_h = MAX(text_h, box_size) + pad * 2;
+ int box_size = ltk_size_to_pixel(theme.box_size, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ self->ideal_w = text_w + pad * 3 + box_size;
+ self->ideal_h = MAX(text_h, box_size) + pad * 2;
}
static void
@@ -243,29 +247,25 @@ ltk_checkbutton_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_size(button);
}
-ltk_checkbutton *
-ltk_checkbutton_create(ltk_window *window, const char *text, int checked) {
+ltk_widget_id
+ltk_checkbutton_create(ltk_widget_id windowid, const char *text, int checked) {
ltk_checkbutton *button = ltk_malloc(sizeof(ltk_checkbutton));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(button), windowid, &vtable, 0, 0);
button->checked = checked;
button->tl = ltk_text_line_create_const_text_default(
theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1
);
recalc_ideal_size(button);
- button->widget.dirty = 1;
+ LTK_CAST_WIDGET(button)->dirty = 1;
- return button;
+ return id;
}
static void
ltk_checkbutton_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_checkbutton *button = LTK_CAST_CHECKBUTTON(self);
- if (!button) {
- ltk_warn("Tried to destroy NULL checkbutton.\n");
- return;
- }
ltk_text_line_destroy(button->tl);
ltk_free(button);
}
diff --git a/src/ltk/checkbutton.h b/src/ltk/checkbutton.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -30,8 +30,8 @@ typedef struct {
int checked;
} ltk_checkbutton;
-ltk_checkbutton *ltk_checkbutton_create(ltk_window *window, const char *text, int checked);
-int ltk_checkbutton_get_checked(ltk_checkbutton *button);
-void ltk_checkbutton_set_checked(ltk_checkbutton *button, int checked);
+ltk_widget_id ltk_checkbutton_create(ltk_widget_id windowid, const char *text, int checked);
+int ltk_checkbutton_get_checked(ltk_widget_id buttonid);
+void ltk_checkbutton_set_checked(ltk_widget_id buttonid, int checked);
#endif /* LTK_CHECKBUTTON_H */
diff --git a/src/ltk/combobox.c b/src/ltk/combobox.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -37,9 +37,9 @@ static void ltk_combobox_draw(ltk_widget *self, ltk_surface *draw_surf, int x, i
static int ltk_combobox_release(ltk_widget *self);
static void ltk_combobox_destroy(ltk_widget *self, int shallow);
static void ltk_combobox_recalc_ideal_size(ltk_widget *self);
-static int ltk_combobox_remove_child(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_combobox_get_child(ltk_widget *self);
-static ltk_widget *ltk_combobox_nearest_child(ltk_widget *self, ltk_rect rect);
+static int ltk_combobox_remove_child(ltk_widget *self, ltk_widget_id widgetid);
+static ltk_widget_id ltk_combobox_get_child(ltk_widget *self);
+static ltk_widget_id ltk_combobox_nearest_child(ltk_widget *self, ltk_rect rect);
static int ltk_combobox_key_press(ltk_widget *self, ltk_key_event *event);
static int choose_external(ltk_widget *self, ltk_key_event *event);
static void ltk_combobox_cmd_return(ltk_widget *self, char *text, size_t len);
@@ -215,19 +215,23 @@ ltk_combobox_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_re
/* FIXME: this is kind of ugly because it uses a lot of internal knowledge about menus */
static void
popup_dropdown(ltk_combobox *combobox) {
- if (!combobox->dropdown || ltk_menu_get_num_entries(combobox->dropdown) == 0)
+ ltk_widget *self = LTK_CAST_WIDGET(combobox);
+ ltk_widget *dropdownw = ltk_get_widget_or_null_from_id(combobox->dropdown);
+ if (!dropdownw || ltk_menu_get_num_entries(combobox->dropdown) == 0)
return;
- ltk_rect combo_rect = LTK_CAST_WIDGET(combobox)->lrect;
- ltk_point combo_global = ltk_widget_pos_to_global(LTK_CAST_WIDGET(combobox), 0, 0);
-
- int win_w = LTK_CAST_WIDGET(combobox)->window->rect.w;
- int win_h = LTK_CAST_WIDGET(combobox)->window->rect.h;
- ltk_menu *dropdown = combobox->dropdown;
- ltk_widget_recalc_ideal_size(LTK_CAST_WIDGET(dropdown));
- int ideal_w = dropdown->widget.ideal_w;
- int ideal_h = dropdown->widget.ideal_h;
+ ltk_menu *dropdown = LTK_CAST_MENU(dropdownw);
+ ltk_rect combo_rect = self->lrect;
+ ltk_point combo_global = ltk_widget_pos_to_global(self, 0, 0);
+
+ ltk_widget *windoww = ltk_get_widget_from_id(self->window);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ int win_w = window->rect.w;
+ int win_h = window->rect.h;
+ ltk_widget_recalc_ideal_size(dropdownw);
+ int ideal_w = dropdownw->ideal_w;
+ int ideal_h = dropdownw->ideal_h;
int x_final = 0, y_final = 0, w_final = ideal_w, h_final = ideal_h;
- int combo_bw = ltk_size_to_pixel(theme.border_width, LTK_CAST_WIDGET(combobox)->last_dpi);
+ int combo_bw = ltk_size_to_pixel(theme.border_width, self->last_dpi);
int space_top = combo_global.y;
int space_bottom = win_h - (combo_global.y + combo_rect.h);
@@ -267,24 +271,25 @@ popup_dropdown(ltk_combobox *combobox) {
dropdown->x_scroll_offset = dropdown->y_scroll_offset = 0;
dropdown->scroll_top_hover = dropdown->scroll_bottom_hover = 0;
dropdown->scroll_left_hover = dropdown->scroll_right_hover = 0;
- dropdown->widget.lrect.x = x_final;
- dropdown->widget.lrect.y = y_final;
- dropdown->widget.lrect.w = w_final;
- dropdown->widget.lrect.h = h_final;
- dropdown->widget.crect = LTK_CAST_WIDGET(dropdown)->lrect;
- dropdown->widget.dirty = 1;
- dropdown->widget.hidden = 0;
+ dropdownw->lrect.x = x_final;
+ dropdownw->lrect.y = y_final;
+ dropdownw->lrect.w = w_final;
+ dropdownw->lrect.h = h_final;
+ dropdownw->crect = LTK_CAST_WIDGET(dropdown)->lrect;
+ dropdownw->dirty = 1;
+ dropdownw->hidden = 0;
dropdown->popup_submenus = 0;
dropdown->unpopup_submenus_on_hide = 1;
- ltk_widget_resize(LTK_CAST_WIDGET(dropdown));
- ltk_window_register_popup(LTK_CAST_WIDGET(combobox)->window, LTK_CAST_WIDGET(dropdown));
- ltk_window_invalidate_widget_rect(LTK_CAST_WIDGET(dropdown)->window, LTK_CAST_WIDGET(dropdown));
+ ltk_widget_resize(dropdownw);
+ ltk_window_register_popup(self->window, combobox->dropdown);
+ ltk_window_invalidate_widget_rect(dropdownw->window, combobox->dropdown);
}
static void
unpopup_dropdown(ltk_combobox *combobox) {
- if (combobox->dropdown && !LTK_CAST_WIDGET(combobox->dropdown)->hidden) {
- ltk_widget_hide(LTK_CAST_WIDGET(combobox->dropdown));
+ ltk_widget *dropdownw = ltk_get_widget_or_null_from_id(combobox->dropdown);
+ if (dropdownw && !dropdownw->hidden) {
+ ltk_widget_hide(dropdownw);
}
}
@@ -294,9 +299,10 @@ unpopup_dropdown(ltk_combobox *combobox) {
static int
ltk_combobox_release(ltk_widget *self) {
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (!combo->dropdown)
+ ltk_widget *dropdownw = ltk_get_widget_or_null_from_id(combo->dropdown);
+ if (!dropdownw)
return 0;
- if (combo->dropdown->widget.hidden)
+ if (dropdownw->hidden)
popup_dropdown(combo);
else
unpopup_dropdown(combo);
@@ -307,12 +313,13 @@ ltk_combobox_release(ltk_widget *self) {
static void
recalc_ideal_size(ltk_combobox *combobox) {
+ ltk_widget *self = LTK_CAST_WIDGET(combobox);
int text_w, text_h;
ltk_text_line_get_size(combobox->tl, &text_w, &text_h);
- int arrow_size = ltk_size_to_pixel(theme.arrow_size, LTK_CAST_WIDGET(combobox)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(combobox)->last_dpi);
- combobox->widget.ideal_w = text_w + pad * 3 + arrow_size;
- combobox->widget.ideal_h = MAX(text_h, arrow_size) + pad * 2;
+ int arrow_size = ltk_size_to_pixel(theme.arrow_size, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ self->ideal_w = text_w + pad * 3 + arrow_size;
+ self->ideal_h = MAX(text_h, arrow_size) + pad * 2;
}
static void
@@ -325,33 +332,35 @@ ltk_combobox_recalc_ideal_size(ltk_widget *self) {
static void
combobox_set_active(ltk_combobox *combo, size_t idx, const char *text) {
+ ltk_widget *self = LTK_CAST_WIDGET(combo);
combo->cur_active = idx;
ltk_text_line_set_const_text(combo->tl, text);
recalc_ideal_size(combo);
- if (combo->widget.parent && combo->widget.parent->vtable->child_size_change) {
- combo->widget.parent->vtable->child_size_change(combo->widget.parent, LTK_CAST_WIDGET(combo));
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent && parent->vtable->child_size_change) {
+ parent->vtable->child_size_change(parent, self->id);
}
- ltk_widget_emit_signal(LTK_CAST_WIDGET(combo), LTK_COMBOBOX_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_COMBOBOX_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
}
static int
handle_entry_pressed(ltk_widget *self, ltk_callback_arglist args, ltk_callback_arg data) {
(void)args;
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- ltk_combobox *combo = LTK_CAST_COMBOBOX(LTK_CAST_ARG_WIDGET(data));
- if (!combo->dropdown) /* shouldn't be possible */
+ ltk_widget *combow = ltk_get_widget_from_id(LTK_CAST_ARG_WIDGET_ID(data));
+ ltk_combobox *combo = LTK_CAST_COMBOBOX(combow);
+ if (LTK_WIDGET_ID_IS_NONE(combo->dropdown)) /* shouldn't be possible */
return 1;
- size_t idx = ltk_menu_get_entry_index(combo->dropdown, e);
+ size_t idx = ltk_menu_get_entry_index(combo->dropdown, self->id);
if (idx == SIZE_MAX) /* shouldn't be possible */
return 1;
- combobox_set_active(combo, idx, ltk_menuentry_get_text(e));
+ combobox_set_active(combo, idx, ltk_menuentry_get_text(self->id));
return 1;
}
static void
ltk_combobox_cmd_return(ltk_widget *self, char *text, size_t len) {
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (!combo->dropdown)
+ if (LTK_WIDGET_ID_IS_NONE(combo->dropdown))
return;
/* need to copy since it's not nul-terminated */
char *textcopy = ltk_strndup(text, len);
@@ -359,7 +368,8 @@ ltk_combobox_cmd_return(ltk_widget *self, char *text, size_t len) {
/* only take text until first newline into account */
if (nl)
*nl = '\0';
- for (size_t i = 0; i < ltk_menu_get_num_entries(combo->dropdown); i++) {
+ size_t num_entries = ltk_menu_get_num_entries(combo->dropdown);
+ for (size_t i = 0; i < num_entries; i++) {
if (!strcmp(textcopy, ltk_menuentry_get_text(ltk_menu_get_entry(combo->dropdown, i)))) {
combobox_set_active(combo, i, textcopy);
break;
@@ -372,7 +382,7 @@ static int
choose_external(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (!combo->dropdown || ltk_menu_get_num_entries(combo->dropdown) == 0)
+ if (LTK_WIDGET_ID_IS_NONE(combo->dropdown) || ltk_menu_get_num_entries(combo->dropdown) == 0)
return 1;
ltk_general_config *config = ltk_config_get_general();
/* FIXME: allow arguments to key mappings - this would allow to have different key mappings
@@ -383,11 +393,12 @@ choose_external(ltk_widget *self, ltk_key_event *event) {
/* FIXME: somehow show that there was an error if this returns 1? */
/* FIXME: change interface to not require length of cmd */
txtbuf *tmpbuf = txtbuf_new();
- for (size_t i = 0; i < ltk_menu_get_num_entries(combo->dropdown); i++) {
+ size_t num_entries = ltk_menu_get_num_entries(combo->dropdown);
+ for (size_t i = 0; i < num_entries; i++) {
txtbuf_append(tmpbuf, ltk_menuentry_get_text(ltk_menu_get_entry(combo->dropdown, i)));
txtbuf_append(tmpbuf, "\n");
}
- ltk_call_cmd(self, config->option_chooser, txtbuf_get_text(tmpbuf), txtbuf_len(tmpbuf));
+ ltk_call_cmd(self->id, config->option_chooser, txtbuf_get_text(tmpbuf), txtbuf_len(tmpbuf));
txtbuf_destroy(tmpbuf);
}
return 1;
@@ -399,18 +410,22 @@ ltk_combobox_key_press(ltk_widget *self, ltk_key_event *event) {
}
const char *
-ltk_combobox_get_text(ltk_combobox *combo) {
- if (!combo->dropdown)
+ltk_combobox_get_text(ltk_widget_id comboboxid) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
+ if (LTK_WIDGET_ID_IS_NONE(combobox->dropdown))
return NULL;
- ltk_menuentry *e = ltk_menu_get_entry(combo->dropdown, combo->cur_active);
- if (!e)
+ ltk_widget_id e = ltk_menu_get_entry(combobox->dropdown, combobox->cur_active);
+ if (LTK_WIDGET_ID_IS_NONE(e))
return NULL;
return ltk_menuentry_get_text(e);
}
size_t
-ltk_combobox_get_index(ltk_combobox *combo) {
- return combo->cur_active;
+ltk_combobox_get_index(ltk_widget_id comboboxid) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
+ return combobox->cur_active;
}
/* FIXME: this is really hacky - it was added to remove some weird effects when moving
@@ -424,29 +439,32 @@ ltk_combobox_get_index(ltk_combobox *combo) {
static int
handle_dropdown_change_state(ltk_widget *self, ltk_callback_arglist args, ltk_callback_arg data) {
(void)args;
- ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_combobox *combo = LTK_CAST_COMBOBOX(LTK_CAST_ARG_WIDGET(data));
- if (menu != combo->dropdown) /* should never happen */
+ ltk_widget *combow = ltk_get_widget_from_id(LTK_CAST_ARG_WIDGET_ID(data));
+ ltk_combobox *combo = LTK_CAST_COMBOBOX(combow);
+ if (!LTK_WIDGET_ID_EQUAL(self->id, combo->dropdown)) /* should never happen */
return 0;
- if (!(menu->widget.state & LTK_ACTIVE) && !menu->widget.hidden)
+ if (!(self->state & LTK_ACTIVE) && !self->hidden)
ltk_widget_hide(self);
return 0;
}
int
-ltk_combobox_insert_option(ltk_combobox *combobox, const char *option, size_t idx) {
+ltk_combobox_insert_option(ltk_widget_id comboboxid, const char *option, size_t idx) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
unpopup_dropdown(combobox); /* just to avoid weird effects */
- if (!combobox->dropdown) {
- combobox->dropdown = ltk_submenu_create(LTK_CAST_WIDGET(combobox)->window);
- LTK_CAST_WIDGET(combobox->dropdown)->parent = LTK_CAST_WIDGET(combobox);
+ if (LTK_WIDGET_ID_IS_NONE(combobox->dropdown)) {
+ combobox->dropdown = ltk_submenu_create(self->window);
+ ltk_widget *dropdownw = ltk_get_widget_from_id(combobox->dropdown);
+ dropdownw->parent = self->id;
ltk_widget_register_signal_handler(
- LTK_CAST_WIDGET(combobox->dropdown), LTK_WIDGET_SIGNAL_CHANGE_STATE,
- &handle_dropdown_change_state, LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(combobox))
+ combobox->dropdown, LTK_WIDGET_SIGNAL_CHANGE_STATE,
+ &handle_dropdown_change_state, LTK_MAKE_ARG_WIDGET_ID(self->id)
);
}
- ltk_menuentry *e = ltk_menuentry_create(LTK_CAST_WIDGET(combobox)->window, option);
+ ltk_widget_id e = ltk_menuentry_create(self->window, option);
if (ltk_menu_insert_entry(combobox->dropdown, e, idx)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(e), 0);
+ ltk_widget_id_destroy(e, 0);
return 1;
}
size_t num = ltk_menu_get_num_entries(combobox->dropdown);
@@ -456,14 +474,16 @@ ltk_combobox_insert_option(ltk_combobox *combobox, const char *option, size_t id
combobox->cur_active++;
}
ltk_widget_register_signal_handler(
- LTK_CAST_WIDGET(e), LTK_MENUENTRY_SIGNAL_PRESSED,
- &handle_entry_pressed, LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(combobox))
+ e, LTK_MENUENTRY_SIGNAL_PRESSED,
+ &handle_entry_pressed, LTK_MAKE_ARG_WIDGET_ID(self->id)
);
return 0;
}
int
-ltk_combobox_add_option(ltk_combobox *combobox, const char *option) {
+ltk_combobox_add_option(ltk_widget_id comboboxid, const char *option) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
/* it's easier to just completely ban options with newlines instead of
dealing with weird cases where the external option-chooser splits
options at newlines */
@@ -471,15 +491,16 @@ ltk_combobox_add_option(ltk_combobox *combobox, const char *option) {
if (strchr(option, '\n'))
return 1;
unpopup_dropdown(combobox); /* just to avoid weird effects */
- if (!combobox->dropdown) {
- combobox->dropdown = ltk_submenu_create(LTK_CAST_WIDGET(combobox)->window);
- LTK_CAST_WIDGET(combobox->dropdown)->parent = LTK_CAST_WIDGET(combobox);
+ if (LTK_WIDGET_ID_IS_NONE(combobox->dropdown)) {
+ combobox->dropdown = ltk_submenu_create(self->window);
+ ltk_widget *dropdownw = ltk_get_widget_from_id(combobox->dropdown);
+ dropdownw->parent = self->id;
ltk_widget_register_signal_handler(
- LTK_CAST_WIDGET(combobox->dropdown), LTK_WIDGET_SIGNAL_CHANGE_STATE,
- &handle_dropdown_change_state, LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(combobox))
+ combobox->dropdown, LTK_WIDGET_SIGNAL_CHANGE_STATE,
+ &handle_dropdown_change_state, LTK_MAKE_ARG_WIDGET_ID(self->id)
);
}
- ltk_menuentry *e = ltk_menuentry_create(LTK_CAST_WIDGET(combobox)->window, option);
+ ltk_widget_id e = ltk_menuentry_create(self->window, option);
/* this should never fail */
ltk_menu_add_entry(combobox->dropdown, e);
size_t num = ltk_menu_get_num_entries(combobox->dropdown);
@@ -487,27 +508,29 @@ ltk_combobox_add_option(ltk_combobox *combobox, const char *option) {
combobox_set_active(combobox, 0, option);
}
ltk_widget_register_signal_handler(
- LTK_CAST_WIDGET(e), LTK_MENUENTRY_SIGNAL_PRESSED,
- &handle_entry_pressed, LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(combobox))
+ e, LTK_MENUENTRY_SIGNAL_PRESSED,
+ &handle_entry_pressed, LTK_MAKE_ARG_WIDGET_ID(self->id)
);
return 0;
}
int
-ltk_combobox_remove_option_index(ltk_combobox *combobox, size_t idx) {
- if (!combobox->dropdown)
+ltk_combobox_remove_option_index(ltk_widget_id comboboxid, size_t idx) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
+ if (LTK_WIDGET_ID_IS_NONE(combobox->dropdown))
return 1;
unpopup_dropdown(combobox); /* just to avoid weird effects */
- ltk_menuentry *e = ltk_menu_remove_entry_index(combobox->dropdown, idx);
- if (!e) return 1;
- ltk_widget_destroy(LTK_CAST_WIDGET(e), 0);
+ ltk_widget_id e = ltk_menu_remove_entry_index(combobox->dropdown, idx);
+ if (LTK_WIDGET_ID_IS_NONE(e)) return 1;
+ ltk_widget_id_destroy(e, 0);
if (idx == combobox->cur_active) {
size_t num = ltk_menu_get_num_entries(combobox->dropdown);
if (num == 0) {
combobox_set_active(combobox, SIZE_MAX, "");
} else {
e = ltk_menu_get_entry(combobox->dropdown, combobox->cur_active);
- if (!e) ltk_fatal("Unable to get menu entry. This should not happen.");
+ if (LTK_WIDGET_ID_IS_NONE(e)) ltk_fatal("Unable to get menu entry. This should not happen.");
combobox_set_active(combobox, idx >= num ? num - 1 : idx, ltk_menuentry_get_text(e));
}
}
@@ -515,8 +538,10 @@ ltk_combobox_remove_option_index(ltk_combobox *combobox, size_t idx) {
}
void
-ltk_combobox_remove_all_options(ltk_combobox *combobox) {
- if (!combobox->dropdown)
+ltk_combobox_remove_all_options(ltk_widget_id comboboxid) {
+ ltk_widget *self = ltk_get_widget_from_id(comboboxid);
+ ltk_combobox *combobox = LTK_CAST_COMBOBOX(self);
+ if (LTK_WIDGET_ID_IS_NONE(combobox->dropdown))
return;
unpopup_dropdown(combobox); /* just to avoid weird effects */
ltk_menu_remove_all_entries(combobox->dropdown);
@@ -526,58 +551,57 @@ ltk_combobox_remove_all_options(ltk_combobox *combobox) {
/* NOTE: This should never be called since the dropdown is managed
completely by the combobox, but it's here just in case. */
static int
-ltk_combobox_remove_child(ltk_widget *self, ltk_widget *widget) {
+ltk_combobox_remove_child(ltk_widget *self, ltk_widget_id widgetid) {
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (widget != LTK_CAST_WIDGET(combo->dropdown))
+ if (!LTK_WIDGET_ID_EQUAL(widgetid, combo->dropdown))
return 1;
- widget->parent = NULL;
- combo->dropdown = NULL;
+ ltk_widget *dropdownw = ltk_get_widget_from_id(widgetid);
+ dropdownw->parent = LTK_WIDGET_ID_NONE;
+ combo->dropdown = LTK_WIDGET_ID_NONE;
return 0;
}
-static ltk_widget *
+static ltk_widget_id
ltk_combobox_get_child(ltk_widget *self) {
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (combo->dropdown && !combo->dropdown->widget.hidden)
- return LTK_CAST_WIDGET(combo->dropdown);
- return NULL;
+ ltk_widget *dropdownw = ltk_get_widget_or_null_from_id(combo->dropdown);
+ if (dropdownw && !dropdownw->hidden)
+ return combo->dropdown;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_combobox_nearest_child(ltk_widget *self, ltk_rect rect) {
(void)rect;
return ltk_combobox_get_child(self);
}
-ltk_combobox *
-ltk_combobox_create(ltk_window *window) {
+ltk_widget_id
+ltk_combobox_create(ltk_widget_id windowid) {
ltk_combobox *combobox = ltk_malloc(sizeof(ltk_combobox));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(combobox), window, &vtable, 0, 0);
- combobox->dropdown = NULL;
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(combobox), windowid, &vtable, 0, 0);
+ combobox->dropdown = LTK_WIDGET_ID_NONE;
combobox->cur_active = SIZE_MAX;
/* FIXME: only create once text has been added */
combobox->tl = ltk_text_line_create_const_text_default(
- theme.font, ltk_size_to_pixel(theme.font_size, combobox->widget.last_dpi), "", -1
+ theme.font, ltk_size_to_pixel(theme.font_size, LTK_CAST_WIDGET(combobox)->last_dpi), "", -1
);
recalc_ideal_size(combobox);
- combobox->widget.dirty = 1;
+ LTK_CAST_WIDGET(combobox)->dirty = 1;
- return combobox;
+ return id;
}
static void
ltk_combobox_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_combobox *combo = LTK_CAST_COMBOBOX(self);
- if (!combo) {
- ltk_warn("Tried to destroy NULL combobox.\n");
- return;
- }
ltk_text_line_destroy(combo->tl);
- if (combo->dropdown) {
- LTK_CAST_WIDGET(combo->dropdown)->parent = NULL;
- ltk_widget_destroy(LTK_CAST_WIDGET(combo->dropdown), 0);
+ ltk_widget *dropdownw = ltk_get_widget_or_null_from_id(combo->dropdown);
+ if (dropdownw) {
+ dropdownw->parent = LTK_WIDGET_ID_NONE;
+ ltk_widget_destroy(dropdownw, 0);
}
ltk_free(combo);
}
diff --git a/src/ltk/combobox.h b/src/ltk/combobox.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -17,6 +17,7 @@
#ifndef LTK_COMBOBOX_H
#define LTK_COMBOBOX_H
+#include <stddef.h>
#include "text.h"
#include "widget.h"
#include "window.h"
@@ -28,16 +29,16 @@
typedef struct {
ltk_widget widget;
ltk_text_line *tl;
- ltk_menu *dropdown;
+ ltk_widget_id dropdown; /* ltk_menu */
size_t cur_active;
} ltk_combobox;
-ltk_combobox *ltk_combobox_create(ltk_window *window);
-int ltk_combobox_insert_option(ltk_combobox *combobox, const char *option, size_t idx);
-int ltk_combobox_add_option(ltk_combobox *combobox, const char *option);
-int ltk_combobox_remove_option_index(ltk_combobox *combobox, size_t idx);
-void ltk_combobox_remove_all_options(ltk_combobox *combobox);
-const char *ltk_combobox_get_text(ltk_combobox *combo);
-size_t ltk_combobox_get_index(ltk_combobox *combo);
+ltk_widget_id ltk_combobox_create(ltk_widget_id windowid);
+int ltk_combobox_insert_option(ltk_widget_id comboboxid, const char *option, size_t idx);
+int ltk_combobox_add_option(ltk_widget_id comboboxid, const char *option);
+int ltk_combobox_remove_option_index(ltk_widget_id comboboxid, size_t idx);
+void ltk_combobox_remove_all_options(ltk_widget_id comboboxid);
+const char *ltk_combobox_get_text(ltk_widget_id comboboxid);
+size_t ltk_combobox_get_index(ltk_widget_id comboboxid);
#endif /* LTK_COMBOBOX_H */
diff --git a/src/ltk/entry.c b/src/ltk/entry.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -58,9 +58,6 @@ 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);
static void ltk_entry_cmd_return(ltk_widget *self, char *text, size_t len);
-/* FIXME: also allow binding key release, not just press */
-typedef void (*cb_func)(ltk_entry *, ltk_key_event *);
-
/* FIXME: configure mouse actions, e.g. select-word-under-pointer, move-cursor-to-pointer */
static int cursor_to_beginning(ltk_widget *self, ltk_key_event *event);
@@ -79,7 +76,6 @@ static int delete_char_backwards(ltk_widget *self, ltk_key_event *event);
static int delete_char_forwards(ltk_widget *self, ltk_key_event *event);
static int edit_external(ltk_widget *self, ltk_key_event *event);
-static void recalc_ideal_size(ltk_entry *entry);
static void ensure_cursor_shown(ltk_entry *entry);
static void insert_text(ltk_entry *entry, char *text, size_t len, int move_cursor);
@@ -279,13 +275,14 @@ xy_to_pos(ltk_entry *e, int x, int y, int snap) {
static void
set_selection(ltk_entry *entry, size_t start, size_t end) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
entry->sel_start = start;
entry->sel_end = end;
ltk_text_line_clear_attrs(entry->tl);
if (start != end)
ltk_text_line_add_attr_bg(entry->tl, entry->sel_start, entry->sel_end, theme.selection_color);
- entry->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(entry->widget.window, &entry->widget);
+ self->dirty = 1;
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
static void
@@ -435,6 +432,7 @@ expand_selection_right(ltk_widget *self, ltk_key_event *event) {
static void
delete_text(ltk_entry *entry, size_t start, size_t end) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
memmove(entry->text + start, entry->text + end, entry->len - end);
entry->len -= end - start;
entry->text[entry->len] = '\0';
@@ -443,8 +441,8 @@ delete_text(ltk_entry *entry, size_t start, size_t end) {
ltk_text_line_set_text(entry->tl, entry->text, 0);
recalc_ideal_size(entry);
ensure_cursor_shown(entry);
- entry->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(entry->widget.window, &entry->widget);
+ self->dirty = 1;
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
static int
@@ -486,40 +484,44 @@ select_all(ltk_widget *self, ltk_key_event *event) {
static void
recalc_ideal_size_with_notification(ltk_entry *entry) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
/* FIXME: need to react to resize and adjust cur_offset */
- unsigned int old_w = entry->widget.ideal_w;
- unsigned int old_h = entry->widget.ideal_h;
+ unsigned int old_w = self->ideal_w;
+ unsigned int old_h = self->ideal_h;
recalc_ideal_size(entry);
- if (old_w != entry->widget.ideal_w || old_h != entry->widget.ideal_h) {
- if (entry->widget.parent && entry->widget.parent->vtable->child_size_change)
- entry->widget.parent->vtable->child_size_change(entry->widget.parent, &entry->widget);
+ if (old_w != self->ideal_w || old_h != self->ideal_h) {
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent && parent->vtable->child_size_change)
+ parent->vtable->child_size_change(parent, self->id);
}
}
static void
ensure_cursor_shown(ltk_entry *entry) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
int x, y, w, h;
ltk_text_line_pos_to_rect(entry->tl, entry->pos, &x, &y, &w, &h);
/* FIXME: test if anything weird can happen since resize is called by parent->child_size_change,
and then the stuff on the next few lines is done afterwards */
/* FIXME: adjustable cursor width */
- int bw = ltk_size_to_pixel(theme.border_width, LTK_CAST_WIDGET(entry)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(entry)->last_dpi);
- int text_w = entry->widget.lrect.w - 2 * bw - 2 * pad;
+ int bw = ltk_size_to_pixel(theme.border_width, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ int text_w = self->lrect.w - 2 * bw - 2 * pad;
if (x + 1 > text_w + entry->cur_offset) {
entry->cur_offset = x - text_w + 1;
- entry->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(entry->widget.window, &entry->widget);
+ self->dirty = 1;
+ ltk_window_invalidate_widget_rect(self->window, self->id);
} else if (x < entry->cur_offset) {
entry->cur_offset = x;
- entry->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(entry->widget.window, &entry->widget);
+ self->dirty = 1;
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
}
/* FIXME: maybe make this a regular key binding with wildcard text like in ledit? */
static void
insert_text(ltk_entry *entry, char *text, size_t len, int move_cursor) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
size_t num = 0;
/* FIXME: this is ugly and there are probably a lot of other
cases that need to be handled */
@@ -554,8 +556,8 @@ insert_text(ltk_entry *entry, char *text, size_t len, int move_cursor) {
ltk_text_line_set_text(entry->tl, entry->text, 0);
recalc_ideal_size_with_notification(entry);
ensure_cursor_shown(entry);
- entry->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(entry->widget.window, &entry->widget);
+ self->dirty = 1;
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
static void
@@ -578,7 +580,7 @@ edit_external(ltk_widget *self, ltk_key_event *event) {
} else {
/* FIXME: somehow show that there was an error if this returns 1? */
/* FIXME: change interface to not require length of cmd */
- ltk_call_cmd(LTK_CAST_WIDGET(entry), config->line_editor, entry->text, entry->len);
+ ltk_call_cmd(self->id, config->line_editor, entry->text, entry->len);
}
return 1;
}
@@ -588,7 +590,7 @@ ltk_entry_key_press(ltk_widget *self, ltk_key_event *event) {
ltk_entry *entry = LTK_CAST_ENTRY(self);
if (ltk_widget_handle_keypress_bindings(self, event, keypresses, 0)) {
self->dirty = 1;
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
return 1;
}
if (event->text && (event->modmask & (LTK_MOD_CTRL | LTK_MOD_ALT | LTK_MOD_SUPER)) == 0) {
@@ -728,12 +730,13 @@ ltk_entry_mouse_leave(ltk_widget *self, ltk_motion_event *event) {
static void
recalc_ideal_size(ltk_entry *entry) {
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
int text_w, text_h;
ltk_text_line_get_size(entry->tl, &text_w, &text_h);
- int bw = ltk_size_to_pixel(theme.border_width, LTK_CAST_WIDGET(entry)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(entry)->last_dpi);
- entry->widget.ideal_w = text_w + bw * 2 + pad * 2;
- entry->widget.ideal_h = text_h + bw * 2 + pad * 2;
+ int bw = ltk_size_to_pixel(theme.border_width, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ self->ideal_w = text_w + bw * 2 + pad * 2;
+ self->ideal_h = text_h + bw * 2 + pad * 2;
}
static void
@@ -744,13 +747,14 @@ ltk_entry_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_size(entry);
}
-ltk_entry *
-ltk_entry_create(ltk_window *window, const char *text) {
+ltk_widget_id
+ltk_entry_create(ltk_widget_id windowid, const char *text) {
ltk_entry *entry = ltk_malloc(sizeof(ltk_entry));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(entry), window, &vtable, 0, 0);
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
+ ltk_widget_id id = ltk_initialize_widget(self, windowid, &vtable, 0, 0);
entry->tl = ltk_text_line_create_const_text_default(
- theme.font, ltk_size_to_pixel(theme.font_size, entry->widget.last_dpi), text, -1
+ theme.font, ltk_size_to_pixel(theme.font_size, self->last_dpi), text, -1
);
recalc_ideal_size(entry);
@@ -761,19 +765,15 @@ ltk_entry_create(ltk_window *window, const char *text) {
entry->pos = entry->sel_start = entry->sel_end = 0;
entry->sel_side = 0;
entry->selecting = 0;
- entry->widget.dirty = 1;
+ self->dirty = 1;
- return entry;
+ return id;
}
static void
ltk_entry_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_entry *entry = LTK_CAST_ENTRY(self);
- if (!entry) {
- ltk_warn("Tried to destroy NULL entry.\n");
- return;
- }
ltk_free(entry->text);
ltk_text_line_destroy(entry->tl);
ltk_free(entry);
diff --git a/src/ltk/entry.h b/src/ltk/entry.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -18,8 +18,6 @@
#define LTK_ENTRY_H
#include <stddef.h>
-#include "config.h"
-#include "graphics.h"
#include "widget.h"
#include "window.h"
#include "text.h"
@@ -38,6 +36,6 @@ typedef struct {
char selecting;
} ltk_entry;
-ltk_entry *ltk_entry_create(ltk_window *window, const char *text);
+ltk_widget_id ltk_entry_create(ltk_widget_id windowid, const char *text);
#endif /* LTK_ENTRY_H */
diff --git a/src/ltk/grid.c b/src/ltk/grid.c
@@ -1,7 +1,7 @@
/* FIXME: sometimes, resizing doesn't work properly when running test.sh */
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -32,37 +32,29 @@
#include "util.h"
#include "grid.h"
#include "graphics.h"
+#include "ltk.h"
-void ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight);
-void ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight);
-ltk_grid *ltk_grid_create(ltk_window *window, int rows, int columns);
-int ltk_grid_add(
- ltk_grid *grid, ltk_widget *widget,
- int row, int column, int row_span, int column_span,
- ltk_sticky_mask sticky
-);
/* just a wrapper around ltk_grid_remove to make types match */
-static int ltk_grid_remove_child(ltk_widget *self, ltk_widget *widget);
-int ltk_grid_remove(ltk_grid *grid, ltk_widget *widget);
+static int ltk_grid_remove_child(ltk_widget *self, ltk_widget_id childid);
static void ltk_grid_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip);
static void ltk_grid_destroy(ltk_widget *self, int shallow);
static void ltk_recalculate_grid(ltk_widget *self);
-static void ltk_grid_child_size_change(ltk_widget *self, ltk_widget *widget);
+static void ltk_grid_child_size_change(ltk_widget *self, ltk_widget_id childid);
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 ltk_widget *ltk_grid_get_child_at_pos(ltk_widget *self, int x, int y);
+static ltk_widget_id ltk_grid_get_child_at_pos(ltk_widget *self, int x, int y);
-static ltk_widget *ltk_grid_prev_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_grid_next_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_grid_first_child(ltk_widget *self);
-static ltk_widget *ltk_grid_last_child(ltk_widget *self);
+static ltk_widget_id ltk_grid_prev_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_grid_next_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_grid_first_child(ltk_widget *self);
+static ltk_widget_id ltk_grid_last_child(ltk_widget *self);
-static ltk_widget *ltk_grid_nearest_child(ltk_widget *self, ltk_rect rect);
-static ltk_widget *ltk_grid_nearest_child_left(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_grid_nearest_child_right(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_grid_nearest_child_above(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_grid_nearest_child_below(ltk_widget *self, ltk_widget *widget);
+static ltk_widget_id ltk_grid_nearest_child(ltk_widget *self, ltk_rect rect);
+static ltk_widget_id ltk_grid_nearest_child_left(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_grid_nearest_child_right(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_grid_nearest_child_above(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_grid_nearest_child_below(ltk_widget *self, ltk_widget_id childid);
static void ltk_grid_recalc_ideal_size(ltk_widget *self);
@@ -98,20 +90,24 @@ static struct ltk_widget_vtable vtable = {
.invalid_signal = LTK_GRID_SIGNAL_INVALID,
};
-/* FIXME: only set "dirty" bit to avoid constand recalculation when
+/* FIXME: only set "dirty" bit to avoid constant recalculation when
setting multiple row/column weights? */
void
-ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight) {
+ltk_grid_set_row_weight(ltk_widget_id gridid, int row, int weight) {
+ ltk_widget *self = ltk_get_widget_from_id(gridid);
+ ltk_grid *grid = LTK_CAST_GRID(self);
ltk_assert(row < grid->rows);
grid->row_weights[row] = weight;
- ltk_recalculate_grid(LTK_CAST_WIDGET(grid));
+ ltk_recalculate_grid(self);
}
void
-ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight) {
+ltk_grid_set_column_weight(ltk_widget_id gridid, int column, int weight) {
+ ltk_widget *self = ltk_get_widget_from_id(gridid);
+ ltk_grid *grid = LTK_CAST_GRID(self);
ltk_assert(column < grid->columns);
grid->column_weights[column] = weight;
- ltk_recalculate_grid(LTK_CAST_WIDGET(grid));
+ ltk_recalculate_grid(self);
}
static void
@@ -120,9 +116,9 @@ ltk_grid_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) {
int i;
ltk_rect real_clip = ltk_rect_intersect((ltk_rect){0, 0, self->lrect.w, self->lrect.h}, clip);
for (i = 0; i < grid->rows * grid->columns; i++) {
- if (!grid->widget_grid[i])
+ if (LTK_WIDGET_ID_IS_NONE(grid->widget_grid[i]))
continue;
- ltk_widget *ptr = grid->widget_grid[i];
+ ltk_widget *ptr = ltk_get_widget_from_id(grid->widget_grid[i]);
int max_w = grid->column_pos[ptr->column + ptr->column_span] - grid->column_pos[ptr->column];
int max_h = grid->row_pos[ptr->row + ptr->row_span] - grid->row_pos[ptr->row];
ltk_rect r = ltk_rect_intersect(
@@ -132,58 +128,51 @@ ltk_grid_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) {
}
}
-ltk_grid *
-ltk_grid_create(ltk_window *window, int rows, int columns) {
+ltk_widget_id
+ltk_grid_create(ltk_widget_id windowid, int rows, int columns) {
ltk_grid *grid = ltk_malloc(sizeof(ltk_grid));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(grid), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(grid), windowid, &vtable, 0, 0);
grid->rows = rows;
grid->columns = columns;
- grid->widget_grid = ltk_malloc(rows * columns * sizeof(ltk_widget));
+ grid->widget_grid = ltk_malloc(rows * columns * sizeof(ltk_widget_id));
grid->row_heights = ltk_malloc(rows * sizeof(int));
- grid->column_widths = ltk_malloc(rows * sizeof(int));
+ grid->column_widths = ltk_malloc(columns * sizeof(int));
grid->row_weights = ltk_malloc(rows * sizeof(int));
grid->column_weights = ltk_malloc(columns * sizeof(int));
/* Positions have one extra for the end */
grid->row_pos = ltk_malloc((rows + 1) * sizeof(int));
grid->column_pos = ltk_malloc((columns + 1) * sizeof(int));
- /* FIXME: wow, that's horrible, this should just use memset */
- int i;
- for (i = 0; i < rows; i++) {
- grid->row_heights[i] = 0;
- grid->row_weights[i] = 0;
- grid->row_pos[i] = 0;
- }
- grid->row_pos[rows] = 0;
- for (i = 0; i < columns; i++) {
- grid->column_widths[i] = 0;
- grid->column_weights[i] = 0;
- grid->column_pos[i] = 0;
- }
- grid->column_pos[columns] = 0;
- for (i = 0; i < rows * columns; i++) {
- grid->widget_grid[i] = NULL;
+
+ memset(grid->row_heights, 0, rows * sizeof(int));
+ memset(grid->row_weights, 0, rows * sizeof(int));
+ memset(grid->row_pos, 0, (rows + 1) * sizeof(int));
+ memset(grid->column_widths, 0, columns * sizeof(int));
+ memset(grid->column_weights, 0, columns * sizeof(int));
+ memset(grid->column_pos, 0, (columns + 1) * sizeof(int));
+ memset(grid->column_pos, 0, (columns + 1) * sizeof(int));
+ for (int i = 0; i < rows * columns; i++) {
+ grid->widget_grid[i] = LTK_WIDGET_ID_NONE;
}
ltk_recalculate_grid(LTK_CAST_WIDGET(grid));
- return grid;
+ return id;
}
static void
ltk_grid_destroy(ltk_widget *self, int shallow) {
ltk_grid *grid = LTK_CAST_GRID(self);
- ltk_widget *ptr;
for (int i = 0; i < grid->rows * grid->columns; i++) {
- if (grid->widget_grid[i]) {
- ptr = grid->widget_grid[i];
- ptr->parent = NULL;
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(grid->widget_grid[i]);
+ if (ptr) {
+ ptr->parent = LTK_WIDGET_ID_NONE;
if (!shallow) {
/* required to avoid freeing a widget multiple times
if row_span or column_span is not 1 */
for (int r = ptr->row; r < ptr->row + ptr->row_span; r++) {
for (int c = ptr->column; c < ptr->column + ptr->column_span; c++) {
- grid->widget_grid[r * grid->columns + c] = NULL;
+ grid->widget_grid[r * grid->columns + c] = LTK_WIDGET_ID_NONE;
}
}
ltk_widget_destroy(ptr, shallow);
@@ -247,7 +236,7 @@ ltk_recalculate_grid(ltk_widget *self) {
int end_column, end_row;
for (i = 0; i < grid->rows; i++) {
for (j = 0; j < grid->columns; j++) {
- ltk_widget *ptr = grid->widget_grid[i * grid->columns + j];
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(grid->widget_grid[i * grid->columns + j]);
if (!ptr || ptr->row != i || ptr->column != j)
continue;
/*orig_width = ptr->lrect.w;
@@ -329,7 +318,7 @@ ltk_grid_recalc_ideal_size(ltk_widget *self) {
if (grid->row_weights[i] == 0)
grid->row_heights[i] = 0;
for (int j = 0; j < grid->columns; j++) {
- ltk_widget *ptr = grid->widget_grid[i * grid->columns + j];
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(grid->widget_grid[i * grid->columns + j]);
if (!ptr || ptr->row != i || ptr->column != j)
continue;
ltk_widget_recalc_ideal_size(ptr);
@@ -352,8 +341,9 @@ ltk_grid_recalc_ideal_size(ltk_widget *self) {
/* FIXME: Maybe add debug stuff to check that grid is actually parent of widget */
static void
-ltk_grid_child_size_change(ltk_widget *self, ltk_widget *widget) {
+ltk_grid_child_size_change(ltk_widget *self, ltk_widget_id widgetid) {
ltk_grid *grid = LTK_CAST_GRID(self);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
short size_changed = 0;
int orig_w = widget->lrect.w;
int orig_h = widget->lrect.h;
@@ -371,10 +361,11 @@ ltk_grid_child_size_change(ltk_widget *self, ltk_widget *widget) {
grid->row_heights[widget->row] = widget->lrect.h;
size_changed = 1;
}
- if (size_changed && self->parent && self->parent->vtable->child_size_change)
- self->parent->vtable->child_size_change(self->parent, LTK_CAST_WIDGET(grid));
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (size_changed && parent && parent->vtable->child_size_change)
+ parent->vtable->child_size_change(parent, self->id);
else
- ltk_recalculate_grid(LTK_CAST_WIDGET(grid));
+ ltk_recalculate_grid(self);
if (widget->lrect.w != orig_w || widget->lrect.h != orig_h)
ltk_widget_resize(widget);
}
@@ -382,11 +373,14 @@ ltk_grid_child_size_change(ltk_widget *self, ltk_widget *widget) {
/* FIXME: Check if widget already exists at position */
int
ltk_grid_add(
- ltk_grid *grid, ltk_widget *widget,
+ ltk_widget_id gridid, ltk_widget_id widgetid,
int row, int column, int row_span, int column_span,
ltk_sticky_mask sticky
) {
- if (widget->parent)
+ ltk_widget *self = ltk_get_widget_from_id(gridid);
+ ltk_grid *grid = LTK_CAST_GRID(self);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ if (!LTK_WIDGET_ID_IS_NONE(widget->parent))
return 1;
/* FIXME: decide which checks should be asserts and which should be error returns */
/* the client-server version of ltk shouldn't abort on errors like these */
@@ -402,34 +396,37 @@ ltk_grid_add(
widget->column_span = column_span;
for (int i = row; i < row + row_span; i++) {
for (int j = column; j < column + column_span; j++) {
- grid->widget_grid[i * grid->columns + j] = widget;
+ grid->widget_grid[i * grid->columns + j] = widgetid;
}
}
- widget->parent = LTK_CAST_WIDGET(grid);
- ltk_grid_child_size_change(LTK_CAST_WIDGET(grid), widget);
- ltk_window_invalidate_widget_rect(LTK_CAST_WIDGET(grid)->window, LTK_CAST_WIDGET(grid));
+ widget->parent = gridid;
+ ltk_grid_child_size_change(self, widgetid);
+ ltk_window_invalidate_widget_rect(self->window, gridid);
return 0;
}
int
-ltk_grid_remove(ltk_grid *grid, ltk_widget *widget) {
- if (widget->parent != LTK_CAST_WIDGET(grid))
+ltk_grid_remove(ltk_widget_id gridid, ltk_widget_id widgetid) {
+ ltk_widget *self = ltk_get_widget_from_id(gridid);
+ ltk_grid *grid = LTK_CAST_GRID(self);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ if (!LTK_WIDGET_ID_EQUAL(widget->parent, gridid))
return 1;
- widget->parent = NULL;
+ widget->parent = LTK_WIDGET_ID_NONE;
for (int i = widget->row; i < widget->row + widget->row_span; i++) {
for (int j = widget->column; j < widget->column + widget->column_span; j++) {
- grid->widget_grid[i * grid->columns + j] = NULL;
+ grid->widget_grid[i * grid->columns + j] = LTK_WIDGET_ID_NONE;
}
}
- ltk_window_invalidate_widget_rect(LTK_CAST_WIDGET(grid)->window, LTK_CAST_WIDGET(grid));
+ ltk_window_invalidate_widget_rect(self->window, gridid);
return 0;
}
static int
-ltk_grid_remove_child(ltk_widget *self, ltk_widget *widget) {
- return ltk_grid_remove(LTK_CAST_GRID(self), widget);
+ltk_grid_remove_child(ltk_widget *self, ltk_widget_id widgetid) {
+ return ltk_grid_remove(self->id, widgetid);
}
static int
@@ -455,17 +452,18 @@ ltk_grid_find_nearest_row(ltk_grid *grid, int y) {
}
/* FIXME: maybe come up with a more efficient method */
-static ltk_widget *
+static ltk_widget_id
ltk_grid_nearest_child(ltk_widget *self, ltk_rect rect) {
ltk_grid *grid = LTK_CAST_GRID(self);
- ltk_widget *minw = NULL;
+ ltk_widget_id minw = LTK_WIDGET_ID_NONE;
int min_dist = INT_MAX;
/* FIXME: rows and columns shouldn't be int */
for (size_t i = 0; i < (size_t)(grid->rows * grid->columns); i++) {
- if (!grid->widget_grid[i])
+ if (LTK_WIDGET_ID_IS_NONE(grid->widget_grid[i]))
continue;
/* FIXME: this checks widgets with row/columnspan > 1 multiple times */
- ltk_rect r = grid->widget_grid[i]->lrect;
+ ltk_widget *widget = ltk_get_widget_from_id(grid->widget_grid[i]);
+ ltk_rect r = widget->lrect;
int dist = ltk_rect_fakedist(rect, r);
if (dist < min_dist) {
min_dist = dist;
@@ -476,109 +474,112 @@ ltk_grid_nearest_child(ltk_widget *self, ltk_rect rect) {
}
/* FIXME: assertions to check that widget row/column are legal */
-static ltk_widget *
-ltk_grid_nearest_child_left(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_grid_nearest_child_left(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
- unsigned int col = widget->column;
- ltk_widget *cur = NULL;
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ unsigned int col = child->column;
while (col-- > 0) {
- cur = grid->widget_grid[widget->row * grid->columns + col];
- if (cur && cur != widget)
+ ltk_widget_id cur = grid->widget_grid[child->row * grid->columns + col];
+ if (!LTK_WIDGET_ID_IS_NONE(cur) && !LTK_WIDGET_ID_EQUAL(cur, childid))
return cur;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_grid_nearest_child_right(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_grid_nearest_child_right(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
- ltk_widget *cur = NULL;
- for (int col = widget->column + 1; col < grid->columns; col++) {
- cur = grid->widget_grid[widget->row * grid->columns + col];
- if (cur && cur != widget)
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ for (int col = child->column + 1; col < grid->columns; col++) {
+ ltk_widget_id cur = grid->widget_grid[child->row * grid->columns + col];
+ if (!LTK_WIDGET_ID_IS_NONE(cur) && !LTK_WIDGET_ID_EQUAL(cur, childid))
return cur;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
/* FIXME: maybe these should also fall back to widgets in other columns if those
exist but no widgets exist in the same column */
-static ltk_widget *
-ltk_grid_nearest_child_above(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_grid_nearest_child_above(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
- unsigned int row = widget->row;
- ltk_widget *cur = NULL;
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ unsigned int row = child->row;
while (row-- > 0) {
- cur = grid->widget_grid[row * grid->columns + widget->column];
- if (cur && cur != widget)
+ ltk_widget_id cur = grid->widget_grid[row * grid->columns + child->column];
+ if (!LTK_WIDGET_ID_IS_NONE(cur) && !LTK_WIDGET_ID_EQUAL(cur, childid))
return cur;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_grid_nearest_child_below(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_grid_nearest_child_below(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
- ltk_widget *cur = NULL;
- for (int row = widget->row + 1; row < grid->rows; row++) {
- cur = grid->widget_grid[row * grid->columns + widget->column];
- if (cur && cur != widget)
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ for (int row = child->row + 1; row < grid->rows; row++) {
+ ltk_widget_id cur = grid->widget_grid[row * grid->columns + child->column];
+ if (!LTK_WIDGET_ID_IS_NONE(cur) && !LTK_WIDGET_ID_EQUAL(cur, childid))
return cur;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_grid_get_child_at_pos(ltk_widget *self, int x, int y) {
ltk_grid *grid = LTK_CAST_GRID(self);
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];
+ return LTK_WIDGET_ID_NONE;
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(grid->widget_grid[row * grid->columns + column]);
if (ptr && ltk_collide_rect(ptr->crect, x, y))
- return ptr;
- return NULL;
+ return ptr->id;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_grid_prev_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_grid_prev_child(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
+ ltk_widget *child = ltk_get_widget_from_id(childid);
unsigned int start = child->row * grid->columns + child->column;
while (start-- > 0) {
- if (grid->widget_grid[start])
+ if (!LTK_WIDGET_ID_IS_NONE(grid->widget_grid[start]))
return grid->widget_grid[start];
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_grid_next_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_grid_next_child(ltk_widget *self, ltk_widget_id childid) {
ltk_grid *grid = LTK_CAST_GRID(self);
+ ltk_widget *child = ltk_get_widget_from_id(childid);
unsigned int start = child->row * grid->columns + child->column;
while (++start < (unsigned int)(grid->rows * grid->columns)) {
- if (grid->widget_grid[start] && grid->widget_grid[start] != child)
+ if (!LTK_WIDGET_ID_IS_NONE(grid->widget_grid[start]) &&
+ !LTK_WIDGET_ID_EQUAL(grid->widget_grid[start], childid))
return grid->widget_grid[start];
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_grid_first_child(ltk_widget *self) {
ltk_grid *grid = LTK_CAST_GRID(self);
for (unsigned int i = 0; i < (unsigned int)(grid->rows * grid->columns); i++) {
- if (grid->widget_grid[i])
+ if (!LTK_WIDGET_ID_IS_NONE(grid->widget_grid[i]))
return grid->widget_grid[i];
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_grid_last_child(ltk_widget *self) {
ltk_grid *grid = LTK_CAST_GRID(self);
for (unsigned int i = grid->rows * grid->columns; i-- > 0;) {
- if (grid->widget_grid[i])
+ if (!LTK_WIDGET_ID_IS_NONE(grid->widget_grid[i]))
return grid->widget_grid[i];
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
diff --git a/src/ltk/grid.h b/src/ltk/grid.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -22,12 +22,10 @@
#define LTK_GRID_SIGNAL_INVALID -1
-/*
- * Struct to represent a grid widget.
- */
+/* FIXME: maybe changes types to unsigned int */
typedef struct {
ltk_widget widget;
- ltk_widget **widget_grid;
+ ltk_widget_id *widget_grid;
int *row_heights;
int *column_widths;
int *row_weights;
@@ -39,14 +37,14 @@ typedef struct {
} ltk_grid;
/* FIXME: proper error handling for different errors in add/remove */
-ltk_grid *ltk_grid_create(ltk_window *window, int rows, int columns);
+ltk_widget_id ltk_grid_create(ltk_widget_id windowid, int rows, int columns);
int ltk_grid_add(
- ltk_grid *grid, ltk_widget *widget,
+ ltk_widget_id gridid, ltk_widget_id widget,
int row, int column, int row_span, int column_span,
ltk_sticky_mask sticky
);
-int ltk_grid_remove(ltk_grid *grid, ltk_widget *widget);
-void ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight);
-void ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight);
+int ltk_grid_remove(ltk_widget_id gridid, ltk_widget_id widget);
+void ltk_grid_set_row_weight(ltk_widget_id gridid, int row, int weight);
+void ltk_grid_set_column_weight(ltk_widget_id gridid, int column, int weight);
#endif /* LTK_GRID_H */
diff --git a/src/ltk/image_widget.c b/src/ltk/image_widget.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2023-2026 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
@@ -71,28 +71,23 @@ ltk_image_widget_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, lt
);
}
-ltk_image_widget *
-ltk_image_widget_create(ltk_window *window, ltk_image *img) {
+ltk_widget_id
+ltk_image_widget_create(ltk_widget_id windowid, ltk_image *img) {
ltk_image_widget *imw = ltk_malloc(sizeof(ltk_image_widget));
imw->img = img;
int w = ltk_image_get_width(imw->img);
int h = ltk_image_get_height(imw->img);
- ltk_fill_widget_defaults(&imw->widget, window, &vtable, w, h);
- imw->widget.ideal_w = w;
- imw->widget.ideal_h = h;
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(imw), windowid, &vtable, w, h);
+ LTK_CAST_WIDGET(imw)->ideal_w = w;
+ LTK_CAST_WIDGET(imw)->ideal_h = h;
- return imw;
+ return id;
}
static void
ltk_image_widget_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_image_widget *img = LTK_CAST_IMAGE_WIDGET(self);
- /* FIXME: make warnings like this consistent across widgets */
- if (!img) {
- ltk_warn("Tried to destroy NULL image widget.\n");
- return;
- }
ltk_image_destroy(img->img);
ltk_free(img);
}
diff --git a/src/ltk/image_widget.h b/src/ltk/image_widget.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2023-2026 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
@@ -27,6 +27,6 @@ typedef struct {
} ltk_image_widget;
/* WARNING: takes over img! */
-ltk_image_widget *ltk_image_widget_create(ltk_window *window, ltk_image *img);
+ltk_widget_id ltk_image_widget_create(ltk_widget_id windowid, ltk_image *img);
#endif /* LTK_IMAGE_WIDGET_H */
diff --git a/src/ltk/label.c b/src/ltk/label.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -113,27 +113,23 @@ ltk_label_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_size(label);
}
-ltk_label *
-ltk_label_create(ltk_window *window, const char *text) {
+ltk_widget_id
+ltk_label_create(ltk_widget_id windowid, const char *text) {
ltk_label *label = ltk_malloc(sizeof(ltk_label));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(label), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(label), windowid, &vtable, 0, 0);
label->tl = ltk_text_line_create_const_text_default(
theme.font, ltk_size_to_pixel(theme.font_size, label->widget.last_dpi), text, -1
);
recalc_ideal_size(label);
- return label;
+ return id;
}
static void
ltk_label_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_label *label = LTK_CAST_LABEL(self);
- if (!label) {
- ltk_warn("Tried to destroy NULL label.\n");
- return;
- }
ltk_text_line_destroy(label->tl);
ltk_free(label);
}
diff --git a/src/ltk/label.h b/src/ltk/label.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -28,6 +28,6 @@ typedef struct {
ltk_text_line *tl;
} ltk_label;
-ltk_label *ltk_label_create(ltk_window *window, const char *text);
+ltk_widget_id ltk_label_create(ltk_widget_id windowid, const char *text);
#endif /* LTK_LABEL_H */
diff --git a/src/ltk/ltk.c b/src/ltk/ltk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -45,44 +45,65 @@
#include "widget_internal.h"
typedef struct {
- ltk_widget *caller;
+ ltk_widget_id caller;
char *infile;
char *outfile;
int pid;
} ltk_cmdinfo;
+typedef struct {
+ void (*callback)(ltk_callback_arg data);
+ ltk_callback_arg data;
+ struct timespec repeat;
+ struct timespec remaining;
+ int id;
+} ltk_timer;
+
LTK_ARRAY_INIT_DECL_STATIC(window, ltk_window *)
LTK_ARRAY_INIT_IMPL_STATIC(window, ltk_window *)
LTK_ARRAY_INIT_DECL_STATIC(rwindow, ltk_renderwindow *)
LTK_ARRAY_INIT_IMPL_STATIC(rwindow, ltk_renderwindow *)
LTK_ARRAY_INIT_DECL_STATIC(cmdinfo, ltk_cmdinfo)
LTK_ARRAY_INIT_IMPL_STATIC(cmdinfo, ltk_cmdinfo)
+LTK_ARRAY_INIT_DECL_STATIC(widget, ltk_widget *)
+LTK_ARRAY_INIT_IMPL_STATIC(widget, ltk_widget *)
+LTK_ARRAY_INIT_DECL_STATIC(gen_num, ltk_widget_id_int_type)
+LTK_ARRAY_INIT_IMPL_STATIC(gen_num, ltk_widget_id_int_type)
+LTK_ARRAY_INIT_DECL_STATIC(timer, ltk_timer)
+LTK_ARRAY_INIT_IMPL_STATIC(timer, ltk_timer)
static struct {
ltk_renderdata *renderdata;
ltk_text_context *text_context;
ltk_clipboard *clipboard;
+ /* FIXME: For the current event mechanism to work properly, the windows need to be stored
+ in a separate array here in addition to the general widgets array that they are stored
+ in anyways. Maybe fix this so they are only stored in one place? */
ltk_array(window) *windows;
ltk_array(rwindow) *rwindows;
/* PID of external command called e.g. by text widget to edit text.
ON exit, cmd_caller->vtable->cmd_return is called with the text
the external command wrote to a file. */
/*FIXME: this needs to be checked whenever a widget is destroyed!*/
+ /* -> actually, if widget ID system is implemented, this can just check if ID is still valid on command return
+ -> although, that could cause weirdness in extreme edge cases when the generation number of the ID has
+ wrapped around so there is actually a valid ID at that position - would at least need to check if cmd_return is actually valid for widget */
ltk_array(cmdinfo) *cmds;
+ ltk_array(timer) *timers;
+ /* widgets is an array of all widgets, with entries of deleted widgets being NULL until
+ they are reused. cur_widget_clock_pos contains the index from which a search for an
+ empty slot should start. If no empty slot is found and the size of the array is less
+ than LTK_MAX_WIDGET_IDX, the array size is increased. In order to give an error when
+ a widget that has been deleted is requested and the slot has already been filled with
+ a new widget, each widget ID contains a generation number. gen_nums contains the
+ current generation number for each slot, which is increased whenever a widget is added
+ to the slot. A valid widget ID can never have a generation number of 0 (this is so
+ there's a special widget ID that can be used to signal that the ID is invalid). */
+ ltk_array(widget) *widgets;
+ ltk_array(gen_num) *gen_nums;
+ ltk_widget_id_int_type cur_widget_clock_pos;
size_t cur_kbd;
-} shared_data = {NULL, NULL, NULL, NULL, NULL, NULL, 0};
-
-typedef struct {
- void (*callback)(ltk_callback_arg data);
- ltk_callback_arg data;
- struct timespec repeat;
- struct timespec remaining;
- int id;
-} ltk_timer;
-
-static ltk_timer *timers = NULL;
-static size_t timers_num = 0;
-static size_t timers_alloc = 0;
+} shared_data = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0};
static void ltk_handle_event(ltk_event *event);
@@ -156,9 +177,70 @@ ltk_init(void) {
shared_data.windows = ltk_array_create(window, 1);
shared_data.rwindows = ltk_array_create(rwindow, 1);
shared_data.cmds = ltk_array_create(cmdinfo, 1);
+ shared_data.widgets = ltk_array_create(widget, 1);
+ shared_data.gen_nums = ltk_array_create(gen_num, 1);
+ shared_data.timers = ltk_array_create(timer, 1);
return 0; /* FIXME: or maybe 1? */
}
+ltk_widget *
+ltk_get_widget_from_id(ltk_widget_id id) {
+ ltk_assert(id.gen > 0);
+ ltk_assert(id.idx < ltk_array_len(shared_data.widgets));
+ ltk_widget *widget = ltk_array_get(shared_data.widgets, id.idx);
+ ltk_assert(widget != NULL);
+ ltk_assert(widget->id.gen == id.gen);
+ return widget;
+}
+
+ltk_widget *
+ltk_get_widget_or_null_from_id(ltk_widget_id id) {
+ return id.gen == 0 ? NULL : ltk_get_widget_from_id(id);
+}
+
+ltk_widget *
+ltk_get_widget_or_null_from_id_nofail(ltk_widget_id id) {
+ if (id.gen == 0 || id.idx >= ltk_array_len(shared_data.widgets))
+ return NULL;
+ ltk_widget *widget = ltk_array_get(shared_data.widgets, id.idx);
+ if (!widget || widget->id.gen != id.gen)
+ return NULL;
+ return widget;
+}
+
+ltk_widget_id
+ltk_store_widget(ltk_widget *widget) {
+ for (size_t i = 0; i < ltk_array_len(shared_data.widgets); ++i) {
+ if (!ltk_array_get(shared_data.widgets, shared_data.cur_widget_clock_pos)) {
+ size_t idx = shared_data.cur_widget_clock_pos;
+ ltk_array_get(shared_data.widgets, idx) = widget;
+ ltk_array_get(shared_data.gen_nums, idx)++;
+ shared_data.cur_widget_clock_pos++;
+ shared_data.cur_widget_clock_pos %= ltk_array_len(shared_data.widgets);
+ return (ltk_widget_id){idx, ltk_array_get(shared_data.gen_nums, idx)};
+ }
+ shared_data.cur_widget_clock_pos++;
+ shared_data.cur_widget_clock_pos %= ltk_array_len(shared_data.widgets);
+ }
+ size_t idx = ltk_array_len(shared_data.widgets);
+ ltk_assert(idx <= LTK_MAX_WIDGET_IDX);
+ ltk_array_append(widget, shared_data.widgets, widget);
+ ltk_array_len_to_capacity(widget, shared_data.widgets, NULL);
+ if (ltk_array_len(shared_data.widgets) > (size_t)LTK_MAX_WIDGET_IDX + 1U)
+ ltk_array_resize(widget, shared_data.widgets, (size_t)LTK_MAX_WIDGET_IDX + 1U, NULL);
+ ltk_array_resize(gen_num, shared_data.gen_nums, ltk_array_len(shared_data.widgets), 0);
+ ltk_array_get(shared_data.gen_nums, idx) = 1;
+ shared_data.cur_widget_clock_pos = idx + 1;
+ shared_data.cur_widget_clock_pos %= ltk_array_len(shared_data.widgets);
+ return (ltk_widget_id){idx, 1};
+}
+
+void
+ltk_clear_widget_slot(ltk_widget_id id) {
+ ltk_assert(id.idx < ltk_array_len(shared_data.widgets));
+ ltk_array_get(shared_data.widgets, id.idx) = NULL;
+}
+
static struct {
struct timespec last;
struct timespec lasttimer;
@@ -197,11 +279,11 @@ ltk_mainloop_step(int limit_framerate) {
for (size_t i = 0; i < ltk_array_len(shared_data.cmds); i++) {
info = &(ltk_array_get(shared_data.cmds, i));
if (info->pid == pid) {
- /* FIXME: actually NULL this when widgets are destroyed */
- if (!info->caller) {
+ ltk_widget *caller = ltk_get_widget_or_null_from_id_nofail(info->caller);
+ if (!caller) {
ltk_warn("Widget disappeared while text was being edited in external program\n");
/* FIXME: call overwritten cmd_return! */
- } else if (info->caller->vtable->cmd_return) {
+ } else if (caller->vtable->cmd_return) {
size_t file_len = 0;
char *errstr = NULL;
char *filename = info->outfile ? info->outfile : info->infile;
@@ -209,7 +291,7 @@ ltk_mainloop_step(int limit_framerate) {
if (!contents) {
ltk_warn("Unable to read file '%s' written by external command: %s\n", filename, errstr);
} else {
- info->caller->vtable->cmd_return(info->caller, contents, file_len);
+ caller->vtable->cmd_return(caller, contents, file_len);
ltk_free0(contents);
}
}
@@ -238,16 +320,17 @@ ltk_mainloop_step(int limit_framerate) {
/* Note: it should be safe to give the same pointer as the first and
last argument, as long as ltk_timespecsub/add isn't changed incompatibly */
size_t i = 0;
- while (i < timers_num) {
- ltk_timespecsub(&timers[i].remaining, &elapsed, &timers[i].remaining);
- if (timers[i].remaining.tv_sec < 0 ||
- (timers[i].remaining.tv_sec == 0 && timers[i].remaining.tv_nsec == 0)) {
- timers[i].callback(timers[i].data);
- if (timers[i].repeat.tv_sec == 0 && timers[i].repeat.tv_nsec == 0) {
+ while (i < ltk_array_len(shared_data.timers)) {
+ ltk_timer *cur_timer = <k_array_get(shared_data.timers, i);
+ ltk_timespecsub(&cur_timer->remaining, &elapsed, &cur_timer->remaining);
+ if (cur_timer->remaining.tv_sec < 0 ||
+ (cur_timer->remaining.tv_sec == 0 && cur_timer->remaining.tv_nsec == 0)) {
+ cur_timer->callback(cur_timer->data);
+ if (cur_timer->repeat.tv_sec == 0 && cur_timer->repeat.tv_nsec == 0) {
/* remove timer because it has no repeat */
- memmove(timers + i, timers + i + 1, sizeof(ltk_timer) * (timers_num - i - 1));
+ ltk_array_delete(timer, shared_data.timers, i, 1);
} else {
- ltk_timespecadd(&timers[i].remaining, &timers[i].repeat, &timers[i].remaining);
+ ltk_timespecadd(&cur_timer->remaining, &cur_timer->repeat, &cur_timer->remaining);
i++;
}
} else {
@@ -256,8 +339,8 @@ ltk_mainloop_step(int limit_framerate) {
}
mainloop_data.lasttimer = now;
- for (size_t i = 0; i < shared_data.windows->len; i++) {
- ltk_window *window = shared_data.windows->buf[i];
+ for (size_t i = 0; i < ltk_array_len(shared_data.windows); i++) {
+ ltk_window *window = ltk_array_get(shared_data.windows, i);
if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) {
ltk_widget_draw(LTK_CAST_WIDGET(window), NULL, 0, 0, (ltk_rect){0, 0, 0, 0});
}
@@ -310,17 +393,31 @@ ltk_deinit(void) {
ltk_array_destroy(cmdinfo, shared_data.cmds);
}
shared_data.cmds = NULL;
- if (shared_data.windows) {
- for (size_t i = 0; i < ltk_array_len(shared_data.windows); i++) {
- ltk_window *window = ltk_array_get(shared_data.windows, i);
- ltk_widget_destroy(LTK_CAST_WIDGET(window), 0);
- }
- ltk_array_destroy(window, shared_data.windows);
+ /* FIXME: maybe special destroy function for cleanup at end that doesn't emit signals, etc. */
+ for (size_t i = 0; i < ltk_array_len(shared_data.windows); i++) {
+ ltk_window *window = ltk_array_get(shared_data.windows, i);
+ ltk_widget_destroy(LTK_CAST_WIDGET(window), 1);
}
+ ltk_array_destroy(window, shared_data.windows);
shared_data.windows = NULL;
- if (shared_data.rwindows)
- ltk_array_destroy(rwindow, shared_data.rwindows);
+ ltk_array_destroy(rwindow, shared_data.rwindows);
shared_data.rwindows = NULL;
+ for (size_t i = 0; i < ltk_array_len(shared_data.widgets); i++) {
+ /* FIXME: not sure about this warning - the idea is to do a deep destroy of the
+ windows above, then warn if there are any widgets that were not destroyed
+ (i.e. not referenced in a window), but I'm not sure how useful that is */
+ ltk_widget *widget = ltk_array_get(shared_data.widgets, i);
+ if (widget) {
+ ltk_warn("Widget not contained within window still stored at index %zu in widget array on exit.\n", i);
+ ltk_widget_destroy(widget, 0);
+ }
+ }
+ ltk_array_destroy(widget, shared_data.widgets);
+ shared_data.widgets = NULL;
+ ltk_array_destroy(gen_num, shared_data.gen_nums);
+ shared_data.gen_nums = NULL;
+ ltk_array_destroy(timer, shared_data.timers);
+ shared_data.timers = NULL;
for (size_t i = 0; i < LENGTH(widget_funcs); i++) {
if (widget_funcs[i].cleanup)
widget_funcs[i].cleanup();
@@ -338,7 +435,7 @@ ltk_deinit(void) {
}
/* FIXME: check everywhere if initialized already */
-ltk_window *
+ltk_widget_id
ltk_window_create(const char *title, int x, int y, unsigned int w, unsigned int h) {
/* FIXME: more asserts, or maybe global "initialized" flag */
ltk_assert(shared_data.renderdata != NULL);
@@ -347,7 +444,7 @@ ltk_window_create(const char *title, int x, int y, unsigned int w, unsigned int
ltk_window *window = ltk_window_create_intern(shared_data.renderdata, title, x, y, w, h);
ltk_array_append(window, shared_data.windows, window);
ltk_array_append(rwindow, shared_data.rwindows, window->renderwindow);
- return window;
+ return LTK_CAST_WIDGET(window)->id;
}
void
@@ -372,24 +469,11 @@ ltk_get_clipboard(void) {
}
/* FIXME: optimize timer handling - maybe also a sort of priority queue */
-/* FIXME: JUST USE A GENERIC DYNAMIC ARRAY ALREADY!!!!! */
void
ltk_unregister_timer(int timer_id) {
- for (size_t i = 0; i < timers_num; i++) {
- if (timers[i].id == timer_id) {
- memmove(
- timers + i,
- timers + i + 1,
- sizeof(ltk_timer) * (timers_num - i - 1)
- );
- timers_num--;
- size_t sz = ideal_array_size(timers_alloc, timers_num);
- if (sz != timers_alloc) {
- timers_alloc = sz;
- timers = ltk_reallocarray(
- timers, sz, sizeof(ltk_timer)
- );
- }
+ for (size_t i = 0; i < ltk_array_len(shared_data.timers); i++) {
+ if (ltk_array_get(shared_data.timers, i).id == timer_id) {
+ ltk_array_delete(timer, shared_data.timers, i, 1);
return;
}
}
@@ -402,27 +486,22 @@ ltk_register_timer(long first, long repeat, void (*callback)(ltk_callback_arg da
first = 0;
if (repeat < 0)
repeat = 0;
- if (timers_num == timers_alloc) {
- timers_alloc = ideal_array_size(timers_alloc, timers_num + 1);
- timers = ltk_reallocarray(
- timers, timers_alloc, sizeof(ltk_timer)
- );
- }
/* FIXME: better finding of id */
/* FIXME: maybe store sorted by id */
int id = 0;
- for (size_t i = 0; i < timers_num; i++) {
- if (timers[i].id >= id)
- id = timers[i].id + 1;
+ for (size_t i = 0; i < ltk_array_len(shared_data.timers); i++) {
+ if (ltk_array_get(shared_data.timers, i).id >= id)
+ id = ltk_array_get(shared_data.timers, i).id + 1;
}
- ltk_timer *t = &timers[timers_num++];
- t->callback = callback;
- t->data = data;
- t->repeat.tv_sec = repeat / 1000;
- t->repeat.tv_nsec = (repeat % 1000) * 1000;
- t->remaining.tv_sec = first / 1000;
- t->remaining.tv_nsec = (first % 1000) * 1000;
- t->id = id;
+ ltk_timer t;
+ t.callback = callback;
+ t.data = data;
+ t.repeat.tv_sec = repeat / 1000;
+ t.repeat.tv_nsec = (repeat % 1000) * 1000;
+ t.remaining.tv_sec = first / 1000;
+ t.remaining.tv_nsec = (first % 1000) * 1000;
+ t.id = id;
+ ltk_array_append(timer, shared_data.timers, t);
return id;
}
@@ -435,11 +514,11 @@ str_free_helper(char *elem) {
}
int
-ltk_call_cmd(ltk_widget *caller, ltk_array(cmd) *cmd, const char *text, size_t textlen) {
+ltk_call_cmd(ltk_widget_id callerid, ltk_array(cmd) *cmd, const char *text, size_t textlen) {
/* FIXME: maybe support stdin/stdout without temporary files by just piping directly */
/* FIXME: support environment variable $TMPDIR */
ltk_cmdinfo info = {
- .caller = NULL, .infile = NULL, .outfile = NULL, .pid = -1
+ .caller = LTK_WIDGET_ID_NONE, .infile = NULL, .outfile = NULL, .pid = -1
};
ltk_array(str) *cmdstr = ltk_array_create(str, 4);
txtbuf *tmpbuf = txtbuf_new();
@@ -511,7 +590,7 @@ ltk_call_cmd(ltk_widget *caller, ltk_array(cmd) *cmd, const char *text, size_t t
goto error;
}
}
- ltk_array_append(str, cmdstr, NULL); /* necessary for execve */
+ ltk_array_append(str, cmdstr, NULL); /* necessary for execvp */
txtbuf_destroy(tmpbuf);
tmpbuf = NULL;
@@ -535,7 +614,7 @@ ltk_call_cmd(ltk_widget *caller, ltk_array(cmd) *cmd, const char *text, size_t t
ltk_array_destroy_deep(str, cmdstr, &str_free_helper);
info.pid = fret;
- info.caller = caller;
+ info.caller = callerid;
ltk_array_append(cmdinfo, shared_data.cmds, info);
if (infd != -1)
@@ -573,7 +652,10 @@ ltk_handle_event(ltk_event *event) {
shared_data.cur_kbd = kbd_idx;
} else {
if (event->any.window_id < ltk_array_len(shared_data.windows)) {
- ltk_window_handle_event(ltk_array_get(shared_data.windows, event->any.window_id), event);
+ ltk_window *window = ltk_array_get(shared_data.windows, event->any.window_id);
+ ltk_window_handle_event(LTK_CAST_WIDGET(window)->id, event);
+ } else {
+ ltk_warn("Invalid window ID %zu in event.", event->any.window_id);
}
}
}
diff --git a/src/ltk/ltk.h b/src/ltk/ltk.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -40,8 +40,26 @@ void ltk_unregister_timer(int timer_id);
int ltk_register_timer(long first, long repeat, void (*callback)(ltk_callback_arg data), ltk_callback_arg data);
/* These are here so they can be added to the global array in ltk.c */
-ltk_window *ltk_window_create(const char *title, int x, int y, unsigned int w, unsigned int h);
-void ltk_window_destroy(ltk_widget *self, int shallow);
+/* (I guess these could be moved to window.h, I just like to have the declarations in the .h file
+ corresponding to the .c file containing the definitions) */
+ltk_widget_id ltk_window_create(const char *title, int x, int y, unsigned int w, unsigned int h);
+
+/* Get widget from ID. An invalid ID or an ID of a deleted widget both are fatal errors
+ (unless the generation number wrapped around and the slot has been reused, causing a
+ different widget to be returned). */
+ltk_widget *ltk_get_widget_from_id(ltk_widget_id id);
+/* Same as above, but NULL is returned when the widget ID is LTK_WIDGET_ID_NONE.
+ If there is some other problem with the ID, a fatal error is still caused. */
+ltk_widget *ltk_get_widget_or_null_from_id(ltk_widget_id id);
+/* Same as above, but NULL is always returned when a fatal error would otherwise be caused. */
+ltk_widget *ltk_get_widget_or_null_from_id_nofail(ltk_widget_id id);
+/* Store a widget and return its ID. If no more widgets can be stored, a fatal
+ error occurs. */
+ltk_widget_id ltk_store_widget(ltk_widget *widget);
+/* Remove a widget that has previously been stored. If no widget exists for the given
+ ID, a fatal error is caused. */
+void ltk_clear_widget_slot(ltk_widget_id id);
+/* FIXME}; move store and clear functions to private header */
/* convenience function to use the default text context */
ltk_text_line *ltk_text_line_create_default(const char *font, int font_size, char *text, int take_over_text, int width);
diff --git a/src/ltk/menu.c b/src/ltk/menu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -95,7 +95,7 @@ static void ltk_menu_get_max_scroll_offset(ltk_menu *menu, int *x_ret, int *y_re
static void ltk_menu_scroll(ltk_menu *menu, char t, char b, char l, char r, int step);
static void ltk_menu_scroll_callback(ltk_callback_arg 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 ltk_widget_id 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_scroll(ltk_widget *self, ltk_scroll_event *event);
static void ltk_menu_hide(ltk_widget *self);
@@ -104,40 +104,39 @@ static void unpopup_active_entry(ltk_menuentry *e);
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 void shrink_entries(ltk_menu *menu);
static void ltk_menu_destroy(ltk_widget *self, int shallow);
-static ltk_menu *ltk_menu_create_base(ltk_window *window, int is_submenu);
+static ltk_widget_id ltk_menu_create_base(ltk_widget_id windowid, int is_submenu);
-static int ltk_menu_remove_child(ltk_widget *self, ltk_widget *widget);
-static int ltk_menuentry_remove_child(ltk_widget *self, ltk_widget *widget);
+static int ltk_menu_remove_child(ltk_widget *self, ltk_widget_id childid);
+static int ltk_menuentry_remove_child(ltk_widget *self, ltk_widget_id childid);
-static ltk_widget *ltk_menu_nearest_child(ltk_widget *self, ltk_rect rect);
-static ltk_widget *ltk_menu_nearest_child_left(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_menu_nearest_child_right(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_menu_nearest_child_above(ltk_widget *self, ltk_widget *widget);
-static ltk_widget *ltk_menu_nearest_child_below(ltk_widget *self, ltk_widget *widget);
+static ltk_widget_id ltk_menu_nearest_child(ltk_widget *self, ltk_rect rect);
+static ltk_widget_id ltk_menu_nearest_child_left(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_menu_nearest_child_right(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_menu_nearest_child_above(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_menu_nearest_child_below(ltk_widget *self, ltk_widget_id childid);
static void ltk_menuentry_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip);
static void ltk_menuentry_destroy(ltk_widget *self, int shallow);
static void ltk_menuentry_change_state(ltk_widget *self, ltk_widget_state old_state);
static int ltk_menuentry_release(ltk_widget *self);
-static ltk_widget *ltk_menu_prev_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_menu_next_child(ltk_widget *self, ltk_widget *child);
-static ltk_widget *ltk_menu_first_child(ltk_widget *self);
-static ltk_widget *ltk_menu_last_child(ltk_widget *self);
-static ltk_widget *ltk_menuentry_get_child(ltk_widget *self);
+static ltk_widget_id ltk_menu_prev_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_menu_next_child(ltk_widget *self, ltk_widget_id childid);
+static ltk_widget_id ltk_menu_first_child(ltk_widget *self);
+static ltk_widget_id ltk_menu_last_child(ltk_widget *self);
+static ltk_widget_id ltk_menuentry_get_child(ltk_widget *self);
/* FIXME: these functions are named really badly */
-static void recalc_ideal_menu_size_with_notification(ltk_widget *self, ltk_widget *widget);
+static void recalc_ideal_menu_size_with_notification(ltk_widget *self, ltk_widget_id childid);
static void recalc_ideal_menu_size(ltk_menu *menu);
static void ltk_menuentry_set_font(ltk_menuentry *entry);
static void ltk_menu_recalc_ideal_size(ltk_widget *self);
static void ltk_menuentry_recalc_ideal_size(ltk_widget *self);
static void ltk_menuentry_recalc_ideal_size_with_notification(ltk_menuentry *entry);
-#define IN_SUBMENU(e) (e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)e->widget.parent)->is_submenu)
+#define IS_SUBMENU(parent) (parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENU && (LTK_CAST_MENU(parent))->is_submenu)
static struct ltk_widget_vtable vtable = {
.key_press = NULL,
@@ -295,34 +294,38 @@ ltk_submenuentry_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) {
static void
ltk_menuentry_change_state(ltk_widget *self, ltk_widget_state old_state) {
ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- int in_submenu = IN_SUBMENU(e);
- int submenus_opened = self->parent && self->parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)self->parent)->popup_submenus;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_menu *parentm = parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENU ? LTK_CAST_MENU(parent) : NULL;
+ ltk_widget *submenu = ltk_get_widget_or_null_from_id(e->submenu);
+ int in_submenu = IS_SUBMENU(parent);
+ int submenus_opened = parentm && parentm->popup_submenus;
if (!(self->state & (LTK_ACTIVE | LTK_PRESSED))) {
/* Note: This only has to take care of the submenu that is the direct child
of e because ltk_window_set_active_widget already calls change_state for
the whole hierarchy */
unpopup_active_entry(e);
} else if ((self->state & LTK_PRESSED) && !(old_state & LTK_PRESSED) && submenus_opened) {
- ((ltk_menu *)self->parent)->popup_submenus = 0;
+ parentm->popup_submenus = 0;
} else if (((self->state & LTK_PRESSED) ||
((self->state & LTK_ACTIVE) && (in_submenu || submenus_opened))) &&
- e->submenu && e->submenu->widget.hidden) {
+ submenu && submenu->hidden) {
popup_active_menu(e);
- if (self->parent && self->parent->vtable->type == LTK_WIDGET_MENU)
- ((ltk_menu *)self->parent)->popup_submenus = 1;
+ if (parentm)
+ parentm->popup_submenus = 1;
}
}
-static ltk_widget *
+static ltk_widget_id
ltk_menuentry_get_child(ltk_widget *self) {
ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- if (e->submenu && !e->submenu->widget.hidden)
- return &e->submenu->widget;
- return NULL;
+ ltk_widget *submenu = ltk_get_widget_or_null_from_id(e->submenu);
+ return submenu && !submenu->hidden ? e->submenu : LTK_WIDGET_ID_NONE;
}
const char *
-ltk_menuentry_get_text(ltk_menuentry *entry) {
+ltk_menuentry_get_text(ltk_widget_id entryid) {
+ ltk_widget *entryw = ltk_get_widget_from_id(entryid);
+ ltk_menuentry *entry = LTK_CAST_MENUENTRY(entryw);
return ltk_text_line_get_text(entry->text_line);
}
@@ -332,7 +335,8 @@ ltk_menuentry_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_r
if (self->hidden)
return;
ltk_menuentry *entry = LTK_CAST_MENUENTRY(self);
- int in_submenu = IN_SUBMENU(entry);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ int in_submenu = IS_SUBMENU(parent);
struct entry_theme *t = in_submenu ? &submenu_entry_theme : &menu_entry_theme;
int bw = ltk_size_to_pixel(t->border_width, self->last_dpi);
int text_pad = ltk_size_to_pixel(t->text_pad, self->last_dpi);
@@ -370,7 +374,7 @@ ltk_menuentry_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_r
ltk_rect text_clip = ltk_rect_intersect(surf_clip, (ltk_rect){text_x, text_y, text_w, text_h});
ltk_text_line_draw_clipped(entry->text_line, draw_surf, text, text_x, text_y, text_clip);
- if (in_submenu && entry->submenu) {
+ if (in_submenu && !LTK_WIDGET_ID_IS_NONE(entry->submenu)) {
ltk_point arrow_points[] = {
{x + lrect.w - arrow_pad - bw, y + lrect.h / 2},
{x + lrect.w - arrow_pad - bw - arrow_size, y + lrect.h / 2 - arrow_size / 2},
@@ -399,19 +403,17 @@ ltk_menu_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) {
ltk_rect surf_clip = {x + clip_final.x, y + clip_final.y, clip_final.w, clip_final.h};
ltk_surface_fill_rect(s, t->background, surf_clip);
- ltk_widget *ptr = NULL;
- for (size_t i = 0; i < menu->num_entries; i++) {
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
/* FIXME: I guess it could be improved *slightly* by making the clip rect
smaller when scrollarrows are shown */
/* draw active entry after others so it isn't hidden with compress_borders */
- if ((menu->entries[i]->widget.state & (LTK_ACTIVE | LTK_PRESSED | LTK_HOVER)) && i < menu->num_entries - 1) {
- ptr = &menu->entries[i + 1]->widget;
- ltk_menuentry_draw(ptr, s, x + ptr->lrect.x, y + ptr->lrect.y, ltk_rect_relative(ptr->lrect, clip_final));
- ptr = &menu->entries[i]->widget;
+ ltk_widget *ptr = ltk_get_widget_from_id(ltk_array_get(menu->entries, i));
+ if ((ptr->state & (LTK_ACTIVE | LTK_PRESSED | LTK_HOVER)) && i < ltk_array_len(menu->entries) - 1) {
+ ltk_widget *ptr2 = ltk_get_widget_from_id(ltk_array_get(menu->entries, i + 1));
+ ltk_menuentry_draw(ptr2, s, x + ptr2->lrect.x, y + ptr2->lrect.y, ltk_rect_relative(ptr2->lrect, clip_final));
ltk_menuentry_draw(ptr, s, x + ptr->lrect.x, y + ptr->lrect.y, ltk_rect_relative(ptr->lrect, clip_final));
i++;
} else {
- ptr = &menu->entries[i]->widget;
ltk_menuentry_draw(ptr, s, x + ptr->lrect.x, y + ptr->lrect.y, ltk_rect_relative(ptr->lrect, clip_final));
}
}
@@ -439,8 +441,10 @@ ltk_menu_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip) {
ltk_surface_fill_polygon(s, t->scroll_arrow_color, arrow_points, 3);
}
if (lrect.h < (int)self->ideal_h) {
- ltk_surface_fill_rect(self->window->surface, t->scroll_background, (ltk_rect){wx + mbw, wy + mbw, ww - mbw * 2, sz});
- ltk_surface_fill_rect(self->window->surface, t->scroll_background, (ltk_rect){wx + mbw, wy + wh - sz - mbw, ww - mbw * 2, sz});
+ ltk_widget *windoww = ltk_get_widget_from_id(self->window);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ ltk_surface_fill_rect(window->surface, t->scroll_background, (ltk_rect){wx + mbw, wy + mbw, ww - mbw * 2, sz});
+ ltk_surface_fill_rect(window->surface, t->scroll_background, (ltk_rect){wx + mbw, wy + wh - sz - mbw, ww - mbw * 2, sz});
ltk_point arrow_points[3] = {
{wx + ww / 2, wy + arrow_pad + mbw},
{wx + ww / 2 - arrow_size / 2, wy + arrow_pad + mbw + arrow_size},
@@ -472,7 +476,6 @@ ltk_menu_resize(ltk_widget *self) {
struct theme *t = menu->is_submenu ? &submenu_theme : &menu_theme;
struct entry_theme *et = menu->is_submenu ? &submenu_entry_theme : &menu_entry_theme;
-
int bw = ltk_size_to_pixel(t->border_width, self->last_dpi);
int pad = ltk_size_to_pixel(t->pad, self->last_dpi);
int arrow_pad = ltk_size_to_pixel(t->arrow_pad, self->last_dpi);
@@ -490,27 +493,27 @@ ltk_menu_resize(ltk_widget *self) {
int cur_abs_x = -(int)menu->x_scroll_offset + start_x + pad;
int cur_abs_y = -(int)menu->y_scroll_offset + start_y + pad;
- for (size_t i = 0; i < menu->num_entries; i++) {
- ltk_menuentry *e = menu->entries[i];
- e->widget.lrect.x = cur_abs_x;
- e->widget.lrect.y = cur_abs_y;
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget *widget = ltk_get_widget_from_id(ltk_array_get(menu->entries, i));
+ widget->lrect.x = cur_abs_x;
+ widget->lrect.y = cur_abs_y;
if (menu->is_submenu) {
- e->widget.lrect.w = ideal_w - 2 * pad - 2 * mbw;
- e->widget.lrect.h = e->widget.ideal_h;
- cur_abs_y += e->widget.ideal_h + pad;
+ widget->lrect.w = ideal_w - 2 * pad - 2 * mbw;
+ widget->lrect.h = widget->ideal_h;
+ cur_abs_y += widget->ideal_h + pad;
if (et->compress_borders)
cur_abs_y -= entry_bw;
} else {
- e->widget.lrect.w = e->widget.ideal_w;
- e->widget.lrect.h = ideal_h - 2 * pad - 2 * mbw;
- cur_abs_x += e->widget.ideal_w + pad;
+ widget->lrect.w = widget->ideal_w;
+ widget->lrect.h = ideal_h - 2 * pad - 2 * mbw;
+ cur_abs_x += widget->ideal_w + pad;
if (et->compress_borders)
cur_abs_x -= entry_bw;
}
- e->widget.crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, e->widget.lrect);
+ widget->crect = ltk_rect_intersect((ltk_rect){0, 0, self->crect.w, self->crect.h}, widget->lrect);
}
self->dirty = 1;
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
static void
@@ -541,17 +544,18 @@ ltk_menu_ensure_rect_shown(ltk_widget *self, ltk_rect r) {
static void
ltk_menu_get_max_scroll_offset(ltk_menu *menu, int *x_ret, int *y_ret) {
+ ltk_widget *self = LTK_CAST_WIDGET(menu);
struct theme *theme = menu->is_submenu ? &submenu_theme : &menu_theme;
- int arrow_pad = ltk_size_to_pixel(theme->arrow_pad, LTK_CAST_WIDGET(menu)->last_dpi);
- int arrow_size = ltk_size_to_pixel(theme->arrow_size, LTK_CAST_WIDGET(menu)->last_dpi);
+ int arrow_pad = ltk_size_to_pixel(theme->arrow_pad, self->last_dpi);
+ int arrow_size = ltk_size_to_pixel(theme->arrow_size, self->last_dpi);
int extra_size = arrow_size * 2 + arrow_pad * 4;
*x_ret = 0;
*y_ret = 0;
- if (menu->widget.lrect.w < (int)menu->widget.ideal_w) {
- *x_ret = menu->widget.ideal_w - (menu->widget.lrect.w - extra_size);
+ if (self->lrect.w < (int)self->ideal_w) {
+ *x_ret = self->ideal_w - (self->lrect.w - extra_size);
}
- if (menu->widget.lrect.h < (int)menu->widget.ideal_h) {
- *y_ret = menu->widget.ideal_h - (menu->widget.lrect.h - extra_size);
+ if (self->lrect.h < (int)self->ideal_h) {
+ *y_ret = self->ideal_h - (self->lrect.h - extra_size);
}
}
@@ -580,16 +584,18 @@ ltk_menu_scroll(ltk_menu *menu, char t, char b, char l, char r, int step) {
/* FIXME: sensible epsilon? */
if (fabs(x_old - menu->x_scroll_offset) > 0.01 ||
fabs(y_old - menu->y_scroll_offset) > 0.01) {
- ltk_menu_resize(&menu->widget);
+ ltk_widget *self = LTK_CAST_WIDGET(menu);
+ ltk_menu_resize(self);
menu->widget.dirty = 1;
- ltk_window_invalidate_widget_rect(menu->widget.window, &menu->widget);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
}
/* FIXME: show scroll arrow disabled when nothing further */
static void
ltk_menu_scroll_callback(ltk_callback_arg data) {
- ltk_widget *self = LTK_CAST_ARG_WIDGET(data);
+ ltk_widget_id id = LTK_CAST_ARG_WIDGET_ID(data);
+ ltk_widget *self = ltk_get_widget_from_id(id);
ltk_menu *menu = LTK_CAST_MENU(self);
ltk_menu_scroll(
menu,
@@ -609,7 +615,7 @@ stop_scrolling(ltk_menu *menu) {
}
/* FIXME: should ideal_w, ideal_h just be int? */
-static ltk_widget *
+static ltk_widget_id
ltk_menu_get_child_at_pos(ltk_widget *self, int x, int y) {
ltk_menu *menu = LTK_CAST_MENU(self);
struct theme *t = menu->is_submenu ? &submenu_theme : &menu_theme;
@@ -629,36 +635,39 @@ ltk_menu_get_child_at_pos(ltk_widget *self, int x, int y) {
}
/* FIXME: use crect for this */
if (!ltk_collide_rect((ltk_rect){start_x, start_y, end_x - start_x, end_y - start_y}, x, y))
- return NULL;
+ return LTK_WIDGET_ID_NONE;
- for (size_t i = 0; i < menu->num_entries; i++) {
- if (ltk_collide_rect(menu->entries[i]->widget.crect, x, y))
- return &menu->entries[i]->widget;
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget_id childid = ltk_array_get(menu->entries, i);
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ if (ltk_collide_rect(child->crect, x, y))
+ return childid;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
/* FIXME: make sure timers are always destroyed when widget is destroyed */
static int
set_scroll_timer(ltk_menu *menu, int x, int y) {
+ ltk_widget *self = LTK_CAST_WIDGET(menu);
/* this check probably isn't necessary, but whatever */
- if (x < 0 || y < 0 || x >= menu->widget.lrect.w || y >= menu->widget.lrect.h)
+ if (x < 0 || y < 0 || x >= self->lrect.w || y >= self->lrect.h)
return 0;
int t = 0, b = 0, l = 0,r = 0;
struct theme *theme = menu->is_submenu ? &submenu_theme : &menu_theme;
- int arrow_pad = ltk_size_to_pixel(theme->arrow_pad, LTK_CAST_WIDGET(menu)->last_dpi);
- int raw_arrow_size = ltk_size_to_pixel(theme->arrow_size, LTK_CAST_WIDGET(menu)->last_dpi);
+ int arrow_pad = ltk_size_to_pixel(theme->arrow_pad, self->last_dpi);
+ int raw_arrow_size = ltk_size_to_pixel(theme->arrow_size, self->last_dpi);
int arrow_size = raw_arrow_size + arrow_pad * 2;
- if (menu->widget.lrect.w < (int)menu->widget.ideal_w) {
+ if (self->lrect.w < (int)self->ideal_w) {
if (x < arrow_size)
l = 1;
- else if (x > menu->widget.lrect.w - arrow_size)
+ else if (x > self->lrect.w - arrow_size)
r = 1;
}
- if (menu->widget.lrect.h < (int)menu->widget.ideal_h) {
+ if (self->lrect.h < (int)self->ideal_h) {
if (y < arrow_size)
t = 1;
- else if (y > menu->widget.lrect.h - arrow_size)
+ else if (y > self->lrect.h - arrow_size)
b = 1;
}
if (t == menu->scroll_top_hover &&
@@ -671,8 +680,8 @@ set_scroll_timer(ltk_menu *menu, int x, int y) {
menu->scroll_bottom_hover = b;
menu->scroll_left_hover = l;
menu->scroll_right_hover = r;
- ltk_menu_scroll_callback(LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(menu)));
- menu->scroll_timer_id = ltk_register_timer(0, 300, <k_menu_scroll_callback, LTK_MAKE_ARG_WIDGET(LTK_CAST_WIDGET(menu)));
+ ltk_menu_scroll_callback(LTK_MAKE_ARG_WIDGET(self));
+ menu->scroll_timer_id = ltk_register_timer(0, 300, <k_menu_scroll_callback, LTK_MAKE_ARG_WIDGET_ID(self->id));
return 1;
}
@@ -681,13 +690,13 @@ set_scroll_timer(ltk_menu *menu, int x, int y) {
be hidden when scrolling in a menu. Maybe widgets also need a "visible rect"? */
static int
ltk_menuentry_release(ltk_widget *self) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- int in_submenu = IN_SUBMENU(e);
- int keep_popup = self->parent && self->parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)self->parent)->popup_submenus;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ int in_submenu = IS_SUBMENU(parent);
+ int keep_popup = parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENU && (LTK_CAST_MENU(parent))->popup_submenus;
if (in_submenu || !keep_popup) {
ltk_window_unregister_all_popups(self->window);
}
- ltk_widget_emit_signal(self, LTK_MENUENTRY_SIGNAL_PRESSED, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_MENUENTRY_SIGNAL_PRESSED, LTK_EMPTY_ARGLIST);
return 1;
}
@@ -717,12 +726,14 @@ ltk_menu_hide(ltk_widget *self) {
ltk_unregister_timer(menu->scroll_timer_id);
menu->scroll_bottom_hover = menu->scroll_top_hover = 0;
menu->scroll_left_hover = menu->scroll_right_hover = 0;
- ltk_window_unregister_popup(self->window, self);
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_window_unregister_popup(self->window, self->id);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
/* FIXME: this is really ugly/hacky */
- if (menu->unpopup_submenus_on_hide && self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY &&
- self->parent->parent && self->parent->parent->vtable->type == LTK_WIDGET_MENU) {
- ((ltk_menu *)self->parent->parent)->popup_submenus = 0;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_widget *pparent = parent ? ltk_get_widget_or_null_from_id(parent->parent) : NULL;
+ if (menu->unpopup_submenus_on_hide && parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENUENTRY &&
+ pparent && LTK_WIDGET_TYPE(pparent) == LTK_WIDGET_MENU) {
+ (LTK_CAST_MENU(pparent))->popup_submenus = 0;
}
menu->unpopup_submenus_on_hide = 1;
}
@@ -730,31 +741,36 @@ ltk_menu_hide(ltk_widget *self) {
/* FIXME: hacky because entries need to know about their parents to be able to properly position the popup */
static void
popup_active_menu(ltk_menuentry *e) {
- if (!e->submenu)
+ ltk_widget *self = LTK_CAST_WIDGET(e);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(e->submenu);
+ if (!submenuw)
return;
+ ltk_menu *submenu = LTK_CAST_MENU(submenuw);
int in_submenu = 0, was_opened_left = 0;
- ltk_rect menu_rect = e->widget.lrect;
- ltk_point entry_global = ltk_widget_pos_to_global(LTK_CAST_WIDGET(e), 0, 0);
+ ltk_rect menu_rect = self->lrect;
+ ltk_point entry_global = ltk_widget_pos_to_global(self, 0, 0);
ltk_point menu_global;
- if (LTK_CAST_WIDGET(e)->parent && LTK_CAST_WIDGET(e)->parent->vtable->type == LTK_WIDGET_MENU) {
- ltk_menu *menu = LTK_CAST_MENU(LTK_CAST_WIDGET(e)->parent);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENU) {
+ ltk_menu *menu = LTK_CAST_MENU(parent);
in_submenu = menu->is_submenu;
was_opened_left = menu->was_opened_left;
- menu_rect = menu->widget.lrect;
- menu_global = ltk_widget_pos_to_global(LTK_CAST_WIDGET(e)->parent, 0, 0);
+ menu_rect = LTK_CAST_WIDGET(menu)->lrect;
+ menu_global = ltk_widget_pos_to_global(parent, 0, 0);
} else {
- menu_global = ltk_widget_pos_to_global(LTK_CAST_WIDGET(e), 0, 0);
+ menu_global = ltk_widget_pos_to_global(self, 0, 0);
}
- int win_w = e->widget.window->rect.w;
- int win_h = e->widget.window->rect.h;
- ltk_menu *submenu = e->submenu;
- ltk_widget_recalc_ideal_size(LTK_CAST_WIDGET(submenu));
- int ideal_w = submenu->widget.ideal_w;
- int ideal_h = submenu->widget.ideal_h;
+ ltk_widget *windoww = ltk_get_widget_from_id(self->window);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ int win_w = window->rect.w;
+ int win_h = window->rect.h;
+ ltk_widget_recalc_ideal_size(submenuw);
+ int ideal_w = submenuw->ideal_w;
+ int ideal_h = submenuw->ideal_h;
int x_final = 0, y_final = 0, w_final = ideal_w, h_final = ideal_h;
- int submenu_bw = ltk_size_to_pixel(submenu_theme.border_width, LTK_CAST_WIDGET(e)->last_dpi);
- int submenu_pad = ltk_size_to_pixel(submenu_theme.pad, LTK_CAST_WIDGET(e)->last_dpi);
- int menu_bw = ltk_size_to_pixel(menu_theme.border_width, LTK_CAST_WIDGET(e)->last_dpi);
+ int submenu_bw = ltk_size_to_pixel(submenu_theme.border_width, self->last_dpi);
+ int submenu_pad = ltk_size_to_pixel(submenu_theme.pad, self->last_dpi);
+ int menu_bw = ltk_size_to_pixel(menu_theme.border_width, self->last_dpi);
if (in_submenu) {
int space_left = menu_global.x;
int space_right = win_w - (menu_global.x + menu_rect.w);
@@ -846,21 +862,23 @@ popup_active_menu(ltk_menuentry *e) {
submenu->widget.lrect.y = y_final;
submenu->widget.lrect.w = w_final;
submenu->widget.lrect.h = h_final;
- submenu->widget.crect = LTK_CAST_WIDGET(submenu)->lrect;
+ submenu->widget.crect = submenuw->lrect;
submenu->widget.dirty = 1;
submenu->widget.hidden = 0;
submenu->popup_submenus = 0;
submenu->unpopup_submenus_on_hide = 1;
- ltk_widget_resize(LTK_CAST_WIDGET(submenu));
- ltk_window_register_popup(LTK_CAST_WIDGET(e)->window, LTK_CAST_WIDGET(submenu));
- ltk_window_invalidate_widget_rect(LTK_CAST_WIDGET(submenu)->window, LTK_CAST_WIDGET(submenu));
+ ltk_widget_resize(submenuw);
+ ltk_window_register_popup(self->window, e->submenu);
+ ltk_window_invalidate_widget_rect(submenuw->window, submenuw->id);
}
static void
unpopup_active_entry(ltk_menuentry *e) {
- if (e->submenu && !e->submenu->widget.hidden) {
- e->submenu->unpopup_submenus_on_hide = 0;
- ltk_widget_hide(&e->submenu->widget);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(e->submenu);
+ if (submenuw && !submenuw->hidden) {
+ ltk_menu *submenu = LTK_CAST_MENU(submenuw);
+ submenu->unpopup_submenus_on_hide = 0;
+ ltk_widget_hide(submenuw);
}
}
@@ -889,18 +907,18 @@ ltk_menu_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_menu_size(menu);
}
-static ltk_menu *
-ltk_menu_create_base(ltk_window *window, int is_submenu) {
+static ltk_widget_id
+ltk_menu_create_base(ltk_widget_id windowid, int is_submenu) {
ltk_menu *menu = ltk_malloc(sizeof(ltk_menu));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(menu), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(menu), windowid, &vtable, 0, 0);
+ ltk_widget *self = LTK_CAST_WIDGET(menu);
- int menu_pad = ltk_size_to_pixel(menu_theme.pad, LTK_CAST_WIDGET(menu)->last_dpi);
- menu->widget.ideal_w = menu_pad;
- menu->widget.ideal_h = menu_pad;
- menu->widget.dirty = 1;
+ int menu_pad = ltk_size_to_pixel(menu_theme.pad, self->last_dpi);
+ self->ideal_w = menu_pad;
+ self->ideal_h = menu_pad;
+ self->dirty = 1;
- menu->entries = NULL;
- menu->num_entries = menu->num_alloc = 0;
+ menu->entries = ltk_array_create(widget_id, 1);
menu->x_scroll_offset = menu->y_scroll_offset = 0;
menu->is_submenu = is_submenu;
menu->was_opened_left = 0;
@@ -914,34 +932,28 @@ ltk_menu_create_base(ltk_window *window, int is_submenu) {
unnecessary redrawing */
recalc_ideal_menu_size(menu);
- return menu;
+ return id;
}
-ltk_menu *
-ltk_menu_create(ltk_window *window) {
- return ltk_menu_create_base(window, 0);
+ltk_widget_id
+ltk_menu_create(ltk_widget_id windowid) {
+ return ltk_menu_create_base(windowid, 0);
}
-ltk_menu *
-ltk_submenu_create(ltk_window *window) {
- return ltk_menu_create_base(window, 1);
+ltk_widget_id
+ltk_submenu_create(ltk_widget_id windowid) {
+ return ltk_menu_create_base(windowid, 1);
}
static int
-insert_entry(ltk_menu *menu, ltk_menuentry *e, size_t idx) {
- if (idx > menu->num_entries)
+insert_entry(ltk_widget_id menuid, ltk_widget_id menuentryid, size_t idx) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ if (!menu->entries)
+ menu->entries = ltk_array_create(widget_id, 1);
+ if (idx > ltk_array_len(menu->entries))
return 1;
- if (menu->num_entries == menu->num_alloc) {
- menu->num_alloc = ideal_array_size(menu->num_alloc, menu->num_entries + 1);
- menu->entries = ltk_reallocarray(menu->entries, menu->num_alloc, sizeof(ltk_menuentry *));
- }
- memmove(
- menu->entries + idx + 1,
- menu->entries + idx,
- sizeof(ltk_menuentry *) * (menu->num_entries - idx)
- );
- menu->num_entries++;
- menu->entries[idx] = e;
+ ltk_array_insert(widget_id, menu->entries, idx, menuentryid);
return 0;
}
@@ -950,71 +962,77 @@ recalc_ideal_menu_size(ltk_menu *menu) {
struct theme *t = menu->is_submenu ? &submenu_theme : &menu_theme;
struct entry_theme *et = menu->is_submenu ? &submenu_entry_theme : &menu_entry_theme;
- int bw = ltk_size_to_pixel(t->border_width, LTK_CAST_WIDGET(menu)->last_dpi);
- int pad = ltk_size_to_pixel(t->pad, LTK_CAST_WIDGET(menu)->last_dpi);
- int entry_bw = ltk_size_to_pixel(et->border_width, LTK_CAST_WIDGET(menu)->last_dpi);
+ ltk_widget *self = LTK_CAST_WIDGET(menu);
+ int bw = ltk_size_to_pixel(t->border_width, self->last_dpi);
+ int pad = ltk_size_to_pixel(t->pad, self->last_dpi);
+ int entry_bw = ltk_size_to_pixel(et->border_width, self->last_dpi);
- menu->widget.ideal_w = menu->widget.ideal_h = pad + bw * 2;
- ltk_menuentry *e;
- for (size_t i = 0; i < menu->num_entries; i++) {
- e = menu->entries[i];
- ltk_widget_recalc_ideal_size(LTK_CAST_WIDGET(e));
+ self->ideal_w = self->ideal_h = pad + bw * 2;
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget_id id = ltk_array_get(menu->entries, i);
+ ltk_widget *ew = ltk_get_widget_from_id(id);
+ ltk_widget_recalc_ideal_size(ew);
if (menu->is_submenu) {
- menu->widget.ideal_w = MAX((pad + bw) * 2 + (int)e->widget.ideal_w, (int)menu->widget.ideal_w);
- menu->widget.ideal_h += e->widget.ideal_h + pad;
+ self->ideal_w = MAX((pad + bw) * 2 + (int)ew->ideal_w, (int)self->ideal_w);
+ self->ideal_h += ew->ideal_h + pad;
if (et->compress_borders && i != 0)
- menu->widget.ideal_h -= entry_bw;
+ self->ideal_h -= entry_bw;
} else {
- menu->widget.ideal_w += e->widget.ideal_w + pad;
+ self->ideal_w += ew->ideal_w + pad;
if (et->compress_borders && i != 0)
- menu->widget.ideal_w -= entry_bw;
- menu->widget.ideal_h = MAX((pad + bw) * 2 + (int)e->widget.ideal_h, (int)menu->widget.ideal_h);
+ self->ideal_w -= entry_bw;
+ self->ideal_h = MAX((pad + bw) * 2 + (int)ew->ideal_h, (int)self->ideal_h);
}
}
}
/* FIXME: rename these functions */
static void
-recalc_ideal_menu_size_with_notification(ltk_widget *self, ltk_widget *widget) {
+recalc_ideal_menu_size_with_notification(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
+ ltk_widget *child = ltk_get_widget_from_id(childid);
/* If widget with size change is submenu, it doesn't affect this menu */
- if (widget && widget->vtable->type == LTK_WIDGET_MENU) {
- ltk_widget_resize(widget);
+ if (child && LTK_WIDGET_TYPE(child) == LTK_WIDGET_MENU) {
+ ltk_widget_resize(child);
return;
}
unsigned int old_w = self->ideal_w, old_h = self->ideal_h;
recalc_ideal_menu_size(menu);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
if ((old_w != self->ideal_w || old_h != self->ideal_h) &&
- self->parent && self->parent->vtable->child_size_change) {
- self->parent->vtable->child_size_change(self->parent, self);
+ parent && parent->vtable->child_size_change) {
+ parent->vtable->child_size_change(parent, self->id);
} else {
ltk_menu_resize(self);
}
self->dirty = 1;
if (!self->hidden)
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
static void
recalc_ideal_menuentry_size(ltk_menuentry *entry) {
- int in_submenu = IN_SUBMENU(entry);
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ int in_submenu = IS_SUBMENU(parent);
struct entry_theme *t = in_submenu ? &submenu_entry_theme : &menu_entry_theme;
- int bw = ltk_size_to_pixel(t->border_width, LTK_CAST_WIDGET(entry)->last_dpi);
- int text_pad = ltk_size_to_pixel(t->text_pad, LTK_CAST_WIDGET(entry)->last_dpi);
- int arrow_pad = ltk_size_to_pixel(t->arrow_pad, LTK_CAST_WIDGET(entry)->last_dpi);
- int arrow_size = ltk_size_to_pixel(t->arrow_size, LTK_CAST_WIDGET(entry)->last_dpi);
- int extra_size = (in_submenu && entry->submenu) ? arrow_pad * 2 + arrow_size : 0;
+ int bw = ltk_size_to_pixel(t->border_width, self->last_dpi);
+ int text_pad = ltk_size_to_pixel(t->text_pad, self->last_dpi);
+ int arrow_pad = ltk_size_to_pixel(t->arrow_pad, self->last_dpi);
+ int arrow_size = ltk_size_to_pixel(t->arrow_size, self->last_dpi);
+ int extra_size = (in_submenu && !LTK_WIDGET_ID_IS_NONE(entry->submenu)) ? arrow_pad * 2 + arrow_size : 0;
int text_w, text_h;
ltk_text_line_get_size(entry->text_line, &text_w, &text_h);
- entry->widget.ideal_w = text_w + extra_size + (text_pad + bw) * 2;
- entry->widget.ideal_h = MAX(text_h + text_pad * 2, extra_size) + bw * 2;
+ self->ideal_w = text_w + extra_size + (text_pad + bw) * 2;
+ self->ideal_h = MAX(text_h + text_pad * 2, extra_size) + bw * 2;
}
static void
ltk_menuentry_recalc_ideal_size(ltk_widget *self) {
ltk_menuentry *entry = LTK_CAST_MENUENTRY(self);
- struct entry_theme *t = IN_SUBMENU(entry) ? &submenu_entry_theme : &menu_entry_theme;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ struct entry_theme *t = IS_SUBMENU(parent) ? &submenu_entry_theme : &menu_entry_theme;
int font_size = ltk_size_to_pixel(t->font_size, self->last_dpi);
ltk_text_line_set_font_size(entry->text_line, font_size);
recalc_ideal_menuentry_size(entry);
@@ -1022,8 +1040,10 @@ ltk_menuentry_recalc_ideal_size(ltk_widget *self) {
static void
ltk_menuentry_set_font(ltk_menuentry *entry) {
- struct entry_theme *t = IN_SUBMENU(entry) ? &submenu_entry_theme : &menu_entry_theme;
- int font_size = ltk_size_to_pixel(t->font_size, LTK_CAST_WIDGET(entry)->last_dpi);
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ struct entry_theme *t = IS_SUBMENU(parent) ? &submenu_entry_theme : &menu_entry_theme;
+ int font_size = ltk_size_to_pixel(t->font_size, self->last_dpi);
ltk_text_line_set_font(entry->text_line, t->font, font_size);
}
@@ -1031,36 +1051,40 @@ static void
ltk_menuentry_recalc_ideal_size_with_notification(ltk_menuentry *entry) {
recalc_ideal_menuentry_size(entry);
/* FIXME: only call if something changed */
- if (entry->widget.parent && entry->widget.parent->vtable->child_size_change) {
- entry->widget.parent->vtable->child_size_change(entry->widget.parent, (ltk_widget *)entry);
+ ltk_widget *self = LTK_CAST_WIDGET(entry);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ if (parent && parent->vtable->child_size_change) {
+ parent->vtable->child_size_change(parent, self->id);
}
}
-ltk_menuentry *
-ltk_menuentry_create(ltk_window *window, const char *text) {
+ltk_widget_id
+ltk_menuentry_create(ltk_widget_id windowid, const char *text) {
ltk_menuentry *e = ltk_malloc(sizeof(ltk_menuentry));
- ltk_fill_widget_defaults(&e->widget, window, &entry_vtable, 0, 0);
+ ltk_widget *self = LTK_CAST_WIDGET(e);
+ ltk_widget_id id = ltk_initialize_widget(self, windowid, &entry_vtable, 0, 0);
e->text_line = ltk_text_line_create_const_text_default(
menu_entry_theme.font,
- ltk_size_to_pixel(menu_entry_theme.font_size, e->widget.last_dpi),
+ ltk_size_to_pixel(menu_entry_theme.font_size, self->last_dpi),
text, -1
);
- e->submenu = NULL;
+ e->submenu = LTK_WIDGET_ID_NONE;
/* Note: This is only set as a dummy value! The actual ideal size can't
be set until it is part of a menu because it needs to know which
theme it should use */
recalc_ideal_menuentry_size(e);
- e->widget.dirty = 1;
- return e;
+ self->dirty = 1;
+ return id;
}
static int
-ltk_menuentry_remove_child(ltk_widget *self, ltk_widget *widget) {
+ltk_menuentry_remove_child(ltk_widget *self, ltk_widget_id widgetid) {
ltk_menuentry *e = LTK_CAST_MENUENTRY(self);
- if (widget != LTK_CAST_WIDGET(e->submenu))
+ if (LTK_WIDGET_ID_IS_NONE(widgetid) || !LTK_WIDGET_ID_EQUAL(widgetid, e->submenu))
return 1;
- widget->parent = NULL;
- e->submenu = NULL;
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ widget->parent = LTK_WIDGET_ID_NONE;
+ e->submenu = LTK_WIDGET_ID_NONE;
ltk_menuentry_recalc_ideal_size_with_notification(e);
return 0;
}
@@ -1071,155 +1095,164 @@ ltk_menuentry_destroy(ltk_widget *self, int shallow) {
ltk_text_line_destroy(e->text_line);
/* FIXME: function to call when parent is destroyed */
/* also function to call when parent added */
- if (e->submenu) {
- e->submenu->widget.parent = NULL;
- if (!shallow) {
- ltk_widget_destroy(LTK_CAST_WIDGET(e->submenu), shallow);
- }
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(e->submenu);
+ if (submenuw) {
+ submenuw->parent = LTK_WIDGET_ID_NONE;
+ if (!shallow)
+ ltk_widget_destroy(submenuw, shallow);
}
ltk_free(e);
}
int
-ltk_menu_insert_entry(ltk_menu *menu, ltk_menuentry *entry, size_t idx) {
- if (entry->widget.parent)
+ltk_menu_insert_entry(ltk_widget_id menuid, ltk_widget_id entryid, size_t idx) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_widget *entryw = ltk_get_widget_from_id(entryid);
+ if (!LTK_WIDGET_ID_IS_NONE(entryw->parent))
return 1; /* already child of some widget */
- if (insert_entry(menu, entry, idx))
+ if (insert_entry(menuid, entryid, idx))
return 2; /* invalid index */
- entry->widget.parent = &menu->widget;
+ entryw->parent = menuid;
/* the theme may have changed if the entry switched between menu and submenu */
- ltk_menuentry_set_font(entry);
- ltk_menuentry_recalc_ideal_size_with_notification(entry);
- recalc_ideal_menu_size_with_notification(LTK_CAST_WIDGET(menu), NULL);
- menu->widget.dirty = 1;
+ ltk_menuentry_set_font(LTK_CAST_MENUENTRY(entryw));
+ ltk_menuentry_recalc_ideal_size_with_notification(LTK_CAST_MENUENTRY(entryw));
+ recalc_ideal_menu_size_with_notification(menuw, entryid);
+ menuw->dirty = 1;
return 0;
}
int
-ltk_menu_add_entry(ltk_menu *menu, ltk_menuentry *entry) {
- return ltk_menu_insert_entry(menu, entry, menu->num_entries);
+ltk_menu_add_entry(ltk_widget_id menuid, ltk_widget_id entryid) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ return ltk_menu_insert_entry(menuid, entryid, ltk_array_len(menu->entries));
}
/* FIXME: maybe allow any menu and just change is_submenu (also need to recalculate size then) */
int
-ltk_menuentry_attach_submenu(ltk_menuentry *e, ltk_menu *submenu) {
+ltk_menuentry_attach_submenu(ltk_widget_id entryid, ltk_widget_id submenuid) {
+ ltk_widget *entryw = ltk_get_widget_from_id(entryid);
+ ltk_menuentry *entry = LTK_CAST_MENUENTRY(entryw);
+ ltk_widget *submenuw = ltk_get_widget_from_id(submenuid);
+ ltk_menu *submenu = LTK_CAST_MENU(submenuw);
if (!submenu->is_submenu)
return 1; /* menu is not submenu */
- else if (e->submenu)
+ else if (!LTK_WIDGET_ID_IS_NONE(entry->submenu))
return 2; /* entry already contains submenu */
- e->submenu = submenu;
- ltk_menuentry_recalc_ideal_size_with_notification(e);
- e->widget.dirty = 1;
- if (submenu) {
- submenu->widget.hidden = 1;
- submenu->widget.parent = LTK_CAST_WIDGET(e);
- }
- if (!e->widget.hidden)
- ltk_window_invalidate_widget_rect(e->widget.window, LTK_CAST_WIDGET(e));
+ entry->submenu = submenuid;
+ ltk_menuentry_recalc_ideal_size_with_notification(entry);
+ entryw->dirty = 1;
+ submenuw->hidden = 1;
+ submenuw->parent = entryid;
+ if (!entryw->hidden)
+ ltk_window_invalidate_widget_rect(entryw->window, entryw->id);
return 0;
}
/* FIXME: hide all entries when menu hidden? */
-static void
-shrink_entries(ltk_menu *menu) {
- size_t new_alloc = ideal_array_size(menu->num_alloc, menu->num_entries);
- if (new_alloc != menu->num_alloc) {
- menu->entries = ltk_reallocarray(menu->entries, new_alloc, sizeof(ltk_menuentry *));
- menu->num_alloc = new_alloc;
- }
-}
-
-ltk_menuentry *
-ltk_menu_remove_entry_index(ltk_menu *menu, size_t idx) {
- if (idx >= menu->num_entries)
- return NULL; /* invalid index */
- menu->entries[idx]->widget.parent = NULL;
+ltk_widget_id
+ltk_menu_remove_entry_index(ltk_widget_id menuid, size_t idx) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ if (idx >= ltk_array_len(menu->entries))
+ return LTK_WIDGET_ID_NONE; /* invalid index */
+ ltk_widget_id entryid = ltk_array_get(menu->entries, idx);
+ ltk_widget *entry = ltk_get_widget_from_id(entryid);
+ entry->parent = LTK_WIDGET_ID_NONE;
/* I don't think this is needed because the entry isn't shown
anywhere. Its size will be recalculated once it is added
to a menu again. */
/* ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[idx]); */
- ltk_menuentry *ret = menu->entries[idx];
- memmove(
- menu->entries + idx,
- menu->entries + idx + 1,
- sizeof(ltk_menuentry *) * (menu->num_entries - idx - 1)
- );
- menu->num_entries--;
- shrink_entries(menu);
- recalc_ideal_menu_size_with_notification(LTK_CAST_WIDGET(menu), NULL);
- return ret;
+ ltk_array_delete(widget_id, menu->entries, idx, 1);
+ recalc_ideal_menu_size_with_notification(menuw, LTK_WIDGET_ID_NONE);
+ return entryid;
}
size_t
-ltk_menu_get_entry_index(ltk_menu *menu, ltk_menuentry *entry) {
- for (size_t i = 0; i < menu->num_entries; i++) {
- if (menu->entries[i] == entry)
+ltk_menu_get_entry_index(ltk_widget_id menuid, ltk_widget_id entryid) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget_id id = ltk_array_get(menu->entries, i);
+ if (LTK_WIDGET_ID_EQUAL(id, entryid))
return i;
}
return SIZE_MAX;
}
size_t
-ltk_menu_get_num_entries(ltk_menu *menu) {
- return menu->num_entries;
+ltk_menu_get_num_entries(ltk_widget_id menuid) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ return ltk_array_len(menu->entries);
}
-ltk_menuentry *
-ltk_menu_get_entry(ltk_menu *menu, size_t idx) {
- if (idx >= menu->num_entries)
- return NULL;
- return menu->entries[idx];
+ltk_widget_id
+ltk_menu_get_entry(ltk_widget_id menuid, size_t idx) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ if (idx >= ltk_array_len(menu->entries))
+ return LTK_WIDGET_ID_NONE;
+ return ltk_array_get(menu->entries, idx);
}
int
-ltk_menu_remove_entry(ltk_menu *menu, ltk_menuentry *entry) {
- size_t idx = ltk_menu_get_entry_index(menu, entry);
- if (idx >= menu->num_entries)
+ltk_menu_remove_entry(ltk_widget_id menuid, ltk_widget_id entryid) {
+ size_t idx = ltk_menu_get_entry_index(menuid, entryid);
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ if (idx >= ltk_array_len(menu->entries))
return 1;
- ltk_menuentry *ret = ltk_menu_remove_entry_index(menu, idx);
- if (!ret) /* shouldn't be possible */
+ ltk_widget_id ret = ltk_menu_remove_entry_index(menuid, idx);
+ if (LTK_WIDGET_ID_IS_NONE(ret)) /* shouldn't be possible */
return 1;
return 0;
}
static int
-ltk_menu_remove_child(ltk_widget *self, ltk_widget *child) {
+ltk_menu_remove_child(ltk_widget *self, ltk_widget_id childid) {
/* FIXME: this is kind of ugly - it's just there to avoid
aborting if a different widget type is given */
- if (child->vtable->type != LTK_WIDGET_MENUENTRY)
+ ltk_widget *child = ltk_get_widget_from_id(childid);
+ if (LTK_WIDGET_TYPE(child) != LTK_WIDGET_MENUENTRY)
return 1;
- return ltk_menu_remove_entry(LTK_CAST_MENU(self), LTK_CAST_MENUENTRY(child));
+ return ltk_menu_remove_entry(self->id, childid);
}
/* TODO: add function to also destroy the entries when removing them */
+/* -> note: ltk_menu_remove_all_entries is used by ltk_menu_destroy, so
+ that has to be updated if this changes */
void
-ltk_menu_remove_all_entries(ltk_menu *menu) {
- for (size_t i = 0; i < menu->num_entries; i++) {
- menu->entries[i]->widget.parent = NULL;
+ltk_menu_remove_all_entries(ltk_widget_id menuid) {
+ ltk_widget *menuw = ltk_get_widget_from_id(menuid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget *child = ltk_get_widget_from_id(ltk_array_get(menu->entries, i));
+ child->parent = LTK_WIDGET_ID_NONE;
/* I don't think this is needed because the entry isn't shown
anywhere. Its size will be recalculated once it is added
to a menu again. */
/* ltk_menuentry_recalc_ideal_size_with_notification(menu->entries[i]); */
}
- menu->num_entries = menu->num_alloc = 0;
- ltk_free0(menu->entries);
- recalc_ideal_menu_size_with_notification(LTK_CAST_WIDGET(menu), NULL);
+ ltk_array_reset(widget_id, menu->entries);
+ recalc_ideal_menu_size_with_notification(menuw, LTK_WIDGET_ID_NONE);
}
-static ltk_widget *
+static ltk_widget_id
ltk_menu_nearest_child(ltk_widget *self, ltk_rect rect) {
ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_widget *minw = NULL;
+ ltk_widget_id minw = LTK_WIDGET_ID_NONE;
int min_dist = INT_MAX;
- for (size_t i = 0; i < menu->num_entries; i++) {
- ltk_rect r = LTK_CAST_WIDGET(menu->entries[i])->lrect;
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget *child = ltk_get_widget_from_id(ltk_array_get(menu->entries, i));
+ ltk_rect r = child->lrect;
/* FIXME: maybe simplify this since all entries are
laid out horizontal/vertical anyways */
int dist = ltk_rect_fakedist(rect, r);
if (dist < min_dist) {
min_dist = dist;
- minw = LTK_CAST_WIDGET(menu->entries[i]);
+ minw = ltk_array_get(menu->entries, i);
}
}
return minw;
@@ -1233,167 +1266,175 @@ ltk_menu_nearest_child(ltk_widget *self, ltk_rect rect) {
the current active hierarchy as child widget again, and nearest_child on that submenu will
(probably) give the bottom widget again, so nothing changes except that all submenus except
for the first and second one disappear */
-static ltk_widget *
-ltk_menu_nearest_child_left(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_menu_nearest_child_left(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_menuentry *entry = LTK_CAST_MENUENTRY(widget);
- ltk_widget *left = NULL;
+ ltk_widget *entryw = ltk_get_widget_from_id(childid);
+ ltk_menuentry *entry = LTK_CAST_MENUENTRY(entryw);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(entry->submenu);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_widget_id left = LTK_WIDGET_ID_NONE;
if (!menu->is_submenu) {
- left = ltk_menu_prev_child(self, widget);
- } else if (widget->vtable->type == LTK_WIDGET_MENUENTRY &&
- entry->submenu &&
- !entry->submenu->widget.hidden &&
- entry->submenu->was_opened_left) {
- left = LTK_CAST_WIDGET(entry->submenu);
- } else if (self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self->parent);
- if (!menu->was_opened_left && IN_SUBMENU(e)) {
+ left = ltk_menu_prev_child(self, childid);
+ } else if (submenuw && !submenuw->hidden && LTK_CAST_MENU(submenuw)->was_opened_left) {
+ left = submenuw->id;
+ } else if (parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENUENTRY) {
+ ltk_widget *grandparent = ltk_get_widget_or_null_from_id(parent->parent);
+ if (!menu->was_opened_left && IS_SUBMENU(grandparent)) {
left = self->parent;
- } else if (!IN_SUBMENU(e) && e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU) {
- left = ltk_menu_prev_child(e->widget.parent, &e->widget);
+ } else if (!IS_SUBMENU(grandparent) && LTK_WIDGET_TYPE(grandparent) == LTK_WIDGET_MENU) {
+ left = ltk_menu_prev_child(grandparent, self->parent);
}
}
return left;
}
-static ltk_widget *
-ltk_menu_nearest_child_right(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_menu_nearest_child_right(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_menuentry *entry = LTK_CAST_MENUENTRY(widget);
- ltk_widget *right = NULL;
+ ltk_widget *entryw = ltk_get_widget_from_id(childid);
+ ltk_menuentry *entry = LTK_CAST_MENUENTRY(entryw);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(entry->submenu);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_widget_id right = LTK_WIDGET_ID_NONE;
if (!menu->is_submenu) {
- right = ltk_menu_next_child(self, widget);
- } else if (widget->vtable->type == LTK_WIDGET_MENUENTRY &&
- entry->submenu &&
- !entry->submenu->widget.hidden &&
- !entry->submenu->was_opened_left) {
- right = LTK_CAST_WIDGET(entry->submenu);
- } else if (self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self->parent);
- if (menu->was_opened_left && IN_SUBMENU(e)) {
+ right = ltk_menu_next_child(self, childid);
+ } else if (submenuw && !submenuw->hidden && !LTK_CAST_MENU(submenuw)->was_opened_left) {
+ right = entry->submenu;
+ } else if (parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENUENTRY) {
+ ltk_widget *grandparent = ltk_get_widget_or_null_from_id(parent->parent);
+ if (menu->was_opened_left && IS_SUBMENU(grandparent)) {
right = self->parent;
- } else if (!IN_SUBMENU(e) && e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU) {
- right = ltk_menu_next_child(e->widget.parent, LTK_CAST_WIDGET(e));
+ } else if (!IS_SUBMENU(grandparent) && LTK_WIDGET_TYPE(grandparent) == LTK_WIDGET_MENU) {
+ right = ltk_menu_next_child(grandparent, self->parent);
}
}
return right;
}
-static ltk_widget *
-ltk_menu_nearest_child_above(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_menu_nearest_child_above(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_widget *above = NULL;
+ ltk_widget *childw = ltk_get_widget_from_id(childid);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_widget_id above = LTK_WIDGET_ID_NONE;
if (menu->is_submenu) {
- above = ltk_menu_prev_child(self, widget);
- if (!above && self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self->parent);
- if (e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU) {
- ltk_menu *pmenu = LTK_CAST_MENU(e->widget.parent);
- if (!menu->was_opened_above && !pmenu->is_submenu) {
+ above = ltk_menu_prev_child(self, childid);
+ if (LTK_WIDGET_ID_IS_NONE(above) && parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENUENTRY) {
+ ltk_widget *grandparent = ltk_get_widget_or_null_from_id(parent->parent);
+ if (grandparent && LTK_WIDGET_TYPE(grandparent) == LTK_WIDGET_MENU) {
+ ltk_menu *gpmenu = LTK_CAST_MENU(grandparent);
+ if (!menu->was_opened_above && !gpmenu->is_submenu) {
above = self->parent;
- } else if (pmenu->is_submenu) {
- above = ltk_menu_prev_child(e->widget.parent, LTK_CAST_WIDGET(e));
+ } else if (gpmenu->is_submenu) {
+ above = ltk_menu_prev_child(grandparent, self->parent);
}
}
}
- } else if (widget->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(widget);
- if (e->submenu && !e->submenu->widget.hidden && e->submenu->was_opened_above) {
- above = LTK_CAST_WIDGET(e->submenu);
+ } else if (LTK_WIDGET_TYPE(childw) == LTK_WIDGET_MENUENTRY) {
+ ltk_menuentry *e = LTK_CAST_MENUENTRY(childw);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(e->submenu);
+ if (submenuw && !submenuw->hidden && LTK_CAST_MENU(submenuw)->was_opened_above) {
+ above = e->submenu;
}
}
return above;
}
-static ltk_widget *
-ltk_menu_nearest_child_below(ltk_widget *self, ltk_widget *widget) {
+static ltk_widget_id
+ltk_menu_nearest_child_below(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- ltk_widget *below = NULL;
+ ltk_widget *childw = ltk_get_widget_from_id(childid);
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(self->parent);
+ ltk_widget_id below = LTK_WIDGET_ID_NONE;
if (menu->is_submenu) {
- below = ltk_menu_next_child(self, widget);
- if (!below && self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(self->parent);
- if (e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU) {
- ltk_menu *pmenu = LTK_CAST_MENU(e->widget.parent);
- if (menu->was_opened_above && !pmenu->is_submenu) {
+ below = ltk_menu_next_child(self, childid);
+ if (LTK_WIDGET_ID_IS_NONE(below) && parent && LTK_WIDGET_TYPE(parent) == LTK_WIDGET_MENUENTRY) {
+ ltk_widget *grandparent = ltk_get_widget_or_null_from_id(parent->parent);
+ if (grandparent && LTK_WIDGET_TYPE(grandparent) == LTK_WIDGET_MENU) {
+ ltk_menu *gpmenu = LTK_CAST_MENU(grandparent);
+ if (menu->was_opened_above && !gpmenu->is_submenu) {
below = self->parent;
- } else if (pmenu->is_submenu) {
- below = ltk_menu_next_child(e->widget.parent, LTK_CAST_WIDGET(e));
+ } else if (gpmenu->is_submenu) {
+ below = ltk_menu_next_child(grandparent, self->parent);
}
}
}
- } else if (widget->vtable->type == LTK_WIDGET_MENUENTRY) {
- ltk_menuentry *e = LTK_CAST_MENUENTRY(widget);
- if (e->submenu && !e->submenu->widget.hidden && !e->submenu->was_opened_above) {
- below = LTK_CAST_WIDGET(e->submenu);
+ } else if (LTK_WIDGET_TYPE(childw) == LTK_WIDGET_MENUENTRY) {
+ ltk_menuentry *e = LTK_CAST_MENUENTRY(childw);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(e->submenu);
+ if (submenuw && !submenuw->hidden && !LTK_CAST_MENU(submenuw)->was_opened_above) {
+ below = e->submenu;
}
}
return below;
}
-static ltk_widget *
-ltk_menu_prev_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_menu_prev_child(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- for (size_t i = menu->num_entries; i-- > 0;) {
- if (LTK_CAST_WIDGET(menu->entries[i]) == child)
- return i > 0 ? LTK_CAST_WIDGET(menu->entries[i-1]) : NULL;
+ for (size_t i = ltk_array_len(menu->entries); i-- > 0;) {
+ ltk_widget_id id = ltk_array_get(menu->entries, i);
+ if (LTK_WIDGET_ID_EQUAL(id, childid))
+ return i > 0 ? ltk_array_get(menu->entries, i - 1) : LTK_WIDGET_ID_NONE;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
-ltk_menu_next_child(ltk_widget *self, ltk_widget *child) {
+static ltk_widget_id
+ltk_menu_next_child(ltk_widget *self, ltk_widget_id childid) {
ltk_menu *menu = LTK_CAST_MENU(self);
- for (size_t i = 0; i < menu->num_entries; i++) {
- if (LTK_CAST_WIDGET(menu->entries[i]) == child)
- return i < menu->num_entries - 1 ? LTK_CAST_WIDGET(menu->entries[i+1]) : NULL;
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
+ ltk_widget_id id = ltk_array_get(menu->entries, i);
+ if (LTK_WIDGET_ID_EQUAL(id, childid))
+ return i < ltk_array_len(menu->entries) - 1 ? ltk_array_get(menu->entries, i + 1) : LTK_WIDGET_ID_NONE;
}
- return NULL;
+ return LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_menu_first_child(ltk_widget *self) {
ltk_menu *menu = LTK_CAST_MENU(self);
- return menu->num_entries > 0 ? LTK_CAST_WIDGET(menu->entries[0]) : NULL;
+ return ltk_array_len(menu->entries) > 0 ? ltk_array_get(menu->entries, 0) : LTK_WIDGET_ID_NONE;
}
-static ltk_widget *
+static ltk_widget_id
ltk_menu_last_child(ltk_widget *self) {
ltk_menu *menu = LTK_CAST_MENU(self);
- return menu->num_entries > 0 ? LTK_CAST_WIDGET(menu->entries[menu->num_entries-1]) : NULL;
+ return ltk_array_len(menu->entries) > 0 ? ltk_array_get(menu->entries, ltk_array_len(menu->entries) - 1) : LTK_WIDGET_ID_NONE;
}
/* FIXME: unregister from window popups? */
int
-ltk_menuentry_detach_submenu(ltk_menuentry *e) {
- if (!e->submenu)
+ltk_menuentry_detach_submenu(ltk_widget_id entryid) {
+ ltk_widget *entryw = ltk_get_widget_from_id(entryid);
+ ltk_menuentry *entry = LTK_CAST_MENUENTRY(entryw);
+ ltk_widget *submenuw = ltk_get_widget_or_null_from_id(entry->submenu);
+ if (!submenuw)
return 1;
- e->submenu->widget.parent = NULL;
- e->submenu = NULL;
- ltk_menuentry_recalc_ideal_size_with_notification(e);
+ submenuw->parent = LTK_WIDGET_ID_NONE;
+ entry->submenu = LTK_WIDGET_ID_NONE;
+ ltk_menuentry_recalc_ideal_size_with_notification(entry);
return 0;
}
static void
ltk_menu_destroy(ltk_widget *self, int shallow) {
ltk_menu *menu = LTK_CAST_MENU(self);
- if (!menu) {
- ltk_warn("Tried to destroy NULL menu.\n");
- return;
- }
if (menu->scroll_timer_id >= 0)
ltk_unregister_timer(menu->scroll_timer_id);
- ltk_window_unregister_popup(self->window, self);
+ ltk_window_unregister_popup(self->window, self->id);
if (!shallow) {
- for (size_t i = 0; i < menu->num_entries; i++) {
+ for (size_t i = 0; i < ltk_array_len(menu->entries); i++) {
/* for efficiency - to avoid ltk_widget_destroy calling
ltk_menu_remove_child for each of the entries */
- menu->entries[i]->widget.parent = NULL;
- ltk_widget_destroy(LTK_CAST_WIDGET(menu->entries[i]), shallow);
+ ltk_widget *child = ltk_get_widget_from_id(ltk_array_get(menu->entries, i));
+ child->parent = LTK_WIDGET_ID_NONE;
+ ltk_widget_destroy(child, shallow);
}
- ltk_free(menu->entries);
+ ltk_array_destroy(widget_id, menu->entries);
} else {
- ltk_menu_remove_all_entries(menu);
+ ltk_menu_remove_all_entries(self->id);
}
ltk_free(menu);
}
diff --git a/src/ltk/menu.h b/src/ltk/menu.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -28,14 +28,9 @@
#define LTK_MENUENTRY_SIGNAL_PRESSED -1
#define LTK_MENUENTRY_SIGNAL_INVALID -2
-struct ltk_menuentry;
-typedef struct ltk_menuentry ltk_menuentry;
-
typedef struct {
ltk_widget widget;
- ltk_menuentry **entries;
- size_t num_entries;
- size_t num_alloc;
+ ltk_array(widget_id) *entries;
double x_scroll_offset;
double y_scroll_offset;
int scroll_timer_id;
@@ -54,32 +49,32 @@ typedef struct {
/* FIXME: maybe need to set entire widget hierarchy to hover state so menu entry
is also hover when nested widget is hover? */
-struct ltk_menuentry {
+typedef struct {
ltk_widget widget;
/* FIXME: I guess if the regular label got the ability to
change its color, a label could just be used instead of this */
ltk_text_line *text_line;
- ltk_menu *submenu;
-};
+ ltk_widget_id submenu;
+} ltk_menuentry;
/* FIXME: ltk_orientation for hor/vert!
should submenus also allow setting orientation?
-> would maybe look weird in some cases */
/* FIXME: allow orientation */
-ltk_menu *ltk_menu_create(ltk_window *window);
-ltk_menu *ltk_submenu_create(ltk_window *window);
-ltk_menuentry *ltk_menuentry_create(ltk_window *window, const char *text);
-int ltk_menuentry_attach_submenu(ltk_menuentry *e, ltk_menu *submenu);
-int ltk_menuentry_detach_submenu(ltk_menuentry *e);
-const char *ltk_menuentry_get_text(ltk_menuentry *entry);
-int ltk_menu_insert_entry(ltk_menu *menu, ltk_menuentry *entry, size_t idx);
-int ltk_menu_add_entry(ltk_menu *menu, ltk_menuentry *entry);
-ltk_menuentry *ltk_menu_remove_entry_index(ltk_menu *menu, size_t idx);
-int ltk_menu_remove_entry(ltk_menu *menu, ltk_menuentry *entry);
-void ltk_menu_remove_all_entries(ltk_menu *menu);
-size_t ltk_menu_get_num_entries(ltk_menu *menu);
-ltk_menuentry *ltk_menu_get_entry(ltk_menu *menu, size_t idx);
-size_t ltk_menu_get_entry_index(ltk_menu *menu, ltk_menuentry *entry);
+ltk_widget_id ltk_menu_create(ltk_widget_id window);
+ltk_widget_id ltk_submenu_create(ltk_widget_id window);
+ltk_widget_id ltk_menuentry_create(ltk_widget_id window, const char *text);
+int ltk_menuentry_attach_submenu(ltk_widget_id entry, ltk_widget_id submenu);
+int ltk_menuentry_detach_submenu(ltk_widget_id entry);
+const char *ltk_menuentry_get_text(ltk_widget_id entry);
+int ltk_menu_insert_entry(ltk_widget_id menu, ltk_widget_id entry, size_t idx);
+int ltk_menu_add_entry(ltk_widget_id menu, ltk_widget_id entry);
+ltk_widget_id ltk_menu_remove_entry_index(ltk_widget_id menu, size_t idx);
+int ltk_menu_remove_entry(ltk_widget_id menu, ltk_widget_id entry);
+void ltk_menu_remove_all_entries(ltk_widget_id menu);
+size_t ltk_menu_get_num_entries(ltk_widget_id menu);
+ltk_widget_id ltk_menu_get_entry(ltk_widget_id menu, size_t idx);
+size_t ltk_menu_get_entry_index(ltk_widget_id menu, ltk_widget_id entry);
#endif /* LTK_MENU_H */
diff --git a/src/ltk/radiobutton.c b/src/ltk/radiobutton.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -210,14 +210,17 @@ ltk_radiobutton_draw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk
static void
uncheck_other_buttons(ltk_radiobutton *button) {
- ltk_radiobutton *prev = button->prev, *next = button->next;
+ ltk_widget *prev = ltk_get_widget_or_null_from_id(button->prev);
+ ltk_widget *next = ltk_get_widget_or_null_from_id(button->next);
while (prev) {
- ltk_radiobutton_set_checked(prev, 0);
- prev = prev->prev;
+ ltk_radiobutton_set_checked(prev->id, 0);
+ ltk_radiobutton *cur_btn = LTK_CAST_RADIOBUTTON(prev);
+ prev = ltk_get_widget_or_null_from_id(cur_btn->prev);
}
while (next) {
- ltk_radiobutton_set_checked(next, 0);
- next = next->next;
+ ltk_radiobutton_set_checked(next->id, 0);
+ ltk_radiobutton *cur_btn = LTK_CAST_RADIOBUTTON(next);
+ next = ltk_get_widget_or_null_from_id(cur_btn->next);
}
}
@@ -227,34 +230,38 @@ ltk_radiobutton_release(ltk_widget *self) {
button->checked = !button->checked;
if (button->checked)
uncheck_other_buttons(button);
- ltk_widget_emit_signal(self, LTK_RADIOBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_RADIOBUTTON_SIGNAL_CHANGED, LTK_EMPTY_ARGLIST);
return 1;
}
int
-ltk_radiobutton_get_checked(ltk_radiobutton *button) {
+ltk_radiobutton_get_checked(ltk_widget_id buttonid) {
+ ltk_widget *self = ltk_get_widget_from_id(buttonid);
+ ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self);
return button->checked;
}
void
-ltk_radiobutton_set_checked(ltk_radiobutton *button, int checked) {
+ltk_radiobutton_set_checked(ltk_widget_id buttonid, int checked) {
+ ltk_widget *self = ltk_get_widget_from_id(buttonid);
+ ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self);
button->checked = checked;
if (checked)
uncheck_other_buttons(button);
- ltk_widget *self = LTK_CAST_WIDGET(button);
- ltk_window_invalidate_widget_rect(self->window, self);
+ ltk_window_invalidate_widget_rect(self->window, self->id);
}
#define MAX(a, b) ((a) > (b) ? (a) : (b))
static void
recalc_ideal_size(ltk_radiobutton *button) {
+ ltk_widget *self = LTK_CAST_WIDGET(button);
int text_w, text_h;
ltk_text_line_get_size(button->tl, &text_w, &text_h);
- int circle_size = ltk_size_to_pixel(theme.circle_size, LTK_CAST_WIDGET(button)->last_dpi);
- int pad = ltk_size_to_pixel(theme.pad, LTK_CAST_WIDGET(button)->last_dpi);
- button->widget.ideal_w = text_w + pad * 3 + circle_size;
- button->widget.ideal_h = MAX(text_h, circle_size) + pad * 2;
+ int circle_size = ltk_size_to_pixel(theme.circle_size, self->last_dpi);
+ int pad = ltk_size_to_pixel(theme.pad, self->last_dpi);
+ self->ideal_w = text_w + pad * 3 + circle_size;
+ self->ideal_h = MAX(text_h, circle_size) + pad * 2;
}
static void
@@ -265,16 +272,18 @@ ltk_radiobutton_recalc_ideal_size(ltk_widget *self) {
recalc_ideal_size(button);
}
-ltk_radiobutton *
-ltk_radiobutton_create(ltk_window *window, const char *text, int checked, ltk_radiobutton *group_member) {
+ltk_widget_id
+ltk_radiobutton_create(ltk_widget_id windowid, const char *text, int checked, ltk_widget_id group_memberid) {
ltk_radiobutton *button = ltk_malloc(sizeof(ltk_radiobutton));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(button), window, &vtable, 0, 0);
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(button), windowid, &vtable, 0, 0);
button->checked = checked;
- button->prev = button->next = NULL;
- if (group_member) {
- button->prev = group_member;
+ button->prev = button->next = LTK_WIDGET_ID_NONE;
+ ltk_widget *group_memberw = ltk_get_widget_or_null_from_id(group_memberid);
+ if (group_memberw) {
+ button->prev = group_memberid;
+ ltk_radiobutton *group_member = LTK_CAST_RADIOBUTTON(group_memberw);
button->next = group_member->next;
- group_member->next = button;
+ group_member->next = id;
/* I guess it's technically possible for a button that is only created and
never added to the widget hierarchy to cause the other buttons to be
unchecked, but I guess that's just the way it is. */
@@ -286,23 +295,25 @@ ltk_radiobutton_create(ltk_window *window, const char *text, int checked, ltk_ra
theme.font, ltk_size_to_pixel(theme.font_size, button->widget.last_dpi), text, -1
);
recalc_ideal_size(button);
- button->widget.dirty = 1;
+ LTK_CAST_WIDGET(button)->dirty = 1;
- return button;
+ return id;
}
static void
ltk_radiobutton_destroy(ltk_widget *self, int shallow) {
(void)shallow;
ltk_radiobutton *button = LTK_CAST_RADIOBUTTON(self);
- if (!button) {
- ltk_warn("Tried to destroy NULL radiobutton.\n");
- return;
- }
ltk_text_line_destroy(button->tl);
- if (button->prev)
- button->prev->next = button->next;
- if (button->next)
- button->next->prev = button->prev;
+ ltk_widget *prevw = ltk_get_widget_or_null_from_id(button->prev);
+ if (prevw) {
+ ltk_radiobutton *prev = LTK_CAST_RADIOBUTTON(prevw);
+ prev->next = button->next;
+ }
+ ltk_widget *nextw = ltk_get_widget_or_null_from_id(button->next);
+ if (nextw) {
+ ltk_radiobutton *next = LTK_CAST_RADIOBUTTON(nextw);
+ next->prev = button->prev;
+ }
ltk_free(button);
}
diff --git a/src/ltk/radiobutton.h b/src/ltk/radiobutton.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -24,16 +24,16 @@
#define LTK_RADIOBUTTON_SIGNAL_CHANGED -1
#define LTK_RADIOBUTTON_SIGNAL_INVALID -2
-typedef struct ltk_radiobutton {
+typedef struct {
ltk_widget widget;
ltk_text_line *tl;
- struct ltk_radiobutton *prev;
- struct ltk_radiobutton *next;
+ ltk_widget_id prev;
+ ltk_widget_id next;
int checked;
} ltk_radiobutton;
-ltk_radiobutton *ltk_radiobutton_create(ltk_window *window, const char *text, int checked, ltk_radiobutton *group_member);
-int ltk_radiobutton_get_checked(ltk_radiobutton *button);
-void ltk_radiobutton_set_checked(ltk_radiobutton *button, int checked);
+ltk_widget_id ltk_radiobutton_create(ltk_widget_id windowid, const char *text, int checked, ltk_widget_id group_memberid);
+int ltk_radiobutton_get_checked(ltk_widget_id buttonid);
+void ltk_radiobutton_set_checked(ltk_widget_id buttonid, int checked);
#endif /* LTK_RADIOBUTTON_H */
diff --git a/src/ltk/scrollbar.c b/src/ltk/scrollbar.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -26,6 +26,7 @@
#include "graphics.h"
#include "scrollbar.h"
#include "eventdefs.h"
+#include "ltk.h"
#define MAX_SCROLLBAR_WIDTH 10000 /* completely arbitrary */
@@ -83,7 +84,9 @@ ltk_scrollbar_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len) {
}
void
-ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size) {
+ltk_scrollbar_set_virtual_size(ltk_widget_id scrollbarid, int virtual_size) {
+ ltk_widget *self = ltk_get_widget_from_id(scrollbarid);
+ ltk_scrollbar *scrollbar = LTK_CAST_SCROLLBAR(self);
/* FIXME: some sort of error? */
if (virtual_size <= 0)
return;
@@ -95,7 +98,7 @@ ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size) {
static ltk_rect
handle_get_rect(ltk_scrollbar *sc) {
ltk_rect r;
- ltk_rect sc_rect = sc->widget.lrect;
+ ltk_rect sc_rect = LTK_CAST_WIDGET(sc)->lrect;
if (sc->orient == LTK_HORIZONTAL) {
r.y = 0;
r.h = sc_rect.h;
@@ -156,20 +159,20 @@ ltk_scrollbar_mouse_press(ltk_widget *self, ltk_button_event *event) {
ltk_rect handle_rect = handle_get_rect(sc);
if (sc->orient == LTK_HORIZONTAL) {
if (ex < handle_rect.x || ex > handle_rect.x + handle_rect.w) {
- sc->cur_pos = (sc->virtual_size / (double)sc->widget.lrect.w) * (ex - handle_rect.w / 2 - sc->widget.lrect.x);
+ sc->cur_pos = (sc->virtual_size / (double)self->lrect.w) * (ex - handle_rect.w / 2 - self->lrect.x);
}
- max_pos = sc->virtual_size > sc->widget.lrect.w ? sc->virtual_size - sc->widget.lrect.w : 0;
+ max_pos = sc->virtual_size > self->lrect.w ? sc->virtual_size - self->lrect.w : 0;
} else {
if (ey < handle_rect.y || ey > handle_rect.y + handle_rect.h) {
- sc->cur_pos = (sc->virtual_size / (double)sc->widget.lrect.h) * (ey - handle_rect.h / 2 - sc->widget.lrect.y);
+ sc->cur_pos = (sc->virtual_size / (double)self->lrect.h) * (ey - handle_rect.h / 2 - self->lrect.y);
}
- max_pos = sc->virtual_size > sc->widget.lrect.h ? sc->virtual_size - sc->widget.lrect.h : 0;
+ max_pos = sc->virtual_size > self->lrect.h ? sc->virtual_size - self->lrect.h : 0;
}
if (sc->cur_pos < 0)
sc->cur_pos = 0;
else if (sc->cur_pos > max_pos)
sc->cur_pos = max_pos;
- ltk_widget_emit_signal(self, LTK_SCROLLBAR_SIGNAL_SCROLL, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_SCROLLBAR_SIGNAL_SCROLL, LTK_EMPTY_ARGLIST);
sc->last_mouse_x = event->x;
sc->last_mouse_y = event->y;
return 1;
@@ -178,16 +181,17 @@ ltk_scrollbar_mouse_press(ltk_widget *self, ltk_button_event *event) {
/* FIXME: also queue redraw */
/* FIXME: improve interface (scaled is weird) */
void
-ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled) {
+ltk_scrollbar_scroll(ltk_widget_id scrollbarid, int delta, int scaled) {
+ ltk_widget *self = ltk_get_widget_from_id(scrollbarid);
ltk_scrollbar *sc = LTK_CAST_SCROLLBAR(self);
int max_pos;
double scale;
if (sc->orient == LTK_HORIZONTAL) {
- max_pos = sc->virtual_size > sc->widget.lrect.w ? sc->virtual_size - sc->widget.lrect.w : 0;
- scale = sc->virtual_size / (double)sc->widget.lrect.w;
+ max_pos = sc->virtual_size > self->lrect.w ? sc->virtual_size - self->lrect.w : 0;
+ scale = sc->virtual_size / (double)self->lrect.w;
} else {
- max_pos = sc->virtual_size > sc->widget.lrect.h ? sc->virtual_size - sc->widget.lrect.h : 0;
- scale = sc->virtual_size / (double)sc->widget.lrect.h;
+ max_pos = sc->virtual_size > self->lrect.h ? sc->virtual_size - self->lrect.h : 0;
+ scale = sc->virtual_size / (double)self->lrect.h;
}
if (scaled)
sc->cur_pos += scale * delta;
@@ -197,7 +201,7 @@ ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled) {
sc->cur_pos = 0;
else if (sc->cur_pos > max_pos)
sc->cur_pos = max_pos;
- ltk_widget_emit_signal(self, LTK_SCROLLBAR_SIGNAL_SCROLL, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_SCROLLBAR_SIGNAL_SCROLL, LTK_EMPTY_ARGLIST);
}
static int
@@ -211,7 +215,7 @@ ltk_scrollbar_motion_notify(ltk_widget *self, ltk_motion_event *event) {
delta = event->x - sc->last_mouse_x;
else
delta = event->y - sc->last_mouse_y;
- ltk_scrollbar_scroll(self, delta, 1);
+ ltk_scrollbar_scroll(self->id, delta, 1);
sc->last_mouse_x = event->x;
sc->last_mouse_y = event->y;
return 1;
@@ -222,15 +226,15 @@ ltk_scrollbar_recalc_ideal_size(ltk_widget *self) {
ltk_scrollbar *sc = LTK_CAST_SCROLLBAR(self);
int size = ltk_size_to_pixel(theme.size, self->last_dpi);
if (sc->orient == LTK_HORIZONTAL)
- sc->widget.ideal_h = size;
+ self->ideal_h = size;
else
- sc->widget.ideal_w = size;
+ self->ideal_w = size;
}
-ltk_scrollbar *
-ltk_scrollbar_create(ltk_window *window, ltk_orientation orient) {
+ltk_widget_id
+ltk_scrollbar_create(ltk_widget_id windowid, ltk_orientation orient) {
ltk_scrollbar *sc = ltk_malloc(sizeof(ltk_scrollbar));
- ltk_fill_widget_defaults(LTK_CAST_WIDGET(sc), window, &vtable, 1, 1); /* FIXME: proper size */
+ ltk_widget_id id = ltk_initialize_widget(LTK_CAST_WIDGET(sc), windowid, &vtable, 1, 1); /* FIXME: proper size */
sc->last_mouse_x = sc->last_mouse_y = 0;
/* This cannot be 0 because that leads to divide-by-zero */
sc->virtual_size = 1;
@@ -238,7 +242,7 @@ ltk_scrollbar_create(ltk_window *window, ltk_orientation orient) {
sc->orient = orient;
ltk_scrollbar_recalc_ideal_size(LTK_CAST_WIDGET(sc));
- return sc;
+ return id;
}
static void
diff --git a/src/ltk/scrollbar.h b/src/ltk/scrollbar.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -32,8 +32,8 @@ typedef struct {
ltk_orientation orient;
} ltk_scrollbar;
-void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size);
-ltk_scrollbar *ltk_scrollbar_create(ltk_window *window, ltk_orientation orient);
-void ltk_scrollbar_scroll(ltk_widget *self, int delta, int scaled);
+void ltk_scrollbar_set_virtual_size(ltk_widget_id scrollbarid, int virtual_size);
+ltk_widget_id ltk_scrollbar_create(ltk_widget_id windowid, ltk_orientation orient);
+void ltk_scrollbar_scroll(ltk_widget_id scrollbarid, int delta, int scaled);
#endif /* LTK_SCROLLBAR_H */
diff --git a/src/ltk/widget.c b/src/ltk/widget.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -24,18 +24,24 @@
#include "memory.h"
#include "array.h"
#include "eventdefs.h"
+#include "ltk.h"
LTK_ARRAY_INIT_FUNC_DECL_STATIC(signal, ltk_signal_callback_info)
LTK_ARRAY_INIT_IMPL_STATIC(signal, ltk_signal_callback_info)
+LTK_ARRAY_INIT_IMPL(widget_id, ltk_widget_id)
/* FIXME: this should probably not take w and h */
-void
-ltk_fill_widget_defaults(
- ltk_widget *widget, ltk_window *window,
+/* FIXME: how much sense does it make to give a window id here?
+ maybe instead set that automatically when adding widget to container?
+ currently, it's weird anyways because each widget has a window set,
+ but when adding a widget to a container, nothing checks if the windows
+ match */
+ltk_widget_id
+ltk_initialize_widget(
+ ltk_widget *widget, ltk_widget_id windowid,
struct ltk_widget_vtable *vtable, int w, int h
) {
- widget->window = window;
- widget->parent = NULL;
+ widget->parent = LTK_WIDGET_ID_NONE;
/* FIXME: possibly check that draw and destroy aren't NULL */
widget->vtable = vtable;
@@ -63,41 +69,55 @@ ltk_fill_widget_defaults(
widget->hidden = 0;
widget->vtable_copied = 0;
widget->signal_cbs = NULL;
- /* FIXME: maybe set this to a dummy value here and don't initialize
- ideal_w/h at all until it is actually needed? */
- widget->last_dpi = ltk_renderer_get_window_dpi(window->renderwindow);
+
+ widget->id = ltk_store_widget(widget);
+ widget->window = windowid;
+ if (LTK_WIDGET_ID_IS_NONE(windowid)) {
+ widget->window = widget->id; /* a bit hacky */
+ } else {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ /* FIXME: maybe set this to a dummy value here and don't initialize
+ ideal_w/h at all until it is actually needed? */
+ widget->last_dpi = ltk_renderer_get_window_dpi(window->renderwindow);
+ }
/* FIXME: null other members! */
+ return widget->id;
}
void
ltk_widget_hide(ltk_widget *widget) {
/* FIXME: it may not make sense to call this here */
- if (ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_HIDE, LTK_EMPTY_ARGLIST))
+ if (ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_HIDE, LTK_EMPTY_ARGLIST))
return;
if (widget->vtable->hide)
widget->vtable->hide(widget);
widget->hidden = 1;
/* remove hover state */
/* FIXME: this needs to call change_state but that might cause issues */
- ltk_widget *hover = widget->window->hover_widget;
+ ltk_widget *windoww = ltk_get_widget_from_id(widget->window);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ ltk_widget *hover_orig = ltk_get_widget_or_null_from_id(window->hover_widget);
+ ltk_widget *hover = hover_orig;
while (hover) {
if (hover == widget) {
- widget->window->hover_widget->state &= ~LTK_HOVER;
- widget->window->hover_widget = NULL;
+ hover_orig->state &= ~LTK_HOVER;
+ window->hover_widget = LTK_WIDGET_ID_NONE;
break;
}
- hover = hover->parent;
+ hover = ltk_get_widget_or_null_from_id(hover->parent);
}
- ltk_widget *pressed = widget->window->pressed_widget;
+ ltk_widget *pressed_orig = ltk_get_widget_or_null_from_id(window->pressed_widget);
+ ltk_widget *pressed = pressed_orig;
while (pressed) {
if (pressed == widget) {
- widget->window->pressed_widget->state &= ~LTK_PRESSED;
- widget->window->pressed_widget = NULL;
+ pressed_orig->state &= ~LTK_PRESSED;
+ window->pressed_widget = LTK_WIDGET_ID_NONE;
break;
}
- pressed = pressed->parent;
+ pressed = ltk_get_widget_or_null_from_id(pressed->parent);
}
- ltk_widget *active = widget->window->active_widget;
+ ltk_widget *active = ltk_get_widget_or_null_from_id(window->active_widget);
/* if current active widget is child, set active widget to widget above in hierarchy */
int set_next = 0;
while (active) {
@@ -105,13 +125,19 @@ ltk_widget_hide(ltk_widget *widget) {
set_next = 1;
/* FIXME: use config values for all_activatable */
} else if (set_next && (active->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
- ltk_window_set_active_widget(active->window, active);
+ ltk_window_set_active_widget(active->window, active->id);
break;
}
- active = active->parent;
+ active = ltk_get_widget_or_null_from_id(active->parent);
}
if (set_next && !active)
- ltk_window_set_active_widget(active->window, NULL);
+ ltk_window_set_active_widget(active->window, LTK_WIDGET_ID_NONE);
+}
+
+void
+ltk_widget_id_hide(ltk_widget_id widgetid) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ ltk_widget_hide(widget);
}
/* FIXME: Maybe pass the new width as arg here?
@@ -119,7 +145,7 @@ ltk_widget_hide(ltk_widget *widget) {
/* FIXME: maybe give global and local position in event */
void
ltk_widget_resize(ltk_widget *widget) {
- if (ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_RESIZE, LTK_EMPTY_ARGLIST))
+ if (ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_RESIZE, LTK_EMPTY_ARGLIST))
return;
if (widget->vtable->resize)
widget->vtable->resize(widget);
@@ -134,7 +160,7 @@ ltk_widget_draw(ltk_widget *widget, ltk_surface *draw_surf, int x, int y, ltk_re
LTK_MAKE_ARG_INT(y),
LTK_MAKE_ARG_RECT(clip_rect)
};
- if (ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_DRAW, (ltk_callback_arglist){args, LENGTH(args)}))
+ if (ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_DRAW, (ltk_callback_arglist){args, LENGTH(args)}))
return;
if (widget->vtable->draw)
widget->vtable->draw(widget, draw_surf, x, y, clip_rect);
@@ -145,40 +171,49 @@ ltk_widget_change_state(ltk_widget *widget, ltk_widget_state old_state) {
if (old_state == widget->state)
return;
ltk_callback_arg args[] = {LTK_MAKE_ARG_INT(old_state)};
- if (ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_CHANGE_STATE, (ltk_callback_arglist){args, LENGTH(args)}))
+ if (ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_CHANGE_STATE, (ltk_callback_arglist){args, LENGTH(args)}))
return;
if (widget->vtable->change_state)
widget->vtable->change_state(widget, old_state);
if (widget->vtable->flags & LTK_NEEDS_REDRAW) {
widget->dirty = 1;
- ltk_window_invalidate_widget_rect(widget->window, widget);
+ ltk_window_invalidate_widget_rect(widget->window, widget->id);
}
}
/* FIXME: document that it's really dangerous to overwrite remove_child or destroy */
int
ltk_widget_destroy(ltk_widget *widget, int shallow) {
- ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_DESTROY, LTK_EMPTY_ARGLIST);
+ ltk_widget_id id = widget->id;
+ ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_DESTROY, LTK_EMPTY_ARGLIST);
/* widget->parent->remove_child should never be NULL because of the fact that
the widget is set as parent, but let's just check anyways... */
int invalid = 0;
- if (widget->parent) {
- if (widget->parent->vtable->remove_child)
- invalid = widget->parent->vtable->remove_child(widget->parent, widget);
- }
- if (widget->vtable_copied) {
- ltk_free(widget->vtable);
- widget->vtable = NULL;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(widget->parent);
+ if (parent) {
+ if (parent->vtable->remove_child)
+ invalid = parent->vtable->remove_child(parent, widget->id);
}
if (widget->signal_cbs) {
ltk_array_destroy(signal, widget->signal_cbs);
widget->signal_cbs = NULL;
}
- widget->vtable->destroy(widget, shallow);
+ ltk_widget_vtable *vtable = widget->vtable;
+ int vtable_copied = widget->vtable_copied;
+ vtable->destroy(widget, shallow);
+ if (vtable_copied)
+ ltk_free(vtable);
+ ltk_clear_widget_slot(id);
return invalid;
}
+int
+ltk_widget_id_destroy(ltk_widget_id widgetid, int shallow) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ return ltk_widget_destroy(widget, shallow);
+}
+
ltk_point
ltk_widget_pos_to_global(ltk_widget *widget, int x, int y) {
ltk_widget *cur = widget;
@@ -187,7 +222,7 @@ ltk_widget_pos_to_global(ltk_widget *widget, int x, int y) {
y += cur->lrect.y;
if (cur->popup)
break;
- cur = cur->parent;
+ cur = ltk_get_widget_or_null_from_id(cur->parent);
}
return (ltk_point){x, y};
}
@@ -200,13 +235,14 @@ ltk_global_to_widget_pos(ltk_widget *widget, int x, int y) {
y -= cur->lrect.y;
if (cur->popup)
break;
- cur = cur->parent;
+ cur = ltk_get_widget_or_null_from_id(cur->parent);
}
return (ltk_point){x, y};
}
int
-ltk_widget_register_signal_handler(ltk_widget *widget, int type, ltk_signal_callback callback, ltk_callback_arg data) {
+ltk_widget_register_signal_handler(ltk_widget_id widgetid, int type, ltk_signal_callback callback, ltk_callback_arg data) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if ((type >= LTK_WIDGET_SIGNAL_INVALID) || type <= widget->vtable->invalid_signal)
return 1;
if (!widget->signal_cbs) {
@@ -216,8 +252,13 @@ ltk_widget_register_signal_handler(ltk_widget *widget, int type, ltk_signal_call
return 0;
}
+/* FIXME: this takes widget_id, but calls functions that take ltk_widget*
+ Should this be changed to also take ltk_widget*?
+ Changing the callbacks to take widget_id doesn't really make sense because they
+ are only called from here anyways, where ltk_widget* already is required anyways */
int
-ltk_widget_emit_signal(ltk_widget *widget, int type, ltk_callback_arglist args) {
+ltk_widget_emit_signal(ltk_widget_id widgetid, int type, ltk_callback_arglist args) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->signal_cbs)
return 0;
int handled = 0;
@@ -235,7 +276,8 @@ filter_by_type(ltk_signal_callback_info *info, void *data) {
}
size_t
-ltk_widget_remove_signal_handler_by_type(ltk_widget *widget, int type) {
+ltk_widget_remove_signal_handler_by_type(ltk_widget_id widgetid, int type) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->signal_cbs)
return 0;
return ltk_array_remove_if(signal, widget->signal_cbs, &filter_by_type, &type);
@@ -251,7 +293,8 @@ filter_by_callback(ltk_signal_callback_info *info, void *data) {
}
size_t
-ltk_widget_remove_signal_handler_by_callback(ltk_widget *widget, ltk_signal_callback callback) {
+ltk_widget_remove_signal_handler_by_callback(ltk_widget_id widgetid, ltk_signal_callback callback) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->signal_cbs)
return 0;
/* callback can't be passed directly because ISO C forbids
@@ -273,10 +316,11 @@ filter_by_info(ltk_signal_callback_info *info, void *data) {
size_t
ltk_widget_remove_signal_handler_by_info(
- ltk_widget *widget,
+ ltk_widget_id widgetid,
int (*filter_func)(ltk_signal_callback_info *to_check, ltk_signal_callback_info *info),
ltk_signal_callback_info *info) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->signal_cbs)
return 0;
struct delete_wrapper data = {filter_func, info};
@@ -284,7 +328,8 @@ ltk_widget_remove_signal_handler_by_info(
}
void
-ltk_widget_remove_all_signal_handlers(ltk_widget *widget) {
+ltk_widget_remove_all_signal_handlers(ltk_widget_id widgetid) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->signal_cbs)
return;
ltk_array_destroy(signal, widget->signal_cbs);
@@ -294,7 +339,8 @@ ltk_widget_remove_all_signal_handlers(ltk_widget *widget) {
int ltk_widget_register_type(void); /* FIXME */
ltk_widget_vtable *
-ltk_widget_get_editable_vtable(ltk_widget *widget) {
+ltk_widget_get_editable_vtable(ltk_widget_id widgetid) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
if (!widget->vtable_copied) {
ltk_widget_vtable *vtable = ltk_malloc(sizeof(ltk_widget_vtable));
memcpy(vtable, widget->vtable, sizeof(ltk_widget_vtable));
@@ -305,7 +351,8 @@ ltk_widget_get_editable_vtable(ltk_widget *widget) {
void
ltk_widget_recalc_ideal_size(ltk_widget *widget) {
- unsigned int dpi = ltk_renderer_get_window_dpi(widget->window->renderwindow);
+ ltk_widget *windoww = ltk_get_widget_from_id(widget->window);
+ unsigned int dpi = ltk_renderer_get_window_dpi(LTK_CAST_WINDOW(windoww)->renderwindow);
if (dpi == widget->last_dpi)
return;
widget->last_dpi = dpi;
diff --git a/src/ltk/widget.h b/src/ltk/widget.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -22,14 +22,38 @@ that handler - maybe loop if container widget deletes children but they call par
/* FIXME: destroy signal for widgets (also window) */
#include <stddef.h>
+#include <stdint.h>
#include "array.h"
#include "event.h"
#include "graphics.h"
#include "rect.h"
#include "util.h"
-struct ltk_widget;
-struct ltk_window;
+/* This MUST be unsigned! */
+/* FIXME: would uint16_t be enough? */
+typedef uint32_t ltk_widget_id_int_type;
+/* This could be decreased if wanted (but it can't be larger than
+ the maximum size of ltk_widget_id_int_type!) */
+#define LTK_MAX_WIDGET_IDX ((ltk_widget_id_int_type)-1)
+typedef struct {
+ /* index in global widget array */
+ ltk_widget_id_int_type idx;
+ /* generation number, increased every time an index is reused
+ this is never allowed to be 0 for a valid widget */
+ ltk_widget_id_int_type gen;
+} ltk_widget_id;
+#define LTK_WIDGET_ID_NONE (ltk_widget_id){0, 0}
+/* Note: This is required in order to initialize a variable with static storage
+ duration since the (ltk_widget_id) cast causes the expression to not be
+ constant. Technically, this isn't necessary as long as the NONE value is just
+ {0, 0} since static variables are initialized to zero anyways, but I prefer
+ to be explicit. */
+#define LTK_WIDGET_ID_NONE_STATIC {0, 0}
+#define LTK_WIDGET_ID_IS_NONE(id) ((id).idx == 0 && (id).gen == 0)
+#define LTK_WIDGET_ID_EQUAL(a, b) ((a).idx == (b).idx && (a).gen == (b).gen)
+
+LTK_ARRAY_INIT_DECL(widget_id, ltk_widget_id)
+
typedef struct ltk_widget ltk_widget;
typedef enum {
@@ -100,6 +124,7 @@ typedef struct {
size_t sz;
char c;
ltk_widget *widget;
+ ltk_widget_id wid;
char *str;
const char *cstr;
ltk_key_event *key_event;
@@ -119,6 +144,7 @@ typedef struct {
LTK_TYPE_SIZE_T,
LTK_TYPE_CHAR,
LTK_TYPE_WIDGET,
+ LTK_TYPE_WIDGET_ID,
LTK_TYPE_STRING,
LTK_TYPE_CONST_STRING,
LTK_TYPE_KEY_EVENT,
@@ -137,6 +163,7 @@ typedef struct {
#define LTK_MAKE_ARG_SIZE_T(data) ((ltk_callback_arg){.type = LTK_TYPE_SIZE_T, .arg = {.sz = (data)}})
#define LTK_MAKE_ARG_CHAR(data) ((ltk_callback_arg){.type = LTK_TYPE_CHAR, .arg = {.c = (data)}})
#define LTK_MAKE_ARG_WIDGET(data) ((ltk_callback_arg){.type = LTK_TYPE_WIDGET, .arg = {.widget = (data)}})
+#define LTK_MAKE_ARG_WIDGET_ID(data) ((ltk_callback_arg){.type = LTK_TYPE_WIDGET_ID, .arg = {.wid = (data)}})
#define LTK_MAKE_ARG_STRING(data) ((ltk_callback_arg){.type = LTK_TYPE_STRING, .arg = {.str = (data)}})
#define LTK_MAKE_ARG_CONST_STRING(data) ((ltk_callback_arg){.type = LTK_TYPE_CONST_STRING, .arg = {.cstr = (data)}})
#define LTK_MAKE_ARG_KEY_EVENT(data) ((ltk_callback_arg){.type = LTK_TYPE_KEY_EVENT, .arg = {.key_event = (data)}})
@@ -153,6 +180,7 @@ typedef struct {
#define LTK_CAST_ARG_SIZE_T(carg) (ltk_assert(carg.type == LTK_TYPE_SIZE_T), carg.arg.sz)
#define LTK_CAST_ARG_CHAR(carg) (ltk_assert(carg.type == LTK_TYPE_CHAR), carg.arg.c)
#define LTK_CAST_ARG_WIDGET(carg) (ltk_assert(carg.type == LTK_TYPE_WIDGET), carg.arg.widget)
+#define LTK_CAST_ARG_WIDGET_ID(carg) (ltk_assert(carg.type == LTK_TYPE_WIDGET_ID), carg.arg.wid)
#define LTK_CAST_ARG_STRING(carg) (ltk_assert(carg.type == LTK_TYPE_STRING), carg.arg.str)
#define LTK_CAST_ARG_CONST_STRING(carg) (ltk_assert(carg.type == LTK_TYPE_CONST_STRING), carg.arg.cstr)
#define LTK_CAST_ARG_KEY_EVENT(carg) (ltk_assert(carg.type == LTK_TYPE_KEY_EVENT), carg.arg.key_event)
@@ -167,6 +195,7 @@ typedef struct {
#define LTK_GET_ARG_SIZE_T(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_SIZE_T(cargs.args[i]))
#define LTK_GET_ARG_CHAR(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_CHAR(cargs.args[i]))
#define LTK_GET_ARG_WIDGET(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_WIDGET(cargs.args[i]))
+#define LTK_GET_ARG_WIDGET_ID(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_WIDGET_ID(cargs.args[i]))
#define LTK_GET_ARG_STRING(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_STRING(cargs.args[i]))
#define LTK_GET_ARG_CONST_STRING(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_CONST_STRING(cargs.args[i]))
#define LTK_GET_ARG_KEY_EVENT(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_KEY_EVENT(cargs.args[i]))
@@ -177,19 +206,21 @@ typedef struct {
#define LTK_GET_ARG_RECT(cargs, i) (ltk_assert(i >= 0 && i < cargs.num), LTK_CAST_ARG_RECT(cargs.args[i]))
#define LTK_CAST_WIDGET(w) (&(w)->widget)
-#define LTK_CAST_WINDOW(w) (ltk_assert(w->vtable->type == LTK_WIDGET_WINDOW), (ltk_window *)(w))
-#define LTK_CAST_LABEL(w) (ltk_assert(w->vtable->type == LTK_WIDGET_LABEL), (ltk_label *)(w))
-#define LTK_CAST_BUTTON(w) (ltk_assert(w->vtable->type == LTK_WIDGET_BUTTON), (ltk_button *)(w))
-#define LTK_CAST_GRID(w) (ltk_assert(w->vtable->type == LTK_WIDGET_GRID), (ltk_grid *)(w))
-#define LTK_CAST_IMAGE_WIDGET(w) (ltk_assert(w->vtable->type == LTK_WIDGET_IMAGE), (ltk_image_widget *)(w))
-#define LTK_CAST_ENTRY(w) (ltk_assert(w->vtable->type == LTK_WIDGET_ENTRY), (ltk_entry *)(w))
-#define LTK_CAST_MENU(w) (ltk_assert(w->vtable->type == LTK_WIDGET_MENU), (ltk_menu *)(w))
-#define LTK_CAST_MENUENTRY(w) (ltk_assert(w->vtable->type == LTK_WIDGET_MENUENTRY), (ltk_menuentry *)(w))
-#define LTK_CAST_SCROLLBAR(w) (ltk_assert(w->vtable->type == LTK_WIDGET_SCROLLBAR), (ltk_scrollbar *)(w))
-#define LTK_CAST_BOX(w) (ltk_assert(w->vtable->type == LTK_WIDGET_BOX), (ltk_box *)(w))
-#define LTK_CAST_CHECKBUTTON(w) (ltk_assert(w->vtable->type == LTK_WIDGET_CHECKBUTTON), (ltk_checkbutton *)(w))
-#define LTK_CAST_RADIOBUTTON(w) (ltk_assert(w->vtable->type == LTK_WIDGET_RADIOBUTTON), (ltk_radiobutton *)(w))
-#define LTK_CAST_COMBOBOX(w) (ltk_assert(w->vtable->type == LTK_WIDGET_COMBOBOX), (ltk_combobox *)(w))
+#define LTK_CAST_WINDOW(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_WINDOW), (ltk_window *)(w))
+#define LTK_CAST_LABEL(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_LABEL), (ltk_label *)(w))
+#define LTK_CAST_BUTTON(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_BUTTON), (ltk_button *)(w))
+#define LTK_CAST_GRID(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_GRID), (ltk_grid *)(w))
+#define LTK_CAST_IMAGE_WIDGET(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_IMAGE), (ltk_image_widget *)(w))
+#define LTK_CAST_ENTRY(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_ENTRY), (ltk_entry *)(w))
+#define LTK_CAST_MENU(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_MENU), (ltk_menu *)(w))
+#define LTK_CAST_MENUENTRY(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_MENUENTRY), (ltk_menuentry *)(w))
+#define LTK_CAST_SCROLLBAR(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_SCROLLBAR), (ltk_scrollbar *)(w))
+#define LTK_CAST_BOX(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_BOX), (ltk_box *)(w))
+#define LTK_CAST_CHECKBUTTON(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_CHECKBUTTON), (ltk_checkbutton *)(w))
+#define LTK_CAST_RADIOBUTTON(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_RADIOBUTTON), (ltk_radiobutton *)(w))
+#define LTK_CAST_COMBOBOX(w) (ltk_assert((w)->vtable->type == LTK_WIDGET_COMBOBOX), (ltk_combobox *)(w))
+
+#define LTK_WIDGET_TYPE(w) (w->vtable->type)
/* FIXME: a bit weird because window never gets some of these signals */
#define LTK_WIDGET_SIGNAL_KEY_PRESS 1
@@ -228,23 +259,24 @@ typedef struct {
int type;
} ltk_signal_callback_info;
-int ltk_widget_register_signal_handler(ltk_widget *widget, int type, ltk_signal_callback callback, ltk_callback_arg data);
-int ltk_widget_emit_signal(ltk_widget *widget, int type, ltk_callback_arglist args);
-size_t ltk_widget_remove_signal_handler_by_type(ltk_widget *widget, int type);
-size_t ltk_widget_remove_signal_handler_by_callback(ltk_widget *widget, ltk_signal_callback callback);
+int ltk_widget_register_signal_handler(ltk_widget_id widgetid, int type, ltk_signal_callback callback, ltk_callback_arg data);
+int ltk_widget_emit_signal(ltk_widget_id widgetid, int type, ltk_callback_arglist args);
+size_t ltk_widget_remove_signal_handler_by_type(ltk_widget_id widgetid, int type);
+size_t ltk_widget_remove_signal_handler_by_callback(ltk_widget_id widgetid, ltk_signal_callback callback);
size_t ltk_widget_remove_signal_handler_by_info(
- ltk_widget *widget,
+ ltk_widget_id widgetid,
int (*filter_func)(ltk_signal_callback_info *to_check, ltk_signal_callback_info *info),
ltk_signal_callback_info *info
);
-void ltk_widget_remove_all_signal_handlers(ltk_widget *widget);
+void ltk_widget_remove_all_signal_handlers(ltk_widget_id widgetid);
int ltk_widget_register_type(void);
LTK_ARRAY_INIT_STRUCT_DECL(signal, ltk_signal_callback_info)
struct ltk_widget {
- struct ltk_window *window;
- struct ltk_widget *parent;
+ ltk_widget_id id;
+ ltk_widget_id window;
+ ltk_widget_id parent;
struct ltk_widget_vtable *vtable;
@@ -292,53 +324,53 @@ struct ltk_widget {
};
typedef struct ltk_widget_vtable {
- int (*key_press)(struct ltk_widget *, ltk_key_event *);
- int (*key_release)(struct ltk_widget *, ltk_key_event *);
+ int (*key_press)(ltk_widget *self, ltk_key_event *event);
+ int (*key_release)(ltk_widget *self, ltk_key_event *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 *);
- int (*press)(struct ltk_widget *);
- int (*release)(struct ltk_widget *);
- void (*cmd_return)(struct ltk_widget *self, char *text, size_t len);
+ int (*mouse_press)(ltk_widget *self, ltk_button_event *event);
+ int (*mouse_release)(ltk_widget *self, ltk_button_event *event);
+ int (*mouse_scroll)(ltk_widget *self, ltk_scroll_event *event);
+ int (*motion_notify)(ltk_widget *self, ltk_motion_event *event);
+ int (*mouse_leave)(ltk_widget *self, ltk_motion_event *event);
+ int (*mouse_enter)(ltk_widget *self, ltk_motion_event *event);
+ int (*press)(ltk_widget *self);
+ int (*release)(ltk_widget *self);
+ void (*cmd_return)(ltk_widget *self, char *text, size_t len);
/* This is called when the DPI changes. It must not call child_size_change on
the parent or do any actual resizing (only ideal_w and ideal_h should be set),
but it should call ltk_widget_recalc_ideal_size on any child widgets. */
/* When a widget is added to a container, the container should call
ltk_widget_recalc_ideal_size on the widget in case the DPI changed. */
- void (*recalc_ideal_size)(struct ltk_widget *);
- void (*resize)(struct ltk_widget *);
- void (*hide)(struct ltk_widget *);
+ void (*recalc_ideal_size)(ltk_widget *self);
+ void (*resize)(ltk_widget *self);
+ void (*hide)(ltk_widget *self);
/* draw_surface: surface to draw it on
x, y: position of logical rectangle on surface
clip: clipping rectangle, relative to logical rectangle */
- void (*draw)(struct ltk_widget *self, ltk_surface *draw_surface, int x, int y, ltk_rect clip);
- void (*change_state)(struct ltk_widget *, ltk_widget_state);
- void (*destroy)(struct ltk_widget *, int);
+ void (*draw)(ltk_widget *self, ltk_surface *draw_surface, int x, int y, ltk_rect clip);
+ void (*change_state)(ltk_widget *self, ltk_widget_state state);
+ void (*destroy)(ltk_widget *self, int shallow);
/* rect is in self's coordinate system */
- struct ltk_widget *(*nearest_child)(struct ltk_widget *self, ltk_rect rect);
- struct ltk_widget *(*nearest_child_left)(struct ltk_widget *self, ltk_widget *widget);
- struct ltk_widget *(*nearest_child_right)(struct ltk_widget *self, ltk_widget *widget);
- struct ltk_widget *(*nearest_child_above)(struct ltk_widget *self, ltk_widget *widget);
- struct ltk_widget *(*nearest_child_below)(struct ltk_widget *self, ltk_widget *widget);
- struct ltk_widget *(*next_child)(struct ltk_widget *self, ltk_widget *child);
- struct ltk_widget *(*prev_child)(struct ltk_widget *self, ltk_widget *child);
- struct ltk_widget *(*first_child)(struct ltk_widget *self);
- struct ltk_widget *(*last_child)(struct ltk_widget *self);
+ ltk_widget_id (*nearest_child)(ltk_widget *self, ltk_rect rect);
+ ltk_widget_id (*nearest_child_left)(ltk_widget *self, ltk_widget_id widget);
+ ltk_widget_id (*nearest_child_right)(ltk_widget *self, ltk_widget_id widget);
+ ltk_widget_id (*nearest_child_above)(ltk_widget *self, ltk_widget_id widget);
+ ltk_widget_id (*nearest_child_below)(ltk_widget *self, ltk_widget_id widget);
+ ltk_widget_id (*next_child)(ltk_widget *self, ltk_widget_id child);
+ ltk_widget_id (*prev_child)(ltk_widget *self, ltk_widget_id child);
+ ltk_widget_id (*first_child)(ltk_widget *self);
+ ltk_widget_id (*last_child)(ltk_widget *self);
/* This is called when the ideal size of a child is changed (e.g. text is
added to a text entry). */
- void (*child_size_change)(struct ltk_widget *, struct ltk_widget *);
- int (*remove_child)(struct ltk_widget *, struct ltk_widget *);
+ void (*child_size_change)(ltk_widget *self, ltk_widget_id child);
+ int (*remove_child)(ltk_widget *self, ltk_widget_id child);
/* x and y relative to widget's lrect! */
- struct ltk_widget *(*get_child_at_pos)(struct ltk_widget *, int x, int y);
+ ltk_widget_id (*get_child_at_pos)(ltk_widget *self, int x, int y);
/* r is in self's coordinate system */
- void (*ensure_rect_shown)(struct ltk_widget *self, ltk_rect r);
+ void (*ensure_rect_shown)(ltk_widget *self, ltk_rect r);
ltk_widget_type type;
ltk_widget_flags flags;
@@ -346,11 +378,14 @@ typedef struct ltk_widget_vtable {
} ltk_widget_vtable;
void ltk_widget_hide(ltk_widget *widget);
+void ltk_widget_id_hide(ltk_widget_id widgetid);
int ltk_widget_destroy(ltk_widget *widget, int shallow);
-void ltk_fill_widget_defaults(
- ltk_widget *widget, struct ltk_window *window,
+int ltk_widget_id_destroy(ltk_widget_id widgetid, int shallow);
+ltk_widget_id ltk_initialize_widget(
+ ltk_widget *widget, ltk_widget_id windowid,
struct ltk_widget_vtable *vtable, int w, int h
);
+/* FIXME: container should always check if parent of child widget is correct (or at least before destroying?) */
void ltk_widget_change_state(ltk_widget *widget, ltk_widget_state old_state);
void ltk_widget_resize(ltk_widget *widget);
void ltk_widget_draw(ltk_widget *widget, ltk_surface *draw_surf, int x, int y, ltk_rect clip_rect);
@@ -359,7 +394,9 @@ void ltk_widget_get_ideal_size(ltk_widget *widget, unsigned int *ideal_w_ret, un
ltk_point ltk_widget_pos_to_global(ltk_widget *widget, int x, int y);
ltk_point ltk_global_to_widget_pos(ltk_widget *widget, int x, int y);
-ltk_widget_vtable *ltk_widget_get_editable_vtable(ltk_widget *widget);
+/* FIXME: either remove this functionality or update all widget functions
+ so they never assume that a certain entry is actually present in the vtable */
+ltk_widget_vtable *ltk_widget_get_editable_vtable(ltk_widget_id widgetid);
void ltk_widget_recalc_ideal_size(ltk_widget *widget);
diff --git a/src/ltk/widget_internal.h b/src/ltk/widget_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2024-2026 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
@@ -17,6 +17,8 @@
#ifndef LTK_WIDGET_INTERNAL_H
#define LTK_WIDGET_INTERNAL_H
+/* FIXME: use __attribute__((visibility("hidden"))) for compilers that support it */
+
#include <stddef.h>
#include "array.h"
@@ -53,6 +55,7 @@ void ltk_entry_get_keybinding_parseinfo(
void ltk_window_get_theme_parseinfo(ltk_theme_parseinfo **p, size_t *len);
ltk_window *ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y, unsigned int w, unsigned int h);
+void ltk_window_destroy(ltk_widget *self, int shallow);
void ltk_window_destroy_intern(ltk_window *window);
void ltk_window_cleanup(void);
void ltk_window_get_keybinding_parseinfo(
@@ -61,10 +64,7 @@ void ltk_window_get_keybinding_parseinfo(
ltk_array(keypress) **presses_ret, ltk_array(keyrelease) **releases_ret
);
-/* FIXME: how to avoid bad things happening while external program open? maybe store cmd widget somewhere (but could be multiple!) and check if widget to destroy is one of those
--> alternative: store all widgets in array and only give out IDs, then when returning from cmd, widget is already destroyed and can be ignored
--> first option maybe just set callback, etc. of current cmd to NULL so widget can still be destroyed */
-int ltk_call_cmd(ltk_widget *caller, ltk_array(cmd) *cmd, const char *text, size_t textlen);
+int ltk_call_cmd(ltk_widget_id caller, ltk_array(cmd) *cmd, const char *text, size_t textlen);
int ltk_widget_handle_keypress_bindings(ltk_widget *widget, ltk_key_event *event, ltk_array(keypress) *keypresses, int handled);
int ltk_widget_handle_keyrelease_bindings(ltk_widget *widget, ltk_key_event *event, ltk_array(keyrelease) *keyreleases, int handled);
diff --git a/src/ltk/window.c b/src/ltk/window.c
@@ -1,6 +1,6 @@
/* FIXME: signal handling is really ugly and inconsistent at the moment */
/*
- * Copyright (c) 2020-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2020-2026 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
@@ -31,7 +31,7 @@
#define MAX_WINDOW_FONT_SIZE 20000
-static void gen_widget_stack(ltk_widget *bottom);
+static void gen_widget_stack(ltk_widget_id bottom);
static ltk_widget *get_hover_popup(ltk_window *window, int x, int y);
static int is_parent(ltk_widget *parent, ltk_widget *child);
static ltk_widget *get_widget_under_pointer(ltk_widget *widget, int x, int y, int *local_x_ret, int *local_y_ret);
@@ -115,9 +115,7 @@ ltk_window_get_keybinding_parseinfo(
}
/* needed for passing keyboard events down the hierarchy */
-static ltk_widget **widget_stack = NULL;
-static size_t widget_stack_alloc = 0;
-static size_t widget_stack_len = 0;
+static ltk_array(widget_id) *widget_stack = NULL;
static struct {
int border_width;
@@ -146,20 +144,22 @@ ltk_window_cleanup(void) {
ltk_keyrelease_bindings_destroy(keyreleases);
keypresses = NULL;
keyreleases = NULL;
- ltk_free(widget_stack);
+ if (widget_stack)
+ ltk_array_destroy(widget_id, widget_stack);
widget_stack = NULL;
}
static void
ensure_active_widget_shown(ltk_window *window) {
- ltk_widget *widget = window->active_widget;
+ ltk_widget *widget = ltk_get_widget_or_null_from_id(window->active_widget);
if (!widget)
return;
ltk_rect r = widget->lrect;
- while (widget->parent) {
- if (widget->parent->vtable->ensure_rect_shown)
- widget->parent->vtable->ensure_rect_shown(widget->parent, r);
- widget = widget->parent;
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(widget->parent);
+ while (parent) {
+ if (parent->vtable->ensure_rect_shown)
+ parent->vtable->ensure_rect_shown(parent, r);
+ widget = parent;
r.x += widget->lrect.x;
r.y += widget->lrect.y;
/* FIXME: this currently just aborts if a widget is positioned
@@ -167,8 +167,12 @@ ensure_active_widget_shown(ltk_window *window) {
be in that case */
if (widget->popup)
break;
+ parent = ltk_get_widget_or_null_from_id(parent->parent);
}
- ltk_window_invalidate_widget_rect(window, widget);
+ /* FIXME: there could be weird situations with widgets that are not
+ geometrically within their parent so that this doesn't
+ invalidate all necessary regions */
+ ltk_window_invalidate_widget_rect(LTK_CAST_WIDGET(window)->id, widget->id);
}
/* FIXME: should keyrelease events be ignored if the corresponding keypress event
@@ -181,11 +185,14 @@ ltk_window_key_press_event(ltk_widget *self, ltk_key_event *event) {
ltk_window *window = LTK_CAST_WINDOW(self);
int handled = 0;
ltk_callback_arg args[] = {LTK_MAKE_ARG_KEY_EVENT(event)};
- if (window->active_widget && (window->active_widget->state & LTK_FOCUSED)) {
+ ltk_widget *active_widget = ltk_get_widget_or_null_from_id(window->active_widget);
+ if (active_widget && (active_widget->state & LTK_FOCUSED)) {
gen_widget_stack(window->active_widget);
- for (size_t i = widget_stack_len; i-- > 0 && !handled;) {
- if (ltk_widget_emit_signal(widget_stack[i], LTK_WIDGET_SIGNAL_KEY_PRESS, (ltk_callback_arglist){args, LENGTH(args)}) ||
- (widget_stack[i]->vtable->key_press && widget_stack[i]->vtable->key_press(widget_stack[i], event))) {
+ for (size_t i = ltk_array_len(widget_stack); i-- > 0 && !handled;) {
+ ltk_widget_id id = ltk_array_get(widget_stack, i);
+ ltk_widget *widget = ltk_get_widget_from_id(id);
+ if (ltk_widget_emit_signal(id, LTK_WIDGET_SIGNAL_KEY_PRESS, (ltk_callback_arglist){args, LENGTH(args)}) ||
+ (widget->vtable->key_press && widget->vtable->key_press(widget, event))) {
handled = 1;
break;
}
@@ -201,11 +208,14 @@ ltk_window_key_release_event(ltk_widget *self, ltk_key_event *event) {
ltk_window *window = LTK_CAST_WINDOW(self);
int handled = 0;
ltk_callback_arg args[] = {LTK_MAKE_ARG_KEY_EVENT(event)};
- if (window->active_widget && (window->active_widget->state & LTK_FOCUSED)) {
+ ltk_widget *active_widget = ltk_get_widget_or_null_from_id(window->active_widget);
+ if (active_widget && (active_widget->state & LTK_FOCUSED)) {
gen_widget_stack(window->active_widget);
- for (size_t i = widget_stack_len; i-- > 0 && !handled;) {
- if (ltk_widget_emit_signal(widget_stack[i], LTK_WIDGET_SIGNAL_KEY_RELEASE, (ltk_callback_arglist){args, LENGTH(args)}) ||
- (widget_stack[i]->vtable->key_release && widget_stack[i]->vtable->key_release(widget_stack[i], event))) {
+ for (size_t i = ltk_array_len(widget_stack); i-- > 0 && !handled;) {
+ ltk_widget_id id = ltk_array_get(widget_stack, i);
+ ltk_widget *widget = ltk_get_widget_from_id(id);
+ if (ltk_widget_emit_signal(id, LTK_WIDGET_SIGNAL_KEY_RELEASE, (ltk_callback_arglist){args, LENGTH(args)}) ||
+ (widget->vtable->key_release && widget->vtable->key_release(widget, event))) {
handled = 1;
break;
}
@@ -222,11 +232,11 @@ ltk_window_mouse_press_event(ltk_widget *self, ltk_button_event *event) {
ltk_widget *widget = get_hover_popup(window, event->x, event->y);
int check_hide = 0;
if (!widget) {
- widget = window->root_widget;
+ widget = ltk_get_widget_or_null_from_id(window->root_widget);
check_hide = 1;
}
if (!widget) {
- ltk_window_unregister_all_popups(window);
+ ltk_window_unregister_all_popups(self->id);
return 1;
}
int orig_x = event->x, orig_y = event->y;
@@ -235,15 +245,15 @@ ltk_window_mouse_press_event(ltk_widget *self, ltk_button_event *event) {
-> also, should the widget still get mouse_press even if state doesn't change? */
/* FIXME: doesn't work with e.g. disabled menu entries */
if (!(cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
- ltk_window_unregister_all_popups(window);
+ ltk_window_unregister_all_popups(self->id);
}
/* FIXME: this doesn't make much sense if the popups aren't a
hierarchy (right now, they're just menus, so that's always
a hierarchy */
/* don't hide popups if they are children of the now pressed widget */
- if (check_hide && !(window->popups_num > 0 && is_parent(cur_widget, window->popups[0])))
- ltk_window_unregister_all_popups(window);
+ if (check_hide && !(ltk_array_len(window->popups) > 0 && is_parent(cur_widget, ltk_get_widget_or_null_from_id(ltk_array_get(window->popups, 0)))))
+ ltk_window_unregister_all_popups(self->id);
/* FIXME: popups don't always have their children geometrically contained within parents,
so this won't work properly in all cases */
@@ -257,18 +267,18 @@ ltk_window_mouse_press_event(ltk_widget *self, 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 */
- handled = ltk_widget_emit_signal(cur_widget, LTK_WIDGET_SIGNAL_MOUSE_PRESS, (ltk_callback_arglist){args, LENGTH(args)});
+ handled = ltk_widget_emit_signal(cur_widget->id, LTK_WIDGET_SIGNAL_MOUSE_PRESS, (ltk_callback_arglist){args, LENGTH(args)});
if (!handled && 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 && event->type == LTK_BUTTONPRESS_EVENT && (cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
- ltk_window_set_pressed_widget(window, cur_widget, 0);
+ ltk_window_set_pressed_widget(self->id, cur_widget->id, 0);
first = 0;
}
}
if (!handled)
- cur_widget = cur_widget->parent;
+ cur_widget = ltk_get_widget_or_null_from_id(cur_widget->parent);
else
break;
}
@@ -281,7 +291,7 @@ ltk_window_mouse_scroll_event(ltk_widget *self, 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;
+ widget = ltk_get_widget_or_null_from_id(window->root_widget);
if (!widget)
return 1;
int orig_x = event->x, orig_y = event->y;
@@ -297,12 +307,12 @@ ltk_window_mouse_scroll_event(ltk_widget *self, ltk_scroll_event *event) {
/* FIXME: see function above
if (queue_scroll_event(cur_widget, event->x, event->y, event->dx, event->dy))
handled = 1; */
- handled = ltk_widget_emit_signal(cur_widget, LTK_WIDGET_SIGNAL_MOUSE_SCROLL, (ltk_callback_arglist){args, LENGTH(args)});
+ handled = ltk_widget_emit_signal(cur_widget->id, LTK_WIDGET_SIGNAL_MOUSE_SCROLL, (ltk_callback_arglist){args, LENGTH(args)});
if (!handled && cur_widget->vtable->mouse_scroll)
handled = cur_widget->vtable->mouse_scroll(cur_widget, event);
}
if (!handled)
- cur_widget = cur_widget->parent;
+ cur_widget = ltk_get_widget_or_null_from_id(cur_widget->parent);
else
break;
}
@@ -310,19 +320,19 @@ ltk_window_mouse_scroll_event(ltk_widget *self, ltk_scroll_event *event) {
}
void
-ltk_window_fake_motion_event(ltk_window *window, int x, int y) {
- ltk_widget *self = LTK_CAST_WIDGET(window);
+ltk_window_fake_motion_event(ltk_widget_id windowid, int x, int y) {
+ ltk_widget *self = ltk_get_widget_from_id(windowid);
ltk_motion_event e = {.type = LTK_MOTION_EVENT, .x = x, .y = y};
ltk_callback_arg args[] = {LTK_MAKE_ARG_MOTION_EVENT(&e)};
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
- self->vtable->motion_notify(LTK_CAST_WIDGET(window), &e);
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
+ self->vtable->motion_notify(self, &e);
}
}
static int
ltk_window_mouse_release_event(ltk_widget *self, ltk_button_event *event) {
ltk_window *window = LTK_CAST_WINDOW(self);
- ltk_widget *widget = window->pressed_widget;
+ ltk_widget *widget = ltk_get_widget_or_null_from_id(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) {
@@ -335,23 +345,24 @@ ltk_window_mouse_release_event(ltk_widget *self, ltk_button_event *event) {
/* NOP */
if (widget) {
ltk_callback_arg args[] = {LTK_MAKE_ARG_BUTTON_EVENT(event)};
- if (!ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_MOUSE_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_MOUSE_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
if (widget->vtable->mouse_release)
widget->vtable->mouse_release(widget, event);
}
}
if (event->button == LTK_BUTTONL && event->type == LTK_BUTTONRELEASE_EVENT) {
int release = 0;
- if (window->pressed_widget) {
- ltk_rect prect = window->pressed_widget->lrect;
- ltk_point pglob = ltk_widget_pos_to_global(window->pressed_widget, 0, 0);
+ ltk_widget *pressed_widget = ltk_get_widget_or_null_from_id(window->pressed_widget);
+ if (pressed_widget) {
+ ltk_rect prect = pressed_widget->lrect;
+ ltk_point pglob = ltk_widget_pos_to_global(pressed_widget, 0, 0);
if (ltk_collide_rect((ltk_rect){pglob.x, pglob.y, prect.w, prect.h}, orig_x, orig_y))
release = 1;
}
- ltk_window_set_pressed_widget(window, NULL, release);
+ ltk_window_set_pressed_widget(self->id, LTK_WIDGET_ID_NONE, release);
/* send motion notify to widget under pointer */
/* FIXME: only when not collide with rect? */
- ltk_window_fake_motion_event(window, orig_x, orig_y);
+ ltk_window_fake_motion_event(self->id, orig_x, orig_y);
}
return 1;
}
@@ -363,24 +374,24 @@ ltk_window_motion_notify_event(ltk_widget *self, ltk_motion_event *event) {
int orig_x = event->x, orig_y = event->y;
ltk_callback_arg args[] = {LTK_MAKE_ARG_MOTION_EVENT(event)};
if (!widget) {
- widget = window->pressed_widget;
+ widget = ltk_get_widget_or_null_from_id(window->pressed_widget);
if (widget) {
ltk_point local = ltk_global_to_widget_pos(widget, event->x, event->y);
event->x = local.x;
event->y = local.y;
- if (!ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(widget->id, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
if (widget->vtable->motion_notify)
widget->vtable->motion_notify(widget, event);
}
return 1;
}
- widget = window->root_widget;
+ widget = ltk_get_widget_or_null_from_id(window->root_widget);
}
if (!widget)
return 1;
ltk_point local = ltk_global_to_widget_pos(widget, event->x, event->y);
if (!ltk_collide_rect((ltk_rect){0, 0, widget->lrect.w, widget->lrect.h}, local.x, local.y)) {
- ltk_window_set_hover_widget(widget->window, NULL, event);
+ ltk_window_set_hover_widget(widget->window, LTK_WIDGET_ID_NONE, event);
return 1;
}
ltk_widget *cur_widget = get_widget_under_pointer(widget, event->x, event->y, &event->x, &event->y);
@@ -394,7 +405,7 @@ ltk_window_motion_notify_event(ltk_widget *self, ltk_motion_event *event) {
/* FIXME: see functions above
if (queue_mouse_event(cur_widget, LTK_MOTION_EVENT, event->x, event->y))
handled = 1; */
- handled = ltk_widget_emit_signal(cur_widget, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)});
+ handled = ltk_widget_emit_signal(cur_widget->id, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)});
if (!handled && cur_widget->vtable->motion_notify)
handled = cur_widget->vtable->motion_notify(cur_widget, event);
/* set first non-disabled widget to hover widget */
@@ -404,37 +415,42 @@ ltk_window_motion_notify_event(ltk_widget *self, ltk_motion_event *event) {
if (first && (cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
event->x = orig_x;
event->y = orig_y;
- ltk_window_set_hover_widget(window, cur_widget, event);
+ ltk_window_set_hover_widget(self->id, cur_widget->id, event);
first = 0;
}
}
if (!handled)
- cur_widget = cur_widget->parent;
+ cur_widget = ltk_get_widget_or_null_from_id(cur_widget->parent);
else
break;
}
if (first) {
event->x = orig_x;
event->y = orig_y;
- ltk_window_set_hover_widget(window, NULL, event);
+ ltk_window_set_hover_widget(self->id, LTK_WIDGET_ID_NONE, event);
}
return 1;
}
void
-ltk_window_set_root_widget(ltk_window *window, ltk_widget *widget) {
- window->root_widget = widget;
+ltk_window_set_root_widget(ltk_widget_id windowid, ltk_widget_id widgetid) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
+ window->root_widget = widgetid;
widget->lrect.x = 0;
widget->lrect.y = 0;
widget->lrect.w = window->rect.w;
widget->lrect.h = window->rect.h;
widget->crect = widget->lrect;
- ltk_window_invalidate_rect(window, widget->lrect);
+ ltk_window_invalidate_rect(windowid, widget->lrect);
ltk_widget_resize(widget);
}
void
-ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
+ltk_window_invalidate_rect(ltk_widget_id windowid, ltk_rect rect) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
if (window->dirty_rect.w == 0 && window->dirty_rect.h == 0)
window->dirty_rect = rect;
else
@@ -442,7 +458,8 @@ ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
}
void
-ltk_window_invalidate_widget_rect(ltk_window *window, ltk_widget *widget) {
+ltk_window_invalidate_widget_rect(ltk_widget_id window, ltk_widget_id widgetid) {
+ ltk_widget *widget = ltk_get_widget_from_id(widgetid);
ltk_point glob = ltk_widget_pos_to_global(widget, 0, 0);
ltk_window_invalidate_rect(window, (ltk_rect){glob.x, glob.y, widget->lrect.w, widget->lrect.h});
}
@@ -455,7 +472,6 @@ ltk_window_redraw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_re
(void)clip;
if (!self) return;
ltk_window *window = LTK_CAST_WINDOW(self);
- ltk_widget *ptr;
if (window->dirty_rect.x >= window->rect.w) return;
if (window->dirty_rect.y >= window->rect.h) return;
if (window->dirty_rect.x + window->dirty_rect.w > window->rect.w)
@@ -465,13 +481,13 @@ ltk_window_redraw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_re
/* FIXME: this should use window->dirty_rect, but that doesn't work
properly with double buffering */
ltk_surface_fill_rect(window->surface, theme.bg, (ltk_rect){0, 0, window->rect.w, window->rect.h});
- if (window->root_widget) {
- ptr = window->root_widget;
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (ptr) {
ltk_widget_draw(ptr, window->surface, 0, 0, window->rect);
}
/* last popup is the newest one, so draw that last */
- for (size_t i = 0; i < window->popups_num; i++) {
- ptr = window->popups[i];
+ for (size_t i = 0; i < ltk_array_len(window->popups); i++) {
+ ptr = ltk_get_widget_from_id(ltk_array_get(window->popups, i));
ltk_widget_draw(ptr, window->surface, ptr->lrect.x, ptr->lrect.y, ltk_rect_relative(ptr->lrect, window->rect));
}
ltk_renderer_swap_buffers(window->renderwindow);
@@ -481,10 +497,11 @@ ltk_window_redraw(ltk_widget *self, ltk_surface *draw_surf, int x, int y, ltk_re
static void
ltk_window_other_event(ltk_window *window, ltk_event *event) {
- ltk_widget *ptr = window->root_widget;
+ ltk_widget *self = LTK_CAST_WIDGET(window);
+ ltk_widget *ptr = ltk_get_widget_or_null_from_id(window->root_widget);
/* FIXME: decide whether this should be moved to separate resize function in window vtable */
if (event->type == LTK_CONFIGURE_EVENT) {
- ltk_window_unregister_all_popups(window);
+ ltk_window_unregister_all_popups(self->id);
int w, h;
w = event->configure.w;
h = event->configure.h;
@@ -493,9 +510,9 @@ ltk_window_other_event(ltk_window *window, ltk_event *event) {
if (orig_w != w || orig_h != h) {
window->rect.w = w;
window->rect.h = h;
- ltk_window_invalidate_rect(window, window->rect);
+ ltk_window_invalidate_rect(self->id, window->rect);
ltk_surface_update_size(window->surface, w, h);
- if (ltk_widget_emit_signal(LTK_CAST_WIDGET(window), LTK_WIDGET_SIGNAL_RESIZE, LTK_EMPTY_ARGLIST))
+ if (ltk_widget_emit_signal(self->id, LTK_WIDGET_SIGNAL_RESIZE, LTK_EMPTY_ARGLIST))
return;
if (ptr) {
ptr->lrect.w = w;
@@ -510,55 +527,41 @@ ltk_window_other_event(ltk_window *window, ltk_event *event) {
r.y = event->expose.y;
r.w = event->expose.w;
r.h = event->expose.h;
- ltk_window_invalidate_rect(window, r);
+ ltk_window_invalidate_rect(self->id, r);
} else if (event->type == LTK_WINDOWCLOSE_EVENT) {
- ltk_widget_emit_signal(LTK_CAST_WIDGET(window), LTK_WINDOW_SIGNAL_CLOSE, LTK_EMPTY_ARGLIST);
+ ltk_widget_emit_signal(self->id, LTK_WINDOW_SIGNAL_CLOSE, LTK_EMPTY_ARGLIST);
} else if (event->type == LTK_DPICHANGE_EVENT) {
- if (window->root_widget) {
- ltk_window_unregister_all_popups(window); /* easier than trying to resize them */
- ltk_widget_recalc_ideal_size(window->root_widget);
- ltk_widget_resize(window->root_widget);
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (root_widget) {
+ ltk_window_unregister_all_popups(self->id); /* easier than trying to resize them */
+ ltk_widget_recalc_ideal_size(root_widget);
+ ltk_widget_resize(root_widget);
}
}
}
/* FIXME: check for duplicates? */
void
-ltk_window_register_popup(ltk_window *window, ltk_widget *popup) {
- if (window->popups_num == window->popups_alloc) {
- window->popups_alloc = ideal_array_size(
- window->popups_alloc, window->popups_num + 1
- );
- window->popups = ltk_reallocarray(
- window->popups, window->popups_alloc, sizeof(ltk_widget *)
- );
- }
- window->popups[window->popups_num++] = popup;
+ltk_window_register_popup(ltk_widget_id windowid, ltk_widget_id popup_id) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ ltk_array_append(widget_id, window->popups, popup_id);
+ ltk_widget *popup = ltk_get_widget_from_id(popup_id);
popup->popup = 1;
}
void
-ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup) {
+ltk_window_unregister_popup(ltk_widget_id windowid, ltk_widget_id popup_id) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
if (window->popups_locked)
return;
- for (size_t i = 0; i < window->popups_num; i++) {
- if (window->popups[i] == popup) {
+ for (size_t i = 0; i < ltk_array_len(window->popups); i++) {
+ ltk_widget_id id = ltk_array_get(window->popups, i);
+ if (LTK_WIDGET_ID_EQUAL(id, popup_id)) {
+ ltk_widget *popup = ltk_get_widget_from_id(popup_id);
popup->popup = 0;
- memmove(
- window->popups + i,
- window->popups + i + 1,
- sizeof(ltk_widget *) * (window->popups_num - i - 1)
- );
- window->popups_num--;
- size_t sz = ideal_array_size(
- window->popups_alloc, window->popups_num
- );
- if (sz != window->popups_alloc) {
- window->popups_alloc = sz;
- window->popups = ltk_reallocarray(
- window->popups, sz, sizeof(ltk_widget *)
- );
- }
+ ltk_array_delete(widget_id, window->popups, i, 1);
return;
}
}
@@ -566,24 +569,20 @@ ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup) {
/* FIXME: where should actual hiding happen? */
void
-ltk_window_unregister_all_popups(ltk_window *window) {
+ltk_window_unregister_all_popups(ltk_widget_id windowid) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
window->popups_locked = 1;
- for (size_t i = 0; i < window->popups_num; i++) {
- window->popups[i]->hidden = 1;
- window->popups[i]->popup = 0;
- ltk_widget_hide(window->popups[i]);
- }
- window->popups_num = 0;
- /* somewhat arbitrary, but should be enough for most cases */
- if (window->popups_num > 4) {
- window->popups = ltk_reallocarray(
- window->popups, 4, sizeof(ltk_widget *)
- );
- window->popups_alloc = 4;
+ for (size_t i = 0; i < ltk_array_len(window->popups); i++) {
+ ltk_widget *popup = ltk_get_widget_from_id(ltk_array_get(window->popups, i));
+ popup->hidden = 1;
+ popup->popup = 0;
+ ltk_widget_hide(popup);
}
+ ltk_array_reset(widget_id, window->popups);
window->popups_locked = 0;
/* I guess just invalidate everything instead of being smart */
- ltk_window_invalidate_rect(window, window->rect);
+ ltk_window_invalidate_rect(windoww->id, window->rect);
}
/* FIXME: support more options like child windows */
@@ -591,8 +590,7 @@ ltk_window *
ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y, unsigned int w, unsigned int h) {
ltk_window *window = ltk_malloc(sizeof(ltk_window));
- window->popups = NULL;
- window->popups_num = window->popups_alloc = 0;
+ window->popups = ltk_array_create(widget_id, 1);;
window->popups_locked = 0;
ltk_general_config *config = ltk_config_get_general();
@@ -600,10 +598,10 @@ ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y,
window->renderwindow = ltk_renderer_create_window(data, title, x, y, w, h, dpi);
ltk_renderer_set_window_properties(window->renderwindow, theme.bg);
- window->root_widget = NULL;
- window->hover_widget = NULL;
- window->active_widget = NULL;
- window->pressed_widget = NULL;
+ window->root_widget = LTK_WIDGET_ID_NONE;
+ window->hover_widget = LTK_WIDGET_ID_NONE;
+ window->active_widget = LTK_WIDGET_ID_NONE;
+ window->pressed_widget = LTK_WIDGET_ID_NONE;
//FIXME: use widget rect
window->rect.w = w;
@@ -620,7 +618,12 @@ ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y,
/* This is a bit weird because the window entry points to itself */
/* This needs to be called after window->renderwindow is set */
- ltk_fill_widget_defaults(&window->widget, window, &vtable, 0, 0);
+ ltk_initialize_widget(LTK_CAST_WIDGET(window), LTK_WIDGET_ID_NONE, &vtable, 0, 0);
+ /* set manually because it isn't set automatically in ltk_initialize_widget
+ because the given window id is none
+ Note: this shouldn't just be set to the initial dpi calculated above since
+ ltk_renderer_create_window can change that */
+ LTK_CAST_WIDGET(window)->last_dpi = ltk_renderer_get_window_dpi(window->renderwindow);
return window;
}
@@ -629,11 +632,11 @@ ltk_window_create_intern(ltk_renderdata *data, const char *title, int x, int y,
void
ltk_window_destroy_intern(ltk_window *window) {
- if (window->root_widget) {
- ltk_widget_destroy(window->root_widget, 0);
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (root_widget) {
+ ltk_widget_destroy(root_widget, 0);
}
- if (window->popups)
- ltk_free(window->popups);
+ ltk_array_destroy(widget_id, window->popups);
ltk_surface_cache_destroy(window->surface_cache);
ltk_surface_destroy(window->surface);
ltk_renderer_destroy_window(window->renderwindow);
@@ -642,12 +645,15 @@ ltk_window_destroy_intern(ltk_window *window) {
/* event must have global coordinates! */
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)
+ltk_window_set_hover_widget(ltk_widget_id windowid, ltk_widget_id widgetid, ltk_motion_event *event) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ ltk_widget_id oldid = window->hover_widget;
+ if (LTK_WIDGET_ID_EQUAL(oldid, widgetid))
return;
int orig_x = event->x, orig_y = event->y;
ltk_callback_arg args[] = {LTK_MAKE_ARG_MOTION_EVENT(event)};
+ ltk_widget *old = ltk_get_widget_or_null_from_id(oldid);
if (old) {
ltk_widget_state old_state = old->state;
old->state &= ~LTK_HOVER;
@@ -655,40 +661,45 @@ ltk_window_set_hover_widget(ltk_window *window, ltk_widget *widget, ltk_motion_e
ltk_point local = ltk_global_to_widget_pos(old, event->x, event->y);
event->x = local.x;
event->y = local.y;
- if (!ltk_widget_emit_signal(old, LTK_WIDGET_SIGNAL_MOUSE_LEAVE, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(oldid, LTK_WIDGET_SIGNAL_MOUSE_LEAVE, (ltk_callback_arglist){args, LENGTH(args)})) {
if (old->vtable->mouse_leave)
old->vtable->mouse_leave(old, event);
}
event->x = orig_x;
event->y = orig_y;
}
- window->hover_widget = widget;
+ window->hover_widget = widgetid;
+ ltk_widget *widget = ltk_get_widget_or_null_from_id(widgetid);
if (widget) {
ltk_point local = ltk_global_to_widget_pos(widget, event->x, event->y);
event->x = local.x;
event->y = local.y;
- if (!ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_MOUSE_ENTER, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(widgetid, LTK_WIDGET_SIGNAL_MOUSE_ENTER, (ltk_callback_arglist){args, LENGTH(args)})) {
if (widget->vtable->mouse_enter)
widget->vtable->mouse_enter(widget, event);
}
ltk_widget_state old_state = widget->state;
widget->state |= LTK_HOVER;
ltk_widget_change_state(widget, old_state);
- if ((widget->vtable->flags & LTK_HOVER_IS_ACTIVE) && widget != window->active_widget)
- ltk_window_set_active_widget(window, widget);
+ if ((widget->vtable->flags & LTK_HOVER_IS_ACTIVE) && !LTK_WIDGET_ID_EQUAL(widgetid, window->active_widget))
+ ltk_window_set_active_widget(windowid, widgetid);
}
}
void
-ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
- if (window->active_widget == widget) {
+ltk_window_set_active_widget(ltk_widget_id windowid, ltk_widget_id widgetid) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ if (LTK_WIDGET_ID_EQUAL(window->active_widget, widgetid)) {
return;
}
- ltk_widget *old = window->active_widget;
+ ltk_widget_id oldid = window->active_widget;
+ ltk_widget *old = ltk_get_widget_or_null_from_id(oldid);
/* Note: this has to be set at the beginning to
avoid infinite recursion in some cases */
- window->active_widget = widget;
+ window->active_widget = widgetid;
ltk_widget *common_parent = NULL;
+ ltk_widget *widget = ltk_get_widget_or_null_from_id(widgetid);
if (widget) {
ltk_widget *cur = widget;
while (cur) {
@@ -702,7 +713,7 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
if (cur == widget && !(cur->vtable->flags & LTK_NEEDS_KEYBOARD))
widget->state |= LTK_FOCUSED;
ltk_widget_change_state(cur, old_state);
- cur = cur->parent;
+ cur = ltk_get_widget_or_null_from_id(cur->parent);
}
}
/* FIXME: better variable names; generally make this nicer */
@@ -711,7 +722,7 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
while (tmp) {
if (tmp == old)
return;
- tmp = tmp->parent;
+ tmp = ltk_get_widget_or_null_from_id(tmp->parent);
}
if (old) {
old->state &= ~LTK_FOCUSED;
@@ -722,33 +733,37 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
ltk_widget_state old_state = cur->state;
cur->state &= ~LTK_ACTIVE;
ltk_widget_change_state(cur, old_state);
- cur = cur->parent;
+ cur = ltk_get_widget_or_null_from_id(cur->parent);
}
}
}
void
-ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget, int release) {
- if (window->pressed_widget == widget)
+ltk_window_set_pressed_widget(ltk_widget_id windowid, ltk_widget_id widgetid, int release) {
+ ltk_widget *windoww = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(windoww);
+ if (LTK_WIDGET_ID_EQUAL(window->pressed_widget, widgetid))
return;
- if (window->pressed_widget) {
- ltk_widget_state old_state = window->pressed_widget->state;
- window->pressed_widget->state &= ~LTK_PRESSED;
- ltk_widget_change_state(window->pressed_widget, old_state);
- ltk_window_set_active_widget(window, window->pressed_widget);
+ ltk_widget *old = ltk_get_widget_or_null_from_id(window->pressed_widget);
+ if (old) {
+ ltk_widget_state old_state = old->state;
+ old->state &= ~LTK_PRESSED;
+ ltk_widget_change_state(old, old_state);
+ ltk_window_set_active_widget(windowid, window->pressed_widget);
/* FIXME: this is a bit weird because the release handler for menuentry
indirectly calls ltk_widget_hide, which messes with the pressed widget */
/* FIXME: isn't it redundant to check that state is pressed? */
if (release && (old_state & LTK_PRESSED)) {
if (!ltk_widget_emit_signal(window->pressed_widget, LTK_WIDGET_SIGNAL_RELEASE, LTK_EMPTY_ARGLIST)) {
- if (window->pressed_widget->vtable->release)
- window->pressed_widget->vtable->release(window->pressed_widget);
+ if (old->vtable->release)
+ old->vtable->release(old);
}
}
}
- window->pressed_widget = widget;
+ window->pressed_widget = widgetid;
+ ltk_widget *widget = ltk_get_widget_or_null_from_id(widgetid);
if (widget) {
- if (!ltk_widget_emit_signal(widget, LTK_WIDGET_SIGNAL_PRESS, LTK_EMPTY_ARGLIST)) {
+ if (!ltk_widget_emit_signal(widgetid, LTK_WIDGET_SIGNAL_PRESS, LTK_EMPTY_ARGLIST)) {
if (widget->vtable->press)
widget->vtable->press(widget);
}
@@ -759,19 +774,20 @@ ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget, int releas
}
void
-ltk_window_handle_event(ltk_window *window, ltk_event *event) {
- ltk_widget *self = LTK_CAST_WIDGET(window);
+ltk_window_handle_event(ltk_widget_id windowid, ltk_event *event) {
+ ltk_widget *self = ltk_get_widget_from_id(windowid);
+ ltk_window *window = LTK_CAST_WINDOW(self);
ltk_callback_arg args[1];
switch (event->type) {
case LTK_KEYPRESS_EVENT:
args[0] = LTK_MAKE_ARG_KEY_EVENT(&event->key);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_KEY_PRESS, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_KEY_PRESS, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_key_press_event(self, &event->key);
}
break;
case LTK_KEYRELEASE_EVENT:
args[0] = LTK_MAKE_ARG_KEY_EVENT(&event->key);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_KEY_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_KEY_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_key_release_event(self, &event->key);
}
break;
@@ -779,13 +795,13 @@ ltk_window_handle_event(ltk_window *window, ltk_event *event) {
case LTK_2BUTTONPRESS_EVENT:
case LTK_3BUTTONPRESS_EVENT:
args[0] = LTK_MAKE_ARG_BUTTON_EVENT(&event->button);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_MOUSE_PRESS, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_MOUSE_PRESS, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_mouse_press_event(self, &event->button);
}
break;
case LTK_SCROLL_EVENT:
args[0] = LTK_MAKE_ARG_SCROLL_EVENT(&event->scroll);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_MOUSE_SCROLL, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_MOUSE_SCROLL, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_mouse_scroll_event(self, &event->scroll);
}
break;
@@ -793,13 +809,13 @@ ltk_window_handle_event(ltk_window *window, ltk_event *event) {
case LTK_2BUTTONRELEASE_EVENT:
case LTK_3BUTTONRELEASE_EVENT:
args[0] = LTK_MAKE_ARG_BUTTON_EVENT(&event->button);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_MOUSE_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_MOUSE_RELEASE, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_mouse_release_event(self, &event->button);
}
break;
case LTK_MOTION_EVENT:
args[0] = LTK_MAKE_ARG_MOTION_EVENT(&event->motion);
- if (!ltk_widget_emit_signal(self, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
+ if (!ltk_widget_emit_signal(windowid, LTK_WIDGET_SIGNAL_MOTION_NOTIFY, (ltk_callback_arglist){args, LENGTH(args)})) {
ltk_window_motion_notify_event(self, &event->motion);
}
break;
@@ -816,7 +832,7 @@ get_widget_under_pointer(ltk_widget *widget, int x, int y, int *local_x_ret, int
*local_x_ret = x - glob.x;
*local_y_ret = y - glob.y;
while (widget && widget->vtable->get_child_at_pos) {
- next = widget->vtable->get_child_at_pos(widget, *local_x_ret, *local_y_ret);
+ next = ltk_get_widget_or_null_from_id(widget->vtable->get_child_at_pos(widget, *local_x_ret, *local_y_ret));
if (!next) {
break;
} else {
@@ -835,9 +851,11 @@ get_widget_under_pointer(ltk_widget *widget, int x, int y, int *local_x_ret, int
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]->crect, x, y))
- return window->popups[i];
+ for (size_t i = ltk_array_len(window->popups); i-- > 0;) {
+ ltk_widget_id id = ltk_array_get(window->popups, i);
+ ltk_widget *widget = ltk_get_widget_from_id(id);
+ if (ltk_collide_rect(widget->crect, x, y))
+ return widget;
}
return NULL;
}
@@ -845,7 +863,7 @@ get_hover_popup(ltk_window *window, int x, int y) {
static int
is_parent(ltk_widget *parent, ltk_widget *child) {
while (child && child != parent) {
- child = child->parent;
+ child = ltk_get_widget_or_null_from_id(child->parent);
}
return child != NULL;
}
@@ -855,23 +873,25 @@ is_parent(ltk_widget *parent, ltk_widget *child) {
/* FIXME: handle disabled state */
static int
prev_child(ltk_window *window) {
- if (!window->root_widget)
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (!root_widget)
return 0;
ltk_general_config *config = ltk_config_get_general();
ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL;
- ltk_widget *new, *cur = window->active_widget;
+ ltk_widget *new, *cur = ltk_get_widget_or_null_from_id(window->active_widget);
int changed = 0;
ltk_widget *prevcur = cur;
while (1) {
if (cur) {
- while (cur->parent) {
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(cur->parent);
+ while (parent) {
new = NULL;
- if (cur->parent->vtable->prev_child)
- new = cur->parent->vtable->prev_child(cur->parent, cur);
+ if (parent->vtable->prev_child)
+ new = ltk_get_widget_or_null_from_id(parent->vtable->prev_child(parent, cur->id));
if (new) {
cur = new;
ltk_widget *last_activatable = (cur->vtable->flags & act_flags) ? cur : NULL;
- while (cur->vtable->last_child && (new = cur->vtable->last_child(cur))) {
+ while (cur->vtable->last_child && (new = ltk_get_widget_or_null_from_id(cur->vtable->last_child(cur)))) {
cur = new;
if (cur->vtable->flags & act_flags)
last_activatable = cur;
@@ -882,18 +902,19 @@ prev_child(ltk_window *window) {
break;
}
} else {
- cur = cur->parent;
+ cur = parent;
if (cur->vtable->flags & act_flags) {
changed = 1;
break;
}
}
+ parent = ltk_get_widget_or_null_from_id(cur->parent);
}
}
if (!changed) {
- cur = window->root_widget;
+ cur = root_widget;
ltk_widget *last_activatable = (cur->vtable->flags & act_flags) ? cur : NULL;
- while (cur->vtable->last_child && (new = cur->vtable->last_child(cur))) {
+ while (cur->vtable->last_child && (new = ltk_get_widget_or_null_from_id(cur->vtable->last_child(cur)))) {
cur = new;
if (cur->vtable->flags & act_flags)
last_activatable = cur;
@@ -906,8 +927,8 @@ prev_child(ltk_window *window) {
prevcur = cur;
}
/* FIXME: What exactly should be done if no activatable widget exists? */
- if (cur != window->active_widget) {
- ltk_window_set_active_widget(window, cur);
+ if (!LTK_WIDGET_ID_EQUAL(cur->id, window->active_widget)) {
+ ltk_window_set_active_widget(LTK_CAST_WIDGET(window)->id, cur->id);
ensure_active_widget_shown(window);
return 1;
}
@@ -916,17 +937,17 @@ prev_child(ltk_window *window) {
static int
next_child(ltk_window *window) {
- if (!window->root_widget)
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (!root_widget)
return 0;
ltk_general_config *config = ltk_config_get_general();
ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL;
- ltk_widget *new, *cur = window->active_widget;
+ ltk_widget *new, *cur = ltk_get_widget_or_null_from_id(window->active_widget);
int changed = 0;
ltk_widget *prevcur = cur;
while (1) {
if (cur) {
-
- while (cur->vtable->first_child && (new = cur->vtable->first_child(cur))) {
+ while (cur->vtable->first_child && (new = ltk_get_widget_or_null_from_id(cur->vtable->first_child(cur)))) {
cur = new;
if (cur->vtable->flags & act_flags) {
changed = 1;
@@ -934,17 +955,18 @@ next_child(ltk_window *window) {
}
}
if (!changed) {
- while (cur->parent) {
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(cur->parent);
+ while (parent) {
new = NULL;
- if (cur->parent->vtable->next_child)
- new = cur->parent->vtable->next_child(cur->parent, cur);
+ if (parent->vtable->next_child)
+ new = ltk_get_widget_or_null_from_id(parent->vtable->next_child(parent, cur->id));
if (new) {
cur = new;
if (cur->vtable->flags & act_flags) {
changed = 1;
break;
}
- while (cur->vtable->first_child && (new = cur->vtable->first_child(cur))) {
+ while (cur->vtable->first_child && (new = ltk_get_widget_or_null_from_id(cur->vtable->first_child(cur)))) {
cur = new;
if (cur->vtable->flags & act_flags) {
changed = 1;
@@ -954,29 +976,30 @@ next_child(ltk_window *window) {
if (changed)
break;
} else {
- cur = cur->parent;
+ cur = parent;
}
+ parent = ltk_get_widget_or_null_from_id(cur->parent);
}
}
}
if (!changed) {
- cur = window->root_widget;
+ cur = root_widget;
if (!(cur->vtable->flags & act_flags)) {
- while (cur->vtable->first_child && (new = cur->vtable->first_child(cur))) {
+ while (cur->vtable->first_child && (new = ltk_get_widget_or_null_from_id(cur->vtable->first_child(cur)))) {
cur = new;
if (cur->vtable->flags & act_flags)
break;
}
}
if (!(cur->vtable->flags & act_flags))
- cur = window->root_widget;
+ cur = root_widget;
}
if (prevcur == cur || (cur && (cur->vtable->flags & act_flags)))
break;
prevcur = cur;
}
- if (cur != window->active_widget) {
- ltk_window_set_active_widget(window, cur);
+ if (!LTK_WIDGET_ID_EQUAL(cur->id, window->active_widget)) {
+ ltk_window_set_active_widget(LTK_CAST_WIDGET(window)->id, cur->id);
ensure_active_widget_shown(window);
return 1;
}
@@ -994,22 +1017,23 @@ nearest_child(ltk_widget *widget, ltk_rect r) {
ltk_point local = ltk_global_to_widget_pos(widget, r.x, r.y);
ltk_rect rect = {local.x, local.y, r.w, r.h};
if (widget->vtable->nearest_child)
- return widget->vtable->nearest_child(widget, rect);
+ return ltk_get_widget_or_null_from_id(widget->vtable->nearest_child(widget, rect));
return NULL;
}
/* FIXME: maybe wrap around in these two functions? */
static int
left_top_child(ltk_window *window, int left) {
- if (!window->root_widget)
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (!root_widget)
return 0;
ltk_general_config *config = ltk_config_get_general();
ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL;
- ltk_widget *new, *cur = window->active_widget;
+ ltk_widget *new, *cur = ltk_get_widget_or_null_from_id(window->active_widget);
ltk_rect old_rect = {0, 0, 0, 0};
ltk_widget *last_activatable = NULL;
if (!cur) {
- cur = window->root_widget;
+ cur = root_widget;
if (cur->vtable->flags & act_flags)
last_activatable = cur;
ltk_rect r = {cur->lrect.w, cur->lrect.h, 0, 0};
@@ -1022,16 +1046,23 @@ left_top_child(ltk_window *window, int left) {
if (last_activatable) {
cur = last_activatable;
} else if (cur) {
- ltk_point glob = cur->parent ? ltk_widget_pos_to_global(cur->parent, cur->lrect.x, cur->lrect.y) : (ltk_point){cur->lrect.x, cur->lrect.y};
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(cur->parent);
+ ltk_point glob = parent ? ltk_widget_pos_to_global(parent, cur->lrect.x, cur->lrect.y) : (ltk_point){cur->lrect.x, cur->lrect.y};
old_rect = (ltk_rect){glob.x, glob.y, cur->lrect.w, cur->lrect.h};
- while (cur->parent) {
+ while (parent) {
new = NULL;
if (left) {
- if (cur->parent->vtable->nearest_child_left)
- new = cur->parent->vtable->nearest_child_left(cur->parent, cur);
+ if (parent->vtable->nearest_child_left) {
+ new = ltk_get_widget_or_null_from_id(
+ parent->vtable->nearest_child_left(parent, cur->id)
+ );
+ }
} else {
- if (cur->parent->vtable->nearest_child_above)
- new = cur->parent->vtable->nearest_child_above(cur->parent, cur);
+ if (parent->vtable->nearest_child_above) {
+ new = ltk_get_widget_or_null_from_id(
+ parent->vtable->nearest_child_above(parent, cur->id)
+ );
+ }
}
if (new) {
cur = new;
@@ -1046,16 +1077,17 @@ left_top_child(ltk_window *window, int left) {
break;
}
} else {
- cur = cur->parent;
+ cur = parent;
if (cur->vtable->flags & act_flags) {
break;
}
}
+ parent = ltk_get_widget_or_null_from_id(cur->parent);
}
}
/* FIXME: What exactly should be done if no activatable widget exists? */
- if (cur && cur != window->active_widget && (cur->vtable->flags & act_flags)) {
- ltk_window_set_active_widget(window, cur);
+ if (cur && !LTK_WIDGET_ID_EQUAL(cur->id, window->active_widget) && (cur->vtable->flags & act_flags)) {
+ ltk_window_set_active_widget(LTK_CAST_WIDGET(window)->id, cur->id);
ensure_active_widget_shown(window);
return 1;
}
@@ -1064,17 +1096,18 @@ left_top_child(ltk_window *window, int left) {
static int
right_bottom_child(ltk_window *window, int right) {
- if (!window->root_widget)
+ ltk_widget *root_widget = ltk_get_widget_or_null_from_id(window->root_widget);
+ if (!root_widget)
return 0;
ltk_general_config *config = ltk_config_get_general();
ltk_widget_flags act_flags = config->all_activatable ? LTK_ACTIVATABLE_ALWAYS : LTK_ACTIVATABLE_NORMAL;
- ltk_widget *new, *cur = window->active_widget;
+ ltk_widget *new, *cur = ltk_get_widget_or_null_from_id(window->active_widget);
int changed = 0;
ltk_rect old_rect = {0, 0, 0, 0};
ltk_rect corner = {0, 0, 0, 0};
int found_activatable = 0;
if (!cur) {
- cur = window->root_widget;
+ cur = root_widget;
if (!(cur->vtable->flags & act_flags)) {
while ((new = nearest_child(cur, (ltk_rect){0, 0, 0, 0}))) {
cur = new;
@@ -1086,7 +1119,8 @@ right_bottom_child(ltk_window *window, int right) {
}
}
if (!found_activatable) {
- ltk_point glob = cur->parent ? ltk_widget_pos_to_global(cur->parent, cur->lrect.x, cur->lrect.y) : (ltk_point){cur->lrect.x, cur->lrect.y};
+ ltk_widget *parent = ltk_get_widget_or_null_from_id(cur->parent);
+ ltk_point glob = parent ? ltk_widget_pos_to_global(parent, cur->lrect.x, cur->lrect.y) : (ltk_point){cur->lrect.x, cur->lrect.y};
corner = (ltk_rect){glob.x, glob.y, 0, 0};
old_rect = (ltk_rect){glob.x, glob.y, cur->lrect.w, cur->lrect.h};
while ((new = nearest_child(cur, corner))) {
@@ -1097,14 +1131,20 @@ right_bottom_child(ltk_window *window, int right) {
}
}
if (!changed) {
- while (cur->parent) {
+ while ((parent = ltk_get_widget_or_null_from_id(cur->parent))) {
new = NULL;
if (right) {
- if (cur->parent->vtable->nearest_child_right)
- new = cur->parent->vtable->nearest_child_right(cur->parent, cur);
+ if (parent->vtable->nearest_child_right) {
+ new = ltk_get_widget_or_null_from_id(
+ parent->vtable->nearest_child_right(parent, cur->id)
+ );
+ }
} else {
- if (cur->parent->vtable->nearest_child_below)
- new = cur->parent->vtable->nearest_child_below(cur->parent, cur);
+ if (parent->vtable->nearest_child_below) {
+ new = ltk_get_widget_or_null_from_id(
+ parent->vtable->nearest_child_below(parent, cur->id)
+ );
+ }
}
if (new) {
cur = new;
@@ -1122,13 +1162,13 @@ right_bottom_child(ltk_window *window, int right) {
if (changed)
break;
} else {
- cur = cur->parent;
+ cur = ltk_get_widget_or_null_from_id(cur->parent);
}
}
}
}
- if (cur && cur != window->active_widget && (cur->vtable->flags & act_flags)) {
- ltk_window_set_active_widget(window, cur);
+ if (cur && !LTK_WIDGET_ID_EQUAL(cur->id, window->active_widget) && (cur->vtable->flags & act_flags)) {
+ ltk_window_set_active_widget(LTK_CAST_WIDGET(window)->id, cur->id);
ensure_active_widget_shown(window);
return 1;
}
@@ -1138,15 +1178,15 @@ right_bottom_child(ltk_window *window, int right) {
/* FIXME: maybe just set this when active widget changes */
/* -> but would also need to change it when widgets are created/destroyed or parents change */
static void
-gen_widget_stack(ltk_widget *bottom) {
- widget_stack_len = 0;
+gen_widget_stack(ltk_widget_id bottomid) {
+ if (!widget_stack)
+ widget_stack = ltk_array_create(widget_id, 1);
+ ltk_array_clear(widget_stack);
+ ltk_widget *bottom = ltk_get_widget_or_null_from_id(bottomid);
while (bottom) {
- if (widget_stack_len + 1 > widget_stack_alloc) {
- widget_stack_alloc = ideal_array_size(widget_stack_alloc, widget_stack_len + 1);
- widget_stack = ltk_reallocarray(widget_stack, widget_stack_alloc, sizeof(ltk_widget *));
- }
- widget_stack[widget_stack_len++] = bottom;
- bottom = bottom->parent;
+ ltk_array_append(widget_id, widget_stack, bottomid);
+ bottomid = bottom->parent;
+ bottom = ltk_get_widget_or_null_from_id(bottomid);
}
}
@@ -1157,11 +1197,12 @@ static int
cb_focus_active(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_window *window = LTK_CAST_WINDOW(self);
- if (window->active_widget && !(window->active_widget->state & LTK_FOCUSED)) {
+ ltk_widget *active_widget = ltk_get_widget_or_null_from_id(window->active_widget);
+ if (active_widget && !(active_widget->state & LTK_FOCUSED)) {
/* FIXME: maybe also set widgets above in hierarchy? */
- ltk_widget_state old_state = window->active_widget->state;
- window->active_widget->state |= LTK_FOCUSED;
- ltk_widget_change_state(window->active_widget, old_state);
+ ltk_widget_state old_state = active_widget->state;
+ active_widget->state |= LTK_FOCUSED;
+ ltk_widget_change_state(active_widget, old_state);
return 1;
}
return 0;
@@ -1171,10 +1212,11 @@ static int
cb_unfocus_active(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_window *window = LTK_CAST_WINDOW(self);
- if (window->active_widget && (window->active_widget->state & LTK_FOCUSED) && (window->active_widget->vtable->flags & LTK_NEEDS_KEYBOARD)) {
- ltk_widget_state old_state = window->active_widget->state;
- window->active_widget->state &= ~LTK_FOCUSED;
- ltk_widget_change_state(window->active_widget, old_state);
+ ltk_widget *active_widget = ltk_get_widget_or_null_from_id(window->active_widget);
+ if (active_widget && (active_widget->state & LTK_FOCUSED) && (active_widget->vtable->flags & LTK_NEEDS_KEYBOARD)) {
+ ltk_widget_state old_state = active_widget->state;
+ active_widget->state &= ~LTK_FOCUSED;
+ ltk_widget_change_state(active_widget, old_state);
return 1;
}
return 0;
@@ -1226,9 +1268,10 @@ static int
cb_set_pressed(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_window *window = LTK_CAST_WINDOW(self);
- if (window->active_widget && (window->active_widget->state & LTK_FOCUSED)) {
+ ltk_widget *active_widget = ltk_get_widget_or_null_from_id(window->active_widget);
+ if (active_widget && (active_widget->state & LTK_FOCUSED)) {
/* FIXME: only set pressed if needs keyboard? */
- ltk_window_set_pressed_widget(window, window->active_widget, 0);
+ ltk_window_set_pressed_widget(self->id, window->active_widget, 0);
return 1;
}
return 0;
@@ -1238,8 +1281,8 @@ static int
cb_unset_pressed(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_window *window = LTK_CAST_WINDOW(self);
- if (window->pressed_widget) {
- ltk_window_set_pressed_widget(window, NULL, 1);
+ if (!LTK_WIDGET_ID_IS_NONE(window->pressed_widget)) {
+ ltk_window_set_pressed_widget(self->id, LTK_WIDGET_ID_NONE, 1);
return 1;
}
return 0;
@@ -1249,8 +1292,8 @@ static int
cb_remove_popups(ltk_widget *self, ltk_key_event *event) {
(void)event;
ltk_window *window = LTK_CAST_WINDOW(self);
- if (window->popups_num > 0) {
- ltk_window_unregister_all_popups(window);
+ if (ltk_array_len(window->popups) > 0) {
+ ltk_window_unregister_all_popups(self->id);
return 1;
}
return 0;
diff --git a/src/ltk/window.h b/src/ltk/window.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2020-2026 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
@@ -33,17 +33,14 @@ typedef struct ltk_window {
ltk_surface_cache *surface_cache;
ltk_surface *surface;
/* FIXME: check if these are reset properly if widget is deleted */
- ltk_widget *root_widget;
- ltk_widget *hover_widget;
- ltk_widget *active_widget;
- ltk_widget *pressed_widget;
+ ltk_widget_id root_widget;
+ ltk_widget_id hover_widget;
+ ltk_widget_id active_widget;
+ ltk_widget_id pressed_widget;
ltk_rect rect;
ltk_rect dirty_rect;
- /* FIXME: generic array */
- ltk_widget **popups;
- size_t popups_num;
- size_t popups_alloc;
+ ltk_array(widget_id) *popups;
/* This is a hack so ltk_window_unregister_all_popups can
call hide for all popup widgets even if the hide function
already calls ltk_window_unregister_popup */
@@ -51,23 +48,28 @@ typedef struct ltk_window {
} ltk_window;
/* FIXME: which of these should be internal to LTK? */
-void ltk_window_handle_event(ltk_window *window, ltk_event *event);
-void ltk_window_fake_motion_event(ltk_window *window, int x, int y);
+/* FIXME: should this maybe still take ltk_window directly? */
+void ltk_window_handle_event(ltk_widget_id windowid, ltk_event *event);
+void ltk_window_fake_motion_event(ltk_widget_id window, int x, int y);
-void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
-void ltk_window_invalidate_widget_rect(ltk_window *window, ltk_widget *widget);
+/* FIXME: should there be versions that directly take window and widget instead of IDs?
+ -> I guess for invalidate_widget_rect, it doesn't really change much efficiency-wise
+ since getting the absolute widget rect goes through the parent hierarchy and needs
+ to look up all the widgets on the path anyways */
+void ltk_window_invalidate_rect(ltk_widget_id window, ltk_rect rect);
+void ltk_window_invalidate_widget_rect(ltk_widget_id window, ltk_widget_id widget);
-void ltk_window_set_root_widget(ltk_window *window, ltk_widget *widget);
-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, int release);
+void ltk_window_set_root_widget(ltk_widget_id window, ltk_widget_id widget);
+void ltk_window_set_hover_widget(ltk_widget_id window, ltk_widget_id widget, ltk_motion_event *event);
+void ltk_window_set_active_widget(ltk_widget_id window, ltk_widget_id widget);
+void ltk_window_set_pressed_widget(ltk_widget_id window, ltk_widget_id widget, int release);
/* IMPORTANT: Callers must call ltk_widget_recalc_ideal_size and ltk_widget_resize
first to take DPI changes into account. It wouldn't make sense for ltk_window_register_popup
to call these functions because the calling function usually needs to know the actual size
of the popup to determine where to place it. */
-void ltk_window_register_popup(ltk_window *window, ltk_widget *popup);
-void ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup);
-void ltk_window_unregister_all_popups(ltk_window *window);
+void ltk_window_register_popup(ltk_widget_id window, ltk_widget_id popup);
+void ltk_window_unregister_popup(ltk_widget_id window, ltk_widget_id popup);
+void ltk_window_unregister_all_popups(ltk_widget_id window);
#endif /* LTK_WINDOW_H */
diff --git a/src/ltkd/box.c b/src/ltkd/box.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -28,20 +28,19 @@
/* box <box id> add <widget id> [sticky] */
static int
ltkd_box_cmd_add(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_box *box = LTK_CAST_BOX(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_UNKNOWN, .optional = 0},
{.type = CMDARG_STICKY, .optional = 1},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- if (ltk_box_add(box, cmd[0].val.widget->widget, cmd[1].initialized ? cmd[1].val.sticky : 0)) {
+ if (ltk_box_add(widget->ltkid, cmd[0].val.widget->ltkid, cmd[1].initialized ? cmd[1].val.sticky : 0)) {
err->type = ERR_WIDGET_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -52,19 +51,18 @@ ltkd_box_cmd_add(
/* box <box id> remove <widget id> */
static int
ltkd_box_cmd_remove(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_box *box = LTK_CAST_BOX(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_UNKNOWN, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- if (ltk_box_remove(box, cmd[0].val.widget->widget)) {
+ if (ltk_box_remove(widget->ltkid, cmd[0].val.widget->ltkid)) {
err->type = ERR_WIDGET_NOT_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -75,7 +73,7 @@ ltkd_box_cmd_remove(
/* box <box id> create <orientation> */
static int
ltkd_box_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -89,9 +87,9 @@ ltkd_box_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_box *box = ltk_box_create(window, cmd[3].val.orient);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(box), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(box), 1);
+ ltk_widget_id box = ltk_box_create(windowid, cmd[3].val.orient);
+ if (!ltkd_widget_create(box, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(box, 1);
err->arg = 1;
return 1;
}
diff --git a/src/ltkd/button.c b/src/ltkd/button.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -41,7 +41,7 @@ static ltkd_event_handler handlers[] = {
/* button <button id> create <text> */
static int
ltkd_button_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -55,9 +55,9 @@ ltkd_button_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_button *button = ltk_button_create(window, cmd[3].val.str);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(button), cmd[1].val.str, handlers, LENGTH(handlers), err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(button), 1);
+ ltk_widget_id button = ltk_button_create(windowid, cmd[3].val.str);
+ if (!ltkd_widget_create(button, cmd[1].val.str, handlers, LENGTH(handlers), err)) {
+ ltk_widget_id_destroy(button, 1);
err->arg = 1;
return 1;
}
diff --git a/src/ltkd/cmd.h b/src/ltkd/cmd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2023-2026 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
@@ -30,7 +30,7 @@
typedef struct {
char *name;
- int (*func)(ltk_window *, ltkd_widget *, ltkd_cmd_token *, size_t, ltkd_error *);
+ int (*func)(ltk_widget_id, ltkd_widget *, ltkd_cmd_token *, size_t, ltkd_error *);
int needs_all;
} ltkd_cmd_info;
@@ -81,7 +81,7 @@ int ltkd_parse_cmd(
/* FIXME: This doesn't really need to be a macro anymore since it doesn't have to be generic for different types anymore */
#define GEN_CMD_HELPERS_PROTO(func_name) \
-int func_name(ltk_window *window, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_error *err);
+int func_name(ltk_widget_id windowid, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_error *err);
#define GEN_CMD_HELPERS(func_name, widget_type, array_name) \
@@ -104,7 +104,7 @@ array_name##_sort_helper(const void *entry1v, const void *entry2v) { \
\
int \
func_name( \
- ltk_window *window, \
+ ltk_widget_id windowid, \
ltkd_cmd_token *tokens, \
size_t num_tokens, \
ltkd_error *err) { \
@@ -139,14 +139,14 @@ func_name( \
return 1; \
} \
if (e->needs_all) { \
- return e->func(window, NULL, tokens, num_tokens, err); \
+ return e->func(windowid, NULL, tokens, num_tokens, err); \
} else { \
ltkd_widget *widget = ltkd_get_widget(tokens[1].text, widget_type, err); \
if (!widget) { \
err->arg = 1; \
return 1; \
} \
- int ret = e->func(window, widget, tokens + 3, num_tokens - 3, err); \
+ int ret = e->func(windowid, widget, tokens + 3, num_tokens - 3, err); \
if (ret && err->arg >= 0) \
err->arg += 3; \
return ret; \
diff --git a/src/ltkd/entry.c b/src/ltkd/entry.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -29,7 +29,7 @@
/* entry <entry id> create <text> */
static int
ltkd_entry_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -43,9 +43,9 @@ ltkd_entry_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_entry *entry = ltk_entry_create(window, cmd[3].val.str);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(entry), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(entry), 1);
+ ltk_widget_id entry = ltk_entry_create(windowid, cmd[3].val.str);
+ if (!ltkd_widget_create(entry, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(entry, 1);
err->arg = 1;
return 1;
}
diff --git a/src/ltkd/grid.c b/src/ltkd/grid.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -24,13 +24,14 @@
/* grid <grid id> add <widget id> <row> <column> <row_span> <column_span> [sticky] */
static int
ltkd_grid_cmd_add(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_grid *grid = LTK_CAST_GRID(widget->widget);
+ (void)windowid;
+ ltk_widget *gridw = ltk_get_widget_from_id(widget->ltkid);
+ ltk_grid *grid = LTK_CAST_GRID(gridw);
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_UNKNOWN, .optional = 0},
{.type = CMDARG_INT, .min = 0, .max = grid->rows - 1, .optional = 0},
@@ -55,7 +56,7 @@ ltkd_grid_cmd_add(
/* FIXME: check if recalculation deals properly with rowspan/columnspan
that goes over the edge of the grid */
if (ltk_grid_add(
- grid, cmd[0].val.widget->widget,
+ widget->ltkid, cmd[0].val.widget->ltkid,
row, col, rowspan, colspan,
cmd[5].initialized ? cmd[5].val.sticky : 0)) {
err->type = ERR_WIDGET_IN_CONTAINER;
@@ -68,19 +69,18 @@ ltkd_grid_cmd_add(
/* grid <grid id> remove <widget id> */
static int
ltkd_grid_cmd_ungrid(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_grid *grid = LTK_CAST_GRID(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_UNKNOWN, .optional = 0}
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- if (ltk_grid_remove(grid, cmd[0].val.widget->widget)) {
+ if (ltk_grid_remove(widget->ltkid, cmd[0].val.widget->ltkid)) {
err->type = ERR_WIDGET_NOT_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -92,7 +92,7 @@ ltkd_grid_cmd_ungrid(
/* grid <grid id> create <rows> <columns> */
static int
ltkd_grid_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -107,9 +107,9 @@ ltkd_grid_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_grid *grid = ltk_grid_create(window, cmd[3].val.i, cmd[4].val.i);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(grid), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(grid), 1);
+ ltk_widget_id grid = ltk_grid_create(windowid, cmd[3].val.i, cmd[4].val.i);
+ if (!ltkd_widget_create(grid, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(grid, 1);
err->arg = 1;
return 1;
}
@@ -121,20 +121,21 @@ ltkd_grid_cmd_create(
/* grid <grid id> set-row-weight <row> <weight> */
static int
ltkd_grid_cmd_set_row_weight(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_grid *grid = LTK_CAST_GRID(widget->widget);
+ (void)windowid;
+ ltk_widget *gridw = ltk_get_widget_from_id(widget->ltkid);
+ ltk_grid *grid = LTK_CAST_GRID(gridw);
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_INT, .min = 0, .max = grid->rows - 1, .optional = 0},
{.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_grid_set_row_weight(grid, cmd[0].val.i, cmd[1].val.i);
+ ltk_grid_set_row_weight(widget->ltkid, cmd[0].val.i, cmd[1].val.i);
return 0;
}
@@ -145,20 +146,21 @@ ltkd_grid_cmd_set_row_weight(
/* grid <grid id> set-column-weight <column> <weight> */
static int
ltkd_grid_cmd_set_column_weight(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_grid *grid = LTK_CAST_GRID(widget->widget);
+ (void)windowid;
+ ltk_widget *gridw = ltk_get_widget_from_id(widget->ltkid);
+ ltk_grid *grid = LTK_CAST_GRID(gridw);
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_INT, .min = 0, .max = grid->columns - 1, .optional = 0},
{.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_grid_set_column_weight(grid, cmd[0].val.i, cmd[1].val.i);
+ ltk_grid_set_column_weight(widget->ltkid, cmd[0].val.i, cmd[1].val.i);
return 0;
}
diff --git a/src/ltkd/image_widget.c b/src/ltkd/image_widget.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2023-2026 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
@@ -29,7 +29,7 @@
/* image <image id> create <filename> <data> */
static int
ltkd_image_widget_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -51,9 +51,9 @@ ltkd_image_widget_cmd_create(
err->arg = -1;
return 1;
}
- ltk_image_widget *imgw = ltk_image_widget_create(window, img);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(imgw), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(imgw), 1);
+ ltk_widget_id imgw = ltk_image_widget_create(windowid, img);
+ if (!ltkd_widget_create(imgw, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(imgw, 1);
err->arg = 1;
return 1;
}
diff --git a/src/ltkd/label.c b/src/ltkd/label.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -28,7 +28,7 @@
/* label <label id> create <text> */
static int
ltkd_label_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -42,9 +42,9 @@ ltkd_label_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_label *label = ltk_label_create(window, cmd[3].val.str);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(label), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(label), 1);
+ ltk_widget_id label = ltk_label_create(windowid, cmd[3].val.str);
+ if (!ltkd_widget_create(label, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(label, 1);
err->arg = 1;
return 1;
}
diff --git a/src/ltkd/ltkd.c b/src/ltkd/ltkd.c
@@ -1,8 +1,9 @@
+/* FIXME: standardize functions taking window or not */
/* FIXME: Figure out how to properly print window id */
/* FIXME: error checking in tokenizer (is this necessary?) */
/* FIXME: strip whitespace at end of lines in socket format */
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -51,6 +52,7 @@
#define READ_BLK_SIZE 128
#define WRITE_BLK_SIZE 128
+/* FIXME: replace with generic array */
struct token_list {
ltkd_cmd_token *tokens;
/* FIXME: size_t everywhere */
@@ -81,7 +83,7 @@ static struct ltkd_sock_info {
static int daemonize_flag = 1;
-static void ltkd_mainloop(ltk_window *window);
+static void ltkd_mainloop(void);
static char *get_sock_path(char *basedir, unsigned long id);
static FILE *open_log(char *dir);
static void daemonize(void);
@@ -90,8 +92,8 @@ static int push_token(struct token_list *tl, char *token);
static int read_sock(struct ltkd_sock_info *sock);
static int write_sock(struct ltkd_sock_info *sock);
static int tokenize_command(struct ltkd_sock_info *sock);
-static int ltkd_set_root_widget_cmd(ltk_window *window, ltkd_cmd_token *tokens, int num_tokens, ltkd_error *err);
-static int process_commands(ltk_window *window, int client);
+static int ltkd_set_root_widget_cmd(ltk_widget_id windowid, ltkd_cmd_token *tokens, int num_tokens, ltkd_error *err);
+static int process_commands(ltk_widget_id windowid, int client);
static int add_client(int fd);
static int listen_sock(const char *sock_path);
static int accept_sock(int listenfd);
@@ -107,11 +109,12 @@ static FILE *ltkd_logfile = NULL;
static char *sock_path = NULL;
/* Note: Most functions still take this explicitly because it wasn't
global originally, but that's just the way it is. */
-static ltk_window *main_window = NULL;
+static ltk_widget_id main_window = LTK_WIDGET_ID_NONE_STATIC;
+static unsigned long main_window_id = 0;
typedef struct {
char *name;
- int (*cmd)(ltk_window *, ltkd_cmd_token *, size_t, ltkd_error *);
+ int (*cmd)(ltk_widget_id, ltkd_cmd_token *, size_t, ltkd_error *);
} ltkd_widget_funcs;
/* FIXME: use binary search when searching for the widget */
@@ -202,11 +205,13 @@ main(int argc, char *argv[]) {
/* FIXME: set window size properly - I only run it in a tiling WM
anyways, so it doesn't matter, but still... */
main_window = ltk_window_create(title, 0, 0, 500, 500);
- ltk_widget_register_signal_handler(LTK_CAST_WIDGET(main_window), LTK_WINDOW_SIGNAL_CLOSE, <kd_window_close, LTK_ARG_VOID);
-
- sock_path = get_sock_path(ltkd_dir, ltk_renderer_get_window_id(main_window->renderwindow));
+ ltk_widget *windoww = ltk_get_widget_from_id(main_window);
+ main_window_id = ltk_renderer_get_window_id(LTK_CAST_WINDOW(windoww)->renderwindow);
+ sock_path = get_sock_path(ltkd_dir, main_window_id);
if (!sock_path) ltkd_fatal_errno("Unable to allocate memory for socket path.\n");
+ ltk_widget_register_signal_handler(main_window, LTK_WINDOW_SIGNAL_CLOSE, <kd_window_close, LTK_ARG_VOID);
+
/* Note: sockets should be initialized to 0 because it is static */
for (int i = 0; i < MAX_SOCK_CONNS; i++) {
sockets[i].fd = -1; /* socket unused */
@@ -216,7 +221,7 @@ main(int argc, char *argv[]) {
sockets[i].tokens.tokens = NULL;
}
- ltkd_mainloop(main_window);
+ ltkd_mainloop();
return 0;
}
@@ -231,7 +236,7 @@ static struct {
can be executed, so for instance the widget that caused the lock could also
be destroyed, causing issues when this function returns */
int
-ltkd_handle_lock_client(ltk_window *window, int client) {
+ltkd_handle_lock_client(ltk_widget_id windowid, int client) {
if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
return 0;
fd_set rfds, wfds, rallfds, wallfds;
@@ -257,7 +262,7 @@ ltkd_handle_lock_client(ltk_window *window, int client) {
int ret;
while ((ret = read_sock(&sockets[client])) == 1) {
int pret;
- if ((pret = process_commands(window, client)) == 1)
+ if ((pret = process_commands(windowid, client)) == 1)
return 1;
else if (pret == -1)
return 0;
@@ -301,7 +306,7 @@ ltkd_handle_lock_client(ltk_window *window, int client) {
/* FIXME: need to remove event masks from all widgets when removing client */
static void
-ltkd_mainloop(ltk_window *window) {
+ltkd_mainloop(void) {
fd_set rfds, wfds;
int retval;
int clifd;
@@ -318,7 +323,7 @@ ltkd_mainloop(ltk_window *window) {
FD_SET(sock_state.listenfd, &sock_state.rallfds);
sock_state.maxfd = sock_state.listenfd;
- printf("%lu", ltk_renderer_get_window_id(main_window->renderwindow));
+ printf("%lu", main_window_id);
fflush(stdout);
if (daemonize_flag)
daemonize();
@@ -357,7 +362,7 @@ ltkd_mainloop(ltk_window *window) {
/* or maybe measure time and break after max time? */
int ret;
while ((ret = read_sock(&sockets[i])) == 1) {
- process_commands(window, i);
+ process_commands(main_window, i);
}
if (ret == 0) {
ltkd_widget_remove_client(i);
@@ -499,7 +504,8 @@ ltkd_cleanup(void) {
}
ltkd_widgets_cleanup();
- main_window = NULL;
+ main_window = LTK_WIDGET_ID_NONE;
+ main_window_id = 0;
ltk_deinit();
}
@@ -518,8 +524,8 @@ ltkd_log_msg(const char *mode, const char *format, va_list args) {
timeptr = localtime(&clock);
strftime(logtime, 25, "%Y-%m-%d %H:%M:%S", timeptr);
- if (main_window)
- fprintf(stderr, "%s ltkd(%lu) %s: ", logtime, ltk_renderer_get_window_id(main_window->renderwindow), mode);
+ if (!LTK_WIDGET_ID_IS_NONE(main_window))
+ fprintf(stderr, "%s ltkd(%lu) %s: ", logtime, main_window_id, mode);
else
fprintf(stderr, "%s ltkd(?) %s: ", logtime, mode);
vfprintf(stderr, format, args);
@@ -527,11 +533,10 @@ ltkd_log_msg(const char *mode, const char *format, va_list args) {
static int
ltkd_set_root_widget_cmd(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_cmd_token *tokens,
int num_tokens,
ltkd_error *err) {
- ltkd_widget *widget;
if (num_tokens != 2) {
err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
err->arg = -1;
@@ -541,12 +546,12 @@ ltkd_set_root_widget_cmd(
err->arg = 1;
return 1;
}
- widget = ltkd_get_widget(tokens[1].text, LTK_WIDGET_UNKNOWN, err);
+ ltkd_widget *widget = ltkd_get_widget(tokens[1].text, LTK_WIDGET_UNKNOWN, err);
if (!widget) {
err->arg = 1;
return 1;
}
- ltk_window_set_root_widget(window, widget->widget);
+ ltk_window_set_root_widget(windowid, widget->ltkid);
return 0;
}
@@ -975,7 +980,7 @@ handle_mask_command(int client, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_
/* Returns 1 if command was 'event-unlock true',
-1 if command was 'event-unlock false', 0 otherwise. */
static int
-process_commands(ltk_window *window, int client) {
+process_commands(ltk_widget_id windowid, int client) {
if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
return 0;
struct ltkd_sock_info *sock = &sockets[client];
@@ -1013,12 +1018,12 @@ process_commands(ltk_window *window, int client) {
err = 1;
seq = sock->last_seq;
} else if (strcmp(tokens[0].text, "set-root-widget") == 0) {
- err = ltkd_set_root_widget_cmd(window, tokens, num_tokens, &errdetail);
+ err = ltkd_set_root_widget_cmd(windowid, tokens, num_tokens, &errdetail);
} else if (strcmp(tokens[0].text, "quit") == 0) {
ltkd_quit();
last = 1;
} else if (strcmp(tokens[0].text, "destroy") == 0) {
- err = ltkd_widget_destroy_cmd(window, tokens, num_tokens, &errdetail);
+ err = ltkd_widget_destroy_cmd(windowid, tokens, num_tokens, &errdetail);
} else if (strncmp(tokens[0].text, "mask", 4) == 0) {
err = handle_mask_command(client, tokens, num_tokens, &errdetail);
} else if (strcmp(tokens[0].text, "event-unlock") == 0) {
@@ -1040,7 +1045,7 @@ process_commands(ltk_window *window, int client) {
int found = 0;
for (size_t i = 0; i < LENGTH(widget_funcs); i++) {
if (widget_funcs[i].cmd && !strcmp(tokens[0].text, widget_funcs[i].name)) {
- err = widget_funcs[i].cmd(window, tokens, num_tokens, &errdetail);
+ err = widget_funcs[i].cmd(windowid, tokens, num_tokens, &errdetail);
found = 1;
}
}
diff --git a/src/ltkd/ltkd.h b/src/ltkd/ltkd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016-2026 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
@@ -33,8 +33,8 @@ typedef enum {
LTK_EVENT_MENU = 1 << 3
} ltkd_userevent_type;
-void ltkd_queue_event(ltk_window *window, ltkd_userevent_type type, const char *id, const char *data);
-int ltkd_handle_lock_client(ltk_window *window, int client);
+void ltkd_queue_event(ltk_widget_id windowid, ltkd_userevent_type type, const char *id, const char *data);
+int ltkd_handle_lock_client(ltk_widget_id windowid, int client);
int ltkd_queue_sock_write(int client, const char *str, int len);
int ltkd_queue_sock_write_fmt(int client, const char *fmt, ...);
diff --git a/src/ltkd/menu.c b/src/ltkd/menu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2022-2026 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
@@ -26,11 +26,12 @@
#include <ltk/ltk.h>
#include <ltk/util.h>
#include <ltk/menu.h>
+#include <ltk/array.h>
/* [sub]menu <menu id> create */
static int
ltkd_menu_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -43,14 +44,14 @@ ltkd_menu_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_menu *menu;
+ ltk_widget_id menu;
if (!strcmp(cmd[0].val.str, "menu")) {
- menu = ltk_menu_create(window);
+ menu = ltk_menu_create(windowid);
} else {
- menu = ltk_submenu_create(window);
+ menu = ltk_submenu_create(windowid);
}
- if (!ltkd_widget_create(LTK_CAST_WIDGET(menu), cmd[1].val.str, NULL, 0, err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(menu), 1);
+ if (!ltkd_widget_create(menu, cmd[1].val.str, NULL, 0, err)) {
+ ltk_widget_id_destroy(menu, 1);
err->arg = 1;
return 1;
}
@@ -60,21 +61,22 @@ ltkd_menu_cmd_create(
/* menu <menu id> insert-entry <entry widget id> <index> */
static int
ltkd_menu_cmd_insert_entry(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_menu *menu = LTK_CAST_MENU(widget->widget);
+ (void)windowid;
+ ltk_widget *menuw = ltk_get_widget_from_id(widget->ltkid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0},
- {.type = CMDARG_INT, .min = 0, .max = menu->num_entries, .optional = 0},
+ {.type = CMDARG_INT, .min = 0, .max = ltk_array_len(menu->entries), .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
int ret;
- if ((ret = ltk_menu_insert_entry(menu, LTK_CAST_MENUENTRY(cmd[0].val.widget->widget), cmd[1].val.i))) {
+ if ((ret = ltk_menu_insert_entry(widget->ltkid, cmd[0].val.widget->ltkid, cmd[1].val.i))) {
err->type = ret == 1 ? ERR_WIDGET_IN_CONTAINER : ERR_INVALID_INDEX;
err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 0 : 1;
return 1;
@@ -85,19 +87,18 @@ ltkd_menu_cmd_insert_entry(
/* menu <menu id> add-entry <entry widget id> */
static int
ltkd_menu_cmd_add_entry(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_menu *menu = LTK_CAST_MENU(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- if (ltk_menu_add_entry(menu, LTK_CAST_MENUENTRY(cmd[0].val.widget->widget))) {
+ if (ltk_menu_add_entry(widget->ltkid, cmd[0].val.widget->ltkid)) {
err->type = ERR_WIDGET_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -108,19 +109,21 @@ ltkd_menu_cmd_add_entry(
/* menu <menu id> remove-entry-index <entry index> */
static int
ltkd_menu_cmd_remove_entry_index(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_menu *menu = LTK_CAST_MENU(widget->widget);
+ (void)windowid;
+ ltk_widget *menuw = ltk_get_widget_from_id(widget->ltkid);
+ ltk_menu *menu = LTK_CAST_MENU(menuw);
ltkd_cmdarg_parseinfo cmd[] = {
- {.type = CMDARG_INT, .min = 0, .max = menu->num_entries - 1, .optional = 0},
+ {.type = CMDARG_INT, .min = 0, .max = ltk_array_len(menu->entries) - 1, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- if (!ltk_menu_remove_entry_index(menu, cmd[0].val.i)) {
+ ltk_widget_id ret = ltk_menu_remove_entry_index(widget->ltkid, cmd[0].val.i);
+ if (LTK_WIDGET_ID_IS_NONE(ret)) {
err->type = ERR_WIDGET_NOT_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -131,20 +134,18 @@ ltkd_menu_cmd_remove_entry_index(
/* menu <menu id> remove-entry-id <entry id> */
static int
ltkd_menu_cmd_remove_entry_id(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_menu *menu = LTK_CAST_MENU(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_menuentry *entry = LTK_CAST_MENUENTRY(cmd[0].val.widget->widget);
- if (!ltk_menu_remove_entry(menu, entry)) {
+ if (!ltk_menu_remove_entry(widget->ltkid, cmd[0].val.widget->ltkid)) {
err->type = ERR_WIDGET_NOT_IN_CONTAINER;
err->arg = 0;
return 1;
@@ -155,17 +156,16 @@ ltkd_menu_cmd_remove_entry_id(
/* menu <menu id> remove-all-entries */
static int
ltkd_menu_cmd_remove_all_entries(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
+ (void)windowid;
(void)tokens;
(void)num_tokens;
(void)err;
- ltk_menu *menu = LTK_CAST_MENU(widget->widget);
- ltk_menu_remove_all_entries(menu);
+ ltk_menu_remove_all_entries(widget->ltkid);
return 0;
}
@@ -184,7 +184,7 @@ static ltkd_event_handler entry_handlers[] = {
/* menuentry <id> create <text> */
static int
ltkd_menuentry_cmd_create(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget_unneeded,
ltkd_cmd_token *tokens,
size_t num_tokens,
@@ -198,9 +198,9 @@ ltkd_menuentry_cmd_create(
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
- ltk_menuentry *e = ltk_menuentry_create(window, cmd[3].val.str);
- if (!ltkd_widget_create(LTK_CAST_WIDGET(e), cmd[1].val.str, entry_handlers, LENGTH(entry_handlers), err)) {
- ltk_widget_destroy(LTK_CAST_WIDGET(e), 1);
+ ltk_widget_id e = ltk_menuentry_create(windowid, cmd[3].val.str);
+ if (!ltkd_widget_create(e, cmd[1].val.str, entry_handlers, LENGTH(entry_handlers), err)) {
+ ltk_widget_id_destroy(e, 1);
err->arg = 1;
return 1;
}
@@ -210,20 +210,19 @@ ltkd_menuentry_cmd_create(
/* menuentry <menuentry id> attach-submenu <submenu id> */
static int
ltkd_menuentry_cmd_attach_submenu(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
- ltk_menuentry *e = LTK_CAST_MENUENTRY(widget->widget);
+ (void)windowid;
ltkd_cmdarg_parseinfo cmd[] = {
{.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENU, .optional = 0},
};
if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err))
return 1;
int ret;
- if ((ret = ltk_menuentry_attach_submenu(e, LTK_CAST_MENU(cmd[0].val.widget->widget)))) {
+ if ((ret = ltk_menuentry_attach_submenu(widget->ltkid, cmd[0].val.widget->ltkid))) {
/* FIXME: allow setting err->arg to arg before the args given to function */
/*err->arg = err->type == ERR_MENU_NOT_SUBMENU ? 0 : -2;*/
err->type = ret == 1 ? ERR_MENU_NOT_SUBMENU : ERR_MENU_ENTRY_CONTAINS_SUBMENU;
@@ -236,17 +235,16 @@ ltkd_menuentry_cmd_attach_submenu(
/* menuentry <menuentry id> detach-submenu */
static int
ltkd_menuentry_cmd_detach_submenu(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_widget *widget,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
+ (void)windowid;
(void)tokens;
(void)num_tokens;
(void)err;
- ltk_menuentry *e = LTK_CAST_MENUENTRY(widget->widget);
- ltk_menuentry_detach_submenu(e);
+ ltk_menuentry_detach_submenu(widget->ltkid);
return 0;
}
diff --git a/src/ltkd/widget.c b/src/ltkd/widget.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -45,13 +45,14 @@ static int ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_argl
/* FIXME: generic event handling functions that take the actual uint32_t event type, etc. */
int
ltkd_widget_queue_specific_event(ltkd_widget *widget, const char *type, uint32_t mask, const char *data) {
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lwmask & mask) {
ltkd_queue_sock_write_fmt(
widget->event_masks[i].client,
"eventl %s %s %s\n", widget->id, type, data
);
- if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
+ if (ltkd_handle_lock_client(ltkwid->window, widget->event_masks[i].client))
return 1;
} else if (widget->event_masks[i].wmask & mask) {
ltkd_queue_sock_write_fmt(
@@ -68,22 +69,23 @@ ltkd_widget_resize(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_cal
(void)widget_unused;
(void)args;
ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_RESIZE) {
ltkd_queue_sock_write_fmt(
widget->event_masks[i].client,
"eventl %s widget configure %d %d %d %d\n",
- widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
- widget->widget->lrect.w, widget->widget->lrect.h
+ widget->id, ltkwid->lrect.x, ltkwid->lrect.y,
+ ltkwid->lrect.w, ltkwid->lrect.h
);
- if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
+ if (ltkd_handle_lock_client(ltkwid->window, widget->event_masks[i].client))
return 1;
} else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_RESIZE) {
ltkd_queue_sock_write_fmt(
widget->event_masks[i].client,
"event %s widget configure %d %d %d %d\n",
- widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
- widget->widget->lrect.w, widget->widget->lrect.h
+ widget->id, ltkwid->lrect.x, ltkwid->lrect.y,
+ ltkwid->lrect.w, ltkwid->lrect.h
);
}
}
@@ -95,6 +97,7 @@ ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, l
(void)widget_unused;
(void)args;
ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
/* FIXME: give old and new state in event */
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_STATECHANGE) {
@@ -102,7 +105,7 @@ ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, l
widget->event_masks[i].client,
"eventl %s widget statechange\n", widget->id
);
- if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
+ if (ltkd_handle_lock_client(ltkwid->window, widget->event_masks[i].client))
return 1;
} else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_STATECHANGE) {
ltkd_queue_sock_write_fmt(
@@ -189,12 +192,12 @@ set_event_handlers(ltkd_widget *widget, uint32_t before, uint32_t after, ltkd_ev
if (!(before & 1) && (after & 1)) {
if (handlers[i].callback) {
ltk_widget_register_signal_handler(
- widget->widget, handlers[i].type,
+ widget->ltkid, handlers[i].type,
handlers[i].callback, LTK_MAKE_ARG_VOIDP(widget)
);
}
} else if ((before & 1) && !(after & 1)) {
- ltk_widget_remove_signal_handler_by_callback(widget->widget, handlers[i].callback);
+ ltk_widget_remove_signal_handler_by_callback(widget->ltkid, handlers[i].callback);
}
before >>= 1;
after >>= 1;
@@ -364,6 +367,7 @@ queue_mouse_event(ltkd_widget *widget, ltk_event_type type, int x, int y) {
typename = "mousepress";
break;
}
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lmask & mask) {
ltkd_queue_sock_write_fmt(
@@ -372,7 +376,7 @@ queue_mouse_event(ltkd_widget *widget, ltk_event_type type, int x, int y) {
widget->id, typename, x, y, x, y
/* x - widget->rect.x, y - widget->rect.y */
);
- if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
+ if (ltkd_handle_lock_client(ltkwid->window, widget->event_masks[i].client))
return 1;
} else if (widget->event_masks[i].mask & mask) {
ltkd_queue_sock_write_fmt(
@@ -408,6 +412,7 @@ ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, l
(void)widget_unused;
ltk_scroll_event *event = LTK_GET_ARG_SCROLL_EVENT(args, 0);
ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
uint32_t mask = LTKD_PEVENTMASK_MOUSESCROLL;
for (size_t i = 0; i < widget->masks_num; i++) {
if (widget->event_masks[i].lmask & mask) {
@@ -417,7 +422,7 @@ ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, l
widget->id, "mousescroll", event->x, event->y, event->x, event->y, event->dx, event->dy
/* x - widget->widget->rect.x, y - widget->widget->rect.y */
);
- if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
+ if (ltkd_handle_lock_client(ltkwid->window, widget->event_masks[i].client))
return 1;
} else if (widget->event_masks[i].mask & mask) {
ltkd_queue_sock_write_fmt(
@@ -451,7 +456,8 @@ ltkd_get_widget(const char *id, ltk_widget_type type, ltkd_error *err) {
return NULL;
}
widget = kh_value(widget_hash, k);
- if (type != LTK_WIDGET_UNKNOWN && widget->widget->vtable->type != type) {
+ ltk_widget *ltkwid = ltk_get_widget_from_id(widget->ltkid);
+ if (type != LTK_WIDGET_UNKNOWN && LTK_WIDGET_TYPE(ltkwid) != type) {
err->type = ERR_INVALID_WIDGET_TYPE;
return NULL;
}
@@ -482,14 +488,14 @@ ltkd_remove_widget(const char *id) {
ltkd_widget *
ltkd_widget_create(
- ltk_widget *widget, const char *id,
+ ltk_widget_id ltkid, const char *id,
ltkd_event_handler *event_handlers, size_t num_event_handlers, ltkd_error *err){
if (!ltkd_widget_id_free(id)) {
err->type = ERR_WIDGET_ID_IN_USE;
return NULL;
}
ltkd_widget *w = ltk_malloc(sizeof(ltkd_widget));
- w->widget = widget;
+ w->ltkid = ltkid;
w->id = ltk_strdup(id);
w->event_masks = NULL;
w->masks_num = w->masks_alloc = 0;
@@ -506,17 +512,17 @@ ltkd_widget_destroy(ltkd_widget *widget, int shallow) {
widget->id = NULL;
ltk_free(widget->event_masks);
widget->event_masks = NULL;
- ltk_widget_destroy(widget->widget, shallow);
+ ltk_widget_id_destroy(widget->ltkid, shallow);
ltk_free(widget);
}
int
ltkd_widget_destroy_cmd(
- ltk_window *window,
+ ltk_widget_id windowid,
ltkd_cmd_token *tokens,
size_t num_tokens,
ltkd_error *err) {
- (void)window;
+ (void)windowid;
int shallow = 1;
if (num_tokens != 2 && num_tokens != 3) {
err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
diff --git a/src/ltkd/widget.h b/src/ltkd/widget.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2021-2026 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
@@ -39,7 +39,7 @@ typedef struct {
} ltkd_event_handler;
typedef struct {
- ltk_widget *widget;
+ ltk_widget_id ltkid;
char *id;
client_event_mask *event_masks;
/* FIXME: kind of a waste of space to use size_t here */
@@ -54,7 +54,7 @@ typedef struct {
void ltkd_widgets_init();
ltkd_widget *ltkd_widget_create(
- ltk_widget *widget, const char *id,
+ ltk_widget_id widgetid, const char *id,
ltkd_event_handler *event_handlers, size_t num_event_handlers, ltkd_error *err
);
ltkd_widget *ltkd_get_widget(const char *id, ltk_widget_type type, ltkd_error *err);
@@ -66,7 +66,7 @@ void ltkd_set_widget(ltkd_widget *widget, const char *id);
void ltkd_remove_widget(const char *id);*/
void ltkd_widget_destroy(ltkd_widget *widget, int shallow);
-int ltkd_widget_destroy_cmd(ltk_window *window, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_error *err);
+int ltkd_widget_destroy_cmd(ltk_widget_id windowid, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_error *err);
void ltkd_widget_remove_client(int client);
void ltkd_widgets_cleanup(void);