# HG changeset patch
# Node ID 2f58b154f3fb
# Parent  f40fc3b5e8d4
diff --git a/talkatu/data/scrolledwindow.ui b/talkatu/data/scrolledwindow.ui
new file mode 100644
--- /dev/null
+++ b/talkatu/data/scrolledwindow.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="TalkatuScrolledWindow" parent="GtkBox">
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolled_window">
+        <property name="vexpand">1</property>
+        <property name="focusable">1</property>
+        <property name="vadjustment">vadjustment</property>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkAdjustment" id="vadjustment">
+    <property name="upper">100</property>
+    <property name="step-increment">1</property>
+    <property name="page-increment">10</property>
+    <signal name="changed" handler="talkatu_scrolled_window_vadjustment_changed_cb" object="TalkatuScrolledWindow" swapped="no"/>
+    <signal name="value-changed" handler="talkatu_scrolled_window_vadjustment_value_changed_cb" object="TalkatuScrolledWindow" swapped="no"/>
+  </object>
+</interface>
diff --git a/talkatu/data/talkatu.gresource.xml b/talkatu/data/talkatu.gresource.xml
--- a/talkatu/data/talkatu.gresource.xml
+++ b/talkatu/data/talkatu.gresource.xml
@@ -7,6 +7,7 @@
     <file compressed="true">history.ui</file>
     <file compressed="true">historyrow.ui</file>
     <file compressed="true">linkdialog.ui</file>
+    <file compressed="true">scrolledwindow.ui</file>
     <file compressed="true">toolbar.ui</file>
     <file compressed="true">typinglabel.ui</file>
   </gresource>
diff --git a/talkatu/talkatuscrolledwindow.h b/talkatu/talkatuscrolledwindow.h
--- a/talkatu/talkatuscrolledwindow.h
+++ b/talkatu/talkatuscrolledwindow.h
@@ -32,13 +32,16 @@
 
 #define TALKATU_TYPE_SCROLLED_WINDOW (talkatu_scrolled_window_get_type())
 
-G_DECLARE_FINAL_TYPE(TalkatuScrolledWindow, talkatu_scrolled_window, TALKATU, SCROLLED_WINDOW, GtkScrolledWindow)
+G_DECLARE_FINAL_TYPE(TalkatuScrolledWindow, talkatu_scrolled_window, TALKATU, SCROLLED_WINDOW, GtkWidget)
 
-GtkWidget *talkatu_scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment);
+GtkWidget *talkatu_scrolled_window_new(void);
 
 void talkatu_scrolled_window_page_up(TalkatuScrolledWindow *sw);
 void talkatu_scrolled_window_page_down(TalkatuScrolledWindow *sw);
 
+GtkWidget *talkatu_scrolled_window_get_child(TalkatuScrolledWindow *sw);
+void talkatu_scrolled_window_set_child(TalkatuScrolledWindow *sw, GtkWidget *child);
+
 G_END_DECLS
 
 #endif /* TALKATU_SCROLLED_WINDOW_H */
diff --git a/talkatu/talkatuscrolledwindow.c b/talkatu/talkatuscrolledwindow.c
--- a/talkatu/talkatuscrolledwindow.c
+++ b/talkatu/talkatuscrolledwindow.c
@@ -28,14 +28,19 @@
  * way to the bottom.
  */
 struct _TalkatuScrolledWindow {
-	GtkScrolledWindow parent;
+	GtkWidget parent;
 
+	GtkWidget *scrolled_window;
 	GtkAdjustment *vadjustment;
 	gboolean auto_scroll;
 };
 
-G_DEFINE_TYPE(TalkatuScrolledWindow, talkatu_scrolled_window,
-              GTK_TYPE_SCROLLED_WINDOW)
+enum {
+	PROP_0,
+	PROP_CHILD,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
 
 /******************************************************************************
  * Callbacks
@@ -70,52 +75,109 @@
 	sw->auto_scroll = (current + pagesize >= upper);
 }
 
-static void
-talkatu_scrolled_window_vadjustment_notify_cb(GObject *obj, GParamSpec *pspec,
-                                              gpointer data)
-{
-	TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(obj);
-	GtkAdjustment *adjustment = NULL;
+/******************************************************************************
+ * GtkBuildable Implementation
+ *****************************************************************************/
+static GtkBuildableIface *parent_buildable_iface = NULL;
 
-	adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(obj));
-	if(g_set_object(&sw->vadjustment, adjustment)) {
-		sw->auto_scroll = TRUE;
-
-		g_signal_connect(G_OBJECT(adjustment), "value-changed",
-		                 G_CALLBACK(talkatu_scrolled_window_vadjustment_value_changed_cb),
-		                 sw);
-		g_signal_connect(G_OBJECT(adjustment), "changed",
-		                 G_CALLBACK(talkatu_scrolled_window_vadjustment_changed_cb),
-		                 sw);
+static void
+talkatu_scrolled_window_build_add_child(GtkBuildable *buildable,
+                                        GtkBuilder *builder, GObject *child,
+                                        const gchar *type)
+{
+	if(GTK_IS_WIDGET(child)) {
+		talkatu_scrolled_window_set_child(TALKATU_SCROLLED_WINDOW(buildable),
+		                                  GTK_WIDGET(child));
+	} else {
+		parent_buildable_iface->add_child(buildable, builder, child, type);
 	}
 }
 
-/******************************************************************************
- * GObject Stuff
- *****************************************************************************/
 static void
-talkatu_scrolled_window_init(TalkatuScrolledWindow *sw) {
-	sw->auto_scroll = TRUE;
+talkatu_scrolled_window_buildable_init(GtkBuildableIface *iface) {
+	parent_buildable_iface = g_type_interface_peek_parent(iface);
+
+	iface->add_child = talkatu_scrolled_window_build_add_child;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE_WITH_CODE(TalkatuScrolledWindow, talkatu_scrolled_window,
+                        GTK_TYPE_SCROLLED_WINDOW,
+                        G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE,
+                                              talkatu_scrolled_window_buildable_init))
 
-	g_signal_connect(G_OBJECT(sw), "notify::vadjustment",
-	                 G_CALLBACK(talkatu_scrolled_window_vadjustment_notify_cb),
-	                 NULL);
+static void
+tatlkatu_scrolled_window_get_property(GObject *obj, guint param_id,
+                                      GValue *value, GParamSpec *pspec)
+{
+	TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(obj);
+
+	switch(param_id) {
+		case PROP_CHILD:
+			g_value_set_object(value, talkatu_scrolled_window_get_child(sw));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
 }
 
 static void
-talkatu_scrolled_window_finalize(GObject *obj) {
+tatlkatu_scrolled_window_set_property(GObject *obj, guint param_id,
+                                      const GValue *value, GParamSpec *pspec)
+{
 	TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(obj);
 
-	g_clear_object(&sw->vadjustment);
+	switch(param_id) {
+		case PROP_CHILD:
+			talkatu_scrolled_window_set_child(sw, g_value_get_object(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
 
-	G_OBJECT_CLASS(talkatu_scrolled_window_parent_class)->finalize(obj);
+static void
+talkatu_scrolled_window_init(TalkatuScrolledWindow *sw) {
+	gtk_widget_init_template(GTK_WIDGET(sw));
+
+	sw->auto_scroll = TRUE;
 }
 
 static void
 talkatu_scrolled_window_class_init(TalkatuScrolledWindowClass *klass) {
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
 
-	obj_class->finalize = talkatu_scrolled_window_finalize;
+	obj_class->get_property = tatlkatu_scrolled_window_get_property;
+	obj_class->set_property = tatlkatu_scrolled_window_set_property;
+
+	/**
+	 * TalkatuScrolledWindow::child:
+	 *
+	 * The child widget that should be displayed.
+	 */
+	properties[PROP_CHILD] = g_param_spec_object(
+		"child", "child",
+		"The child widget to display",
+		GTK_TYPE_WIDGET,
+		G_PARAM_READWRITE);
+
+	gtk_widget_class_set_template_from_resource(
+		widget_class,
+		"/org/imfreedom/keep/talkatu/talkatu/ui/scrolled_window.ui"
+	);
+
+	gtk_widget_class_bind_template_child(widget_class, TalkatuScrolledWindow,
+	                                     scrolled_window);
+	gtk_widget_class_bind_template_child(widget_class, TalkatuScrolledWindow,
+	                                     vadjustment);
+
+	gtk_widget_class_bind_template_callback(widget_class, talkatu_scrolled_window_vadjustment_changed_cb);
+	gtk_widget_class_bind_template_callback(widget_class, talkatu_scrolled_window_vadjustment_value_changed_cb);
 }
 
 /******************************************************************************
@@ -124,22 +186,14 @@
 
 /**
  * talkatu_scrolled_window_new:
- * @hadjustment: The GtkAdjustment for the horizontal position.
- * @vadjustment: The GtkAdjustment for the vertical position.
  *
  * Creates a new #TalkatuScrolledWindow.
  *
  * Returns: (transfer full): The new #TalkatuScrolledWindow instance.
  */
-GtkWidget *talkatu_scrolled_window_new(GtkAdjustment *hadjustment,
-                                       GtkAdjustment *vadjustment)
-{
-	return GTK_WIDGET(g_object_new(
-		TALKATU_TYPE_SCROLLED_WINDOW,
-		"hadjustment", hadjustment,
-		"vadjustment", vadjustment,
-		NULL
-	));
+GtkWidget *
+talkatu_scrolled_window_new(void) {
+	return g_object_new(TALKATU_TYPE_SCROLLED_WINDOW, NULL);
 }
 
 /**
@@ -150,15 +204,13 @@
  */
 void
 talkatu_scrolled_window_page_up(TalkatuScrolledWindow *sw) {
+	gdouble value = 0.0;
+
 	g_return_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw));
 
-	if(sw->vadjustment != NULL) {
-		double value = 0.0;
-
-		value = gtk_adjustment_get_value(sw->vadjustment);
-		value -= gtk_adjustment_get_page_increment(sw->vadjustment);
-		gtk_adjustment_set_value(sw->vadjustment, value);
-	}
+	value = gtk_adjustment_get_value(sw->vadjustment);
+	value -= gtk_adjustment_get_page_increment(sw->vadjustment);
+	gtk_adjustment_set_value(sw->vadjustment, value);
 }
 
 /**
@@ -169,13 +221,42 @@
  */
 void
 talkatu_scrolled_window_page_down(TalkatuScrolledWindow *sw) {
+	gdouble value = 0.0;
+
 	g_return_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw));
 
-	if(sw->vadjustment != NULL) {
-		double value = 0.0;
+	value = gtk_adjustment_get_value(sw->vadjustment);
+	value += gtk_adjustment_get_page_increment(sw->vadjustment);
+	gtk_adjustment_set_value(sw->vadjustment, value);
+}
+
+/**
+ * talkatu_scrolled_window_get_child:
+ * @sw: The scrolled window instance.
+ *
+ * Gets the child of @sw.
+ *
+ * Returns: (transfer none) (nullable): The child of @sw.
+ */
+GtkWidget *
+talkatu_scrolled_window_get_child(TalkatuScrolledWindow *sw) {
+	g_return_val_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw), NULL);
 
-		value = gtk_adjustment_get_value(sw->vadjustment);
-		value += gtk_adjustment_get_page_increment(sw->vadjustment);
-		gtk_adjustment_set_value(sw->vadjustment, value);
-	}
+	return gtk_scrolled_window_get_child(GTK_SCROLLED_WINDOW(sw->scrolled_window));
 }
+
+/**
+ * talkatu_scrolled_window_set_child:
+ * @sw: The scrolled window instance.
+ * @child: (nullable): The new child or NULL to unset.
+ *
+ * Sets the child of @sw to @child.
+ */
+void
+talkatu_scrolled_window_set_child(TalkatuScrolledWindow *sw, GtkWidget *child)
+{
+	g_return_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw));
+
+	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw->scrolled_window),
+	                              child);
+}
