mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-25 06:55:17 +02:00
Make ReminderDialog resizable/wider and fix Options dialog bottom clipping
ReminderDialog: override the shared dialog base's resizable(False, False), widen the initial layout, and pin the items-table edit row's amount field to the exact same pixel width as the table's fixed "amount" column (via a pack_propagate(False) frame) while the description field stretches like the table's own column does -- keeps alignment exact even after resizing. OptionsDialog: compute the window's initial geometry from its actual required size after all tabs (incl. "Mahnungen") are built, instead of a static guess that clipped the Mahnungen tab's tables/buttons at the bottom. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3940926a22
commit
f3bdffd347
+28
-13
@@ -878,6 +878,10 @@ class ReminderDialog(_Dialog):
|
|||||||
|
|
||||||
def __init__(self, master, repository, member_id, claim_id, on_saved):
|
def __init__(self, master, repository, member_id, claim_id, on_saved):
|
||||||
super().__init__(master, "Mahnung vorbereiten", 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
|
self.repository, self.member_id, self.claim_id = repository, member_id, claim_id
|
||||||
data, _claim = repository.get_claim(member_id, claim_id)
|
data, _claim = repository.get_claim(member_id, claim_id)
|
||||||
existing_levels = {
|
existing_levels = {
|
||||||
@@ -891,6 +895,7 @@ class ReminderDialog(_Dialog):
|
|||||||
self.items: list[dict[str, str]] = []
|
self.items: list[dict[str, str]] = []
|
||||||
|
|
||||||
self.frame.columnconfigure(1, weight=1)
|
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="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(
|
ttk.Label(self.frame, text=str(self.level), style="Mono.TLabel").grid(
|
||||||
row=0, column=1, sticky="w", pady=5
|
row=0, column=1, sticky="w", pady=5
|
||||||
@@ -904,20 +909,20 @@ class ReminderDialog(_Dialog):
|
|||||||
textvariable=self.preset_var,
|
textvariable=self.preset_var,
|
||||||
values=list(self.preset_by_label),
|
values=list(self.preset_by_label),
|
||||||
state="readonly",
|
state="readonly",
|
||||||
width=38,
|
width=55,
|
||||||
)
|
)
|
||||||
preset_combo.grid(row=1, column=1, sticky="ew", pady=5)
|
preset_combo.grid(row=1, column=1, sticky="ew", pady=5)
|
||||||
preset_combo.bind("<<ComboboxSelected>>", lambda _event: self._apply_preset())
|
preset_combo.bind("<<ComboboxSelected>>", lambda _event: self._apply_preset())
|
||||||
|
|
||||||
self.name_var = tk.StringVar()
|
self.name_var = tk.StringVar()
|
||||||
ttk.Label(self.frame, text="Bezeichnung").grid(row=2, column=0, sticky="w", pady=5, padx=(0, 12))
|
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
|
row=2, column=1, sticky="ew", pady=5
|
||||||
)
|
)
|
||||||
|
|
||||||
self.detail_var = tk.StringVar()
|
self.detail_var = tk.StringVar()
|
||||||
ttk.Label(self.frame, text="Details").grid(row=3, column=0, sticky="w", pady=5, padx=(0, 12))
|
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
|
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
|
# instead of whatever size Tk guessed before everything was in place -- the
|
||||||
# base dialog sets resizable(False, False) before this subclass's content
|
# base dialog sets resizable(False, False) before this subclass's content
|
||||||
# exists, so an explicit geometry() is needed or the window stays clipped.
|
# 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()
|
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.geometry(f"{width}x{height}")
|
||||||
self.minsize(width, height)
|
self.minsize(width, height)
|
||||||
|
|
||||||
@@ -984,23 +992,27 @@ class ReminderDialog(_Dialog):
|
|||||||
table_frame = ttk.LabelFrame(self.frame, text="Positionen", padding=8)
|
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.grid(row=row, column=0, columnspan=2, sticky="nsew", pady=(8, 0))
|
||||||
table_frame.columnconfigure(0, weight=1)
|
table_frame.columnconfigure(0, weight=1)
|
||||||
|
table_frame.rowconfigure(0, weight=1)
|
||||||
|
amount_column_width = 100
|
||||||
self.items_tree = ttk.Treeview(
|
self.items_tree = ttk.Treeview(
|
||||||
table_frame, columns=("description", "amount"), show="headings", height=4, selectmode="browse"
|
table_frame, columns=("description", "amount"), show="headings", height=4, selectmode="browse"
|
||||||
)
|
)
|
||||||
self.items_tree.heading("description", text="Beschreibung")
|
self.items_tree.heading("description", text="Beschreibung")
|
||||||
self.items_tree.heading("amount", text="Betrag")
|
self.items_tree.heading("amount", text="Betrag")
|
||||||
self.items_tree.column("description", width=220, anchor="w")
|
self.items_tree.column("description", width=320, anchor="w")
|
||||||
self.items_tree.column("amount", width=100, anchor="w", stretch=False)
|
self.items_tree.column("amount", width=amount_column_width, anchor="w", stretch=False)
|
||||||
self.items_tree.grid(row=0, column=0, sticky="ew")
|
self.items_tree.grid(row=0, column=0, sticky="nsew")
|
||||||
self.items_tree.bind("<<TreeviewSelect>>", lambda _event: self._load_selected_item())
|
self.items_tree.bind("<<TreeviewSelect>>", lambda _event: self._load_selected_item())
|
||||||
|
|
||||||
# Edit row lines up under the table's own columns (same 220:100 width ratio as
|
# Edit row lines up under the table's own columns: "Beschreibung" stretches to
|
||||||
# "Beschreibung"/"Betrag" above) instead of the fields and buttons being crammed
|
# fill remaining space just like that Treeview column does, while "Betrag" sits
|
||||||
# into one row.
|
# 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 = ttk.Frame(table_frame)
|
||||||
edit_row.grid(row=1, column=0, sticky="ew", pady=(8, 0))
|
edit_row.grid(row=1, column=0, sticky="ew", pady=(8, 0))
|
||||||
edit_row.columnconfigure(0, weight=22)
|
edit_row.columnconfigure(0, weight=1)
|
||||||
edit_row.columnconfigure(1, weight=10)
|
edit_row.columnconfigure(1, weight=0)
|
||||||
self.item_description_var = tk.StringVar()
|
self.item_description_var = tk.StringVar()
|
||||||
description_combo = ttk.Combobox(
|
description_combo = ttk.Combobox(
|
||||||
edit_row,
|
edit_row,
|
||||||
@@ -1009,8 +1021,11 @@ class ReminderDialog(_Dialog):
|
|||||||
)
|
)
|
||||||
description_combo.grid(row=0, column=0, sticky="ew", padx=(0, 8))
|
description_combo.grid(row=0, column=0, sticky="ew", padx=(0, 8))
|
||||||
description_combo.bind("<<ComboboxSelected>>", lambda _event: self._prefill_item_amount())
|
description_combo.bind("<<ComboboxSelected>>", 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()
|
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 = ttk.Frame(table_frame)
|
||||||
buttons_row.grid(row=2, column=0, sticky="w", pady=(8, 0))
|
buttons_row.grid(row=2, column=0, sticky="w", pady=(8, 0))
|
||||||
|
|||||||
@@ -61,13 +61,21 @@ class OptionsDialog(tk.Toplevel):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
self.title("Optionen")
|
self.title("Optionen")
|
||||||
self.geometry("900x650")
|
|
||||||
self.minsize(760, 540)
|
|
||||||
self.transient(master.winfo_toplevel())
|
self.transient(master.winfo_toplevel())
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
self.resizable(True, True)
|
self.resizable(True, True)
|
||||||
self._build_ui()
|
self._build_ui()
|
||||||
self.bind("<Escape>", lambda _event: self.destroy())
|
self.bind("<Escape>", 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)
|
self.after_idle(self._center_on_parent)
|
||||||
|
|
||||||
def _build_ui(self) -> None:
|
def _build_ui(self) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user