Make EvTable respect col-widths while retaining total table width.

Resolves #1614.
This commit is contained in:
Griatch 2018-04-22 14:48:55 +02:00
parent 060cfde23e
commit 9bbfc422ee
3 changed files with 79 additions and 21 deletions

View file

@ -253,7 +253,7 @@ class CmdMail(default_cmds.MuxCommand):
index += 1
table.reformat_column(0, width=6)
table.reformat_column(1, width=17)
table.reformat_column(1, width=18)
table.reformat_column(2, width=34)
table.reformat_column(3, width=13)
table.reformat_column(4, width=7)

View file

@ -688,7 +688,7 @@ class TestMail(CommandTest):
"You have received a new @mail from TestAccount2(account 2)|You sent your message.", caller=self.account2)
self.call(mail.CmdMail(), "TestAccount=Message 1", "You sent your message.", caller=self.account2)
self.call(mail.CmdMail(), "TestAccount=Message 2", "You sent your message.", caller=self.account2)
self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.account)
self.call(mail.CmdMail(), "", "| ID: From: Subject:", caller=self.account)
self.call(mail.CmdMail(), "2", "From: TestAccount2", caller=self.account)
self.call(mail.CmdMail(), "/forward TestAccount2 = 1/Forward message", "You sent your message.|Message forwarded.", caller=self.account)
self.call(mail.CmdMail(), "/reply 2=Reply Message2", "You sent your message.", caller=self.account)

View file

@ -893,6 +893,9 @@ class EvColumn(object):
"""
col = self.column
# fixed options for the column will override those requested in the call!
# this is particularly relevant to things like width/height, to avoid
# fixed-widths columns from being auto-balanced
kwargs.update(self.options)
# use fixed width or adjust to the largest cell
if "width" not in kwargs:
@ -1283,25 +1286,59 @@ class EvTable(object):
cwidths_min = [max(cell.get_min_width() for cell in col) for col in self.worktable]
cwmin = sum(cwidths_min)
if cwmin > width:
# we cannot shrink any more
raise Exception("Cannot shrink table width to %s. Minimum size is %s." % (self.width, cwmin))
# get which cols have separately set widths - these should be locked
# note that we need to remove cwidths_min for each lock to avoid counting
# it twice (in cwmin and in locked_cols)
locked_cols = {icol: col.options['width'] - cwidths_min[icol]
for icol, col in enumerate(self.worktable) if 'width' in col.options}
locked_width = sum(locked_cols.values())
excess = width - cwmin - locked_width
if len(locked_cols) >= ncols and excess:
# we can't adjust the width at all - all columns are locked
raise Exception("Cannot balance table to width %s - "
"all columns have a set, fixed width summing to %s!" % (
self.width, sum(cwidths)))
if excess < 0:
# the locked cols makes it impossible
raise Exception("Cannot shrink table width to %s. "
"Minimum size (and/or fixed-width columns) "
"sets minimum at %s." % (self.width, cwmin + locked_width))
excess = width - cwmin
if self.evenwidth:
# make each column of equal width
for _ in range(excess):
# use cwidths as a work-array to track weights
cwidths = copy(cwidths_min)
correction = 0
while correction < excess:
# flood-fill the minimum table starting with the smallest columns
ci = cwidths_min.index(min(cwidths_min))
cwidths_min[ci] += 1
ci = cwidths.index(min(cwidths))
if ci in locked_cols:
# locked column, make sure it's not picked again
cwidths[ci] += 9999
cwidths_min[ci] = locked_cols[ci]
else:
cwidths_min[ci] += 1
correction += 1
cwidths = cwidths_min
else:
# make each column expand more proportional to their data size
for _ in range(excess):
# we use cwidth as a work-array to track weights
correction = 0
while correction < excess:
# fill wider columns first
ci = cwidths.index(max(cwidths))
cwidths_min[ci] += 1
cwidths[ci] -= 3
if ci in locked_cols:
# locked column, make sure it's not picked again
cwidths[ci] -= 9999
cwidths_min[ci] = locked_cols[ci]
else:
cwidths_min[ci] += 1
correction += 1
# give a just changed col less prio next run
cwidths[ci] -= 3
cwidths = cwidths_min
# reformat worktable (for width align)
@ -1323,28 +1360,46 @@ class EvTable(object):
for cell in (col[iy] for col in self.worktable)) for iy in range(nrowmax)]
chmin = sum(cheights_min)
# get which cols have separately set heights - these should be locked
# note that we need to remove cheights_min for each lock to avoid counting
# it twice (in chmin and in locked_cols)
locked_cols = {icol: col.options['height'] - cheights_min[icol]
for icol, col in enumerate(self.worktable) if 'height' in col.options}
locked_height = sum(locked_cols.values())
excess = self.height - chmin - locked_height
if chmin > self.height:
# we cannot shrink any more
raise Exception("Cannot shrink table height to %s. Minimum size is %s." % (self.height, chmin))
raise Exception("Cannot shrink table height to %s. Minimum "
"size (and/or fixed-height rows) sets minimum at %s." % (
self.height, chmin + locked_height))
# now we add all the extra height up to the desired table-height.
# We do this so that the tallest cells gets expanded first (and
# thus avoid getting cropped)
excess = self.height - chmin
even = self.height % 2 == 0
for position in range(excess):
correction = 0
while correction < excess:
# expand the cells with the most rows first
if 0 <= position < nrowmax and nrowmax > 1:
if 0 <= correction < nrowmax and nrowmax > 1:
# avoid adding to header first round (looks bad on very small tables)
ci = cheights[1:].index(max(cheights[1:])) + 1
else:
ci = cheights.index(max(cheights))
cheights_min[ci] += 1
if ci == 0 and self.header:
# it doesn't look very good if header expands too fast
cheights[ci] -= 2 if even else 3
cheights[ci] -= 2 if even else 1
if ci in locked_cols:
# locked row, make sure it's not picked again
cheights[ci] -= 9999
cheights_min[ci] = locked_cols[ci]
else:
cheights_min[ci] += 1
# change balance
if ci == 0 and self.header:
# it doesn't look very good if header expands too fast
cheights[ci] -= 2 if even else 3
cheights[ci] -= 2 if even else 1
correction += 1
cheights = cheights_min
# we must tell cells to crop instead of expanding
@ -1554,6 +1609,8 @@ class EvTable(object):
"""
if index > len(self.table):
raise Exception("Not a valid column index")
# we update the columns' options which means eventual width/height
# will be 'locked in' and withstand auto-balancing width/height from the table later
self.table[index].options.update(kwargs)
self.table[index].reformat(**kwargs)
@ -1569,6 +1626,7 @@ class EvTable(object):
def __str__(self):
"""print table (this also balances it)"""
# h = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
return str(unicode(ANSIString("\n").join([line for line in self._generate_lines()])))
def __unicode__(self):