diff --git a/src/ccma/ui/claim_tab.py b/src/ccma/ui/claim_tab.py index fc875fb..99cd081 100644 --- a/src/ccma/ui/claim_tab.py +++ b/src/ccma/ui/claim_tab.py @@ -878,6 +878,10 @@ class ReminderDialog(_Dialog): def __init__(self, master, repository, member_id, claim_id, on_saved): super().__init__(master, "Mahnung vorbereiten", on_saved) + # Unlike the small fixed-size dialogs sharing _Dialog, this one holds a table + # of freely-editable fee items whose content varies a lot -- make it resizable + # so a long/multi-item Mahnung doesn't get cramped. + self.resizable(True, True) self.repository, self.member_id, self.claim_id = repository, member_id, claim_id data, _claim = repository.get_claim(member_id, claim_id) existing_levels = { @@ -891,6 +895,7 @@ class ReminderDialog(_Dialog): self.items: list[dict[str, str]] = [] self.frame.columnconfigure(1, weight=1) + self.frame.rowconfigure(6, weight=1) ttk.Label(self.frame, text="Mahnstufe").grid(row=0, column=0, sticky="w", pady=5, padx=(0, 12)) ttk.Label(self.frame, text=str(self.level), style="Mono.TLabel").grid( row=0, column=1, sticky="w", pady=5 @@ -904,20 +909,20 @@ class ReminderDialog(_Dialog): textvariable=self.preset_var, values=list(self.preset_by_label), state="readonly", - width=38, + width=55, ) preset_combo.grid(row=1, column=1, sticky="ew", pady=5) preset_combo.bind("<>", lambda _event: self._apply_preset()) self.name_var = tk.StringVar() ttk.Label(self.frame, text="Bezeichnung").grid(row=2, column=0, sticky="w", pady=5, padx=(0, 12)) - ttk.Entry(self.frame, textvariable=self.name_var, width=45).grid( + ttk.Entry(self.frame, textvariable=self.name_var, width=64).grid( row=2, column=1, sticky="ew", pady=5 ) self.detail_var = tk.StringVar() ttk.Label(self.frame, text="Details").grid(row=3, column=0, sticky="w", pady=5, padx=(0, 12)) - ttk.Entry(self.frame, textvariable=self.detail_var, width=45).grid( + ttk.Entry(self.frame, textvariable=self.detail_var, width=64).grid( row=3, column=1, sticky="ew", pady=5 ) @@ -943,8 +948,11 @@ class ReminderDialog(_Dialog): # instead of whatever size Tk guessed before everything was in place -- the # base dialog sets resizable(False, False) before this subclass's content # exists, so an explicit geometry() is needed or the window stays clipped. + # The floor of 700 keeps the dialog comfortably wide even for a short name/ + # detail; the user can still grow it further since it's resizable now. self.update_idletasks() - width, height = self.winfo_reqwidth(), self.winfo_reqheight() + width = max(700, self.winfo_reqwidth()) + height = self.winfo_reqheight() self.geometry(f"{width}x{height}") self.minsize(width, height) @@ -984,23 +992,27 @@ class ReminderDialog(_Dialog): table_frame = ttk.LabelFrame(self.frame, text="Positionen", padding=8) table_frame.grid(row=row, column=0, columnspan=2, sticky="nsew", pady=(8, 0)) table_frame.columnconfigure(0, weight=1) + table_frame.rowconfigure(0, weight=1) + amount_column_width = 100 self.items_tree = ttk.Treeview( table_frame, columns=("description", "amount"), show="headings", height=4, selectmode="browse" ) self.items_tree.heading("description", text="Beschreibung") self.items_tree.heading("amount", text="Betrag") - self.items_tree.column("description", width=220, anchor="w") - self.items_tree.column("amount", width=100, anchor="w", stretch=False) - self.items_tree.grid(row=0, column=0, sticky="ew") + self.items_tree.column("description", width=320, anchor="w") + self.items_tree.column("amount", width=amount_column_width, anchor="w", stretch=False) + self.items_tree.grid(row=0, column=0, sticky="nsew") self.items_tree.bind("<>", lambda _event: self._load_selected_item()) - # Edit row lines up under the table's own columns (same 220:100 width ratio as - # "Beschreibung"/"Betrag" above) instead of the fields and buttons being crammed - # into one row. + # Edit row lines up under the table's own columns: "Beschreibung" stretches to + # fill remaining space just like that Treeview column does, while "Betrag" sits + # in a frame pinned to the exact same pixel width as the fixed "amount" column + # above it (Entry's width= is in characters, not pixels, so a fixed-size frame + # is what gets a genuinely exact match instead of an approximate ratio). edit_row = ttk.Frame(table_frame) edit_row.grid(row=1, column=0, sticky="ew", pady=(8, 0)) - edit_row.columnconfigure(0, weight=22) - edit_row.columnconfigure(1, weight=10) + edit_row.columnconfigure(0, weight=1) + edit_row.columnconfigure(1, weight=0) self.item_description_var = tk.StringVar() description_combo = ttk.Combobox( edit_row, @@ -1009,8 +1021,11 @@ class ReminderDialog(_Dialog): ) description_combo.grid(row=0, column=0, sticky="ew", padx=(0, 8)) description_combo.bind("<>", lambda _event: self._prefill_item_amount()) + amount_frame = ttk.Frame(edit_row, width=amount_column_width) + amount_frame.grid(row=0, column=1, sticky="ns") + amount_frame.pack_propagate(False) self.item_amount_var = tk.StringVar() - ttk.Entry(edit_row, textvariable=self.item_amount_var).grid(row=0, column=1, sticky="ew") + ttk.Entry(amount_frame, textvariable=self.item_amount_var).pack(fill="both", expand=True) buttons_row = ttk.Frame(table_frame) buttons_row.grid(row=2, column=0, sticky="w", pady=(8, 0)) diff --git a/src/ccma/ui/options_dialog.py b/src/ccma/ui/options_dialog.py index dac9788..057790e 100644 --- a/src/ccma/ui/options_dialog.py +++ b/src/ccma/ui/options_dialog.py @@ -61,13 +61,21 @@ class OptionsDialog(tk.Toplevel): ) } self.title("Optionen") - self.geometry("900x650") - self.minsize(760, 540) self.transient(master.winfo_toplevel()) self.grab_set() self.resizable(True, True) self._build_ui() self.bind("", lambda _event: self.destroy()) + # A ttk.Notebook sizes itself to its tallest/widest tab, so once every tab + # (including "Mahnungen" with its two tables) is built, the natural required + # size reflects whichever one actually needs the most room -- open at that size + # instead of a static guess that clips a table/buttons at the bottom. The floor + # keeps the window from opening awkwardly small on short content. + self.update_idletasks() + width = max(900, self.winfo_reqwidth()) + height = max(650, self.winfo_reqheight()) + self.geometry(f"{width}x{height}") + self.minsize(760, 540) self.after_idle(self._center_on_parent) def _build_ui(self) -> None: