summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Weghorn <m.weghorn@posteo.de>2024-02-22 11:58:08 +0100
committerMichael Weghorn <m.weghorn@posteo.de>2024-02-22 15:20:36 +0100
commite268efd612d12ae9a459d6b9d0cb23220f025163 (patch)
tree2e8ef2ff5e0a6668ee0c6f2462a56024fb4dabe2
parentgtk4 a11y: Move LO_ACCESSIBLE define to header (diff)
downloadcore-e268efd612d12ae9a459d6b9d0cb23220f025163.tar.gz
core-e268efd612d12ae9a459d6b9d0cb23220f025163.zip
gtk4 a11y: Implement new GtkAccessibleTextInterface
Implement most of the methods of the `GtkAccessibleInterface` newly added to Gtk 4 in Gtk commit [1] commit 0ca8d74842837b1ad5dc42c1fcff8b1270e5750b Author: Matthias Clasen <mclasen@redhat.com> Date: Tue Feb 20 12:18:27 2024 -0500 a11y: Add GtkAccessibleText interface The AccessibleText interface is meant to be implemented by widgets and other accessible objects that expose selectable, navigatable, or rich text to assistive technologies. This kind of text is not covered by the plain accessible name and description, as it contains things like a caret, or text attributes. This commit adds a stub GtkAccessibleText with its basic virtual functions; the interface will be implemented by widgets like GtkLabel, GtkInscription, GtkText, and GtkTextView. A further commit will ensure that the AT-SPI implementation will convert from GTK to AT-SPI through a generic (internal API); and, finally, we'll remove the widget type checks in the AT-SPI implementation of GtkATContext, and only check for GtkAccessibleText. Fixes: #5912 and follow-up commits. The `css::accessibility::XAccessibleText` interface provides the required functionality. With a Writer paragraph consisting of the text "Hello world. And another sentence." and the word "world" selected, using some of the AT-SPI Text interface methods via Accerciser's IPython console behaves as expected now when the paragraph's a11y object is selected in Accerciser's treeview: In [9]: text = acc.queryText() In [10]: text.get_caretOffset() Out[10]: 11 In [11]: text.getText(0, -1) Out[11]: 'Hello world. And another sentence.' In [12]: text.getText(2,5) Out[12]: 'llo' In [13]: text.getStringAtOffset(10, pyatspi.TEXT_GRANULARITY_CHAR) Out[13]: ('d', 10, 11) In [14]: text.getStringAtOffset(10, pyatspi.TEXT_GRANULARITY_WORD) Out[14]: ('world', 6, 11) In [15]: text.getStringAtOffset(10, pyatspi.TEXT_GRANULARITY_SENTENCE) Out[15]: ('Hello world. ', 0, 13) In [16]: text.getStringAtOffset(10, pyatspi.TEXT_GRANULARITY_PARAGRAPH) Out[16]: ('Hello world. And another sentence.', 0, 34) In [17]: text.getNSelections() Out[17]: 1 In [18]: text.getSelection(0) Out[18]: (6, 11) Actual handling of text attributes is left for later (s. TODO comment in the newly added `lo_accessible_text_get_attributes`). [1] https://gitlab.gnome.org/GNOME/gtk/-/commit/0ca8d74842837b1ad5dc42c1fcff8b1270e5750b Change-Id: Icad236cd87285d9a336883e67b191f633e9e4413 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163733 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
-rw-r--r--vcl/Library_vclplug_gtk4.mk1
-rw-r--r--vcl/unx/gtk4/a11y.cxx7
-rw-r--r--vcl/unx/gtk4/gtkaccessibletext.cxx145
-rw-r--r--vcl/unx/gtk4/gtkaccessibletext.hxx20
4 files changed, 172 insertions, 1 deletions
diff --git a/vcl/Library_vclplug_gtk4.mk b/vcl/Library_vclplug_gtk4.mk
index 72ffeb08e267..a383414d17ed 100644
--- a/vcl/Library_vclplug_gtk4.mk
+++ b/vcl/Library_vclplug_gtk4.mk
@@ -90,6 +90,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk4,\
vcl/unx/gtk4/customcellrenderer \
vcl/unx/gtk4/gtkaccessibleeventlistener \
vcl/unx/gtk4/gtkaccessibleregistry \
+ vcl/unx/gtk4/gtkaccessibletext \
vcl/unx/gtk4/gtkdata \
vcl/unx/gtk4/gtkinst \
vcl/unx/gtk4/gtksys \
diff --git a/vcl/unx/gtk4/a11y.cxx b/vcl/unx/gtk4/a11y.cxx
index 19cb941b9158..41e49bf2845b 100644
--- a/vcl/unx/gtk4/a11y.cxx
+++ b/vcl/unx/gtk4/a11y.cxx
@@ -23,6 +23,7 @@
#include "a11y.hxx"
#include "gtkaccessibleeventlistener.hxx"
#include "gtkaccessibleregistry.hxx"
+#include "gtkaccessibletext.hxx"
#define OOO_TYPE_FIXED (ooo_fixed_get_type())
#define OOO_FIXED(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), OOO_TYPE_FIXED, OOoFixed))
@@ -410,9 +411,13 @@ const struct
GetGIfaceType const aGetGIfaceType;
const css::uno::Type& (*aGetUnoType)();
} TYPE_TABLE[] = {
+#if GTK_CHECK_VERSION(4, 13, 8)
+ { "Text", reinterpret_cast<GInterfaceInitFunc>(lo_accessible_text_init),
+ gtk_accessible_text_get_type, cppu::UnoType<css::accessibility::XAccessibleText>::get },
+#endif
#if GTK_CHECK_VERSION(4, 10, 0)
{ "Value", reinterpret_cast<GInterfaceInitFunc>(lo_accessible_range_init),
- gtk_accessible_range_get_type, cppu::UnoType<css::accessibility::XAccessibleValue>::get }
+ gtk_accessible_range_get_type, cppu::UnoType<css::accessibility::XAccessibleValue>::get },
#endif
};
diff --git a/vcl/unx/gtk4/gtkaccessibletext.cxx b/vcl/unx/gtk4/gtkaccessibletext.cxx
new file mode 100644
index 000000000000..32e1448a5f65
--- /dev/null
+++ b/vcl/unx/gtk4/gtkaccessibletext.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/TextSegment.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <sal/log.hxx>
+
+#include "a11y.hxx"
+#include "gtkaccessibletext.hxx"
+
+#if GTK_CHECK_VERSION(4, 13, 8)
+
+namespace
+{
+sal_Int16 lcl_GtkTextGranularityToUNOBoundaryType(GtkAccessibleTextGranularity eGranularity)
+{
+ switch (eGranularity)
+ {
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_CHARACTER:
+ return com::sun::star::accessibility::AccessibleTextType::CHARACTER;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_WORD:
+ return com::sun::star::accessibility::AccessibleTextType::WORD;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_SENTENCE:
+ return com::sun::star::accessibility::AccessibleTextType::SENTENCE;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_LINE:
+ return com::sun::star::accessibility::AccessibleTextType::LINE;
+ case GTK_ACCESSIBLE_TEXT_GRANULARITY_PARAGRAPH:
+ return com::sun::star::accessibility::AccessibleTextType::PARAGRAPH;
+ default:
+ assert(false && "Unhandled GtkAccessibleTextGranularity.");
+ return GTK_ACCESSIBLE_TEXT_GRANULARITY_CHARACTER;
+ }
+}
+
+css::uno::Reference<css::accessibility::XAccessibleText> getXText(GtkAccessibleText* pGtkText)
+{
+ LoAccessible* pAccessible = LO_ACCESSIBLE(pGtkText);
+ if (!pAccessible->uno_accessible)
+ return nullptr;
+
+ css::uno::Reference<css::accessibility::XAccessibleContext> xContext(
+ pAccessible->uno_accessible->getAccessibleContext());
+
+ css::uno::Reference<css::accessibility::XAccessibleText> xText(xContext, css::uno::UNO_QUERY);
+ return xText;
+}
+}
+
+static GBytes* lo_accessible_text_get_contents(GtkAccessibleText* self, unsigned int start,
+ unsigned int end)
+{
+ css::uno::Reference<css::accessibility::XAccessibleText> xText = getXText(self);
+ if (!xText.is())
+ return nullptr;
+
+ // G_MAXUINT has special meaning: end of the text
+ const sal_Int32 nEndIndex = (end == G_MAXUINT) ? xText->getCharacterCount() : end;
+
+ const OString sText
+ = rtl::OUStringToOString(xText->getTextRange(start, nEndIndex), RTL_TEXTENCODING_UTF8);
+ return g_bytes_new(sText.getStr(), sText.getLength());
+}
+
+static GBytes* lo_accessible_text_get_contents_at(GtkAccessibleText* self, unsigned int offset,
+ GtkAccessibleTextGranularity eGranularity,
+ unsigned int* start, unsigned int* end)
+{
+ css::uno::Reference<css::accessibility::XAccessibleText> xText = getXText(self);
+ if (!xText.is())
+ return nullptr;
+
+ if (offset < 0 || offset > o3tl::make_unsigned(xText->getCharacterCount()))
+ {
+ SAL_WARN("vcl.gtk",
+ "lo_accessible_text_get_contents_at called with invalid offset: " << offset);
+ return nullptr;
+ }
+
+ const sal_Int16 nUnoBoundaryType = lcl_GtkTextGranularityToUNOBoundaryType(eGranularity);
+ const css::accessibility::TextSegment aSegment
+ = xText->getTextAtIndex(offset, nUnoBoundaryType);
+ *start = o3tl::make_unsigned(aSegment.SegmentStart);
+ *end = o3tl::make_unsigned(aSegment.SegmentEnd);
+ const OString sText = rtl::OUStringToOString(aSegment.SegmentText, RTL_TEXTENCODING_UTF8);
+ return g_bytes_new(sText.getStr(), sText.getLength());
+}
+
+static unsigned int lo_accessible_text_get_caret_position(GtkAccessibleText* self)
+{
+ css::uno::Reference<css::accessibility::XAccessibleText> xText = getXText(self);
+ if (!xText.is())
+ return 0;
+
+ return std::max(0, xText->getCaretPosition());
+}
+
+static gboolean lo_accessible_text_get_selection(GtkAccessibleText* self, gsize* n_ranges,
+ GtkAccessibleTextRange** ranges)
+{
+ css::uno::Reference<css::accessibility::XAccessibleText> xText = getXText(self);
+ if (!xText.is())
+ return 0;
+
+ if (xText->getSelectedText().isEmpty())
+ return false;
+
+ const sal_Int32 nSelectionStart = xText->getSelectionStart();
+ const sal_Int32 nSelectionEnd = xText->getSelectionEnd();
+
+ *n_ranges = 1;
+ *ranges = g_new(GtkAccessibleTextRange, 1);
+ (*ranges)[0].start = std::min(nSelectionStart, nSelectionEnd);
+ (*ranges)[0].length = std::abs(nSelectionEnd - nSelectionStart);
+ return true;
+}
+
+static gboolean lo_accessible_text_get_attributes(GtkAccessibleText* /* self */,
+ unsigned int /* offset */, gsize* /* n_ranges */,
+ GtkAccessibleTextRange** /* ranges */,
+ char*** /* attribute_names */,
+ char*** /* attribute_values */)
+{
+ // TODO: implement
+ return false;
+}
+
+void lo_accessible_text_init(GtkAccessibleTextInterface* iface)
+{
+ iface->get_contents = lo_accessible_text_get_contents;
+ iface->get_contents_at = lo_accessible_text_get_contents_at;
+ iface->get_caret_position = lo_accessible_text_get_caret_position;
+ iface->get_selection = lo_accessible_text_get_selection;
+ iface->get_attributes = lo_accessible_text_get_attributes;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk4/gtkaccessibletext.hxx b/vcl/unx/gtk4/gtkaccessibletext.hxx
new file mode 100644
index 000000000000..3e8a08db0d73
--- /dev/null
+++ b/vcl/unx/gtk4/gtkaccessibletext.hxx
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#if GTK_CHECK_VERSION(4, 13, 8)
+
+void lo_accessible_text_init(GtkAccessibleTextInterface* iface);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */