Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support wrapping column text over multiple lines #1634

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion alot/utils/configobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,27 @@ def width_tuple(value):
to use at least width min, and cut of at width max.
Here, min and max are positive integers or 0 to disable
the boundary.
('wrap', minw, maxw, minl, maxl): behave like 'fit' but wrap text content
that exceeds width max to the next line.
Here, minl represents the minimum number
of content lines to display, regardless
of whether the content is wrapped, and
maxl represents the maximum number of
content lines to display.
('weight',n): have it relative weight of n compared to other columns.
Here, n is an int.
"""
if value is None:
res = 'fit', 0, 0
elif not isinstance(value, (list, tuple)):
raise VdtTypeError(value)
elif value[0] not in ['fit', 'weight']:
elif value[0] not in ['fit', 'wrap', 'weight']:
raise VdtTypeError(value)
try:
if value[0] == 'fit':
res = 'fit', int(value[1]), int(value[2])
elif value[0] == 'wrap':
res = 'wrap', int(value[1]), int(value[2]), int(value[3]), int(value[4])
else:
res = 'weight', int(value[1])
except IndexError:
Expand Down
54 changes: 42 additions & 12 deletions alot/widgets/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Widgets specific to search mode
"""
import urwid
import textwrap

from ..settings.const import settings
from ..helper import shorten_author_string
Expand Down Expand Up @@ -137,22 +138,39 @@ def build_text_part(name, thread, struct):
# extract min and max allowed width from theme
minw = 0
maxw = None
min_lines = 1
max_lines = 1
width_tuple = struct['width']
if width_tuple is not None:
if width_tuple[0] == 'fit':
minw, maxw = width_tuple[1:]

content = prepare_string(name, thread, maxw)

# pad content if not long enough
if minw:
alignment = struct['alignment']
if alignment == 'left':
content = content.ljust(minw)
elif alignment == 'center':
content = content.center(minw)
else:
content = content.rjust(minw)
elif width_tuple[0] == 'wrap':
minw, maxw, min_lines, max_lines = width_tuple[1:]

content = prepare_string(name, thread, max_lines * maxw)
alignment = struct['alignment']
if width_tuple[0] == 'wrap':
lines = textwrap.wrap(
content,
width=maxw,
max_lines=max_lines,
expand_tabs=False,
placeholder='',
)

# ensure minimum number of lines
if len(lines) < min_lines:
lines = lines + [''] * (min_lines - len(lines))

# pad content line by line if not long enough
if minw:
lines = [pad_content(line, alignment, minw) for line in lines]

content = '\n'.join(lines)
else:
# pad content if not long enough
if minw:
content = pad_content(content, alignment, minw)

# define width and part_w
text = urwid.Text(content, wrap='clip')
Expand All @@ -162,6 +180,18 @@ def build_text_part(name, thread, struct):
return width, part_w


def pad_content(content, alignment, min_width):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed documenting this new function. Will fix.

"""
pad 'content' to 'min_width' justified according to 'alignment'.
"""
if alignment == 'left':
return content.ljust(min_width)
elif alignment == 'center':
return content.center(min_width)
else:
return content.rjust(min_width)


def prepare_date_string(thread):
newest = thread.get_newest_date()
if newest is not None:
Expand Down
16 changes: 14 additions & 2 deletions tests/utils/test_configobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,22 @@ def test_validates_width_tuple_for_weight_needs_an_argument(self):
with self.assertRaises(VdtTypeError):
checks.width_tuple(['weight'])

def test_arg_for_width_must_be_a_number(self):
def test_arg_for_weight_must_be_a_number(self):
with self.assertRaises(VdtValueError):
checks.width_tuple(['weight', 'not-a-number'])

def test_width_with_a_number(self):
def test_weight_with_a_number(self):
weight_result = checks.width_tuple(['weight', 123])
self.assertEqual(('weight', 123), weight_result)

def test_validates_width_tuple_for_wrap_requires_four_args(self):
with self.assertRaises(VdtTypeError):
checks.width_tuple(['wrap', 123, 456, 789])

def test_validates_width_tuple_for_wrap_must_be_numbers(self):
with self.assertRaises(VdtValueError):
checks.width_tuple(['wrap', 12, 34, 56, 'not-a-number'])

def test_wrap_with_four_numbers(self):
fit_result = checks.width_tuple(['wrap', 12, 34, 56, 78])
self.assertEqual(('wrap', 12, 34, 56, 78), fit_result)