Compare commits

...

5 Commits

Author SHA1 Message Date
Timm Bäder
2afd0dad69 gl renderer: Ignore nodes outside of the clip
Pretty sure this will bite me later but for now we avoid rendering nodes
that we are not going to see anyway.
2018-05-01 12:51:04 +02:00
Timm Bäder
7328cd01fc gl renderer: Use simple offset for selected offset node children 2018-05-01 08:19:48 +02:00
Timm Bäder
5efcf649ae emojichooser: Load emojis 'asynchronously'
Don't block the main loop until we've loaded those thousand emojis.
Instead, load a few in an idle.
2018-05-01 07:52:55 +02:00
Timm Bäder
e5e60921f8 widget: add shortcut to gtk_widget_set_child_visible
So we don't do unnecessary work when just setting priv->child_visible to
the same value again.
2018-04-30 18:21:31 +02:00
Timm Bäder
acf9425f4a widget: Don't mention gtk_widget_snapshot() in the docs
It's not public so people can't know about it.
2018-04-30 18:21:31 +02:00
4 changed files with 211 additions and 134 deletions

View File

@@ -625,21 +625,40 @@ render_offset_node (GskGLRenderer *self,
RenderOpBuilder *builder)
{
GskRenderNode *child = gsk_offset_node_get_child (node);
graphene_matrix_t prev_mv;
graphene_matrix_t transform, transformed_mv;
const guint child_type = gsk_render_node_get_node_type (child);
const float dx = gsk_offset_node_get_x_offset (node);
const float dy = gsk_offset_node_get_y_offset (node);
graphene_matrix_init_translate (&transform,
&GRAPHENE_POINT3D_INIT(
gsk_offset_node_get_x_offset (node),
gsk_offset_node_get_y_offset (node),
1.0
));
graphene_matrix_multiply (&transform, &builder->current_modelview, &transformed_mv);
prev_mv = ops_set_modelview (builder, &transformed_mv);
/* TODO: We do this only for selected node type we know handle
* builder->dx/dy correctly. That should be "all nodes" eventually.
*/
switch (child_type)
{
case GSK_TEXT_NODE:
case GSK_TEXTURE_NODE:
case GSK_COLOR_NODE:
{
ops_offset (builder, dx, dy);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_offset (builder, - dx, - dy);
}
break;
gsk_gl_renderer_add_render_ops (self, child, builder);
default:
{
graphene_matrix_t prev_mv;
graphene_matrix_t transform, transformed_mv;
ops_set_modelview (builder, &prev_mv);
graphene_matrix_init_translate (&transform,
&GRAPHENE_POINT3D_INIT(dx, dy, 1.0));
graphene_matrix_multiply (&transform, &builder->current_modelview, &transformed_mv);
prev_mv = ops_set_modelview (builder, &transformed_mv);
gsk_gl_renderer_add_render_ops (self, child, builder);
ops_set_modelview (builder, &prev_mv);
}
}
}
static inline void
@@ -2036,7 +2055,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
const float min_y = builder->dy + node->bounds.origin.y;
const float max_x = min_x + node->bounds.size.width;
const float max_y = min_y + node->bounds.size.height;
/* Default vertex data */
const GskQuadVertex vertex_data[GL_N_VERTICES] = {
{ { min_x, min_y }, { 0, 0 }, },
@@ -2053,6 +2071,21 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
if (node->bounds.size.width == 0.0f || node->bounds.size.height == 0.0f)
return;
/* Check whether the render node is entirely out of the current
* already transformed clip region */
{
graphene_rect_t real_node_bounds;
graphene_matrix_transform_bounds (&builder->current_modelview,
&node->bounds,
&real_node_bounds);
graphene_rect_offset (&real_node_bounds, builder->dx, builder->dy);
if (!graphene_rect_intersection (&builder->current_clip.bounds,
&real_node_bounds, NULL))
return;
}
#if DEBUG_OPS
if (gsk_render_node_get_node_type (node) != GSK_CONTAINER_NODE)
g_message ("Adding ops for node %s with type %u", node->name,

View File

@@ -34,6 +34,8 @@
#include "gtkintl.h"
#include "gtkprivate.h"
#define MAX_RECENT (7*3)
typedef struct {
GtkWidget *box;
GtkWidget *heading;
@@ -43,6 +45,14 @@ typedef struct {
gboolean empty;
} EmojiSection;
typedef struct {
GtkEmojiChooser *chooser;
GVariantIter iter;
GtkWidget *box; /* We need to keep this around so subsequent
emojis get added to the rigth section */
gint64 start;
} PopulateData;
struct _GtkEmojiChooser
{
GtkPopover parent_instance;
@@ -74,6 +84,9 @@ struct _GtkEmojiChooser
GVariant *data;
GSettings *settings;
guint populated : 1;
guint populate_idle_id;
};
struct _GtkEmojiChooserClass {
@@ -89,6 +102,13 @@ static int signals[LAST_SIGNAL];
G_DEFINE_TYPE (GtkEmojiChooser, gtk_emoji_chooser, GTK_TYPE_POPOVER)
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
GtkEmojiChooser *chooser);
static void
gtk_emoji_chooser_finalize (GObject *object)
{
@@ -124,14 +144,6 @@ scroll_to_section (GtkButton *button,
gtk_adjustment_animate_to_value (adj, alloc.y);
}
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
GtkEmojiChooser *chooser);
#define MAX_RECENT (7*3)
static void
populate_recent_section (GtkEmojiChooser *chooser)
@@ -332,111 +344,6 @@ popup_menu (GtkWidget *widget,
return TRUE;
}
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
GtkEmojiChooser *chooser)
{
GtkWidget *child;
GtkWidget *label;
PangoAttrList *attrs;
GVariant *codes;
char text[64];
char *p = text;
int i;
PangoLayout *layout;
PangoRectangle rect;
codes = g_variant_get_child_value (item, 0);
for (i = 0; i < g_variant_n_children (codes); i++)
{
gunichar code;
g_variant_get_child (codes, i, "u", &code);
if (code == 0)
code = modifier;
if (code != 0)
p += g_unichar_to_utf8 (code, p);
}
g_variant_unref (codes);
p[0] = 0;
label = gtk_label_new (text);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
layout = gtk_label_get_layout (GTK_LABEL (label));
pango_layout_get_extents (layout, &rect, NULL);
/* Check for fallback rendering that generates too wide items */
if (rect.width >= 2 * chooser->emoji_max_width)
{
gtk_widget_destroy (label);
return;
}
child = gtk_flow_box_child_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji");
g_object_set_data_full (G_OBJECT (child), "emoji-data",
g_variant_ref (item),
(GDestroyNotify)g_variant_unref);
if (modifier != 0)
g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier));
if (chooser)
g_signal_connect (child, "popup-menu", G_CALLBACK (popup_menu), chooser);
gtk_container_add (GTK_CONTAINER (child), label);
gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1);
}
static void
populate_emoji_chooser (GtkEmojiChooser *chooser)
{
GBytes *bytes = NULL;
GVariantIter iter;
GVariant *item;
GtkWidget *box;
bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
g_variant_iter_init (&iter, chooser->data);
box = chooser->people.box;
while ((item = g_variant_iter_next_value (&iter)))
{
const char *name;
g_variant_get_child (item, 1, "&s", &name);
if (strcmp (name, chooser->body.first) == 0)
box = chooser->body.box;
else if (strcmp (name, chooser->nature.first) == 0)
box = chooser->nature.box;
else if (strcmp (name, chooser->food.first) == 0)
box = chooser->food.box;
else if (strcmp (name, chooser->travel.first) == 0)
box = chooser->travel.box;
else if (strcmp (name, chooser->activities.first) == 0)
box = chooser->activities.box;
else if (strcmp (name, chooser->objects.first) == 0)
box = chooser->objects.box;
else if (strcmp (name, chooser->symbols.first) == 0)
box = chooser->symbols.box;
else if (strcmp (name, chooser->flags.first) == 0)
box = chooser->flags.box;
add_emoji (box, FALSE, item, 0, chooser);
g_variant_unref (item);
}
g_bytes_unref (bytes);
}
static void
adj_value_changed (GtkAdjustment *adj,
gpointer data)
@@ -604,10 +511,123 @@ setup_section (GtkEmojiChooser *chooser,
g_signal_connect (section->button, "clicked", G_CALLBACK (scroll_to_section), section);
}
static void
add_emoji (GtkWidget *box,
gboolean prepend,
GVariant *item,
gunichar modifier,
GtkEmojiChooser *chooser)
{
GtkWidget *child;
GtkWidget *label;
PangoAttrList *attrs;
GVariant *codes;
char text[64];
char *p = text;
guint i;
codes = g_variant_get_child_value (item, 0);
for (i = 0; i < g_variant_n_children (codes); i++)
{
gunichar code;
g_variant_get_child (codes, i, "u", &code);
if (code == 0)
code = modifier;
if (code != 0)
p += g_unichar_to_utf8 (code, p);
}
g_variant_unref (codes);
p[0] = 0;
label = gtk_label_new (text);
attrs = pango_attr_list_new ();
pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE));
gtk_label_set_attributes (GTK_LABEL (label), attrs);
pango_attr_list_unref (attrs);
if (chooser != NULL)
{
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (label));
PangoRectangle rect;
pango_layout_get_extents (layout, &rect, NULL);
/* Check for fallback rendering that generates too wide items */
if (rect.width >= 2 * chooser->emoji_max_width)
{
gtk_widget_destroy (label);
return;
}
}
child = gtk_flow_box_child_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (child), "emoji");
g_object_set_data_full (G_OBJECT (child), "emoji-data",
g_variant_ref (item),
(GDestroyNotify)g_variant_unref);
if (modifier != 0)
g_object_set_data (G_OBJECT (child), "modifier", GUINT_TO_POINTER (modifier));
if (chooser)
g_signal_connect (child, "popup-menu", G_CALLBACK (popup_menu), chooser);
gtk_container_add (GTK_CONTAINER (child), label);
gtk_flow_box_insert (GTK_FLOW_BOX (box), child, prepend ? 0 : -1);
}
static gboolean
populate_one_emoji (gpointer user_data)
{
const guint N = 4; /* Kinda-sorta sweetspot on my system... */
PopulateData *data = user_data;
const char *name;
guint i = 0;
static int iter = 0;
for (i = 0; i < N; i ++)
{
GVariant *item = g_variant_iter_next_value (&data->iter);
if (item == NULL)
{
data->chooser->populate_idle_id = 0;
return G_SOURCE_REMOVE;
}
g_variant_get_child (item, 1, "&s", &name);
if (strcmp (name, data->chooser->body.first) == 0)
data->box = data->chooser->body.box;
else if (strcmp (name, data->chooser->nature.first) == 0)
data->box = data->chooser->nature.box;
else if (strcmp (name, data->chooser->food.first) == 0)
data->box = data->chooser->food.box;
else if (strcmp (name, data->chooser->travel.first) == 0)
data->box = data->chooser->travel.box;
else if (strcmp (name, data->chooser->activities.first) == 0)
data->box = data->chooser->activities.box;
else if (strcmp (name, data->chooser->objects.first) == 0)
data->box = data->chooser->objects.box;
else if (strcmp (name, data->chooser->symbols.first) == 0)
data->box = data->chooser->symbols.box;
else if (strcmp (name, data->chooser->flags.first) == 0)
data->box = data->chooser->flags.box;
add_emoji (data->box, FALSE, item, 0, data->chooser);
g_variant_unref (item);
}
iter ++;
return G_SOURCE_CONTINUE;
}
static void
gtk_emoji_chooser_init (GtkEmojiChooser *chooser)
{
GtkAdjustment *adj;
GBytes *bytes;
chooser->settings = g_settings_new ("org.gtk.Settings.EmojiChooser");
@@ -665,7 +685,11 @@ gtk_emoji_chooser_init (GtkEmojiChooser *chooser)
setup_section (chooser, &chooser->symbols, "ATM sign", 0x2764);
setup_section (chooser, &chooser->flags, "chequered flag", 0x1f3f4);
populate_emoji_chooser (chooser);
bytes = g_resources_lookup_data ("/org/gtk/libgtk/emoji/emoji.data", 0, NULL);
chooser->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)"), bytes, TRUE));
g_bytes_unref (bytes);
/* Populate recent section now, do the heavy lifting on show() */
populate_recent_section (chooser);
/* We scroll to the top on show, so check the right button for the 1st time */
@@ -684,6 +708,23 @@ gtk_emoji_chooser_show (GtkWidget *widget)
gtk_adjustment_set_value (adj, 0);
gtk_entry_set_text (GTK_ENTRY (chooser->search_entry), "");
if (!chooser->populated)
{
PopulateData *data = g_malloc0 (sizeof (PopulateData));
data->chooser = chooser;
g_variant_iter_init (&data->iter, chooser->data);
data->box = chooser->people.box;
data->start = g_get_monotonic_time ();
chooser->populate_idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
populate_one_emoji,
data,
g_free);
chooser->populated = TRUE;
}
}
static void

View File

@@ -7618,7 +7618,7 @@ gtk_widget_get_parent_surface (GtkWidget *widget)
/**
* gtk_widget_set_child_visible:
* @widget: a #GtkWidget
* @is_visible: if %TRUE, @widget should be mapped along with its parent.
* @child_visible: if %TRUE, @widget should be mapped along with its parent.
*
* Sets whether @widget should be mapped along with its when its parent
* is mapped and @widget has been shown with gtk_widget_show().
@@ -7640,17 +7640,22 @@ gtk_widget_get_parent_surface (GtkWidget *widget)
**/
void
gtk_widget_set_child_visible (GtkWidget *widget,
gboolean is_visible)
gboolean child_visible)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (!_gtk_widget_is_toplevel (widget));
child_visible = !!child_visible;
if (priv->child_visible == child_visible)
return;
g_object_ref (widget);
gtk_widget_verify_invariants (widget);
if (is_visible)
if (child_visible)
priv->child_visible = TRUE;
else
{
@@ -13467,9 +13472,7 @@ gtk_widget_forall (GtkWidget *widget,
* the @snapshot the widget received.
*
* gtk_widget_snapshot_child() takes care of translating the origin of
* @snapshot, and deciding whether the child needs to be snapshot. It is a
* convenient and optimized way of getting the same effect as calling
* gtk_widget_snapshot() on the child directly.
* @snapshot, and deciding whether the child needs to be snapshot.
**/
void
gtk_widget_snapshot_child (GtkWidget *widget,

View File

@@ -589,7 +589,7 @@ GdkSurface * gtk_widget_get_parent_surface (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_widget_set_child_visible (GtkWidget *widget,
gboolean is_visible);
gboolean child_visible);
GDK_AVAILABLE_IN_ALL
gboolean gtk_widget_get_child_visible (GtkWidget *widget);