window: add simple text tooltip handlers
authorTiago Vignatti <tiago.vignatti@intel.com>
Wed, 23 May 2012 19:06:27 +0000 (22:06 +0300)
committerKristian Høgsberg <krh@bitplanet.net>
Sat, 26 May 2012 02:53:59 +0000 (22:53 -0400)
Using set_transient.

Signed-off-by: Tiago Vignatti <tiago.vignatti@intel.com>
clients/window.c
clients/window.h

index 489c35a..9e9b209 100644 (file)
@@ -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)
 {
index db232e0..bc0e43a 100644 (file)
@@ -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