mirror of
https://github.com/TomSchimansky/CustomTkinter.git
synced 2023-08-10 21:13:13 +03:00
added CTkScrollbar
This commit is contained in:
parent
3b259e4d01
commit
79ecd2e946
@ -53,6 +53,7 @@ from .widgets.ctk_canvas import CTkCanvas
|
||||
from .widgets.ctk_switch import CTkSwitch
|
||||
from .widgets.ctk_optionmenu import CTkOptionMenu
|
||||
from .widgets.ctk_combobox import CTkComboBox
|
||||
from .widgets.ctk_scrollbar import CTkScrollbar
|
||||
|
||||
# import windows
|
||||
from .windows.ctk_tk import CTk
|
||||
|
@ -33,7 +33,9 @@
|
||||
"combobox_button_hover": ["#6E7174", "#7A848D"],
|
||||
"dropdown_color": ["gray90", "gray20"],
|
||||
"dropdown_hover": ["gray75", "gray28"],
|
||||
"dropdown_text": ["gray10", "#DCE4EE"]
|
||||
"dropdown_text": ["gray10", "#DCE4EE"],
|
||||
"scrollbar": ["gray75", "gray25"],
|
||||
"scrollbar_hover": ["gray50", "gray40"]
|
||||
},
|
||||
"text": {
|
||||
"macOS": {
|
||||
@ -70,6 +72,8 @@
|
||||
"switch_border_width": 3,
|
||||
"switch_corner_radius": 1000,
|
||||
"switch_button_corner_radius": 1000,
|
||||
"switch_button_length": 0
|
||||
"switch_button_length": 0,
|
||||
"scrollbar_corner_radius": 6,
|
||||
"scrollbar_border_spacing": 4
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ class DrawEngine:
|
||||
- draw_rounded_rect_with_border_vertical_split()
|
||||
- draw_rounded_progress_bar_with_border()
|
||||
- draw_rounded_slider_with_border_and_button()
|
||||
- draw_rounded_scrollbar()
|
||||
- draw_checkmark()
|
||||
- draw_dropdown_arrow()
|
||||
|
||||
@ -38,7 +39,7 @@ class DrawEngine:
|
||||
else:
|
||||
return round(user_corner_radius)
|
||||
|
||||
# optimize forx drawing with antialiased font shapes
|
||||
# optimize for drawing with antialiased font shapes
|
||||
elif self.preferred_drawing_method == "font_shapes":
|
||||
return round(user_corner_radius)
|
||||
|
||||
@ -490,22 +491,27 @@ class DrawEngine:
|
||||
# create canvas border corner parts if not already created, but only if needed, and delete if not needed
|
||||
if not self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_1_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("border_oval_1_a") and "border_oval_1" in exclude_parts:
|
||||
self._canvas.delete("border_oval_1_a", "border_oval_1_b")
|
||||
|
||||
if not self._canvas.find_withtag("border_oval_2_a") and width > 2 * corner_radius and "border_oval_2" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
|
||||
anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_2_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
|
||||
anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("border_oval_2_a") and (not width > 2 * corner_radius or "border_oval_2" in exclude_parts):
|
||||
self._canvas.delete("border_oval_2_a", "border_oval_2_b")
|
||||
|
||||
if not self._canvas.find_withtag("border_oval_3_a") and height > 2 * corner_radius \
|
||||
and width > 2 * corner_radius and "border_oval_3" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_a", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
|
||||
anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_3_b", "border_corner_part", "border_parts_right", "border_parts", "right_parts"),
|
||||
anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("border_oval_3_a") and (not (height > 2 * corner_radius
|
||||
and width > 2 * corner_radius) or "border_oval_3" in exclude_parts):
|
||||
@ -513,7 +519,8 @@ class DrawEngine:
|
||||
|
||||
if not self._canvas.find_withtag("border_oval_4_a") and height > 2 * corner_radius and "border_oval_4" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_a", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("border_oval_4_b", "border_corner_part", "border_parts_left", "border_parts", "left_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("border_oval_4_a") and (not height > 2 * corner_radius or "border_oval_4" in exclude_parts):
|
||||
self._canvas.delete("border_oval_4_a", "border_oval_4_b")
|
||||
@ -554,14 +561,16 @@ class DrawEngine:
|
||||
# create canvas border corner parts if not already created, but only if they're needed and delete if not needed
|
||||
if not self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_1_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_1_a") and "inner_oval_1" in exclude_parts:
|
||||
self._canvas.delete("inner_oval_1_a", "inner_oval_1_b")
|
||||
|
||||
if not self._canvas.find_withtag("inner_oval_2_a") and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_2" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part","inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_a", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_2_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_2_a") and (not width - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_2" in exclude_parts):
|
||||
self._canvas.delete("inner_oval_2_a", "inner_oval_2_b")
|
||||
@ -569,7 +578,8 @@ class DrawEngine:
|
||||
if not self._canvas.find_withtag("inner_oval_3_a") and height - (2 * border_width) > 2 * inner_corner_radius \
|
||||
and width - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_3" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_a", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_3_b", "inner_corner_part", "inner_parts_right", "inner_parts", "right_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_3_a") and (not (height - (2 * border_width) > 2 * inner_corner_radius
|
||||
and width - (2 * border_width) > 2 * inner_corner_radius) or "inner_oval_3" in exclude_parts):
|
||||
@ -577,7 +587,8 @@ class DrawEngine:
|
||||
|
||||
if not self._canvas.find_withtag("inner_oval_4_a") and height - (2 * border_width) > 2 * inner_corner_radius and "inner_oval_4" not in exclude_parts:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_a", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("inner_oval_4_b", "inner_corner_part", "inner_parts_left", "inner_parts", "left_parts"), anchor=tkinter.CENTER,
|
||||
angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("inner_oval_4_a") and (not height - (2 * border_width) > 2 * inner_corner_radius or "inner_oval_4" in exclude_parts):
|
||||
self._canvas.delete("inner_oval_4_a", "inner_oval_4_b")
|
||||
@ -959,6 +970,138 @@ class DrawEngine:
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_rounded_scrollbar(self, width: Union[float, int], height: Union[float, int], corner_radius: Union[float, int],
|
||||
border_spacing: Union[float, int], start_value: float, end_value: float, orientation: str) -> bool:
|
||||
width = math.floor(width / 2) * 2 # round _current_width and _current_height and restrict them to even values only
|
||||
height = math.floor(height / 2) * 2
|
||||
|
||||
if corner_radius > width / 2 or corner_radius > height / 2: # restrict corner_radius if it's too larger
|
||||
corner_radius = min(width / 2, height / 2)
|
||||
|
||||
border_spacing = round(border_spacing)
|
||||
corner_radius = self.__calc_optimal_corner_radius(corner_radius) # optimize corner_radius for different drawing methods (different rounding)
|
||||
|
||||
if corner_radius >= border_spacing:
|
||||
inner_corner_radius = corner_radius - border_spacing
|
||||
else:
|
||||
inner_corner_radius = 0
|
||||
|
||||
if self.preferred_drawing_method == "polygon_shapes" or self.preferred_drawing_method == "circle_shapes":
|
||||
return self.__draw_rounded_scrollbar_polygon_shapes(width, height, corner_radius, inner_corner_radius,
|
||||
start_value, end_value, orientation)
|
||||
elif self.preferred_drawing_method == "font_shapes":
|
||||
return self.__draw_rounded_scrollbar_font_shapes(width, height, corner_radius, inner_corner_radius,
|
||||
start_value, end_value, orientation)
|
||||
|
||||
def __draw_rounded_scrollbar_polygon_shapes(self, width: int, height: int, corner_radius: int, inner_corner_radius: int,
|
||||
start_value: float, end_value: float, orientation: str) -> bool:
|
||||
requires_recoloring = False
|
||||
|
||||
if not self._canvas.find_withtag("border_parts"):
|
||||
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_parts"), width=0)
|
||||
requires_recoloring = True
|
||||
self._canvas.coords("border_rectangle_1", 0, 0, width, height)
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_parts"):
|
||||
self._canvas.create_polygon((0, 0, 0, 0), tags=("scrollbar_polygon_1", "scrollbar_parts"), joinstyle=tkinter.ROUND)
|
||||
self._canvas.tag_raise("scrollbar_parts", "border_parts")
|
||||
requires_recoloring = True
|
||||
|
||||
if orientation == "vertical":
|
||||
self._canvas.coords("scrollbar_polygon_1",
|
||||
corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
|
||||
width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
|
||||
width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value,
|
||||
corner_radius, corner_radius + (height - 2 * corner_radius) * end_value)
|
||||
elif orientation == "horizontal":
|
||||
self._canvas.coords("scrollbar_polygon_1",
|
||||
corner_radius + (width - 2 * corner_radius) * start_value, corner_radius,
|
||||
corner_radius + (width - 2 * corner_radius) * end_value, corner_radius,
|
||||
corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius,
|
||||
corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius,)
|
||||
|
||||
self._canvas.itemconfig("scrollbar_polygon_1", width=inner_corner_radius * 2)
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def __draw_rounded_scrollbar_font_shapes(self, width: int, height: int, corner_radius: int, inner_corner_radius: int,
|
||||
start_value: float, end_value: float, orientation: str) -> bool:
|
||||
requires_recoloring = False
|
||||
|
||||
if not self._canvas.find_withtag("border_parts"):
|
||||
self._canvas.create_rectangle(0, 0, 0, 0, tags=("border_rectangle_1", "border_parts"), width=0)
|
||||
requires_recoloring = True
|
||||
self._canvas.coords("border_rectangle_1", 0, 0, width, height)
|
||||
|
||||
if inner_corner_radius > 0:
|
||||
if not self._canvas.find_withtag("scrollbar_oval_1_a"):
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_1_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_1_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_oval_2_a") and width > 2 * corner_radius:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_2_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_2_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("scrollbar_oval_2_a") and not width > 2 * corner_radius:
|
||||
self._canvas.delete("scrollbar_oval_2_a", "scrollbar_oval_2_b")
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_oval_3_a") and height > 2 * corner_radius and width > 2 * corner_radius:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_3_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_3_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("scrollbar_oval_3_a") and not (height > 2 * corner_radius and width > 2 * corner_radius):
|
||||
self._canvas.delete("scrollbar_oval_3_a", "scrollbar_oval_3_b")
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_oval_4_a") and height > 2 * corner_radius:
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_4_a", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER)
|
||||
self._canvas.create_aa_circle(0, 0, 0, tags=("scrollbar_oval_4_b", "scrollbar_corner_part", "scrollbar_parts"), anchor=tkinter.CENTER, angle=180)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("scrollbar_oval_4_a") and not height > 2 * corner_radius:
|
||||
self._canvas.delete("scrollbar_oval_4_a", "scrollbar_oval_4_b")
|
||||
else:
|
||||
self._canvas.delete("scrollbar_corner_part")
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_rectangle_1") and width > 2 * corner_radius:
|
||||
self._canvas.create_rectangle(0, 0, 0, 0, tags=("scrollbar_rectangle_1", "scrollbar_rectangle_part", "scrollbar_parts"), width=0)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("scrollbar_rectangle_1") and not width > 2 * corner_radius:
|
||||
self._canvas.delete("scrollbar_rectangle_1")
|
||||
|
||||
if not self._canvas.find_withtag("scrollbar_rectangle_2"):
|
||||
self._canvas.create_rectangle(0, 0, 0, 0, tags=("scrollbar_rectangle_2", "scrollbar_rectangle_part", "scrollbar_parts"), width=0)
|
||||
requires_recoloring = True
|
||||
elif self._canvas.find_withtag("scrollbar_rectangle_2") and not height > 2 * corner_radius:
|
||||
self._canvas.delete("scrollbar_rectangle_2")
|
||||
|
||||
self._canvas.coords("scrollbar_rectangle_1",
|
||||
corner_radius - inner_corner_radius, corner_radius + (height - 2 * corner_radius) * start_value,
|
||||
width - (corner_radius - inner_corner_radius), corner_radius + (height - 2 * corner_radius) * end_value)
|
||||
self._canvas.coords("scrollbar_rectangle_2",
|
||||
corner_radius, corner_radius - inner_corner_radius + (height - 2 * corner_radius) * start_value,
|
||||
width - (corner_radius), corner_radius + inner_corner_radius + (height - 2 * corner_radius) * end_value)
|
||||
|
||||
if orientation == "vertical":
|
||||
self._canvas.coords("scrollbar_oval_1_a", corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_1_b", corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_2_a", width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_2_b", width - corner_radius, corner_radius + (height - 2 * corner_radius) * start_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_3_a", width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_3_b", width - corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_4_a", corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_4_b", corner_radius, corner_radius + (height - 2 * corner_radius) * end_value, inner_corner_radius)
|
||||
if orientation == "horizontal":
|
||||
self._canvas.coords("scrollbar_oval_1_a", corner_radius + (width - 2 * corner_radius) * start_value, corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_1_b", corner_radius + (width - 2 * corner_radius) * start_value, corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_2_a", corner_radius + (width - 2 * corner_radius) * end_value, corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_2_b", corner_radius + (width - 2 * corner_radius) * end_value, corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_3_a", corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_3_b", corner_radius + (width - 2 * corner_radius) * end_value, height - corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_4_a", corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius, inner_corner_radius)
|
||||
self._canvas.coords("scrollbar_oval_4_b", corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius, inner_corner_radius)
|
||||
|
||||
return requires_recoloring
|
||||
|
||||
def draw_checkmark(self, width: Union[float, int], height: Union[float, int], size: Union[int, float]) -> bool:
|
||||
""" Draws a rounded rectangle with a corner_radius and border_width on the canvas. The border elements have a 'border_parts' tag,
|
||||
the main foreground elements have an 'inner_parts' tag to color the elements accordingly.
|
||||
|
200
customtkinter/widgets/ctk_scrollbar.py
Normal file
200
customtkinter/widgets/ctk_scrollbar.py
Normal file
@ -0,0 +1,200 @@
|
||||
from .ctk_canvas import CTkCanvas
|
||||
from ..theme_manager import ThemeManager
|
||||
from ..draw_engine import DrawEngine
|
||||
from .widget_base_class import CTkBaseClass
|
||||
|
||||
|
||||
class CTkScrollbar(CTkBaseClass):
|
||||
def __init__(self, *args,
|
||||
bg_color=None,
|
||||
fg_color=None,
|
||||
scrollbar_color="default_theme",
|
||||
scrollbar_hover_color="default_theme",
|
||||
border_spacing="default_theme",
|
||||
corner_radius="default_theme",
|
||||
width=None,
|
||||
height=None,
|
||||
orientation="vertical",
|
||||
command=None,
|
||||
hover=True,
|
||||
**kwargs):
|
||||
|
||||
# set default dimensions according to orientation
|
||||
if width is None:
|
||||
if orientation.lower() == "vertical":
|
||||
width = 16
|
||||
else:
|
||||
width = 200
|
||||
if height is None:
|
||||
if orientation.lower() == "vertical":
|
||||
height = 200
|
||||
else:
|
||||
height = 16
|
||||
|
||||
# transfer basic functionality (bg_color, size, _appearance_mode, scaling) to CTkBaseClass
|
||||
super().__init__(*args, bg_color=bg_color, width=width, height=height, **kwargs)
|
||||
|
||||
# color
|
||||
self.fg_color = fg_color
|
||||
self.scrollbar_color = ThemeManager.theme["color"]["scrollbar"] if scrollbar_color == "default_theme" else scrollbar_color
|
||||
self.scrollbar_hover_color = ThemeManager.theme["color"]["scrollbar_hover"] if scrollbar_hover_color == "default_theme" else scrollbar_hover_color
|
||||
|
||||
# shape
|
||||
self.corner_radius = ThemeManager.theme["shape"]["scrollbar_corner_radius"] if corner_radius == "default_theme" else corner_radius
|
||||
self.border_spacing = ThemeManager.theme["shape"]["scrollbar_border_spacing"] if border_spacing == "default_theme" else border_spacing
|
||||
|
||||
self.hover = hover
|
||||
self.hover_state = False
|
||||
self.command = command
|
||||
self.orientation = orientation
|
||||
self.start_value: float = 0 # 0 to 1
|
||||
self.end_value: float = 1 # 0 to 1
|
||||
|
||||
self.canvas = CTkCanvas(master=self,
|
||||
highlightthickness=0,
|
||||
width=self.apply_widget_scaling(self._current_width),
|
||||
height=self.apply_widget_scaling(self._current_height))
|
||||
self.canvas.place(x=0, y=0, relwidth=1, relheight=1)
|
||||
self.canvas.configure(bg="green")
|
||||
self.draw_engine = DrawEngine(self.canvas)
|
||||
|
||||
self.canvas.bind("<Enter>", self.on_enter)
|
||||
self.canvas.bind("<Leave>", self.on_leave)
|
||||
self.canvas.tag_bind("border_parts", "<Button-1>", self.clicked)
|
||||
self.canvas.bind("<B1-Motion>", self.clicked)
|
||||
self.canvas.bind("<MouseWheel>", self.mouse_scroll_event)
|
||||
self.bind('<Configure>', self.update_dimensions_event)
|
||||
|
||||
self.draw()
|
||||
|
||||
def set_scaling(self, *args, **kwargs):
|
||||
super().set_scaling(*args, **kwargs)
|
||||
|
||||
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width), height=self.apply_widget_scaling(self._desired_height))
|
||||
self.draw(no_color_updates=True)
|
||||
|
||||
def set_dimensions(self, width=None, height=None):
|
||||
super().set_dimensions(width, height)
|
||||
|
||||
self.canvas.configure(width=self.apply_widget_scaling(self._desired_width),
|
||||
height=self.apply_widget_scaling(self._desired_height))
|
||||
self.draw(no_color_updates=True)
|
||||
|
||||
def draw(self, no_color_updates=False):
|
||||
requires_recoloring = self.draw_engine.draw_rounded_scrollbar(self.apply_widget_scaling(self._current_width),
|
||||
self.apply_widget_scaling(self._current_height),
|
||||
self.apply_widget_scaling(self.corner_radius),
|
||||
self.apply_widget_scaling(self.border_spacing),
|
||||
self.start_value, self.end_value,
|
||||
self.orientation)
|
||||
|
||||
if no_color_updates is False or requires_recoloring:
|
||||
if self.hover_state is True:
|
||||
self.canvas.itemconfig("scrollbar_parts",
|
||||
fill=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode),
|
||||
outline=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode))
|
||||
else:
|
||||
self.canvas.itemconfig("scrollbar_parts",
|
||||
fill=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode),
|
||||
outline=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode))
|
||||
|
||||
if self.fg_color is None:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
fill=ThemeManager.single_color(self.bg_color, self._appearance_mode),
|
||||
outline=ThemeManager.single_color(self.bg_color, self._appearance_mode))
|
||||
else:
|
||||
self.canvas.itemconfig("border_parts",
|
||||
fill=ThemeManager.single_color(self.fg_color, self._appearance_mode),
|
||||
outline=ThemeManager.single_color(self.fg_color, self._appearance_mode))
|
||||
|
||||
def set(self, start_value: float, end_value: float):
|
||||
self.start_value = float(start_value)
|
||||
self.end_value = float(end_value)
|
||||
self.scrollbar_height = self.end_value - self.start_value
|
||||
self.draw()
|
||||
|
||||
def get(self):
|
||||
return self.start_value, self.end_value
|
||||
|
||||
def configure(self, *args, **kwargs):
|
||||
require_redraw = False # some attribute changes require a call of self.draw() at the end
|
||||
|
||||
if "bg_color" in kwargs:
|
||||
if kwargs["bg_color"] is None:
|
||||
self.bg_color = self.detect_color_of_master()
|
||||
else:
|
||||
self.bg_color = kwargs["bg_color"]
|
||||
require_redraw = True
|
||||
del kwargs["bg_color"]
|
||||
|
||||
if "fg_color" in kwargs:
|
||||
self.fg_color = kwargs["fg_color"]
|
||||
require_redraw = True
|
||||
del kwargs["fg_color"]
|
||||
|
||||
if "scrollbar_color" in kwargs:
|
||||
self.scrollbar_color = kwargs["scrollbar_color"]
|
||||
require_redraw = True
|
||||
del kwargs["scrollbar_color"]
|
||||
|
||||
if "scrollbar_hover_color" in kwargs:
|
||||
self.scrollbar_hover_color = kwargs["scrollbar_hover_color"]
|
||||
require_redraw = True
|
||||
del kwargs["scrollbar_hover_color"]
|
||||
|
||||
if "command" in kwargs:
|
||||
self.command = kwargs["command"]
|
||||
del kwargs["command"]
|
||||
|
||||
if "corner_radius" in kwargs:
|
||||
self.corner_radius = kwargs["corner_radius"]
|
||||
require_redraw = True
|
||||
del kwargs["corner_radius"]
|
||||
|
||||
if "border_spacing" in kwargs:
|
||||
self.border_spacing = kwargs["border_spacing"]
|
||||
require_redraw = True
|
||||
del kwargs["border_spacing"]
|
||||
|
||||
if "width" in kwargs:
|
||||
self.set_dimensions(width=kwargs["width"])
|
||||
del kwargs["width"]
|
||||
|
||||
if "height" in kwargs:
|
||||
self.set_dimensions(height=kwargs["height"])
|
||||
del kwargs["height"]
|
||||
|
||||
super().configure(*args, **kwargs)
|
||||
|
||||
if require_redraw:
|
||||
self.draw()
|
||||
|
||||
def on_enter(self, event=0):
|
||||
if self.hover is True:
|
||||
self.hover_state = True
|
||||
self.canvas.itemconfig("scrollbar_parts",
|
||||
outline=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode),
|
||||
fill=ThemeManager.single_color(self.scrollbar_hover_color, self._appearance_mode))
|
||||
|
||||
def on_leave(self, event=0):
|
||||
self.hover_state = False
|
||||
self.canvas.itemconfig("scrollbar_parts",
|
||||
outline=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode),
|
||||
fill=ThemeManager.single_color(self.scrollbar_color, self._appearance_mode))
|
||||
|
||||
def clicked(self, event):
|
||||
if self.orientation == "vertical":
|
||||
value = ((event.y - self.border_spacing) / (self._current_height - 2 * self.border_spacing)) / self._widget_scaling
|
||||
current_scrollbar_length = self.end_value - self.start_value
|
||||
value = max(current_scrollbar_length / 2, min(value, 1 - (current_scrollbar_length / 2)))
|
||||
self.start_value = value - (current_scrollbar_length / 2)
|
||||
self.end_value = value + (current_scrollbar_length / 2)
|
||||
self.draw()
|
||||
|
||||
if self.command is not None:
|
||||
self.command('moveto', self.start_value)
|
||||
|
||||
def mouse_scroll_event(self, event=None):
|
||||
if self.command is not None:
|
||||
self.command('scroll', event.delta, 'units')
|
||||
|
@ -61,7 +61,7 @@ class CTkSlider(CTkBaseClass):
|
||||
self.border_width = ThemeManager.theme["shape"]["slider_border_width"] if border_width == "default_theme" else border_width
|
||||
self.button_length = ThemeManager.theme["shape"]["slider_button_length"] if button_length == "default_theme" else button_length
|
||||
self.value = 0.5 # initial value of slider in percent
|
||||
self.orient = orient
|
||||
self.orientation = orient
|
||||
self.hover_state = False
|
||||
self.from_ = from_
|
||||
self.to = to
|
||||
@ -139,9 +139,9 @@ class CTkSlider(CTkBaseClass):
|
||||
self.configure(cursor="arrow")
|
||||
|
||||
def draw(self, no_color_updates=False):
|
||||
if self.orient.lower() == "horizontal":
|
||||
if self.orientation.lower() == "horizontal":
|
||||
orientation = "w"
|
||||
elif self.orient.lower() == "vertical":
|
||||
elif self.orientation.lower() == "vertical":
|
||||
orientation = "s"
|
||||
else:
|
||||
orientation = "w"
|
||||
@ -185,7 +185,7 @@ class CTkSlider(CTkBaseClass):
|
||||
|
||||
def clicked(self, event=None):
|
||||
if self.state == "normal":
|
||||
if self.orient.lower() == "horizontal":
|
||||
if self.orientation.lower() == "horizontal":
|
||||
self.value = (event.x / self._current_width) / self._widget_scaling
|
||||
else:
|
||||
self.value = 1 - (event.y / self._current_height) / self._widget_scaling
|
||||
|
37
test/manual_integration_tests/test_scrollbar.py
Normal file
37
test/manual_integration_tests/test_scrollbar.py
Normal file
@ -0,0 +1,37 @@
|
||||
import tkinter
|
||||
import customtkinter
|
||||
|
||||
# customtkinter.DrawEngine.preferred_drawing_method = "font_shapes"
|
||||
|
||||
|
||||
def to_scollbar(*args, **kwargs):
|
||||
tk_textbox_scrollbar.set(*args, **kwargs)
|
||||
ctk_textbox_scrollbar.set(*args, **kwargs)
|
||||
ctk_textbox_scrollbar.update_idletasks()
|
||||
tk_textbox_scrollbar.update_idletasks()
|
||||
print("to_scollbar:", args, **kwargs)
|
||||
|
||||
|
||||
def from_scrollbar(*args, **kwargs):
|
||||
tk_textbox.yview(*args, **kwargs)
|
||||
print("from_scrollbar:", args, **kwargs)
|
||||
|
||||
|
||||
app = customtkinter.CTk()
|
||||
app.grid_rowconfigure(0, weight=1)
|
||||
app.grid_columnconfigure(0, weight=1)
|
||||
|
||||
tk_textbox = tkinter.Text(app)
|
||||
tk_textbox.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
ctk_textbox_scrollbar = customtkinter.CTkScrollbar(app, command=from_scrollbar, fg_color="red")
|
||||
ctk_textbox_scrollbar.grid(row=0, column=1, padx=0, sticky="ns")
|
||||
|
||||
tk_textbox_scrollbar = tkinter.Scrollbar(app, command=from_scrollbar)
|
||||
tk_textbox_scrollbar.grid(row=0, column=2, padx=1, sticky="ns")
|
||||
|
||||
tk_textbox.configure(yscrollcommand=to_scollbar)
|
||||
|
||||
tk_textbox.insert("insert", "\n".join([str(i) for i in range(100)]))
|
||||
|
||||
app.mainloop()
|
Loading…
Reference in New Issue
Block a user