# markdownconverter/gui/editor.py import tkinter as tk from tkinter import messagebox import ttkbootstrap as tb from tkinter.scrolledtext import ScrolledText from tkhtmlview import HTMLScrolledText import markdown as MarkdownParser class EditorWindow(tb.Toplevel): """ A Toplevel window that provides a side-by-side Markdown editor and HTML preview with synchronized scrolling. """ def __init__(self, parent, file_path): super().__init__(master=parent, title=f"Editing: {file_path}") self.geometry("1200x800") self.file_path = file_path self.md_parser = MarkdownParser.Markdown(extensions=['fenced_code', 'tables']) self._setup_widgets() # Set the custom scroll command self.editor_text.vbar.config(command=self._on_editor_scroll) if not self._load_file(): return self.editor_text.bind("", self._update_preview) def _setup_widgets(self): """Creates and arranges the widgets for the editor.""" paned_window = tb.PanedWindow(self, orient=tk.HORIZONTAL) paned_window.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) editor_frame = tb.Frame(paned_window) tb.Label(editor_frame, text="Markdown Editor").pack(anchor="w", pady=(0, 5)) self.editor_text = ScrolledText(editor_frame, wrap=tk.WORD, width=60, font=("Courier New", 10)) self.editor_text.pack(fill=tk.BOTH, expand=True) paned_window.add(editor_frame, weight=1) preview_frame = tb.Frame(paned_window) tb.Label(preview_frame, text="Live Preview").pack(anchor="w", pady=(0, 5)) self.preview_widget = HTMLScrolledText(preview_frame, background="white") self.preview_widget.pack(fill=tk.BOTH, expand=True) paned_window.add(preview_frame, weight=1) button_frame = tb.Frame(self) button_frame.pack(fill=tk.X, padx=10, pady=5) tb.Button( button_frame, text="Save & Close", command=self._save_and_close, bootstyle="success" ).pack(side=tk.RIGHT, padx=5) tb.Button( button_frame, text="Cancel", command=self.destroy, bootstyle="secondary" ).pack(side=tk.RIGHT) def _on_editor_scroll(self, *args): """ This method is set as the command for the editor's scrollbar. It scrolls the editor text and syncs the preview. """ # Scroll the editor widget itself self.editor_text.yview(*args) # Get the new position scroll_position_fraction = self.editor_text.yview()[0] # --- CORREZIONE: Forza l'aggiornamento del widget di anteprima --- # This ensures the preview widget knows its full size before we try to scroll it. self.preview_widget.update_idletasks() if hasattr(self.preview_widget, 'html_label') and hasattr(self.preview_widget.html_label, '_w'): self.preview_widget.html_label._w.yview_moveto(scroll_position_fraction) def _load_file(self): """ Loads the content of the file into the editor. Returns True on success, False on failure. """ try: with open(self.file_path, 'r', encoding='utf-8') as f: content = f.read() self.editor_text.delete('1.0', tk.END) self.editor_text.insert('1.0', content) self._update_preview() return True except Exception as e: messagebox.showerror( title="Error Loading File", message=f"Could not load the file:\n{e}", ) self.destroy() return False def _update_preview(self, event=None): """Updates the HTML preview with the current editor content.""" md_text = self.editor_text.get("1.0", tk.END) self.md_parser.reset() html_text = self.md_parser.convert(md_text) self.preview_widget.set_html(html_text) # --- CORREZIONE: Sincronizza sempre la vista dopo un aggiornamento --- self._on_editor_scroll('moveto', self.editor_text.yview()[0]) def _save_and_close(self): """Saves the editor content back to the file and closes the window.""" try: content = self.editor_text.get("1.0", tk.END).strip() with open(self.file_path, 'w', encoding='utf-8') as f: f.write(content) self.destroy() except Exception as e: messagebox.showerror( title="Error Saving File", message=f"Could not save the file:\n{e}", )