diff --git a/src/thechart/ui/ui_manager.py b/src/thechart/ui/ui_manager.py index 902b58a..2ed28e4 100644 --- a/src/thechart/ui/ui_manager.py +++ b/src/thechart/ui/ui_manager.py @@ -602,52 +602,199 @@ class UIManager: pass def create_edit_window(self, values, callbacks): - """Minimal edit window allowing date and note changes. + """Create the full edit window with dynamic fields. - This simplified version passes missing pathology/medicine values as zeros - and an empty dose mapping to the caller's save callback for compatibility. + Expected `values` format (as provided by main._create_edit_window): + (date, + [pathology values...], + [for each medicine: taken, doses_string], + note) + + On save, call the provided callback with: + (window, date, pathology_values..., medicine_values..., note, dose_data_dict) """ win = tk.Toplevel(master=self.root) win.title("Edit Entry") win.transient(self.root) - win.minsize(400, 240) + win.minsize(520, 420) - container = ttk.Frame(win, padding=12) - container.pack(fill=tk.BOTH, expand=True) + # Scrollable content area + outer = ttk.Frame(win, padding=12) + outer.pack(fill=tk.BOTH, expand=True) - ttk.Label(container, text="Date (mm/dd/yyyy):").grid( - row=0, column=0, sticky="w" - ) - date_var = tk.StringVar(value=values[0] if values else "") + canvas = tk.Canvas(outer, highlightthickness=0) + vscroll = ttk.Scrollbar(outer, orient="vertical", command=canvas.yview) + content = ttk.Frame(canvas) + content.grid_columnconfigure(1, weight=1) + canvas_window_id = canvas.create_window((0, 0), window=content, anchor="nw") + canvas.configure(yscrollcommand=vscroll.set) + canvas.grid(row=0, column=0, sticky="nsew") + vscroll.grid(row=0, column=1, sticky="ns") + outer.grid_rowconfigure(0, weight=1) + outer.grid_columnconfigure(0, weight=1) + + def _on_configure(_evt=None): + canvas.configure(scrollregion=canvas.bbox("all")) + + def _on_canvas_width(_evt=None): + # Keep inner frame width in sync so widgets expand + bbox = canvas.bbox("all") + if bbox: + canvas.itemconfigure(canvas_window_id, width=canvas.winfo_width()) + + content.bind("", _on_configure) + canvas.bind("", _on_canvas_width) + + # Unpack incoming values using managers' key orders + pathology_keys = list(self.pathology_manager.get_pathology_keys()) + medicine_keys = list(self.medicine_manager.get_medicine_keys()) + + idx = 0 + date_str = values[idx] if values else "" + idx += 1 + + # Pathology values + pathology_values: dict[str, int] = {} + for key in pathology_keys: + try: + pathology_values[key] = int(values[idx]) + except Exception: + pathology_values[key] = 0 + idx += 1 + + # Medicine taken and stored dose strings + medicine_taken: dict[str, int] = {} + medicine_doses_str: dict[str, str] = {} + for key in medicine_keys: + try: + medicine_taken[key] = int(values[idx]) + except Exception: + medicine_taken[key] = 0 + idx += 1 + try: + medicine_doses_str[key] = str(values[idx]) + except Exception: + medicine_doses_str[key] = "" + idx += 1 + + note_val = values[idx] if idx < len(values) else "" + + # --- Build UI --- + # Date + ttk.Label(content, text="Date (mm/dd/yyyy):").grid(row=0, column=0, sticky="w") + date_var = tk.StringVar(value=str(date_str)) ttk.Entry( - container, textvariable=date_var, justify="center", style="Modern.TEntry" + content, + textvariable=date_var, + justify="center", + style="Modern.TEntry", ).grid(row=0, column=1, sticky="ew", padx=8, pady=4) - ttk.Label(container, text="Note:").grid(row=1, column=0, sticky="w") - note_val = values[-1] if values else "" + # Pathologies section + row = 1 + ttk.Label(content, text="Pathologies:").grid(row=row, column=0, sticky="w") + path_frame = ttk.Frame(content) + path_frame.grid(row=row, column=1, sticky="ew", padx=8, pady=4) + path_frame.grid_columnconfigure(1, weight=1) + row += 1 + + pathology_vars: dict[str, tk.IntVar] = {} + for i, key in enumerate(pathology_keys): + pathology_vars[key] = tk.IntVar(value=pathology_values.get(key, 0)) + pathology = self.pathology_manager.get_pathology(key) + label = ( + pathology.display_name + if getattr(pathology, "display_name", None) + else key.capitalize() + ) + ttk.Label(path_frame, text=f"{label}:").grid( + row=i, column=0, sticky="w", padx=2 + ) + scale = ttk.Scale(path_frame, from_=0, to=10, orient=tk.HORIZONTAL) + scale.grid(row=i, column=1, sticky="ew", padx=4) + with suppress(Exception): + scale.set(pathology_vars[key].get()) + + # Keep IntVar in sync when dragging + def _mk_scale_cmd(k: str, s: ttk.Scale): + def _cmd(_evt=None): + with suppress(Exception): + pathology_vars[k].set(int(float(s.get()))) + + return _cmd + + scale.bind("", _mk_scale_cmd(key, scale)) + + # Medicines section + ttk.Label(content, text="Medicines:").grid(row=row, column=0, sticky="w") + meds_frame = ttk.Frame(content) + meds_frame.grid(row=row, column=1, sticky="ew", padx=8, pady=4) + meds_frame.grid_columnconfigure(0, weight=1) + row += 1 + + medicine_vars: dict[str, tk.IntVar] = {} + for i, key in enumerate(medicine_keys): + medicine_vars[key] = tk.IntVar(value=medicine_taken.get(key, 0)) + med = self.medicine_manager.get_medicine(key) + text = ( + med.display_name + if getattr(med, "display_name", None) + else key.capitalize() + ) + chk = ttk.Checkbutton( + meds_frame, + text=text, + variable=medicine_vars[key], + style="Modern.TCheckbutton", + ) + chk.grid(row=i, column=0, sticky="w", padx=2, pady=2) + + # Note field + ttk.Label(content, text="Note:").grid(row=row, column=0, sticky="nw") note_var = tk.StringVar(value=str(note_val)) - ttk.Entry(container, textvariable=note_var, style="Modern.TEntry").grid( - row=1, column=1, sticky="ew", padx=8, pady=4 + ttk.Entry(content, textvariable=note_var, style="Modern.TEntry").grid( + row=row, column=1, sticky="ew", padx=8, pady=4 ) + row += 1 - container.grid_columnconfigure(1, weight=1) - - buttons = ttk.Frame(container) - buttons.grid(row=2, column=0, columnspan=2, pady=10) + # Buttons + buttons = ttk.Frame(content) + buttons.grid(row=row, column=0, columnspan=2, pady=10) def _on_save(): - # Only provide date and note; caller will default others. + # Build args matching main._save_edit expectation + args: list[object] = [date_var.get()] + # Pathology values in key order + for key in pathology_keys: + args.append(int(pathology_vars[key].get())) + # Medicine 'taken' values in key order + for key in medicine_keys: + args.append(int(medicine_vars[key].get())) + # Note + args.append(note_var.get()) + # Preserve existing dose strings unless caller offers an editor elsewhere + dose_map = {k: medicine_doses_str.get(k, "") for k in medicine_keys} + args.append(dose_map) with suppress(Exception): - callbacks.get("save")(win, date_var.get(), note_var.get(), {}) + callbacks.get("save")(win, *args) def _on_delete(): with suppress(Exception): callbacks.get("delete")(win) + def _on_cancel(): + with suppress(Exception): + win.destroy() + ttk.Button(buttons, text="Save", command=_on_save, style="Action.TButton").pack( side="left", padx=5 ) ttk.Button(buttons, text="Delete", command=_on_delete).pack(side="left", padx=5) + ttk.Button(buttons, text="Cancel", command=_on_cancel).pack(side="left", padx=5) + + # Finalize scroll region + content.update_idletasks() + canvas.configure(scrollregion=canvas.bbox("all")) return win