SXXXXXXX_MarkdownConverter/markdownconverter/gui/editor.py
2025-06-18 09:19:00 +02:00

129 lines
4.6 KiB
Python

# 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("<KeyRelease>", 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}",
)