diff --git a/evennia/contrib/mail.py b/evennia/contrib/mail.py index 6e8585136d..dbecb62fcd 100644 --- a/evennia/contrib/mail.py +++ b/evennia/contrib/mail.py @@ -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) diff --git a/evennia/contrib/tests.py b/evennia/contrib/tests.py index ff6b01d5cf..1e526bd9cb 100644 --- a/evennia/contrib/tests.py +++ b/evennia/contrib/tests.py @@ -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) diff --git a/evennia/utils/evtable.py b/evennia/utils/evtable.py index 31218189ab..ffb29873c4 100644 --- a/evennia/utils/evtable.py +++ b/evennia/utils/evtable.py @@ -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):