talkatu/talkatu

79f814e6b5cb
Parents 3c633be6b13e
Children b2089d187528
Replace TalkatuScrolledWindow with TalkatuAutoScroller

GtkScrolledWindow is a final type in GTK4, so we can no longer subclass it. To
work around this, TalkatuAutoScroller was created which is a GtkAdjustment
subclass that does the same thing TalkatuScrolledWindow did.

Testing Done:
Ran the demo and verified that auto scroll worked properly.

Reviewed at https://reviews.imfreedom.org/r/1658/
--- a/demo/data/demo.ui Thu Aug 25 03:22:24 2022 -0500
+++ b/demo/data/demo.ui Fri Aug 26 03:08:51 2022 -0500
@@ -25,6 +25,7 @@
<!-- interface-name Talkatu -->
<!-- interface-description GTK widgets for chat applications -->
<!-- interface-copyright Gary Kramlich <grim@reaperworld.com> -->
+ <object class="TalkatuAutoScroller" id="auto_scroller"/>
<template class="TalkatuDemoWindow" parent="GtkApplicationWindow">
<property name="child">
<object class="GtkPaned">
@@ -33,14 +34,11 @@
<property name="orientation">vertical</property>
<property name="wide-handle">1</property>
<child>
- <object class="TalkatuScrolledWindow">
- <child internal-child="scrolled_window">
- <object class="GtkScrolledWindow" id="scrolled_window">
- <child>
- <object class="TalkatuHistory" id="history">
- <property name="name">history</property>
- </object>
- </child>
+ <object class="GtkScrolledWindow">
+ <property name="vadjustment">auto_scroller</property>
+ <child>
+ <object class="TalkatuHistory" id="history">
+ <property name="name">history</property>
</object>
</child>
</object>
--- a/po/POTFILES Thu Aug 25 03:22:24 2022 -0500
+++ b/po/POTFILES Fri Aug 26 03:08:51 2022 -0500
@@ -16,6 +16,7 @@
talkatu/talkatuattachment.c
talkatu/talkatuattachmentdialog.c
talkatu/talkatuattachmentpreview.c
+talkatu/talkatuautoscroller.c
talkatu/talkatubuffer.c
talkatu/talkatucodeset.c
talkatu/talkatucore.c
@@ -29,7 +30,6 @@
talkatu/talkatumarkdownbuffer.c
talkatu/talkatumarkup.c
talkatu/talkatumessage.c
-talkatu/talkatuscrolledwindow.c
talkatu/talkatusimpleattachment.c
talkatu/talkatutag.c
talkatu/talkatutagtable.c
--- a/talkatu/data/scrolledwindow.ui Thu Aug 25 03:22:24 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
- <requires lib="gtk" version="4.0"/>
- <template class="TalkatuScrolledWindow" parent="GtkWidget">
- <child>
- <object class="GtkScrolledWindow" id="scrolled_window">
- <property name="vexpand">1</property>
- <property name="hexpand">1</property>
- <property name="focusable">1</property>
- <property name="vadjustment">vadjustment</property>
- <property name="min-content-height">150</property>
- <property name="vscrollbar-policy">2</property>
- </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>
--- a/talkatu/data/talkatu.gresource.xml Thu Aug 25 03:22:24 2022 -0500
+++ b/talkatu/data/talkatu.gresource.xml Fri Aug 26 03:08:51 2022 -0500
@@ -8,7 +8,6 @@
<file compressed="true">historyrow.ui</file>
<file compressed="true">input.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>
<file compressed="true">view.ui</file>
--- a/talkatu/meson.build Thu Aug 25 03:22:24 2022 -0500
+++ b/talkatu/meson.build Fri Aug 26 03:08:51 2022 -0500
@@ -8,6 +8,7 @@
'talkatuattachment.h',
'talkatuattachmentdialog.h',
'talkatuattachmentpreview.h',
+ 'talkatuautoscroller.h',
'talkatubuffer.h',
'talkatucodeset.h',
'talkatucore.h',
@@ -22,7 +23,6 @@
'talkatumarkdownbuffer.h',
'talkatumarkup.h',
'talkatumessage.h',
- 'talkatuscrolledwindow.h',
'talkatusimpleattachment.h',
'talkatutag.h',
'talkatutagtable.h',
@@ -37,6 +37,7 @@
'talkatuattachment.c',
'talkatuattachmentdialog.c',
'talkatuattachmentpreview.c',
+ 'talkatuautoscroller.c',
'talkatubuffer.c',
'talkatucodeset.c',
'talkatucore.c',
@@ -51,7 +52,6 @@
'talkatumarkdownbuffer.c',
'talkatumarkup.c',
'talkatumessage.c',
- 'talkatuscrolledwindow.c',
'talkatusimpleattachment.c',
'talkatutag.c',
'talkatutagtable.c',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/talkatu/talkatuautoscroller.c Fri Aug 26 03:08:51 2022 -0500
@@ -0,0 +1,154 @@
+/*
+ * Talkatu - GTK widgets for chat applications
+ * Copyright (C) 2017-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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 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 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/>.
+ */
+
+#include <gtk/gtk.h>
+
+#include <talkatu/talkatuautoscroller.h>
+
+/**
+ * TalkatuAutoScroller:
+ *
+ * This is a simple subclass of [class@Gtk.Adjustment] that has helpers for
+ * keyboard navigation as well as the ability to automatically scroll to the
+ * max when new items are added if the widget was already scrolled all the
+ * way to the bottom.
+ */
+struct _TalkatuAutoScroller {
+ GtkAdjustment parent;
+
+ gboolean auto_scroll;
+};
+
+G_DEFINE_TYPE(TalkatuAutoScroller, talkatu_auto_scroller, GTK_TYPE_ADJUSTMENT)
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static gboolean
+talkatu_auto_scroller_scroll(gpointer data) {
+ GtkAdjustment *adjustment = data;
+
+ gdouble upper, pagesize;
+
+ upper = gtk_adjustment_get_upper(adjustment);
+ pagesize = gtk_adjustment_get_page_size(adjustment);
+
+ gtk_adjustment_set_value(adjustment, upper - pagesize);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+talkatu_auto_scroller_changed(GtkAdjustment *adjustment) {
+ TalkatuAutoScroller *auto_scroller = TALKATU_AUTO_SCROLLER(adjustment);
+
+ if(auto_scroller->auto_scroll) {
+ /* If we set the value here, we interrupt the updates to the other
+ * values of the adjustment which breaks the adjustment.
+ *
+ * The specific behavior we've seen is that the upper property doesn't
+ * get updated when we call set value here. Which means you can't even
+ * scroll down anymore in a scrolled window because you're already at
+ * the upper bound. However, if you scroll up, even if there's no where
+ * to scroll, it will update the adjustment's properties and you can
+ * then scroll down.
+ *
+ * By using a timeout to set the value during the next main loop
+ * iteration we avoid this problem.
+ */
+ g_timeout_add(0, talkatu_auto_scroller_scroll, adjustment);
+ }
+}
+
+static void
+talkatu_auto_scroller_value_changed(GtkAdjustment *adjustment) {
+ TalkatuAutoScroller *auto_scroller = TALKATU_AUTO_SCROLLER(adjustment);
+ gdouble current, upper, pagesize;
+
+ current = gtk_adjustment_get_value(adjustment);
+ upper = gtk_adjustment_get_upper(adjustment);
+ pagesize = gtk_adjustment_get_page_size(adjustment);
+
+ auto_scroller->auto_scroll = (current + pagesize >= upper);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+talkatu_auto_scroller_init(TalkatuAutoScroller *auto_scroller) {
+ auto_scroller->auto_scroll = TRUE;
+}
+
+static void
+talkatu_auto_scroller_class_init(TalkatuAutoScrollerClass *klass) {
+ GtkAdjustmentClass *adjustment_class = GTK_ADJUSTMENT_CLASS(klass);
+
+ adjustment_class->changed = talkatu_auto_scroller_changed;
+ adjustment_class->value_changed = talkatu_auto_scroller_value_changed;
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/**
+ * talkatu_auto_scroller_new:
+ *
+ * Creates a new #TalkatuAutoScroller.
+ *
+ * Returns: (transfer full): The new #TalkatuAutoScroller instance.
+ */
+GtkAdjustment *
+talkatu_auto_scroller_new(void) {
+ return g_object_new(TALKATU_TYPE_AUTO_SCROLLER, NULL);
+}
+
+/**
+ * talkatu_auto_scroller_decrement:
+ * @auto_scroller: The #TalkatuAutoScroller instance.
+ *
+ * Decrements the value of @auto_scroller by a page increment.
+ */
+void
+talkatu_auto_scroller_decrement(TalkatuAutoScroller *auto_scroller) {
+ gdouble value = 0.0;
+
+ g_return_if_fail(TALKATU_IS_AUTO_SCROLLER(auto_scroller));
+
+ value = gtk_adjustment_get_value(GTK_ADJUSTMENT(auto_scroller));
+ value -= gtk_adjustment_get_page_increment(GTK_ADJUSTMENT(auto_scroller));
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(auto_scroller), value);
+}
+
+/**
+ * talkatu_auto_scroller_increment:
+ * @auto_scroller: The #TalkatuAutoScroller instance.
+ *
+ * Increments the value of @auto_scroller by a page increment.
+ */
+void
+talkatu_auto_scroller_page_down(TalkatuAutoScroller *auto_scroller) {
+ gdouble value = 0.0;
+
+ g_return_if_fail(TALKATU_IS_AUTO_SCROLLER(auto_scroller));
+
+ value = gtk_adjustment_get_value(GTK_ADJUSTMENT(auto_scroller));
+ value += gtk_adjustment_get_page_increment(GTK_ADJUSTMENT(auto_scroller));
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(auto_scroller), value);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/talkatu/talkatuautoscroller.h Fri Aug 26 03:08:51 2022 -0500
@@ -0,0 +1,45 @@
+/*
+ * Talkatu - GTK widgets for chat applications
+ * Copyright (C) 2017-2020 Gary Kramlich <grim@reaperworld.com>
+ *
+ * This library 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 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 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/>.
+ */
+
+#if !defined(TALKATU_GLOBAL_HEADER_INSIDE) && !defined(TALKATU_COMPILATION)
+#error "only <talkatu.h> may be included directly"
+#endif
+
+#ifndef TALKATU_AUTO_SCROLLER_H
+#define TALKATU_AUTO_SCROLLER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define TALKATU_TYPE_AUTO_SCROLLER (talkatu_auto_scroller_get_type())
+
+G_DECLARE_FINAL_TYPE(TalkatuAutoScroller, talkatu_auto_scroller, TALKATU,
+ AUTO_SCROLLER, GtkAdjustment)
+
+GtkAdjustment *talkatu_auto_scroller_new(void);
+
+void talkatu_auto_scroller_decrement(TalkatuAutoScroller *auto_scroller);
+void talkatu_auto_scroller_increment(TalkatuAutoScroller *auto_scroller);
+
+G_END_DECLS
+
+#endif /* TALKATU_AUTO_SCROLLER_H */
--- a/talkatu/talkatuscrolledwindow.c Thu Aug 25 03:22:24 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-/*
- * Talkatu - GTK widgets for chat applications
- * Copyright (C) 2017-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library 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 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 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/>.
- */
-
-#include <gtk/gtk.h>
-
-#include <talkatu/talkatuscrolledwindow.h>
-
-/**
- * TalkatuScrolledWindow:
- * This widget is a simple subclass of #GtkScrolledWindow that has helpers for
- * keyboard navigation as well as the ability to automatically scroll to the
- * bottom when new items are added if the widget was already scrolled all the
- * way to the bottom.
- */
-struct _TalkatuScrolledWindow {
- GtkWidget parent;
-
- GtkWidget *scrolled_window;
- GtkAdjustment *vadjustment;
- gboolean auto_scroll;
-};
-
-enum {
- PROP_0,
- PROP_CHILD,
- N_PROPERTIES,
-};
-static GParamSpec *properties[N_PROPERTIES] = { NULL, };
-
-/******************************************************************************
- * Callbacks
- *****************************************************************************/
-static void
-talkatu_scrolled_window_vadjustment_changed_cb(GtkAdjustment *adjustment,
- gpointer data)
-{
- TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(data);
-
- if(sw->auto_scroll) {
- gdouble upper, pagesize;
-
- upper = gtk_adjustment_get_upper(adjustment);
- pagesize = gtk_adjustment_get_page_size(adjustment);
-
- gtk_adjustment_set_value(adjustment, upper - pagesize);
- }
-}
-
-static void
-talkatu_scrolled_window_vadjustment_value_changed_cb(GtkAdjustment *adjustment,
- gpointer data)
-{
- TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(data);
- gdouble current, upper, pagesize;
-
- current = gtk_adjustment_get_value(adjustment);
- upper = gtk_adjustment_get_upper(adjustment);
- pagesize = gtk_adjustment_get_page_size(adjustment);
-
- sw->auto_scroll = (current + pagesize >= upper);
-}
-
-/******************************************************************************
- * GObject Implementation
- *****************************************************************************/
-G_DEFINE_TYPE(TalkatuScrolledWindow, talkatu_scrolled_window, GTK_TYPE_WIDGET)
-
-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
-tatlkatu_scrolled_window_set_property(GObject *obj, guint param_id,
- const GValue *value, GParamSpec *pspec)
-{
- TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(obj);
-
- 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;
- }
-}
-
-static void
-talkatu_scrolled_window_dispose(GObject *obj) {
- TalkatuScrolledWindow *sw = TALKATU_SCROLLED_WINDOW(obj);
-
- g_clear_pointer(&sw->scrolled_window, gtk_widget_unparent);
-
- G_OBJECT_CLASS(talkatu_scrolled_window_parent_class)->dispose(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->get_property = tatlkatu_scrolled_window_get_property;
- obj_class->set_property = tatlkatu_scrolled_window_set_property;
- obj_class->dispose = talkatu_scrolled_window_dispose;
-
- /**
- * 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_layout_manager_type(widget_class, GTK_TYPE_BIN_LAYOUT);
-
- gtk_widget_class_set_template_from_resource(
- widget_class,
- "/org/imfreedom/keep/talkatu/talkatu/ui/scrolledwindow.ui"
- );
-
- gtk_widget_class_bind_template_child_internal(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);
-}
-
-/******************************************************************************
- * Public API
- *****************************************************************************/
-
-/**
- * talkatu_scrolled_window_new:
- *
- * Creates a new #TalkatuScrolledWindow.
- *
- * Returns: (transfer full): The new #TalkatuScrolledWindow instance.
- */
-GtkWidget *
-talkatu_scrolled_window_new(void) {
- return g_object_new(TALKATU_TYPE_SCROLLED_WINDOW, NULL);
-}
-
-/**
- * talkatu_scrolled_window_page_up:
- * @sw: The #TalkatuScrolledWindow instance.
- *
- * Scrolls @sw up one page.
- */
-void
-talkatu_scrolled_window_page_up(TalkatuScrolledWindow *sw) {
- gdouble value = 0.0;
-
- g_return_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw));
-
- 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_page_down:
- * @sw: The #TalkatuScrolledWindow instance.
- *
- * Scrolls @sw down one page.
- */
-void
-talkatu_scrolled_window_page_down(TalkatuScrolledWindow *sw) {
- gdouble value = 0.0;
-
- g_return_if_fail(TALKATU_IS_SCROLLED_WINDOW(sw));
-
- 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);
-
- 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);
-}
--- a/talkatu/talkatuscrolledwindow.h Thu Aug 25 03:22:24 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Talkatu - GTK widgets for chat applications
- * Copyright (C) 2017-2020 Gary Kramlich <grim@reaperworld.com>
- *
- * This library 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 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 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/>.
- */
-
-#if !defined(TALKATU_GLOBAL_HEADER_INSIDE) && !defined(TALKATU_COMPILATION)
-#error "only <talkatu.h> may be included directly"
-#endif
-
-#ifndef TALKATU_SCROLLED_WINDOW_H
-#define TALKATU_SCROLLED_WINDOW_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define TALKATU_TYPE_SCROLLED_WINDOW (talkatu_scrolled_window_get_type())
-
-G_DECLARE_FINAL_TYPE(TalkatuScrolledWindow, talkatu_scrolled_window, TALKATU, SCROLLED_WINDOW, GtkWidget)
-
-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 */