From 196738fc55b61bd9c3148ac32c4fb38f13f743de Mon Sep 17 00:00:00 2001 From: RiceCrispy Date: Thu, 17 Apr 2025 17:29:58 +0000 Subject: [PATCH 1/3] Issue #2712 where the user couldn't navigate the checkboxes using tab and fill with space as tkinter's checkbutton --- customtkinter/windows/widgets/ctk_checkbox.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/customtkinter/windows/widgets/ctk_checkbox.py b/customtkinter/windows/widgets/ctk_checkbox.py index 42f04f5e..da0b4d68 100644 --- a/customtkinter/windows/widgets/ctk_checkbox.py +++ b/customtkinter/windows/widgets/ctk_checkbox.py @@ -101,6 +101,8 @@ def __init__(self, width=self._apply_widget_scaling(self._checkbox_width), height=self._apply_widget_scaling(self._checkbox_height)) self._canvas.grid(row=0, column=0, sticky="e") + self._canvas.configure(takefocus=True) + self._draw_engine = DrawEngine(self._canvas) self._text_label = tkinter.Label(master=self, @@ -118,10 +120,25 @@ def __init__(self, if self._variable is not None and self._variable != "": self._variable_callback_name = self._variable.trace_add("write", self._variable_callback) self._check_state = True if self._variable.get() == self._onvalue else False + + self.bind("", lambda e: self._highlight(True)) + self.bind("", lambda e: self._highlight(False)) self._create_bindings() self._set_cursor() self._draw() + + def _highlight(self, is_active): + if is_active: + # Pull original themed values to restore them + themed_border = ThemeManager.theme["CTkButton"]["fg_color"] # mimic the CTkButton theme fg_color + themed_text = ThemeManager.theme["CTkButton"]["fg_color"] # mimic the CTkButton theme fg_color + self.configure(border_color=themed_border, border_width=2, text_color=themed_text) + else: + # Pull original themed values to restore them + themed_border = ThemeManager.theme["CTkCheckBox"]["border_color"] # return normal CTkCheckBox colors + themed_text = ThemeManager.theme["CTkCheckBox"]["text_color"] # return normal CTkCheckBox colors + self.configure(border_color=themed_border, border_width=2, text_color=themed_text) def _create_bindings(self, sequence: Optional[str] = None): """ set necessary bindings for functionality of widget, will overwrite other bindings """ @@ -134,6 +151,9 @@ def _create_bindings(self, sequence: Optional[str] = None): if sequence is None or sequence == "": self._canvas.bind("", self.toggle) self._text_label.bind("", self.toggle) + if sequence is None or sequence == "": + self._canvas.bind("", self.toggle) + self._text_label.bind("", self.toggle) def _set_scaling(self, *args, **kwargs): super()._set_scaling(*args, **kwargs) From 74524f6c3c02f89bf5bee56ea1f18fc0b33341d4 Mon Sep 17 00:00:00 2001 From: RiceCrispy Date: Thu, 17 Apr 2025 18:00:27 +0000 Subject: [PATCH 2/3] changed comment of text being changed and checbox color being changed from original to theme --- customtkinter/windows/widgets/ctk_checkbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customtkinter/windows/widgets/ctk_checkbox.py b/customtkinter/windows/widgets/ctk_checkbox.py index da0b4d68..905dfbd7 100644 --- a/customtkinter/windows/widgets/ctk_checkbox.py +++ b/customtkinter/windows/widgets/ctk_checkbox.py @@ -130,7 +130,7 @@ def __init__(self, def _highlight(self, is_active): if is_active: - # Pull original themed values to restore them + # Changes colors of text and border to be the theme the user chooses. themed_border = ThemeManager.theme["CTkButton"]["fg_color"] # mimic the CTkButton theme fg_color themed_text = ThemeManager.theme["CTkButton"]["fg_color"] # mimic the CTkButton theme fg_color self.configure(border_color=themed_border, border_width=2, text_color=themed_text) From 50b3d49c7f2a379325af9e26b46a37208cab4fe1 Mon Sep 17 00:00:00 2001 From: RiceCrispy Date: Fri, 2 Jan 2026 09:43:03 -0500 Subject: [PATCH 3/3] Introduces generic keyboard focus handling in CTkBaseClass The base class only tracks focus state and dispatches focus changes. Visual focus indication is implemented per widget using theme-defined values. --- .../core_widget_classes/ctk_base_class.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py b/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py index afd9431d..0c32677d 100644 --- a/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py +++ b/customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py @@ -70,6 +70,11 @@ class GeometryCallDict(TypedDict): # add configure callback to tkinter.Frame super().bind('', self._update_dimensions_event) + # focus handling + self._has_focus: bool = False + self._focus_enabled: bool = True + self._enable_focus_handling() + # overwrite configure methods of master when master is tkinter widget, so that bg changes get applied on child CTk widget as well if isinstance(self.master, (tkinter.Tk, tkinter.Toplevel, tkinter.Frame, tkinter.LabelFrame, ttk.Frame, ttk.LabelFrame, ttk.Notebook)) and not isinstance(self.master, (CTkBaseClass, CTkAppearanceModeBaseClass)): master_old_configure = self.master.config @@ -90,6 +95,37 @@ def new_configure(*args, **kwargs): self.master.config = new_configure self.master.configure = new_configure + + def _enable_focus_handling(self): + """Enable keyboard focus handling (Tab navigation).""" + + if not self._focus_enabled: + return + + # allow widget to receive focus via Tab + try: + super().configure(takefocus=True) + except Exception: + pass + + # bind focus events + super().bind("", self._on_focus_in, add="+") + super().bind("", self._on_focus_out, add="+") + + def _on_focus_in(self, event=None): + self._has_focus = True + self._apply_focus_state() + + def _on_focus_out(self, event=None): + self._has_focus = False + self._apply_focus_state() + + def _apply_focus_state(self): + """ + Called when focus state changes. + Subclasses override this to apply visual focus indication. + """ + pass def destroy(self): """ Destroy this and all descendants widgets. """