forked from AuroraMiddleware/gtk
Merge branch 'wip/chergert/fix-4094' into 'master'
joinedmenu: add helper to join menus instead of nesting Closes #4094 See merge request GNOME/gtk!3739
This commit is contained in:
commit
c85f3f4942
315
gtk/gtkjoinedmenu.c
Normal file
315
gtk/gtkjoinedmenu.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright © 2020 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkjoinedmenuprivate.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GMenuModel *model;
|
||||
gulong items_changed_handler;
|
||||
} Menu;
|
||||
|
||||
struct _GtkJoinedMenu
|
||||
{
|
||||
GMenuModel parent_instance;
|
||||
GArray *menus;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkJoinedMenu, gtk_joined_menu, G_TYPE_MENU_MODEL)
|
||||
|
||||
static void
|
||||
clear_menu (gpointer data)
|
||||
{
|
||||
Menu *menu = data;
|
||||
|
||||
g_clear_signal_handler (&menu->items_changed_handler, menu->model);
|
||||
g_clear_object (&menu->model);
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_joined_menu_get_offset_at_index (GtkJoinedMenu *self,
|
||||
gint index)
|
||||
{
|
||||
gint offset = 0;
|
||||
|
||||
for (guint i = 0; i < index; i++)
|
||||
offset += g_menu_model_get_n_items (g_array_index (self->menus, Menu, i).model);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_joined_menu_get_offset_at_model (GtkJoinedMenu *self,
|
||||
GMenuModel *model)
|
||||
{
|
||||
gint offset = 0;
|
||||
|
||||
for (guint i = 0; i < self->menus->len; i++)
|
||||
{
|
||||
const Menu *menu = &g_array_index (self->menus, Menu, i);
|
||||
|
||||
if (menu->model == model)
|
||||
break;
|
||||
|
||||
offset += g_menu_model_get_n_items (menu->model);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_joined_menu_is_mutable (GMenuModel *model)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gint
|
||||
gtk_joined_menu_get_n_items (GMenuModel *model)
|
||||
{
|
||||
GtkJoinedMenu *self = (GtkJoinedMenu *)model;
|
||||
|
||||
if (self->menus->len == 0)
|
||||
return 0;
|
||||
|
||||
return gtk_joined_menu_get_offset_at_index (self, self->menus->len);
|
||||
}
|
||||
|
||||
static const Menu *
|
||||
gtk_joined_menu_get_item (GtkJoinedMenu *self,
|
||||
gint *item_index)
|
||||
{
|
||||
g_assert (GTK_IS_JOINED_MENU (self));
|
||||
|
||||
for (guint i = 0; i < self->menus->len; i++)
|
||||
{
|
||||
const Menu *menu = &g_array_index (self->menus, Menu, i);
|
||||
gint n_items = g_menu_model_get_n_items (menu->model);
|
||||
|
||||
if (n_items > *item_index)
|
||||
return menu;
|
||||
|
||||
(*item_index) -= n_items;
|
||||
}
|
||||
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_get_item_attributes (GMenuModel *model,
|
||||
gint item_index,
|
||||
GHashTable **attributes)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attributes (menu->model, item_index, attributes);
|
||||
}
|
||||
|
||||
static GMenuAttributeIter *
|
||||
gtk_joined_menu_iterate_item_attributes (GMenuModel *model,
|
||||
gint item_index)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_attributes (menu->model, item_index);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
gtk_joined_menu_get_item_attribute_value (GMenuModel *model,
|
||||
gint item_index,
|
||||
const gchar *attribute,
|
||||
const GVariantType *expected_type)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attribute_value (menu->model, item_index, attribute, expected_type);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_get_item_links (GMenuModel *model,
|
||||
gint item_index,
|
||||
GHashTable **links)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
G_MENU_MODEL_GET_CLASS (menu->model)->get_item_links (menu->model, item_index, links);
|
||||
}
|
||||
|
||||
static GMenuLinkIter *
|
||||
gtk_joined_menu_iterate_item_links (GMenuModel *model,
|
||||
gint item_index)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_links (menu->model, item_index);
|
||||
}
|
||||
|
||||
static GMenuModel *
|
||||
gtk_joined_menu_get_item_link (GMenuModel *model,
|
||||
gint item_index,
|
||||
const gchar *link)
|
||||
{
|
||||
const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index);
|
||||
return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_link (menu->model, item_index, link);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_finalize (GObject *object)
|
||||
{
|
||||
GtkJoinedMenu *self = (GtkJoinedMenu *)object;
|
||||
|
||||
g_clear_pointer (&self->menus, g_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_joined_menu_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_class_init (GtkJoinedMenuClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_joined_menu_finalize;
|
||||
|
||||
menu_model_class->is_mutable = gtk_joined_menu_is_mutable;
|
||||
menu_model_class->get_n_items = gtk_joined_menu_get_n_items;
|
||||
menu_model_class->get_item_attributes = gtk_joined_menu_get_item_attributes;
|
||||
menu_model_class->iterate_item_attributes = gtk_joined_menu_iterate_item_attributes;
|
||||
menu_model_class->get_item_attribute_value = gtk_joined_menu_get_item_attribute_value;
|
||||
menu_model_class->get_item_links = gtk_joined_menu_get_item_links;
|
||||
menu_model_class->iterate_item_links = gtk_joined_menu_iterate_item_links;
|
||||
menu_model_class->get_item_link = gtk_joined_menu_get_item_link;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_init (GtkJoinedMenu *self)
|
||||
{
|
||||
self->menus = g_array_new (FALSE, FALSE, sizeof (Menu));
|
||||
g_array_set_clear_func (self->menus, clear_menu);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_on_items_changed (GtkJoinedMenu *self,
|
||||
guint offset,
|
||||
guint removed,
|
||||
guint added,
|
||||
GMenuModel *model)
|
||||
{
|
||||
g_assert (GTK_IS_JOINED_MENU (self));
|
||||
g_assert (G_IS_MENU_MODEL (model));
|
||||
|
||||
offset += gtk_joined_menu_get_offset_at_model (self, model);
|
||||
g_menu_model_items_changed (G_MENU_MODEL (self), offset, removed, added);
|
||||
}
|
||||
|
||||
GtkJoinedMenu *
|
||||
gtk_joined_menu_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_JOINED_MENU, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_joined_menu_insert (GtkJoinedMenu *self,
|
||||
GMenuModel *model,
|
||||
gint index)
|
||||
{
|
||||
Menu menu = { 0 };
|
||||
gint offset;
|
||||
gint n_items;
|
||||
|
||||
g_assert (GTK_IS_JOINED_MENU (self));
|
||||
g_assert (G_IS_MENU_MODEL (model));
|
||||
g_assert (index >= 0);
|
||||
g_assert (index <= self->menus->len);
|
||||
|
||||
menu.model = g_object_ref (model);
|
||||
menu.items_changed_handler =
|
||||
g_signal_connect_swapped (menu.model,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_joined_menu_on_items_changed),
|
||||
self);
|
||||
g_array_insert_val (self->menus, index, menu);
|
||||
|
||||
n_items = g_menu_model_get_n_items (model);
|
||||
offset = gtk_joined_menu_get_offset_at_index (self, index);
|
||||
g_menu_model_items_changed (G_MENU_MODEL (self), offset, 0, n_items);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_joined_menu_append_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model)
|
||||
{
|
||||
|
||||
g_return_if_fail (GTK_IS_JOINED_MENU (self));
|
||||
g_return_if_fail (G_MENU_MODEL (model));
|
||||
|
||||
gtk_joined_menu_insert (self, model, self->menus->len);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_joined_menu_prepend_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_JOINED_MENU (self));
|
||||
g_return_if_fail (G_MENU_MODEL (model));
|
||||
|
||||
gtk_joined_menu_insert (self, model, 0);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_joined_menu_remove_index (GtkJoinedMenu *self,
|
||||
guint index)
|
||||
{
|
||||
const Menu *menu;
|
||||
gint n_items;
|
||||
gint offset;
|
||||
|
||||
g_return_if_fail (GTK_IS_JOINED_MENU (self));
|
||||
g_return_if_fail (index < self->menus->len);
|
||||
|
||||
menu = &g_array_index (self->menus, Menu, index);
|
||||
|
||||
offset = gtk_joined_menu_get_offset_at_index (self, index);
|
||||
n_items = g_menu_model_get_n_items (menu->model);
|
||||
|
||||
g_array_remove_index (self->menus, index);
|
||||
|
||||
g_menu_model_items_changed (G_MENU_MODEL (self), offset, n_items, 0);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_joined_menu_remove_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_JOINED_MENU (self));
|
||||
g_return_if_fail (G_IS_MENU_MODEL (model));
|
||||
|
||||
for (guint i = 0; i < self->menus->len; i++)
|
||||
{
|
||||
if (g_array_index (self->menus, Menu, i).model == model)
|
||||
{
|
||||
gtk_joined_menu_remove_index (self, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_joined_menu_get_n_joined (GtkJoinedMenu *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_JOINED_MENU (self), 0);
|
||||
|
||||
return self->menus->len;
|
||||
}
|
41
gtk/gtkjoinedmenuprivate.h
Normal file
41
gtk/gtkjoinedmenuprivate.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2020 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_JOINED_MENU (gtk_joined_menu_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (GtkJoinedMenu, gtk_joined_menu, GTK, JOINED_MENU, GMenuModel)
|
||||
|
||||
GtkJoinedMenu *gtk_joined_menu_new (void);
|
||||
guint gtk_joined_menu_get_n_joined (GtkJoinedMenu *self);
|
||||
void gtk_joined_menu_append_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model);
|
||||
void gtk_joined_menu_prepend_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model);
|
||||
void gtk_joined_menu_remove_menu (GtkJoinedMenu *self,
|
||||
GMenuModel *model);
|
||||
void gtk_joined_menu_remove_index (GtkJoinedMenu *self,
|
||||
guint index);
|
||||
|
||||
G_END_DECLS
|
@ -52,6 +52,7 @@
|
||||
#include "gtkdragsourceprivate.h"
|
||||
#include "gtkdragicon.h"
|
||||
#include "gtkcsscolorvalueprivate.h"
|
||||
#include "gtkjoinedmenuprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
@ -5468,9 +5469,11 @@ gtk_label_move_cursor (GtkLabel *self,
|
||||
static GMenuModel *
|
||||
gtk_label_get_menu_model (GtkLabel *self)
|
||||
{
|
||||
GtkJoinedMenu *joined;
|
||||
GMenu *menu, *section;
|
||||
GMenuItem *item;
|
||||
|
||||
joined = gtk_joined_menu_new ();
|
||||
menu = g_menu_new ();
|
||||
|
||||
section = g_menu_new ();
|
||||
@ -5498,10 +5501,13 @@ gtk_label_get_menu_model (GtkLabel *self)
|
||||
g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
|
||||
g_object_unref (section);
|
||||
|
||||
if (self->extra_menu)
|
||||
g_menu_append_section (menu, NULL, self->extra_menu);
|
||||
gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu));
|
||||
g_object_unref (menu);
|
||||
|
||||
return G_MENU_MODEL (menu);
|
||||
if (self->extra_menu)
|
||||
gtk_joined_menu_append_menu (joined, self->extra_menu);
|
||||
|
||||
return G_MENU_MODEL (joined);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcsspositionvalueprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkjoinedmenuprivate.h"
|
||||
|
||||
|
||||
/**
|
||||
@ -658,6 +659,7 @@ void
|
||||
gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry,
|
||||
GMenuModel *model)
|
||||
{
|
||||
GtkJoinedMenu *joined;
|
||||
GMenu *menu;
|
||||
GMenu *section;
|
||||
GMenuItem *item;
|
||||
@ -671,6 +673,7 @@ gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry,
|
||||
return;
|
||||
}
|
||||
|
||||
joined = gtk_joined_menu_new ();
|
||||
menu = g_menu_new ();
|
||||
|
||||
section = g_menu_new ();
|
||||
@ -682,13 +685,16 @@ gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry,
|
||||
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 (entry->entry), G_MENU_MODEL (menu));
|
||||
|
||||
gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu));
|
||||
g_object_unref (menu);
|
||||
|
||||
if (model)
|
||||
gtk_joined_menu_append_menu (joined, model);
|
||||
|
||||
gtk_text_set_extra_menu (GTK_TEXT (entry->entry), G_MENU_MODEL (joined));
|
||||
|
||||
g_object_unref (joined);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_EXTRA_MENU]);
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "gtkwindow.h"
|
||||
#include "gtknative.h"
|
||||
#include "gtkactionmuxerprivate.h"
|
||||
#include "gtkjoinedmenuprivate.h"
|
||||
|
||||
#include <cairo-gobject.h>
|
||||
#include <string.h>
|
||||
@ -6035,9 +6036,11 @@ static GMenuModel *
|
||||
gtk_text_get_menu_model (GtkText *self)
|
||||
{
|
||||
GtkTextPrivate *priv = gtk_text_get_instance_private (self);
|
||||
GtkJoinedMenu *joined;
|
||||
GMenu *menu, *section;
|
||||
GMenuItem *item;
|
||||
|
||||
joined = gtk_joined_menu_new ();
|
||||
menu = g_menu_new ();
|
||||
|
||||
section = g_menu_new ();
|
||||
@ -6075,10 +6078,13 @@ gtk_text_get_menu_model (GtkText *self)
|
||||
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);
|
||||
gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu));
|
||||
g_object_unref (menu);
|
||||
|
||||
return G_MENU_MODEL (menu);
|
||||
if (priv->extra_menu)
|
||||
gtk_joined_menu_append_menu (joined, priv->extra_menu);
|
||||
|
||||
return G_MENU_MODEL (joined);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "gtkpango.h"
|
||||
#include "gtknative.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkjoinedmenuprivate.h"
|
||||
|
||||
/**
|
||||
* GtkTextView:
|
||||
@ -8733,9 +8734,12 @@ static GMenuModel *
|
||||
gtk_text_view_get_menu_model (GtkTextView *text_view)
|
||||
{
|
||||
GtkTextViewPrivate *priv = text_view->priv;
|
||||
GtkJoinedMenu *joined;
|
||||
GMenu *menu, *section;
|
||||
GMenuItem *item;
|
||||
|
||||
joined = gtk_joined_menu_new ();
|
||||
|
||||
menu = g_menu_new ();
|
||||
|
||||
section = g_menu_new ();
|
||||
@ -8785,10 +8789,13 @@ gtk_text_view_get_menu_model (GtkTextView *text_view)
|
||||
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);
|
||||
gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu));
|
||||
g_object_unref (menu);
|
||||
|
||||
return G_MENU_MODEL (menu);
|
||||
if (priv->extra_menu)
|
||||
gtk_joined_menu_append_menu (joined, priv->extra_menu);
|
||||
|
||||
return G_MENU_MODEL (joined);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -114,6 +114,7 @@ gtk_private_sources = files([
|
||||
'gtkiconcache.c',
|
||||
'gtkiconcachevalidator.c',
|
||||
'gtkiconhelper.c',
|
||||
'gtkjoinedmenu.c',
|
||||
'gtkkineticscrolling.c',
|
||||
'gtkmagnifier.c',
|
||||
'gtkmenusectionbox.c',
|
||||
|
Loading…
Reference in New Issue
Block a user