Fix right-click context menus firing their first item on Linux
Bound to <Button-3> with finally: menu.grab_release(), the menu lost its grab immediately and the trailing <ButtonRelease-3> activated the entry under the cursor — right-clicking a history row jumped straight into the edit dialog and deleting was impossible. - RIGHT_CLICK constant: <ButtonRelease-3> (<ButtonRelease-2> on macOS) - history + queue menus built once on self, not per click - drop the premature grab_release() Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,10 @@ UI_FONT_SIZE = 11
|
|||||||
#: DRK-Rot – für Akzente in der Oberfläche und die Kartenpunkte.
|
#: DRK-Rot – für Akzente in der Oberfläche und die Kartenpunkte.
|
||||||
DRK_RED = "#CC0000"
|
DRK_RED = "#CC0000"
|
||||||
|
|
||||||
|
#: Rechtsklick-Event. ``<ButtonRelease-…>`` statt ``<Button-…>``: sonst landet
|
||||||
|
#: unter X11 das Loslassen der Maustaste direkt im ersten Menüeintrag.
|
||||||
|
RIGHT_CLICK = "<ButtonRelease-2>" if sys.platform == "darwin" else "<ButtonRelease-3>"
|
||||||
|
|
||||||
#: Regionen, die bei der Geokodierung bevorzugt durchsucht werden (Reihenfolge zählt).
|
#: Regionen, die bei der Geokodierung bevorzugt durchsucht werden (Reihenfolge zählt).
|
||||||
GEOCODE_REGIONS = ["Baden-Württemberg", "Hessen"]
|
GEOCODE_REGIONS = ["Baden-Württemberg", "Hessen"]
|
||||||
|
|
||||||
@@ -1050,8 +1054,16 @@ class App(ttk.Window):
|
|||||||
self.tree.pack(side=LEFT, fill=BOTH, expand=True)
|
self.tree.pack(side=LEFT, fill=BOTH, expand=True)
|
||||||
sb.pack(side=RIGHT, fill=Y)
|
sb.pack(side=RIGHT, fill=Y)
|
||||||
|
|
||||||
|
self._queue_menu = tk.Menu(self, tearoff=0)
|
||||||
|
self._queue_menu.add_command(
|
||||||
|
label="Entfernen", command=self._delete_selected_queue_rows
|
||||||
|
)
|
||||||
|
self._queue_menu.add_command(
|
||||||
|
label="Koordinaten erneut suchen", command=self._retry_selected_geocode
|
||||||
|
)
|
||||||
|
|
||||||
self.tree.bind("<ButtonRelease-1>", self._on_tree_click)
|
self.tree.bind("<ButtonRelease-1>", self._on_tree_click)
|
||||||
self.tree.bind("<Button-3>", self._on_queue_right_click)
|
self.tree.bind(RIGHT_CLICK, self._on_queue_right_click)
|
||||||
self.tree.bind("<Delete>", lambda _e: self._delete_selected_queue_rows())
|
self.tree.bind("<Delete>", lambda _e: self._delete_selected_queue_rows())
|
||||||
|
|
||||||
# Aktionsleiste
|
# Aktionsleiste
|
||||||
@@ -1113,8 +1125,16 @@ class App(ttk.Window):
|
|||||||
|
|
||||||
self.hist_tree.tag_configure("missing", foreground=DRK_RED)
|
self.hist_tree.tag_configure("missing", foreground=DRK_RED)
|
||||||
|
|
||||||
|
self._hist_menu = tk.Menu(self, tearoff=0)
|
||||||
|
self._hist_menu.add_command(label="Bearbeiten…", command=self._on_hist_edit)
|
||||||
|
self._hist_menu.add_command(
|
||||||
|
label="Koordinaten suchen", command=self._on_hist_geocode
|
||||||
|
)
|
||||||
|
self._hist_menu.add_separator()
|
||||||
|
self._hist_menu.add_command(label="Löschen", command=self._on_hist_delete)
|
||||||
|
|
||||||
self.hist_tree.bind("<Double-1>", self._on_hist_double_click)
|
self.hist_tree.bind("<Double-1>", self._on_hist_double_click)
|
||||||
self.hist_tree.bind("<Button-3>", self._on_hist_right_click)
|
self.hist_tree.bind(RIGHT_CLICK, self._on_hist_right_click)
|
||||||
|
|
||||||
self._sort_col = "date"
|
self._sort_col = "date"
|
||||||
self._sort_asc = False # neueste zuerst
|
self._sort_asc = False # neueste zuerst
|
||||||
@@ -1212,15 +1232,8 @@ class App(ttk.Window):
|
|||||||
if not iid:
|
if not iid:
|
||||||
return
|
return
|
||||||
self.hist_tree.selection_set(iid)
|
self.hist_tree.selection_set(iid)
|
||||||
menu = tk.Menu(self, tearoff=0)
|
self.hist_tree.focus(iid)
|
||||||
menu.add_command(label="Bearbeiten", command=self._on_hist_edit)
|
self._hist_menu.tk_popup(event.x_root, event.y_root)
|
||||||
menu.add_command(label="Koordinaten suchen", command=self._on_hist_geocode)
|
|
||||||
menu.add_separator()
|
|
||||||
menu.add_command(label="Löschen", command=self._on_hist_delete)
|
|
||||||
try:
|
|
||||||
menu.tk_popup(event.x_root, event.y_root)
|
|
||||||
finally:
|
|
||||||
menu.grab_release()
|
|
||||||
|
|
||||||
def _on_hist_geocode(self) -> None:
|
def _on_hist_geocode(self) -> None:
|
||||||
sel = self.hist_tree.selection()
|
sel = self.hist_tree.selection()
|
||||||
@@ -1406,14 +1419,13 @@ class App(ttk.Window):
|
|||||||
row_id = self.tree.identify_row(event.y)
|
row_id = self.tree.identify_row(event.y)
|
||||||
if not row_id:
|
if not row_id:
|
||||||
return
|
return
|
||||||
menu = tk.Menu(self, tearoff=0)
|
if row_id not in self.tree.selection():
|
||||||
menu.add_command(label="Löschen", command=lambda: self._delete_queue_row(row_id))
|
self.tree.selection_set(row_id)
|
||||||
menu.add_command(label="Koordinaten erneut suchen",
|
self._queue_menu.tk_popup(event.x_root, event.y_root)
|
||||||
command=lambda: self._retry_geocode(row_id))
|
|
||||||
try:
|
def _retry_selected_geocode(self) -> None:
|
||||||
menu.tk_popup(event.x_root, event.y_root)
|
for row_id in self.tree.selection():
|
||||||
finally:
|
self._retry_geocode(row_id)
|
||||||
menu.grab_release()
|
|
||||||
|
|
||||||
def _delete_queue_row(self, row_id: str) -> None:
|
def _delete_queue_row(self, row_id: str) -> None:
|
||||||
self._queue = [r for r in self._queue if r["_id"] != row_id]
|
self._queue = [r for r in self._queue if r["_id"] != row_id]
|
||||||
|
|||||||
@@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
Notable changes to the app. Newest first.
|
Notable changes to the app. Newest first.
|
||||||
|
|
||||||
|
## 2026-09-07 — fix: right-click context menu instantly triggered its first item
|
||||||
|
|
||||||
|
On Linux the Tab 2 (and queue) right-click menu flashed and immediately ran
|
||||||
|
"Bearbeiten" / "Löschen", so entries couldn't be deleted. Cause: the menu was
|
||||||
|
bound to `<Button-3>` (press) and `finally: menu.grab_release()` dropped the
|
||||||
|
grab, so the following `<ButtonRelease-3>` selected the item under the cursor.
|
||||||
|
Fix: bind `<ButtonRelease-3>` (`<ButtonRelease-2>` on macOS), build each menu
|
||||||
|
once, and drop the premature `grab_release()`.
|
||||||
|
|
||||||
## 2026-09-07 — map tiles → Esri (OSM 403s local files)
|
## 2026-09-07 — map tiles → Esri (OSM 403s local files)
|
||||||
|
|
||||||
The switch to `tiles="OpenStreetMap"` worked on the dev machine but the target
|
The switch to `tiles="OpenStreetMap"` worked on the dev machine but the target
|
||||||
|
|||||||
@@ -57,6 +57,20 @@ Lesson: a tile source that works from `curl` or an `http://` page can still fail
|
|||||||
from `file://`. Test the map by **opening the generated `karte.html` directly**,
|
from `file://`. Test the map by **opening the generated `karte.html` directly**,
|
||||||
not just by checking the URL in the HTML.
|
not just by checking the URL in the HTML.
|
||||||
|
|
||||||
|
## Tk popup menus: bind `<ButtonRelease-3>`, not `<Button-3>` (2026-09-07)
|
||||||
|
|
||||||
|
`widget.bind("<Button-3>", …)` + `menu.tk_popup(x, y)` + `finally:
|
||||||
|
menu.grab_release()` is the Windows idiom and **misfires on X11**: `tk_popup`
|
||||||
|
returns immediately, `grab_release()` drops the menu's grab, and the pending
|
||||||
|
`<ButtonRelease-3>` then activates whatever entry is under the cursor (the first
|
||||||
|
one). Symptom here: right-click a history row → menu flashes → edit dialog opens.
|
||||||
|
|
||||||
|
Rules for this codebase:
|
||||||
|
- bind the release event: `RIGHT_CLICK` constant (`<ButtonRelease-3>`, or
|
||||||
|
`<ButtonRelease-2>` on macOS),
|
||||||
|
- build each context menu **once** (store on `self`), don't recreate per click,
|
||||||
|
- just call `menu.tk_popup(...)` — no `grab_release()`.
|
||||||
|
|
||||||
## Nominatim query order matters
|
## Nominatim query order matters
|
||||||
|
|
||||||
`geocode_city` returns the **first** hit, trying PLZ-qualified first, then each
|
`geocode_city` returns the **first** hit, trying PLZ-qualified first, then each
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ then save the whole batch at once.
|
|||||||
as the background geocoder works through the queue.
|
as the background geocoder works through the queue.
|
||||||
- Select a row and press <kbd>Delete</kbd>, use the **"Auswahl entfernen"**
|
- Select a row and press <kbd>Delete</kbd>, use the **"Auswahl entfernen"**
|
||||||
button, or click the `✕` cell.
|
button, or click the `✕` cell.
|
||||||
- Right-click a row for *Löschen* / *Koordinaten erneut suchen*.
|
- Right-click a row for *Entfernen* / *Koordinaten erneut suchen*.
|
||||||
3. **Action bar**
|
3. **Action bar**
|
||||||
- **Alle speichern** – appends every queued row to `orte.csv`. If any row
|
- **Alle speichern** – appends every queued row to `orte.csv`. If any row
|
||||||
looks like a duplicate (same date + city + PLZ), a **modal** lists them and
|
looks like a duplicate (same date + city + PLZ), a **modal** lists them and
|
||||||
|
|||||||
Reference in New Issue
Block a user