[PATCH 2/3] window: add simple text tooltip handlers

Tiago Vignatti tiago.vignatti at intel.com
Wed May 23 12:06:27 PDT 2012


Using set_transient.

Signed-off-by: Tiago Vignatti <tiago.vignatti at intel.com>
---
 clients/window.c |  188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 clients/window.h |    7 ++
 2 files changed, 195 insertions(+)

diff --git a/clients/window.c b/clients/window.c
index 489c35a..9e9b209 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -36,6 +36,7 @@
 #include <cairo.h>
 #include <sys/mman.h>
 #include <sys/epoll.h>
+#include <sys/timerfd.h>
 
 #include <pixman.h>
 
@@ -169,6 +170,7 @@ struct window {
 
 struct widget {
 	struct window *window;
+	struct tooltip *tooltip;
 	struct wl_list child_list;
 	struct wl_list link;
 	struct rectangle allocation;
@@ -180,6 +182,7 @@ struct widget {
 	widget_button_handler_t button_handler;
 	void *user_data;
 	int opaque;
+	int tooltip_count;
 };
 
 struct input {
@@ -266,6 +269,16 @@ struct menu {
 	menu_func_t func;
 };
 
+struct tooltip {
+	struct widget *parent;
+	struct window *window;
+	struct widget *widget;
+	char *entry;
+	struct task tooltip_task;
+	int tooltip_fd;
+	float x, y;
+};
+
 struct shm_pool {
 	struct wl_shm_pool *pool;
 	size_t size;
@@ -862,6 +875,8 @@ widget_create(struct window *window, void *data)
 	widget->allocation = window->allocation;
 	wl_list_init(&widget->child_list);
 	widget->opaque = 0;
+	widget->tooltip = NULL;
+	widget->tooltip_count = 0;
 
 	return widget;
 }
@@ -892,6 +907,11 @@ widget_destroy(struct widget *widget)
 	struct display *display = widget->window->display;
 	struct input *input;
 
+	if (widget->tooltip) {
+		free(widget->tooltip);
+		widget->tooltip = NULL;
+	}
+
 	wl_list_for_each(input, &display->input_list, link) {
 		if (input->focus_widget == widget)
 			input->focus_widget = NULL;
@@ -1000,6 +1020,174 @@ window_get_wl_shell_surface(struct window *window)
 }
 
 static void
+tooltip_redraw_handler(struct widget *widget, void *data)
+{
+	cairo_t *cr;
+	const int32_t r = 3;
+	struct tooltip *tooltip = data;
+	int32_t width, height;
+	struct window *window = widget->window;
+
+	cr = cairo_create(window->cairo_surface);
+	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+	cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
+	cairo_paint(cr);
+
+	width = window->allocation.width;
+	height = window->allocation.height;
+	rounded_rect(cr, 0, 0, width, height, r);
+
+	cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+	cairo_set_source_rgba(cr, 0.0, 0.0, 0.4, 0.8);
+	cairo_fill(cr);
+
+	cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+	cairo_move_to(cr, 10, 16);
+	cairo_show_text(cr, tooltip->entry);
+	cairo_destroy(cr);
+}
+
+static cairo_text_extents_t
+get_text_extents(struct tooltip *tooltip)
+{
+	struct window *window;
+	cairo_t *cr;
+	cairo_text_extents_t extents;
+
+	/* we borrow cairo_surface from the parent cause tooltip's wasn't
+	 * created yet */
+	window = tooltip->widget->window->parent;
+	cr = cairo_create(window->cairo_surface);
+	cairo_text_extents(cr, tooltip->entry, &extents);
+	cairo_destroy(cr);
+
+	return extents;
+}
+
+static int
+window_create_tooltip(struct tooltip *tooltip)
+{
+	struct widget *parent = tooltip->parent;
+	struct display *display = parent->window->display;
+	struct window *window;
+	const int offset_y = 27;
+	const int margin = 3;
+	cairo_text_extents_t extents;
+
+	if (tooltip->widget)
+		return 0;
+
+	window = window_create_transient(display, parent->window, tooltip->x,
+					 tooltip->y + offset_y,
+					 WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
+	if (!window)
+		return -1;
+
+	tooltip->window = window;
+	tooltip->widget = window_add_widget(tooltip->window, tooltip);
+
+	extents = get_text_extents(tooltip);
+	widget_set_redraw_handler(tooltip->widget, tooltip_redraw_handler);
+	window_schedule_resize(window, extents.width + 20, 20 + margin * 2);
+
+	return 0;
+}
+
+void
+widget_destroy_tooltip(struct widget *parent)
+{
+	struct tooltip *tooltip = parent->tooltip;
+
+	parent->tooltip_count = 0;
+	if (!tooltip)
+		return;
+
+	if (tooltip->widget) {
+		widget_destroy(tooltip->widget);
+		window_destroy(tooltip->window);
+		tooltip->widget = NULL;
+		tooltip->window = NULL;
+	}
+
+	close(tooltip->tooltip_fd);
+	free(tooltip->entry);
+	free(tooltip);
+	parent->tooltip = NULL;
+}
+
+static void
+tooltip_func(struct task *task, uint32_t events)
+{
+	struct tooltip *tooltip =
+		container_of(task, struct tooltip, tooltip_task);
+	uint64_t exp;
+
+	read(tooltip->tooltip_fd, &exp, sizeof (uint64_t));
+	window_create_tooltip(tooltip);
+}
+
+#define TOOLTIP_TIMEOUT 500
+static int
+tooltip_timer_reset(struct tooltip *tooltip)
+{
+	struct itimerspec its;
+
+	its.it_interval.tv_sec = 0;
+	its.it_interval.tv_nsec = 0;
+	its.it_value.tv_sec = TOOLTIP_TIMEOUT / 1000;
+	its.it_value.tv_nsec = (TOOLTIP_TIMEOUT % 1000) * 1000 * 1000;
+	if (timerfd_settime(tooltip->tooltip_fd, 0, &its, NULL) < 0) {
+		fprintf(stderr, "could not set timerfd\n: %m");
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+widget_set_tooltip(struct widget *parent, char *entry, float x, float y)
+{
+	struct tooltip *tooltip = parent->tooltip;
+
+	parent->tooltip_count++;
+	if (tooltip) {
+		tooltip->x = x;
+		tooltip->y = y;
+		tooltip_timer_reset(tooltip);
+		return 0;
+	}
+
+	/* the handler might be triggered too fast via input device motion, so
+	 * we need this check here to make sure tooltip is fully initialized */
+	if (parent->tooltip_count > 1)
+		return 0;
+
+        tooltip = malloc(sizeof *tooltip);
+        if (!tooltip)
+                return -1;
+
+	parent->tooltip = tooltip;
+	tooltip->parent = parent;
+	tooltip->widget = NULL;
+	tooltip->window = NULL;
+	tooltip->x = x;
+	tooltip->y = y;
+	tooltip->entry = strdup(entry);
+	tooltip->tooltip_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+	if (tooltip->tooltip_fd < 0) {
+		fprintf(stderr, "could not create timerfd\n: %m");
+		return -1;
+	}
+
+	tooltip->tooltip_task.run = tooltip_func;
+	display_watch_fd(parent->window->display, tooltip->tooltip_fd,
+			 EPOLLIN, &tooltip->tooltip_task);
+	tooltip_timer_reset(tooltip);
+
+	return 0;
+}
+
+static void
 frame_resize_handler(struct widget *widget,
 		     int32_t width, int32_t height, void *data)
 {
diff --git a/clients/window.h b/clients/window.h
index db232e0..bc0e43a 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -306,8 +306,15 @@ window_set_title(struct window *window, const char *title);
 const char *
 window_get_title(struct window *window);
 
+int
+widget_set_tooltip(struct widget *parent, char *entry, float x, float y);
+
+void
+widget_destroy_tooltip(struct widget *parent);
+
 struct widget *
 widget_add_widget(struct widget *parent, void *data);
+
 void
 widget_destroy(struct widget *widget);
 void
-- 
1.7.9.5



More information about the wayland-devel mailing list