Modify the hover effects so that they cover the whole width of the sidebar. Python 3.12 code: import tkinter as tk
from tkinter import ttk, messagebox, font
import sqlite3
import sys

# Enable High DPI support on Windows
if sys.platform.startswith('win'):
    try:
        import ctypes
        # Set DPI awareness to per-monitor
        ctypes.windll.shcore.SetProcessDpiAwareness(1)
    except Exception as e:
        print("Error setting DPI awareness:", e)


# Database functions
def run_query(query, parameters=()):
    with sqlite3.connect('media.db') as conn:
        cursor = conn.cursor()
        result = cursor.execute(query, parameters)
        if query.strip().lower().startswith("select"):
            return result.fetchall()
        conn.commit()


def create_tables():
    create_movies_table_sql = '''
    CREATE TABLE IF NOT EXISTS movies (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        movie TEXT,
        year TEXT,
        present BOOLEAN,
        watched BOOLEAN,
        notes TEXT
    );
    '''
    create_series_table_sql = '''
    CREATE TABLE IF NOT EXISTS series (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        series TEXT,
        seasons TEXT,
        year TEXT,
        present BOOLEAN,
        seasons_present TEXT,
        complete BOOLEAN,
        watched BOOLEAN,
        seasons_seen TEXT,
        notes TEXT
    );
    '''
    run_query(create_movies_table_sql)
    run_query(create_series_table_sql)


class MediaTracker(tk.Tk):
    def __init__(self):
        super().__init__()

        # Set root window background to white to eliminate grey gaps
        self.configure(bg='white')

        # Set scaling for High DPI displays
        if sys.platform.startswith('win'):
            try:
                # Calculate scaling based on the system DPI
                scaling = self.winfo_fpixels('1i') / 72  # Tk uses 72 DPI as the base
                self.tk.call('tk', 'scaling', scaling)
            except Exception as e:
                print("Error setting scaling:", e)

        self.title("Movies and Series Tracker")
        self.geometry("1200x600")  # Adjusted for better layout

        # Set font to Literata
        try:
            self.custom_font = font.Font(family='Literata', size=12)
        except:
            # Fallback to default font if 'Literata' is not available
            self.custom_font = font.Font(family='Arial', size=12)

        # Calculate row height based on font metrics
        font_line_space = self.custom_font.metrics('linespace')
        desired_padding = 4  # Additional pixels for padding
        row_height = font_line_space + desired_padding

        # Initialize ttk Style
        style = ttk.Style()
        style.theme_use("default")  # Ensure a consistent theme

        # Remove border from Treeview by customizing the layout
        style.layout(
            'Custom.Treeview',
            [('Custom.Treeview.treearea', {'sticky': 'nsew'})]
        )
        style.configure(
            "Custom.Treeview",
            font=self.custom_font,
            rowheight=row_height,
            borderwidth=0,
            relief='flat',
            highlightthickness=0,
            background='white',        # Optional: set Treeview background to white
            fieldbackground='white'
        )

        # Configure the Treeview heading style with borders
        style.configure(
            "Custom.Treeview.Heading",
            font=(
                self.custom_font.cget("family"),
                self.custom_font.cget("size"),
                "bold"
            ),
            background='white',        # Set header background to white
            relief='solid',             # Add solid relief to enable borders
            borderwidth=1,              # Set border width to 1 pixel
            anchor='center'            # Center the header text
        )
        style.map(
            "Custom.Treeview.Heading",
            background=[('active', 'white')],
            relief=[('active', 'solid')]
        )

        # Optionally, configure Button style
        style.configure("Custom.TButton", font=self.custom_font)

        # Define the new sidebar background color
        self.sidebar_bg = '#F7F8FC'  # Light blue

        # Configure grid layout for root window
        self.grid_rowconfigure(1, weight=1)      # Row 1 will expand vertically
        self.grid_columnconfigure(2, weight=1)   # Column 2 (Treeview content) will expand horizontally

        # Header Frame at the top, aligned with the sidebar (spanning columns 1-3)
        self.header = tk.Frame(self, bg='white', height=50)
        self.header.grid(row=0, column=1, columnspan=3, sticky='ew')
        self.header.grid_propagate(False)  # Prevent frame from resizing based on content

        self.header_label = tk.Label(
            self.header,
            text="Movies",
            bg='white',
            font=self.custom_font,
            anchor='w',  # Align text to the left
            padx=10
        )
        self.header_label.pack(fill='x', pady=10)

        # Sidebar spanning both rows to cover full height
        self.sidebar = tk.Frame(self, width=150, bg=self.sidebar_bg)
        self.sidebar.grid(row=0, column=0, rowspan=2, sticky='ns')
        self.sidebar.grid_propagate(False)  # Prevent frame from resizing based on content

        # Spacer (tripled size from 20 to 60 pixels)
        self.spacer = tk.Frame(self, width=60, bg='white')  # Updated width to 60px
        self.spacer.grid(row=1, column=1, sticky='ns')
        self.spacer.grid_propagate(False)  # Prevent frame from resizing based on content

        # Content (Treeview)
        self.content = tk.Frame(self, bg='white')
        self.content.grid(row=1, column=2, sticky='nsew')

        # Button Bar on the far right
        self.button_bar = tk.Frame(self, width=50, bg='white')
        self.button_bar.grid(row=1, column=3, sticky='ns')
        self.button_bar.grid_propagate(False)  # Prevent frame from resizing based on content

        # Sidebar labels instead of buttons
        self.create_sidebar_label("Movies", self.show_movies)
        self.create_sidebar_label("Series", self.show_series)

        # Add and Delete buttons in button bar with custom style
        self.add_button = ttk.Button(
            self.button_bar,
            text="+",
            command=self.add_entry,
            width=3,
            style="Custom.TButton"
        )
        self.add_button.pack(pady=20)

        self.delete_button = ttk.Button(
            self.button_bar,
            text="-",
            command=self.delete_entry,
            width=3,
            style="Custom.TButton"
        )
        self.delete_button.pack(pady=10)

        # Initialize Treeview and Scrollbar
        self.tree = None
        self.scrollbar = None
        self.current_table = None
        self.current_row_id = None

        # Define boolean columns for each table
        self.boolean_columns = {
            'movies': ['Present', 'Watched'],
            'series': ['Present', 'Complete', 'Watched']
        }

        # Load data
        self.show_movies()

    def create_sidebar_label(self, text, command):
        label = tk.Label(
            self.sidebar,
            text=text,
            bg=self.sidebar_bg,  # Sidebar background color
            font=self.custom_font,
            cursor="hand2",
            anchor='w',
            padx=10,
            pady=5
        )
        label.pack(pady=10, padx=10, fill='x')

        # Bind hover effects
        label.bind("<Enter>", lambda e: label.config(bg='#EAEDEF'))
        label.bind("<Leave>", lambda e: label.config(bg=self.sidebar_bg))  # Revert to sidebar_bg

        # Bind click event
        label.bind("<Button-1>", lambda e: command())

    def clear_content(self):
        """Destroy existing Treeview and Scrollbar if they exist."""
        if self.tree:
            self.tree.destroy()
            self.tree = None
        if self.scrollbar:
            self.scrollbar.destroy()
            self.scrollbar = None

    def setup_treeview(self, columns, headings):
        """Create and pack Treeview and Scrollbar."""
        self.tree = ttk.Treeview(
            self.content,
            columns=columns,
            show='headings',
            selectmode='browse',
            style="Custom.Treeview"
        )
        for col, head in zip(columns, headings):
            self.tree.heading(col, text=head, anchor='center')
            if head.lower() == 'notes':
                self.tree.column(col, width=300, anchor='w')
            elif head in self.boolean_columns.get(self.current_table, []):
                self.tree.column(col, width=100, anchor='center')
            else:
                self.tree.column(col, width=150, anchor='center')

        # Creating and configuring Scrollbar
        self.scrollbar = ttk.Scrollbar(
            self.content,
            orient="vertical",
            command=self.tree.yview
        )
        self.tree.configure(yscrollcommand=self.scrollbar.set)
        self.scrollbar.pack(side='right', fill='y')
        self.tree.pack(fill='both', expand=True)

        # Bind events
        self.tree.bind('<<TreeviewSelect>>', self.on_row_select)
        self.tree.bind('<Double-1>', self.on_double_click)
        self.tree.bind('<Button-1>', self.on_single_click)

        # Change cursor for boolean columns
        self.tree.bind("<Motion>", self.treeview_click_cursor)

    def treeview_click_cursor(self, event):
        """Change cursor to hand2 when hovering over boolean columns."""
        region = self.tree.identify("region", event.x, event.y)
        if region == "cell":
            column = self.tree.identify_column(event.x)
            try:
                col_index = int(column.replace('#', '')) - 1
                column_name = self.tree['columns'][col_index]
                if column_name in self.boolean_columns.get(self.current_table, []):
                    self.tree.configure(cursor="hand2")
                else:
                    self.tree.configure(cursor="")
            except (IndexError, ValueError):
                self.tree.configure(cursor="")
        else:
            self.tree.configure(cursor="")

    def load_movie_data(self):
        self.clear_content()
        columns = ["Movie", "Year", "Present", "Watched", "Notes"]
        headings = ["Movie", "Year", "Present", "Watched", "Notes"]
        self.setup_treeview(columns, headings)
        self.current_table = 'movies'

        # Update header
        self.header_label.config(text="Movies")

        for row in run_query('SELECT * FROM movies'):
            record_id = row[0]  # ID from the database
            movie, year, present, watched, notes = row[1], row[2], row[3], row[4], row[5]
            display_row = [
                movie,
                year,
                self.bool_to_checkbox(present),
                self.bool_to_checkbox(watched),
                notes
            ]
            self.tree.insert('', 'end', iid=str(record_id), values=display_row)

    def load_series_data(self):
        self.clear_content()
        columns = ["Series", "Seasons", "Year", "Present",
                   "Seasons Present", "Complete", "Watched", "Seasons Seen", "Notes"]
        headings = ["Series", "Seasons", "Year", "Present",
                   "Seasons Present", "Complete", "Watched", "Seasons Seen", "Notes"]
        self.setup_treeview(columns, headings)
        self.current_table = 'series'

        # Update header
        self.header_label.config(text="Series")

        for row in run_query('SELECT * FROM series'):
            record_id = row[0]  # ID from the database
            series, seasons, year, present, seasons_present, complete, watched, seasons_seen, notes = (
                row[1], row[2], row[3], row[4], row[5],
                row[6], row[7], row[8], row[9]
            )
            display_row = [
                series,
                seasons,
                year,
                self.bool_to_checkbox(present),
                seasons_present,
                self.bool_to_checkbox(complete),
                self.bool_to_checkbox(watched),
                seasons_seen,
                notes
            ]
            self.tree.insert('', 'end', iid=str(record_id), values=display_row)

    def show_movies(self):
        self.load_movie_data()

    def show_series(self):
        self.load_series_data()

    def add_entry(self):
        if self.current_table == 'movies':
            run_query(
                "INSERT INTO movies (movie, year, present, watched, notes) VALUES (?, ?, ?, ?, ?)",
                ('', '', False, False, '')
            )
            self.load_movie_data()
        elif self.current_table == 'series':
            run_query(
                """INSERT INTO series
                (series, seasons, year, present, seasons_present, complete, watched, seasons_seen, notes)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                ('', '', '', False, '', False, False, '', '')
            )
            self.load_series_data()

    def delete_entry(self):
        selected_item = self.tree.selection()
        if not selected_item:
            messagebox.showwarning("No selection", "Please select a row to delete.")
            return

        row_id = selected_item[0]
        if self.current_table == 'movies':
            run_query("DELETE FROM movies WHERE id = ?", (row_id,))
            self.load_movie_data()
        elif self.current_table == 'series':
            run_query("DELETE FROM series WHERE id = ?", (row_id,))
            self.load_series_data()

    def on_row_select(self, event):
        selected_item = self.tree.selection()
        if selected_item:
            self.current_row_id = selected_item[0]
        else:
            self.current_row_id = None

    def on_single_click(self, event):
        # Identify the region where the click occurred
        region = self.tree.identify("region", event.x, event.y)
        if region != "cell":
            return

        # Identify the row and column
        row_id = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)
        if not row_id or not column:
            return

        # Get column index
        try:
            col_index = int(column.replace('#', '')) - 1
        except ValueError:
            return  # Invalid column

        if col_index < 0 or col_index >= len(self.tree['columns']):
            return  # Out of range

        column_name = self.tree['columns'][col_index]

        # Check if the column is a boolean field
        if self.current_table and column_name in self.boolean_columns.get(self.current_table, []):
            # Get current value
            current_value = self.tree.set(row_id, column_name)
            # Toggle the value
            new_value = not self.checkbox_to_bool(current_value)
            self.tree.set(row_id, column_name, self.bool_to_checkbox(new_value))

            # Update the database
            row_values = self.tree.item(row_id)['values']
            self.update_database(row_values)

    def on_double_click(self, event):
        item = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)
        if not item:
            return
        try:
            col_num = int(column.replace('#', '')) - 1
        except ValueError:
            return  # Invalid column

        # Prevent editing boolean columns
        if (self.current_table == 'movies' and self.tree['columns'][col_num] in self.boolean_columns['movies']) or \
           (self.current_table == 'series' and self.tree['columns'][col_num] in self.boolean_columns['series']):
            return

        if col_num < 0 or col_num >= len(self.tree['columns']):
            return  # Out of range

        column_name = self.tree['columns'][col_num]
        value = self.tree.set(item, column_name)

        # Determine if the column is boolean (if so, do not allow editing here)
        if self.current_table and column_name in self.boolean_columns.get(self.current_table, []):
            return  # Do nothing; handled by single-click

        # Get cell bounding box
        bbox = self.tree.bbox(item, column)
        if not bbox:
            return
        x, y, width, height = bbox

        # Create entry widget with flat appearance
        entry = tk.Entry(self.content, font=self.custom_font, relief='flat', borderwidth=0, highlightthickness=0, bg='white')
        entry.place(x=x, y=y, width=width, height=height)
        entry.insert(0, value)
        entry.focus()

        def save_edit(event):
            new_value = entry.get()
            entry.destroy()
            self.tree.set(item, column_name, new_value)
            row_values = self.tree.item(item)['values']
            self.update_database(row_values)

        entry.bind('<Return>', save_edit)
        entry.bind('<FocusOut>', save_edit)

    def update_database(self, row_values):
        if self.current_table == 'movies':
            # Map '☑'/'☐' back to boolean
            present = self.checkbox_to_bool(row_values[2])
            watched = self.checkbox_to_bool(row_values[3])
            query = "UPDATE movies SET movie = ?, year = ?, present = ?, watched = ?, notes = ? WHERE id = ?"
            parameters = (row_values[0], row_values[1], present, watched,
                          row_values[4], self.current_row_id)
        elif self.current_table == 'series':
            # Map '☑'/'☐' back to boolean
            present = self.checkbox_to_bool(row_values[3])
            complete = self.checkbox_to_bool(row_values[5])
            watched = self.checkbox_to_bool(row_values[6])
            query = """UPDATE series SET series = ?, seasons = ?, year = ?, present = ?,
            seasons_present = ?, complete = ?, watched = ?, seasons_seen = ?, notes = ? WHERE id = ?"""
            parameters = (
                row_values[0], row_values[1], row_values[2],
                present, row_values[4],
                complete, watched,
                row_values[7], row_values[8], self.current_row_id
            )
        run_query(query, parameters)

    @staticmethod
    def bool_to_checkbox(value):
        """Convert boolean to checkbox character."""
        return '☑' if value else '☐'

    @staticmethod
    def checkbox_to_bool(value):
        """Convert checkbox character to boolean."""
        return True if value == '☑' else False


if __name__ == "__main__":
    create_tables()
    app = MediaTracker()
    app.mainloop()