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

Commits:

2 changed files:

Changes:

  • gajim/gtk/chat_list.py
    ... ... @@ -22,7 +22,6 @@
    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
    
    ... ... @@ -75,7 +74,6 @@ def __init__(self, workspace_id: str) -> None:
    75 74
             self._chats: dict[tuple[str, JID], Any] = {}
    
    76 75
             self._current_filter: str = 'all'
    
    77 76
             self._current_filter_text: str = ''
    
    78
    -        self._chat_order: dict[tuple[str, JID], int] = {}
    
    79 77
     
    
    80 78
             self.get_style_context().add_class('chatlist')
    
    81 79
             self.set_filter_func(self._filter_func)
    
    ... ... @@ -97,12 +95,19 @@ def __init__(self, workspace_id: str) -> None:
    97 95
                 entries,
    
    98 96
                 Gdk.DragAction.MOVE)
    
    99 97
     
    
    98
    +        self._drag_row: Optional[ChatRow] = None
    
    99
    +        self._row_before: Optional[ChatRow] = None
    
    100
    +        self._row_after: Optional[ChatRow] = None
    
    101
    +        self._chat_order: list[ChatRow] = []
    
    102
    +
    
    100 103
             self.register_events([
    
    101 104
                 ('account-enabled', ged.GUI2, self._on_account_changed),
    
    102 105
                 ('account-disabled', ged.GUI2, self._on_account_changed),
    
    103 106
                 ('bookmarks-received', ged.GUI1, self._on_bookmarks_received),
    
    104 107
             ])
    
    105 108
     
    
    109
    +        self.connect('drag-motion', self._on_drag_motion)
    
    110
    +        self.connect('drag-data-received', self._on_drag_data_received)
    
    106 111
             self.connect('destroy', self._on_destroy)
    
    107 112
     
    
    108 113
             self._timer_id = GLib.timeout_add_seconds(60, self._update_timer)
    
    ... ... @@ -149,6 +154,100 @@ def emit_unread_changed(self) -> None:
    149 154
                            self._workspace_id,
    
    150 155
                            count)
    
    151 156
     
    
    157
    +    def _get_row_before(self, row: ChatRow) -> Optional[ChatRow]:
    
    158
    +        row = cast(ChatRow, self.get_row_at_index(row.get_index() - 1))
    
    159
    +        return row
    
    160
    +
    
    161
    +    def _get_row_after(self, row: ChatRow) -> Optional[ChatRow]:
    
    162
    +        row = cast(ChatRow, self.get_row_at_index(row.get_index() + 1))
    
    163
    +        return row
    
    164
    +
    
    165
    +    def _get_last_row(self) -> ChatRow:
    
    166
    +        row = cast(ChatRow, self.get_row_at_index(len(self.get_children())))
    
    167
    +        return row
    
    168
    +
    
    169
    +    def set_drag_row(self, row: ChatRow) -> None:
    
    170
    +        self._drag_row = row
    
    171
    +
    
    172
    +    def _on_drag_motion(self,
    
    173
    +                        _widget: Gtk.Widget,
    
    174
    +                        _drag_context: Gdk.DragContext,
    
    175
    +                        _x_coord: int,
    
    176
    +                        y_coord: int,
    
    177
    +                        _time: int
    
    178
    +                        ) -> bool:
    
    179
    +
    
    180
    +        row = cast(ChatRow, self.get_row_at_y(y_coord))
    
    181
    +        if row:
    
    182
    +            alloc = row.get_allocation()
    
    183
    +            hover_row_y = alloc.y
    
    184
    +            hover_row_height = alloc.height
    
    185
    +
    
    186
    +            if y_coord < hover_row_y + hover_row_height / 2:
    
    187
    +                self._row_before = self._get_row_before(row)
    
    188
    +                self._row_after = row
    
    189
    +            else:
    
    190
    +                self._row_before = row
    
    191
    +                self._row_after = self._get_row_after(row)
    
    192
    +        else:
    
    193
    +            self._row_before = self._get_last_row()
    
    194
    +            self._row_after = None
    
    195
    +
    
    196
    +        if self._drag_row in (self._row_before, self._row_after):
    
    197
    +            return False
    
    198
    +
    
    199
    +        return True
    
    200
    +
    
    201
    +    def _on_drag_data_received(self,
    
    202
    +                               _widget: Gtk.Widget,
    
    203
    +                               _drag_context: Gdk.DragContext,
    
    204
    +                               _x_coord: int,
    
    205
    +                               y_coord: int,
    
    206
    +                               selection_data: Gtk.SelectionData,
    
    207
    +                               _info: int,
    
    208
    +                               _time: int
    
    209
    +                               ) -> None:
    
    210
    +
    
    211
    +        item_type = selection_data.get_data_type().name()
    
    212
    +        if item_type == 'CHAT_LIST_ITEM':
    
    213
    +            self._change_pinned_order()
    
    214
    +        else:
    
    215
    +            log.debug('Unknown item type dropped')
    
    216
    +
    
    217
    +    def _change_pinned_order(self) -> None:
    
    218
    +        assert self._drag_row is not None
    
    219
    +
    
    220
    +        if not self._drag_row.is_pinned:
    
    221
    +            log.debug('Dragged row is not pinned')
    
    222
    +            return
    
    223
    +
    
    224
    +        if self._drag_row in (self._row_before, self._row_after):
    
    225
    +            log.debug('Dragged row dropped on self')
    
    226
    +            return
    
    227
    +
    
    228
    +        if self._row_after is None:
    
    229
    +            log.debug('Dragged row dropped in empty space')
    
    230
    +            return
    
    231
    +
    
    232
    +        self._chat_order.remove(self._drag_row)
    
    233
    +
    
    234
    +        if self._row_before is None:
    
    235
    +            self._chat_order.insert(0, self._drag_row)
    
    236
    +        else:
    
    237
    +            if self._row_before.position < self._drag_row.position:
    
    238
    +                offset = 1
    
    239
    +            else:
    
    240
    +                offset = 0
    
    241
    +            self._chat_order.insert(
    
    242
    +                self._row_before.position + offset, self._drag_row)
    
    243
    +
    
    244
    +        for row in cast(list[ChatRow], self.get_children()):
    
    245
    +            if row in self._chat_order:
    
    246
    +                row.position = self._chat_order.index(row)
    
    247
    +
    
    248
    +        app.window.store_open_chats(self._workspace_id)
    
    249
    +        self.invalidate_sort()
    
    250
    +
    
    152 251
         def _on_destroy(self, _widget: Gtk.Widget) -> None:
    
    153 252
             GLib.source_remove(self._timer_id)
    
    154 253
     
    
    ... ... @@ -273,10 +372,11 @@ def add_chat(self,
    273 372
                           jid,
    
    274 373
                           type_,
    
    275 374
                           pinned,
    
    276
    -                      self._chat_order)
    
    375
    +                      position)
    
    277 376
     
    
    278
    -        self._chat_order[key] = position
    
    279 377
             self._chats[key] = row
    
    378
    +        if position != -1:
    
    379
    +            self._chat_order.insert(position, row)
    
    280 380
     
    
    281 381
             self.add(row)
    
    282 382
     
    
    ... ... @@ -353,6 +453,16 @@ def select_chat_number(self, number: int) -> None:
    353 453
         def toggle_chat_pinned(self, account: str, jid: JID) -> None:
    
    354 454
             row = self._chats[(account, jid)]
    
    355 455
             row.toggle_pinned()
    
    456
    +
    
    457
    +        self._chat_order.clear()
    
    458
    +        chat_rows = cast(list[ChatRow], self.get_children())
    
    459
    +        for row in chat_rows:
    
    460
    +            if row.is_pinned:
    
    461
    +                row.position = chat_rows.index(row)
    
    462
    +                self._chat_order.append(row)
    
    463
    +            else:
    
    464
    +                row.position = -1
    
    465
    +
    
    356 466
             self.invalidate_sort()
    
    357 467
     
    
    358 468
         def remove_chat(self,
    
    ... ... @@ -362,6 +472,8 @@ def remove_chat(self,
    362 472
                         ) -> None:
    
    363 473
     
    
    364 474
             row = self._chats.pop((account, jid))
    
    475
    +        if row.position != -1:
    
    476
    +            self._chat_order.remove(row)
    
    365 477
             self.remove(row)
    
    366 478
             row.destroy()
    
    367 479
             if emit_unread:
    
    ... ... @@ -563,29 +675,6 @@ def _on_bookmarks_received(self, _event: events.BookmarksReceived) -> None:
    563 675
             for row in rows:
    
    564 676
                 row.update_name()
    
    565 677
     
    
    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 678
     
    
    590 679
     class ChatRow(Gtk.ListBoxRow):
    
    591 680
         def __init__(self,
    
    ... ... @@ -594,7 +683,7 @@ def __init__(self,
    594 683
                      jid: JID,
    
    595 684
                      type_: str,
    
    596 685
                      pinned: bool,
    
    597
    -                 position: dict[tuple[str, JID], int]
    
    686
    +                 position: int
    
    598 687
                      ) -> None:
    
    599 688
     
    
    600 689
             Gtk.ListBoxRow.__init__(self)
    
    ... ... @@ -767,7 +856,11 @@ def is_pinned(self) -> bool:
    767 856
     
    
    768 857
         @property
    
    769 858
         def position(self) -> int:
    
    770
    -        return self._position.get((self.account, self.jid), -1)
    
    859
    +        return self._position
    
    860
    +
    
    861
    +    @position.setter
    
    862
    +    def position(self, position: int) -> None:
    
    863
    +        self._position = position
    
    771 864
     
    
    772 865
         def _on_button_press(self,
    
    773 866
                              _widget: Gtk.Widget,
    
    ... ... @@ -799,6 +892,10 @@ def _on_drag_begin(self,
    799 892
                            widget: Gtk.Widget,
    
    800 893
                            drag_context: Gdk.DragContext
    
    801 894
                            ) -> None:
    
    895
    +
    
    896
    +        chat_list = cast(ChatList, self.get_parent())
    
    897
    +        chat_list.set_drag_row(self)
    
    898
    +
    
    802 899
             # Use rendered ChatListRow as drag icon
    
    803 900
             alloc = self.get_allocation()
    
    804 901
             surface = cairo.ImageSurface(
    

  • gajim/gtk/main.py
    ... ... @@ -654,6 +654,10 @@ def update_workspace(self, workspace_id: str) -> None:
    654 654
             self._chat_page.update_workspace(workspace_id)
    
    655 655
             self._workspace_side_bar.update_avatar(workspace_id)
    
    656 656
     
    
    657
    +    def store_open_chats(self, workspace_id: str) -> None:
    
    658
    +        chat_list_stack = self._chat_page.get_chat_list_stack()
    
    659
    +        chat_list_stack.store_open_chats(workspace_id)
    
    660
    +
    
    657 661
         def get_chat_list(self, workspace_id: str) -> ChatList:
    
    658 662
             chat_list_stack = self._chat_page.get_chat_list_stack()
    
    659 663
             return chat_list_stack.get_chatlist(workspace_id)