diff --git a/desktop_viewer.py b/desktop_viewer.py index b2a4af1..6909ed5 100644 --- a/desktop_viewer.py +++ b/desktop_viewer.py @@ -56,8 +56,9 @@ WARN_RED = "#e0895a" POLL_MS = 1500 CARD_W, CARD_H = 150, 114 -# Untergrenze beim Ziehen am Anfasser -- knapp unter der Groesse, die das -# 4x5-Grid + Encoder mindestens brauchen, damit nichts abgeschnitten wird. +# Untergrenze fuers Verkleinern (self.minsize) -- knapp unter der Groesse, +# die das 4x5-Grid + Encoder mindestens brauchen, damit nichts +# abgeschnitten wird. MIN_W, MIN_H = 700, 500 SERIAL_POLL_S = 1.5 SERIAL_IDLE_S = 3.0 @@ -130,8 +131,8 @@ class VersaPadViewer(tk.Tk): self.configure(bg=BG) # Hoehe muss Kopfzeile + Tabs + 5 Kartenreihen + Encoder + Fusszeile # fassen -- seit die Karten eine Notizzeile haben (CARD_H 84 -> 114) - # reichten die alten 760px nicht mehr: Encoder und der Groessen- - # Anfasser lagen unterhalb des Fensterrands und waren unerreichbar. + # reichten die alten 760px nicht mehr: der Encoder-Bereich lag + # unterhalb des Fensterrands. Wer CARD_H aendert, muss hier mit. self.geometry("790x960") self.profile = 0 self._mtimes = {} @@ -165,32 +166,23 @@ class VersaPadViewer(tk.Tk): ) self._tray_icon.run_detached() - # Titelleiste ausgeblendet (reines Tk `overrideredirect`, KEINE - # ctypes/WinAPI-Fenstertricks -- siehe Speicher-Notiz "keine - # Selbstversteck-Fenstertricks": das Nachruesten eines Taskleisten- - # Icons per SetWindowLongW hat frueher AV-Fehlalarme ausgeloest). - # Ersatz fuer die fehlende Systemleiste: Kopfzeile ist ziehbar, und - # die Buttons rechts uebernehmen Minimieren/Schliessen (beides ins - # Tray, wie vorher schon das X der echten Titelleiste). - self.overrideredirect(True) + # Normales Fenster MIT Windows-Titelleiste. Bis 2026-08-28 lief es + # randlos (`overrideredirect(True)`) -- das kostet unter Windows aber + # zwingend den Taskleisten-Eintrag, und der wurde ausdruecklich + # gebraucht. Nachruesten liesse er sich nur per `SetWindowLongW` + # (WS_EX_APPWINDOW) -- genau der Aufruf, der laut Speicher-Notiz + # "keine Selbstversteck-Fenstertricks" schon AV-Fehlalarme + # ausgeloest hat und deshalb nicht in Frage kommt. Mit der echten + # Titelleiste kommen Taskleiste, Alt+Tab, Aero-Snap, Ziehen und + # Groessenaendern am Rahmen nativ zurueck; die Eigenbau-Loesungen + # dafuer (ziehbare Kopfzeile, Anfasser unten rechts, eigene + # ✕/—-Knoepfe) sind damit ersatzlos entfallen. + self.minsize(MIN_W, MIN_H) self.toggles_row = header = tk.Frame(self, bg=BG) header.pack(fill="x", padx=20, pady=(10, 6)) - title = tk.Label(header, text="VersaPad", bg=BG, fg=TEXT, - font=("Segoe UI", 11, "bold")) - title.pack(side="left", anchor="w", padx=(0, 16)) - - for widget in (header, title): - widget.bind("", self._start_move) - widget.bind("", self._on_move) - - for text in ("✕", "—"): - btn = tk.Label(header, text=text, bg=BG, fg=TEXT_DIM, - font=("Segoe UI", 11), cursor="hand2", padx=6) - btn.pack(side="right") - btn.bind("", lambda e: self._hide_to_tray()) - btn.bind("", lambda e, b=btn: b.configure(fg=TEXT)) - btn.bind("", lambda e, b=btn: b.configure(fg=TEXT_DIM)) + tk.Label(header, text="VersaPad", bg=BG, fg=TEXT, + font=("Segoe UI", 11, "bold")).pack(side="left", anchor="w", padx=(0, 16)) info_btn = tk.Label(header, text="ⓘ", bg=BG, fg=TEXT_DIM, font=("Segoe UI", 12), cursor="hand2", padx=6) @@ -264,29 +256,18 @@ class VersaPadViewer(tk.Tk): self.enc_frame = tk.Frame(self, bg=BG) self.enc_frame.pack(fill="x", padx=20) - # Fusszeile + Anfasser zum Groessenaendern: mit ausgeblendeter - # Titelleiste (overrideredirect) entfernt Windows auch die - # Fensterraender, an denen man sonst zieht -- ohne diesen Griff - # liesse sich das Fenster gar nicht mehr skalieren. footer_row = tk.Frame(self, bg=BG) footer_row.pack(fill="x", padx=20, pady=(14, 8)) self.footer = tk.Label(footer_row, text="", bg=BG, fg=TEXT_DIM, font=("Segoe UI", 8)) self.footer.pack(side="left", anchor="w") - # Der Griff haengt per place() an der FENSTER-Ecke, nicht am Ende des - # gepackten Inhalts: sonst wandert er mit dem Inhalt aus dem Bild, - # sobald das Fenster kleiner als der Inhalt ist -- also genau dann, - # wenn man ihn zum Vergroessern braucht. - grip = tk.Label(self, text="◢", bg=BG, fg=TEXT_DIM, - font=("Segoe UI", 11), cursor="sizing") - grip.place(relx=1.0, rely=1.0, anchor="se", x=-3, y=-1) - grip.bind("", self._start_resize) - grip.bind("", self._on_resize) - grip.bind("", lambda e: grip.configure(fg=TEXT)) - grip.bind("", lambda e: grip.configure(fg=TEXT_DIM)) - + # Schliessen legt weiterhin ins Tray statt zu beenden (wie die + # offizielle VersaGUI -- das Programm soll im Hintergrund + # weiterlaufen). Minimieren geht jetzt aber ganz normal in die + # Taskleiste; frueher hat ein -Handler auch das ins Tray + # umgeleitet, was ohne Taskleisten-Eintrag sinnvoll war und mit + # einem das Fenster nur unauffindbar machen wuerde. self.protocol("WM_DELETE_WINDOW", self._hide_to_tray) - self.bind("", self._on_unmap) self._bind_shortcuts() self._serial_thread.start() @@ -388,25 +369,6 @@ class VersaPadViewer(tk.Tk): def _on_toggle_topmost(self): self.attributes("-topmost", self.always_on_top.get()) - # ── Fenster ziehen (Ersatz fuer die ausgeblendete Titelleiste) ── - - def _start_move(self, event): - self._drag_origin = (event.x_root - self.winfo_x(), event.y_root - self.winfo_y()) - - def _on_move(self, event): - dx, dy = self._drag_origin - self.geometry(f"+{event.x_root - dx}+{event.y_root - dy}") - - def _start_resize(self, event): - self._resize_origin = (event.x_root, event.y_root, - self.winfo_width(), self.winfo_height()) - - def _on_resize(self, event): - x0, y0, w0, h0 = self._resize_origin - width = max(MIN_W, w0 + (event.x_root - x0)) - height = max(MIN_H, h0 + (event.y_root - y0)) - self.geometry(f"{width}x{height}") - # ── Live-Sync mit dem Board ──────────────────────────────────── def _on_toggle_sync(self): @@ -452,13 +414,7 @@ class VersaPadViewer(tk.Tk): text = SERIAL_STATUS_TEXT.get(error, error or "Fehler") self.sync_status.configure(text=text, fg=WARN_RED) - # ── Tray-Icon (kein Taskleisten-Eintrag beim Minimieren, wie VersaGUI) ── - - def _on_unmap(self, event): - """Minimieren faengt Windows normalerweise als Taskleisten-Icon ab -- - wir wollen stattdessen: Fenster komplett weg, nur noch Tray-Icon.""" - if event.widget is self and self.state() == "iconic": - self._hide_to_tray() + # ── Tray-Icon (Schliessen beendet nicht, wie bei der VersaGUI) ── def _hide_to_tray(self): self.withdraw() @@ -636,7 +592,11 @@ class VersaPadViewer(tk.Tk): haelt dessen grab_set() die Tastatur -- die Kuerzel hier koennen ihm also nicht dazwischenfunken.""" for seq, handler in ( - ("", lambda e: self._hide_to_tray()), + # Minimieren statt ins Tray: seit das Fenster eine Titelleiste + # und damit einen Taskleisten-Eintrag hat, waere "verschwindet + # spurlos" die unangenehmere Ueberraschung. Ins Tray legt + # weiterhin das ✕ der Titelleiste. + ("", lambda e: self.iconify()), ("", lambda e: self._rename_tab(self.profile)), ("", lambda e: self._toggle_editing_shortcut()), ("", lambda e: self._toggle_editing_shortcut()),