Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Hörist <philipp@hoerist.com>2022-09-14 22:02:02 +0300
committerPhilipp Hörist <philipp@hoerist.com>2022-09-17 10:16:54 +0300
commit13dc0166e157d9d74c74187b0319e79df9e6630d (patch)
tree850c92451d4a809ef9b53b12313a8972e459c0e4
parentad5a9075678d9f63d461dadc597ed8e18b29a5b9 (diff)
refactor: Merge ScrolledView into ConversationViewrefactor_view
-rw-r--r--gajim/gtk/control.py67
-rw-r--r--gajim/gtk/conversation/scrolled.py203
-rw-r--r--gajim/gtk/conversation/view.py262
3 files changed, 250 insertions, 282 deletions
diff --git a/gajim/gtk/control.py b/gajim/gtk/control.py
index ba7d17b5e..ab4adab5d 100644
--- a/gajim/gtk/control.py
+++ b/gajim/gtk/control.py
@@ -48,8 +48,8 @@ from gajim.common.modules.contacts import GroupchatParticipant
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
self._contact = None
self._client = None
- self.reset_view()
self._scrolled_view.clear()
self._groupchat_state.clear()
self._roster.clear()
@@ -199,7 +196,7 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
self._jump_to_end_button.reset_unread_count()
def _on_autoscroll_changed(self,
- _widget: ScrolledView,
+ _widget: ConversationView,
autoscroll: bool
) -> None:
@@ -480,17 +477,17 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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 @@ class ChatControl(EventHelper):
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)
diff --git a/gajim/gtk/conversation/scrolled.py b/gajim/gtk/conversation/scrolled.py
deleted file mode 100644
index 2542a5c7c..000000000
--- a/gajim/gtk/conversation/scrolled.py
+++ /dev/null
@@ -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'
diff --git a/gajim/gtk/conversation/view.py b/gajim/gtk/conversation/view.py
index 6b23939fa..d8daf6147 100644
--- a/gajim/gtk/conversation/view.py
+++ b/gajim/gtk/conversation/view.py
@@ -28,6 +28,7 @@ from datetime import timedelta
from gi.repository import GLib
from gi.repository import Gtk
+from gi.repository import GObject
from nbxmpp.errors import StanzaError
from nbxmpp.modules.security_labels import Displaymarking
@@ -64,13 +65,47 @@ from .rows.user_status import UserStatus
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 +121,49 @@ class ConversationView(Gtk.ListBox):
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.add(self._list_box)
+ self.set_focus_vadjustment(Gtk.Adjustment())
+
+ def _enable_signal_handlers(self, enable: bool) -> None:
+ if self._signal_handlers_enabled == enable:
+ return
+ vadjustment = self.get_vadjustment()
+
+ if enable:
+ vadjustment.connect('notify::upper', self._on_adj_upper_changed)
+ vadjustment.connect('notify::value', self._on_adj_value_changed)
+ else:
+ vadjustment.disconnect_by_func(self._on_adj_upper_changed)
+ vadjustment.disconnect_by_func(self._on_adj_value_changed)
+
+ 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 +177,30 @@ class ConversationView(Gtk.ListBox):
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 +213,125 @@ class ConversationView(Gtk.ListBox):
# 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 +467,7 @@ class ConversationView(Gtk.ListBox):
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 +485,9 @@ class ConversationView(Gtk.ListBox):
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 +514,7 @@ class ConversationView(Gtk.ListBox):
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 +535,7 @@ class ConversationView(Gtk.ListBox):
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 +556,7 @@ class ConversationView(Gtk.ListBox):
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 +573,8 @@ class ConversationView(Gtk.ListBox):
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 +596,12 @@ class ConversationView(Gtk.ListBox):
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 +617,7 @@ class ConversationView(Gtk.ListBox):
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 +625,7 @@ class ConversationView(Gtk.ListBox):
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 +633,7 @@ class ConversationView(Gtk.ListBox):
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 +642,7 @@ class ConversationView(Gtk.ListBox):
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 +663,7 @@ class ConversationView(Gtk.ListBox):
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 +712,5 @@ class ConversationView(Gtk.ListBox):
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()