diff options
Diffstat (limited to 'alot/buffers')
-rw-r--r-- | alot/buffers/thread.py | 137 |
1 files changed, 76 insertions, 61 deletions
diff --git a/alot/buffers/thread.py b/alot/buffers/thread.py index 8615b37d..c60b123c 100644 --- a/alot/buffers/thread.py +++ b/alot/buffers/thread.py @@ -18,7 +18,20 @@ class ThreadBuffer(Buffer): modename = 'thread' - _msg_widgets = None + # list of the widgets containing the message body + # indexed by its depth-first position in the thread tree + _msg_widgets = None + # widget showing the thread tree + _msgtree_widget = None + + # WidgetPlaceholder that wraps currently displayed message + _cur_msg_holder = None + # WidgetPlaceholder that wraps current divider + _divider_holder = None + + # divider widgets placed between the thread tree and the message + _divider_up = None + _divider_down = None def __init__(self, ui, thread): """ @@ -27,10 +40,29 @@ class ThreadBuffer(Buffer): :param thread: thread to display :type thread: :class:`~alot.db.Thread` """ - self.thread = thread - + self.thread = thread self._indent_width = settings.get('thread_indent_replies') + + # create the widgets composing the buffer + self._msgtree_widget = urwid.ListBox(urwid.SimpleFocusListWalker([])) + self._divider_up = urwid.Divider('↑') + self._divider_down = urwid.Divider('↓') + self._divider_holder = urwid.WidgetPlaceholder(self._divider_up) + self._cur_msg_holder = urwid.WidgetPlaceholder(urwid.SolidFill()) + + self.body = urwid.Pile([ + # 0 is a dummy value and is overridden later rebuild() + ('weight', 0, self._msgtree_widget), + ('pack', self._divider_holder), + # fixed weight for the message body + # TODO: should it depend on the message body length (or perhaps something else)? + ('weight', 50, self._cur_msg_holder), + ]) + + urwid.connect_signal(self._msgtree_widget.body, "modified", self._update_cur_msg) + self.rebuild() + super().__init__(ui, self.body) def __str__(self): @@ -38,6 +70,12 @@ class ThreadBuffer(Buffer): self.thread.total_messages, 's' * (self.thread.total_messages > 1)) + def _update_cur_msg(self): + pos = self._msgtree_widget.body.focus + if pos is not None and pos < len(self._msg_widgets): + logging.debug('displaying message %s ', pos) + self._cur_msg_holder.original_widget = self._msg_widgets[pos] + def translated_tags_str(self, intersection=False): tags = self.thread.get_tags(intersection=intersection) trans = [settings.get_tagstring_representation(tag)['translated'] @@ -55,27 +93,35 @@ class ThreadBuffer(Buffer): return info def rebuild(self): + self._msg_widgets = [] + self._msgtree_widget.body.clear() + self._cur_msg_holder.original_widget = urwid.SolidFill() + try: self.thread.refresh() except NonexistantObjectError: - self.body = urwid.SolidFill() return - self._msg_widgets = [] - - body_walker = urwid.SimpleFocusListWalker([]) + list_walker = self._msgtree_widget.body for pos, msg in enumerate(self.thread.message_list): - msg_wgt = MessageWidget(msg, pos & 1) - wgt = ThreadNode(msg_wgt, self.thread, pos, self._indent_width) + msg_wgt = MessageWidget(msg) + wgt = ThreadNode(msg, self.thread, pos, self._indent_width) self._msg_widgets.append(msg_wgt) - body_walker.append(wgt) + list_walker.append(wgt) + + # the weight given to the thread-tree widget is equal to the number of + # messages in it, up to the limit of 50 (when it is equal to the message + # body widget) + tree_weight = min(len(self._msg_widgets), 50) + self.body.contents[0] = (self._msgtree_widget, ('weight', tree_weight)) - self.body = urwid.ListBox(body_walker) + if len(self._msg_widgets) > 0: + self._cur_msg_holder.original_widget = self._msg_widgets[0] def get_selected_message_position(self): """Return position of focussed message in the thread tree.""" - return self.body.focus_position + return self._msgtree_widget.focus_position def get_selected_message_widget(self): """Return currently focused :class:`MessageWidget`.""" @@ -107,7 +153,7 @@ class ThreadBuffer(Buffer): def set_focus(self, pos): "Set the focus in the underlying body widget." logging.debug('setting focus to %s ', pos) - self.body.set_focus(pos) + self._msgtree_widget.set_focus(pos) def focus_first(self): """set focus to first message of thread""" @@ -193,51 +239,20 @@ class ThreadBuffer(Buffer): """focus previous matching message in depth first order""" self._focus_property(lambda x: x.get_message().matches(querystring), -1) - def focus_next_unfolded(self): - """focus next unfolded message in depth first order""" - self._focus_property(lambda x: x.display_content, 1) - - def focus_prev_unfolded(self): - """focus previous unfolded message in depth first order""" - self._focus_property(lambda x: x.display_content, -1) - - def expand(self, msgpos): - """expand message at given position""" - self.body.body[msgpos].expand() - - def expand_all(self): - """expand all messages in thread""" - for msg in self.message_widgets(): - msg.expand() - - def collapse(self, msgpos): - """collapse message at given position""" - self.body.body[msgpos].collapse() - self.focus_selected_message() - - def collapse_all(self): - """collapse all messages in thread""" - for msg in self.message_widgets(): - msg.collapse() - self.focus_selected_message() - - def unfold_matching(self, querystring, focus_first=True): - """ - expand all messages that match a given querystring. - - :param querystring: query to match - :type querystring: str - :param focus_first: set the focus to the first matching message - :type focus_first: bool - """ - focus_set = False - first = None - for pos, msg_wgt in enumerate(self.message_widgets()): - msg = msg_wgt.get_message() - if msg.matches(querystring): - msg_wgt.expand() - if focus_first and not focus_set: - self.set_focus(pos) - focus_set = True - else: - msg_wgt.collapse() + def focus_thread_widget(self): + """set focus on the thread widget""" + logging.debug('setting focus to thread widget') + self.body.focus_position = 0 + self._divider_holder.original_widget = self._divider_up + def focus_msg_widget(self): + """set focus on the message widget""" + logging.debug('setting focus to message widget') + self.body.focus_position = 2 + self._divider_holder.original_widget = self._divider_down + def focus_toggle(self): + if self.body.focus_position == 0: + self.focus_msg_widget() + elif self.body.focus_position == 2: + self.focus_thread_widget() + else: + raise ValueError('Invalid focus position: %s' % str(self.body.focus_position)) |