changeset 608:2527b000aaa5

ui: do the same with gridmenu
author David Demelier <markand@malikania.fr>
date Fri, 11 Aug 2023 19:45:00 +0200
parents 8444f83d48af
children d97674d33764
files examples/example-gridmenu/example-gridmenu.c libmlk-ui/mlk/ui/gridmenu.c libmlk-ui/mlk/ui/gridmenu.h
diffstat 3 files changed, 332 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/examples/example-gridmenu/example-gridmenu.c	Fri Aug 11 13:34:17 2023 +0200
+++ b/examples/example-gridmenu/example-gridmenu.c	Fri Aug 11 19:45:00 2023 +0200
@@ -32,14 +32,13 @@
 #include <mlk/ui/frame.h>
 #include <mlk/ui/gridmenu.h>
 #include <mlk/ui/label.h>
+#include <mlk/ui/style.h>
 #include <mlk/ui/ui.h>
 
 #include <mlk/example/example.h>
 #include <mlk/example/glower.h>
 #include <mlk/example/registry.h>
 
-static void menu_update(struct mlk_gridmenu_delegate *, struct mlk_gridmenu *, unsigned int);
-
 static const char * const items[] = {
 	"Feu mineur",
 	"Feu majeur",
@@ -60,21 +59,21 @@
 	"Double tour"
 };
 
+static struct mlk_style style;
+
 static struct mlk_frame frame = {
 	.w = 300,
-	.h = 100
-};
-static struct mlk_gridmenu_style menu_style = {0};
-static struct mlk_gridmenu_delegate menu_delegate = {
-	.update = menu_update
+	.h = 100,
+	.delegate = &mlk_frame_delegate,
+	.style = &mlk_style
 };
 static struct mlk_gridmenu menu = {
 	.nrows = 3,
 	.ncols = 2,
 	.items = items,
 	.itemsz = MLK_UTIL_SIZE(items),
-	.style = &menu_style,
-	.delegate = &menu_delegate
+	.delegate = &mlk_gridmenu_delegate,
+	.style = &style,
 };
 static struct mlk_glower menu_glower = {
 	.start = 0x00bfa3ff,
@@ -83,13 +82,10 @@
 };
 
 static void
-menu_update(struct mlk_gridmenu_delegate *delegate, struct mlk_gridmenu *menu, unsigned int ticks)
+update_color(unsigned int ticks)
 {
-	(void)delegate;
-	(void)menu;
-
 	mlk_glower_update(&menu_glower, ticks);
-	menu_style.selected_color = menu_glower.color;
+	style.selected.color.text = menu_glower.color;
 }
 
 static void
@@ -98,7 +94,8 @@
 	if (mlk_example_init("example-gridmenu") < 0)
 		mlk_panic();
 
-	menu_style = mlk_gridmenu_style;
+	/* Copy style. */
+	style = mlk_style;
 	mlk_glower_init(&menu_glower);
 }
 
@@ -123,7 +120,7 @@
 {
 	(void)st;
 
-	mlk_glower_update(&menu_glower, ticks);
+	update_color(ticks);
 	mlk_gridmenu_update(&menu, ticks);
 }
 
--- a/libmlk-ui/mlk/ui/gridmenu.c	Fri Aug 11 13:34:17 2023 +0200
+++ b/libmlk-ui/mlk/ui/gridmenu.c	Fri Aug 11 19:45:00 2023 +0200
@@ -18,19 +18,18 @@
 
 #include <assert.h>
 #include <math.h>
-#include <string.h>
 
 #include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/font.h>
 #include <mlk/core/maths.h>
 #include <mlk/core/painter.h>
-#include <mlk/core/panic.h>
 #include <mlk/core/texture.h>
 #include <mlk/core/trace.h>
+#include <mlk/core/window.h>
 
 #include "gridmenu.h"
-#include "ui.h"
+#include "style.h"
 #include "ui_p.h"
 
 struct index {
@@ -38,15 +37,6 @@
 	unsigned int col;
 };
 
-static inline struct mlk_font *
-style_font(struct mlk_gridmenu_style *style)
-{
-	if (style && style->text_font)
-		return style->text_font;
-
-	return &mlk_ui_fonts[MLK_UI_FONT_INTERFACE];
-}
-
 static struct index
 get_index(const struct mlk_gridmenu *menu)
 {
@@ -56,53 +46,6 @@
 	};
 }
 
-static void
-geometry(struct mlk_gridmenu *menu)
-{
-	const struct mlk_gridmenu_style *style;
-	unsigned int reqw = 0, reqh = 0, lw, lh;
-
-	/* Compute which item has the bigger width/height to create a spacing. */
-	menu->eltw = menu->elth = 0;
-	menu->spacew = menu->spaceh = 0;
-
-	style = MLK__STYLE(menu, mlk_gridmenu_style);
-
-	for (size_t i = 0; i < menu->itemsz; ++i) {
-		if (!(menu->items[i]))
-			continue;
-
-		mlk_font_query(style_font(menu->style), menu->items[i], &lw, &lh);
-
-		menu->eltw = fmax(menu->eltw, lw);
-		menu->elth = fmax(menu->elth, lh);
-	}
-
-	/* Total texture size required to draw items. */
-	reqw = (style->padding * 3) + (menu->eltw * menu->ncols);
-	reqh = (style->padding * 3) + (menu->elth * menu->nrows);
-
-	/*
-	 * Compute spacing between elements. We remove the padding because it
-	 * is outside of the elements.
-	 */
-	if (reqw > menu->w) {
-		mlk_tracef(_("gridmenu width is too small: %u < %u"), menu->w, reqw);
-		menu->spacew = 1;
-	} else if (menu->ncols > 1) {
-		reqw -= style->padding * 2;
-		menu->spacew = (menu->w - reqw) / menu->ncols;
-	}
-
-	if (reqh > menu->h) {
-		mlk_tracef(_("gridmenu height is too small: %u < %u"), menu->h, reqh);
-		menu->spaceh = 1;
-	} else if (menu->nrows > 1) {
-		reqh -= style->padding * 2;
-		menu->spaceh = (menu->h - reqh) / menu->nrows;
-	}
-}
-
 static int
 handle_keydown(struct mlk_gridmenu *menu, const struct mlk_event_key *key)
 {
@@ -145,17 +88,16 @@
 {
 	assert(click->type == MLK_EVENT_CLICKDOWN);
 
-	const struct mlk_gridmenu_style *style;
+	const struct mlk_style_attr *attr = &menu->style->normal;
 	size_t pagesz, pagenr, selected, c = 0, r = 0;
 	int x, y;
 
-	style  = MLK__STYLE(menu, mlk_gridmenu_style);
 	pagesz = menu->nrows * menu->ncols;
 	pagenr = menu->selected / pagesz;
 
 	for (size_t i = 0; i < pagesz; ++i) {
-		x = (int)(menu->x + style->padding + (c * menu->eltw) + (c * menu->spacew));
-		y = (int)(menu->y + style->padding + (r * menu->elth) + (r * menu->spaceh));
+		x = (int)(menu->x + attr->geo.padding + (c * menu->eltw) + (c * menu->spacew));
+		y = (int)(menu->y + attr->geo.padding + (r * menu->elth) + (r * menu->spaceh));
 
 		if (mlk_maths_is_boxed(click->x, click->y, x, y, menu->eltw, menu->elth)) {
 			selected  = c + r * menu->ncols;
@@ -177,12 +119,78 @@
 }
 
 static void
-draw(struct mlk_gridmenu_delegate *delegate, const struct mlk_gridmenu *menu)
+resize(struct mlk_gridmenu_delegate *self, struct mlk_gridmenu *menu)
 {
-	(void)delegate;
+	(void)self;
+
+	const struct mlk_style_attr *attr = &menu->style->normal;
+	unsigned int reqw = 0, reqh = 0, lw, lh;
+
+	/* Compute which item has the bigger width/height to create a spacing. */
+	menu->eltw = menu->elth = 0;
+	menu->spacew = menu->spaceh = 0;
+
+	for (size_t i = 0; i < menu->itemsz; ++i) {
+		if (!(menu->items[i]))
+			continue;
+
+		mlk_font_query(attr->font, menu->items[i], &lw, &lh);
+
+		menu->eltw = fmax(menu->eltw, lw);
+		menu->elth = fmax(menu->elth, lh);
+	}
+
+	/* Total texture size required to draw items. */
+	reqw = (attr->geo.padding * 3) + (menu->eltw * menu->ncols);
+	reqh = (attr->geo.padding * 3) + (menu->elth * menu->nrows);
+
+	/*
+	 * Compute spacing between elements. We remove the padding because it
+	 * is outside of the elements.
+	 */
+	if (reqw > menu->w) {
+		mlk_tracef(_("gridmenu width is too small: %u < %u"), menu->w, reqw);
+		menu->spacew = 1;
+	} else if (menu->ncols > 1) {
+		reqw -= attr->geo.padding * 2;
+		menu->spacew = (menu->w - reqw) / menu->ncols;
+	}
 
+	if (reqh > menu->h) {
+		mlk_tracef(_("gridmenu height is too small: %u < %u"), menu->h, reqh);
+		menu->spaceh = 1;
+	} else if (menu->nrows > 1) {
+		reqh -= attr->geo.padding * 2;
+		menu->spaceh = (menu->h - reqh) / menu->nrows;
+	}
+}
+
+static int
+handle(struct mlk_gridmenu_delegate *self,
+       struct mlk_gridmenu *menu,
+       const union mlk_event *ev)
+{
+	(void)self;
+
+	switch (ev->type) {
+	case MLK_EVENT_KEYDOWN:
+		return handle_keydown(menu, &ev->key);
+	case MLK_EVENT_CLICKDOWN:
+		return handle_clickdown(menu, &ev->click);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void
+draw(struct mlk_gridmenu_delegate *self, const struct mlk_gridmenu *menu)
+{
+	(void)self;
+
+	const struct mlk_style *st = menu->style;
 	size_t pagesz, pagenr, item, c = 0, r = 0;
-	const struct mlk_gridmenu_style *style;
 	struct mlk_texture tex;
 	struct mlk_font *font;
 	unsigned long color;
@@ -195,22 +203,22 @@
 	pagesz = menu->nrows * menu->ncols;
 	pagenr = menu->selected / pagesz;
 
-	style = MLK__STYLE(menu, mlk_gridmenu_style);
-	font = style_font(menu->style);
-
 	for (size_t i = 0; i < pagesz; ++i) {
 		item = i + pagenr * pagesz;
 
 		if (item >= menu->itemsz || !menu->items[item])
 			continue;
 
-		x = (int)(menu->x + style->padding + (c * menu->eltw) + (c * menu->spacew));
-		y = (int)(menu->y + style->padding + (r * menu->elth) + (r * menu->spaceh));
+		x = (int)(menu->x + st->normal.geo.padding + (c * menu->eltw) + (c * menu->spacew));
+		y = (int)(menu->y + st->normal.geo.padding + (r * menu->elth) + (r * menu->spaceh));
 
-		if (i == menu->selected % pagesz)
-			color = style->selected_color;
-		else
-			color = style->text_color;
+		if (i == menu->selected % pagesz) {
+			color = st->selected.color.text;
+			font = st->selected.font;
+		} else {
+			color = st->normal.color.text;
+			font = st->normal.font;
+		}
 
 		if (mlk_font_render(font, &tex, menu->items[item], color) < 0) {
 			mlk_tracef(_("unable to render grid menu item: %s"), mlk_err());
@@ -227,24 +235,43 @@
 	}
 }
 
-struct mlk_gridmenu_style mlk_gridmenu_style = {
-	.padding                = 10,
-	.text_color             = MLK_UI_COLOR_TEXT,
-	.selected_color         = MLK_UI_COLOR_SELECTED
+struct mlk_gridmenu_delegate mlk_gridmenu_delegate = {
+	.resize = resize,
+	.handle = handle,
+	.draw = draw
 };
 
-struct mlk_gridmenu_delegate mlk_gridmenu_delegate = {
-	.draw                   = draw
-};
+void
+mlk_gridmenu_init(struct mlk_gridmenu *menu,
+                  struct mlk_gridmenu_delegate *dt,
+                  struct mlk_style *st)
+{
+	assert(menu);
 
-int
-mlk_gridmenu_ok(const struct mlk_gridmenu *menu)
-{
-	return menu && menu->items && menu->itemsz;
+	menu->x = 0;
+	menu->y = 0;
+	menu->w = 0;
+	menu->h = 0;
+	menu->items = NULL;
+	menu->itemsz = 0;
+	menu->nrows = 0;
+	menu->ncols = 0;
+	menu->delegate = dt ? dt : &mlk_gridmenu_delegate;
+	menu->style = st ? st : &mlk_style;
+
+	/* Can't be computed yet. */
+	menu->eltw = 0;
+	menu->elth = 0;
+	menu->spacew = 0;
+	menu->spaceh = 0;
 }
 
 void
-mlk_gridmenu_resize(struct mlk_gridmenu *menu, int x, int y, unsigned int w, unsigned int h)
+mlk_gridmenu_resize(struct mlk_gridmenu *menu,
+                    int x,
+                    int y,
+                    unsigned int w,
+                    unsigned int h)
 {
 	assert(menu);
 
@@ -253,7 +280,8 @@
 	menu->w = w;
 	menu->h = h;
 
-	geometry(menu);
+	if (menu->delegate->resize)
+		menu->delegate->resize(menu->delegate, menu);
 }
 
 int
@@ -262,16 +290,8 @@
 	assert(menu);
 	assert(ev);
 
-	switch (ev->type) {
-	case MLK_EVENT_KEYDOWN:
-		return handle_keydown(menu, &ev->key);
-		break;
-	case MLK_EVENT_CLICKDOWN:
-		return handle_clickdown(menu, &ev->click);
-		break;
-	default:
-		break;
-	}
+	if (menu->delegate->handle)
+		return menu->delegate->handle(menu->delegate, menu, ev);
 
 	return 0;
 }
@@ -279,9 +299,10 @@
 void
 mlk_gridmenu_update(struct mlk_gridmenu *menu, unsigned int ticks)
 {
-	assert(mlk_gridmenu_ok(menu));
+	assert(menu);
 
-	MLK__DELEGATE_INVOKE(menu->delegate, mlk_gridmenu_delegate, update, menu, ticks);
+	if (menu->delegate->update)
+		menu->delegate->update(menu->delegate, menu, ticks);
 }
 
 void
@@ -289,5 +310,6 @@
 {
 	assert(menu);
 
-	MLK__DELEGATE_INVOKE(menu->delegate, mlk_gridmenu_delegate, draw, menu);
+	if (menu->delegate->draw)
+		menu->delegate->draw(menu->delegate, menu);
 }
--- a/libmlk-ui/mlk/ui/gridmenu.h	Fri Aug 11 13:34:17 2023 +0200
+++ b/libmlk-ui/mlk/ui/gridmenu.h	Fri Aug 11 19:45:00 2023 +0200
@@ -21,66 +21,233 @@
 
 #include <stddef.h>
 
-#include "label.h"
+struct mlk_gridmenu;
+struct mlk_gridmenu_delegate;
+struct mlk_style;
 
 union mlk_event;
 
-struct mlk_font;
-struct mlk_gridmenu;
+struct mlk_gridmenu {
+	/**
+	 * (read-write)
+	 *
+	 * Position in x.
+	 */
+	int x;
 
-struct mlk_gridmenu_style {
-	unsigned int padding;
-	unsigned long text_color;
-	struct mlk_font *text_font;
-	unsigned long selected_color;
-};
+	/**
+	 * (read-write)
+	 *
+	 * Position in y.
+	 */
+	int y;
+
+	/**
+	 * (read-write)
+	 *
+	 * Menu frame width.
+	 */
+	unsigned int w;
+
+	/**
+	 * (read-write)
+	 *
+	 * Menu frame height.
+	 */
+	unsigned int h;
 
-struct mlk_gridmenu_delegate {
-	void *data;
-	void (*update)(struct mlk_gridmenu_delegate *, struct mlk_gridmenu *, unsigned int);
-	void (*draw)(struct mlk_gridmenu_delegate *, const struct mlk_gridmenu *);
-};
+	/**
+	 * (read-write, borrowed)
+	 *
+	 * List of strings to show.
+	 */
+	const char * const *items;
+
+	/**
+	 * (read-write)
+	 *
+	 * Number of items in ::mlk_gridmenu::items.
+	 */
+	size_t itemsz;
 
-struct mlk_gridmenu {
-	/* public */
-	int x, y;
-	unsigned int w, h;
-	const char * const *items;
-	size_t itemsz;
+	/**
+	 * (read-write)
+	 *
+	 * Current selected item in the grid.
+	 */
 	size_t selected;
+
+	/**
+	 * (read-write)
+	 *
+	 * Number of rows to be shown within the frame height.
+	 */
 	unsigned int nrows;
+
+	/**
+	 * (read-write)
+	 *
+	 * Number of colums to be shown within the frame width.
+	 */
 	unsigned int ncols;
-	struct mlk_gridmenu_style *style;
+
+	/**
+	 * (read-write, borrowed)
+	 *
+	 * Grid menu delegate.
+	 */
 	struct mlk_gridmenu_delegate *delegate;
 
-	/* private */
+	/**
+	 * (read-write, borrowed)
+	 *
+	 * Grid menu style.
+	 */
+	struct mlk_style *style;
+
+	/** \cond MLK_PRIVATE_DECLS */
 	unsigned int eltw;      /* maximum entry label width */
 	unsigned int elth;      /* maximum entry label height */
 	unsigned int spacew;    /* space between element horizontally */
 	unsigned int spaceh;    /* and vertically */
+	/** \endcond MLK_PRIVATE_DECLS */
 };
 
-extern struct mlk_gridmenu_style mlk_gridmenu_style;
+/**
+ * \struct mlk_gridmenu_delegate
+ * \brief Grid menu delegate.
+ */
+struct mlk_gridmenu_delegate {
+	/*
+	 * (read-write, borrowed, optional)
+	 *
+	 * Arbitrary user data.
+	 */
+	void *data;
+
+	/**
+	 * (read-write, optional)
+	 *
+	 * Called after resizing the grid menu dimensions.
+	 *
+	 * \param self this delegate
+	 * \param menu the grid menu
+	 */
+	void (*resize)(struct mlk_gridmenu_delegate *self,
+	               struct mlk_gridmenu *menu);
+
+	/**
+	 * (read-write, optional)
+	 *
+	 * Handle an event.
+	 *
+	 * \param self this delegate
+	 * \param menu the menu
+	 * \param ev the event
+	 * \return non-zero if an item has been selected
+	 */
+	int (*handle)(struct mlk_gridmenu_delegate *self,
+	              struct mlk_gridmenu *menu,
+	              const union mlk_event *ev);
+
+	/**
+	 * (read-write, optional)
+	 *
+	 * Update the grid menu.
+	 *
+	 * \param self this delegate
+	 * \param menu the menu to update
+	 * \param ticks number of ticks since last frame
+	 */
+	void (*update)(struct mlk_gridmenu_delegate *self,
+	               struct mlk_gridmenu *menu,
+	               unsigned int ticks);
+
+	/**
+	 * (read-write, optional)
+	 *
+	 * Draw this menu.
+	 *
+	 * \param self this delegate
+	 * \param menu the menu to draw
+	 */
+	void (*draw)(struct mlk_gridmenu_delegate *self,
+	             const struct mlk_gridmenu *menu);
+
+	/**
+	 * (read-write, optional)
+	 *
+	 * Cleanup this delegate associated with the menu.
+	 *
+	 * \param self this delegate
+	 * \param frame the underlying frame
+	 */
+	void (*finish)(struct mlk_gridmenu_delegate *self,
+	               struct mlk_gridmenu *menu);
+
+};
+
 extern struct mlk_gridmenu_delegate mlk_gridmenu_delegate;
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
-int
-mlk_gridmenu_ok(const struct mlk_gridmenu *);
+/**
+ * Initialize the menu with default values.
+ *
+ * This is not required if you use designated initializers.
+ *
+ * \pre menu != NULL
+ * \param menu the grid menu to default initialize
+ * \param st style to use (or NULL to use a default)
+ * \param dt delegate to use (or NULL to use a default)
+ */
+void
+mlk_gridmenu_init(struct mlk_gridmenu *menu,
+                  struct mlk_gridmenu_delegate *dt,
+                  struct mlk_style *st);
 
+/**
+ * Copy new dimensions and then invoke ::mlk_gridmenu_delegate::resize.
+ *
+ * \pre menu != NULL
+ * \param menu the menu to resize
+ * \param x new position in x
+ * \param y new position in y
+ * \param w new width
+ * \param h new height
+ */
 void
-mlk_gridmenu_resize(struct mlk_gridmenu *, int, int, unsigned int, unsigned int);
+mlk_gridmenu_resize(struct mlk_gridmenu *menu,
+                    int x,
+                    int y,
+                    unsigned int w,
+                    unsigned int h);
 
+/**
+ * Invoke ::mlk_gridmenu_delegate::handle.
+ */
 int
-mlk_gridmenu_handle(struct mlk_gridmenu *, const union mlk_event *);
+mlk_gridmenu_handle(struct mlk_gridmenu *menu, const union mlk_event *ev);
 
+/**
+ * Invoke ::mlk_gridmenu_delegate::update.
+ */
 void
-mlk_gridmenu_update(struct mlk_gridmenu *, unsigned int);
+mlk_gridmenu_update(struct mlk_gridmenu *menu, unsigned int ticks);
 
+/**
+ * Invoke ::mlk_gridmenu_delegate::draw.
+ */
 void
-mlk_gridmenu_draw(const struct mlk_gridmenu *);
+mlk_gridmenu_draw(const struct mlk_gridmenu *menu);
+
+/**
+ * Invoke ::mlk_gridmenu_delegate::finish.
+ */
+void
+mlk_gridmenu_finish(struct mlk_gridmenu *menu);
 
 #if defined(__cplusplus)
 }