summaryrefslogtreecommitdiff
path: root/alot/buffers
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2020-03-01 21:49:28 +0100
committerAnton Khirnov <anton@khirnov.net>2020-03-01 21:49:28 +0100
commitd035e850d3dde1fbc8d556861f7ef72d581c5506 (patch)
treece691c85f28caac28f32f453484d5ab3b3dff74f /alot/buffers
parentc6e3144579bd8310b04d326f4ee32035fe237925 (diff)
buffers/thread: make the widget split-window
The top part displayes the thread structure, the bottom half the message body. This makes more sense then displaying the message inside the tree structure and makes it easier to implement features such as folding a part of the message body. Drop commands related to folding, since that functionality does not exist anymore.
Diffstat (limited to 'alot/buffers')
-rw-r--r--alot/buffers/thread.py137
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))