Skip to content

Commit ff9da66

Browse files
authored
Merge pull request #791 from OpenSAK-Org/merge-beta-to-1.17.2
Merge beta to 1.17.2
2 parents 5a52d14 + e594f7d commit ff9da66

16 files changed

Lines changed: 538 additions & 64 deletions

CHANGELOG.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,44 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

55
---
66

7-
## [Unreleased]
7+
## [1.17.2] — 2026-08-26
8+
9+
### Fixed
10+
11+
- **Detail panel header showed large empty grey padding when the bottom
12+
panel was resized taller (#779)** — regression from the #755 fix shipped
13+
in v1.17.1. Reported by Mike via email with side-by-side screenshots.
14+
#755 gave the cache-detail `QTabWidget` a vertical `QSizePolicy` of
15+
`Ignored` so Qt would disregard the tab pages' minimum-size contribution
16+
when computing the panel's minimum height. `Ignored`, unlike the tab
17+
widget's default `Expanding`, doesn't carry `QSizePolicy.ExpandFlag`
18+
so the tabs stopped being the preferred recipient of any leftover
19+
vertical space in the panel's `QVBoxLayout`, and Qt spread that space
20+
across every row instead, including the compact header/meta rows.
21+
Fixed by replacing the policy change with a small `QTabWidget` subclass
22+
in `src/opensak/gui/cache_detail.py` that overrides `minimumSizeHint()`
23+
to return `QSize(0, 0)` — keeping the #755 fix intact while the tabs
24+
keep their normal `Expanding` policy and vertical stretch priority, as
25+
in v1.17.0.
26+
27+
- **GSAK Mark As Found status not detected on GPX/GGZ import when GSAK
28+
encodes it via `<type>...|Found</type>` instead of `<sym>` or a personal
29+
log entry (#766)**
30+
31+
- **Found status from genuine GSAK GPX/GGZ exports not detected on import
32+
when GSAK's `<sym>` doesn't reflect found state (#766)**
33+
34+
### Known Issues
35+
36+
- **Boundary updates don't always recognize when they're already current
37+
(#781)** — the reverse-geocoding boundary refresh can reprocess polygon
38+
data that's already up to date. Under investigation.
39+
40+
- **A small number of caches reverse-geocode to the wrong region (#782)**
41+
— e.g. GCAJWVJ is assigned country/county values that don't match its
42+
position on the map. Not yet clear whether the cause is in the polygon
43+
data, the boundary-matching logic, or the map visualization. Under
44+
investigation.
845

946
---
1047

opensak.spec

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,30 @@ block_cipher = None
1818
import certifi
1919
certifi_datas = [(certifi.where(), "certifi")]
2020

21+
# Qt's own translation catalogs (qtbase_xx.qm) — needed so Qt's built-in
22+
# dialog strings (Ok/Cancel/Close/Yes/No etc. from
23+
# QDialogButtonBox.StandardButton) are translated too, not just OpenSAK's
24+
# own tr() strings. See app.py::_install_qt_translator(). Only bundle the
25+
# languages OpenSAK actually supports, to avoid dragging in unused
26+
# catalogs for every Qt-supported locale.
27+
import PySide6
28+
_qt_translations_dir = Path(PySide6.__file__).parent / "Qt" / "translations"
29+
_QT_LANG_FILES = {
30+
"da": "qtbase_da.qm",
31+
"en": "qtbase_en.qm",
32+
"fr": "qtbase_fr.qm",
33+
"nl": "qtbase_nl.qm",
34+
"pt": "qtbase_pt_BR.qm", # no European-Portuguese qtbase catalog upstream; pt_BR is closest
35+
"cs": "qtbase_cs.qm",
36+
"se": "qtbase_sv.qm", # OpenSAK uses "se" for Swedish; Qt's own ISO code is "sv"
37+
"de": "qtbase_de.qm",
38+
}
39+
qt_translation_datas: list[tuple[str, str]] = []
40+
for _fname in _QT_LANG_FILES.values():
41+
_fpath = _qt_translations_dir / _fname
42+
if _fpath.exists():
43+
qt_translation_datas.append((str(_fpath), "PySide6/Qt/translations"))
44+
2145
# Boundary baseline (country/state only — generated by
2246
# tools/boundaries/gsak_to_opensak.py; gitignored). Counties are never
2347
# bundled: they're fetched on demand per country from OpenSAK-Data, same as
@@ -59,7 +83,7 @@ a = Analysis(
5983
("src/opensak/assets/icons/cache_types", "opensak/assets/icons/cache_types"),
6084
("src/opensak/assets/icons/cache_found", "opensak/assets/icons/cache_found"),
6185
("src/opensak/assets/icon_guide.html", "opensak/assets/icon_guide.html"),
62-
] + certifi_datas + boundary_datas,
86+
] + certifi_datas + boundary_datas + qt_translation_datas,
6387
hiddenimports=[
6488
"PySide6.QtWebEngineWidgets",
6589
"PySide6.QtWebEngineCore",
-601 Bytes
Loading
2.37 KB
Loading
7.75 KB
Loading
1.14 KB
Loading

site/user-guide.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>OpenSAK User Guide — v1.17.1</title>
6+
<title>OpenSAK User Guide — v1.17.2</title>
77
<style>
88
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Source+Serif+4:ital,wght@0,300;0,400;0,600;1,400&family=JetBrains+Mono:wght@400;500&display=swap');
99

@@ -473,7 +473,7 @@
473473
<nav>
474474
<div class="nav-header">
475475
<span class="nav-logo">OpenSAK</span>
476-
<div class="nav-version">User Guide · v1.17.1</div>
476+
<div class="nav-version">User Guide · v1.17.2</div>
477477
</div>
478478

479479
<div class="nav-section-heading">Getting Started</div>
@@ -535,7 +535,7 @@
535535
<div class="hero-eyebrow">Complete User Guide</div>
536536
<h1>OpenSAK</h1>
537537
<p class="hero-sub">The open-source geocache management tool for Windows, Linux, and macOS.</p>
538-
<div class="hero-meta">Version 1.17.1 &nbsp;·&nbsp; MIT Licence &nbsp;·&nbsp; <a href="https://github.com/OpenSAK-Org/opensak">github.com/OpenSAK-Org/opensak</a></div>
538+
<div class="hero-meta">Version 1.17.2 &nbsp;·&nbsp; MIT Licence &nbsp;·&nbsp; <a href="https://github.com/OpenSAK-Org/opensak">github.com/OpenSAK-Org/opensak</a></div>
539539
</div>
540540

541541
<figure class="screenshot" style="margin-top:-0.5rem;">
@@ -1463,7 +1463,7 @@ <h3>Debug Log</h3>
14631463
<tr><td>Bug reports &amp; feature requests</td><td><a href="https://github.com/OpenSAK-Org/opensak/issues">github.com/OpenSAK-Org/opensak/issues</a></td></tr>
14641464
<tr><td>Community discussion</td><td><a href="https://www.facebook.com/groups/opensak">Facebook group: OpenSAK</a></td></tr>
14651465
<tr><td>Releases &amp; downloads</td><td><a href="https://github.com/OpenSAK-Org/opensak/releases">github.com/OpenSAK-Org/opensak/releases</a></td></tr>
1466-
<tr><td>Changelog</td><td><a href="https://github.com/OpenSAK-Org/opensak/blob/v1.17.1/CHANGELOG.md">CHANGELOG.md on GitHub</a></td></tr>
1466+
<tr><td>Changelog</td><td><a href="https://github.com/OpenSAK-Org/opensak/blob/v1.17.2/CHANGELOG.md">CHANGELOG.md on GitHub</a></td></tr>
14671467
<tr><td>Contributing</td><td><a href="https://github.com/OpenSAK-Org/opensak/blob/main/CONTRIBUTING.md">CONTRIBUTING.md on GitHub</a></td></tr>
14681468
<tr><td>Support the project</td><td><a href="https://opencollective.com/opensak">opencollective.com/opensak</a></td></tr>
14691469
<tr><td>Website</td><td><a href="https://opensak.com">opensak.com</a></td></tr>
@@ -1473,7 +1473,7 @@ <h3>Debug Log</h3>
14731473
<div class="callout tip"><div class="callout-icon">💡</div><div>OpenSAK is free and open-source software released under the MIT licence. Contributions of any kind — code, translations, documentation, or testing — are very welcome.</div></div>
14741474

14751475
<p style="margin-top:2rem;color:var(--ink-light);font-size:0.85rem;font-style:italic;">
1476-
This guide was generated from the OpenSAK source code (v1.17.1). Last updated August 2026.
1476+
This guide was generated from the OpenSAK source code (v1.17.2). Last updated August 2026.
14771477
</p>
14781478
</section>
14791479

src/opensak/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""OpenSAK — cross-platform geocache management tool."""
2-
__version__ = "1.17.1"
2+
__version__ = "1.17.2"
33
__author__ = "OpenSAK Contributors"
44
__license__ = "MIT"

src/opensak/app.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,78 @@
1010
from opensak.gui.icon import get_app_icon
1111

1212
if TYPE_CHECKING:
13-
from PySide6.QtWidgets import QSplashScreen
13+
from PySide6.QtWidgets import QSplashScreen, QApplication
14+
from PySide6.QtCore import QTranslator
1415

1516
logger = logging.getLogger(__name__)
1617

18+
# Holder en reference til den installerede Qt-oversætter, så den ikke
19+
# bliver garbage collected efter main() (QApplication.installTranslator()
20+
# tager kun en svag reference internt i Qt).
21+
_qt_translator: "QTranslator | None" = None
22+
23+
# OpenSAK's sprogkoder følger ikke altid Qt's egne locale-koder.
24+
# "se" bruges i AVAILABLE_LANGUAGES for svensk, men Qt's oversættelsesfiler
25+
# hedder qtbase_sv.qm (ISO 639-1 for svensk er "sv", ikke "se").
26+
_QT_LOCALE_OVERRIDES: dict[str, str] = {
27+
"se": "sv",
28+
}
29+
30+
31+
def _install_qt_translator(app: "QApplication", lang_code: str) -> None:
32+
"""
33+
Installer Qt's indbyggede oversættelse (qtbase_xx.qm) for standard
34+
dialog-knapper (Close/OK/Cancel osv. fra QDialogButtonBox.StandardButton),
35+
som IKKE går gennem OpenSAK's eget sprogsystem (opensak.lang).
36+
37+
Uden dette vises fx "Close"-knappen i Koordinaten-Konverter altid på
38+
engelsk, selv når resten af UI'et er oversat (#issue: German Close btn).
39+
40+
Fejler stille (logger blot en advarsel) hvis der ikke findes en
41+
matchende qtbase_xx.qm — appen fungerer stadig fint, knappen falder
42+
bare tilbage til engelsk, som den gjorde før dette fix.
43+
"""
44+
global _qt_translator
45+
from PySide6.QtCore import QTranslator, QLibraryInfo
46+
47+
qt_locale = _QT_LOCALE_OVERRIDES.get(lang_code, lang_code)
48+
qm_name = f"qtbase_{qt_locale}"
49+
50+
# Kandidat-mapper i prioriteret rækkefølge. QLibraryInfo's egen sti er
51+
# korrekt ved kørsel fra source, men er ikke altid pålidelig i en
52+
# PyInstaller-frosset build (afhænger af hvordan PySide6-hooket
53+
# placerer Qt/translations i bundlet) — så vi tjekker også et par
54+
# kendte bundle-layouts som fallback. Se opensak.spec for hvor
55+
# qtbase_*.qm faktisk bliver lagt ved packaging.
56+
candidate_dirs = [QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)]
57+
if getattr(sys, "frozen", False):
58+
meipass = Path(getattr(sys, "_MEIPASS", ""))
59+
candidate_dirs += [
60+
str(meipass / "PySide6" / "Qt" / "translations"),
61+
str(meipass / "Qt" / "translations"),
62+
]
63+
64+
translator = QTranslator()
65+
loaded = False
66+
for tdir in candidate_dirs:
67+
if translator.load(qm_name, tdir):
68+
loaded = True
69+
break
70+
71+
if loaded:
72+
app.installTranslator(translator)
73+
_qt_translator = translator # bevar reference
74+
logger.info(
75+
"startup: installed Qt base translator for '%s' (qtbase_%s)",
76+
lang_code, qt_locale,
77+
)
78+
else:
79+
logger.warning(
80+
"startup: no Qt base translator found for '%s' (qtbase_%s) — "
81+
"standard dialog buttons will show in English",
82+
lang_code, qt_locale,
83+
)
84+
1785
def _migrate_legacy_db() -> None:
1886
"""
1987
Migrer gammel opensak.db til Default.db.
@@ -232,6 +300,7 @@ def splash_msg(text: str) -> None:
232300
from opensak.config import get_language
233301
from opensak.lang import load_language
234302
load_language(get_language())
303+
_install_qt_translator(app, get_language())
235304

236305
# Vis velkomst-wizard ved første opstart (issue #210)
237306
if is_first_run():

src/opensak/gui/cache_detail.py

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import webbrowser
1010
from datetime import datetime
1111
from opensak.utils.constants import LOG_COLOURS
12-
from PySide6.QtCore import Qt, QUrl, Signal, QDate, QLocale, QEvent
12+
from PySide6.QtCore import Qt, QUrl, Signal, QDate, QLocale, QEvent, QSize
1313
from PySide6.QtWidgets import (
1414
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
1515
QTextBrowser, QTabWidget, QFrame, QSizePolicy,
@@ -88,6 +88,39 @@ def acceptNavigationRequest(self, url: QUrl | str, nav_type, is_main_frame: bool
8888
return False
8989

9090

91+
class _TabWidget(QTabWidget):
92+
"""QTabWidget whose minimumSizeHint doesn't propagate the largest tab
93+
page's minimum height up into the parent layout.
94+
95+
Issue #755: QTabWidget.minimumSizeHint() normally reports the size
96+
needed to fit whichever tab page has the largest minimum size (since any
97+
tab could be switched to at any time). That propagated straight up into
98+
CacheDetailPanel's — and therefore the whole bottom_splitter's — minimum
99+
height, which made the main vertical splitter refuse to shrink the
100+
bottom panel much past that content's natural minimum.
101+
102+
v1.17.1 "fixed" this by setting the widget's vertical QSizePolicy to
103+
Ignored instead of overriding this method. That was a regression
104+
(reported by Mike, see follow-up issue): QSizePolicy.Ignored does not
105+
carry the ExpandFlag that Expanding does, so the tabs stopped being the
106+
preferred recipient of any leftover vertical space in the panel's
107+
QVBoxLayout. With no widget left flagged as expansive, Qt's box-layout
108+
algorithm instead spread that leftover space across ALL rows in the
109+
layout — including the compact header/meta rows — producing large empty
110+
grey padding around them that grew worse the taller the panel was
111+
dragged.
112+
Overriding minimumSizeHint() here fixes the original #755 problem
113+
directly, without touching the size *policy* — the tabs keep their
114+
normal Expanding policy, so they still get first claim on extra space,
115+
exactly as before v1.17.1. Each tab's own content (QTextBrowser/log
116+
list/etc.) already scrolls internally when squeezed, so nothing becomes
117+
unreachable when the splitter is dragged small.
118+
"""
119+
120+
def minimumSizeHint(self) -> QSize:
121+
return QSize(0, 0)
122+
123+
91124
class CacheDetailPanel(QWidget):
92125
"""Displays full details for a single selected cache."""
93126

@@ -249,7 +282,7 @@ def _setup_ui(self) -> None:
249282
layout.addWidget(self._placed_lbl)
250283

251284
# ── Tabs: Description | Hint | Logs ───────────────────────────────────
252-
self._tabs = QTabWidget()
285+
self._tabs = _TabWidget() # see _TabWidget docstring — issue #755
253286
self._tabs.setDocumentMode(True)
254287

255288
# Beskrivelse — QWebEngineView så eksterne billeder og CJK-fonte virker.
@@ -345,27 +378,6 @@ def _setup_ui(self) -> None:
345378
note_layout.addWidget(self._note_editor)
346379
self._tabs.addTab(note_widget, tr("detail_tab_notes"))
347380

348-
# Issue #755: QTabWidget reports its own minimumSizeHint as the
349-
# LARGEST of its tab pages' minimums (since any tab could be
350-
# switched to at any time), and that propagates straight up into
351-
# this panel's — and therefore the whole bottom_splitter's —
352-
# minimum height, which is what made the main vertical splitter
353-
# refuse to shrink the bottom panel much past its content's
354-
# natural minimum ("stuck around the middle of the window").
355-
#
356-
# QSizePolicy.Policy.Ignored on the vertical component tells Qt's
357-
# layout system to disregard this widget's height hint entirely
358-
# when computing the *minimum* size for this panel — the splitter
359-
# can then be dragged freely down to a small size. It still grows
360-
# to fill available space normally when there's room (Ignored only
361-
# affects the minimum/hint contribution, not actual sizing), and
362-
# each tab's own content already scrolls internally when squeezed
363-
# (QTextBrowser/log list/etc. all have their own scrollbars), so
364-
# nothing becomes unreachable — content just scrolls instead of
365-
# ever blocking the splitter.
366-
size_policy = self._tabs.sizePolicy()
367-
size_policy.setVerticalPolicy(QSizePolicy.Policy.Ignored)
368-
self._tabs.setSizePolicy(size_policy)
369381
layout.addWidget(self._tabs)
370382
self._tabs.currentChanged.connect(self._on_tab_changed)
371383

0 commit comments

Comments
 (0)