diff --git a/customtkinter/windows/widgets/core_rendering/draw_engine.py b/customtkinter/windows/widgets/core_rendering/draw_engine.py index 5acea560..4ffe5021 100644 --- a/customtkinter/windows/widgets/core_rendering/draw_engine.py +++ b/customtkinter/windows/widgets/core_rendering/draw_engine.py @@ -32,28 +32,39 @@ def __init__(self, canvas: CTkCanvas): self._canvas = canvas self._round_width_to_even_numbers: bool = True self._round_height_to_even_numbers: bool = True + if sys.platform.startswith("linux"): + self.preferred_drawing_method = "circle_shapes" def set_round_to_even_numbers(self, round_width_to_even_numbers: bool = True, round_height_to_even_numbers: bool = True): self._round_width_to_even_numbers: bool = round_width_to_even_numbers self._round_height_to_even_numbers: bool = round_height_to_even_numbers def __calc_optimal_corner_radius(self, user_corner_radius: Union[float, int]) -> Union[float, int]: - # optimize for drawing with polygon shapes + """Optimize corner radius based on the preferred drawing method and platform.""" + + # Optimize for drawing with polygon shapes if self.preferred_drawing_method == "polygon_shapes": if sys.platform == "darwin": - return user_corner_radius + return user_corner_radius # Direct use for macOS + elif sys.platform.startswith("linux"): + return round(user_corner_radius * 1.25) / 1.25 # Fine-tuned rounding for smoother rendering on Linux else: - return round(user_corner_radius) + return round(user_corner_radius) # Default for other platforms - # optimize for drawing with antialiased font shapes + # Optimize for drawing with antialiased font shapes elif self.preferred_drawing_method == "font_shapes": return round(user_corner_radius) - # optimize for drawing with circles and rects + # Optimize for drawing with circles and rects elif self.preferred_drawing_method == "circle_shapes": - user_corner_radius = 0.5 * round(user_corner_radius / 0.5) # round to 0.5 steps + if sys.platform.startswith("linux"): + # Allow finer increments for smoother Linux rendering + user_corner_radius = 0.25 * round(user_corner_radius / 0.25) + else: + # Default behavior for other platforms + user_corner_radius = 0.5 * round(user_corner_radius / 0.5) - # make sure the value is always with .5 at the end for smoother corners + # Ensure the value always ends in .5 for smoother corners if user_corner_radius == 0: return 0 elif user_corner_radius % 1 == 0: @@ -325,6 +336,11 @@ def __draw_rounded_rect_with_border_font_shapes(self, width: int, height: int, c def __draw_rounded_rect_with_border_circle_shapes(self, width: int, height: int, corner_radius: int, border_width: int, inner_corner_radius: int) -> bool: requires_recoloring = False + # Adjust shapes for Linux rendering + if sys.platform.startswith("linux"): + corner_radius = max(corner_radius - 0.5, 0) + border_width = max(border_width - 0.2, 0) + # border button parts if border_width > 0: if corner_radius > 0: @@ -1079,7 +1095,7 @@ def __draw_rounded_scrollbar_polygon_shapes(self, width: int, height: int, corne 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,) + corner_radius + (width - 2 * corner_radius) * start_value, height - corner_radius, ) self._canvas.itemconfig("scrollbar_polygon_1", width=inner_corner_radius * 2) diff --git a/customtkinter/windows/widgets/font/font_manager.py b/customtkinter/windows/widgets/font/font_manager.py index b3ef369d..86b5c2de 100644 --- a/customtkinter/windows/widgets/font/font_manager.py +++ b/customtkinter/windows/widgets/font/font_manager.py @@ -2,25 +2,29 @@ import os import shutil from typing import Union +import subprocess class FontManager: - - linux_font_path = "~/.fonts/" + linux_font_paths = [ + os.path.expanduser("~/.fonts/"), # Default path + os.path.expanduser("~/.local/share/fonts/"), # Fallback path + ] @classmethod def init_font_manager(cls): - # Linux + """ + Initialize font manager by ensuring the required font directories exist. + """ if sys.platform.startswith("linux"): try: - if not os.path.isdir(os.path.expanduser(cls.linux_font_path)): - os.mkdir(os.path.expanduser(cls.linux_font_path)) + for path in cls.linux_font_paths: + if not os.path.isdir(path): + os.makedirs(path, exist_ok=True) return True except Exception as err: - sys.stderr.write("FontManager error: " + str(err) + "\n") + sys.stderr.write(f"FontManager error (init): {err}\n") return False - - # other platforms else: return True @@ -48,19 +52,42 @@ def windows_load_font(cls, font_path: Union[str, bytes], private: bool = True, e @classmethod def load_font(cls, font_path: str) -> bool: + """ + Load a font into the system for different platforms. + """ + # Check if the font file exists + if not os.path.isfile(font_path): + sys.stderr.write(f"FontManager error: Font file '{font_path}' does not exist.\n") + return False + # Windows if sys.platform.startswith("win"): return cls.windows_load_font(font_path, private=True, enumerable=False) # Linux elif sys.platform.startswith("linux"): - try: - shutil.copy(font_path, os.path.expanduser(cls.linux_font_path)) - return True - except Exception as err: - sys.stderr.write("FontManager error: " + str(err) + "\n") - return False + for path in cls.linux_font_paths: + try: + dest_path = os.path.join(path, os.path.basename(font_path)) + if not os.path.isfile(dest_path): # Avoid redundant copying + shutil.copy(font_path, dest_path) + cls.refresh_font_cache(path) # Refresh the font cache + return True + except Exception as err: + sys.stderr.write(f"FontManager error (Linux): {err}\n") + return False # macOS and others else: + sys.stderr.write("FontManager warning: Font loading is not supported on this platform.\n") return False + + @staticmethod + def refresh_font_cache(directory: str): + """ + Refresh the font cache on Linux using fc-cache. + """ + try: + subprocess.run(["fc-cache", "-fv", directory], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except Exception as err: + sys.stderr.write(f"FontManager error (fc-cache): {err}\n")