Daniel Brötzmann pushed to branch pinned-chat-dnd at gajim / gajim

Commits:

2 changed files:

Changes:

  • gajim/gtk/chat_list.py
    ... ... @@ -22,11 +22,11 @@
    22 22
     import logging
    
    23 23
     import time
    
    24 24
     import pickle
    
    25
    -import operator
    
    26 25
     
    
    27 26
     from gi.repository import Gio
    
    28 27
     from gi.repository import Gdk
    
    29 28
     from gi.repository import GLib
    
    29
    +from gi.repository import GObject
    
    30 30
     from gi.repository import Gtk
    
    31 31
     import cairo
    
    32 32
     
    
    ... ... @@ -67,6 +67,13 @@
    67 67
     
    
    68 68
     
    
    69 69
     class ChatList(Gtk.ListBox, EventHelper):
    
    70
    +
    
    71
    +    __gsignals__ = {
    
    72
    +        'chat-order-changed': (GObject.SignalFlags.RUN_LAST,
    
    73
    +                               None,
    
    74
    +                               ()),
    
    75
    +    }
    
    76
    +
    
    70 77
         def __init__(self, workspace_id: str) -> None:
    
    71 78
             Gtk.ListBox.__init__(self)
    
    72 79
             EventHelper.__init__(self)
    
    ... ... @@ -75,7 +82,6 @@ def __init__(self, workspace_id: str) -> None:
    75 82
             self._chats: dict[tuple[str, JID], Any] = {}
    
    76 83
             self._current_filter: str = 'all'
    
    77 84
             self._current_filter_text: str = ''
    
    78
    -        self._chat_order: dict[tuple[str, JID], int] = {}
    
    79 85
     
    
    80 86
             self.get_style_context().add_class('chatlist')
    
    81 87
             self.set_filter_func(self._filter_func)
    
    ... ... @@ -97,12 +103,16 @@ def __init__(self, workspace_id: str) -> None:
    97 103
                 entries,
    
    98 104
                 Gdk.DragAction.MOVE)
    
    99 105
     
    
    106
    +        self._drag_row: Optional[ChatRow] = None
    
    107
    +        self._chat_order: list[ChatRow] = []
    
    108
    +
    
    100 109
             self.register_events([
    
    101 110
                 ('account-enabled', ged.GUI2, self._on_account_changed),
    
    102 111
                 ('account-disabled', ged.GUI2, self._on_account_changed),
    
    103 112
                 ('bookmarks-received', ged.GUI1, self._on_bookmarks_received),
    
    104 113
             ])
    
    105 114
     
    
    115
    +        self.connect('drag-data-received', self._on_drag_data_received)
    
    106 116
             self.connect('destroy', self._on_destroy)
    
    107 117
     
    
    108 118
             self._timer_id = GLib.timeout_add_seconds(60, self._update_timer)
    
    ... ... @@ -149,6 +159,94 @@ def emit_unread_changed(self) -> None:
    149 159
                            self._workspace_id,
    
    150 160
                            count)
    
    151 161
     
    
    162
    +    def _get_row_before(self, row: ChatRow) -> Optional[ChatRow]:
    
    163
    +        row = cast(ChatRow, self.get_row_at_index(row.get_index() - 1))
    
    164
    +        return row
    
    165
    +
    
    166
    +    def _get_row_after(self, row: ChatRow) -> Optional[ChatRow]:
    
    167
    +        row = cast(ChatRow, self.get_row_at_index(row.get_index() + 1))
    
    168
    +        return row
    
    169
    +
    
    170
    +    def _get_last_row(self) -> ChatRow:
    
    171
    +        row = cast(ChatRow, self.get_row_at_index(len(self.get_children())))
    
    172
    +        return row
    
    173
    +
    
    174
    +    def set_drag_row(self, row: ChatRow) -> None:
    
    175
    +        self._drag_row = row
    
    176
    +
    
    177
    +    def _on_drag_data_received(self,
    
    178
    +                               _widget: Gtk.Widget,
    
    179
    +                               _drag_context: Gdk.DragContext,
    
    180
    +                               _x_coord: int,
    
    181
    +                               y_coord: int,
    
    182
    +                               selection_data: Gtk.SelectionData,
    
    183
    +                               _info: int,
    
    184
    +                               _time: int
    
    185
    +                               ) -> None:
    
    186
    +
    
    187
    +        item_type = selection_data.get_data_type().name()
    
    188
    +        if item_type == 'CHAT_LIST_ITEM':
    
    189
    +            row = cast(ChatRow, self.get_row_at_y(y_coord))
    
    190
    +            if row:
    
    191
    +                alloc = row.get_allocation()
    
    192
    +                hover_row_y = alloc.y
    
    193
    +                hover_row_height = alloc.height
    
    194
    +
    
    195
    +                if y_coord < hover_row_y + hover_row_height / 2:
    
    196
    +                    row_before = self._get_row_before(row)
    
    197
    +                    row_after = row
    
    198
    +                else:
    
    199
    +                    row_before = row
    
    200
    +                    row_after = self._get_row_after(row)
    
    201
    +            else:
    
    202
    +                row_before = self._get_last_row()
    
    203
    +                row_after = None
    
    204
    +
    
    205
    +            if self._drag_row in (row_before, row_after):
    
    206
    +                return
    
    207
    +
    
    208
    +            self._change_pinned_order(row_before, row_after)
    
    209
    +        else:
    
    210
    +            log.debug('Unknown item type dropped')
    
    211
    +
    
    212
    +    def _change_pinned_order(self,
    
    213
    +                             row_before: Optional[ChatRow],
    
    214
    +                             row_after: Optional[ChatRow]
    
    215
    +                             ) -> None:
    
    216
    +
    
    217
    +        assert self._drag_row is not None
    
    218
    +
    
    219
    +        if not self._drag_row.is_pinned:
    
    220
    +            log.debug('Dropped row is not pinned')
    
    221
    +            return
    
    222
    +
    
    223
    +        if self._drag_row in (row_before, row_after):
    
    224
    +            # TODO: already checked this case!?
    
    225
    +            log.debug('Dropped row on self')
    
    226
    +            return
    
    227
    +
    
    228
    +        if row_after is None:
    
    229
    +            log.debug('Dropped row into empty space')
    
    230
    +            return
    
    231
    +
    
    232
    +        self._chat_order.remove(self._drag_row)
    
    233
    +
    
    234
    +        if row_before is None:
    
    235
    +            self._chat_order.insert(0, self._drag_row)
    
    236
    +        else:
    
    237
    +            if row_before.position < self._drag_row.position:
    
    238
    +                offset = 1
    
    239
    +            else:
    
    240
    +                offset = 0
    
    241
    +            self._chat_order.insert(
    
    242
    +                row_before.position + offset, self._drag_row)
    
    243
    +
    
    244
    +        for row in self._chat_order:
    
    245
    +            row.position = self._chat_order.index(row)
    
    246
    +
    
    247
    +        self.emit('chat-order-changed')
    
    248
    +        self.invalidate_sort()
    
    249
    +
    
    152 250
         def _on_destroy(self, _widget: Gtk.Widget) -> None:
    
    153 251
             GLib.source_remove(self._timer_id)
    
    154 252
     
    
    ... ... @@ -273,10 +371,11 @@ def add_chat(self,
    273 371
                           jid,
    
    274 372
                           type_,
    
    275 373
                           pinned,
    
    276
    -                      self._chat_order)
    
    374
    +                      position)
    
    277 375
     
    
    278
    -        self._chat_order[key] = position
    
    279 376
             self._chats[key] = row
    
    377
    +        if position != -1:
    
    378
    +            self._chat_order.insert(position, row)
    
    280 379
     
    
    281 380
             self.add(row)
    
    282 381
     
    
    ... ... @@ -353,6 +452,16 @@ def select_chat_number(self, number: int) -> None:
    353 452
         def toggle_chat_pinned(self, account: str, jid: JID) -> None:
    
    354 453
             row = self._chats[(account, jid)]
    
    355 454
             row.toggle_pinned()
    
    455
    +
    
    456
    +        self._chat_order.clear()
    
    457
    +        chat_rows = cast(list[ChatRow], self.get_children())
    
    458
    +        for row in chat_rows:
    
    459
    +            if row.is_pinned:
    
    460
    +                row.position = chat_rows.index(row)
    
    461
    +                self._chat_order.append(row)
    
    462
    +            else:
    
    463
    +                row.position = -1
    
    464
    +
    
    356 465
             self.invalidate_sort()
    
    357 466
     
    
    358 467
         def remove_chat(self,
    
    ... ... @@ -362,6 +471,8 @@ def remove_chat(self,
    362 471
                         ) -> None:
    
    363 472
     
    
    364 473
             row = self._chats.pop((account, jid))
    
    474
    +        if row.position != -1:
    
    475
    +            self._chat_order.remove(row)
    
    365 476
             self.remove(row)
    
    366 477
             row.destroy()
    
    367 478
             if emit_unread:
    
    ... ... @@ -563,29 +674,6 @@ def _on_bookmarks_received(self, _event: events.BookmarksReceived) -> None:
    563 674
             for row in rows:
    
    564 675
                 row.update_name()
    
    565 676
     
    
    566
    -    def _change_pinned_order(self,
    
    567
    -                             row: ChatRow,
    
    568
    -                             row_after: Optional[ChatRow]) -> None:
    
    569
    -
    
    570
    -        # Make list ordered by position of tuple[account, JID]
    
    571
    -        order = [item[0] for item in sorted(self._chat_order.items(),
    
    572
    -                                            key=operator.itemgetter(1))]
    
    573
    -
    
    574
    -        key = (row.account, row.jid)
    
    575
    -
    
    576
    -        order.remove(key)
    
    577
    -        if row_after is None or not row_after.is_pinned:
    
    578
    -            order.append(key)
    
    579
    -        else:
    
    580
    -            pos = order.index((row_after.account, row_after.jid))
    
    581
    -            order.insert(pos, key)
    
    582
    -
    
    583
    -        self._chat_order.clear()
    
    584
    -        for pos, value in enumerate(order):
    
    585
    -            self._chat_order[value] = pos
    
    586
    -
    
    587
    -        self.invalidate_sort()
    
    588
    -
    
    589 677
     
    
    590 678
     class ChatRow(Gtk.ListBoxRow):
    
    591 679
         def __init__(self,
    
    ... ... @@ -594,7 +682,7 @@ def __init__(self,
    594 682
                      jid: JID,
    
    595 683
                      type_: str,
    
    596 684
                      pinned: bool,
    
    597
    -                 position: dict[tuple[str, JID], int]
    
    685
    +                 position: int
    
    598 686
                      ) -> None:
    
    599 687
     
    
    600 688
             Gtk.ListBoxRow.__init__(self)
    
    ... ... @@ -767,7 +855,11 @@ def is_pinned(self) -> bool:
    767 855
     
    
    768 856
         @property
    
    769 857
         def position(self) -> int:
    
    770
    -        return self._position.get((self.account, self.jid), -1)
    
    858
    +        return self._position
    
    859
    +
    
    860
    +    @position.setter
    
    861
    +    def position(self, position: int) -> None:
    
    862
    +        self._position = position
    
    771 863
     
    
    772 864
         def _on_button_press(self,
    
    773 865
                              _widget: Gtk.Widget,
    
    ... ... @@ -799,6 +891,10 @@ def _on_drag_begin(self,
    799 891
                            widget: Gtk.Widget,
    
    800 892
                            drag_context: Gdk.DragContext
    
    801 893
                            ) -> None:
    
    894
    +
    
    895
    +        chat_list = cast(ChatList, self.get_parent())
    
    896
    +        chat_list.set_drag_row(self)
    
    897
    +
    
    802 898
             # Use rendered ChatListRow as drag icon
    
    803 899
             alloc = self.get_allocation()
    
    804 900
             surface = cairo.ImageSurface(
    

  • gajim/gtk/chat_list_stack.py
    ... ... @@ -14,7 +14,6 @@
    14 14
     
    
    15 15
     from __future__ import annotations
    
    16 16
     
    
    17
    -from typing import Dict
    
    18 17
     from typing import Optional
    
    19 18
     from typing import cast
    
    20 19
     
    
    ... ... @@ -66,7 +65,7 @@ def __init__(self,
    66 65
             self.set_vexpand(True)
    
    67 66
             self.set_vhomogeneous(False)
    
    68 67
     
    
    69
    -        self._chat_lists: Dict[str, ChatList] = {}
    
    68
    +        self._chat_lists: dict[str, ChatList] = {}
    
    70 69
     
    
    71 70
             self._last_visible_child_name: str = 'default'
    
    72 71
     
    
    ... ... @@ -153,6 +152,7 @@ def _on_search_changed(self, search_entry: Gtk.SearchEntry) -> None:
    153 152
         def add_chat_list(self, workspace_id: str) -> ChatList:
    
    154 153
             chat_list = ChatList(workspace_id)
    
    155 154
             chat_list.connect('row-selected', self._on_row_selected)
    
    155
    +        chat_list.connect('chat-order-changed', self._on_chat_order_changed)
    
    156 156
     
    
    157 157
             self._chat_lists[workspace_id] = chat_list
    
    158 158
             self.add_named(chat_list, workspace_id)
    
    ... ... @@ -179,6 +179,12 @@ def _on_row_selected(self,
    179 179
     
    
    180 180
             self.emit('chat-selected', row.workspace_id, row.account, row.jid)
    
    181 181
     
    
    182
    +    def _on_chat_order_changed(self,
    
    183
    +                               chat_list: ChatList
    
    184
    +                               ) -> None:
    
    185
    +
    
    186
    +        self.store_open_chats(chat_list.workspace_id)
    
    187
    +
    
    182 188
         def show_chat_list(self, workspace_id: str) -> None:
    
    183 189
             cur_workspace_id = self.get_visible_child_name()
    
    184 190
             if cur_workspace_id == workspace_id: