# HG changeset patch
# Node ID ff33873155a6
# Parent  0b26ebc842db
diff --git a/libpurple/purplecontactmanager.c b/libpurple/purplecontactmanager.c
--- a/libpurple/purplecontactmanager.c
+++ b/libpurple/purplecontactmanager.c
@@ -580,6 +580,7 @@
 	PurpleAccount *account = NULL;
 	PurpleContact *contact = NULL;
 	PurpleContactInfo *info = NULL;
+	PurplePerson *person = NULL;
 	PurplePresence *buddy_presence = NULL;
 	PurplePresence *contact_presence = NULL;
 	const gchar *id = NULL;
@@ -593,6 +594,10 @@
 	contact = purple_contact_new(account, id);
 	info = PURPLE_CONTACT_INFO(contact);
 
+	person = purple_person_new();
+	purple_contact_info_set_person(info, person);
+	purple_person_add_contact_info(person, info);
+
 	/* Bind all of the properties. */
 	g_object_bind_property(buddy, "name", contact, "username",
 	                       G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
diff --git a/meson.build b/meson.build
--- a/meson.build
+++ b/meson.build
@@ -235,7 +235,7 @@
 # Check Pidgin dependencies
 #######################################################################
 if get_option('gtkui')
-	gtk = dependency('gtk4', version : '>= 4.0.0')
+	gtk = dependency('gtk4', version : '>= 4.6.0')
 	libadwaita = dependency('libadwaita-1', version : '>= 1.2')
 
 	talkatu_dep = dependency('talkatu',
diff --git a/pidgin/meson.build b/pidgin/meson.build
--- a/pidgin/meson.build
+++ b/pidgin/meson.build
@@ -34,6 +34,7 @@
 	'pidginavatar.c',
 	'pidgincolor.c',
 	'pidgincommands.c',
+	'pidgincontactlist.c',
 	'pidgincontactlistwindow.c',
 	'pidgindebug.c',
 	'pidgindisplaywindow.c',
@@ -98,6 +99,7 @@
 	'pidginattachment.h',
 	'pidginavatar.h',
 	'pidgincolor.h',
+	'pidgincontactlist.h',
 	'pidgincontactlistwindow.h',
 	'pidgincore.h',
 	'pidgindisplaywindow.h',
diff --git a/pidgin/pidgincontactlist.h b/pidgin/pidgincontactlist.h
new file mode 100644
--- /dev/null
+++ b/pidgin/pidgincontactlist.h
@@ -0,0 +1,64 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_CONTACT_LIST_H
+#define PIDGIN_CONTACT_LIST_H
+
+#include <glib.h>
+
+#include <adwaita.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginContactList:
+ *
+ * #PidginContactList is a widget that displays the [iface@Gio.ListModel] of
+ * [class@Purple.Person] from [class@Purple.ContactManager].
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_CONTACT_LIST (pidgin_contact_list_get_type())
+G_DECLARE_FINAL_TYPE(PidginContactList, pidgin_contact_list, PIDGIN,
+                     CONTACT_LIST, AdwBin)
+
+/**
+ * pidgin_contact_list_new:
+ *
+ * Creates a new #PidginContactList instance that will display
+ * [iface@Gio.ListModel] of [class@Purple.Person] from
+ * [class@Purple.ContactManager].
+ *
+ * Returns: (transfer full): The new #PidginContactList instance.
+ */
+GtkWidget *pidgin_contact_list_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_CONTACT_LIST_H */
diff --git a/pidgin/pidgincontactlist.c b/pidgin/pidgincontactlist.c
new file mode 100644
--- /dev/null
+++ b/pidgin/pidgincontactlist.c
@@ -0,0 +1,138 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * Pidgin is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <purple.h>
+
+#include "pidgin/pidgincontactlist.h"
+
+struct _PidginContactList {
+	AdwBin parent;
+
+	GtkWidget *view;
+};
+
+G_DEFINE_TYPE(PidginContactList, pidgin_contact_list, ADW_TYPE_BIN)
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static GdkTexture *
+pidgin_contact_list_avatar_cb(G_GNUC_UNUSED GObject *self,
+                              PurplePerson *person,
+                              G_GNUC_UNUSED gpointer data)
+{
+	PurpleContactInfo *info = NULL;
+	PurpleContact *contact = NULL;
+	PurpleBuddyIcon *icon = NULL;
+	GdkTexture *texture = NULL;
+
+	info = purple_person_get_priority_contact_info(person);
+
+	/* All of the contact info in the manager are PurpleContact's so this cast
+	 * is fine.
+	 */
+	contact = PURPLE_CONTACT(info);
+
+	icon = purple_buddy_icons_find(purple_contact_get_account(contact),
+	                               purple_contact_info_get_username(info));
+
+	if(icon != NULL) {
+		GBytes *bytes = NULL;
+		GError *error = NULL;
+		gsize size;
+		gconstpointer data = NULL;
+
+		data = purple_buddy_icon_get_data(icon, &size);
+		bytes = g_bytes_new(data, size);
+
+		texture = gdk_texture_new_from_bytes(bytes, &error);
+		g_bytes_unref(bytes);
+
+		if(error != NULL) {
+			g_warning("Failed to create texture: %s", error->message);
+
+			g_clear_error(&error);
+		}
+	}
+
+	return texture;
+}
+
+static char *
+pidgin_contact_list_text_cb(G_GNUC_UNUSED GObject *self,
+                            PurplePerson *person,
+                            G_GNUC_UNUSED gpointer data)
+{
+	PurpleContactInfo *info = NULL;
+	GString *text = NULL;
+
+	info = purple_person_get_priority_contact_info(person);
+
+	text = g_string_new(purple_contact_info_get_name_for_display(info));
+
+	return g_string_free(text, FALSE);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_contact_list_init(PidginContactList *list) {
+	PurpleContactManager *manager = NULL;
+	GtkSingleSelection *selection = NULL;
+
+	gtk_widget_init_template(GTK_WIDGET(list));
+
+	manager = purple_contact_manager_get_default();
+	selection = gtk_single_selection_new(G_LIST_MODEL(manager));
+
+	gtk_list_view_set_model(GTK_LIST_VIEW(list->view),
+	                        GTK_SELECTION_MODEL(selection));
+}
+
+static void
+pidgin_contact_list_class_init(PidginContactListClass *klass) {
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	gtk_widget_class_set_template_from_resource(
+	    widget_class,
+	    "/im/pidgin/Pidgin3/ContactList/widget.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, PidginContactList,
+	                                     view);
+
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_contact_list_avatar_cb);
+	gtk_widget_class_bind_template_callback(widget_class,
+	                                        pidgin_contact_list_text_cb);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_contact_list_new(void) {
+	return g_object_new(PIDGIN_TYPE_CONTACT_LIST, NULL);
+}
diff --git a/pidgin/resources/ContactList/widget.ui b/pidgin/resources/ContactList/widget.ui
new file mode 100644
--- /dev/null
+++ b/pidgin/resources/ContactList/widget.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Pidgin - Internet Messenger
+Copyright (C) Pidgin Developers <devel@pidgin.im>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this library; if not, see <https://www.gnu.org/licenses/>.
+-->
+<interface domain="pidgin">
+  <requires lib="gtk" version="4.0"/>
+  <requires lib="Adw" version="1.0"/>
+  <requires lib="pidgin" version="3.0"/>
+  <!-- interface-license-type gplv2 -->
+  <!-- interface-name Pidgin -->
+  <!-- interface-description Internet Messenger -->
+  <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+  <template class="PidginContactList" parent="AdwBin">
+    <child>
+      <object class="GtkListView" id="view">
+        <property name="factory">
+          <object class="GtkBuilderListItemFactory">
+            <property name="bytes">
+    <![CDATA[
+    <?xml version="1.0" encoding="UTF-8"?>
+    <interface>
+      <template class="GtkListItem">
+        <property name="child">
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">6</property>
+            <child>
+              <!-- switch to a PidginAvatar when than can take a PurpleAvatar -->
+              <object class="GtkImage" id="avatar">
+                <property name="icon-size">large</property>
+                <binding name="paintable">
+                  <closure type="GdkTexture" function="pidgin_contact_list_avatar_cb">
+                    <lookup name="item">GtkListItem</lookup>
+                  </closure>
+                </binding>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="xalign">0</property>
+                <property name="use-markup">1</property>
+                <binding name="label">
+                  <closure type="gchararray" function="pidgin_contact_list_text_cb">
+                    <lookup name="item">GtkListItem</lookup>
+                  </closure>
+                </binding>
+              </object>
+            </child>
+          </object>
+        </property>
+      </template>
+    </interface>
+    ]]>
+            </property>
+          </object>
+        </property>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/pidgin/resources/Display/window.ui b/pidgin/resources/Display/window.ui
--- a/pidgin/resources/Display/window.ui
+++ b/pidgin/resources/Display/window.ui
@@ -110,10 +110,7 @@
                   <object class="AdwViewStackPage">
                     <property name="name">__contacts__</property>
                     <property name="child">
-                      <object class="AdwStatusPage" id="contact_list">
-                        <property name="icon-name">system-users-symbolic</property>
-                        <property name="title" translatable="1">Contacts</property>
-                        <property name="description" translatable="1">This is just a placeholder until we build the new ContactList widget.</property>
+                      <object class="PidginContactList" id="contact_list">
                       </object>
                     </property>
                   </object>
diff --git a/pidgin/resources/pidgin.gresource.xml b/pidgin/resources/pidgin.gresource.xml
--- a/pidgin/resources/pidgin.gresource.xml
+++ b/pidgin/resources/pidgin.gresource.xml
@@ -13,6 +13,7 @@
     <file compressed="true" preprocess="xml-stripblanks">Conversations/infopane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">Conversations/invite_dialog.ui</file>
     <file compressed="true">Conversations/tab-label.css</file>
+    <file compressed="true" preprocess="xml-stripblanks">ContactList/widget.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">Debug/debug.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">Dialogs/addbuddy.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">Dialogs/addchat.ui</file>
