File: single_selection.py

package info (click to toggle)
secrets 12.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,616 kB
  • sloc: python: 6,838; xml: 7; makefile: 4
file content (131 lines) | stat: -rw-r--r-- 4,195 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# SPDX-License-Identifier: GPL-3.0-only
from gi.repository import Gio, GObject, Gtk

from gsecrets.safe_element import SafeElement


class SingleSelection(GObject.Object, Gio.ListModel, Gtk.SelectionModel):
    def __init__(self, model):
        super().__init__()

        self._inner = Gtk.SingleSelection.new()
        self._inner.props.autoselect = False
        self._inner.props.can_unselect = True
        self._inner.props.model = model

        self._item_pos = Gtk.INVALID_LIST_POSITION
        self._hide_selected = False
        self._selected_item = None

        self._inner.connect("items-changed", self._on_items_changed)

    def do_get_item(self, pos):
        return self._inner.get_item(pos)

    def do_get_n_items(self):
        return self._inner.get_n_items()

    def do_get_item_type(self):
        return self._inner.get_item_type()

    def do_is_selected(self, pos):
        item_pos = self._item_pos
        if self._hide_selected or item_pos == Gtk.INVALID_LIST_POSITION:
            return False

        return pos == item_pos

    @GObject.Property(
        type=bool,
        default=False,
        flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.EXPLICIT_NOTIFY,
    )
    def hide_selected(self):
        return self._hide_selected

    @hide_selected.setter  # type: ignore
    def hide_selected(self, hide_selected):
        if self._hide_selected == hide_selected:
            return

        self._hide_selected = hide_selected

        if self._item_pos != Gtk.INVALID_LIST_POSITION:
            self.selection_changed(self._item_pos, 1)

        self.notify("hide_selected")

    @GObject.Property(
        type=SafeElement,
        flags=GObject.ParamFlags.READWRITE | GObject.ParamFlags.EXPLICIT_NOTIFY,
    )
    def selected_item(self):
        return self._selected_item

    def selected_item_pos(self) -> int:
        return self._item_pos

    @selected_item.setter  # type: ignore
    def selected_item(self, selected_item):
        if self._selected_item == selected_item:
            return

        pos = self._find_item_position(selected_item, 0, self.get_n_items())
        self._set_selected_item_inner(selected_item, pos)

    def _set_selected_item_inner(self, selected_item, pos):
        if self._item_pos == pos:
            return

        self._selected_item = selected_item

        old_pos = self._item_pos
        self._item_pos = pos

        if not self._hide_selected:
            if old_pos == Gtk.INVALID_LIST_POSITION:
                self.selection_changed(pos, 1)
            elif pos == Gtk.INVALID_LIST_POSITION:
                self.selection_changed(old_pos, 1)
            elif pos < old_pos:
                self.selection_changed(pos, old_pos - pos + 1)
            else:
                self.selection_changed(old_pos, pos - old_pos + 1)

        self.notify("selected-item")

    def _find_item_position(self, item, start_pos, end_pos):
        model = self._inner.props.model
        for pos in range(start_pos, end_pos):
            if model.get_item(pos) == item:
                return pos

        return Gtk.INVALID_LIST_POSITION

    def _on_items_changed(self, _model, position, removed, added):
        item_position = self._item_pos

        if selected_item := self._selected_item:
            if item_position == Gtk.INVALID_LIST_POSITION:
                # Maybe the item got newly added
                self._item_pos = self._find_item_position(
                    selected_item,
                    position,
                    position + added,
                )
            elif item_position < position:
                # Nothing to do, position stays the same
                pass
            elif item_position < position + removed:
                self._item_pos = self._find_item_position(
                    selected_item,
                    position,
                    position + added,
                )
            elif item_position + added >= removed:
                self._item_pos = item_position + added - removed
            else:
                # This should never happen.
                self._item_pos = 0

        self.items_changed(position, removed, added)