From 9d618386e1d3f0c00a9006f70f7363ca6d979245 Mon Sep 17 00:00:00 2001 From: Tom Schimansky Date: Fri, 27 May 2022 01:09:54 +0200 Subject: [PATCH] fixed scaling for DropdownMenu --- customtkinter/draw_engine.py | 35 ++++++++++++++++++ customtkinter/widgets/ctk_optionmenu.py | 11 ++++-- customtkinter/widgets/dropdown_menu.py | 47 ++++++++++++++++++++----- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/customtkinter/draw_engine.py b/customtkinter/draw_engine.py index 8d062d2..e0274f9 100644 --- a/customtkinter/draw_engine.py +++ b/customtkinter/draw_engine.py @@ -21,6 +21,7 @@ class DrawEngine: - draw_rounded_progress_bar_with_border() - draw_rounded_slider_with_border_and_button() - draw_checkmark() + - draw_dropdown_arrow() """ @@ -986,3 +987,37 @@ class DrawEngine: self._canvas.coords("checkmark", round(width / 2), round(height / 2)) return requires_recoloring + + def draw_dropdown_arrow(self, x_position: Union[int, float], y_position: Union[int, float], size: Union[int, float]) -> bool: + """ Draws a dropdown bottom facing arrow at (x_position, y_position) in a given size + + returns bool if recoloring is necessary """ + + x_position, y_position, size = round(x_position), round(y_position), round(size) + requires_recoloring = False + + if self.preferred_drawing_method == "polygon_shapes" or self.preferred_drawing_method == "circle_shapes": + if not self._canvas.find_withtag("dropdown_arrow"): + self._canvas.create_line(0, 0, 0, 0, tags=("dropdown_arrow"), width=round(size / 4), joinstyle=tkinter.MITER, capstyle=tkinter.ROUND) + self._canvas.tag_raise("dropdown_arrow") + requires_recoloring = True + + self._canvas.coords("dropdown_arrow", + x_position - (size / 2), + y_position - (size / 5), + x_position, + y_position + (size / 5), + x_position + (size / 2), + y_position - (size / 5)) + + elif self.preferred_drawing_method == "font_shapes": + return False + + if not self._canvas.find_withtag("checkmark"): + self._canvas.create_text(0, 0, text="Z", font=("CustomTkinter_shapes_font", -size), tags=("checkmark", "create_text"), anchor=tkinter.CENTER) + self._canvas.tag_raise("checkmark") + requires_recoloring = True + + self._canvas.coords("checkmark", round(width / 2), round(height / 2)) + + return requires_recoloring diff --git a/customtkinter/widgets/ctk_optionmenu.py b/customtkinter/widgets/ctk_optionmenu.py index 922f27d..1f71982 100644 --- a/customtkinter/widgets/ctk_optionmenu.py +++ b/customtkinter/widgets/ctk_optionmenu.py @@ -120,6 +120,9 @@ class CTkOptionMenu(CTkBaseClass): 0, self.apply_widget_scaling(left_section_width)) + requires_recoloring_2 = self.draw_engine.draw_dropdown_arrow(self.apply_widget_scaling(self.current_width - (self.current_height / 2)), + self.apply_widget_scaling(self.current_height / 2), + self.apply_widget_scaling(self.current_height / 3)) if self.text_label is None: self.text_label = tkinter.Label(master=self, font=self.apply_font_scaling(self.text_font)) @@ -135,7 +138,7 @@ class CTkOptionMenu(CTkBaseClass): if self.current_value is not None: self.text_label.configure(text=self.current_value) - if no_color_updates is False or requires_recoloring: + if no_color_updates is False or requires_recoloring or requires_recoloring_2: self.canvas.configure(bg=ThemeManager.single_color(self.bg_color, self.appearance_mode)) @@ -150,14 +153,18 @@ class CTkOptionMenu(CTkBaseClass): if self.state == tkinter.DISABLED: self.text_label.configure(fg=(ThemeManager.single_color(self.text_color_disabled, self.appearance_mode))) + self.canvas.itemconfig("dropdown_arrow", + fill=ThemeManager.single_color(self.text_color_disabled, self.appearance_mode)) else: self.text_label.configure(fg=ThemeManager.single_color(self.text_color, self.appearance_mode)) + self.canvas.itemconfig("dropdown_arrow", + fill=ThemeManager.single_color(self.text_color, self.appearance_mode)) self.text_label.configure(bg=ThemeManager.single_color(self.fg_color, self.appearance_mode)) def open_dropdown_menu(self): self.dropdown_menu = DropdownMenu(x_position=self.winfo_rootx(), - y_position=self.winfo_rooty() + self.current_height + 4, + y_position=self.winfo_rooty() + self.apply_widget_scaling(self.current_height + 4), width=self.current_width, values=self.values, command=self.set, diff --git a/customtkinter/widgets/dropdown_menu.py b/customtkinter/widgets/dropdown_menu.py index 74849e9..5c14ce6 100644 --- a/customtkinter/widgets/dropdown_menu.py +++ b/customtkinter/widgets/dropdown_menu.py @@ -4,6 +4,7 @@ import sys from ..theme_manager import ThemeManager from ..appearance_mode_tracker import AppearanceModeTracker +from ..scaling_tracker import ScalingTracker class DropdownMenu(tkinter.Toplevel): @@ -25,6 +26,9 @@ class DropdownMenu(tkinter.Toplevel): **kwargs): super().__init__(*args, **kwargs) + ScalingTracker.add_widget(self.set_scaling, self) + self.widget_scaling = ScalingTracker.get_widget_scaling(self) + self.values = values self.command = command @@ -41,7 +45,9 @@ class DropdownMenu(tkinter.Toplevel): self.button_corner_radius = button_corner_radius self.button_height = button_height - self.geometry(f"{round(self.width)}x{round(len(self.values) * (self.button_height + y_spacing) + y_spacing)}+{round(x_position)}+{round(y_position)}") + self.geometry(f"{round(self.apply_widget_scaling(self.width))}x" + + f"{round(self.apply_widget_scaling(len(self.values) * (self.button_height + y_spacing) + y_spacing))}+" + + f"{round(x_position)}+{round(y_position)}") self.grid_columnconfigure(0, weight=1) if sys.platform.startswith("darwin"): @@ -49,7 +55,10 @@ class DropdownMenu(tkinter.Toplevel): self.overrideredirect(False) self.wm_attributes("-transparent", True) # turn off window shadow self.config(bg='systemTransparent') # transparent bg - self.frame = customtkinter.CTkFrame(self, border_width=0, width=self.width, corner_radius=self.corner_radius, + self.frame = customtkinter.CTkFrame(self, + border_width=0, + width=self.width, + corner_radius=self.corner_radius, fg_color=ThemeManager.single_color(self.fg_color, self.appearance_mode)) elif sys.platform.startswith("win"): @@ -57,13 +66,19 @@ class DropdownMenu(tkinter.Toplevel): self.configure(bg="#010302") self.wm_attributes("-transparentcolor", "#010302") self.focus() - self.frame = customtkinter.CTkFrame(self, border_width=0, width=120, corner_radius=self.corner_radius, + self.frame = customtkinter.CTkFrame(self, + border_width=0, + width=self.width, + corner_radius=self.corner_radius, fg_color=self.fg_color, overwrite_preferred_drawing_method="circle_shapes") else: self.overrideredirect(True) # remove title-bar self.configure(bg="#010302") self.wm_attributes("-transparentcolor", "#010302") - self.frame = customtkinter.CTkFrame(self, border_width=0, width=120, corner_radius=self.corner_radius, + self.frame = customtkinter.CTkFrame(self, + border_width=0, + width=self.width, + corner_radius=self.corner_radius, fg_color=self.fg_color, overwrite_preferred_drawing_method="circle_shapes") self.frame.grid(row=0, column=0, sticky="nsew", rowspan=len(self.values) + 1) @@ -72,17 +87,33 @@ class DropdownMenu(tkinter.Toplevel): self.button_list = [] for index, option in enumerate(self.values): - button = customtkinter.CTkButton(self.frame, text=option, height=self.button_height, width=self.width - 2 * x_spacing, - fg_color=self.button_color, text_color=self.text_color, - hover_color=self.button_hover_color, corner_radius=self.button_corner_radius, + button = customtkinter.CTkButton(self.frame, + text=option, + height=self.button_height, + width=self.width - 2 * x_spacing, + fg_color=self.button_color, + text_color=self.text_color, + hover_color=self.button_hover_color, + corner_radius=self.button_corner_radius, command=lambda i=index: self.button_callback(i)) button.text_label.grid(row=0, column=0, rowspan=2, columnspan=2, sticky="w") - button.grid(row=index, column=0, padx=x_spacing, pady=(y_spacing, 0), sticky="ew") + button.grid(row=index, column=0, + padx=x_spacing, + pady=(y_spacing, 0), sticky="ew") self.button_list.append(button) self.bind("", self.focus_loss_event) self.frame.canvas.bind("", self.focus_loss_event) + def apply_widget_scaling(self, value): + if isinstance(value, (int, float)): + return value * self.widget_scaling + else: + return value + + def set_scaling(self, new_widget_scaling, new_spacing_scaling, new_window_scaling): + return + def focus_loss_event(self, event): self.destroy() if sys.platform.startswith("darwin"):