[pygobject/wip/jfelder/gtk4-sort-list-model] gtk: Add override for Gtk.CustomSorter.set_sort_func




commit 348a97c926bb02fac648200f30ab4963fbf3ce76
Author: Jean Felder <jfelder src gnome org>
Date:   Sat Apr 4 13:09:37 2020 +0200

    gtk: Add override for Gtk.CustomSorter.set_sort_func
    
    This function use CompareDataFunc which works with pointers and
    doesn't know anything about GObjects.
    
    The same logic as in Gio.List.sort and Gio.List.insert_sorted is
    used.
    
    An associated unit_test is also added.

 gi/overrides/Gio.py         | 17 ++------
 gi/overrides/Gtk.py         | 18 ++++++++-
 gi/overrides/__init__.py    | 12 +++++-
 tests/test_overrides_gtk.py | 94 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 124 insertions(+), 17 deletions(-)
---
diff --git a/gi/overrides/Gio.py b/gi/overrides/Gio.py
index 5cc12a6c..c807fe0b 100644
--- a/gi/overrides/Gio.py
+++ b/gi/overrides/Gio.py
@@ -21,9 +21,8 @@
 import warnings
 
 from .._ossighelper import wakeup_on_signal, register_sigint_fallback
-from ..overrides import override, deprecated_init
+from ..overrides import override, deprecated_init, wrap_list_store_sort_func
 from ..module import get_introspection_module
-from gi._gi import pygobject_new_full
 from gi import PyGIWarning
 
 from gi.repository import GLib
@@ -461,16 +460,6 @@ ListModel = override(ListModel)
 __all__.append('ListModel')
 
 
-def _wrap_list_store_sort_func(func):
-
-    def wrap(a, b, *user_data):
-        a = pygobject_new_full(a, False)
-        b = pygobject_new_full(b, False)
-        return func(a, b, *user_data)
-
-    return wrap
-
-
 if (GLib.MAJOR_VERSION, GLib.MINOR_VERSION, GLib.MICRO_VERSION) < (2, 57, 1):
     # The "additions" functionality in splice() was broken in older glib
     # https://bugzilla.gnome.org/show_bug.cgi?id=795307
@@ -487,11 +476,11 @@ else:
 class ListStore(Gio.ListStore):
 
     def sort(self, compare_func, *user_data):
-        compare_func = _wrap_list_store_sort_func(compare_func)
+        compare_func = wrap_list_store_sort_func(compare_func)
         return super(ListStore, self).sort(compare_func, *user_data)
 
     def insert_sorted(self, item, compare_func, *user_data):
-        compare_func = _wrap_list_store_sort_func(compare_func)
+        compare_func = wrap_list_store_sort_func(compare_func)
         return super(ListStore, self).insert_sorted(
             item, compare_func, *user_data)
 
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py
index 5b810b60..00ab5715 100644
--- a/gi/overrides/Gtk.py
+++ b/gi/overrides/Gtk.py
@@ -26,7 +26,8 @@ from collections import abc
 from gi.repository import GObject
 from .._ossighelper import wakeup_on_signal, register_sigint_fallback
 from .._gtktemplate import Template
-from ..overrides import override, strip_boolean_result, deprecated_init
+from ..overrides import (override, strip_boolean_result, deprecated_init,
+                         wrap_list_store_sort_func)
 from ..module import get_introspection_module
 from gi import PyGIDeprecationWarning
 
@@ -1637,6 +1638,21 @@ class TreeModelFilter(Gtk.TreeModelFilter):
 TreeModelFilter = override(TreeModelFilter)
 __all__.append('TreeModelFilter')
 
+if GTK4:
+    class CustomSorter(Gtk.CustomSorter):
+
+        def __init__(self, sort_func=None, user_data=None):
+            Gtk.CustomSorter.__init__(self)
+            if sort_func:
+                self.set_sort_func(sort_func, user_data)
+
+        def set_sort_func(self, sort_func, user_data=None):
+            compare_func = wrap_list_store_sort_func(sort_func)
+            return super(CustomSorter, self).set_sort_func(compare_func, user_data)
+
+    CustomSorter = override(CustomSorter)
+    __all__.append("CustomSorter")
+
 if GTK3:
     class Menu(Gtk.Menu):
         def popup(self, parent_menu_shell, parent_menu_item, func, data, button, activate_time):
diff --git a/gi/overrides/__init__.py b/gi/overrides/__init__.py
index 1572d251..37dfbbe5 100644
--- a/gi/overrides/__init__.py
+++ b/gi/overrides/__init__.py
@@ -6,7 +6,7 @@ import sys
 from pkgutil import get_loader
 
 from gi import PyGIDeprecationWarning
-from gi._gi import CallableInfo
+from gi._gi import CallableInfo, pygobject_new_full
 from gi._constants import \
     TYPE_NONE, \
     TYPE_INVALID
@@ -341,3 +341,13 @@ def strip_boolean_result(method, exc_type=None, exc_str=None, fail_ret=None):
                 raise exc_type(exc_str or 'call failed')
             return fail_ret
     return wrapped
+
+
+def wrap_list_store_sort_func(func):
+
+    def wrap(a, b, *user_data):
+        a = pygobject_new_full(a, False)
+        b = pygobject_new_full(b, False)
+        return func(a, b, *user_data)
+
+    return wrap
diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py
index 61cff2cb..c74e224e 100644
--- a/tests/test_overrides_gtk.py
+++ b/tests/test_overrides_gtk.py
@@ -14,7 +14,7 @@ from .helper import ignore_gi_deprecation_warnings, capture_glib_warnings
 
 import gi.overrides
 import gi.types
-from gi.repository import GLib, GObject
+from gi.repository import Gio, GLib, GObject
 
 try:
     from gi.repository import Gtk, GdkPixbuf, Gdk
@@ -1236,6 +1236,98 @@ class TestTreeModelRow(unittest.TestCase):
         assert row.previous.previous is None
 
 
+@unittest.skipUnless(Gtk, "Gtk not available")
+@unittest.skipIf(Gtk_version != "4.0", "only in gtk4")
+class TestCustomSorter():
+    class Person(GObject.GObject):
+
+        name = GObject.Property(type=str, default="")
+
+        def __init__(self, name):
+            super().__init__()
+            self.props.name = name
+
+    user_data = "user_data"
+
+    def names_sort(self, name_a, name_b, user_data):
+        assert user_data is None
+        if name_a.props.name < name_b.props.name:
+            return Gtk.Ordering.SMALLER
+        elif name_a.props.name > name_b.props.name:
+            return Gtk.Ordering.LARGER
+        else:
+            return Gtk.Ordering.EQUAL
+
+    def names_invert_sort(self, name_a, name_b, user_data):
+        assert user_data == self.user_data
+        if name_a.props.name < name_b.props.name:
+            return Gtk.Ordering.LARGER
+        elif name_a.props.name > name_b.props.name:
+            return Gtk.Ordering.SMALLER
+        else:
+            return Gtk.Ordering.EQUAL
+
+    def test_custom_sorter_init(self):
+        custom_sorter_empty = Gtk.CustomSorter()
+        assert isinstance(custom_sorter_empty, Gtk.CustomSorter)
+
+        custom_sorter_empty.set_sort_func(self.names_sort)
+        assert isinstance(custom_sorter_empty, Gtk.CustomSorter)
+
+        custom_sorter_empty.set_sort_func(
+            self.names_invert_sort, self.user_data)
+        assert isinstance(custom_sorter_empty, Gtk.CustomSorter)
+
+        custom_sorter_empty_sort = Gtk.CustomSorter(None)
+        assert isinstance(custom_sorter_empty_sort, Gtk.CustomSorter)
+
+        custom_sorter_with_sort = Gtk.CustomSorter(self.names_sort, None)
+        assert isinstance(custom_sorter_with_sort, Gtk.CustomSorter)
+
+        custom_sorter_with_sort_ud = Gtk.CustomSorter(
+            self.names_invert_sort, self.user_data)
+        assert isinstance(custom_sorter_with_sort_ud, Gtk.CustomSorter)
+
+    def test_custom_sorter_with_model(self):
+        model = Gio.ListStore.new(self.Person)
+        sort_model = Gtk.SortListModel.new(model)
+        assert sort_model.props.sorter is None
+
+        empty_sorter = Gtk.CustomSorter()
+        empty_sorter.set_sort_func(self.names_sort, None)
+        sort_model.set_sorter(empty_sorter)
+        assert sort_model.props.sorter is empty_sorter
+
+        john = self.Person("john")
+        bob = self.Person("bob")
+        model.append(john)
+        model.append(bob)
+        assert sort_model[0] == bob
+        assert sort_model[1] == john
+
+        alice = self.Person("alice")
+        model.append(alice)
+        assert sort_model[0] == alice
+        assert sort_model[2] == john
+
+        new_model = Gio.ListStore.new(self.Person)
+        new_sort_model = Gtk.SortListModel.new(new_model)
+        assert new_sort_model.props.sorter is None
+
+        invert_sorter = Gtk.CustomSorter(
+            self.names_invert_sort, self.user_data)
+        new_sort_model.set_sorter(invert_sorter)
+        assert new_sort_model.props.sorter is invert_sorter
+
+        beatles = ["john", "paul", "george", "ringo"]
+        for member in beatles:
+            new_model.append(self.Person(member))
+
+        expected_result = ["ringo", "paul", "john", "george"]
+        for result, member in zip(new_sort_model, expected_result):
+            assert result.props.name == member
+
+
 @ignore_gi_deprecation_warnings
 @unittest.skipUnless(Gtk, 'Gtk not available')
 class TestTreeModel(unittest.TestCase):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]