diff libmlk-ui/mlk/ui/notify.c @ 433:862b15c3a3ae

ui: cleanup hierarchy
author David Demelier <markand@malikania.fr>
date Sat, 15 Oct 2022 21:19:25 +0200
parents src/libmlk-ui/ui/notify.c@8f59201dc76b
children 773a082f0b91
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-ui/mlk/ui/notify.c	Sat Oct 15 21:19:25 2022 +0200
@@ -0,0 +1,209 @@
+/*
+ * notify.c -- in game notifications
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <mlk/core/font.h>
+#include <mlk/core/texture.h>
+#include <mlk/core/trace.h>
+#include <mlk/core/window.h>
+
+#include "align.h"
+#include "frame.h"
+#include "label.h"
+#include "notify.h"
+#include "theme.h"
+
+#define WIDTH   (window.w / 3)
+#define HEIGHT  (window.h / 10)
+
+struct geo {
+	const struct theme *theme;
+	int frame_x;
+	int frame_y;
+	unsigned int frame_w;
+	unsigned int frame_h;
+	int icon_x;
+	int icon_y;
+	int title_x;
+	int title_y;
+	int body_x;
+	int body_y;
+};
+
+static void draw(const struct notify *, size_t);
+
+static const struct notify_system default_system = {
+	.draw = draw
+};
+static const struct notify_system *system = &default_system;
+static struct notify stack[NOTIFY_MAX];
+static size_t stacksz;
+
+static void
+geometry(struct geo *geo, const struct notify *n, size_t index)
+{
+	int x, y;
+
+	/* Determine theme. */
+	geo->theme = system->theme ? system->theme : theme_default();
+
+	/* Determine notification position. */
+	x  = window.w - geo->theme->padding;
+	x -= WIDTH;
+
+	y  = geo->theme->padding * (index + 1);;
+	y += HEIGHT * index;
+
+	/* Content frame. */
+	geo->frame_x = x;
+	geo->frame_y = y;
+	geo->frame_w = WIDTH;
+	geo->frame_h = HEIGHT;
+
+	/* Align icon at the left center. */
+	if (n->icon->h >= HEIGHT) {
+		tracef("notification icon is too large: %u > %u", n->icon->h, HEIGHT);
+		geo->icon_x = x + geo->theme->padding;
+		geo->icon_y = y + geo->theme->padding;
+	} else {
+		align(ALIGN_LEFT, &geo->icon_x, &geo->icon_y, n->icon->w, n->icon->h, x, y, WIDTH, HEIGHT);
+		geo->icon_x += geo->icon_y - y;
+	}
+
+	/* Align title to the right of the icon at the same y coordinate. */
+	geo->title_x  = geo->icon_x + n->icon->w + geo->theme->padding;
+	geo->title_y  = geo->icon_y;
+	geo->title_y -= font_height(geo->theme->fonts[THEME_FONT_IMPORTANT]) / 2;
+
+	/* Align body so it ends at the end of the icon. */
+	geo->body_x  = geo->title_x;
+	geo->body_y  = geo->icon_y + n->icon->h;
+	geo->body_y -= font_height(geo->theme->fonts[THEME_FONT_INTERFACE]) / 2;
+}
+
+static void
+draw_frame(const struct geo *geo)
+{
+	const struct frame f = {
+		.x = geo->frame_x,
+		.y = geo->frame_y,
+		.w = geo->frame_w,
+		.h = geo->frame_h
+	};
+
+	frame_draw(&f);
+}
+
+static void
+draw_icon(const struct geo *geo, const struct notify *n)
+{
+	texture_draw(n->icon, geo->icon_x, geo->icon_y);
+}
+
+#include <stdio.h>
+
+static void
+draw_title(const struct geo *geo, const struct notify *n)
+{
+	const struct label l = {
+		.x = geo->title_x,
+		.y = geo->title_y,
+		.text = n->title,
+		.flags = LABEL_FLAGS_SHADOW | LABEL_FLAGS_IMPORTANT
+	};
+
+	label_draw(&l);
+}
+
+static void
+draw_body(const struct geo *geo, const struct notify *n)
+{
+	const struct label l = {
+		.x = geo->body_x,
+		.y = geo->body_y,
+		.text = n->body,
+		.flags = LABEL_FLAGS_SHADOW
+	};
+
+	label_draw(&l);
+}
+
+static void
+draw(const struct notify *n, size_t index)
+{
+	struct geo geo;
+
+	/* Compute notification size and widgets. */
+	geometry(&geo, n, index);
+
+	draw_frame(&geo);
+	draw_icon(&geo, n);
+	draw_title(&geo, n);
+	draw_body(&geo, n);
+}
+
+void
+notify(const struct texture *icon, const char *title, const char *body)
+{
+	assert(icon);
+	assert(title);
+	assert(body);
+
+	struct notify *n;
+
+	if (stacksz >= NOTIFY_MAX) {
+		memmove(&stack[0], &stack[1], sizeof (stack[0]) - NOTIFY_MAX - 1);
+		n = &stack[NOTIFY_MAX - 1];
+	} else
+		n = &stack[stacksz++];
+
+	memset(n, 0, sizeof (*n));
+	n->icon = icon;
+	n->title = title;
+	n->body = body;
+}
+
+void
+notify_update(unsigned int ticks)
+{
+	struct notify *n;
+
+	for (size_t i = 0; i < stacksz; ++i) {
+		n = &stack[i];
+		n->elapsed += ticks;
+
+		if (n->elapsed >= NOTIFY_TIMEOUT_DEFAULT)
+			memmove(n, n + 1, sizeof (*n) * (--stacksz - i));
+	}
+}
+
+void
+notify_draw(void)
+{
+	for (size_t i = 0; i < stacksz; ++i)
+		system->draw(&stack[i], i);
+}
+
+void
+notify_set_system(const struct notify_system *sys)
+{
+	system = sys ? sys : &default_system;
+}