/* * Copyright © 2018 Benjamin Otte * * 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 . * * Authors: Benjamin Otte */ #include "config.h" #include "gtklistitemwidgetprivate.h" #include "gtkbinlayout.h" #include "gtkeventcontrollermotion.h" #include "gtkgestureclick.h" #include "gtklistitemfactoryprivate.h" #include "gtklistitemprivate.h" #include "gtklistbaseprivate.h" #include "gtkwidget.h" #include "gtkwidgetprivate.h" G_DEFINE_TYPE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_LIST_FACTORY_WIDGET) static gboolean gtk_list_item_widget_focus (GtkWidget *widget, GtkDirectionType direction) { GtkWidget *child = gtk_widget_get_first_child (widget); if (gtk_widget_get_focus_child (widget)) { /* focus is in the child */ if (direction == GTK_DIR_TAB_BACKWARD) return gtk_widget_grab_focus_self (widget); else return FALSE; } else if (gtk_widget_is_focus (widget)) { /* The widget has focus */ if (direction == GTK_DIR_TAB_FORWARD) { if (child) return gtk_widget_child_focus (child, direction); } return FALSE; } else { /* focus coming in from the outside */ if (direction == GTK_DIR_TAB_BACKWARD) { if (child && gtk_widget_child_focus (child, direction)) return TRUE; return gtk_widget_grab_focus_self (widget); } else { if (gtk_widget_grab_focus_self (widget)) return TRUE; if (child && gtk_widget_child_focus (child, direction)) return TRUE; return FALSE; } } } static gboolean gtk_list_item_widget_grab_focus (GtkWidget *widget) { GtkWidget *child; if (GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget)) return TRUE; child = gtk_widget_get_first_child (widget); if (child && gtk_widget_grab_focus (child)) return TRUE; return FALSE; } static gpointer gtk_list_item_widget_create_object (GtkListFactoryWidget *fw) { return gtk_list_item_new (); } static void gtk_list_item_widget_setup_object (GtkListFactoryWidget *fw, gpointer object) { GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); GtkListItem *list_item = object; GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->setup_object (fw, object); list_item->owner = self; gtk_list_item_widget_set_child (self, list_item->child); gtk_list_factory_widget_set_activatable (fw, list_item->activatable); gtk_list_factory_widget_set_selectable (fw, list_item->selectable); gtk_widget_set_focusable (GTK_WIDGET (self), list_item->focusable); gtk_accessible_update_property (GTK_ACCESSIBLE (self), GTK_ACCESSIBLE_PROPERTY_LABEL, list_item->accessible_label, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, list_item->accessible_description, -1); gtk_list_item_do_notify (list_item, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)) != GTK_INVALID_LIST_POSITION, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self))); } static void gtk_list_item_widget_teardown_object (GtkListFactoryWidget *fw, gpointer object) { GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); GtkListItem *list_item = object; GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->teardown_object (fw, object); list_item->owner = NULL; gtk_list_item_widget_set_child (self, NULL); gtk_list_factory_widget_set_activatable (fw, FALSE); gtk_list_factory_widget_set_selectable (fw, FALSE); gtk_widget_set_focusable (GTK_WIDGET (self), TRUE); gtk_accessible_reset_property (GTK_ACCESSIBLE (self), GTK_ACCESSIBLE_PROPERTY_LABEL); gtk_accessible_reset_property (GTK_ACCESSIBLE (self), GTK_ACCESSIBLE_PROPERTY_DESCRIPTION); gtk_list_item_do_notify (list_item, gtk_list_item_base_get_item (GTK_LIST_ITEM_BASE (self)) != NULL, gtk_list_item_base_get_position (GTK_LIST_ITEM_BASE (self)) != GTK_INVALID_LIST_POSITION, gtk_list_item_base_get_selected (GTK_LIST_ITEM_BASE (self))); /* FIXME: This is technically not correct, the child is user code, isn't it? */ gtk_list_item_set_child (list_item, NULL); } static void gtk_list_item_widget_update_object (GtkListFactoryWidget *fw, gpointer object, guint position, gpointer item, gboolean selected) { GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (fw); GtkListItemBase *base = GTK_LIST_ITEM_BASE (self); GtkListItem *list_item = object; /* Track notify manually instead of freeze/thaw_notify for performance reasons. */ gboolean notify_item = FALSE, notify_position = FALSE, notify_selected = FALSE; /* FIXME: It's kinda evil to notify external objects from here... */ notify_item = gtk_list_item_base_get_item (base) != item; notify_position = gtk_list_item_base_get_position (base) != position; notify_selected = gtk_list_item_base_get_selected (base) != selected; GTK_LIST_FACTORY_WIDGET_CLASS (gtk_list_item_widget_parent_class)->update_object (fw, object, position, item, selected); if (list_item) gtk_list_item_do_notify (list_item, notify_item, notify_position, notify_selected); } static void gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass) { GtkListFactoryWidgetClass *factory_class = GTK_LIST_FACTORY_WIDGET_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); factory_class->create_object = gtk_list_item_widget_create_object; factory_class->setup_object = gtk_list_item_widget_setup_object; factory_class->update_object = gtk_list_item_widget_update_object; factory_class->teardown_object = gtk_list_item_widget_teardown_object; widget_class->focus = gtk_list_item_widget_focus; widget_class->grab_focus = gtk_list_item_widget_grab_focus; /* This gets overwritten by gtk_list_item_widget_new() but better safe than sorry */ gtk_widget_class_set_css_name (widget_class, I_("row")); gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); } static void gtk_list_item_widget_init (GtkListItemWidget *self) { gtk_widget_set_focusable (GTK_WIDGET (self), TRUE); } GtkWidget * gtk_list_item_widget_new (GtkListItemFactory *factory, const char *css_name, GtkAccessibleRole role) { g_return_val_if_fail (css_name != NULL, NULL); return g_object_new (GTK_TYPE_LIST_ITEM_WIDGET, "css-name", css_name, "accessible-role", role, "factory", factory, NULL); } void gtk_list_item_widget_set_child (GtkListItemWidget *self, GtkWidget *child) { GtkWidget *cur_child = gtk_widget_get_first_child (GTK_WIDGET (self)); if (cur_child == child) return; g_clear_pointer (&cur_child, gtk_widget_unparent); if (child) gtk_widget_set_parent (child, GTK_WIDGET (self)); }