diff --git a/install.ps1 b/install.ps1 index 7bb0e43c..38b7ee9e 100644 --- a/install.ps1 +++ b/install.ps1 @@ -11,7 +11,7 @@ $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" - $defaultInstallSha256 = "3d36f9aaf0bfd17cc2eeff61d0f99d35ff60d7cf967572ec146b166681334e5d" + $defaultInstallSha256 = "eaa912af47dc6f29e4436ec200a1b431b8ddd8006ca08e57c8c73d3a05717818" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index c91ddc7a..d7987b3a 100644 --- a/install.py +++ b/install.py @@ -10178,6 +10178,9 @@ class TuiRow(NamedTuple): right: str = "" value: object = None style: str = "" + # Shorter forms of right, longest first, for a narrow screen: the label + # keeps its room before the right-hand text does. + narrow: tuple = () # Screens that scroll text instead of listing rows. @@ -10358,16 +10361,21 @@ def _system_rows(state: TuiState) -> list[TuiRow]: need = _needed(row) count, size = shared.get(row["system"], (0, 0)) own, own_size = need - count, row["bytes_needed"] - size + narrow: tuple = () if not need: right = "in place" elif not count: right = f"{_plural(need, 'file')}, {format_size(row['bytes_needed'])}" + narrow = (format_size(row["bytes_needed"]),) elif not own: right = f"{_plural(need, 'file')}, {format_size(size)}, all shared" + narrow = (f"{format_size(size)}, shared", format_size(size)) else: right = f"{_plural(own, 'file')}, {format_size(own_size)} + {count} shared" + narrow = (f"{format_size(own_size)} + {count} shared", + f"{format_size(own_size)} + {count} sh.", format_size(own_size)) rows.append(TuiRow("system", f"{box} {label}", right, row["system"], - "" if need else "dim")) + "" if need else "dim", narrow)) return rows @@ -11292,8 +11300,20 @@ _ROW_VERBS = { def _row_line(row: TuiRow, focused: bool, width: int) -> TuiLine: - right = middle(row.right, max(12, width // 2)) if row.right else "" - text = _columns(("> " if focused else " ") + row.label, right, width) + left = ("> " if focused else " ") + row.label + if row.narrow: + # The name first: the longest right-hand form that leaves it whole, + # else none at all when that is what keeps it whole. Nintendo Game + # Boy, Game Boy Advance and Game Boy Color read alike once cut. + whole = width - cell_width(left) - 2 + right = next((form for form in (row.right, *row.narrow) + if cell_width(form) <= whole), "") + if not right and cell_width(left) > width: + right = row.narrow[-1] + text = _columns(left, right, width) + else: + right = middle(row.right, max(12, width // 2)) if row.right else "" + text = _columns(left, right, width) return _styled(text, "focus" if focused else row.style) diff --git a/install.sh b/install.sh index 0a7fce9a..f64306ff 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="3d36f9aaf0bfd17cc2eeff61d0f99d35ff60d7cf967572ec146b166681334e5d" +DEFAULT_INSTALL_SHA256="eaa912af47dc6f29e4436ec200a1b431b8ddd8006ca08e57c8c73d3a05717818" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/tests/test_install_tui.py b/tests/test_install_tui.py index 1f46b96d..f8ba0aa8 100644 --- a/tests/test_install_tui.py +++ b/tests/test_install_tui.py @@ -2578,6 +2578,39 @@ class AuditRoundFour(unittest.TestCase): self.assertIn(line, install.tui_outcome(state)[1]) +class NarrowSystemNames(unittest.TestCase): + """At 40 columns the size column took half the line: Game Boy, Game Boy + Advance and Game Boy Color all read "Nintendo Gam".""" + + def test_the_name_comes_before_the_size(self): + state = checked_state() + names = {"nintendo-gb": "Nintendo Game Boy", "nintendo-gba": "Nintendo Game Boy Advance", + "nintendo-gbc": "Nintendo Game Boy Color", + "videoton-tvc": "Videoton TV Computer"} + state.manifest = {**state.manifest, "system_titles": names} + state.systems = [ + {"event": "system", "platform": "retroarch", "system": system, "name": system, + "files": 30, "present": 0, "missing": 30, "wrong_hash": 0, + "bytes_needed": 30 * 1024 * 1024} + for system in names] + state.files = [{"event": "file", "platform": "retroarch", "dest": f"s{n}.bin", + "systems": list(names), "size": 1024, "state": "missing"} + for n in range(27)] + state.ticked = set(names) + state.stack.append("systems") + for cols, rows in ((40, 15), (40, 12), (52, 15)): + with self.subTest(size=f"{cols}x{rows}"): + text = "\n".join(install.tui_view(state, cols, rows)) + for name in names.values(): + self.assertIn(name, text) + self.assertNotIn("...", text) + for line in text.splitlines(): + self.assertLessEqual(install.cell_width(line), cols) + # Wide, the full form stays. + self.assertIn("3 files, 30.0 MB + 27 shared", + "\n".join(install.tui_view(state, 120, 40))) + + class Crash(unittest.TestCase): """An unexpected error ends on one sentence and a log, never a traceback."""