Philipp Hörist pushed to branch master at gajim / gajim
Commits: 8e88bb56 by Philipp Hörist at 2022-09-18T22:24:15+02:00 refactor: Merge ScrolledView into ConversationView
- - - - - 7ab50205 by Philipp Hörist at 2022-09-18T22:24:15+02:00 refactor: ConversationView: Use action for Scroll Up/Down
- - - - -
6 changed files:
- gajim/data/other/shortcuts.json - gajim/gtk/const.py - gajim/gtk/control.py - − gajim/gtk/conversation/scrolled.py - gajim/gtk/conversation/view.py - gajim/gtk/message_actions_box.py
Changes:
===================================== gajim/data/other/shortcuts.json ===================================== @@ -37,5 +37,7 @@ "win.switch-workspace-7": ["<Primary>7", "<Primary>KP_7"], "win.switch-workspace-8": ["<Primary>8", "<Primary>KP_8"], "win.switch-workspace-9": ["<Primary>9", "<Primary>KP_9"], - "win.toggle-chat-list": ["<Primary>R"] + "win.toggle-chat-list": ["<Primary>R"], + "win.scroll-view-up": ["<Shift>Page_Up"], + "win.scroll-view-down": ["<Shift>Page_Down"] }
===================================== gajim/gtk/const.py ===================================== @@ -214,6 +214,8 @@ def __str__(self): ('muc-change-role', 'as', False), ('muc-change-affiliation', 'as', False), ('muc-request-voice', None, False), + ('scroll-view-up', None, True), + ('scroll-view-down', None, True), ]
===================================== gajim/gtk/control.py ===================================== @@ -48,8 +48,8 @@ from gajim.common.modules.contacts import GroupchatContact from gajim.common.modules.httpupload import HTTPFileTransfer from gajim.common.storage.archive import ConversationRow +from gajim.gtk.conversation.view import ConversationView
-from gajim.gui.conversation.scrolled import ScrolledView from gajim.gui.conversation.jump_to_end_button import JumpToEndButton from gajim.gui.builder import get_builder from gajim.gui.groupchat_roster import GroupchatRoster @@ -69,15 +69,13 @@ def __init__(self) -> None:
self._ui = get_builder('chat_control.ui')
- self._scrolled_view = ScrolledView() + self._scrolled_view = ConversationView() self._scrolled_view.connect('autoscroll-changed', self._on_autoscroll_changed) self._scrolled_view.connect('request-history', self._fetch_n_lines_history, 20) self._ui.conv_view_overlay.add(self._scrolled_view)
- self.conversation_view = self._scrolled_view.get_view() - self._groupchat_state = GroupchatState() self._ui.conv_view_overlay.add_overlay(self._groupchat_state)
@@ -136,7 +134,6 @@ def clear(self) -> None:
self._contact = None self._client = None - self.reset_view() self._scrolled_view.clear() self._groupchat_state.clear() self._roster.clear() @@ -199,7 +196,7 @@ def switch_contact(self, contact: Union[BareContact, muc_data = self._client.get_module('MUC').get_muc_data( str(contact.jid)) if muc_data is not None and muc_data.subject is not None: - self.conversation_view.add_muc_subject( + self._scrolled_view.add_muc_subject( muc_data.subject, muc_data.last_subject_timestamp)
def _register_events(self) -> None: @@ -245,9 +242,9 @@ def _on_presence_received(self, event: events.PresenceReceived) -> None: contact = self.client.get_module('Contacts').get_contact(event.fjid) if isinstance(contact, BareContact): return - self.conversation_view.add_user_status(self.contact.name, - contact.show.value, - contact.status) + self._scrolled_view.add_user_status(self.contact.name, + contact.show.value, + contact.status)
def _on_message_sent(self, event: events.MessageSent) -> None: if not self._is_event_processable(event): @@ -267,7 +264,7 @@ def _on_message_sent(self, event: events.MessageSent) -> None: displaymarking = None
if event.correct_id: - self.conversation_view.correct_message( + self._scrolled_view.correct_message( event.correct_id, event.message, self.get_our_nick()) return
@@ -363,7 +360,7 @@ def _on_message_updated(self, event: events.MessageUpdated) -> None: if not self._is_event_processable(event): return
- self.conversation_view.correct_message( + self._scrolled_view.correct_message( event.correct_id, event.msgtxt, event.nickname)
def _on_message_moderated(self, event: events.MessageModerated) -> None: @@ -374,32 +371,32 @@ def _on_message_moderated(self, event: events.MessageModerated) -> None: self.contact.account, event.moderation.moderator_jid, event.moderation.reason) - self.conversation_view.show_message_retraction( + self._scrolled_view.show_message_retraction( event.moderation.stanza_id, text)
def _on_receipt_received(self, event: events.ReceiptReceived) -> None: if not self._is_event_processable(event): return
- self.conversation_view.show_receipt(event.receipt_id) + self._scrolled_view.show_receipt(event.receipt_id)
def _on_displayed_received(self, event: events.DisplayedReceived) -> None: if not self._is_event_processable(event): return
- self.conversation_view.set_read_marker(event.marker_id) + self._scrolled_view.set_read_marker(event.marker_id)
def _on_message_error(self, event: events.MessageError) -> None: if not self._is_event_processable(event): return
- self.conversation_view.show_error(event.message_id, event.error) + self._scrolled_view.show_error(event.message_id, event.error)
def _on_call_stopped(self, event: events.CallStopped) -> None: if not self._is_event_processable(event): return
- self.conversation_view.update_call_rows() + self._scrolled_view.update_call_rows()
def _on_jingle_request_received(self, event: events.JingleRequestReceived @@ -439,7 +436,7 @@ def mark_as_read(self, send_marker: bool = True) -> None: self._jump_to_end_button.reset_unread_count()
def _on_autoscroll_changed(self, - _widget: ScrolledView, + _widget: ConversationView, autoscroll: bool ) -> None:
@@ -480,17 +477,17 @@ def _allow_add_message(self) -> bool: return self._scrolled_view.get_lower_complete()
def add_command_output(self, text: str, is_error: bool) -> None: - self.conversation_view.add_command_output(text, is_error) + self._scrolled_view.add_command_output(text, is_error)
def add_info_message(self, text: str, timestamp: Optional[float] = None ) -> None:
- self.conversation_view.add_info_message(text, timestamp) + self._scrolled_view.add_info_message(text, timestamp)
def add_file_transfer(self, transfer: HTTPFileTransfer) -> None: - self.conversation_view.add_file_transfer(transfer) + self._scrolled_view.add_file_transfer(transfer)
def add_jingle_file_transfer(self, event: Union[ @@ -499,11 +496,11 @@ def add_jingle_file_transfer(self, None] ) -> None: if self._allow_add_message(): - self.conversation_view.add_jingle_file_transfer(event) + self._scrolled_view.add_jingle_file_transfer(event)
def add_call_message(self, event: events.JingleRequestReceived) -> None: if self._allow_add_message(): - self.conversation_view.add_call_message(event=event) + self._scrolled_view.add_call_message(event=event)
def _add_message(self, text: str, @@ -521,7 +518,7 @@ def _add_message(self, additional_data = AdditionalDataDict()
if self._allow_add_message(): - self.conversation_view.add_message( + self._scrolled_view.add_message( text, kind, name, @@ -547,7 +544,7 @@ def get_autoscroll(self) -> bool: return self._scrolled_view.get_autoscroll()
def scroll_to_message(self, log_line_id: int, timestamp: float) -> None: - row = self.conversation_view.get_row_by_log_line_id(log_line_id) + row = self._scrolled_view.get_row_by_log_line_id(log_line_id) if row is None: # Clear view and reload conversation around timestamp self._scrolled_view.block_signals(True) @@ -558,7 +555,7 @@ def scroll_to_message(self, log_line_id: int, timestamp: float) -> None: self.add_messages(at_after)
GLib.idle_add( - self.conversation_view.scroll_to_message_and_highlight, + self._scrolled_view.scroll_to_message_and_highlight, log_line_id) GLib.idle_add(self._scrolled_view.block_signals, False)
@@ -571,9 +568,9 @@ def _fetch_n_lines_history(self, self._scrolled_view.block_signals(True)
if before: - row = self.conversation_view.get_first_message_row() + row = self._scrolled_view.get_first_message_row() else: - row = self.conversation_view.get_last_message_row() + row = self._scrolled_view.get_last_message_row()
if row is None: timestamp = time.time() @@ -598,7 +595,7 @@ def _fetch_n_lines_history(self, self._scrolled_view.set_history_complete(before, True)
# if self._scrolled_view.get_autoscroll(): - # if self.conversation_view.reduce_message_count(before): + # if self._scrolled_view.reduce_message_count(before): # self._scrolled_view.set_history_complete(before, False)
assert self._contact is not None @@ -645,13 +642,13 @@ def add_messages(self, messages: list[ConversationRow]): if msg.kind in (KindConstant.FILE_TRANSFER_INCOMING, KindConstant.FILE_TRANSFER_OUTGOING): if msg.additional_data.get_value('gajim', 'type') == 'jingle': - self.conversation_view.add_jingle_file_transfer( + self._scrolled_view.add_jingle_file_transfer( db_message=msg) continue
if msg.kind in (KindConstant.CALL_INCOMING, KindConstant.CALL_OUTGOING): - self.conversation_view.add_call_message(db_message=msg) + self._scrolled_view.add_call_message(db_message=msg) continue
if not msg.message: @@ -683,7 +680,7 @@ def add_messages(self, messages: list[ConversationRow]): message_text = get_retraction_text( self.contact.account, retracted_by, reason)
- self.conversation_view.add_message( + self._scrolled_view.add_message( message_text, kind, contact_name, @@ -1041,7 +1038,7 @@ def _process_muc_user_joined(self, event: events.MUCUserJoined) -> None:
if not event.is_self: if self.contact.is_joined: - self.conversation_view.add_muc_user_joined(event) + self._scrolled_view.add_muc_user_joined(event) return
status_codes = event.status_codes or [] @@ -1083,7 +1080,7 @@ def _process_muc_user_left(self, event: events.MUCUserLeft) -> None:
if StatusCode.REMOVED_ERROR in status_codes: # Handle 333 before 307, some MUCs add both - self.conversation_view.add_muc_user_left(event, error=True) + self._scrolled_view.add_muc_user_left(event, error=True) return
reason = event.reason @@ -1118,7 +1115,7 @@ def _process_muc_user_left(self, event: events.MUCUserLeft) -> None: message = message.format(nick=nick, by=actor, reason=reason)
else: - self.conversation_view.add_muc_user_left(event) + self._scrolled_view.add_muc_user_left(event) return
self.add_info_message(message, event.timestamp) @@ -1158,4 +1155,4 @@ def _on_room_subject(self,
if (app.settings.get('show_subject_on_join') or not contact.is_joining): - self.conversation_view.add_muc_subject(subject) + self._scrolled_view.add_muc_subject(subject)
===================================== gajim/gtk/conversation/scrolled.py deleted ===================================== @@ -1,203 +0,0 @@ -# This file is part of Gajim. -# -# Gajim is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published -# by the Free Software Foundation; version 3 only. -# -# Gajim is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Gajim. If not, see http://www.gnu.org/licenses/. - -from __future__ import annotations - -from typing import Any -from typing import Optional - -from gi.repository import Gtk -from gi.repository import GObject - -from gajim.common.types import ChatContactT - -from .view import ConversationView - - -class ScrolledView(Gtk.ScrolledWindow): - - __gsignals__ = { - 'request-history': ( - GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, - None, - (bool, ) - ), - 'autoscroll-changed': ( - GObject.SignalFlags.RUN_LAST, - None, - (bool,) - ) - } - - def __init__(self) -> None: - Gtk.ScrolledWindow.__init__(self) - - self.set_overlay_scrolling(False) - self.get_style_context().add_class('scrolled-no-border') - self.get_style_context().add_class('no-scroll-indicator') - self.get_style_context().add_class('scrollbar-style') - self.set_shadow_type(Gtk.ShadowType.IN) - self.set_vexpand(True) - - self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - - # This is a workaround: as soon as a line break occurs in Gtk.TextView - # with word-char wrapping enabled, a hyphen character is automatically - # inserted before the line break. This triggers the hscrollbar to show, - # see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2384 - # Using set_hscroll_policy(Gtk.Scrollable.Policy.NEVER) would cause bad - # performance during resize, and prevent the window from being shrunk - # horizontally under certain conditions (applies to GroupchatControl) - self.get_hscrollbar().hide() - - self._current_upper: float = 0 - self._autoscroll: bool = True - self._request_history_at_upper: Optional[float] = None - self._upper_complete: bool = False - self._lower_complete: bool = False - self._requesting: Optional[str] = None - self._block_signals = False - - vadjustment = self.get_vadjustment() - vadjustment.connect('notify::upper', self._on_adj_upper_changed) - vadjustment.connect('notify::value', self._on_adj_value_changed) - - self._view = ConversationView() - self.add(self._view) - self.set_focus_vadjustment(Gtk.Adjustment()) - - def clear(self) -> None: - self._block_signals = True - self._reset() - self._view.clear() - - def switch_contact(self, contact: ChatContactT) -> None: - self._block_signals = True - self._reset() - self._view.switch_contact(contact) - self._block_signals = False - self._emit('request-history', True) - - def get_autoscroll(self) -> bool: - return self._autoscroll - - def get_view(self) -> ConversationView: - return self._view - - def block_signals(self, value: bool) -> None: - self._block_signals = value - - def _emit(self, signal_name: str, *args: Any) -> None: - if not self._block_signals: - self.emit(signal_name, *args) - - def _reset(self) -> None: - self._current_upper = 0 - self._request_history_at_upper = None - self._upper_complete = False - self._lower_complete = False - self._requesting = None - self.set_history_complete(True, False) - - def reset(self) -> None: - self._reset() - self._view.reset() - - def set_history_complete(self, before: bool, complete: bool) -> None: - if before: - self._upper_complete = complete - self._view.set_history_complete(complete) - else: - self._lower_complete = complete - - def get_lower_complete(self) -> bool: - return self._lower_complete - - def _on_adj_upper_changed(self, - adj: Gtk.Adjustment, - _pspec: GObject.ParamSpec) -> None: - - upper = adj.get_upper() - diff = upper - self._current_upper - - if diff != 0: - self._current_upper = upper - if self._autoscroll: - adj.set_value(adj.get_upper() - adj.get_page_size()) - else: - # Workaround - # https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 - self.set_kinetic_scrolling(True) - if self._requesting == 'before': - adj.set_value(adj.get_value() + diff) - - if upper == adj.get_page_size(): - # There is no scrollbar - if not self._block_signals: - self._emit('request-history', True) - self._lower_complete = True - self._autoscroll = True - self._emit('autoscroll-changed', self._autoscroll) - - self._requesting = None - - def _on_adj_value_changed(self, - adj: Gtk.Adjustment, - _pspec: GObject.ParamSpec) -> None: - - if self._requesting is not None: - return - - bottom = adj.get_upper() - adj.get_page_size() - if (bottom - adj.get_value()) < 1: - self._autoscroll = True - self._emit('autoscroll-changed', self._autoscroll) - else: - self._autoscroll = False - self._emit('autoscroll-changed', self._autoscroll) - - if self._upper_complete: - self._request_history_at_upper = None - if self._lower_complete: - return - - if self._request_history_at_upper == adj.get_upper(): - # Abort here if we already did a history request and the upper - # did not change. This can happen if we scroll very fast and the - # value changes while the request has not been fulfilled. - return - - self._request_history_at_upper = None - - distance = adj.get_page_size() * 2 - if adj.get_value() < distance: - # Load messages when we are near the top - if self._upper_complete: - return - self._request_history_at_upper = adj.get_upper() - # Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 - self.set_kinetic_scrolling(False) - if not self._block_signals: - self._emit('request-history', True) - self._requesting = 'before' - elif (adj.get_upper() - (adj.get_value() + adj.get_page_size()) < - distance): - # ..or near the bottom - if self._lower_complete: - return - # Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 - self.set_kinetic_scrolling(False) - if not self._block_signals: - self._emit('request-history', False) - self._requesting = 'after'
===================================== gajim/gtk/conversation/view.py ===================================== @@ -15,6 +15,7 @@ from __future__ import annotations
from typing import Any +from typing import Literal from typing import Optional from typing import Union from typing import Generator @@ -28,6 +29,8 @@
from gi.repository import GLib from gi.repository import Gtk +from gi.repository import GObject +from gi.repository import Gio
from nbxmpp.errors import StanzaError from nbxmpp.modules.security_labels import Displaymarking @@ -64,13 +67,47 @@ log = logging.getLogger('gajim.gui.conversation_view')
-class ConversationView(Gtk.ListBox): - def __init__(self) -> None: - Gtk.ListBox.__init__(self) - self.set_selection_mode(Gtk.SelectionMode.NONE) - self.set_sort_func(self._sort_func) +class ConversationView(Gtk.ScrolledWindow):
- self._contact = None + __gsignals__ = { + 'request-history': ( + GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, + None, + (bool, ) + ), + 'autoscroll-changed': ( + GObject.SignalFlags.RUN_LAST, + None, + (bool,) + ) + } + + def __init__(self) -> None: + Gtk.ScrolledWindow.__init__(self) + + self.set_overlay_scrolling(False) + self.get_style_context().add_class('scrolled-no-border') + self.get_style_context().add_class('no-scroll-indicator') + self.get_style_context().add_class('scrollbar-style') + self.set_shadow_type(Gtk.ShadowType.IN) + self.set_vexpand(True) + + self.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + + # This is a workaround: as soon as a line break occurs in Gtk.TextView + # with word-char wrapping enabled, a hyphen character is automatically + # inserted before the line break. This triggers the hscrollbar to show, + # see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2384 + # Using set_hscroll_policy(Gtk.Scrollable.Policy.NEVER) would cause bad + # performance during resize, and prevent the window from being shrunk + # horizontally under certain conditions (applies to GroupchatControl) + self.get_hscrollbar().hide() + + self._list_box = Gtk.ListBox() + self._list_box.set_selection_mode(Gtk.SelectionMode.NONE) + self._list_box.set_sort_func(self._sort_func) + + self._contact: Optional[ChatContactT] = None self._client = None
# Keeps track of the number of rows shown in ConversationView @@ -86,24 +123,70 @@ def __init__(self) -> None: self._read_marker_row = None self._scroll_hint_row = None
- @property - def contact(self) -> types.ChatContactT: - assert self._contact is not None - return self._contact + self._current_upper: float = 0 + self._autoscroll: bool = True + self._request_history_at_upper: Optional[float] = None + self._upper_complete: bool = False + self._lower_complete: bool = True + self._requesting: Optional[str] = None + self._block_signals = False
- def clear(self) -> None: - self._contact = None - self._client = None + self._signal_handlers_enabled = False + self._signal_handler_ids = (0, 0) + + self.add(self._list_box) + self.set_focus_vadjustment(Gtk.Adjustment()) + + app.window.get_action('scroll-view-up').connect( + 'activate', self._on_scroll_view) + app.window.get_action('scroll-view-down').connect( + 'activate', self._on_scroll_view) + + def _on_scroll_view(self, + action: Gio.SimpleAction, + _param: Literal[None]) -> None: + + action_name = action.get_name() + if action_name == 'scroll-view-down': + self.emit('scroll-child', Gtk.ScrollType.PAGE_DOWN, False)
+ elif action_name == 'scroll-view-up': + self.emit('scroll-child', Gtk.ScrollType.PAGE_UP, False) + + def _enable_signal_handlers(self, enable: bool) -> None: + if self._signal_handlers_enabled == enable: + return + + vadjustment = self.get_vadjustment() + + if enable: + upper_id = vadjustment.connect('notify::upper', + self._on_adj_upper_changed) + value_id = vadjustment.connect('notify::value', + self._on_adj_value_changed) + self._signal_handler_ids = (upper_id, value_id) + else: + upper_id, value_id = self._signal_handler_ids + vadjustment.disconnect(upper_id) + vadjustment.disconnect(value_id) + + self._signal_handlers_enabled = enable + + def clear(self) -> None: app.settings.disconnect_signals(self) + self._enable_signal_handlers(False) + self._reset()
- self.reset() + self._contact = None + self._client = None
def switch_contact(self, contact: ChatContactT) -> None: self._contact = contact self._client = app.get_client(contact.account)
- self.reset() + self._enable_signal_handlers(False) + self._block_signals = True + self._reset()
app.settings.disconnect_signals(self)
@@ -117,11 +200,30 @@ def switch_contact(self, contact: ChatContactT) -> None: account=contact.account, jid=contact.jid)
- def get_row_at_index(self, index: int) -> BaseRow: - return cast(BaseRow, Gtk.ListBox.get_row_at_index(self, index)) + self._block_signals = False + self._enable_signal_handlers(True) + self._emit('request-history', True)
- def reset(self) -> None: - for row in self.get_children(): + def get_autoscroll(self) -> bool: + return self._autoscroll + + def block_signals(self, value: bool) -> None: + self._block_signals = value + + def _emit(self, signal_name: str, *args: Any) -> None: + if not self._block_signals: + log.debug('emit %s, %s', signal_name, args) + self.emit(signal_name, *args) + + def _reset(self) -> None: + self._current_upper = 0 + self._request_history_at_upper = None + self._upper_complete = False + self._lower_complete = True + self._requesting = None + self.set_history_complete(True, False) + + for row in self._list_box.get_children(): row.destroy()
self._row_count = 0 @@ -134,30 +236,125 @@ def reset(self) -> None: # These need to be present if ConversationView is reset # without switch_contact being invoked self._read_marker_row = ReadMarkerRow(self._contact) - self.add(self._read_marker_row) + self._list_box.add(self._read_marker_row)
self._scroll_hint_row = ScrollHintRow(self._contact.account) - self.add(self._scroll_hint_row) + self._list_box.add(self._scroll_hint_row) + + def reset(self) -> None: + assert self._contact is not None + self.switch_contact(self._contact) + + def set_history_complete(self, before: bool, complete: bool) -> None: + if before: + self._upper_complete = complete + if self._scroll_hint_row is None: + return + self._scroll_hint_row.set_history_complete(complete) + else: + self._lower_complete = complete + + def get_lower_complete(self) -> bool: + return self._lower_complete + + def _on_adj_upper_changed(self, + adj: Gtk.Adjustment, + _pspec: GObject.ParamSpec) -> None: + + upper = adj.get_upper() + diff = upper - self._current_upper + + if diff != 0: + self._current_upper = upper + if self._autoscroll: + adj.set_value(adj.get_upper() - adj.get_page_size()) + else: + # Workaround + # https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 + self.set_kinetic_scrolling(True) + if self._requesting == 'before': + adj.set_value(adj.get_value() + diff) + + if upper == adj.get_page_size(): + # There is no scrollbar + if not self._block_signals: + self._emit('request-history', True) + self._lower_complete = True + self._autoscroll = True + self._emit('autoscroll-changed', self._autoscroll) + + self._requesting = None + + def _on_adj_value_changed(self, + adj: Gtk.Adjustment, + _pspec: GObject.ParamSpec) -> None: + + if self._requesting is not None: + return + + bottom = adj.get_upper() - adj.get_page_size() + + self._autoscroll = bottom - adj.get_value() < 1 + self._emit('autoscroll-changed', self._autoscroll) + + if self._upper_complete: + self._request_history_at_upper = None + if self._lower_complete: + return + + if self._request_history_at_upper == adj.get_upper(): + # Abort here if we already did a history request and the upper + # did not change. This can happen if we scroll very fast and the + # value changes while the request has not been fulfilled. + return + + self._request_history_at_upper = None + + distance = adj.get_page_size() * 2 + if adj.get_value() < distance: + # Load messages when we are near the top + if self._upper_complete: + return + self._request_history_at_upper = adj.get_upper() + # Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 + self.set_kinetic_scrolling(False) + if not self._block_signals: + self._emit('request-history', True) + self._requesting = 'before' + + elif (adj.get_upper() - (adj.get_value() + adj.get_page_size()) < + distance): + # ..or near the bottom + if self._lower_complete: + return + # Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 + self.set_kinetic_scrolling(False) + if not self._block_signals: + self._emit('request-history', False) + self._requesting = 'after' + + @property + def contact(self) -> types.ChatContactT: + assert self._contact is not None + return self._contact + + def _get_row_at_index(self, index: int) -> BaseRow: + return cast(BaseRow, self._list_box.get_row_at_index(index))
def get_first_message_row(self) -> Optional[MessageRow]: - for row in self.get_children(): + for row in self._list_box.get_children(): if isinstance(row, MessageRow): return row return None
def get_last_message_row(self) -> Optional[MessageRow]: - children = self.get_children() + children = self._list_box.get_children() children.reverse() for row in children: if isinstance(row, MessageRow): return row return None
- def set_history_complete(self, complete: bool) -> None: - if self._scroll_hint_row is None: - return - self._scroll_hint_row.set_history_complete(complete) - @staticmethod def _sort_func(row1: BaseRow, row2: BaseRow) -> int: if row1.timestamp == row2.timestamp: @@ -293,7 +490,7 @@ def add_message(self, self._insert_message(message_row)
def _insert_message(self, message: BaseRow) -> None: - self.add(message) + self._list_box.add(message) self._add_date_row(message.timestamp) self._check_for_merge(message) assert self._read_marker_row is not None @@ -311,9 +508,9 @@ def _add_date_row(self, timestamp: datetime) -> None:
date_row = DateRow(self.contact.account, start_of_day) self._active_date_rows.add(start_of_day) - self.add(date_row) + self._list_box.add(date_row)
- row = self.get_row_at_index(date_row.get_index() + 1) + row = self._get_row_at_index(date_row.get_index() + 1) if row is None: return
@@ -340,7 +537,7 @@ def _find_ancestor(self, message: MessageRow) -> Optional[MessageRow]: index = message.get_index() while index != 0: index -= 1 - row = self.get_row_at_index(index) + row = self._get_row_at_index(index) if row is None: return None
@@ -361,7 +558,7 @@ def _update_descendants(self, message: MessageRow) -> None: index = message.get_index() while True: index += 1 - row = self.get_row_at_index(index) + row = self._get_row_at_index(index) if row is None: return
@@ -382,7 +579,7 @@ def _update_descendants(self, message: MessageRow) -> None:
def reduce_message_count(self, before: bool) -> bool: success = False - row_count = len(self.get_children()) + row_count = len(self._list_box.get_children()) while row_count > self._max_row_count: if before: if self._reduce_messages_before(): @@ -399,8 +596,8 @@ def _reduce_messages_before(self) -> bool: success = False
# We want to keep relevant DateRows when removing rows - row1 = self.get_row_at_index(2) - row2 = self.get_row_at_index(3) + row1 = self._get_row_at_index(2) + row2 = self._get_row_at_index(3)
if row1.type == 'date' and row2.type == 'date': # First two rows are date rows, @@ -422,12 +619,12 @@ def _reduce_messages_before(self) -> bool: return success
def _reduce_messages_after(self) -> None: - row = self.get_row_at_index(len(self.get_children()) - 1) + row = self._get_row_at_index(len(self._list_box.get_children()) - 1) row.destroy()
def scroll_to_message_and_highlight(self, log_line_id: int) -> None: highlight_row = None - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): row.get_style_context().remove_class( 'conversation-search-highlight') if row.log_line_id == log_line_id: @@ -443,7 +640,7 @@ def _get_row_by_message_id(self, id_: str) -> Optional[MessageRow]: return self._message_id_row_map.get(id_)
def get_row_by_log_line_id(self, log_line_id: int) -> Optional[MessageRow]: - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): if not isinstance(row, MessageRow): continue if row.log_line_id == log_line_id: @@ -451,7 +648,7 @@ def get_row_by_log_line_id(self, log_line_id: int) -> Optional[MessageRow]: return None
def get_row_by_stanza_id(self, stanza_id: str) -> Optional[MessageRow]: - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): if not isinstance(row, MessageRow): continue if row.stanza_id == stanza_id: @@ -459,7 +656,7 @@ def get_row_by_stanza_id(self, stanza_id: str) -> Optional[MessageRow]: return None
def iter_rows(self) -> Generator[BaseRow, None, None]: - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): yield row
def remove_rows_by_type(self, row_type: str) -> None: @@ -468,7 +665,7 @@ def remove_rows_by_type(self, row_type: str) -> None: row.destroy()
def update_call_rows(self) -> None: - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): if isinstance(row, CallRow): row.update()
@@ -489,7 +686,7 @@ def set_read_marker(self, id_: str) -> None: self._read_marker_row.set_timestamp(timestamp)
def update_avatars(self) -> None: - for row in cast(list[BaseRow], self.get_children()): + for row in cast(list[BaseRow], self._list_box.get_children()): if isinstance(row, MessageRow): row.update_avatar()
@@ -538,5 +735,5 @@ def _on_contact_setting_changed(self, self.remove_rows_by_type('muc-user-status')
def remove(self, widget: Gtk.Widget) -> None: - super().remove(widget) + self._list_box.remove(widget) widget.destroy()
===================================== gajim/gtk/message_actions_box.py ===================================== @@ -515,12 +515,6 @@ def _on_msg_textview_key_press_event(self, Direction.PREV, unread_first=True) return True
- if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up): - control = app.window.get_control() - if control.has_active_chat(): - control.conversation_view.event(event) - return True - if event_state & Gdk.ModifierType.CONTROL_MASK: if event.keyval == Gdk.KEY_Tab: app.window.select_next_chat(Direction.NEXT, unread_first=True)
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/9fd1b32662a3d54daa1bde9844fefa76...