Merge branch 'wip/matthiasc/context-menu' into 'master'

context menu api

See merge request GNOME/gtk!539
This commit is contained in:
Matthias Clasen 2019-06-13 12:17:54 +00:00
commit 121011b05b
22 changed files with 4408 additions and 1245 deletions

View File

@ -8,8 +8,8 @@
#include <gtk/gtk.h>
static GtkWidget *window = NULL;
static GtkWidget *menu = NULL;
static GtkWidget *notebook = NULL;
static GSimpleActionGroup *actions = NULL;
static guint search_progress_id = 0;
static guint finish_search_id = 0;
@ -83,69 +83,42 @@ stop_search (GtkButton *button,
}
static void
clear_entry (GtkEntry *entry)
clear_entry (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_editable_set_text (GTK_EDITABLE (entry), "");
GtkEditable *editable = user_data;
gtk_editable_set_text (editable, "");
}
static void
search_by_name (GtkWidget *item,
GtkEntry *entry)
set_search_by (GSimpleAction *action,
GVariant *value,
gpointer user_data)
{
gtk_entry_set_icon_tooltip_text (entry,
GTK_ENTRY_ICON_PRIMARY,
"Search by name\n"
"Click here to change the search type");
gtk_entry_set_placeholder_text (entry, "name");
}
GtkEntry *entry = user_data;
const char *term;
static void
search_by_description (GtkWidget *item,
GtkEntry *entry)
{
term = g_variant_get_string (value, NULL);
gtk_entry_set_icon_tooltip_text (entry,
GTK_ENTRY_ICON_PRIMARY,
"Search by description\n"
"Click here to change the search type");
gtk_entry_set_placeholder_text (entry, "description");
}
g_simple_action_set_state (action, value);
static void
search_by_file (GtkWidget *item,
GtkEntry *entry)
{
gtk_entry_set_icon_tooltip_text (entry,
GTK_ENTRY_ICON_PRIMARY,
"Search by file name\n"
"Click here to change the search type");
gtk_entry_set_placeholder_text (entry, "file name");
}
GtkWidget *
create_search_menu (GtkWidget *entry)
{
GtkWidget *menu;
GtkWidget *item;
menu = gtk_menu_new ();
item = gtk_menu_item_new_with_mnemonic ("Search by _name");
g_signal_connect (item, "activate",
G_CALLBACK (search_by_name), entry);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
item = gtk_menu_item_new_with_mnemonic ("Search by _description");
g_signal_connect (item, "activate",
G_CALLBACK (search_by_description), entry);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
item = gtk_menu_item_new_with_mnemonic ("Search by _file name");
g_signal_connect (item, "activate",
G_CALLBACK (search_by_file), entry);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
return menu;
if (g_str_equal (term, "name"))
{
gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_PRIMARY, "Search by name");
gtk_entry_set_placeholder_text (entry, "name");
}
else if (g_str_equal (term, "description"))
{
gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_PRIMARY, "Search by description");
gtk_entry_set_placeholder_text (entry, "description");
}
else if (g_str_equal (term, "filename"))
{
gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_PRIMARY, "Search by file name");
gtk_entry_set_placeholder_text (entry, "file name");
}
}
static void
@ -154,7 +127,28 @@ icon_press_cb (GtkEntry *entry,
gpointer data)
{
if (position == GTK_ENTRY_ICON_PRIMARY)
gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
{
GAction *action;
GVariant *state;
GVariant *new_state;
const char *s;
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "search-by");
state = g_action_get_state (action);
s = g_variant_get_string (state, NULL);
if (g_str_equal (s, "name"))
new_state = g_variant_new_string ("description");
else if (g_str_equal (s, "description"))
new_state = g_variant_new_string ("filename");
else if (g_str_equal (s, "filename"))
new_state = g_variant_new_string ("name");
else
g_assert_not_reached ();
g_action_change_state (action, new_state);
g_variant_unref (state);
}
}
static void
@ -165,7 +159,6 @@ activate_cb (GtkEntry *entry,
return;
start_search (button, entry);
}
static void
@ -187,32 +180,62 @@ search_entry_destroyed (GtkWidget *widget)
}
static void
entry_populate_popup (GtkEntry *entry,
GtkMenu *menu,
gpointer user_data)
text_changed (GObject *object,
GParamSpec *pspec,
gpointer data)
{
GtkWidget *item;
GtkWidget *search_menu;
GtkEntry *entry = GTK_ENTRY (object);
GActionMap *actions = data;
GAction *action;
gboolean has_text;
has_text = gtk_entry_get_text_length (entry) > 0;
item = gtk_separator_menu_item_new ();
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
action = g_action_map_lookup_action (actions, "clear");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), has_text);
}
item = gtk_menu_item_new_with_mnemonic ("C_lear");
gtk_widget_show (item);
g_signal_connect_swapped (item, "activate",
G_CALLBACK (clear_entry), entry);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_set_sensitive (item, has_text);
static GMenuModel *
create_search_menu_model (void)
{
GMenu *menu = g_menu_new ();
g_menu_append (menu, _("Name"), "search.search-by::name");
g_menu_append (menu, _("Description"), "search.search-by::description");
g_menu_append (menu, _("File Name"), "search.search-by::filename");
search_menu = create_search_menu (GTK_WIDGET (entry));
item = gtk_menu_item_new_with_label ("Search by");
gtk_widget_show (item);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), search_menu);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
return G_MENU_MODEL (menu);
}
static void
entry_add_to_context_menu (GtkEntry *entry)
{
GMenu *menu;
GActionEntry entries[] = {
{ "clear", clear_entry, NULL, NULL, NULL },
{ "search-by", NULL, "s", "'name'", set_search_by }
};
GMenuModel *submenu;
GMenuItem *item;
actions = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS(entries), entry);
gtk_widget_insert_action_group (GTK_WIDGET (entry), "search", G_ACTION_GROUP (actions));
menu = g_menu_new ();
item = g_menu_item_new (_("C_lear"), "search.clear");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-clear-symbolic");
g_menu_append_item (G_MENU (menu), item);
g_object_unref (item);
submenu = create_search_menu_model ();
g_menu_append_submenu (menu, _("Search By"), submenu);
g_object_unref (submenu);
gtk_entry_set_extra_menu (entry, G_MENU_MODEL (menu));
g_object_unref (menu);
g_signal_connect (entry, "notify::text", G_CALLBACK (text_changed), actions);
}
GtkWidget *
@ -271,29 +294,25 @@ do_search_entry (GtkWidget *do_widget)
gtk_widget_show (cancel_button);
/* Set up the search icon */
search_by_name (NULL, GTK_ENTRY (entry));
GVariant *value = g_variant_ref_sink (g_variant_new_string ("name"));
set_search_by (NULL, value, entry);
g_variant_unref (value);
/* Set up the clear icon */
g_signal_connect (entry, "icon-press",
G_CALLBACK (icon_press_cb), NULL);
g_signal_connect (entry, "activate",
G_CALLBACK (activate_cb), NULL);
gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, TRUE);
gtk_entry_set_icon_sensitive (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, TRUE);
/* Create the menu */
menu = create_search_menu (entry);
gtk_menu_attach_to_widget (GTK_MENU (menu), entry, NULL);
g_signal_connect (entry, "icon-press", G_CALLBACK (icon_press_cb), NULL);
g_signal_connect (entry, "activate", G_CALLBACK (activate_cb), NULL);
/* add accessible alternatives for icon functionality */
g_object_set (entry, "populate-all", TRUE, NULL);
g_signal_connect (entry, "populate-popup",
G_CALLBACK (entry_populate_popup), NULL);
entry_add_to_context_menu (GTK_ENTRY (entry));
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
{
gtk_widget_destroy (menu);
g_clear_object (&actions);
gtk_widget_destroy (window);
}

View File

@ -1303,96 +1303,113 @@ page_combo_separator_func (GtkTreeModel *model,
}
static void
activate_item (GtkWidget *item, GtkTextView *tv)
toggle_format (GSimpleAction *action,
GVariant *value,
gpointer user_data)
{
const gchar *tag;
GtkTextView *text_view = user_data;
GtkTextIter start, end;
gboolean active;
const char *name;
g_object_get (item, "active", &active, NULL);
tag = (const gchar *)g_object_get_data (G_OBJECT (item), "tag");
gtk_text_buffer_get_selection_bounds (gtk_text_view_get_buffer (tv), &start, &end);
if (active)
gtk_text_buffer_apply_tag_by_name (gtk_text_view_get_buffer (tv), tag, &start, &end);
name = g_action_get_name (G_ACTION (action));
g_simple_action_set_state (action, value);
gtk_text_buffer_get_selection_bounds (gtk_text_view_get_buffer (text_view), &start, &end);
if (g_variant_get_boolean (value))
gtk_text_buffer_apply_tag_by_name (gtk_text_view_get_buffer (text_view), name, &start, &end);
else
gtk_text_buffer_remove_tag_by_name (gtk_text_view_get_buffer (tv), tag, &start, &end);
gtk_text_buffer_remove_tag_by_name (gtk_text_view_get_buffer (text_view), name, &start, &end);
}
static void
add_item (GtkTextView *tv,
GtkWidget *popup,
const gchar *text,
const gchar *tag,
gboolean set)
{
GtkWidget *item, *label;
if (GTK_IS_MENU (popup))
{
item = gtk_check_menu_item_new ();
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), set);
g_signal_connect (item, "toggled", G_CALLBACK (activate_item), tv);
}
else
{
item = gtk_check_button_new ();
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item), set);
gtk_widget_set_focus_on_click (item, FALSE);
g_signal_connect (item, "clicked", G_CALLBACK (activate_item), tv);
}
label = gtk_label_new ("");
gtk_label_set_xalign (GTK_LABEL (label), 0);
gtk_label_set_markup (GTK_LABEL (label), text);
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (item), label);
g_object_set_data (G_OBJECT (item), "tag", (gpointer)tag);
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (popup), item);
}
static GActionGroup *actions;
static void
populate_popup (GtkTextView *tv,
GtkWidget *popup)
text_changed (GtkTextBuffer *buffer)
{
gboolean has_selection;
GtkWidget *item;
GtkTextIter start, end, iter;
GAction *bold;
GAction *italic;
GAction *underline;
GtkTextIter iter;
GtkTextTagTable *tags;
GtkTextTag *bold, *italic, *underline;
GtkTextTag *bold_tag, *italic_tag, *underline_tag;
gboolean all_bold, all_italic, all_underline;
GtkTextIter start, end;
gboolean has_selection;
has_selection = gtk_text_buffer_get_selection_bounds (gtk_text_view_get_buffer (tv), &start, &end);
bold = g_action_map_lookup_action (G_ACTION_MAP (actions), "bold");
italic = g_action_map_lookup_action (G_ACTION_MAP (actions), "italic");
underline = g_action_map_lookup_action (G_ACTION_MAP (actions), "underline");
has_selection = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
g_simple_action_set_enabled (G_SIMPLE_ACTION (bold), has_selection);
g_simple_action_set_enabled (G_SIMPLE_ACTION (italic), has_selection);
g_simple_action_set_enabled (G_SIMPLE_ACTION (underline), has_selection);
if (!has_selection)
return;
tags = gtk_text_buffer_get_tag_table (gtk_text_view_get_buffer (tv));
bold = gtk_text_tag_table_lookup (tags, "bold");
italic = gtk_text_tag_table_lookup (tags, "italic");
underline = gtk_text_tag_table_lookup (tags, "underline");
tags = gtk_text_buffer_get_tag_table (buffer);
bold_tag = gtk_text_tag_table_lookup (tags, "bold");
italic_tag = gtk_text_tag_table_lookup (tags, "italic");
underline_tag = gtk_text_tag_table_lookup (tags, "underline");
all_bold = TRUE;
all_italic = TRUE;
all_underline = TRUE;
gtk_text_iter_assign (&iter, &start);
while (!gtk_text_iter_equal (&iter, &end))
{
all_bold &= gtk_text_iter_has_tag (&iter, bold);
all_italic &= gtk_text_iter_has_tag (&iter, italic);
all_underline &= gtk_text_iter_has_tag (&iter, underline);
all_bold &= gtk_text_iter_has_tag (&iter, bold_tag);
all_italic &= gtk_text_iter_has_tag (&iter, italic_tag);
all_underline &= gtk_text_iter_has_tag (&iter, underline_tag);
gtk_text_iter_forward_char (&iter);
}
if (GTK_IS_MENU (popup))
{
item = gtk_separator_menu_item_new ();
gtk_widget_show (item);
gtk_container_add (GTK_CONTAINER (popup), item);
}
g_simple_action_set_state (G_SIMPLE_ACTION (bold), g_variant_new_boolean (all_bold));
g_simple_action_set_state (G_SIMPLE_ACTION (italic), g_variant_new_boolean (all_italic));
g_simple_action_set_state (G_SIMPLE_ACTION (underline), g_variant_new_boolean (all_underline));
}
add_item (tv, popup, "<b>Bold</b>", "bold", all_bold);
add_item (tv, popup, "<i>Italics</i>", "italic", all_italic);
add_item (tv, popup, "<u>Underline</u>", "underline", all_underline);
static void
text_view_add_to_context_menu (GtkTextView *text_view)
{
GMenu *menu;
GActionEntry entries[] = {
{ "bold", NULL, NULL, "false", toggle_format },
{ "italic", NULL, NULL, "false", toggle_format },
{ "underline", NULL, NULL, "false", toggle_format },
};
GMenuItem *item;
GAction *action;
actions = G_ACTION_GROUP (g_simple_action_group_new ());
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), text_view);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "bold");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "italic");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "underline");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
gtk_widget_insert_action_group (GTK_WIDGET (text_view), "format", G_ACTION_GROUP (actions));
menu = g_menu_new ();
item = g_menu_item_new (_("Bold"), "format.bold");
g_menu_item_set_attribute (item, "touch-icon", "s", "format-text-bold-symbolic");
g_menu_append_item (G_MENU (menu), item);
g_object_unref (item);
item = g_menu_item_new (_("Italics"), "format.italic");
g_menu_item_set_attribute (item, "touch-icon", "s", "format-text-italic-symbolic");
g_menu_append_item (G_MENU (menu), item);
g_object_unref (item);
item = g_menu_item_new (_("Underline"), "format.underline");
g_menu_item_set_attribute (item, "touch-icon", "s", "format-text-underline-symbolic");
g_menu_append_item (G_MENU (menu), item);
gtk_text_view_set_extra_menu (text_view, G_MENU_MODEL (menu));
g_signal_connect (gtk_text_view_get_buffer (text_view), "changed", G_CALLBACK (text_changed), NULL);
g_signal_connect (gtk_text_view_get_buffer (text_view), "mark-set", G_CALLBACK (text_changed), NULL);
}
static void
@ -1877,8 +1894,7 @@ activate (GApplication *app)
g_object_set_data (G_OBJECT (widget), "osd", widget2);
widget = (GtkWidget *)gtk_builder_get_object (builder, "textview1");
g_signal_connect (widget, "populate-popup",
G_CALLBACK (populate_popup), NULL);
text_view_add_to_context_menu (GTK_TEXT_VIEW (widget));
widget = (GtkWidget *)gtk_builder_get_object (builder, "open_popover");
widget2 = (GtkWidget *)gtk_builder_get_object (builder, "open_popover_entry");

View File

@ -1207,7 +1207,6 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<property name="wrap-mode">2</property>
<property name="left-margin">10</property>
<property name="right-margin">10</property>
<property name="populate-all">1</property>
</object>
</child>
</object>

View File

@ -893,6 +893,8 @@ gtk_text_get_attributes
gtk_text_set_tabs
gtk_text_get_tabs
gtk_text_grab_focus_without_selecting
gtk_text_set_extra_menu
gtk_text_get_extra_menu
<SUBSECTION Private>
gtk_text_get_type
</SECTION>
@ -963,6 +965,8 @@ GtkInputHints
gtk_entry_set_input_hints
gtk_entry_get_input_hints
gtk_entry_grab_focus_without_selecting
gtk_entry_set_extra_menu
gtk_entry_get_extra_menu
<SUBSECTION Standard>
GTK_ENTRY
@ -985,6 +989,8 @@ GtkPasswordEntry
gtk_password_entry_new
gtk_password_entry_set_show_peek_icon
gtk_password_entry_get_show_peek_icon
gtk_password_entry_set_extra_menu
gtk_password_entry_get_extra_menu
<SUBSECTION Private>
gtk_password_entry_get_type
</SECTION>
@ -1713,6 +1719,9 @@ gtk_label_set_single_line_mode
gtk_label_get_current_uri
gtk_label_set_track_visited_links
gtk_label_get_track_visited_links
gtk_label_set_extra_menu
gtk_label_get_extra_menu
<SUBSECTION Standard>
GTK_LABEL
GTK_IS_LABEL
@ -3089,6 +3098,9 @@ gtk_text_view_set_input_hints
gtk_text_view_get_input_hints
gtk_text_view_set_monospace
gtk_text_view_get_monospace
gtk_text_view_set_extra_menu
gtk_text_view_get_extra_menu
GTK_TEXT_VIEW_PRIORITY_VALIDATE
<SUBSECTION Standard>
GTK_TEXT_VIEW

View File

@ -205,7 +205,6 @@ struct _GtkCellRendererTextPrivate
guint align_set : 1;
gulong focus_out_id;
gulong populate_popup_id;
gulong entry_menu_popdown_timeout;
};
@ -1778,12 +1777,6 @@ gtk_cell_renderer_text_editing_done (GtkCellEditable *entry,
priv->focus_out_id = 0;
}
if (priv->populate_popup_id > 0)
{
g_signal_handler_disconnect (entry, priv->populate_popup_id);
priv->populate_popup_id = 0;
}
if (priv->entry_menu_popdown_timeout)
{
g_source_remove (priv->entry_menu_popdown_timeout);
@ -1804,70 +1797,14 @@ gtk_cell_renderer_text_editing_done (GtkCellEditable *entry,
g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text);
}
static gboolean
popdown_timeout (gpointer data)
{
GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data);
GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext);
priv->entry_menu_popdown_timeout = 0;
if (!gtk_widget_has_focus (priv->entry))
gtk_cell_renderer_text_editing_done (GTK_CELL_EDITABLE (priv->entry), data);
return FALSE;
}
static void
gtk_cell_renderer_text_popup_unmap (GtkMenu *menu,
gpointer data)
{
GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data);
GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext);
priv->in_entry_menu = FALSE;
if (priv->entry_menu_popdown_timeout)
return;
priv->entry_menu_popdown_timeout = g_timeout_add (500, popdown_timeout, data);
g_source_set_name_by_id (priv->entry_menu_popdown_timeout, "[gtk] popdown_timeout");
}
static void
gtk_cell_renderer_text_populate_popup (GtkEntry *entry,
GtkMenu *menu,
gpointer data)
{
GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data);
GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext);
if (priv->entry_menu_popdown_timeout)
{
g_source_remove (priv->entry_menu_popdown_timeout);
priv->entry_menu_popdown_timeout = 0;
}
priv->in_entry_menu = TRUE;
g_signal_connect (menu, "unmap",
G_CALLBACK (gtk_cell_renderer_text_popup_unmap), data);
}
static void
gtk_cell_renderer_text_focus_changed (GtkWidget *entry,
GParamSpec *pspec,
gpointer data)
{
GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (data);
GtkCellRendererTextPrivate *priv = gtk_cell_renderer_text_get_instance_private (celltext);
if (gtk_widget_has_focus (entry))
return;
if (priv->in_entry_menu)
return;
g_object_set (entry,
"editing-canceled", TRUE,
NULL);
@ -1921,11 +1858,6 @@ gtk_cell_renderer_text_start_editing (GtkCellRenderer *cell,
priv->focus_out_id = g_signal_connect_after (priv->entry, "notify::has-focus",
G_CALLBACK (gtk_cell_renderer_text_focus_changed),
celltext);
priv->populate_popup_id =
g_signal_connect (priv->entry, "populate-popup",
G_CALLBACK (gtk_cell_renderer_text_populate_popup),
celltext);
gtk_widget_show (priv->entry);
return GTK_CELL_EDITABLE (priv->entry);

View File

@ -90,6 +90,8 @@ struct _GtkColorChooserWidgetPrivate
gboolean has_default_palette;
GSettings *settings;
GActionMap *context_actions;
};
enum
@ -134,31 +136,6 @@ select_swatch (GtkColorChooserWidget *cc,
g_object_notify (G_OBJECT (cc), "rgba");
}
static void
swatch_activate (GtkColorSwatch *swatch,
GtkColorChooserWidget *cc)
{
GdkRGBA color;
gtk_color_swatch_get_rgba (swatch, &color);
_gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
}
static void
swatch_customize (GtkColorSwatch *swatch,
GtkColorChooserWidget *cc)
{
GtkColorChooserWidgetPrivate *priv = gtk_color_chooser_widget_get_instance_private (cc);
GdkRGBA color;
gtk_color_swatch_get_rgba (swatch, &color);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (priv->editor), &color);
gtk_widget_hide (priv->palette);
gtk_widget_show (priv->editor);
g_object_notify (G_OBJECT (cc), "show-editor");
}
static void
swatch_selected (GtkColorSwatch *swatch,
GtkStateFlags previous,
@ -176,31 +153,14 @@ static void
connect_swatch_signals (GtkWidget *p,
gpointer data)
{
g_signal_connect (p, "activate", G_CALLBACK (swatch_activate), data);
g_signal_connect (p, "customize", G_CALLBACK (swatch_customize), data);
g_signal_connect (p, "state-flags-changed", G_CALLBACK (swatch_selected), data);
}
static void
button_activate (GtkColorSwatch *swatch,
GtkColorChooserWidget *cc)
{
GtkColorChooserWidgetPrivate *priv = gtk_color_chooser_widget_get_instance_private (cc);
/* somewhat random, makes the hairline nicely visible */
GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 };
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (priv->editor), &color);
gtk_widget_hide (priv->palette);
gtk_widget_show (priv->editor);
g_object_notify (G_OBJECT (cc), "show-editor");
}
static void
connect_button_signals (GtkWidget *p,
gpointer data)
{
g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
// g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
}
static void
@ -532,6 +492,57 @@ add_default_palette (GtkColorChooserWidget *cc)
priv->has_default_palette = TRUE;
}
static void
customize_color (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkColorChooserWidget *cc = user_data;
GtkColorChooserWidgetPrivate *priv = gtk_color_chooser_widget_get_instance_private (cc);
GdkRGBA color;
g_variant_get (parameter, "(dddd)", &color.red, &color.green, &color.blue, &color.alpha);
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (priv->editor), &color);
gtk_widget_hide (priv->palette);
gtk_widget_show (priv->editor);
g_object_notify (G_OBJECT (cc), "show-editor");
}
static void
select_color (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkColorChooserWidget *cc = user_data;
GdkRGBA color;
g_variant_get (parameter, "(dddd)", &color.red, &color.green, &color.blue, &color.alpha);
_gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
}
static void
gtk_color_chooser_widget_add_context_actions (GtkColorChooserWidget *cc)
{
GtkColorChooserWidgetPrivate *priv = gtk_color_chooser_widget_get_instance_private (cc);
GActionEntry entries[] = {
{ "select", select_color, "(dddd)", NULL, NULL },
{ "customize", customize_color, "(dddd)", NULL, NULL },
};
GSimpleActionGroup *actions = g_simple_action_group_new ();
priv->context_actions = G_ACTION_MAP (actions);
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), cc);
gtk_widget_insert_action_group (GTK_WIDGET (cc), "color", G_ACTION_GROUP (actions));
}
static void
gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
{
@ -623,6 +634,8 @@ gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
gtk_size_group_add_widget (priv->size_group, priv->palette);
gtk_size_group_add_widget (priv->size_group, box);
gtk_color_chooser_widget_add_context_actions (cc);
}
/* GObject implementation {{{1 */

View File

@ -33,7 +33,7 @@
#include "gtkintl.h"
#include "gtkmain.h"
#include "gtkmodelbutton.h"
#include "gtkpopover.h"
#include "gtkpopovermenu.h"
#include "gtkprivate.h"
#include "gtksnapshot.h"
#include "gtkstylecontextprivate.h"
@ -76,16 +76,6 @@ enum
PROP_HAS_MENU
};
enum
{
ACTIVATE,
CUSTOMIZE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE_WITH_PRIVATE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
@ -231,6 +221,32 @@ swatch_drag_data_received (GtkWidget *widget,
gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
}
static void
activate_color (GtkColorSwatch *swatch)
{
GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
gtk_widget_activate_action (GTK_WIDGET (swatch),
"color.select",
g_variant_new ("(dddd)",
priv->color.red,
priv->color.green,
priv->color.blue,
priv->color.alpha));
}
static void
customize_color (GtkColorSwatch *swatch)
{
GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
gtk_widget_activate_action (GTK_WIDGET (swatch),
"color.customize",
g_variant_new ("(dddd)",
priv->color.red,
priv->color.green,
priv->color.blue,
priv->color.alpha));
}
static gboolean
key_controller_key_pressed (GtkEventControllerKey *controller,
guint keyval,
@ -252,40 +268,51 @@ key_controller_key_pressed (GtkEventControllerKey *controller,
(gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
else
g_signal_emit (swatch, signals[ACTIVATE], 0);
customize_color (swatch);
return TRUE;
}
return FALSE;
}
static void
emit_customize (GtkColorSwatch *swatch)
static GMenuModel *
gtk_color_swatch_get_menu_model (GtkColorSwatch *swatch)
{
g_signal_emit (swatch, signals[CUSTOMIZE], 0);
GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
GMenu *menu, *section;
GMenuItem *item;
menu = g_menu_new ();
section = g_menu_new ();
item = g_menu_item_new (_("Customize"), NULL);
g_menu_item_set_action_and_target_value (item, "color.customize",
g_variant_new ("(dddd)",
priv->color.red,
priv->color.green,
priv->color.blue,
priv->color.alpha));
g_menu_append_item (section, item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (item);
g_object_unref (section);
return G_MENU_MODEL (menu);
}
static void
do_popup (GtkColorSwatch *swatch)
{
GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
GMenuModel *model;
if (priv->popover == NULL)
{
GtkWidget *box;
GtkWidget *item;
g_clear_pointer (&priv->popover, gtk_widget_unparent);
priv->popover = gtk_popover_new (GTK_WIDGET (swatch));
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (priv->popover), box);
g_object_set (box, "margin", 10, NULL);
item = g_object_new (GTK_TYPE_MODEL_BUTTON,
"text", _("C_ustomize"),
NULL);
g_signal_connect_swapped (item, "clicked",
G_CALLBACK (emit_customize), swatch);
gtk_container_add (GTK_CONTAINER (box), item);
}
model = gtk_color_swatch_get_menu_model (swatch);
priv->popover = gtk_popover_menu_new_from_model (GTK_WIDGET (swatch), model);
g_object_unref (model);
gtk_popover_popup (GTK_POPOVER (priv->popover));
}
@ -300,7 +327,7 @@ swatch_primary_action (GtkColorSwatch *swatch)
flags = gtk_widget_get_state_flags (widget);
if (!priv->has_color)
{
g_signal_emit (swatch, signals[ACTIVATE], 0);
customize_color (swatch);
return TRUE;
}
else if (priv->selectable &&
@ -340,7 +367,7 @@ tap_action (GtkGestureClick *gesture,
if (n_press == 1)
swatch_primary_action (swatch);
else if (n_press > 1)
g_signal_emit (swatch, signals[ACTIVATE], 0);
activate_color (swatch);
}
else if (button == GDK_BUTTON_SECONDARY)
{
@ -505,7 +532,7 @@ swatch_dispose (GObject *object)
GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
GtkColorSwatchPrivate *priv = gtk_color_swatch_get_instance_private (swatch);
g_clear_pointer (&priv->popover, gtk_widget_destroy);
g_clear_pointer (&priv->popover, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_color_swatch_parent_class)->dispose (object);
}
@ -530,20 +557,6 @@ gtk_color_swatch_class_init (GtkColorSwatchClass *class)
widget_class->size_allocate = swatch_size_allocate;
widget_class->state_flags_changed = swatch_state_flags_changed;
signals[ACTIVATE] =
g_signal_new (I_("activate"),
GTK_TYPE_COLOR_SWATCH,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
NULL, NULL, NULL, G_TYPE_NONE, 0);
signals[CUSTOMIZE] =
g_signal_new (I_("customize"),
GTK_TYPE_COLOR_SWATCH,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
NULL, NULL, NULL, G_TYPE_NONE, 0);
g_object_class_install_property (object_class, PROP_RGBA,
g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
@ -569,6 +582,10 @@ gtk_color_swatch_init (GtkColorSwatch *swatch)
priv->use_alpha = TRUE;
priv->selectable = TRUE;
priv->has_menu = TRUE;
priv->color.red = 0.75;
priv->color.green = 0.25;
priv->color.blue = 0.25;
priv->color.alpha = 1.0;
gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
gtk_widget_set_overflow (GTK_WIDGET (swatch), GTK_OVERFLOW_HIDDEN);

View File

@ -222,8 +222,8 @@ enum {
PROP_INPUT_PURPOSE,
PROP_INPUT_HINTS,
PROP_ATTRIBUTES,
PROP_POPULATE_ALL,
PROP_TABS,
PROP_EXTRA_MENU,
PROP_SHOW_EMOJI_ICON,
PROP_ENABLE_EMOJI_COMPLETION,
PROP_EDITING_CANCELED,
@ -798,19 +798,6 @@ gtk_entry_class_init (GtkEntryClass *class)
PANGO_TYPE_ATTR_LIST,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkEntry:populate-all:
*
* If :populate-all is %TRUE, the #GtkEntry::populate-popup
* signal is also emitted for touch popups.
*/
entry_props[PROP_POPULATE_ALL] =
g_param_spec_boolean ("populate-all",
P_("Populate all"),
P_("Whether to emit ::populate-popup for touch popups"),
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkEntry::tabs:
*
@ -836,6 +823,19 @@ gtk_entry_class_init (GtkEntryClass *class)
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkEntry:extra-menu:
*
* A menu model whose contents will be appended to
* the context menu.
*/
entry_props[PROP_EXTRA_MENU] =
g_param_spec_object ("extra-menu",
P_("Extra menu"),
P_("Model menu to append to the context menu"),
G_TYPE_MENU_MODEL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
entry_props[PROP_ENABLE_EMOJI_COMPLETION] =
g_param_spec_boolean ("enable-emoji-completion",
P_("Enable Emoji completion"),
@ -944,7 +944,6 @@ gtk_entry_set_property (GObject *object,
case PROP_INPUT_PURPOSE:
case PROP_INPUT_HINTS:
case PROP_ATTRIBUTES:
case PROP_POPULATE_ALL:
case PROP_TABS:
case PROP_ENABLE_EMOJI_COMPLETION:
g_object_set_property (G_OBJECT (priv->text), pspec->name, value);
@ -1062,6 +1061,10 @@ gtk_entry_set_property (GObject *object,
set_show_emoji_icon (entry, g_value_get_boolean (value));
break;
case PROP_EXTRA_MENU:
gtk_entry_set_extra_menu (entry, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1096,7 +1099,6 @@ gtk_entry_get_property (GObject *object,
case PROP_INPUT_PURPOSE:
case PROP_INPUT_HINTS:
case PROP_ATTRIBUTES:
case PROP_POPULATE_ALL:
case PROP_TABS:
case PROP_ENABLE_EMOJI_COMPLETION:
g_object_get_property (G_OBJECT (priv->text), pspec->name, value);
@ -1219,6 +1221,10 @@ gtk_entry_get_property (GObject *object,
g_value_set_boolean (value, priv->show_emoji_icon);
break;
case PROP_EXTRA_MENU:
g_value_set_object (value, gtk_entry_get_extra_menu (entry));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -3471,6 +3477,8 @@ set_show_emoji_icon (GtkEntry *entry,
gboolean value)
{
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
GActionGroup *actions;
GAction *action;
if (priv->show_emoji_icon == value)
return;
@ -3512,6 +3520,12 @@ set_show_emoji_icon (GtkEntry *entry,
g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_SHOW_EMOJI_ICON]);
gtk_widget_queue_resize (GTK_WIDGET (entry));
actions = gtk_widget_get_action_group (priv->text, "context");
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "insert-emoji");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
priv->show_emoji_icon ||
(gtk_entry_get_input_hints (entry) & GTK_INPUT_HINT_NO_EMOJI) == 0);
}
GtkEventController *
@ -3529,3 +3543,42 @@ gtk_entry_get_text_widget (GtkEntry *entry)
return GTK_TEXT (priv->text);
}
/**
* gtk_entry_set_extra_menu:
* @entry: a #GtkEntry
* @model: (allow-none): a #GMenuModel
*
* Sets a menu model to add when constructing
* the context menu for @entry.
*/
void
gtk_entry_set_extra_menu (GtkEntry *entry,
GMenuModel *model)
{
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
g_return_if_fail (GTK_IS_ENTRY (entry));
gtk_text_set_extra_menu (GTK_TEXT (priv->text), model);
g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_EXTRA_MENU]);
}
/**
* gtk_entry_get_extra_menu:
* @self: a #GtkText
*
* Gets the menu model set with gtk_entry_set_extra_menu().
*
* Returns: (transfer none): (nullable): the menu model
*/
GMenuModel *
gtk_entry_get_extra_menu (GtkEntry *entry)
{
GtkEntryPrivate *priv = gtk_entry_get_instance_private (entry);
g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
return gtk_text_get_extra_menu (GTK_TEXT (priv->text));
}

View File

@ -77,9 +77,6 @@ struct _GtkEntry
/**
* GtkEntryClass:
* @parent_class: The parent class.
* @populate_popup: Class handler for the #GtkEntry::populate-popup signal. If
* non-%NULL, this will be called to add additional entries to the context
* menu when it is displayed.
* @activate: Class handler for the #GtkEntry::activate signal. The default
* implementation activates the gtk.activate-default action.
* @move_cursor: Class handler for the #GtkEntry::move-cursor signal. The
@ -310,6 +307,12 @@ PangoTabArray *gtk_entry_get_tabs (GtkEntry
GDK_AVAILABLE_IN_ALL
void gtk_entry_grab_focus_without_selecting (GtkEntry *entry);
GDK_AVAILABLE_IN_ALL
void gtk_entry_set_extra_menu (GtkEntry *entry,
GMenuModel *model);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_entry_get_extra_menu (GtkEntry *entry);
G_END_DECLS
#endif /* __GTK_ENTRY_H__ */

View File

@ -55,6 +55,8 @@
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
#include "gtkwindow.h"
#include "gtkpopovermenu.h"
#include "gtknative.h"
#include "a11y/gtklabelaccessibleprivate.h"
@ -269,9 +271,6 @@ struct _GtkLabelClass
gboolean extend_selection);
void (* copy_clipboard) (GtkLabel *label);
void (* populate_popup) (GtkLabel *label,
GtkMenu *menu);
gboolean (*activate_link) (GtkLabel *label,
const gchar *uri);
};
@ -286,6 +285,10 @@ struct _GtkLabelPrivate
PangoAttrList *markup_attrs;
PangoLayout *layout;
GActionMap *context_actions;
GtkWidget *popup_menu;
GMenuModel *extra_menu;
gchar *label;
gchar *text;
@ -355,12 +358,12 @@ struct _GtkLabelSelectionInfo
{
gint selection_anchor;
gint selection_end;
GtkWidget *popup_menu;
GtkCssNode *selection_node;
GdkContentProvider *provider;
GList *links;
GtkLabelLink *active_link;
GtkLabelLink *context_link;
GtkGesture *drag_gesture;
GtkGesture *click_gesture;
@ -378,7 +381,6 @@ struct _GtkLabelSelectionInfo
enum {
MOVE_CURSOR,
COPY_CLIPBOARD,
POPULATE_POPUP,
ACTIVATE_LINK,
ACTIVATE_CURRENT_LINK,
LAST_SIGNAL
@ -407,6 +409,7 @@ enum {
PROP_LINES,
PROP_XALIGN,
PROP_YALIGN,
PROP_EXTRA_MENU,
NUM_PROPERTIES
};
@ -444,7 +447,6 @@ static gboolean gtk_label_focus (GtkWidget *widget,
static void gtk_label_realize (GtkWidget *widget);
static void gtk_label_unrealize (GtkWidget *widget);
static void gtk_label_unmap (GtkWidget *widget);
static void gtk_label_motion (GtkEventControllerMotion *controller,
double x,
@ -483,6 +485,9 @@ static void gtk_label_recalculate (GtkLabel *label);
static void gtk_label_root (GtkWidget *widget);
static void gtk_label_unroot (GtkWidget *widget);
static gboolean gtk_label_popup_menu (GtkWidget *widget);
static void gtk_label_do_popup (GtkLabel *label,
double x,
double y);
static void gtk_label_set_selectable_hint (GtkLabel *label);
static void gtk_label_ensure_select_info (GtkLabel *label);
@ -536,8 +541,6 @@ static void gtk_label_move_cursor (GtkLabel *label,
gboolean extend_selection);
static void gtk_label_copy_clipboard (GtkLabel *label);
static void gtk_label_select_all (GtkLabel *label);
static void gtk_label_do_popup (GtkLabel *label,
const GdkEvent *event);
static gint gtk_label_move_forward_word (GtkLabel *label,
gint start);
static gint gtk_label_move_backward_word (GtkLabel *label,
@ -572,6 +575,9 @@ static void gtk_label_drag_gesture_update (GtkGestureDrag *gesture,
gdouble offset_y,
GtkLabel *label);
static void gtk_label_add_context_actions (GtkLabel *label);
static void gtk_label_update_clipboard_actions (GtkLabel *label);
static GtkSizeRequestMode gtk_label_get_request_mode (GtkWidget *widget);
static void gtk_label_measure (GtkWidget *widget,
GtkOrientation orientation,
@ -581,6 +587,8 @@ static void gtk_label_measure (GtkWidget *widget,
int *minimum_baseline,
int *natural_baseline);
static GtkBuildableIface *buildable_parent_iface = NULL;
G_DEFINE_TYPE_WITH_CODE (GtkLabel, gtk_label, GTK_TYPE_WIDGET,
@ -630,13 +638,12 @@ gtk_label_class_init (GtkLabelClass *class)
widget_class->snapshot = gtk_label_snapshot;
widget_class->realize = gtk_label_realize;
widget_class->unrealize = gtk_label_unrealize;
widget_class->unmap = gtk_label_unmap;
widget_class->root = gtk_label_root;
widget_class->unroot = gtk_label_unroot;
widget_class->mnemonic_activate = gtk_label_mnemonic_activate;
widget_class->popup_menu = gtk_label_popup_menu;
widget_class->drag_data_get = gtk_label_drag_data_get;
widget_class->grab_focus = gtk_label_grab_focus;
widget_class->popup_menu = gtk_label_popup_menu;
widget_class->focus = gtk_label_focus;
widget_class->get_request_mode = gtk_label_get_request_mode;
widget_class->measure = gtk_label_measure;
@ -701,28 +708,6 @@ gtk_label_class_init (GtkLabelClass *class)
NULL,
G_TYPE_NONE, 0);
/**
* GtkLabel::populate-popup:
* @label: The label on which the signal is emitted
* @menu: the menu that is being populated
*
* The ::populate-popup signal gets emitted before showing the
* context menu of the label. Note that only selectable labels
* have context menus.
*
* If you need to add items to the context menu, connect
* to this signal and append your menuitems to the @menu.
*/
signals[POPULATE_POPUP] =
g_signal_new (I_("populate-popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkLabelClass, populate_popup),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GTK_TYPE_MENU);
/**
* GtkLabel::activate-current-link:
* @label: The label on which the signal was emitted
@ -1019,6 +1004,19 @@ gtk_label_class_init (GtkLabelClass *class)
-1,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkLabel:extra-menu:
*
* A menu model whose contents will be appended to
* the context menu.
*/
label_props[PROP_EXTRA_MENU] =
g_param_spec_object ("extra-menu",
P_("Extra menu"),
P_("Menu model to append to the context menu"),
G_TYPE_MENU_MODEL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, label_props);
/*
@ -1211,6 +1209,9 @@ gtk_label_set_property (GObject *object,
case PROP_YALIGN:
gtk_label_set_yalign (label, g_value_get_float (value));
break;
case PROP_EXTRA_MENU:
gtk_label_set_extra_menu (label, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1288,6 +1289,9 @@ gtk_label_get_property (GObject *object,
case PROP_YALIGN:
g_value_set_float (value, gtk_label_get_yalign (label));
break;
case PROP_EXTRA_MENU:
g_value_set_object (value, gtk_label_get_extra_menu (label));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1326,6 +1330,8 @@ gtk_label_init (GtkLabel *label)
priv->mnemonic_window = NULL;
priv->mnemonics_visible = TRUE;
gtk_label_add_context_actions (label);
}
@ -3199,6 +3205,10 @@ gtk_label_finalize (GObject *object)
gtk_label_clear_links (label);
g_free (priv->select_info);
g_clear_object (&priv->context_actions);
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_clear_object (&priv->extra_menu);
G_OBJECT_CLASS (gtk_label_parent_class)->finalize (object);
}
@ -3681,6 +3691,9 @@ gtk_label_size_allocate (GtkWidget *widget,
else
pango_layout_set_width (priv->layout, -1);
}
if (priv->popup_menu)
gtk_native_check_resize (GTK_NATIVE (priv->popup_menu));
}
static void
@ -4115,24 +4128,6 @@ gtk_label_unrealize (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_label_parent_class)->unrealize (widget);
}
static void
gtk_label_unmap (GtkWidget *widget)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
if (priv->select_info)
{
if (priv->select_info->popup_menu)
{
gtk_widget_destroy (priv->select_info->popup_menu);
priv->select_info->popup_menu = NULL;
}
}
GTK_WIDGET_CLASS (gtk_label_parent_class)->unmap (widget);
}
static gboolean
get_layout_index (GtkLabel *label,
gint x,
@ -4471,7 +4466,7 @@ gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
{
info->link_clicked = 1;
update_link_state (label);
gtk_label_do_popup (label, event);
gtk_label_do_popup (label, widget_x, widget_y);
return;
}
else if (button == GDK_BUTTON_PRIMARY)
@ -4494,7 +4489,7 @@ gtk_label_click_gesture_pressed (GtkGestureClick *gesture,
info->select_words = FALSE;
if (gdk_event_triggers_context_menu (event))
gtk_label_do_popup (label, event);
gtk_label_do_popup (label, widget_x, widget_y);
else if (button == GDK_BUTTON_PRIMARY)
{
if (!gtk_widget_has_focus (widget))
@ -5972,167 +5967,220 @@ gtk_label_select_all (GtkLabel *label)
gtk_label_select_region_index (label, 0, strlen (priv->text));
}
/* Quick hack of a popup menu
*/
static void
activate_cb (GtkWidget *menuitem,
GtkLabel *label)
open_link_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
const gchar *signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
g_signal_emit_by_name (label, signal);
}
static void
append_action_signal (GtkLabel *label,
GtkWidget *menu,
const gchar *text,
const gchar *signal,
gboolean sensitive)
{
GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (text);
g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
g_signal_connect (menuitem, "activate",
G_CALLBACK (activate_cb), label);
gtk_widget_set_sensitive (menuitem, sensitive);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
static void
popup_menu_detach (GtkWidget *attach_widget,
GtkMenu *menu)
{
GtkLabel *label = GTK_LABEL (attach_widget);
GtkLabel *label = GTK_LABEL (user_data);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelLink *link = priv->select_info->context_link;
if (link)
emit_activate_link (label, link);
}
static void
copy_link_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkLabel *label = GTK_LABEL (user_data);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelLink *link = priv->select_info->context_link;
if (link)
{
GdkClipboard *clipboard;
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label));
gdk_clipboard_set_text (clipboard, link->uri);
}
}
static void
copy_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "copy-clipboard");
}
static void
select_all_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_label_select_all (GTK_LABEL (user_data));
}
static void
gtk_label_update_clipboard_actions (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
gboolean have_selection = FALSE;
GAction *action;
if (priv->select_info)
priv->select_info->popup_menu = NULL;
have_selection = priv->select_info->selection_anchor != priv->select_info->selection_end;
action = g_action_map_lookup_action (priv->context_actions, "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), have_selection);
action = g_action_map_lookup_action (priv->context_actions, "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), gtk_label_get_selectable (label));
}
static void
open_link_activate_cb (GtkMenuItem *menuitem,
GtkLabel *label)
gtk_label_update_link_actions (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
gboolean have_selection = FALSE;
GAction *action;
GtkLabelLink *link;
link = g_object_get_qdata (G_OBJECT (menuitem), quark_link);
emit_activate_link (label, link);
have_selection = priv->select_info->selection_anchor != priv->select_info->selection_end;
if (priv->select_info->link_clicked)
link = priv->select_info->active_link;
else
link = gtk_label_get_focus_link (label);
action = g_action_map_lookup_action (priv->context_actions, "open-link");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !have_selection && link);
action = g_action_map_lookup_action (priv->context_actions, "copy-link");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !have_selection && link);
}
static void
copy_link_activate_cb (GtkMenuItem *menuitem,
GtkLabel *label)
gtk_label_add_context_actions (GtkLabel *label)
{
GtkLabelLink *link;
GdkClipboard *clipboard;
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
link = g_object_get_qdata (G_OBJECT (menuitem), quark_link);
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (label));
gdk_clipboard_set_text (clipboard, link->uri);
GActionEntry entries[] = {
{ "cut-clipboard", NULL, NULL, NULL, NULL },
{ "copy-clipboard", copy_clipboard_activated, NULL, NULL, NULL },
{ "paste-clipboard", NULL, NULL, NULL, NULL },
{ "delete-selection", NULL, NULL, NULL, NULL },
{ "select-all", select_all_activated, NULL, NULL, NULL },
{ "open-link", open_link_activated, NULL, NULL, NULL },
{ "copy-link", copy_link_activated, NULL, NULL, NULL },
};
GSimpleActionGroup *actions = g_simple_action_group_new ();
GAction *action;
priv->context_actions = G_ACTION_MAP (actions);
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), label);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "cut-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "paste-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "delete-selection");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "open-link");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-link");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
gtk_widget_insert_action_group (GTK_WIDGET (label), "context", G_ACTION_GROUP (actions));
}
static GMenuModel *
gtk_label_get_menu_model (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GMenu *menu, *section;
GMenuItem *item;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append (section, _("Cu_t"), "context.cut-clipboard");
g_menu_append (section, _("_Copy"), "context.copy-clipboard");
g_menu_append (section, _("_Paste"), "context.paste-clipboard");
g_menu_append (section, _("_Delete"), "context.delete-selection");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
section = g_menu_new ();
g_menu_append (section, _("Select _All"), "context.select-all");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
section = g_menu_new ();
item = g_menu_item_new (_("_Open Link"), "context.open-link");
g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("Copy _Link Address"), "context.copy-link");
g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
if (priv->extra_menu)
g_menu_append_section (menu, NULL, priv->extra_menu);
return G_MENU_MODEL (menu);
}
static void
gtk_label_do_popup (GtkLabel *label,
double x,
double y)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
gtk_label_update_clipboard_actions (label);
gtk_label_update_link_actions (label);
if (!priv->popup_menu)
{
GMenuModel *model;
model = gtk_label_get_menu_model (label);
priv->popup_menu = gtk_popover_menu_new_from_model (GTK_WIDGET (label), model);
gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM);
gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE);
gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START);
g_object_unref (model);
}
if (x != -1 && y != -1)
{
GdkRectangle rect = { x, y, 1, 1 };
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &rect);
}
else
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), NULL);
gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
}
static gboolean
gtk_label_popup_menu (GtkWidget *widget)
{
gtk_label_do_popup (GTK_LABEL (widget), NULL);
return TRUE;
}
static void
gtk_label_do_popup (GtkLabel *label,
const GdkEvent *event)
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkWidget *menuitem;
GtkWidget *menu;
gboolean have_selection;
GtkLabelLink *link;
if (!priv->select_info)
return;
return FALSE;
if (priv->select_info->popup_menu)
gtk_widget_destroy (priv->select_info->popup_menu);
priv->select_info->popup_menu = menu = gtk_menu_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (menu),
GTK_STYLE_CLASS_CONTEXT_MENU);
gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (label), popup_menu_detach);
have_selection =
priv->select_info->selection_anchor != priv->select_info->selection_end;
if (event)
{
if (priv->select_info->link_clicked)
link = priv->select_info->active_link;
else
link = NULL;
}
if (priv->select_info->link_clicked)
priv->select_info->context_link = priv->select_info->active_link;
else
link = gtk_label_get_focus_link (label);
priv->select_info->context_link = gtk_label_get_focus_link (label);
if (!have_selection && link)
{
/* Open Link */
menuitem = gtk_menu_item_new_with_mnemonic (_("_Open Link"));
g_object_set_qdata (G_OBJECT (menuitem), quark_link, link);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (G_OBJECT (menuitem), "activate",
G_CALLBACK (open_link_activate_cb), label);
/* Copy Link Address */
menuitem = gtk_menu_item_new_with_mnemonic (_("Copy _Link Address"));
g_object_set_qdata (G_OBJECT (menuitem), quark_link, link);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
g_signal_connect (G_OBJECT (menuitem), "activate",
G_CALLBACK (copy_link_activate_cb), label);
}
else
{
append_action_signal (label, menu, _("Cu_t"), "cut-clipboard", FALSE);
append_action_signal (label, menu, _("_Copy"), "copy-clipboard", have_selection);
append_action_signal (label, menu, _("_Paste"), "paste-clipboard", FALSE);
menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
gtk_widget_set_sensitive (menuitem, FALSE);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_label_select_all), label);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
if (event && gdk_event_triggers_context_menu (event))
gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
else
{
gtk_menu_popup_at_widget (GTK_MENU (menu),
GTK_WIDGET (label),
GDK_GRAVITY_SOUTH,
GDK_GRAVITY_NORTH_WEST,
event);
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
}
gtk_label_do_popup (label, -1, -1);
return TRUE;
}
static void
@ -6646,3 +6694,44 @@ gtk_label_get_yalign (GtkLabel *label)
return priv->yalign;
}
/**
* gtk_label_set_extra_menu:
* @label: a #GtkLabel
* @model: (allow-none): a #GMenuModel
*
* Sets a menu model to add when constructing
* the context menu for @label.
*/
void
gtk_label_set_extra_menu (GtkLabel *label,
GMenuModel *model)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
g_return_if_fail (GTK_IS_LABEL (label));
if (g_set_object (&priv->extra_menu, model))
{
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_object_notify_by_pspec (G_OBJECT (label), label_props[PROP_EXTRA_MENU]);
}
}
/**
* gtk_label_get_extra_menu:
* @label: a #GtkLabel
*
* Gets the menu model set with gtk_label_set_extra_menu().
*
* Returns: (transfer none): (nullable): the menu model
*/
GMenuModel *
gtk_label_get_extra_menu (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
return priv->extra_menu;
}

View File

@ -176,6 +176,13 @@ void gtk_label_set_yalign (GtkLabel *label,
GDK_AVAILABLE_IN_ALL
gfloat gtk_label_get_yalign (GtkLabel *label);
GDK_AVAILABLE_IN_ALL
void gtk_label_set_extra_menu (GtkLabel *label,
GMenuModel *model);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_label_get_extra_menu (GtkLabel *label);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GtkLabel, g_object_unref)
G_END_DECLS

View File

@ -60,8 +60,7 @@
#include "gtklabel.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtkpopovermenu.h"
#include "gtkprivate.h"
#include "gtkshow.h"
#include "gtksizerequest.h"
@ -97,6 +96,7 @@ struct _GtkLinkButtonPrivate
gboolean visited;
GActionMap *context_actions;
GtkWidget *popup_menu;
};
@ -223,6 +223,43 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass)
gtk_widget_class_set_css_name (widget_class, I_("button"));
}
static void copy_activate_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data);
static void
gtk_link_button_add_context_actions (GtkLinkButton *link_button)
{
GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
GActionEntry entries[] = {
{ "copy-clipboard", copy_activate_cb, NULL, NULL, NULL },
};
GSimpleActionGroup *actions = g_simple_action_group_new ();
priv->context_actions = G_ACTION_MAP (actions);
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), link_button);
gtk_widget_insert_action_group (GTK_WIDGET (link_button), "context", G_ACTION_GROUP (actions));
}
static GMenuModel *
gtk_link_button_get_menu_model (void)
{
GMenu *menu, *section;
menu = g_menu_new ();
section = g_menu_new ();
g_menu_append (section, _("_Copy URL"), "context.copy-clipboard");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
return G_MENU_MODEL (menu);
}
static void
gtk_link_button_init (GtkLinkButton *link_button)
{
@ -261,6 +298,7 @@ gtk_link_button_init (GtkLinkButton *link_button)
gtk_style_context_add_class (context, "link");
gtk_widget_set_cursor_from_name (GTK_WIDGET (link_button), "pointer");
gtk_link_button_add_context_actions (link_button);
}
static void
@ -270,7 +308,10 @@ gtk_link_button_finalize (GObject *object)
GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
g_free (priv->uri);
g_clear_object (&priv->context_actions);
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_link_button_parent_class)->finalize (object);
}
@ -320,19 +361,11 @@ gtk_link_button_set_property (GObject *object,
}
static void
popup_menu_detach (GtkWidget *attach_widget,
GtkMenu *menu)
{
GtkLinkButton *link_button = GTK_LINK_BUTTON (attach_widget);
GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
priv->popup_menu = NULL;
}
static void
copy_activate_cb (GtkWidget *widget,
GtkLinkButton *link_button)
copy_activate_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkLinkButton *link_button = user_data;
GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
gdk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (link_button)),
@ -340,45 +373,35 @@ copy_activate_cb (GtkWidget *widget,
}
static void
gtk_link_button_do_popup (GtkLinkButton *link_button,
const GdkEvent *event)
gtk_link_button_do_popup (GtkLinkButton *link_button,
double x,
double y)
{
GtkLinkButtonPrivate *priv = gtk_link_button_get_instance_private (link_button);
if (gtk_widget_get_realized (GTK_WIDGET (link_button)))
if (!priv->popup_menu)
{
GtkWidget *menu_item;
GMenuModel *model;
if (priv->popup_menu)
gtk_widget_destroy (priv->popup_menu);
model = gtk_link_button_get_menu_model ();
priv->popup_menu = gtk_popover_menu_new_from_model (GTK_WIDGET (link_button), model);
gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM);
priv->popup_menu = gtk_menu_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (priv->popup_menu),
GTK_STYLE_CLASS_CONTEXT_MENU);
gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE);
gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START);
gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
GTK_WIDGET (link_button),
popup_menu_detach);
menu_item = gtk_menu_item_new_with_mnemonic (_("Copy URL"));
g_signal_connect (menu_item, "activate",
G_CALLBACK (copy_activate_cb), link_button);
gtk_widget_show (menu_item);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menu_item);
if (event && gdk_event_triggers_context_menu (event))
gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), event);
else
{
gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
GTK_WIDGET (link_button),
GDK_GRAVITY_SOUTH,
GDK_GRAVITY_NORTH_WEST,
event);
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
}
g_object_unref (model);
}
if (x != -1 && y != -1)
{
GdkRectangle rect = { x, y, 1, 1 };
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &rect);
}
else
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), NULL);
gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
}
static void
@ -399,7 +422,7 @@ gtk_link_button_pressed_cb (GtkGestureClick *gesture,
if (gdk_event_triggers_context_menu (event) &&
priv->uri != NULL)
{
gtk_link_button_do_popup (link_button, event);
gtk_link_button_do_popup (link_button, x, y);
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
}
else
@ -445,9 +468,8 @@ gtk_link_button_clicked (GtkButton *button)
static gboolean
gtk_link_button_popup_menu (GtkWidget *widget)
{
gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), NULL);
return TRUE;
gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), -1, -1);
return TRUE;
}
static void

View File

@ -58,6 +58,7 @@ typedef struct {
GtkWidget *icon;
GtkWidget *peek_icon;
GdkKeymap *keymap;
GMenuModel *extra_menu;
} GtkPasswordEntryPrivate;
struct _GtkPasswordEntryClass
@ -69,6 +70,7 @@ enum {
PROP_PLACEHOLDER_TEXT = 1,
PROP_ACTIVATES_DEFAULT,
PROP_SHOW_PEEK_ICON,
PROP_EXTRA_MENU,
NUM_PROPERTIES
};
@ -105,7 +107,7 @@ focus_changed (GtkWidget *widget)
if (priv->keymap)
keymap_state_changed (priv->keymap, widget);
}
static void
gtk_password_entry_toggle_peek (GtkPasswordEntry *entry)
{
@ -125,27 +127,6 @@ gtk_password_entry_toggle_peek (GtkPasswordEntry *entry)
}
}
static void
populate_popup (GtkText *text,
GtkWidget *popup,
GtkPasswordEntry *entry)
{
GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
if (priv->peek_icon != NULL)
{
GtkWidget *item;
item = gtk_check_menu_item_new_with_mnemonic (_("_Show text"));
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
gtk_text_get_visibility (text));
g_signal_connect_swapped (item, "activate",
G_CALLBACK (gtk_password_entry_toggle_peek), entry);
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
}
}
static void
gtk_password_entry_init (GtkPasswordEntry *entry)
{
@ -156,7 +137,6 @@ gtk_password_entry_init (GtkPasswordEntry *entry)
gtk_widget_set_parent (priv->entry, GTK_WIDGET (entry));
gtk_editable_init_delegate (GTK_EDITABLE (entry));
g_signal_connect_swapped (priv->entry, "notify::has-focus", G_CALLBACK (focus_changed), entry);
g_signal_connect (priv->entry, "populate-popup", G_CALLBACK (populate_popup), entry);
priv->icon = gtk_image_new_from_icon_name ("caps-lock-symbolic");
gtk_widget_set_tooltip_text (priv->icon, _("Caps Lock is on"));
@ -165,6 +145,8 @@ gtk_password_entry_init (GtkPasswordEntry *entry)
gtk_widget_set_parent (priv->icon, GTK_WIDGET (entry));
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (entry)), I_("password"));
gtk_password_entry_set_extra_menu (entry, NULL);
}
static void
@ -195,6 +177,7 @@ gtk_password_entry_dispose (GObject *object)
g_clear_pointer (&priv->entry, gtk_widget_unparent);
g_clear_pointer (&priv->icon, gtk_widget_unparent);
g_clear_pointer (&priv->peek_icon, gtk_widget_unparent);
g_clear_object (&priv->extra_menu);
G_OBJECT_CLASS (gtk_password_entry_parent_class)->dispose (object);
}
@ -235,6 +218,10 @@ gtk_password_entry_set_property (GObject *object,
gtk_password_entry_set_show_peek_icon (entry, g_value_get_boolean (value));
break;
case PROP_EXTRA_MENU:
gtk_password_entry_set_extra_menu (entry, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -267,6 +254,10 @@ gtk_password_entry_get_property (GObject *object,
g_value_set_boolean (value, gtk_password_entry_get_show_peek_icon (entry));
break;
case PROP_EXTRA_MENU:
g_value_set_object (value, priv->extra_menu);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -389,7 +380,6 @@ gtk_password_entry_class_init (GtkPasswordEntryClass *klass)
widget_class->get_accessible = gtk_password_entry_get_accessible;
widget_class->grab_focus = gtk_password_entry_grab_focus;
widget_class->mnemonic_activate = gtk_password_entry_mnemonic_activate;
props[PROP_PLACEHOLDER_TEXT] =
g_param_spec_string ("placeholder-text",
P_("Placeholder text"),
@ -411,6 +401,19 @@ gtk_password_entry_class_init (GtkPasswordEntryClass *klass)
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkPasswordEntry:extra-menu:
*
* A menu model whose contents will be appended to
* the context menu.
*/
props[PROP_EXTRA_MENU] =
g_param_spec_object ("extra-menu",
P_("Extra menu"),
P_("Model menu to append to the context menu"),
G_TYPE_MENU_MODEL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
gtk_editable_install_properties (object_class, NUM_PROPERTIES);
@ -509,3 +512,64 @@ gtk_password_entry_get_show_peek_icon (GtkPasswordEntry *entry)
return priv->peek_icon != NULL;
}
/**
* gtk_password_entry_set_extra_menu:
* @entry: a #GtkPasswordEntry
* @model: (allow-none): a #GMenuModel
*
* Sets a menu model to add when constructing
* the context menu for @entry.
*/
void
gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry,
GMenuModel *model)
{
GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
GMenu *menu;
GMenu *section;
GMenuItem *item;
g_return_if_fail (GTK_IS_PASSWORD_ENTRY (entry));
if (!g_set_object (&priv->extra_menu, model))
return;
menu = g_menu_new ();
section = g_menu_new ();
item = g_menu_item_new (_("_Show Text"), "context.toggle-visibility");
g_menu_item_set_attribute (item, "touch-icon", "s", "eye-not-looking-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
if (model)
g_menu_append_section (menu, NULL, model);
gtk_text_set_extra_menu (GTK_TEXT (priv->entry), G_MENU_MODEL (menu));
g_object_unref (menu);
g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_EXTRA_MENU]);
}
/**
* gtk_password_entry_get_extra_menu:
* @self: a #GtkText
*
* Gets the menu model set with gtk_password_entry_set_extra_menu().
*
* Returns: (transfer none): (nullable): the menu model
*/
GMenuModel *
gtk_password_entry_get_extra_menu (GtkPasswordEntry *entry)
{
GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
g_return_val_if_fail (GTK_IS_PASSWORD_ENTRY (entry), NULL);
return priv->extra_menu;
}

View File

@ -53,6 +53,12 @@ void gtk_password_entry_set_show_peek_icon (GtkPasswordEntry *entry,
GDK_AVAILABLE_IN_ALL
gboolean gtk_password_entry_get_show_peek_icon (GtkPasswordEntry *entry);
GDK_AVAILABLE_IN_ALL
void gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry,
GMenuModel *model);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_password_entry_get_extra_menu (GtkPasswordEntry *entry);
G_END_DECLS
#endif /* __GTK_PASSWORD_ENTRY_H__ */

View File

@ -228,7 +228,6 @@ struct _GtkPlacesSidebarClass {
enum {
OPEN_LOCATION,
POPULATE_POPUP,
SHOW_ERROR_MESSAGE,
SHOW_ENTER_LOCATION,
DRAG_ACTION_REQUESTED,
@ -251,7 +250,6 @@ enum {
PROP_SHOW_STARRED_LOCATION,
PROP_LOCAL_ONLY,
PROP_SHOW_OTHER_LOCATIONS,
PROP_POPULATE_ALL,
NUM_PROPERTIES
};
@ -3663,33 +3661,6 @@ create_row_popover (GtkPlacesSidebar *sidebar,
/* Update everything! */
check_popover_sensitivity (row, &data);
if (sidebar->populate_all)
{
gchar *uri;
GVolume *volume;
GFile *file;
g_object_get (row,
"uri", &uri,
"volume", &volume,
NULL);
if (uri)
file = g_file_new_for_uri (uri);
else
file = NULL;
g_signal_emit (sidebar, places_sidebar_signals[POPULATE_POPUP], 0,
box, file, volume);
if (file)
g_object_unref (file);
g_free (uri);
if (volume)
g_object_unref (volume);
}
}
static void
@ -4235,14 +4206,6 @@ gtk_places_sidebar_set_property (GObject *obj,
gtk_places_sidebar_set_local_only (sidebar, g_value_get_boolean (value));
break;
case PROP_POPULATE_ALL:
if (sidebar->populate_all != g_value_get_boolean (value))
{
sidebar->populate_all = g_value_get_boolean (value);
g_object_notify_by_pspec (obj, pspec);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
@ -4295,10 +4258,6 @@ gtk_places_sidebar_get_property (GObject *obj,
g_value_set_boolean (value, gtk_places_sidebar_get_local_only (sidebar));
break;
case PROP_POPULATE_ALL:
g_value_set_boolean (value, sidebar->populate_all);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
@ -4496,53 +4455,6 @@ gtk_places_sidebar_class_init (GtkPlacesSidebarClass *class)
G_TYPE_OBJECT,
GTK_TYPE_PLACES_OPEN_FLAGS);
/*
* GtkPlacesSidebar::populate-popup:
* @sidebar: the object which received the signal.
* @container: (type Gtk.Widget): a #GtkMenu or another #GtkContainer
* @selected_item: (type Gio.File) (nullable): #GFile with the item to which
* the popup should refer, or %NULL in the case of a @selected_volume.
* @selected_volume: (type Gio.Volume) (nullable): #GVolume if the selected
* item is a volume, or %NULL if it is a file.
*
* The places sidebar emits this signal when the user invokes a contextual
* popup on one of its items. In the signal handler, the application may
* add extra items to the menu as appropriate. For example, a file manager
* may want to add a "Properties" command to the menu.
*
* It is not necessary to store the @selected_item for each menu item;
* during their callbacks, the application can use gtk_places_sidebar_get_location()
* to get the file to which the item refers.
*
* The @selected_item argument may be %NULL in case the selection refers to
* a volume. In this case, @selected_volume will be non-%NULL. In this case,
* the calling application will have to g_object_ref() the @selected_volume and
* keep it around to use it in the callback.
*
* The @container and all its contents are destroyed after the user
* dismisses the popup. The popup is re-created (and thus, this signal is
* emitted) every time the user activates the contextual menu.
*
* Before 3.18, the @container always was a #GtkMenu, and you were expected
* to add your items as #GtkMenuItems. The popup may be implemented
* as a #GtkPopover, in which case @container will be something else, e.g. a
* #GtkBox, to which you may add #GtkModelButtons or other widgets, such as
* #GtkEntries, #GtkSpinButtons, etc. If your application can deal with this
* situation, you can set #GtkPlacesSidebar::populate-all to %TRUE to request
* that this signal is emitted for populating popovers as well.
*/
places_sidebar_signals [POPULATE_POPUP] =
g_signal_new (I_("populate-popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GtkPlacesSidebarClass, populate_popup),
NULL, NULL,
_gtk_marshal_VOID__OBJECT_OBJECT_OBJECT,
G_TYPE_NONE, 3,
GTK_TYPE_WIDGET,
G_TYPE_FILE,
G_TYPE_VOLUME);
/*
* GtkPlacesSidebar::show-error-message:
* @sidebar: the object which received the signal.
@ -4802,20 +4714,6 @@ gtk_places_sidebar_class_init (GtkPlacesSidebarClass *class)
FALSE,
GTK_PARAM_READWRITE);
/*
* GtkPlacesSidebar:populate-all:
*
* If :populate-all is %TRUE, the #GtkPlacesSidebar::populate-popup signal
* is also emitted for popovers.
*/
properties[PROP_POPULATE_ALL] =
g_param_spec_boolean (I_("populate-all"),
P_("Populate all"),
P_("Whether to emit ::populate-popup for popups that are not menus"),
FALSE,
G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
gtk_widget_class_set_css_name (widget_class, I_("placessidebar"));
@ -4954,10 +4852,7 @@ gtk_places_sidebar_set_location (GtkPlacesSidebar *sidebar,
* been called with a location that is not among the sidebars list of places to
* show.
*
* You can use this function to get the selection in the @sidebar. Also, if you
* connect to the #GtkPlacesSidebar::populate-popup signal, you can use this
* function to get the location that is being referred to during the callbacks
* for your menu items.
* You can use this function to get the selection in the @sidebar.
*
* Returns: (nullable) (transfer full): a #GFile with the selected location, or
* %NULL if nothing is visually selected.

View File

@ -23,6 +23,7 @@
#include "gtktextprivate.h"
#include "gtkactionable.h"
#include "gtkadjustment.h"
#include "gtkbindings.h"
#include "gtkbox.h"
@ -51,7 +52,7 @@
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtkpango.h"
#include "gtkpopover.h"
#include "gtkpopovermenu.h"
#include "gtkprivate.h"
#include "gtkseparatormenuitem.h"
#include "gtkselection.h"
@ -145,7 +146,6 @@ struct _GtkTextPrivate
{
GtkEntryBuffer *buffer;
GtkIMContext *im_context;
GtkWidget *popup_menu;
int text_baseline;
@ -174,6 +174,10 @@ struct _GtkTextPrivate
GtkCssNode *block_cursor_node;
GtkCssNode *undershoot_node[2];
GActionMap *context_actions;
GtkWidget *popup_menu;
GMenuModel *extra_menu;
float xalign;
int ascent; /* font ascent in pango units */
@ -233,7 +237,6 @@ struct _GtkTextPasswordHint
enum {
ACTIVATE,
POPULATE_POPUP,
MOVE_CURSOR,
INSERT_AT_CURSOR,
DELETE_FROM_CURSOR,
@ -263,10 +266,10 @@ enum {
PROP_INPUT_PURPOSE,
PROP_INPUT_HINTS,
PROP_ATTRIBUTES,
PROP_POPULATE_ALL,
PROP_TABS,
PROP_ENABLE_EMOJI_COMPLETION,
PROP_PROPAGATE_TEXT_WIDTH,
PROP_EXTRA_MENU,
NUM_PROPERTIES
};
@ -383,6 +386,7 @@ static void gtk_text_set_alignment (GtkText *self,
/* Default signal handlers
*/
static GMenuModel *gtk_text_get_menu_model (GtkText *self);
static gboolean gtk_text_popup_menu (GtkWidget *widget);
static void gtk_text_move_cursor (GtkText *self,
GtkMovementStep step,
@ -512,8 +516,6 @@ static void gtk_text_paste (GtkText *self,
GdkClipboard *clipboard);
static void gtk_text_update_primary_selection (GtkText *self);
static void gtk_text_schedule_im_reset (GtkText *self);
static void gtk_text_do_popup (GtkText *self,
const GdkEvent *event);
static gboolean gtk_text_mnemonic_activate (GtkWidget *widget,
gboolean group_cycling);
static void gtk_text_check_cursor_blink (GtkText *self);
@ -540,6 +542,10 @@ static void begin_change (GtkText *self);
static void end_change (GtkText *self);
static void emit_changed (GtkText *self);
static void gtk_text_add_context_actions (GtkText *self);
static void gtk_text_update_clipboard_actions (GtkText *self);
static void gtk_text_update_emoji_action (GtkText *self);
/* GtkTextContent implementation
*/
@ -859,20 +865,7 @@ gtk_text_class_init (GtkTextClass *class)
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkText:populate-all:
*
* If :populate-all is %TRUE, the #GtkText::populate-popup
* signal is also emitted for touch popups.
*/
text_props[PROP_POPULATE_ALL] =
g_param_spec_boolean ("populate-all",
P_("Populate all"),
P_("Whether to emit ::populate-popup for touch popups"),
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkText::tabs:
* GtkText:tabs:
*
* A list of tabstops to apply to the text of the self.
*/
@ -904,38 +897,23 @@ gtk_text_class_init (GtkTextClass *class)
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkText:extra-menu:
*
* A menu model whose contents will be appended to
* the context menu.
*/
text_props[PROP_EXTRA_MENU] =
g_param_spec_object ("extra-menu",
P_("Extra menu"),
P_("Menu model to append to the context menu"),
G_TYPE_MENU_MODEL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, text_props);
gtk_editable_install_properties (gobject_class, NUM_PROPERTIES);
/**
* GtkText::populate-popup:
* @self: The self on which the signal is emitted
* @widget: the container that is being populated
*
* The ::populate-popup signal gets emitted before showing the
* context menu of the self.
*
* If you need to add items to the context menu, connect
* to this signal and append your items to the @widget, which
* will be a #GtkMenu in this case.
*
* If #GtkText:populate-all is %TRUE, this signal will
* also be emitted to populate touch popups. In this case,
* @widget will be a different container, e.g. a #GtkToolbar.
* The signal handler should not make assumptions about the
* type of @widget.
*/
signals[POPULATE_POPUP] =
g_signal_new (I_("populate-popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkTextClass, populate_popup),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
/* Action signals */
/**
@ -1505,14 +1483,6 @@ gtk_text_set_property (GObject *object,
gtk_text_set_attributes (self, g_value_get_boxed (value));
break;
case PROP_POPULATE_ALL:
if (priv->populate_all != g_value_get_boolean (value))
{
priv->populate_all = g_value_get_boolean (value);
g_object_notify_by_pspec (object, pspec);
}
break;
case PROP_TABS:
gtk_text_set_tabs (self, g_value_get_boxed (value));
break;
@ -1530,6 +1500,10 @@ gtk_text_set_property (GObject *object,
}
break;
case PROP_EXTRA_MENU:
gtk_text_set_extra_menu (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1633,10 +1607,6 @@ gtk_text_get_property (GObject *object,
g_value_set_boxed (value, priv->attrs);
break;
case PROP_POPULATE_ALL:
g_value_set_boolean (value, priv->populate_all);
break;
case PROP_TABS:
g_value_set_boxed (value, priv->tabs);
break;
@ -1649,6 +1619,10 @@ gtk_text_get_property (GObject *object,
g_value_set_boolean (value, priv->propagate_text_width);
break;
case PROP_EXTRA_MENU:
g_value_set_object (value, priv->extra_menu);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1746,6 +1720,7 @@ gtk_text_init (GtkText *self)
}
set_text_cursor (GTK_WIDGET (self));
gtk_text_add_context_actions (self);
}
static void
@ -1791,6 +1766,10 @@ gtk_text_dispose (GObject *object)
keymap = gdk_display_get_keymap (gtk_widget_get_display (GTK_WIDGET (object)));
g_signal_handlers_disconnect_by_func (keymap, keymap_direction_changed, self);
g_clear_object (&priv->context_actions);
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_clear_object (&priv->extra_menu);
G_OBJECT_CLASS (gtk_text_parent_class)->dispose (object);
}
@ -2056,12 +2035,6 @@ gtk_text_unrealize (GtkWidget *widget)
if (gdk_clipboard_get_content (clipboard) == priv->selection_content)
gdk_clipboard_set_content (clipboard, NULL);
if (priv->popup_menu)
{
gtk_widget_destroy (priv->popup_menu);
priv->popup_menu = NULL;
}
GTK_WIDGET_CLASS (gtk_text_parent_class)->unrealize (widget);
}
@ -2200,6 +2173,9 @@ gtk_text_size_allocate (GtkWidget *widget,
if (priv->magnifier_popover)
gtk_native_check_resize (GTK_NATIVE (priv->magnifier_popover));
if (priv->popup_menu)
gtk_native_check_resize (GTK_NATIVE (priv->popup_menu));
}
static void
@ -2448,6 +2424,40 @@ gesture_get_current_point_in_layout (GtkGestureSingle *gesture,
*y = py - ty;
}
static void
gtk_text_do_popup (GtkText *self,
double x,
double y)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
gtk_text_update_clipboard_actions (self);
if (!priv->popup_menu)
{
GMenuModel *model;
model = gtk_text_get_menu_model (self);
priv->popup_menu = gtk_popover_menu_new_from_model (GTK_WIDGET (self), model);
gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM);
gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE);
gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START);
g_object_unref (model);
}
if (x != -1 && y != -1)
{
GdkRectangle rect = { x, y, 1, 1 };
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &rect);
}
else
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), NULL);
gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
}
static void
gtk_text_click_gesture_pressed (GtkGestureClick *gesture,
int n_press,
@ -2483,7 +2493,7 @@ gtk_text_click_gesture_pressed (GtkGestureClick *gesture,
if (gdk_event_triggers_context_menu (event))
{
gtk_text_do_popup (self, event);
gtk_text_do_popup (self, x, y);
}
else if (n_press == 1 && button == GDK_BUTTON_MIDDLE &&
get_middle_click_paste (self))
@ -5251,10 +5261,15 @@ gtk_text_set_visibility (GtkText *self,
if (priv->visible != visible)
{
GAction *action;
priv->visible = visible;
g_object_notify (G_OBJECT (self), "visibility");
gtk_text_recompute (self);
action = g_action_map_lookup_action (priv->context_actions, "toggle-visibility");
g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible));
}
}
@ -5590,18 +5605,213 @@ gtk_text_set_alignment (GtkText *self,
}
}
/* Quick hack of a popup menu
*/
static void
activate_cb (GtkWidget *menuitem,
GtkText *self)
hide_selection_bubble (GtkText *self)
{
const char *signal;
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
g_signal_emit_by_name (self, signal);
if (priv->selection_bubble && gtk_widget_get_visible (priv->selection_bubble))
gtk_widget_hide (priv->selection_bubble);
}
static void
cut_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "cut-clipboard");
hide_selection_bubble (GTK_TEXT (user_data));
}
static void
copy_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "copy-clipboard");
hide_selection_bubble (GTK_TEXT (user_data));
}
static void
paste_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "paste-clipboard");
hide_selection_bubble (GTK_TEXT (user_data));
}
static void
delete_selection_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_text_delete_cb (GTK_TEXT (user_data));
hide_selection_bubble (GTK_TEXT (user_data));
}
static void
select_all_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_text_select_all (GTK_TEXT (user_data));
}
static void
insert_emoji_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_text_insert_emoji (GTK_TEXT (user_data));
hide_selection_bubble (GTK_TEXT (user_data));
}
static void
toggle_visibility (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkText *text = GTK_TEXT (user_data);
gtk_text_set_visibility (text, !gtk_text_get_visibility (text));
}
static void
gtk_text_add_context_actions (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GActionEntry entries[] = {
{ "cut-clipboard", cut_clipboard_activated, NULL, NULL, NULL },
{ "copy-clipboard", copy_clipboard_activated, NULL, NULL, NULL },
{ "paste-clipboard", paste_clipboard_activated, NULL, NULL, NULL },
{ "delete-selection", delete_selection_activated, NULL, NULL, NULL },
{ "select-all", select_all_activated, NULL, NULL, NULL },
{ "insert-emoji", insert_emoji_activated, NULL, NULL, NULL },
{ "toggle-visibility", toggle_visibility, NULL, "true", NULL },
};
GSimpleActionGroup *actions = g_simple_action_group_new ();
GAction *action;
priv->context_actions = G_ACTION_MAP (actions);
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), self);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "cut-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "paste-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "delete-selection");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "insert-emoji");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
gtk_widget_insert_action_group (GTK_WIDGET (self), "context", G_ACTION_GROUP (actions));
}
static void
gtk_text_update_clipboard_actions (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
DisplayMode mode;
GdkClipboard *clipboard;
gboolean has_clipboard;
GAction *action;
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (self));
has_clipboard = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), G_TYPE_STRING);
mode = gtk_text_get_display_mode (self);
action = g_action_map_lookup_action (priv->context_actions, "cut-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
mode == DISPLAY_NORMAL &&
priv->editable &&
priv->current_pos != priv->selection_bound);
action = g_action_map_lookup_action (priv->context_actions, "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
mode == DISPLAY_NORMAL &&
priv->current_pos != priv->selection_bound);
action = g_action_map_lookup_action (priv->context_actions, "paste-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
priv->editable && has_clipboard);
action = g_action_map_lookup_action (priv->context_actions, "delete-selection");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
priv->editable &&
priv->current_pos != priv->selection_bound);
action = g_action_map_lookup_action (priv->context_actions, "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
priv->buffer && (gtk_entry_buffer_get_length (priv->buffer) > 0));
}
static void
gtk_text_update_emoji_action (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GAction *action;
action = g_action_map_lookup_action (priv->context_actions, "insert-emoji");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
(gtk_text_get_input_hints (self) & GTK_INPUT_HINT_NO_EMOJI) == 0);
}
static GMenuModel *
gtk_text_get_menu_model (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GMenu *menu, *section;
GMenuItem *item;
menu = g_menu_new ();
section = g_menu_new ();
item = g_menu_item_new (_("Cu_t"), "context.cut-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-cut-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Copy"), "context.copy-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-copy-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Paste"), "context.paste-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-paste-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Delete"), "context.delete-selection");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-delete-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
section = g_menu_new ();
item = g_menu_item_new (_("Select _All"), "context.select-all");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-select-all-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new ( _("Insert _Emoji"), "context.insert-emoji");
g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
g_menu_item_set_attribute (item, "touch-icon", "s", "face-smile-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
if (priv->extra_menu)
g_menu_append_section (menu, NULL, priv->extra_menu);
return G_MENU_MODEL (menu);
}
static gboolean
gtk_text_mnemonic_activate (GtkWidget *widget,
@ -5611,132 +5821,11 @@ gtk_text_mnemonic_activate (GtkWidget *widget,
return GDK_EVENT_STOP;
}
static void
append_action_signal (GtkText *self,
GtkWidget *menu,
const char *label,
const char *signal,
gboolean sensitive)
{
GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (label);
g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
g_signal_connect (menuitem, "activate",
G_CALLBACK (activate_cb), self);
gtk_widget_set_sensitive (menuitem, sensitive);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
static void
popup_menu_detach (GtkWidget *attach_widget,
GtkMenu *menu)
{
GtkText *self_attach = GTK_TEXT (attach_widget);
GtkTextPrivate *priv_attach = gtk_text_get_instance_private (self_attach);
priv_attach->popup_menu = NULL;
}
static void
gtk_text_do_popup (GtkText *self,
const GdkEvent *event)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
GdkEvent *trigger_event;
/* In order to know what entries we should make sensitive, we
* ask for the current targets of the clipboard, and when
* we get them, then we actually pop up the menu.
*/
trigger_event = event ? gdk_event_copy (event) : gtk_get_current_event ();
if (gtk_widget_get_realized (GTK_WIDGET (self)))
{
DisplayMode mode;
gboolean clipboard_contains_text;
GtkWidget *menu;
GtkWidget *menuitem;
clipboard_contains_text = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (gtk_widget_get_clipboard (GTK_WIDGET (self))),
G_TYPE_STRING);
if (priv->popup_menu)
gtk_widget_destroy (priv->popup_menu);
priv->popup_menu = menu = gtk_menu_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (menu),
GTK_STYLE_CLASS_CONTEXT_MENU);
gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), popup_menu_detach);
mode = gtk_text_get_display_mode (self);
append_action_signal (self, menu, _("Cu_t"), "cut-clipboard",
priv->editable && mode == DISPLAY_NORMAL &&
priv->current_pos != priv->selection_bound);
append_action_signal (self, menu, _("_Copy"), "copy-clipboard",
mode == DISPLAY_NORMAL &&
priv->current_pos != priv->selection_bound);
append_action_signal (self, menu, _("_Paste"), "paste-clipboard",
priv->editable && clipboard_contains_text);
menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
gtk_widget_set_sensitive (menuitem, priv->editable && priv->current_pos != priv->selection_bound);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_text_delete_cb), self);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
gtk_widget_set_sensitive (menuitem, gtk_entry_buffer_get_length (priv->buffer) > 0);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_text_select_all), self);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
if ((gtk_text_get_input_hints (self) & GTK_INPUT_HINT_NO_EMOJI) == 0)
{
menuitem = gtk_menu_item_new_with_mnemonic (_("Insert _Emoji"));
gtk_widget_set_sensitive (menuitem,
mode == DISPLAY_NORMAL &&
priv->editable);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_text_insert_emoji), self);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
}
g_signal_emit (self, signals[POPULATE_POPUP], 0, menu);
if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
else
{
gtk_menu_popup_at_widget (GTK_MENU (menu),
GTK_WIDGET (self),
GDK_GRAVITY_SOUTH_EAST,
GDK_GRAVITY_NORTH_WEST,
trigger_event);
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
}
}
g_clear_object (&trigger_event);
}
static gboolean
gtk_text_popup_menu (GtkWidget *widget)
{
gtk_text_do_popup (GTK_TEXT (widget), NULL);
return GDK_EVENT_STOP;
gtk_text_do_popup (GTK_TEXT (widget), -1, -1);
return TRUE;
}
static void
@ -5766,40 +5855,56 @@ show_or_hide_handles (GtkWidget *popover,
}
static void
activate_bubble_cb (GtkWidget *item,
GtkText *self)
append_bubble_item (GtkText *self,
GtkWidget *toolbar,
GMenuModel *model,
int index)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
const char *signal;
signal = g_object_get_qdata (G_OBJECT (item), quark_gtk_signal);
gtk_widget_hide (priv->selection_bubble);
if (strcmp (signal, "select-all") == 0)
gtk_text_select_all (self);
else
g_signal_emit_by_name (self, signal);
}
static void
append_bubble_action (GtkText *self,
GtkWidget *toolbar,
const char *label,
const char *icon_name,
const char *signal,
gboolean sensitive)
{
GtkWidget *item, *image;
GVariant *att;
const char *icon_name;
const char *action_name;
GAction *action;
GMenuModel *link;
link = g_menu_model_get_item_link (model, index, "section");
if (link)
{
int i;
for (i = 0; i < g_menu_model_get_n_items (link); i++)
append_bubble_item (self, toolbar, link, i);
g_object_unref (link);
return;
}
att = g_menu_model_get_item_attribute_value (model, index, "touch-icon", G_VARIANT_TYPE_STRING);
if (att == NULL)
return;
icon_name = g_variant_get_string (att, NULL);
g_variant_unref (att);
att = g_menu_model_get_item_attribute_value (model, index, "action", G_VARIANT_TYPE_STRING);
if (att == NULL)
return;
action_name = g_variant_get_string (att, NULL);
g_variant_unref (att);
if (g_str_has_prefix (action_name, "context."))
{
action = g_action_map_lookup_action (priv->context_actions, action_name + strlen ("context."));
if (action && !g_action_get_enabled (action))
return;
}
item = gtk_button_new ();
gtk_widget_set_focus_on_click (item, FALSE);
image = gtk_image_new_from_icon_name (icon_name);
gtk_widget_show (image);
gtk_container_add (GTK_CONTAINER (item), image);
gtk_widget_set_tooltip_text (item, label);
gtk_style_context_add_class (gtk_widget_get_style_context (item), "image-button");
g_object_set_qdata (G_OBJECT (item), quark_gtk_signal, (char *)signal);
g_signal_connect (item, "clicked", G_CALLBACK (activate_bubble_cb), self);
gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
gtk_widget_show (GTK_WIDGET (item));
gtk_container_add (GTK_CONTAINER (toolbar), item);
}
@ -5811,21 +5916,19 @@ gtk_text_selection_bubble_popup_show (gpointer user_data)
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
cairo_rectangle_int_t rect;
GtkAllocation allocation;
int start_x, end_x;
gboolean has_selection;
gboolean has_clipboard;
gboolean all_selected;
DisplayMode mode;
int start_x, end_x;
GtkWidget *box;
GtkWidget *toolbar;
int length;
GtkAllocation text_allocation;
GMenuModel *model;
int i;
gtk_text_update_clipboard_actions (self);
gtk_text_get_text_allocation (self, &text_allocation);
has_selection = priv->selection_bound != priv->current_pos;
length = gtk_entry_buffer_get_length (get_buffer (self));
all_selected = (priv->selection_bound == 0) && (priv->current_pos == length);
if (!has_selection && !priv->editable)
{
@ -5851,24 +5954,12 @@ gtk_text_selection_bubble_popup_show (gpointer user_data)
gtk_container_add (GTK_CONTAINER (priv->selection_bubble), box);
gtk_container_add (GTK_CONTAINER (box), toolbar);
has_clipboard = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (gtk_widget_get_clipboard (GTK_WIDGET (self))),
G_TYPE_STRING);
mode = gtk_text_get_display_mode (self);
model = gtk_text_get_menu_model (self);
if (priv->editable && has_selection && mode == DISPLAY_NORMAL)
append_bubble_action (self, toolbar, _("Select all"), "edit-select-all-symbolic", "select-all", !all_selected);
for (i = 0; i < g_menu_model_get_n_items (model); i++)
append_bubble_item (self, toolbar, model, i);
if (priv->editable && has_selection && mode == DISPLAY_NORMAL)
append_bubble_action (self, toolbar, _("Cut"), "edit-cut-symbolic", "cut-clipboard", TRUE);
if (has_selection && mode == DISPLAY_NORMAL)
append_bubble_action (self, toolbar, _("Copy"), "edit-copy-symbolic", "copy-clipboard", TRUE);
if (priv->editable)
append_bubble_action (self, toolbar, _("Paste"), "edit-paste-symbolic", "paste-clipboard", has_clipboard);
if (priv->populate_all)
g_signal_emit (self, signals[POPULATE_POPUP], 0, box);
g_object_unref (model);
gtk_widget_get_allocation (GTK_WIDGET (self), &allocation);
@ -5944,7 +6035,7 @@ gtk_text_drag_begin (GtkWidget *widget,
text = _gtk_text_get_selected_text (self);
if (text)
if (self)
{
int *ranges, n_ranges;
GdkPaintable *paintable;
@ -6492,6 +6583,7 @@ gtk_text_set_input_hints (GtkText *self,
NULL);
g_object_notify_by_pspec (G_OBJECT (self), text_props[PROP_INPUT_HINTS]);
gtk_text_update_emoji_action (self);
}
}
@ -6686,3 +6778,45 @@ gtk_text_get_key_controller (GtkText *self)
return priv->key_controller;
}
/**
* gtk_text_set_extra_menu:
* @self: a #GtkText
* @model: (allow-none): a #GMenuModel
*
* Sets a menu model to add when constructing
* the context menu for @self.
*/
void
gtk_text_set_extra_menu (GtkText *self,
GMenuModel *model)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
g_return_if_fail (GTK_IS_TEXT (self));
if (g_set_object (&priv->extra_menu, model))
{
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_object_notify_by_pspec (G_OBJECT (self), text_props[PROP_EXTRA_MENU]);
}
}
/**
* gtk_text_get_extra_menu:
* @self: a #GtkText
*
* Gets the menu model set with gtk_text_set_extra_menu().
*
* Returns: (transfer none): (nullable): the menu model
*/
GMenuModel *
gtk_text_get_extra_menu (GtkText *self)
{
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
g_return_val_if_fail (GTK_IS_TEXT (self), NULL);
return priv->extra_menu;
}

View File

@ -134,6 +134,12 @@ PangoTabArray * gtk_text_get_tabs (GtkText *self);
GDK_AVAILABLE_IN_ALL
void gtk_text_grab_focus_without_selecting (GtkText *self);
GDK_AVAILABLE_IN_ALL
void gtk_text_set_extra_menu (GtkText *self,
GMenuModel *model);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_text_get_extra_menu (GtkText *self);
G_END_DECLS
#endif /* __GTK_TEXT_H__ */

View File

@ -34,9 +34,6 @@ typedef struct _GtkTextClass GtkTextClass;
/*<private>
* GtkTextClass:
* @parent_class: The parent class.
* @populate_popup: Class handler for the #GtkText::populate-popup signal. If
* non-%NULL, this will be called to add additional entries to the context
* menu when it is displayed.
* @activate: Class handler for the #GtkText::activate signal. The default
* implementation activates the gtk.activate-default action.
* @move_cursor: Class handler for the #GtkText::move-cursor signal. The
@ -70,10 +67,6 @@ struct _GtkTextClass
{
GtkWidgetClass parent_class;
/* Hook to customize right-click popup */
void (* populate_popup) (GtkText *self,
GtkWidget *popup);
/* Action signals
*/
void (* activate) (GtkText *self);

View File

@ -58,6 +58,8 @@
#include "gtkemojichooser.h"
#include "gtkpango.h"
#include "gtknative.h"
#include "gtkwidgetprivate.h"
#include "gtkactionmuxerprivate.h"
#include "a11y/gtktextviewaccessibleprivate.h"
@ -180,6 +182,8 @@ struct _GtkTextViewPrivate
GtkAdjustment *hadjustment;
GtkAdjustment *vadjustment;
GActionMap *context_actions;
/* X offset between widget coordinates and buffer coordinates
* taking left_padding in account
*/
@ -219,6 +223,7 @@ struct _GtkTextViewPrivate
GtkIMContext *im_context;
GtkWidget *popup_menu;
GMenuModel *extra_menu;
GSList *children;
@ -273,7 +278,6 @@ struct _GtkTextViewPrivate
guint vscroll_policy : 1;
guint cursor_handle_dragged : 1;
guint selection_handle_dragged : 1;
guint populate_all : 1;
};
struct _GtkTextPendingScroll
@ -294,7 +298,6 @@ typedef enum
enum
{
POPULATE_POPUP,
MOVE_CURSOR,
PAGE_HORIZONTALLY,
SET_ANCHOR,
@ -340,8 +343,8 @@ enum
PROP_VSCROLL_POLICY,
PROP_INPUT_PURPOSE,
PROP_INPUT_HINTS,
PROP_POPULATE_ALL,
PROP_MONOSPACE
PROP_MONOSPACE,
PROP_EXTRA_MENU
};
static GQuark quark_text_selection_data = 0;
@ -443,7 +446,6 @@ static void gtk_text_view_drag_data_received (GtkWidget *widget,
GtkSelectionData *selection_data);
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
static void gtk_text_view_move_cursor (GtkTextView *text_view,
GtkMovementStep step,
gint count,
@ -593,6 +595,10 @@ static void extend_selection (GtkTextView *text_view,
GtkTextIter *start,
GtkTextIter *end);
static void gtk_text_view_add_context_actions (GtkTextView *text_view);
static void gtk_text_view_update_clipboard_actions (GtkTextView *text_view);
static void gtk_text_view_update_emoji_action (GtkTextView *text_view);
/* FIXME probably need the focus methods. */
@ -958,22 +964,9 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
GTK_INPUT_HINT_NONE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/**
* GtkTextView:populate-all:
*
* If :populate-all is %TRUE, the #GtkTextView::populate-popup
* signal is also emitted for touch popups.
*/
g_object_class_install_property (gobject_class,
PROP_POPULATE_ALL,
g_param_spec_boolean ("populate-all",
P_("Populate all"),
P_("Whether to emit ::populate-popup for touch popups"),
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/**
* GtkTextview:monospace:
* GtkTextView:monospace:
*
* If %TRUE, set the %GTK_STYLE_CLASS_MONOSPACE style class on the
* text view to indicate that a monospace font is desired.
@ -986,7 +979,13 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
FALSE,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
g_object_class_install_property (gobject_class,
PROP_EXTRA_MENU,
g_param_spec_object ("extra-menu",
P_("Extra menu"),
P_("Menu model to append to the context menu"),
G_TYPE_MENU_MODEL,
GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
/* GtkScrollable interface */
g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
@ -1244,36 +1243,6 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
NULL,
G_TYPE_NONE, 0);
/**
* GtkTextView::populate-popup:
* @text_view: The text view on which the signal is emitted
* @popup: the container that is being populated
*
* The ::populate-popup signal gets emitted before showing the
* context menu of the text view.
*
* If you need to add items to the context menu, connect
* to this signal and append your items to the @popup, which
* will be a #GtkMenu in this case.
*
* If #GtkTextView:populate-all is %TRUE, this signal will
* also be emitted to populate touch popups. In this case,
* @popup will be a different container, e.g. a #GtkToolbar.
*
* The signal handler should not make assumptions about the
* type of @widget, but check whether @popup is a #GtkMenu
* or #GtkToolbar or another kind of container.
*/
signals[POPULATE_POPUP] =
g_signal_new (I_("populate-popup"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkTextViewClass, populate_popup),
NULL, NULL,
NULL,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
/**
* GtkTextView::select-all:
* @text_view: the object which received the signal
@ -1725,6 +1694,8 @@ gtk_text_view_init (GtkTextView *text_view)
gtk_css_node_get_state (priv->text_window->css_node) & ~GTK_STATE_FLAG_DROP_ACTIVE);
gtk_css_node_set_visible (priv->selection_node, FALSE);
g_object_unref (priv->selection_node);
gtk_text_view_add_context_actions (text_view);
}
GtkCssNode *
@ -3645,6 +3616,10 @@ gtk_text_view_finalize (GObject *object)
g_free (priv->im_module);
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_clear_object (&priv->context_actions);
g_clear_object (&priv->extra_menu);
G_OBJECT_CLASS (gtk_text_view_parent_class)->finalize (object);
}
@ -3767,17 +3742,14 @@ gtk_text_view_set_property (GObject *object,
gtk_text_view_set_input_hints (text_view, g_value_get_flags (value));
break;
case PROP_POPULATE_ALL:
if (text_view->priv->populate_all != g_value_get_boolean (value))
{
text_view->priv->populate_all = g_value_get_boolean (value);
g_object_notify_by_pspec (object, pspec);
}
break;
case PROP_MONOSPACE:
gtk_text_view_set_monospace (text_view, g_value_get_boolean (value));
break;
case PROP_EXTRA_MENU:
gtk_text_view_set_extra_menu (text_view, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -3890,14 +3862,14 @@ gtk_text_view_get_property (GObject *object,
g_value_set_flags (value, gtk_text_view_get_input_hints (text_view));
break;
case PROP_POPULATE_ALL:
g_value_set_boolean (value, priv->populate_all);
break;
case PROP_MONOSPACE:
g_value_set_boolean (value, gtk_text_view_get_monospace (text_view));
break;
case PROP_EXTRA_MENU:
g_value_set_object (value, gtk_text_view_get_extra_menu (text_view));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -4191,6 +4163,9 @@ gtk_text_view_size_allocate (GtkWidget *widget,
if (priv->magnifier_popover)
gtk_native_check_resize (GTK_NATIVE (priv->magnifier_popover));
if (priv->popup_menu)
gtk_native_check_resize (GTK_NATIVE (priv->popup_menu));
}
static void
@ -4500,11 +4475,7 @@ gtk_text_view_unrealize (GtkWidget *widget)
gtk_text_view_remove_validate_idles (text_view);
if (priv->popup_menu)
{
gtk_widget_destroy (priv->popup_menu);
priv->popup_menu = NULL;
}
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
gtk_im_context_set_client_widget (priv->im_context, NULL);
@ -8458,35 +8429,40 @@ gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
text_view->priv->virtual_cursor_y = (y == -1) ? pos.y + pos.height / 2 : y;
}
/* Quick hack of a popup menu
*/
static void
activate_cb (GtkWidget *menuitem,
GtkTextView *text_view)
hide_selection_bubble (GtkTextView *text_view)
{
const gchar *signal;
GtkTextViewPrivate *priv = text_view->priv;
signal = g_object_get_qdata (G_OBJECT (menuitem), quark_gtk_signal);
g_signal_emit_by_name (text_view, signal);
if (priv->selection_bubble && gtk_widget_get_visible (priv->selection_bubble))
gtk_widget_hide (priv->selection_bubble);
}
static void
append_action_signal (GtkTextView *text_view,
GtkWidget *menu,
const gchar *label,
const gchar *signal,
gboolean sensitive)
cut_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkWidget *menuitem = gtk_menu_item_new_with_mnemonic (label);
g_signal_emit_by_name (user_data, "cut-clipboard");
hide_selection_bubble (GTK_TEXT_VIEW (user_data));
}
g_object_set_qdata (G_OBJECT (menuitem), quark_gtk_signal, (char *)signal);
g_signal_connect (menuitem, "activate",
G_CALLBACK (activate_cb), text_view);
static void
copy_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "copy-clipboard");
hide_selection_bubble (GTK_TEXT_VIEW (user_data));
}
gtk_widget_set_sensitive (menuitem, sensitive);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
static void
paste_clipboard_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
g_signal_emit_by_name (user_data, "paste-clipboard");
hide_selection_bubble (GTK_TEXT_VIEW (user_data));
}
static void
@ -8512,24 +8488,32 @@ gtk_text_view_select_all (GtkWidget *widget,
}
static void
select_all_cb (GtkWidget *menuitem,
GtkTextView *text_view)
select_all_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkTextView *text_view = user_data;
gtk_text_view_select_all (GTK_WIDGET (text_view), TRUE);
}
static void
delete_cb (GtkTextView *text_view)
delete_selection_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkTextView *text_view = user_data;
gtk_text_buffer_delete_selection (get_buffer (text_view), TRUE,
text_view->priv->editable);
}
static void
popup_menu_detach (GtkWidget *attach_widget,
GtkMenu *menu)
insert_emoji_activated (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GTK_TEXT_VIEW (attach_widget)->priv->popup_menu = NULL;
gtk_text_view_insert_emoji (GTK_TEXT_VIEW (user_data));
}
static gboolean
@ -8550,6 +8534,147 @@ range_contains_editable_text (const GtkTextIter *start,
return FALSE;
}
static void
gtk_text_view_add_context_actions (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
GActionEntry entries[] = {
{ "cut-clipboard", cut_clipboard_activated, NULL, NULL, NULL },
{ "copy-clipboard", copy_clipboard_activated, NULL, NULL, NULL },
{ "paste-clipboard", paste_clipboard_activated, NULL, NULL, NULL },
{ "delete-selection", delete_selection_activated, NULL, NULL, NULL },
{ "select-all", select_all_activated, NULL, NULL, NULL },
{ "insert-emoji", insert_emoji_activated, NULL, NULL, NULL },
};
GSimpleActionGroup *actions = g_simple_action_group_new ();
GAction *action;
priv->context_actions = G_ACTION_MAP (actions);
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), text_view);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "cut-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "paste-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "delete-selection");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
action = g_action_map_lookup_action (G_ACTION_MAP (actions), "insert-emoji");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
gtk_widget_insert_action_group (GTK_WIDGET (text_view), "context", G_ACTION_GROUP (actions));
}
static void
gtk_text_view_update_clipboard_actions (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
GdkClipboard *clipboard;
gboolean have_selection;
gboolean can_paste, can_insert;
GAction *action;
GtkTextIter iter, sel_start, sel_end;
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
can_paste = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), G_TYPE_STRING);
have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
&sel_start, &sel_end);
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
&iter,
gtk_text_buffer_get_insert (get_buffer (text_view)));
can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
action = g_action_map_lookup_action (priv->context_actions, "cut-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
have_selection &&
range_contains_editable_text (&sel_start, &sel_end, priv->editable));
action = g_action_map_lookup_action (priv->context_actions, "copy-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), have_selection);
action = g_action_map_lookup_action (priv->context_actions, "paste-clipboard");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_insert && can_paste);
action = g_action_map_lookup_action (priv->context_actions, "delete-selection");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
have_selection &&
range_contains_editable_text (&sel_start, &sel_end, priv->editable));
action = g_action_map_lookup_action (priv->context_actions, "select-all");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
gtk_text_buffer_get_char_count (priv->buffer) > 0);
}
static void
gtk_text_view_update_emoji_action (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
GAction *action;
action = g_action_map_lookup_action (priv->context_actions, "insert-emoji");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
(gtk_text_view_get_input_hints (text_view) & GTK_INPUT_HINT_NO_EMOJI) == 0);
}
static GMenuModel *
gtk_text_view_get_menu_model (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
GMenu *menu, *section;
GMenuItem *item;
menu = g_menu_new ();
section = g_menu_new ();
item = g_menu_item_new (_("Cu_t"), "context.cut-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-cut-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Copy"), "context.copy-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-copy-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Paste"), "context.paste-clipboard");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-paste-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new (_("_Delete"), "context.delete-selection");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-delete-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
section = g_menu_new ();
item = g_menu_item_new (_("Select _All"), "context.select-all");
g_menu_item_set_attribute (item, "touch-icon", "s", "edit-select-all-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
item = g_menu_item_new ( _("Insert _Emoji"), "context.insert-emoji");
g_menu_item_set_attribute (item, "hidden-when", "s", "action-disabled");
g_menu_item_set_attribute (item, "touch-icon", "s", "face-smile-symbolic");
g_menu_append_item (section, item);
g_object_unref (item);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
g_object_unref (section);
if (priv->extra_menu)
g_menu_append_section (menu, NULL, priv->extra_menu);
return G_MENU_MODEL (menu);
}
static void
gtk_text_view_do_popup (GtkTextView *text_view,
const GdkEvent *event)
@ -8557,134 +8682,95 @@ gtk_text_view_do_popup (GtkTextView *text_view,
GtkTextViewPrivate *priv = text_view->priv;
GdkEvent *trigger_event;
if (!gtk_widget_get_realized (GTK_WIDGET (text_view)))
return;
if (event)
trigger_event = gdk_event_copy (event);
else
trigger_event = gtk_get_current_event ();
if (gtk_widget_get_realized (GTK_WIDGET (text_view)))
gtk_text_view_update_clipboard_actions (text_view);
if (!priv->popup_menu)
{
GMenuModel *model;
model = gtk_text_view_get_menu_model (text_view);
priv->popup_menu = gtk_popover_menu_new_from_model (GTK_WIDGET (text_view), model);
gtk_popover_set_position (GTK_POPOVER (priv->popup_menu), GTK_POS_BOTTOM);
gtk_popover_set_has_arrow (GTK_POPOVER (priv->popup_menu), FALSE);
gtk_widget_set_halign (priv->popup_menu, GTK_ALIGN_START);
g_object_unref (model);
}
if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
{
GdkDevice *device;
GdkRectangle rect = { 0, 0, 1, 1 };
device = gdk_event_get_device (trigger_event);
if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
device = gdk_device_get_associated_device (device);
if (device)
{
GdkSurface *surface;
double px, py;
surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (text_view)));
gdk_surface_get_device_position (surface, device, &px, &py, NULL);
rect.x = round (px);
rect.y = round (py);
gtk_widget_translate_coordinates (GTK_WIDGET (gtk_widget_get_native (GTK_WIDGET (text_view))),
GTK_WIDGET (text_view),
rect.x, rect.y,
&rect.x, &rect.y);
}
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &rect);
}
else
{
GtkWidget *menuitem;
gboolean have_selection;
gboolean can_insert, can_paste;
GtkTextIter iter;
GtkTextIter sel_start, sel_end;
GdkRectangle iter_location;
GdkRectangle visible_rect;
gboolean is_visible;
if (priv->popup_menu)
gtk_widget_destroy (priv->popup_menu);
gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
gtk_text_view_get_visible_rect (text_view, &visible_rect);
priv->popup_menu = gtk_menu_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (priv->popup_menu),
GTK_STYLE_CLASS_CONTEXT_MENU);
is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
iter_location.x < visible_rect.x + visible_rect.width &&
iter_location.y + iter_location.height > visible_rect.y &&
iter_location.y < visible_rect.y + visible_rect.height);
gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
GTK_WIDGET (text_view),
popup_menu_detach);
have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
&sel_start, &sel_end);
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
&iter,
gtk_text_buffer_get_insert (get_buffer (text_view)));
can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
can_paste = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (gtk_widget_get_clipboard (GTK_WIDGET (text_view))),
GTK_TYPE_TEXT_BUFFER);
append_action_signal (text_view, priv->popup_menu, _("Cu_t"), "cut-clipboard",
have_selection &&
range_contains_editable_text (&sel_start, &sel_end,
priv->editable));
append_action_signal (text_view, priv->popup_menu, _("_Copy"), "copy-clipboard",
have_selection);
append_action_signal (text_view, priv->popup_menu, _("_Paste"), "paste-clipboard",
can_insert && can_paste);
menuitem = gtk_menu_item_new_with_mnemonic (_("_Delete"));
gtk_widget_set_sensitive (menuitem,
have_selection &&
range_contains_editable_text (&sel_start, &sel_end,
priv->editable));
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (delete_cb), text_view);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
menuitem = gtk_separator_menu_item_new ();
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
menuitem = gtk_menu_item_new_with_mnemonic (_("Select _All"));
gtk_widget_set_sensitive (menuitem,
gtk_text_buffer_get_char_count (priv->buffer) > 0);
g_signal_connect (menuitem, "activate",
G_CALLBACK (select_all_cb), text_view);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
if ((gtk_text_view_get_input_hints (text_view) & GTK_INPUT_HINT_NO_EMOJI) == 0)
if (is_visible)
{
menuitem = gtk_menu_item_new_with_mnemonic (_("Insert _Emoji"));
gtk_widget_set_sensitive (menuitem, can_insert);
g_signal_connect_swapped (menuitem, "activate",
G_CALLBACK (gtk_text_view_insert_emoji), text_view);
gtk_widget_show (menuitem);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
}
gtk_text_view_buffer_to_surface_coords (text_view,
GTK_TEXT_WINDOW_WIDGET,
iter_location.x,
iter_location.y,
&iter_location.x,
&iter_location.y);
g_signal_emit (text_view, signals[POPULATE_POPUP],
0, priv->popup_menu);
if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), trigger_event);
else
{
gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
gtk_text_view_get_visible_rect (text_view, &visible_rect);
is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
iter_location.x < visible_rect.x + visible_rect.width &&
iter_location.y + iter_location.height > visible_rect.y &&
iter_location.y < visible_rect.y + visible_rect.height);
if (is_visible)
{
gtk_text_view_buffer_to_surface_coords (text_view,
GTK_TEXT_WINDOW_WIDGET,
iter_location.x,
iter_location.y,
&iter_location.x,
&iter_location.y);
gtk_menu_popup_at_rect (GTK_MENU (priv->popup_menu),
gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (text_view))),
&iter_location,
GDK_GRAVITY_SOUTH_EAST,
GDK_GRAVITY_NORTH_WEST,
trigger_event);
}
else
gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
GTK_WIDGET (text_view),
GDK_GRAVITY_CENTER,
GDK_GRAVITY_CENTER,
trigger_event);
gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
gtk_popover_set_pointing_to (GTK_POPOVER (priv->popup_menu), &iter_location);
}
}
gtk_popover_popup (GTK_POPOVER (priv->popup_menu));
g_clear_object (&trigger_event);
}
static gboolean
gtk_text_view_popup_menu (GtkWidget *widget)
{
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);
gtk_text_view_do_popup (GTK_TEXT_VIEW (widget), NULL);
return TRUE;
}
@ -8741,32 +8827,81 @@ show_or_hide_handles (GtkWidget *popover,
}
static void
activate_bubble_cb (GtkWidget *item,
GtkTextView *text_view)
append_bubble_item (GtkTextView *text_view,
GtkWidget *toolbar,
GMenuModel *model,
int index)
{
const gchar *signal;
GtkWidget *item, *image;
GVariant *att;
const char *icon_name;
const char *action_name;
GMenuModel *link;
char **split = NULL;
gboolean is_toggle_action = FALSE;
signal = g_object_get_qdata (G_OBJECT (item), quark_gtk_signal);
gtk_widget_hide (text_view->priv->selection_bubble);
g_signal_emit_by_name (text_view, signal);
}
link = g_menu_model_get_item_link (model, index, "section");
if (link)
{
int i;
for (i = 0; i < g_menu_model_get_n_items (link); i++)
append_bubble_item (text_view, toolbar, link, i);
g_object_unref (link);
return;
}
static void
append_bubble_action (GtkTextView *text_view,
GtkWidget *toolbar,
const gchar *label,
const gchar *icon_name,
const gchar *signal,
gboolean sensitive)
{
GtkWidget *item;
att = g_menu_model_get_item_attribute_value (model, index, "touch-icon", G_VARIANT_TYPE_STRING);
if (att == NULL)
return;
item = gtk_button_new_from_icon_name (icon_name);
icon_name = g_variant_get_string (att, NULL);
g_variant_unref (att);
att = g_menu_model_get_item_attribute_value (model, index, "action", G_VARIANT_TYPE_STRING);
if (att == NULL)
return;
action_name = g_variant_get_string (att, NULL);
g_variant_unref (att);
split = g_strsplit (action_name, ".", 2);
if (split[0] && split[1])
{
GActionGroup *group = NULL;
gboolean enabled;
const GVariantType *param_type;
const GVariantType *state_type;
GtkActionMuxer *muxer;
muxer = _gtk_widget_get_action_muxer (GTK_WIDGET (text_view), FALSE);
if (muxer)
group = gtk_action_muxer_lookup (muxer, split[0]);
if (group)
{
g_action_group_query_action (group, split[1], &enabled, &param_type, &state_type, NULL, NULL);
if (!enabled)
return;
if (param_type == NULL &&
state_type != NULL &&
g_variant_type_equal (state_type, G_VARIANT_TYPE_BOOLEAN))
is_toggle_action = TRUE;
}
}
g_strfreev (split);
if (is_toggle_action)
item = gtk_toggle_button_new ();
else
item = gtk_button_new ();
gtk_widget_set_focus_on_click (item, FALSE);
gtk_widget_set_tooltip_text (item, label);
g_object_set_qdata (G_OBJECT (item), quark_gtk_signal, (char *)signal);
g_signal_connect (item, "clicked", G_CALLBACK (activate_bubble_cb), text_view);
gtk_widget_set_sensitive (GTK_WIDGET (item), sensitive);
image = gtk_image_new_from_icon_name (icon_name);
gtk_widget_show (image);
gtk_container_add (GTK_CONTAINER (item), image);
gtk_style_context_add_class (gtk_widget_get_style_context (item), "image-button");
gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
gtk_widget_show (GTK_WIDGET (item));
gtk_container_add (GTK_CONTAINER (toolbar), item);
}
@ -8776,24 +8911,14 @@ gtk_text_view_selection_bubble_popup_show (gpointer user_data)
GtkTextView *text_view = user_data;
GtkTextViewPrivate *priv = text_view->priv;
cairo_rectangle_int_t rect;
GdkClipboard *clipboard;
gboolean has_selection;
gboolean has_clipboard;
gboolean can_insert;
gboolean all_selected;
GtkTextIter iter;
GtkTextIter sel_start, sel_end;
GtkTextIter start, end;
GtkWidget *box;
GtkWidget *toolbar;
GMenuModel *model;
int i;
gtk_text_view_update_clipboard_actions (text_view);
priv->selection_bubble_timeout_id = 0;
has_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
&sel_start, &sel_end);
gtk_text_buffer_get_bounds (get_buffer (text_view), &start, &end);
all_selected = gtk_text_iter_equal (&start, &sel_start) &&
gtk_text_iter_equal (&end, &sel_end);
g_clear_pointer (&priv->selection_bubble, gtk_widget_unparent);
@ -8813,25 +8938,12 @@ gtk_text_view_selection_bubble_popup_show (gpointer user_data)
gtk_container_add (GTK_CONTAINER (priv->selection_bubble), box);
gtk_container_add (GTK_CONTAINER (box), toolbar);
gtk_text_buffer_get_iter_at_mark (get_buffer (text_view), &iter,
gtk_text_buffer_get_insert (get_buffer (text_view)));
can_insert = gtk_text_iter_can_insert (&iter, priv->editable);
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view));
has_clipboard = gdk_content_formats_contain_gtype (gdk_clipboard_get_formats (clipboard), GTK_TYPE_TEXT_BUFFER);
model = gtk_text_view_get_menu_model (text_view);
append_bubble_action (text_view, toolbar, _("Select all"), "edit-select-all-symbolic", "select-all", !all_selected);
for (i = 0; i < g_menu_model_get_n_items (model); i++)
append_bubble_item (text_view, toolbar, model, i);
if (range_contains_editable_text (&sel_start, &sel_end, priv->editable) && has_selection)
append_bubble_action (text_view, toolbar, _("Cut"), "edit-cut-symbolic", "cut-clipboard", TRUE);
if (has_selection)
append_bubble_action (text_view, toolbar, _("Copy"), "edit-copy-symbolic", "copy-clipboard", TRUE);
if (can_insert)
append_bubble_action (text_view, toolbar, _("Paste"), "edit-paste-symbolic", "paste-clipboard", has_clipboard);
if (priv->populate_all)
g_signal_emit (text_view, signals[POPULATE_POPUP], 0, box);
g_object_unref (model);
gtk_text_view_get_selection_rect (text_view, &rect);
rect.x -= priv->xoffset;
@ -9782,6 +9894,7 @@ gtk_text_view_set_input_hints (GtkTextView *text_view,
NULL);
g_object_notify (G_OBJECT (text_view), "input-hints");
gtk_text_view_update_emoji_action (text_view);
}
}
@ -9891,3 +10004,44 @@ gtk_text_view_insert_emoji (GtkTextView *text_view)
gtk_popover_popup (GTK_POPOVER (chooser));
}
/**
* gtk_text_view_set_extra_menu:
* @text_view: a #GtkTextView
* @model: (allow-none): a #GMenuModel
*
* Sets a menu model to add when constructing
* the context menu for @text_view.
*/
void
gtk_text_view_set_extra_menu (GtkTextView *text_view,
GMenuModel *model)
{
GtkTextViewPrivate *priv = text_view->priv;
g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
if (g_set_object (&priv->extra_menu, model))
{
g_clear_pointer (&priv->popup_menu, gtk_widget_unparent);
g_object_notify (G_OBJECT (text_view), "extra-menu");
}
}
/**
* gtk_text_view_get_extra_menu:
* @text_view: a #GtkTextView
*
* Gets the menu model set with gtk_text_view_set_extra_menu().
*
* Returns: (transfer none): (nullable): the menu model
*/
GMenuModel *
gtk_text_view_get_extra_menu (GtkTextView *text_view)
{
GtkTextViewPrivate *priv = text_view->priv;
g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
return priv->extra_menu;
}

View File

@ -120,8 +120,6 @@ struct _GtkTextView
/**
* GtkTextViewClass:
* @parent_class: The object class structure needs to be the first
* @populate_popup: The class handler for the #GtkTextView::populate-popup
* signal.
* @move_cursor: The class handler for the #GtkTextView::move-cursor
* keybinding signal.
* @set_anchor: The class handler for the #GtkTextView::set-anchor
@ -159,8 +157,6 @@ struct _GtkTextViewClass
/*< public >*/
void (* populate_popup) (GtkTextView *text_view,
GtkWidget *popup);
void (* move_cursor) (GtkTextView *text_view,
GtkMovementStep step,
gint count,
@ -431,6 +427,12 @@ void gtk_text_view_set_monospace (GtkTextView *text_vi
GDK_AVAILABLE_IN_ALL
gboolean gtk_text_view_get_monospace (GtkTextView *text_view);
GDK_AVAILABLE_IN_ALL
void gtk_text_view_set_extra_menu (GtkTextView *text_view,
GMenuModel *model);
GDK_AVAILABLE_IN_ALL
GMenuModel * gtk_text_view_get_extra_menu (GtkTextView *text_view);
G_END_DECLS
#endif /* __GTK_TEXT_VIEW_H__ */

View File

@ -841,9 +841,6 @@ static void gtk_tree_view_search_window_hide (GtkWidget *searc
static void gtk_tree_view_search_position_func (GtkTreeView *tree_view,
GtkWidget *search_window,
gpointer user_data);
static void gtk_tree_view_search_disable_popdown (GtkEntry *entry,
GtkMenu *menu,
gpointer data);
static void gtk_tree_view_search_preedit_changed (GtkText *text,
const char *preedit,
GtkTreeView *tree_view);
@ -851,9 +848,6 @@ static void gtk_tree_view_search_changed (GtkEditable *edita
GtkTreeView *tree_view);
static void gtk_tree_view_search_activate (GtkEntry *entry,
GtkTreeView *tree_view);
static gboolean gtk_tree_view_real_search_enable_popdown(gpointer data);
static void gtk_tree_view_search_enable_popdown (GtkWidget *widget,
gpointer data);
static void gtk_tree_view_search_pressed_cb (GtkGesture *gesture,
int n_press,
double x,
@ -10235,8 +10229,6 @@ gtk_tree_view_ensure_interactive_directory (GtkTreeView *tree_view)
/* add entry */
tree_view->priv->search_entry = gtk_text_new ();
g_signal_connect (tree_view->priv->search_entry, "populate-popup",
G_CALLBACK (gtk_tree_view_search_disable_popdown), tree_view);
g_signal_connect (tree_view->priv->search_entry, "activate",
G_CALLBACK (gtk_tree_view_search_activate), tree_view);
g_signal_connect (tree_view->priv->search_entry, "preedit-changed",
@ -13791,18 +13783,6 @@ gtk_tree_view_search_position_func (GtkTreeView *tree_view,
{
}
static void
gtk_tree_view_search_disable_popdown (GtkEntry *entry,
GtkMenu *menu,
gpointer data)
{
GtkTreeView *tree_view = (GtkTreeView *)data;
tree_view->priv->disable_popdown = 1;
g_signal_connect (menu, "hide",
G_CALLBACK (gtk_tree_view_search_enable_popdown), data);
}
/* Because we're visible but offscreen, we just set a flag in the preedit
* callback.
*/
@ -13855,27 +13835,6 @@ gtk_tree_view_search_activate (GtkEntry *entry,
}
}
static gboolean
gtk_tree_view_real_search_enable_popdown (gpointer data)
{
GtkTreeView *tree_view = (GtkTreeView *)data;
tree_view->priv->disable_popdown = 0;
return FALSE;
}
static void
gtk_tree_view_search_enable_popdown (GtkWidget *widget,
gpointer data)
{
guint id = g_timeout_add_full (G_PRIORITY_HIGH, 200,
gtk_tree_view_real_search_enable_popdown,
g_object_ref (data),
g_object_unref);
g_source_set_name_by_id (id, "[gtk] gtk_tree_view_real_search_enable_popdown");
}
static void
gtk_tree_view_search_pressed_cb (GtkGesture *gesture,
int n_press,

2768
log Normal file

File diff suppressed because it is too large Load Diff