|
1 | | -import tkinter as tk |
2 | | -from tkinter import ttk |
3 | | -from utils.constants import FONT_UI_HEADER, FONT_UI_NORMAL, SECONDARY_BG, TERTIARY_BG, TEXT_PRIMARY |
| 1 | +import customtkinter as ctk |
4 | 2 |
|
5 | | -class CollapsiblePane(ttk.Frame): |
6 | | - """A collapsible pane widget that can hide or show its content.""" |
7 | | - def __init__(self, parent, text="", body_background=SECONDARY_BG): |
8 | | - super().__init__(parent, style='CardInner.TFrame') |
| 3 | +class CollapsiblePane(ctk.CTkFrame): |
| 4 | + """A collapsible pane widget for customtkinter.""" |
| 5 | + def __init__(self, parent, text="", **kwargs): |
| 6 | + super().__init__(parent, fg_color="transparent", **kwargs) |
9 | 7 |
|
10 | 8 | self.columnconfigure(0, weight=1) |
11 | | - self.body_background = body_background |
12 | 9 |
|
13 | 10 | # Header |
14 | | - self.header_frame = ttk.Frame(self, style='CardInner.TFrame') |
| 11 | + self.header_frame = ctk.CTkFrame(self, fg_color="transparent", cursor="hand2") |
15 | 12 | self.header_frame.grid(row=0, column=0, sticky='ew') |
16 | 13 | self.header_frame.columnconfigure(1, weight=1) |
17 | 14 |
|
18 | | - self.toggle_button = ttk.Label(self.header_frame, text="▼", font=('Segoe UI', 10), style='TLabel') |
19 | | - self.toggle_button.grid(row=0, column=0, padx=5, sticky='w') |
| 15 | + self.toggle_button = ctk.CTkLabel(self.header_frame, text="▼", font=('Segoe UI', 14, 'bold')) |
| 16 | + self.toggle_button.grid(row=0, column=0, padx=(5, 10), sticky='w') |
20 | 17 |
|
21 | | - self.title_label = ttk.Label(self.header_frame, text=text, font=FONT_UI_HEADER, style='Header.TLabel') |
| 18 | + self.title_label = ctk.CTkLabel(self.header_frame, text=text, font=('Segoe UI', 16, 'bold')) |
22 | 19 | self.title_label.grid(row=0, column=1, sticky='w') |
23 | 20 |
|
24 | 21 | # Body |
25 | | - self.body = ttk.Frame(self, style='CardInner.TFrame', padding=(15, 10)) |
| 22 | + self.body = ctk.CTkFrame(self, fg_color="transparent") |
| 23 | + self.body.grid(row=1, column=0, sticky='nsew', padx=10, pady=5) |
26 | 24 |
|
| 25 | + self.header_frame.bind("<Button-1>", self._toggle) |
27 | 26 | self.toggle_button.bind("<Button-1>", self._toggle) |
28 | 27 | self.title_label.bind("<Button-1>", self._toggle) |
| 28 | + |
29 | 29 | self._is_collapsed = False |
30 | | - self.body.grid(row=1, column=0, sticky='nsew', padx=5, pady=5) # Start expanded |
31 | 30 |
|
32 | | - def _toggle(self, event): |
| 31 | + def _toggle(self, event=None): |
33 | 32 | if self._is_collapsed: |
34 | | - self.body.grid(row=1, column=0, sticky='nsew', padx=5, pady=5) |
| 33 | + self.body.grid(row=1, column=0, sticky='nsew', padx=10, pady=5) |
35 | 34 | self.toggle_button.configure(text="▼") |
36 | 35 | else: |
37 | 36 | self.body.grid_remove() |
38 | 37 | self.toggle_button.configure(text="▶") |
39 | 38 | self._is_collapsed = not self._is_collapsed |
40 | | - |
41 | | -class ToolTip: |
42 | | - def __init__(self, widget, text): |
43 | | - self.widget, self.text, self.tooltip_window = widget, text, None |
44 | | - self.widget.bind("<Enter>", self.show) |
45 | | - self.widget.bind("<Leave>", self.hide) |
46 | | - def show(self, event=None): |
47 | | - if self.tooltip_window or not self.text: return |
48 | | - x, y, _, _ = self.widget.bbox("insert") |
49 | | - x += self.widget.winfo_rootx() + 25 |
50 | | - y += self.widget.winfo_rooty() + 25 |
51 | | - self.tooltip_window = tw = tk.Toplevel(self.widget) |
52 | | - tw.wm_overrideredirect(True) |
53 | | - tw.wm_geometry(f"+{x}+{y}") |
54 | | - ttk.Label(tw, text=self.text, background=TERTIARY_BG, foreground=TEXT_PRIMARY, font=FONT_UI_NORMAL, relief='solid', borderwidth=1, padding=5).pack() |
55 | | - def hide(self, event=None): |
56 | | - if self.tooltip_window: self.tooltip_window.destroy() |
57 | | - self.tooltip_window = None |
58 | | - |
59 | | -class CustomDropdownMenu(ttk.Frame): |
60 | | - def __init__(self, parent, textvariable, options, style_prefix, **kwargs): |
61 | | - super().__init__(parent, **kwargs) |
62 | | - self.textvariable = textvariable |
63 | | - self.options = options |
64 | | - self.style_prefix = style_prefix |
65 | | - self.is_open = False |
66 | | - |
67 | | - self.button = ttk.Button(self, textvariable=self.textvariable, command=self.toggle, style=f"{self.style_prefix}.TButton") |
68 | | - self.button.pack(fill=tk.BOTH, expand=True) |
69 | | - |
70 | | - self.window = tk.Toplevel(self.master) |
71 | | - self.window.withdraw() |
72 | | - self.window.wm_overrideredirect(True) |
73 | | - |
74 | | - self.frame = ttk.Frame(self.window, style=f"{self.style_prefix}.TFrame") |
75 | | - self.frame.pack() |
76 | | - |
77 | | - self.canvas = tk.Canvas(self.frame, background=SECONDARY_BG, highlightthickness=0) |
78 | | - self.scrollbar = ttk.Scrollbar(self.frame, orient="vertical", command=self.canvas.yview, style="Vertical.TScrollbar") |
79 | | - self.scrollable_frame = ttk.Frame(self.canvas, style=f"{self.style_prefix}.TFrame") |
80 | | - |
81 | | - self.scrollable_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))) |
82 | | - self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") |
83 | | - self.canvas.configure(yscrollcommand=self.scrollbar.set) |
84 | | - |
85 | | - self.canvas.pack(side="left", fill="both", expand=True) |
86 | | - self.scrollbar.pack(side="right", fill="y") |
87 | | - |
88 | | - self.master.bind("<Configure>", self.hide) |
89 | | - self.button.bind("<Destroy>", self.hide) |
90 | | - |
91 | | - def toggle(self, event=None): |
92 | | - if self.is_open: |
93 | | - self.hide() |
94 | | - else: |
95 | | - self.show() |
96 | | - |
97 | | - def show(self): |
98 | | - self.is_open = True |
99 | | - self.update_options(self.options) |
100 | | - x = self.button.winfo_rootx() |
101 | | - y = self.button.winfo_rooty() + self.button.winfo_height() |
102 | | - self.window.wm_geometry(f"+{x}+{y}") |
103 | | - self.window.deiconify() |
104 | | - self.window.lift() |
105 | | - self.window.focus_set() |
106 | | - self.master.bind_all("<Button-1>", self._on_click_elsewhere, add=True) |
107 | | - |
108 | | - def hide(self, event=None): |
109 | | - self.is_open = False |
110 | | - self.window.withdraw() |
111 | | - self.master.unbind_all("<Button-1>") |
112 | | - |
113 | | - def _on_click_elsewhere(self, event): |
114 | | - if self.window.winfo_containing(event.x_root, event.y_root) is None: |
115 | | - if self.button.winfo_containing(event.x_root, event.y_root) is None: |
116 | | - self.hide() |
117 | | - |
118 | | - def update_options(self, options): |
119 | | - for widget in self.scrollable_frame.winfo_children(): |
120 | | - widget.destroy() |
121 | | - |
122 | | - self.options = options |
123 | | - for option in self.options: |
124 | | - btn = ttk.Button(self.scrollable_frame, text=option, |
125 | | - command=lambda o=option: self.select(o), |
126 | | - style=f"{self.style_prefix}.Item.TButton") |
127 | | - btn.pack(fill=tk.X, expand=True) |
128 | | - |
129 | | - def select(self, option): |
130 | | - self.textvariable.set(option) |
131 | | - self.hide() |
0 commit comments