gdouble progress_pulse_fraction;
gdouble progress_pulse_current;
+ gchar *placeholder_text;
+
gfloat xalign;
gint ascent; /* font ascent in pango units */
PROP_TOOLTIP_MARKUP_PRIMARY,
PROP_TOOLTIP_MARKUP_SECONDARY,
PROP_IM_MODULE,
- PROP_EDITING_CANCELED
+ PROP_EDITING_CANCELED,
+ PROP_PLACEHOLDER_TEXT,
+ PROP_COMPLETION
};
static guint signals[LAST_SIGNAL] = { 0 };
gint *width,
gint *height);
static void get_frame_size (GtkEntry *entry,
+ gboolean relative_to_window,
gint *x,
gint *y,
gint *width,
static void gtk_entry_move_adjustments (GtkEntry *entry);
static void gtk_entry_ensure_pixbuf (GtkEntry *entry,
GtkEntryIconPosition icon_pos);
-
+static void gtk_entry_update_cached_style_values(GtkEntry *entry);
/* Completion */
static gint gtk_entry_completion_timeout (gpointer data);
static void end_change (GtkEntry *entry);
static void emit_changed (GtkEntry *entry);
-
static void buffer_inserted_text (GtkEntryBuffer *buffer,
guint position,
const gchar *chars,
static void buffer_disconnect_signals (GtkEntry *entry);
static GtkEntryBuffer *get_buffer (GtkEntry *entry);
-
G_DEFINE_TYPE_WITH_CODE (GtkEntry, gtk_entry, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE,
gtk_entry_editable_init)
GTK_PARAM_READWRITE));
/**
+ * GtkEntry:placeholder-text:
+ *
+ * The text that will be displayed in the #GtkEntry when it is empty and unfocused.
+ *
+ * Since: 3.2
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_PLACEHOLDER_TEXT,
+ g_param_spec_string ("placeholder-text",
+ P_("Placeholder text"),
+ P_("Show text in the entry when it's empty and unfocused"),
+ NULL,
+ GTK_PARAM_READWRITE));
+
+ /**
* GtkEntry:primary-icon-pixbuf:
*
* A pixbuf to use as the primary icon for the entry.
NULL,
GTK_PARAM_READWRITE));
+ /**
+ * GtkEntry:completion:
+ *
+ * The auxiliary completion object to use with the entry.
+ *
+ * Since: 3.2
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_COMPLETION,
+ g_param_spec_object ("completion",
+ P_("Completion"),
+ P_("The auxiliary completion object"),
+ GTK_TYPE_ENTRY_COMPLETION,
+ GTK_PARAM_READWRITE));
+
/**
* GtkEntry:icon-prelight:
*
gtk_entry_set_progress_pulse_step (entry, g_value_get_double (value));
break;
+ case PROP_PLACEHOLDER_TEXT:
+ gtk_entry_set_placeholder_text (entry, g_value_get_string (value));
+ break;
+
case PROP_PIXBUF_PRIMARY:
gtk_entry_set_icon_from_pixbuf (entry,
GTK_ENTRY_ICON_PRIMARY,
priv->editing_canceled = g_value_get_boolean (value);
break;
+ case PROP_COMPLETION:
+ gtk_entry_set_completion (entry, GTK_ENTRY_COMPLETION (g_value_get_object (value)));
+ break;
+
case PROP_SCROLL_OFFSET:
case PROP_CURSOR_POSITION:
default:
g_value_set_double (value, priv->progress_pulse_fraction);
break;
+ case PROP_PLACEHOLDER_TEXT:
+ g_value_set_string (value, gtk_entry_get_placeholder_text (entry));
+ break;
+
case PROP_PIXBUF_PRIMARY:
g_value_set_object (value,
gtk_entry_get_icon_pixbuf (entry,
priv->editing_canceled);
break;
+ case PROP_COMPLETION:
+ g_value_set_object (value, G_OBJECT (gtk_entry_get_completion (entry)));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
priv->editable = TRUE;
priv->visible = TRUE;
- priv->invisible_char = find_invisible_char (GTK_WIDGET (entry));
priv->dnd_position = -1;
priv->width_chars = -1;
priv->is_cell_renderer = FALSE;
context = gtk_widget_get_style_context (GTK_WIDGET (entry));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
+
+ gtk_entry_update_cached_style_values (entry);
}
static gint
{
GtkEntry *entry = GTK_ENTRY (object);
GtkEntryPrivate *priv = entry->priv;
+ GdkKeymap *keymap;
gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
gtk_entry_set_icon_tooltip_markup (entry, GTK_ENTRY_ICON_PRIMARY, NULL);
priv->buffer = NULL;
}
+ keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (object)));
+ g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
+ g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
G_OBJECT_CLASS (gtk_entry_parent_class)->dispose (object);
}
if (priv->recompute_idle)
g_source_remove (priv->recompute_idle);
+ g_free (priv->placeholder_text);
g_free (priv->im_module);
G_OBJECT_CLASS (gtk_entry_parent_class)->finalize (object);
return g_string_free (str, FALSE);
}
-
}
static void
get_text_area_size (entry, &attributes.x, &attributes.y, &attributes.width, &attributes.height);
- get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+ get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
attributes.x += frame_x;
attributes.y += frame_y;
GtkAllocation secondary;
EntryIconInfo *icon_info = NULL;
- get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+ get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
get_text_area_size (entry, &x, &y, &width, &height);
get_icon_allocations (entry, &primary, &secondary);
_gtk_entry_get_borders (entry, &xborder, &yborder);
if (gtk_widget_get_realized (widget))
- get_frame_size (entry, NULL, NULL, NULL, &frame_height);
+ get_frame_size (entry, TRUE, NULL, NULL, NULL, &frame_height);
else
frame_height = requisition.height;
static void
get_frame_size (GtkEntry *entry,
+ gboolean relative_to_window,
gint *x,
gint *y,
gint *width,
gtk_widget_get_allocation (widget, &allocation);
if (x)
- *x = allocation.x;
+ *x = relative_to_window ? allocation.x : 0;
if (y)
{
if (priv->is_cell_renderer)
- *y = allocation.y;
+ *y = 0;
else
- *y = allocation.y + (allocation.height - requisition.height) / 2;
+ *y = (allocation.height - requisition.height) / 2;
+
+ if (relative_to_window)
+ *y += allocation.y;
}
if (width)
GtkEntry *entry = GTK_ENTRY (widget);
GtkEntryPrivate *priv = entry->priv;
gint x = 0, y = 0, width, height;
- GtkAllocation allocation;
gint frame_x, frame_y;
cairo_save (cr);
- get_frame_size (GTK_ENTRY (widget), &frame_x, &frame_y, &width, &height);
- gtk_widget_get_allocation (widget, &allocation);
+ get_frame_size (GTK_ENTRY (widget), FALSE, &frame_x, &frame_y, &width, &height);
- cairo_translate (cr, frame_x - allocation.x, frame_y - allocation.y);
+ cairo_translate (cr, frame_x, frame_y);
/* Fix a problem with some themes which assume that entry->text_area's
* width equals widget->window's width
gtk_style_context_save (context);
gtk_style_context_set_state (context, state);
- if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
- {
- /* Draw entry_bg, shadow, progress and focus */
- gtk_entry_draw_frame (widget, context, cr);
+ /* Draw entry_bg, shadow, progress and focus */
+ gtk_entry_draw_frame (widget, context, cr);
- /* Draw text and cursor */
- cairo_save (cr);
+ /* Draw text and cursor */
+ cairo_save (cr);
- gtk_cairo_transform_to_window (cr, widget, priv->text_area);
+ gtk_cairo_transform_to_window (cr, widget, priv->text_area);
- if (priv->dnd_position != -1)
- gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
-
- gtk_entry_draw_text (GTK_ENTRY (widget), cr);
+ if (priv->dnd_position != -1)
+ gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_DND);
+
+ gtk_entry_draw_text (GTK_ENTRY (widget), cr);
- /* When no text is being displayed at all, don't show the cursor */
- if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK &&
- gtk_widget_has_focus (widget) &&
- priv->selection_bound == priv->current_pos && priv->cursor_visible)
- gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD);
+ /* When no text is being displayed at all, don't show the cursor */
+ if (gtk_entry_get_display_mode (entry) != DISPLAY_BLANK &&
+ gtk_widget_has_focus (widget) &&
+ priv->selection_bound == priv->current_pos && priv->cursor_visible)
+ gtk_entry_draw_cursor (GTK_ENTRY (widget), cr, CURSOR_STANDARD);
- cairo_restore (cr);
+ cairo_restore (cr);
- /* Draw icons */
- for (i = 0; i < MAX_ICONS; i++)
- {
- EntryIconInfo *icon_info = priv->icons[i];
+ /* Draw icons */
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ EntryIconInfo *icon_info = priv->icons[i];
- if (icon_info != NULL)
- {
- cairo_save (cr);
+ if (icon_info != NULL)
+ {
+ cairo_save (cr);
- gtk_cairo_transform_to_window (cr, widget, icon_info->window);
+ gtk_cairo_transform_to_window (cr, widget, icon_info->window);
- draw_icon (widget, cr, i);
+ draw_icon (widget, cr, i);
- cairo_restore (cr);
- }
+ cairo_restore (cr);
}
}
g_signal_connect (keymap, "direction-changed",
G_CALLBACK (keymap_direction_changed), entry);
- gtk_entry_reset_blink_time (entry);
- gtk_entry_check_cursor_blink (entry);
+ if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+ priv->placeholder_text != NULL)
+ {
+ gtk_entry_recompute (entry);
+ }
+ else
+ {
+ gtk_entry_reset_blink_time (entry);
+ gtk_entry_check_cursor_blink (entry);
+ }
return FALSE;
}
remove_capslock_feedback (entry);
}
- gtk_entry_check_cursor_blink (entry);
-
+ if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+ priv->placeholder_text != NULL)
+ {
+ gtk_entry_recompute (entry);
+ }
+ else
+ {
+ gtk_entry_check_cursor_blink (entry);
+ }
+
g_signal_handlers_disconnect_by_func (keymap, keymap_state_changed, entry);
g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, entry);
completion = gtk_entry_get_completion (entry);
if (completion)
_gtk_entry_completion_popdown (completion);
-
+
return FALSE;
}
static void
-gtk_entry_grab_focus (GtkWidget *widget)
+gtk_entry_grab_focus (GtkWidget *widget)
{
GtkEntry *entry = GTK_ENTRY (widget);
GtkEntryPrivate *priv = entry->priv;
gboolean select_on_focus;
-
+
GTK_WIDGET_CLASS (gtk_entry_parent_class)->grab_focus (widget);
if (priv->editable && !priv->in_click)
"gtk-entry-select-on-focus",
&select_on_focus,
NULL);
-
+
if (select_on_focus)
gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
}
}
-static void
+static void
gtk_entry_direction_changed (GtkWidget *widget,
- GtkTextDirection previous_dir)
+ GtkTextDirection previous_dir)
{
GtkEntry *entry = GTK_ENTRY (widget);
gtk_entry_recompute (entry);
-
+
GTK_WIDGET_CLASS (gtk_entry_parent_class)->direction_changed (widget, previous_dir);
}
GtkEntry *entry = GTK_ENTRY (widget);
GtkEntryPrivate *priv = entry->priv;
GdkCursor *cursor;
-
+
if (gtk_widget_get_realized (widget))
{
if (gtk_widget_is_sensitive (widget))
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget), GDK_XTERM);
- else
+ else
cursor = NULL;
gdk_window_set_cursor (priv->text_area, cursor);
/* Clear any selection */
gtk_editable_select_region (GTK_EDITABLE (entry), priv->current_pos, priv->current_pos);
}
-
- gtk_widget_queue_draw (widget);
+
+ gtk_entry_update_cached_style_values (entry);
}
static void
priv->icon_margin = border.left;
}
-static void
-gtk_entry_style_updated (GtkWidget *widget)
+static void
+gtk_entry_update_cached_style_values (GtkEntry *entry)
{
- GtkEntry *entry = GTK_ENTRY (widget);
GtkEntryPrivate *priv = entry->priv;
gint focus_width;
gboolean interior_focus;
- GTK_WIDGET_CLASS (gtk_entry_parent_class)->style_updated (widget);
-
- gtk_widget_style_get (widget,
+ gtk_widget_style_get (GTK_WIDGET (entry),
"focus-line-width", &focus_width,
"interior-focus", &interior_focus,
NULL);
-
priv->focus_width = focus_width;
priv->interior_focus = interior_focus;
if (!priv->invisible_char_set)
- priv->invisible_char = find_invisible_char (GTK_WIDGET (entry));
+ {
+ gunichar ch = find_invisible_char (GTK_WIDGET (entry));
+
+ if (priv->invisible_char != ch)
+ {
+ priv->invisible_char = ch;
+ g_object_notify (G_OBJECT (entry), "invisible-char");
+ }
+ }
+}
+
+static void
+gtk_entry_style_updated (GtkWidget *widget)
+{
+ GtkEntry *entry = GTK_ENTRY (widget);
+
+ GTK_WIDGET_CLASS (gtk_entry_parent_class)->style_updated (widget);
+
+ gtk_entry_update_cached_style_values (entry);
gtk_entry_recompute (entry);
}
}
+static void
+gtk_entry_get_placeholder_text_color (GtkEntry *entry,
+ PangoColor *color)
+{
+ GtkWidget *widget = GTK_WIDGET (entry);
+ GtkStyleContext *context;
+ GdkRGBA fg = { 0.5, 0.5, 0.5 };
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_lookup_color (context, "placeholder_text_color", &fg);
+
+ color->red = CLAMP (fg.red * 65535. + 0.5, 0, 65535);
+ color->green = CLAMP (fg.green * 65535. + 0.5, 0, 65535);
+ color->blue = CLAMP (fg.blue * 65535. + 0.5, 0, 65535);
+}
+
+static inline gboolean
+show_placeholder_text (GtkEntry *entry)
+{
+ GtkEntryPrivate *priv = entry->priv;
+
+ if (!gtk_widget_has_focus (GTK_WIDGET (entry)) &&
+ gtk_entry_buffer_get_bytes (get_buffer (entry)) == 0 &&
+ priv->placeholder_text != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
static PangoLayout *
gtk_entry_create_layout (GtkEntry *entry,
gboolean include_preedit)
GtkWidget *widget = GTK_WIDGET (entry);
PangoLayout *layout = gtk_widget_create_pango_layout (widget, NULL);
PangoAttrList *tmp_attrs = pango_attr_list_new ();
+ gboolean placeholder_layout = show_placeholder_text (entry);
gchar *preedit_string = NULL;
gint preedit_length = 0;
pango_layout_set_single_paragraph_mode (layout, TRUE);
- display = gtk_entry_get_display_text (entry, 0, -1);
+ display = placeholder_layout ? g_strdup (priv->placeholder_text) : gtk_entry_get_display_text (entry, 0, -1);
n_bytes = strlen (display);
- if (include_preedit)
+ if (!placeholder_layout && include_preedit)
{
gtk_im_context_get_preedit_string (priv->im_context,
&preedit_string, &preedit_attrs, NULL);
preedit_length = priv->preedit_length;
}
+ else if (placeholder_layout)
+ {
+ PangoColor color;
+ PangoAttribute *attr;
+
+ gtk_entry_get_placeholder_text_color (entry, &color);
+ attr = pango_attr_foreground_new (color.red, color.green, color.blue);
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+ pango_attr_list_insert (tmp_attrs, attr);
+ }
if (preedit_length)
{
GtkStateFlags state = 0;
GdkRGBA text_color, bar_text_color;
GtkStyleContext *context;
- gint pos_x, pos_y;
gint width, height;
gint progress_x, progress_y, progress_width, progress_height;
gint clip_width, clip_height;
&progress_x, &progress_y,
&progress_width, &progress_height);
+ cairo_save (cr);
+
clip_width = gdk_window_get_width (priv->text_area);
clip_height = gdk_window_get_height (priv->text_area);
cairo_rectangle (cr, 0, 0, clip_width, clip_height);
}
else
{
+ int frame_x, frame_y, area_x, area_y;
+
width = gdk_window_get_width (priv->text_area);
height = gdk_window_get_height (priv->text_area);
cairo_save (cr);
- cairo_rectangle (cr, 0, 0, width, height);
- cairo_clip (cr);
- cairo_save (cr);
-
cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
cairo_rectangle (cr, 0, 0, width, height);
- gdk_window_get_position (priv->text_area, &pos_x, &pos_y);
- progress_x -= pos_x;
- progress_y -= pos_y;
+ /* progres area is frame-relative, we need it text-area window
+ * relative */
+ get_frame_size (entry, TRUE, &frame_x, &frame_y, NULL, NULL);
+ gdk_window_get_position (priv->text_area, &area_x, &area_y);
+ progress_x += frame_x - area_x;
+ progress_y += frame_y - area_y;
cairo_rectangle (cr, progress_x, progress_y,
progress_width, progress_height);
draw_text_with_color (entry, cr, &text_color);
cairo_restore (cr);
+ cairo_save (cr);
+
cairo_rectangle (cr, progress_x, progress_y,
progress_width, progress_height);
cairo_clip (cr);
cairo_restore (cr);
}
+
+ cairo_restore (cr);
}
static void
* @x: the x coordinate of the position to find
* @y: the y coordinate of the position to find
*
- * Finds the icon at the given position and return its index.
+ * Finds the icon at the given position and return its index. The
+ * position's coordinates are relative to the @entry's top left corner.
* If @x, @y doesn't lie inside an icon, -1 is returned.
* This function is intended for use in a #GtkWidget::query-tooltip
* signal handler.
g_return_val_if_fail (GTK_IS_ENTRY (entry), -1);
- get_frame_size (entry, &frame_x, &frame_y, NULL, NULL);
+ get_frame_size (entry, FALSE, &frame_x, &frame_y, NULL, NULL);
x -= frame_x;
y -= frame_y;
gtk_menu_shell_append (GTK_MENU_SHELL (info_entry_priv->popup_menu), menuitem);
menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
+ gtk_widget_set_sensitive (menuitem, gtk_entry_buffer_get_length (info_entry_priv->buffer) > 0);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_entry_select_all), entry);
gtk_widget_show (menuitem);
old->priv->completion_timeout = 0;
}
+ if (old->priv->check_completion_idle)
+ {
+ g_source_destroy (old->priv->check_completion_idle);
+ old->priv->check_completion_idle = NULL;
+ }
+
if (gtk_widget_get_mapped (old->priv->popup_window))
_gtk_entry_completion_popdown (old);
connect_completion_signals (entry, completion);
completion->priv->entry = GTK_WIDGET (entry);
g_object_set_data (G_OBJECT (entry), I_(GTK_ENTRY_COMPLETION_KEY), completion);
+
+ g_object_notify (G_OBJECT (entry), "completion");
}
/**
gtk_widget_queue_draw (GTK_WIDGET (entry));
}
+/**
+ * gtk_entry_set_placeholder_text:
+ * @entry: a #GtkEntry
+ * @text: a string to be displayed when @entry is empty an unfocused, or %NULL
+ *
+ * Sets text to be displayed in @entry when
+ * it is empty and unfocused. This can be used to give a visual hint
+ * of the expected contents of the #GtkEntry.
+ *
+ * Since: 3.2
+ **/
+void
+gtk_entry_set_placeholder_text (GtkEntry *entry,
+ const gchar *text)
+{
+ GtkEntryPrivate *priv;
+
+ g_return_if_fail (GTK_IS_ENTRY (entry));
+
+ priv = entry->priv;
+
+ if (g_strcmp0 (priv->placeholder_text, text) == 0)
+ return;
+
+ g_free (priv->placeholder_text);
+ priv->placeholder_text = g_strdup (text);
+
+ gtk_entry_recompute (entry);
+
+ g_object_notify (G_OBJECT (entry), "placeholder-text");
+}
+
+/**
+ * gtk_entry_get_placeholder_text:
+ * @entry: a #GtkEntry
+ *
+ * Retrieves the text that will be displayed when @entry is empty and unfocused
+ *
+ * Returns: a pointer to the placeholder text as a string. This string points to internally allocated
+ * storage in the widget and must not be freed, modified or stored.
+ *
+ * Since: 3.2
+ **/
+G_CONST_RETURN gchar *
+gtk_entry_get_placeholder_text (GtkEntry *entry)
+{
+ GtkEntryPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
+
+ priv = entry->priv;
+
+ return priv->placeholder_text;
+}
+
/* Caps Lock warning for password entries */
static void