commit 68

This commit is contained in:
2025-08-08 12:13:02 +02:00
parent 053b0c22c5
commit 3d2ffcc69e
8 changed files with 228 additions and 92 deletions

View File

@@ -10,6 +10,7 @@ from shared_libs.message import MessageDialog
from shared_libs.common_tools import IconManager, Tooltip, ConfigManager, LxTools
from cfd_app_config import AppConfig, CfdConfigManager
from cfd_ui_setup import StyleManager, WidgetManager, get_xdg_user_dir
from cfd_animated_icon import AnimatedIcon, PIL_AVAILABLE
try:
import send2trash
@@ -45,20 +46,15 @@ class SettingsDialog(tk.Toplevel):
value=self.settings.get("use_trash", False))
self.confirm_delete = tk.BooleanVar(
value=self.settings.get("confirm_delete", False))
self.use_pillow_animation = tk.BooleanVar(
value=self.settings.get("use_pillow_animation", False))
self.animation_type = tk.StringVar(
value=self.settings.get("animation_type", "double_arc"))
# --- UI Elements ---
main_frame = ttk.Frame(self, padding=10)
main_frame.pack(fill="both", expand=True)
# Search Icon Position
search_frame = ttk.LabelFrame(
main_frame, text="Position der Such-Lupe", padding=10)
search_frame.pack(fill="x", pady=5)
ttk.Radiobutton(search_frame, text="Links", variable=self.search_icon_pos,
value="left").pack(side="left", padx=5)
ttk.Radiobutton(search_frame, text="Rechts", variable=self.search_icon_pos,
value="right").pack(side="left", padx=5)
# Button Box Position
button_box_frame = ttk.LabelFrame(
main_frame, text="Position der Dialog-Buttons", padding=10)
@@ -113,6 +109,29 @@ class SettingsDialog(tk.Toplevel):
variable=self.confirm_delete)
self.confirm_delete_checkbutton.pack(anchor="w")
# Pillow Animation
pillow_frame = ttk.LabelFrame(
main_frame, text="Animationseinstellungen", padding=10)
pillow_frame.pack(fill="x", pady=5)
self.use_pillow_animation_checkbutton = ttk.Checkbutton(pillow_frame, text="Hochauflösende Animation verwenden (Pillow)",
variable=self.use_pillow_animation)
self.use_pillow_animation_checkbutton.pack(anchor="w")
if not PIL_AVAILABLE:
self.use_pillow_animation_checkbutton.config(state=tk.DISABLED)
ttk.Label(pillow_frame, text="(Pillow-Bibliothek nicht gefunden)",
font=("TkDefaultFont", 9, "italic")).pack(anchor="w", padx=(20, 0))
# Animation Type
anim_type_frame = ttk.LabelFrame(
main_frame, text="Animationstyp", padding=10)
anim_type_frame.pack(fill="x", pady=5)
ttk.Radiobutton(anim_type_frame, text="Gegenläufige Bogen", variable=self.animation_type,
value="counter_arc").pack(side="left", padx=5)
ttk.Radiobutton(anim_type_frame, text="Doppelbogen", variable=self.animation_type,
value="double_arc").pack(side="left", padx=5)
ttk.Radiobutton(anim_type_frame, text="Linie", variable=self.animation_type,
value="line").pack(side="left", padx=5)
# Disable deletion options in "open" mode
if not self.dialog_mode == "save":
self.use_trash_checkbutton.config(state=tk.DISABLED)
@@ -134,14 +153,15 @@ class SettingsDialog(tk.Toplevel):
def save_settings(self):
new_settings = {
"search_icon_pos": self.search_icon_pos.get(),
"button_box_pos": self.button_box_pos.get(),
"window_size_preset": self.window_size_preset.get(),
"default_view_mode": self.default_view_mode.get(),
"search_hidden_files": self.search_hidden_files.get(),
"recursive_search": self.recursive_search.get(),
"use_trash": self.use_trash.get(),
"confirm_delete": self.confirm_delete.get()
"confirm_delete": self.confirm_delete.get(),
"use_pillow_animation": self.use_pillow_animation.get(),
"animation_type": self.animation_type.get()
}
CfdConfigManager.save(new_settings)
self.master.reload_config_and_rebuild_ui()
@@ -149,7 +169,6 @@ class SettingsDialog(tk.Toplevel):
def reset_to_defaults(self):
defaults = CfdConfigManager._default_settings
self.search_icon_pos.set(defaults["search_icon_pos"])
self.button_box_pos.set(defaults["button_box_pos"])
self.window_size_preset.set(defaults["window_size_preset"])
self.default_view_mode.set(defaults["default_view_mode"])
@@ -157,6 +176,8 @@ class SettingsDialog(tk.Toplevel):
self.recursive_search.set(defaults["recursive_search"])
self.use_trash.set(defaults["use_trash"])
self.confirm_delete.set(defaults["confirm_delete"])
self.use_pillow_animation.set(defaults.get("use_pillow_animation", False))
self.animation_type.set(defaults.get("animation_type", "counter_arc"))
class CustomFileDialog(tk.Toplevel):
@@ -209,6 +230,8 @@ class CustomFileDialog(tk.Toplevel):
self.style_manager = StyleManager(self)
self.widget_manager = WidgetManager(self, self.settings)
self.update_animation_settings() # Add this line
self._update_view_mode_buttons()
# Defer initial navigation until the window geometry is calculated
@@ -240,7 +263,7 @@ class CustomFileDialog(tk.Toplevel):
def activate_search(self, event=None):
"""Activates the search entry or cancels an ongoing search."""
if self.widget_manager.search_animation.is_animating:
if self.widget_manager.search_animation.running:
# If animating, it means a search is active, so cancel it
if self.search_thread and self.search_thread.is_alive():
if self.search_process:
@@ -248,7 +271,7 @@ class CustomFileDialog(tk.Toplevel):
os.killpg(os.getpgid(self.search_process.pid), 9) # Send SIGKILL to process group
except (ProcessLookupError, AttributeError):
pass # Process might have already finished or not started
self.widget_manager.search_animation.stop_animation()
self.widget_manager.search_animation.stop()
self.widget_manager.search_status_label.config(text="Suche abgebrochen.")
self.hide_search_bar() # Reset UI after cancellation
else:
@@ -267,7 +290,7 @@ class CustomFileDialog(tk.Toplevel):
self.widget_manager.filename_entry.insert(0, event.char)
self.widget_manager.filename_entry.bind("<Return>", self.execute_search)
self.widget_manager.filename_entry.bind("<Escape>", self.hide_search_bar)
# Removed: self.widget_manager.search_animation.start_animation()
# Removed: self.widget_manager.search_animation.start()
def hide_search_bar(self, event=None):
self.search_mode = False
@@ -278,7 +301,7 @@ class CustomFileDialog(tk.Toplevel):
if self.dialog_mode == "save":
self.widget_manager.filename_entry.bind("<Return>", lambda e: self.on_save())
self.populate_files()
self.widget_manager.search_animation.stop_animation()
self.widget_manager.search_animation.stop()
def execute_search(self, event=None):
if self.search_thread and self.search_thread.is_alive():
@@ -288,7 +311,7 @@ class CustomFileDialog(tk.Toplevel):
self.hide_search_bar()
return
self.widget_manager.search_status_label.config(text=f"Suche nach '{search_term}'...")
self.widget_manager.search_animation.start_animation()
self.widget_manager.search_animation.start()
self.update_idletasks()
self.search_thread = threading.Thread(target=self._perform_search_in_thread, args=(search_term,))
self.search_thread.start()
@@ -342,7 +365,7 @@ class CustomFileDialog(tk.Toplevel):
else:
self.after(0, lambda: MessageDialog(message_type="error", text=f"Fehler bei der Suche: {e}", title="Suchfehler", master=self).show())
finally:
self.after(0, self.widget_manager.search_animation.stop_animation)
self.after(0, self.widget_manager.search_animation.stop)
self.search_process = None
def handle_path_entry_return(self, event):
@@ -396,11 +419,36 @@ class CustomFileDialog(tk.Toplevel):
if self.search_mode:
self.hide_search_bar() # This will correctly reset the UI
self.update_animation_settings() # Add this line
self.navigate_to(self.current_dir)
def open_settings_dialog(self):
SettingsDialog(self, dialog_mode=self.dialog_mode)
def update_animation_settings(self):
use_pillow = self.settings.get('use_pillow_animation', False)
anim_type = self.settings.get('animation_type', 'double')
is_running = self.widget_manager.search_animation.running
if is_running:
self.widget_manager.search_animation.stop()
self.widget_manager.search_animation.destroy()
self.widget_manager.search_animation = AnimatedIcon(
self.widget_manager.status_container,
width=23,
height=23,
use_pillow=use_pillow,
animation_type=anim_type,
color="#2a6fde",
highlight_color="#5195ff",
bg=self.style_manager.bottom_color
)
self.widget_manager.search_animation.grid(row=0, column=0, sticky='w', padx=(0, 5), pady=(4,0))
self.widget_manager.search_animation.bind("<Button-1>", lambda e: self.activate_search())
if is_running:
self.widget_manager.search_animation.start()
def get_file_icon(self, filename, size='large'):
ext = os.path.splitext(filename)[1].lower()