From 4a913528ad7603a885a0dfbbe00c11f8855c0a4c Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 12:12:47 +0800 Subject: [PATCH 1/2] Default theme: readable text at every screen size Text sizes were set by aspect ratio only, so 960x720 and 1024x768 got 640x480's sizes and 1920x1080 got 1280x720's: about 60% of the size they needed. Text now scales with the screen's height (font-scale per resolution, after the aspect ratio ones, which stay for other screens). - The ports and themes lists' info and description were placed in pixels from the top of their pane, so on bigger screens the text overlapped (and a fourth info line, eg two runtimes, overlapped at 640x480 too). They're now fractions of the pane, made from each aspect ratio's own size, with room for four info lines. - The 16:9 and 1920x1152 info / description sizes that shrank them below the rest of the screen are gone. - Port info: bigger text, scrolling when it doesn't fit. - Options: a wider list, so "Experimental Ports: Disabled" fits. Checked every screen at 12 resolutions with tools/theme_server. The runtime manager showed "{runtime_info.name}" and the like until the selection moved: its first update_selection() only ran when there were no runtimes (d378e03 turned it off to avoid a crash with nothing selected). It now runs whenever something is selected. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/default_theme/theme.json | 73 ++++++++++---------- PortMaster/pylibs/pugwash/scenes/runtimes.py | 6 +- tests/test_gui_commands.py | 27 ++++++++ 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/PortMaster/pylibs/default_theme/theme.json b/PortMaster/pylibs/default_theme/theme.json index 35ff6405..e4baa91e 100644 --- a/PortMaster/pylibs/default_theme/theme.json +++ b/PortMaster/pylibs/default_theme/theme.json @@ -169,10 +169,17 @@ "font[ko_KR]": "NotoSansKR-Regular.ttf", "font[ja_JP]": "NotoSansJP-Regular.ttf", "font-scale[30:17]": 1.0, - "font-scale[1920x1152]": 2.0, "font-scale[16:9]": 1.4, "font-scale[3:2]": 0.85, - "font-scale[427:240]": 0.9 + "font-scale[427:240]": 0.9, + "font-scale[720x480]": 1.0, + "font-scale[720x720]": 1.45, + "font-scale[960x720]": 1.5, + "font-scale[1024x768]": 1.6, + "font-scale[1280x720]": 1.5, + "font-scale[1280x800]": 1.6, + "font-scale[1920x1080]": 2.2, + "font-scale[1920x1152]": 2.3 }, "#elements": { "left_pane": { @@ -333,15 +340,14 @@ "port_general_info": { "parent": "port_info_text_area", "area": [ 5, 5, -5, -5 ], - "area[4:3]": [ 5, 70, -5, -5 ], - "area[1:1]": [ 5,88, -5, -5 ], - "area[16:9]": [ 5, 80, -5, -5 ], + "area[4:3]": [ 0.011, 0.243, 0.989, 0.983 ], + "area[1:1]": [ 0.01, 0.204, 0.99, 0.988 ], + "area[16:9]": [ 0.007, 0.185, 0.993, 0.988 ], "area[427:240]": [ 5, 80, -5, -5 ], "area[30:17]": [ 5, 80, -5, -5 ], - "area[1920x1152]": [ 0.05, 0.0, 1.0, 0.8 ], + "area[1920x1152]": [ 0.011, 0.03, 0.989, 0.983 ], + "area[3:2]": [ 0.017, 0.026, 0.983, 0.974 ], "font-size": 16, - "font-size[1920x1152]": 11, - "font-size[16:9]": 11, "font-color": "general_font", "text-wrap": true, "text": "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}" @@ -349,16 +355,14 @@ "port_desc": { "parent": "port_info_text_area", "area": [ 5, 65, -5, -45 ], - "area[4:3]": [ 5, 130, -5, -50 ], - "area[1:1]": [ 5, 145, -5, -50 ], - "area[3:2]": [ 5, 65, -5, -25 ], - "area[16:9]": [ 5, 165, -5, -100 ], - "area[1920x1152]": [ 0.05, 0.2, 0.8, 0.7 ], + "area[4:3]": [ 0.011, 0.51, 0.989, 0.826 ], + "area[1:1]": [ 0.01, 0.463, 0.99, 0.884 ], + "area[3:2]": [ 0.017, 0.391, 0.983, 0.87 ], + "area[16:9]": [ 0.007, 0.472, 0.993, 0.769 ], + "area[1920x1152]": [ 0.011, 0.36, 0.989, 0.85 ], "area[427:240]": [ 5, 145, -5, -50 ], "area[30:17]": [ 5, 145, -5, -65 ], "font-size": 17, - "font-size[1920x1152]": 13, - "font-size[16:9]": 13, "font-color": "general_font", "text-wrap": true, "autoscroll": "marquee", @@ -394,7 +398,7 @@ "scroll-speed": 60, "scroll-delay-start": 1500, "scroll-delay-end": 1500, - "font-size": 15, + "font-size": 17, "font-size[427:240]": 22, "font-color": "general_font", "text-wrap": true @@ -415,11 +419,12 @@ }, "port_info_instruction": { "area": [ 0.6, 0.1, 0.95, 0.65 ], + "autoscroll": "marquee", "text": "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}", "fill": "panels", "thickness": 6, "border": 8, - "font-size": 13, + "font-size": 15, "font-color": "general_font", "text-wrap": true, "parent": "element:background" @@ -472,8 +477,8 @@ "list": [], "options": [], "align": "midleft", - "area": [ 0.0, 0.10, 0.5, 0.85 ], - "area[16:9]": [ 0.0, 0.15, 0.5, 0.85 ], + "area": [ 0.0, 0.1, 0.62, 0.85 ], + "area[16:9]": [ 0.0, 0.15, 0.55, 0.85 ], "text-clip": true, "font-size": 25, "no-select-color": "list_unselectable", @@ -495,7 +500,8 @@ "text": "{scene.tooltip}", "font-size": 25, "font-color": "general_font", - "area": [ 0.5, 0.4, 1.0, 0.85 ], + "area": [ 0.63, 0.4, 0.98, 0.85 ], + "area[16:9]": [ 0.56, 0.4, 0.98, 0.85 ], "align": "topleft", "text-wrap": true }, @@ -541,15 +547,14 @@ "theme_general_info": { "parent": "port_info_text_area", "area": [ 5, 5, -5, -5 ], - "area[4:3]": [ 5, 60, -5, -5 ], - "area[1:1]": [ 5, 88, -5, -5 ], - "area[16:9]": [ 5, 80, -5, -5 ], + "area[4:3]": [ 0.011, 0.243, 0.989, 0.983 ], + "area[1:1]": [ 0.01, 0.204, 0.99, 0.988 ], + "area[16:9]": [ 0.007, 0.185, 0.993, 0.988 ], "area[427:240]": [ 5, 80, -5, -5 ], "area[30:17]": [ 5, 80, -5, -5 ], - "area[1920x1152]": [ 0.05, 0.0, 1.0, 0.8 ], + "area[1920x1152]": [ 0.011, 0.03, 0.989, 0.983 ], + "area[3:2]": [ 0.017, 0.026, 0.983, 0.974 ], "font-size": 16, - "font-size[1920x1152]": 11, - "font-size[16:9]": 11, "font-color": "general_font", "text-wrap": true, "text": "Creator: {theme_info.creator}\nStatus: {theme_info.status}" @@ -557,16 +562,14 @@ "theme_desc": { "parent": "port_info_text_area", "area": [ 5, 65, -5, -45 ], - "area[4:3]": [ 5, 118, -5, -50 ], - "area[1:1]": [ 5, 145, -5, -5 ], - "area[3:2]": [ 5, 65, -5, -25 ], - "area[16:9]": [ 5, 165, -5, -100 ], - "area[1920x1152]": [ 0.05, 0.2, 0.8, 0.7 ], + "area[4:3]": [ 0.011, 0.51, 0.989, 0.826 ], + "area[1:1]": [ 0.01, 0.463, 0.99, 0.884 ], + "area[3:2]": [ 0.017, 0.391, 0.983, 0.87 ], + "area[16:9]": [ 0.007, 0.472, 0.993, 0.769 ], + "area[1920x1152]": [ 0.011, 0.36, 0.989, 0.85 ], "area[427:240]": [ 5, 145, -5, -50 ], "area[30:17]": [ 5, 145, -5, -65 ], "font-size": 17, - "font-size[1920x1152]": 13, - "font-size[16:9]": 13, "font-color": "general_font", "text-wrap": true, "autoscroll": "marquee", @@ -800,8 +803,6 @@ "area[1920x1152]": [ 0.05, 0.34, 0.8, 0.7 ], "area[30:17|427:240]": [ 0.02, 0.62, 0.98, 0.88 ], "font-size": 17, - "font-size[1920x1152]": 13, - "font-size[16:9]": 13, "font-color": "general_font", "text-wrap": true, "autoscroll": "marquee", @@ -839,8 +840,6 @@ "area": [ 0.0, 0.70, 0.95, 0.85 ], "border-x": 16, "font-size": 16, - "font-size[1920x1152]": 11, - "font-size[16:9]": 11, "font-color": "general_font", "text-wrap": true, "text": "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}" diff --git a/PortMaster/pylibs/pugwash/scenes/runtimes.py b/PortMaster/pylibs/pugwash/scenes/runtimes.py index aed66e30..4f669f24 100644 --- a/PortMaster/pylibs/pugwash/scenes/runtimes.py +++ b/PortMaster/pylibs/pugwash/scenes/runtimes.py @@ -81,9 +81,11 @@ def update_runtimes(self): self.last_select = self.tags['runtime_list'].selected_option() self.last_verified = None - if not runtimes: + ## Fill in the selected runtime's details now: do_update only does it when the + ## selection changes. (Nothing to fill in without a selection, eg: no runtimes.) + if self.last_select in self.runtimes: self.update_selection() - + def update_selection(self): runtime_info = self.runtimes[self.last_select] diff --git a/tests/test_gui_commands.py b/tests/test_gui_commands.py index 9eb360a0..14ace530 100644 --- a/tests/test_gui_commands.py +++ b/tests/test_gui_commands.py @@ -131,3 +131,30 @@ def download(file_name, file_url, md5_source=None, md5_result=None, callback=Non assert not (hm_dirs['ports_dir'] / "notinstalled").exists() assert (app.hm.cfg_dir / "gameinfo_pm.zip").read_bytes() == gameinfo assert_idle(app) + + +def test_runtime_manager_shows_the_selected_runtime(app, monkeypatch): + ## The details are filled in when the screen opens, not only after the selection moves + ## (it showed "{runtime_info.name}" until then). + from pugwash.scenes import RuntimesScene + + runtimes = [ + ("love_11.5.squashfs", {'name': "Love 11.5", 'remote': {'aarch64': {'size': 12582912}}}), + ("godot_4.3.squashfs", {'name': "Godot 4.3", 'remote': {'aarch64': {'size': 1048576}}}), + ] + monkeypatch.setattr(app.hm, "list_runtimes", lambda: runtimes) + for key in ('runtime_info.name', 'runtime_info.download_size'): + app.set_data(key, None) + + scene = RuntimesScene(app) + assert scene.last_select == "godot_4.3.squashfs" + assert app.get_data('runtime_info.name') == "Godot 4.3" + assert app.get_data('runtime_info.download_size') == harbourmaster.nice_size(1048576) + + +def test_runtime_manager_without_runtimes(app, monkeypatch): + ## Only "Download All" (after a separator): nothing to fill in yet, and no crash. + from pugwash.scenes import RuntimesScene + + monkeypatch.setattr(app.hm, "list_runtimes", lambda: []) + RuntimesScene(app) From 1bc605396c3af1e2199e8119354c5514dfbae87d Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 12:24:34 +0800 Subject: [PATCH 2/2] Disclaimer and startup warning: text sizes for every screen The built-in disclaimer section (the piracy disclaimer and the unsupported-system warning, the same in every theme) scales its text per resolution, but had no entry for 720x480, 960x720, 1024x768 or 1280x800: those got the unscaled size (1280x800's barely a third of the box), or whatever the theme's own #base said. They're filled in, between their neighbours' sizes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/pugwash/theme.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 4eb9107a..80a939ad 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -194,10 +194,14 @@ def theme_load(gui, theme_file, color_scheme=None): "border": 10, "font-scale[1920x1152]": 3.0, "font-scale[1920x1080]": 3.0, + "font-scale[1280x800]": 2.0, "font-scale[1280x720]": 1.9, + "font-scale[1024x768]": 1.7, + "font-scale[960x720]": 1.6, "font-scale[960x544]": 1.4, "font-scale[720x720]": 1.4, "font-scale[854x480]": 1.3, + "font-scale[720x480]": 1.1, "font-scale[640x480]": 1.1, "font-scale[480x320]": 0.75, "font-size": 18, @@ -214,10 +218,14 @@ def theme_load(gui, theme_file, color_scheme=None): "area": [0.0, 0.93, 0.95, -5], "font-scale[1920x1152]": 3.0, "font-scale[1920x1080]": 3.0, + "font-scale[1280x800]": 2.0, "font-scale[1280x720]": 1.9, + "font-scale[1024x768]": 1.7, + "font-scale[960x720]": 1.6, "font-scale[960x544]": 1.4, "font-scale[720x720]": 1.4, "font-scale[854x480]": 1.3, + "font-scale[720x480]": 1.1, "font-scale[640x480]": 1.1, "font-scale[480x320]": 0.75, "font-size": 16,