scustomtkinter 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scustomtkinter/__init__.py +81 -0
- scustomtkinter/assets/images/error.png +0 -0
- scustomtkinter/assets/images/info.png +0 -0
- scustomtkinter/assets/images/warning.png +0 -0
- scustomtkinter/assets/sCTkThemes.json +1963 -0
- scustomtkinter/license_morecustomtkinterwidgets.txt +22 -0
- scustomtkinter/sctk_button_primary.py +366 -0
- scustomtkinter/sctk_button_secondary.py +354 -0
- scustomtkinter/sctk_button_tertiary.py +375 -0
- scustomtkinter/sctk_checkbox.py +270 -0
- scustomtkinter/sctk_combobox.py +155 -0
- scustomtkinter/sctk_core.py +15 -0
- scustomtkinter/sctk_dial.py +1472 -0
- scustomtkinter/sctk_dialog.py +701 -0
- scustomtkinter/sctk_dialog_toplevel.py +268 -0
- scustomtkinter/sctk_entry_primary.py +400 -0
- scustomtkinter/sctk_entry_secondary.py +342 -0
- scustomtkinter/sctk_file_explorer.py +879 -0
- scustomtkinter/sctk_frame.py +210 -0
- scustomtkinter/sctk_frame_labeled_primary.py +667 -0
- scustomtkinter/sctk_frame_labeled_secondary.py +598 -0
- scustomtkinter/sctk_frequency_display.py +636 -0
- scustomtkinter/sctk_label_primary.py +283 -0
- scustomtkinter/sctk_label_secondary.py +275 -0
- scustomtkinter/sctk_label_tertiary.py +278 -0
- scustomtkinter/sctk_messagebox.py +275 -0
- scustomtkinter/sctk_notebook.py +1103 -0
- scustomtkinter/sctk_optionmenu_border_mixin.py +203 -0
- scustomtkinter/sctk_optionmenu_primary.py +378 -0
- scustomtkinter/sctk_optionmenu_secondary.py +346 -0
- scustomtkinter/sctk_path_chooser.py +516 -0
- scustomtkinter/sctk_progress_bar.py +274 -0
- scustomtkinter/sctk_radiobutton.py +360 -0
- scustomtkinter/sctk_scroll_mixin.py +546 -0
- scustomtkinter/sctk_scrollable_frame.py +795 -0
- scustomtkinter/sctk_scrollbar.py +313 -0
- scustomtkinter/sctk_segmentedbutton.py +424 -0
- scustomtkinter/sctk_selector.py +581 -0
- scustomtkinter/sctk_separator.py +308 -0
- scustomtkinter/sctk_slider.py +264 -0
- scustomtkinter/sctk_smeter.py +405 -0
- scustomtkinter/sctk_smeter_bar.py +557 -0
- scustomtkinter/sctk_spinbox.py +664 -0
- scustomtkinter/sctk_switch.py +463 -0
- scustomtkinter/sctk_tableview.py +869 -0
- scustomtkinter/sctk_tabview.py +409 -0
- scustomtkinter/sctk_textbox_primary.py +291 -0
- scustomtkinter/sctk_textbox_secondary.py +225 -0
- scustomtkinter/sctk_toplevel.py +153 -0
- scustomtkinter/themeable_widget.py +938 -0
- scustomtkinter-0.2.1.dist-info/METADATA +336 -0
- scustomtkinter-0.2.1.dist-info/RECORD +101 -0
- scustomtkinter-0.2.1.dist-info/WHEEL +5 -0
- scustomtkinter-0.2.1.dist-info/entry_points.txt +2 -0
- scustomtkinter-0.2.1.dist-info/licenses/LICENSE +21 -0
- scustomtkinter-0.2.1.dist-info/licenses/scustomtkinter/license_morecustomtkinterwidgets.txt +22 -0
- scustomtkinter-0.2.1.dist-info/top_level.txt +2 -0
- scustomtkinter_pygubu/__init__.py +1 -0
- scustomtkinter_pygubu/designer/__init__.py +0 -0
- scustomtkinter_pygubu/designer/plugin.py +1854 -0
- scustomtkinter_pygubu/designer/properties.py +247 -0
- scustomtkinter_pygubu/sCTkButtonPrimarybo.py +46 -0
- scustomtkinter_pygubu/sCTkButtonSecondarybo.py +45 -0
- scustomtkinter_pygubu/sCTkButtonTertiarybo.py +44 -0
- scustomtkinter_pygubu/sCTkCheckBoxbo.py +45 -0
- scustomtkinter_pygubu/sCTkComboBoxbo.py +45 -0
- scustomtkinter_pygubu/sCTkCorebo.py +120 -0
- scustomtkinter_pygubu/sCTkDialbo.py +371 -0
- scustomtkinter_pygubu/sCTkDialogbo.py +397 -0
- scustomtkinter_pygubu/sCTkEntryPrimarybo.py +100 -0
- scustomtkinter_pygubu/sCTkEntrySecondarybo.py +98 -0
- scustomtkinter_pygubu/sCTkFileExplorerbo.py +189 -0
- scustomtkinter_pygubu/sCTkFrameLabeledPrimarybo.py +69 -0
- scustomtkinter_pygubu/sCTkFrameLabeledSecondarybo.py +71 -0
- scustomtkinter_pygubu/sCTkFramebo.py +46 -0
- scustomtkinter_pygubu/sCTkFrequencyDisplaybo.py +175 -0
- scustomtkinter_pygubu/sCTkLabelPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkLabelSecondarybo.py +44 -0
- scustomtkinter_pygubu/sCTkLabelTertiarybo.py +45 -0
- scustomtkinter_pygubu/sCTkNotebookbo.py +392 -0
- scustomtkinter_pygubu/sCTkOptionMenuPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkOptionMenuSecondarybo.py +39 -0
- scustomtkinter_pygubu/sCTkPathChooserbo.py +204 -0
- scustomtkinter_pygubu/sCTkProgressBarbo.py +45 -0
- scustomtkinter_pygubu/sCTkRadioButtonbo.py +45 -0
- scustomtkinter_pygubu/sCTkSMeterBarbo.py +153 -0
- scustomtkinter_pygubu/sCTkSMeterbo.py +101 -0
- scustomtkinter_pygubu/sCTkScrollableFramebo.py +66 -0
- scustomtkinter_pygubu/sCTkScrollbarbo.py +48 -0
- scustomtkinter_pygubu/sCTkSegmentedButtonbo.py +47 -0
- scustomtkinter_pygubu/sCTkSelectorbo.py +167 -0
- scustomtkinter_pygubu/sCTkSeparatorbo.py +141 -0
- scustomtkinter_pygubu/sCTkSliderbo.py +45 -0
- scustomtkinter_pygubu/sCTkSpinboxbo.py +200 -0
- scustomtkinter_pygubu/sCTkSwitchbo.py +45 -0
- scustomtkinter_pygubu/sCTkTableviewbo.py +188 -0
- scustomtkinter_pygubu/sCTkTabviewbo.py +226 -0
- scustomtkinter_pygubu/sCTkTextboxPrimarybo.py +45 -0
- scustomtkinter_pygubu/sCTkTextboxSecondarybo.py +45 -0
- scustomtkinter_pygubu/sCTkToplevelbo.py +55 -0
- scustomtkinter_pygubu/sCTkWidgetSetForPygubuDesigner.py +141 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""
|
|
3
|
+
sCTkScrollbar
|
|
4
|
+
|
|
5
|
+
An advanced, theme-compliant custom scrollbar component and native container.
|
|
6
|
+
Inherits cleanly from ctk.CTkScrollbar to preserve native macOS high-precision
|
|
7
|
+
touchpad event streams and keep global light/dark theme tracking active.
|
|
8
|
+
"""
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
|
|
11
|
+
import customtkinter as ctk
|
|
12
|
+
from .themeable_widget import ThemeableWidget
|
|
13
|
+
from .sctk_scroll_mixin import ScrollBindingMixin
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class sCTkScrollbar(ctk.CTkScrollbar, ThemeableWidget):
|
|
17
|
+
# Required at the TOP LEVEL of the theme block. There is no disabled_map:
|
|
18
|
+
# CustomTkinter's scrollbar has no disabled state to lock, and containers
|
|
19
|
+
# that need an inert bar block dragging at the binding level instead --
|
|
20
|
+
# see ScrollBindingMixin._set_scroll_drag_blocked().
|
|
21
|
+
_REQUIRED_THEME_KEYS = ("button_color", "button_hover_color")
|
|
22
|
+
|
|
23
|
+
def __init__(self, master=None, **kwargs):
|
|
24
|
+
# 1. Run shared mixin logic first to parse master themes.json safely
|
|
25
|
+
ThemeableWidget.__init__(self, kwargs)
|
|
26
|
+
self._local_defaults = dict(self.final_kw)
|
|
27
|
+
self._validate_theme_keys()
|
|
28
|
+
|
|
29
|
+
# 2. Track orientation layout constraints cleanly.
|
|
30
|
+
#
|
|
31
|
+
# FIX: this previously read kwargs.get("orientation", ...) -- the RAW
|
|
32
|
+
# constructor dict, after ThemeableWidget.__init__ had already
|
|
33
|
+
# processed it. Reading the resolved final_kw instead means the value
|
|
34
|
+
# is picked up whether it came from the constructor or the theme
|
|
35
|
+
# block, and it cannot be affected by anything the mixin does to the
|
|
36
|
+
# incoming dict. str() guards against a non-string value reaching
|
|
37
|
+
# .lower().
|
|
38
|
+
orientation = str(self.final_kw.get("orientation", "vertical")).lower()
|
|
39
|
+
self._is_horizontal = orientation == "horizontal"
|
|
40
|
+
|
|
41
|
+
# 3. Securely set default sizes based on orientation layout rules
|
|
42
|
+
if self._is_horizontal:
|
|
43
|
+
self.final_kw.setdefault("height", 14)
|
|
44
|
+
else:
|
|
45
|
+
self.final_kw.setdefault("width", 14)
|
|
46
|
+
|
|
47
|
+
# 4. Initialize CustomTkinter natively so Apple Magic Mouse / trackpad streams stay intact
|
|
48
|
+
super().__init__(master, **self.final_kw)
|
|
49
|
+
self._apply_custom_theme_colors()
|
|
50
|
+
self._finalize_themeable_lifecycle()
|
|
51
|
+
|
|
52
|
+
def _validate_theme_keys(self) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Hard-fails at construction on an incomplete theme block, naming the
|
|
55
|
+
missing key. Replaces the hardcoded fallbacks previously used in
|
|
56
|
+
_apply_custom_theme_colors(), which silently substituted a plausible
|
|
57
|
+
colour and made an incomplete block look merely slightly-off rather
|
|
58
|
+
than broken.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
KeyError: naming the first missing key found.
|
|
62
|
+
"""
|
|
63
|
+
name = self.__class__.__name__
|
|
64
|
+
for key in self._REQUIRED_THEME_KEYS:
|
|
65
|
+
if self._local_defaults.get(key) is None:
|
|
66
|
+
raise KeyError(
|
|
67
|
+
f"'{name}' theme block is missing '{key}' at the top level "
|
|
68
|
+
f"of sCTkThemes.json."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def _apply_custom_theme_colors(self):
|
|
72
|
+
"""Cascades color profiles directly out of your centralized stylesheet json maps."""
|
|
73
|
+
# No fallbacks: validated at construction, so both lookups resolve.
|
|
74
|
+
normal_color = self._local_defaults.get("button_color")
|
|
75
|
+
normal_hover = self._local_defaults.get("button_hover_color")
|
|
76
|
+
|
|
77
|
+
super().configure(
|
|
78
|
+
button_color=tuple(normal_color) if isinstance(normal_color, list) else normal_color,
|
|
79
|
+
button_hover_color=tuple(normal_hover) if isinstance(normal_hover, list) else normal_hover
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Theme keys that _apply_custom_theme_colors() re-pushes on every repaint.
|
|
83
|
+
# configure() records these into _local_defaults so a runtime override
|
|
84
|
+
# survives the repaint instead of being reverted by it.
|
|
85
|
+
_THEME_TRACKED_KEYS = frozenset({"button_color", "button_hover_color"})
|
|
86
|
+
|
|
87
|
+
def configure(self, *args, **kwargs):
|
|
88
|
+
"""
|
|
89
|
+
Processes standard configuration queries and manages visual refreshes.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
*args: At most one positional argument is meaningful:
|
|
93
|
+
- a dict: merged into kwargs and processed normally.
|
|
94
|
+
- a tracked theme key name: returns a Tkinter-style
|
|
95
|
+
(name, name, name, default, current) query tuple.
|
|
96
|
+
- anything else: forwarded to the native widget.
|
|
97
|
+
**kwargs: Any native CTkScrollbar option.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The query tuple described above for the single-argument case,
|
|
101
|
+
otherwise None.
|
|
102
|
+
"""
|
|
103
|
+
# FIX: an earlier version tested `if args and isinstance(args, dict)`.
|
|
104
|
+
# args is ALWAYS a tuple, so that was never true -- the dict-merge
|
|
105
|
+
# branch was dead code, and there was no query branch at all, so
|
|
106
|
+
# configure("button_color") silently returned None instead of a
|
|
107
|
+
# property tuple. Same tautology fixed across the batch-one widgets.
|
|
108
|
+
if len(args) == 1:
|
|
109
|
+
if isinstance(args[0], dict):
|
|
110
|
+
kwargs = {**args[0], **kwargs}
|
|
111
|
+
else:
|
|
112
|
+
pname = args[0]
|
|
113
|
+
if pname in self._THEME_TRACKED_KEYS:
|
|
114
|
+
# The DEFAULT comes from the untouched theme, the CURRENT
|
|
115
|
+
# from the live map. Both slots used to read
|
|
116
|
+
# _local_defaults, which configure() writes overrides into
|
|
117
|
+
# -- so clearing a colour in the Designer reported the
|
|
118
|
+
# override as its own default and put it straight back.
|
|
119
|
+
return (pname, pname, pname,
|
|
120
|
+
self._query_value(self._theme_default(pname)),
|
|
121
|
+
self._query_value(self._local_defaults.get(pname)))
|
|
122
|
+
return self._configure_query(pname)
|
|
123
|
+
|
|
124
|
+
# FIX: record theme overrides BEFORE the repaint below.
|
|
125
|
+
#
|
|
126
|
+
# _apply_custom_theme_colors() re-pushes button_color and
|
|
127
|
+
# button_hover_color from self._local_defaults on every call. Since
|
|
128
|
+
# configure() never wrote to that dict, the sequence was:
|
|
129
|
+
# super().configure(button_color="red") applied red, then the repaint
|
|
130
|
+
# on the very next line overwrote it with the theme value -- so
|
|
131
|
+
# runtime color overrides silently did nothing.
|
|
132
|
+
#
|
|
133
|
+
# The repaint isn't wrong to exist (the _set_appearance_mode hook
|
|
134
|
+
# needs it); it was wrong to treat _local_defaults as authoritative
|
|
135
|
+
# when configure() had just introduced a newer value it never
|
|
136
|
+
# recorded. Writing the override in first makes the repaint reproduce
|
|
137
|
+
# it rather than revert it, and makes it survive later appearance-mode
|
|
138
|
+
# changes -- matching CustomTkinter's own semantics, where
|
|
139
|
+
# configure(button_color=...) sticks.
|
|
140
|
+
# Through the shared helper now rather than a local loop. This widget
|
|
141
|
+
# was where the pattern started; ThemeableWidget._record_theme_overrides()
|
|
142
|
+
# generalised it and adds two things this loop lacked -- it normalises
|
|
143
|
+
# a value from the Designer into the (light, dark) shape the theme maps
|
|
144
|
+
# hold, and it snapshots the theme before the first override so the
|
|
145
|
+
# query above has a real default to report.
|
|
146
|
+
self._record_theme_overrides(kwargs)
|
|
147
|
+
|
|
148
|
+
if kwargs:
|
|
149
|
+
super().configure(**kwargs)
|
|
150
|
+
self._apply_custom_theme_colors()
|
|
151
|
+
|
|
152
|
+
config = configure
|
|
153
|
+
|
|
154
|
+
def _set_appearance_mode(self, mode_string: str):
|
|
155
|
+
if hasattr(super(), "_set_appearance_mode"):
|
|
156
|
+
try:
|
|
157
|
+
super()._set_appearance_mode(mode_string)
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
self._apply_custom_theme_colors()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class sCTkScrollArea(ctk.CTkFrame, ScrollBindingMixin):
|
|
164
|
+
"""
|
|
165
|
+
An unblocked scrollable container frame supporting high-precision Apple momentum streams.
|
|
166
|
+
Bypasses rigid layout masks to give developers full opt-in control over child event bundling.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(self, master=None, **kwargs):
|
|
170
|
+
super().__init__(master, fg_color="transparent", border_width=0, **kwargs)
|
|
171
|
+
|
|
172
|
+
current_mode = str(ctk.get_appearance_mode()).lower()
|
|
173
|
+
initial_bg = "#FAFAFA" if current_mode == "light" else "#1A1A1A"
|
|
174
|
+
|
|
175
|
+
self.canvas = tk.Canvas(self, highlightthickness=0, bd=0, bg=initial_bg)
|
|
176
|
+
self.canvas.pack(side="left", fill="both", expand=True)
|
|
177
|
+
|
|
178
|
+
self.scroll_content = tk.Frame(self.canvas, bd=0, bg=initial_bg)
|
|
179
|
+
self.canvas_window = self.canvas.create_window((0, 0), window=self.scroll_content, anchor="nw")
|
|
180
|
+
|
|
181
|
+
# Widgets registered through propagate_scroll_events() that aren't
|
|
182
|
+
# descendants of scroll_content -- see that method.
|
|
183
|
+
self._extra_scroll_targets = []
|
|
184
|
+
|
|
185
|
+
# A scrollbar attached via hook_scrollbar(), so the wheel keeps
|
|
186
|
+
# working while the pointer is over the bar itself.
|
|
187
|
+
self._hooked_scrollbar = None
|
|
188
|
+
|
|
189
|
+
# Scroll handling now comes from ScrollBindingMixin, the library's
|
|
190
|
+
# single shared implementation. This class previously carried its own
|
|
191
|
+
# copy, which had drifted from the canonical version in three ways,
|
|
192
|
+
# all corrected by this change:
|
|
193
|
+
#
|
|
194
|
+
# - Two's-complement sign correction used `> 32768` rather than
|
|
195
|
+
# `>= 0x8000`. Those differ at exactly 32768, which is the
|
|
196
|
+
# smallest NEGATIVE value in a signed 16-bit field: the old form
|
|
197
|
+
# read it as +32768, inverting scroll direction at that value.
|
|
198
|
+
# - Windows wheel scaling doubled the delta (`/120 * 2`), scrolling
|
|
199
|
+
# twice as far per notch as everywhere else in the library.
|
|
200
|
+
# - The packed touchpad delta was decoded by reading event.delta_y
|
|
201
|
+
# when present and otherwise applying a 16-bit correction to what
|
|
202
|
+
# may be a 32-bit packed value, instead of bit-shifting out the
|
|
203
|
+
# signed 16-bit X and Y components.
|
|
204
|
+
#
|
|
205
|
+
# It also gains the nested-scrollable boundary guard, blocking
|
|
206
|
+
# handlers when scrolling is switched off, and the debounced
|
|
207
|
+
# content rebind.
|
|
208
|
+
self._init_scroll_state()
|
|
209
|
+
self._install_scroll_activation(content_widget=self.scroll_content)
|
|
210
|
+
|
|
211
|
+
self.scroll_content.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
|
|
212
|
+
self.canvas.bind("<Configure>",
|
|
213
|
+
lambda e: self.canvas.itemconfig(self.canvas_window, width=self.canvas.winfo_width()))
|
|
214
|
+
|
|
215
|
+
# ------------------------------------------------------------------
|
|
216
|
+
# ScrollBindingMixin contract
|
|
217
|
+
# ------------------------------------------------------------------
|
|
218
|
+
def _scroll_target(self):
|
|
219
|
+
"""This area builds its own canvas, so no parent lookup is needed."""
|
|
220
|
+
canvas = getattr(self, "canvas", None)
|
|
221
|
+
try:
|
|
222
|
+
if canvas is not None and canvas.winfo_exists():
|
|
223
|
+
return canvas
|
|
224
|
+
except Exception:
|
|
225
|
+
pass
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
def _scroll_layers(self):
|
|
229
|
+
"""
|
|
230
|
+
The area itself, its canvas, any hooked scrollbar, the full content
|
|
231
|
+
tree, and anything explicitly registered via propagate_scroll_events().
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
An ordered, deduplicated list of widgets.
|
|
235
|
+
"""
|
|
236
|
+
layers = [self]
|
|
237
|
+
|
|
238
|
+
canvas = self._scroll_target()
|
|
239
|
+
if canvas is not None:
|
|
240
|
+
layers.append(canvas)
|
|
241
|
+
|
|
242
|
+
if self._hooked_scrollbar is not None:
|
|
243
|
+
self._collect_scroll_descendants(self._hooked_scrollbar, layers)
|
|
244
|
+
|
|
245
|
+
content = getattr(self, "scroll_content", None)
|
|
246
|
+
if content is not None:
|
|
247
|
+
try:
|
|
248
|
+
if content.winfo_exists():
|
|
249
|
+
self._collect_scroll_descendants(content, layers)
|
|
250
|
+
except Exception:
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
for widget in list(self._extra_scroll_targets):
|
|
254
|
+
try:
|
|
255
|
+
if widget.winfo_exists():
|
|
256
|
+
self._collect_scroll_descendants(widget, layers)
|
|
257
|
+
else:
|
|
258
|
+
self._extra_scroll_targets.remove(widget)
|
|
259
|
+
except Exception:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
return layers
|
|
263
|
+
|
|
264
|
+
def hook_scrollbar(self, scrollbar_widget):
|
|
265
|
+
"""
|
|
266
|
+
Connects a scrollbar to this area's canvas in both directions.
|
|
267
|
+
|
|
268
|
+
The scrollbar is also recorded as a scroll layer, so the wheel keeps
|
|
269
|
+
working while the pointer is over the bar itself rather than dying
|
|
270
|
+
the moment it leaves the content.
|
|
271
|
+
"""
|
|
272
|
+
self.canvas.configure(yscrollcommand=scrollbar_widget.set)
|
|
273
|
+
scrollbar_widget.configure(command=self.canvas.yview)
|
|
274
|
+
self._hooked_scrollbar = scrollbar_widget
|
|
275
|
+
self._activate_scroll_bindings()
|
|
276
|
+
|
|
277
|
+
def propagate_scroll_events(self, target_widget: tk.Widget):
|
|
278
|
+
"""
|
|
279
|
+
Registers a widget outside this area's own content tree to receive
|
|
280
|
+
scroll events, along with its descendants.
|
|
281
|
+
|
|
282
|
+
RARELY NEEDED NOW. Content placed inside scroll_content is bound
|
|
283
|
+
automatically, and re-bound whenever it changes, so this is only for
|
|
284
|
+
widgets that sit outside that tree. Registered widgets are remembered
|
|
285
|
+
and re-bound on every subsequent pass, rather than bound once and
|
|
286
|
+
forgotten as the previous implementation did.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
target_widget: The widget to register. Its descendants are
|
|
290
|
+
included automatically.
|
|
291
|
+
"""
|
|
292
|
+
if target_widget not in self._extra_scroll_targets:
|
|
293
|
+
self._extra_scroll_targets.append(target_widget)
|
|
294
|
+
self._activate_scroll_bindings()
|
|
295
|
+
|
|
296
|
+
def process_incoming_scroll(self, event):
|
|
297
|
+
"""
|
|
298
|
+
COMPATIBILITY SHIM. Scroll events are dispatched by
|
|
299
|
+
ScrollBindingMixin directly; this remains only for external callers
|
|
300
|
+
that bound this method themselves before the consolidation.
|
|
301
|
+
|
|
302
|
+
Delegates to the shared wheel handler. Trackpad <TouchpadScroll>
|
|
303
|
+
events are routed to the mixin's own accumulator-gated handler by its
|
|
304
|
+
bindings, so they do not pass through here.
|
|
305
|
+
"""
|
|
306
|
+
return self._process_scroll_wheel(event)
|
|
307
|
+
|
|
308
|
+
def _set_appearance_mode(self, mode_string: str):
|
|
309
|
+
if hasattr(super(), "_set_appearance_mode"):
|
|
310
|
+
super()._set_appearance_mode(mode_string)
|
|
311
|
+
target_bg = "#FAFAFA" if str(mode_string).lower() == "light" else "#1A1A1A"
|
|
312
|
+
if hasattr(self, "canvas") and self.canvas: self.canvas.configure(bg=target_bg)
|
|
313
|
+
if hasattr(self, "scroll_content") and self.scroll_content: self.scroll_content.configure(bg=target_bg)
|