commit 57

This commit is contained in:
2025-08-05 18:29:58 +02:00
parent 160d8acafb
commit c8db431c06
7 changed files with 228 additions and 138 deletions

View File

@@ -38,6 +38,8 @@ class SettingsDialog(tk.Toplevel):
value=self.settings.get("default_view_mode", "icons"))
self.search_hidden_files = tk.BooleanVar(
value=self.settings.get("search_hidden_files", False))
self.recursive_search = tk.BooleanVar(
value=self.settings.get("recursive_search", True))
self.use_trash = tk.BooleanVar(
value=self.settings.get("use_trash", False))
self.confirm_delete = tk.BooleanVar(
@@ -89,6 +91,8 @@ class SettingsDialog(tk.Toplevel):
search_hidden_frame.pack(fill="x", pady=5)
ttk.Checkbutton(search_hidden_frame, text="Versteckte Dateien und Ordner durchsuchen",
variable=self.search_hidden_files).pack(anchor="w")
ttk.Checkbutton(search_hidden_frame, text="Rekursiv suchen",
variable=self.recursive_search).pack(anchor="w")
# Deletion Settings
delete_frame = ttk.LabelFrame(
@@ -134,6 +138,7 @@ class SettingsDialog(tk.Toplevel):
"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()
}
@@ -148,6 +153,7 @@ class SettingsDialog(tk.Toplevel):
self.window_size_preset.set(defaults["window_size_preset"])
self.default_view_mode.set(defaults["default_view_mode"])
self.search_hidden_files.set(defaults["search_hidden_files"])
self.recursive_search.set(defaults["recursive_search"])
self.use_trash.set(defaults["use_trash"])
self.confirm_delete.set(defaults["confirm_delete"])
@@ -194,6 +200,7 @@ class CustomFileDialog(tk.Toplevel):
self.items_to_load_per_batch = 250
self.item_path_map = {}
self.responsive_buttons_hidden = None # State for responsive buttons
self.search_job = None
self.icon_manager = IconManager()
self.style_manager = StyleManager(self)
@@ -217,31 +224,63 @@ class CustomFileDialog(tk.Toplevel):
# Bind the intelligent return handler
self.widget_manager.path_entry.bind(
"<Return>", self.handle_path_entry_return)
self.bind("<Key>", self.show_search_bar)
# Bind the delete key only in "save" mode
if self.dialog_mode == "save":
self.bind("<Delete>", self.delete_selected_item)
def handle_path_entry_return(self, event):
"""Intelligently handles the Enter key in the path entry.
If the text is a valid directory, it navigates there.
Otherwise, if in search mode, it executes a search.
def show_search_bar(self, event=None):
# Ignore key presses if they are coming from an entry widget or have no character
if isinstance(event.widget, (ttk.Entry, tk.Entry)) or not event.char.strip():
return
self.search_mode = True
if self.dialog_mode == "open":
self.widget_manager.status_bar.pack_forget()
self.widget_manager.search_entry.pack(side="top", fill="x", in_=self.widget_manager.center_container)
self.widget_manager.search_entry.focus_set()
self.widget_manager.search_entry.insert(0, event.char)
self.widget_manager.search_entry.bind("<Return>", self.execute_search)
self.widget_manager.search_entry.bind("<Escape>", self.hide_search_bar)
else: # save mode
self.widget_manager.filename_entry.focus_set()
self.widget_manager.filename_entry.insert(tk.END, event.char)
self.widget_manager.filename_entry.bind("<Return>", self.execute_search)
self.widget_manager.filename_entry.bind("<Escape>", self.hide_search_bar)
def hide_search_bar(self, event=None):
self.search_mode = False
if self.dialog_mode == "open":
self.widget_manager.search_entry.pack_forget()
self.widget_manager.status_bar.pack(side="top", fill="x", in_=self.widget_manager.center_container)
self.widget_manager.search_entry.delete(0, tk.END)
else: # save mode
self.widget_manager.filename_entry.delete(0, tk.END)
self.widget_manager.search_status_label.config(text="")
self.populate_files()
def toggle_search_mode(self, event=None):
if self.search_mode:
self.hide_search_bar(event)
else:
self.show_search_bar(event)
def handle_path_entry_return(self, event):
"""Handles the Enter key in the path entry to navigate.
Search is handled by on_path_entry_key_release.
"""
path_text = self.widget_manager.path_entry.get().strip()
# Try to interpret as a path first
# Expand user-home and resolve relative paths
potential_path = os.path.realpath(os.path.expanduser(path_text))
if os.path.isdir(potential_path):
# If search was active, turn it off before navigating
if self.search_mode:
self.toggle_search_mode()
self.navigate_to(potential_path)
elif self.search_mode:
# If not a valid path and in search mode, execute search
self.execute_search(event)
# If not a directory, do nothing on Enter. Search is triggered on key release.
def load_settings(self):
self.settings = CfdConfigManager.load()
@@ -435,36 +474,7 @@ class CustomFileDialog(tk.Toplevel):
widget_y - buffer <= y <= widget_y + widget_height + buffer):
self.widget_manager.devices_scrollbar.grid_remove()
def toggle_search_mode(self):
"""Toggle between search mode and normal mode"""
if not self.search_mode:
# Enter search mode
self.search_mode = True
self.original_path_text = self.widget_manager.path_entry.get()
self.widget_manager.path_entry.delete(0, tk.END)
self.widget_manager.path_entry.insert(0, "Suchbegriff eingeben...")
# Use after() to ensure the focus is set after the UI has updated
self.after(10, lambda: self.widget_manager.path_entry.focus_set())
self.after(
20, lambda: self.widget_manager.path_entry.select_range(0, tk.END))
self.widget_manager.path_entry.bind(
"<FocusIn>", self.clear_search_placeholder)
# Show search options
self.widget_manager.recursive_button.pack(side="left", padx=5)
else:
# Exit search mode
self.search_mode = False
self.widget_manager.path_entry.delete(0, tk.END)
self.widget_manager.path_entry.insert(0, self.original_path_text)
self.widget_manager.path_entry.unbind("<FocusIn>")
# Hide search options
self.widget_manager.recursive_button.pack_forget()
# Return to normal file view
self.populate_files()
def toggle_recursive_search(self):
"""Toggle recursive search on/off and update button style"""
@@ -502,17 +512,21 @@ class CustomFileDialog(tk.Toplevel):
self._update_view_mode_buttons()
self.populate_files()
def clear_search_placeholder(self, event):
"""Clear placeholder text when focus enters search field"""
if self.widget_manager.path_entry.get() == "Suchbegriff eingeben...":
self.widget_manager.path_entry.delete(0, tk.END)
def execute_search(self, event):
"""Execute search when Enter is pressed in search mode"""
search_term = self.widget_manager.path_entry.get().strip()
if not search_term or search_term == "Suchbegriff eingeben...":
def execute_search(self, event=None):
if self.dialog_mode == "open":
search_term = self.widget_manager.search_entry.get().strip()
else:
search_term = self.widget_manager.filename_entry.get().strip()
if not search_term:
self.hide_search_bar()
return
self.widget_manager.search_status_label.config(text=f"Suche nach '{search_term}'...")
self.update_idletasks()
# Clear previous search results
self.search_results.clear()
@@ -550,12 +564,12 @@ class CustomFileDialog(tk.Toplevel):
os.chdir(search_dir)
# Build find command based on recursive setting (use . for current directory)
if self.widget_manager.recursive_search.get():
# Find both files and directories
find_cmd = ['find', '.', '-iname', f'*{search_term}*']
if self.settings.get("recursive_search", True):
# Find both files and directories, following symlinks
find_cmd = ['find', '-L', '.', '-iname', f'*{search_term}*']
else:
# Find both files and directories, but only in the current level
find_cmd = ['find', '.', '-maxdepth', '1',
# Find both files and directories, but only in the current level, following symlinks
find_cmd = ['find', '-L', '.', '-maxdepth', '1',
'-iname', f'*{search_term}*']
result = subprocess.run(
@@ -604,7 +618,11 @@ class CustomFileDialog(tk.Toplevel):
# Show search results in TreeView
if self.search_results:
self.show_search_results_treeview()
folder_count = sum(1 for p in self.search_results if os.path.isdir(p))
file_count = len(self.search_results) - folder_count
self.widget_manager.search_status_label.config(text=f"{folder_count} Ordner und {file_count} Dateien gefunden.")
else:
self.widget_manager.search_status_label.config(text=f"Keine Ergebnisse für '{search_term}'.")
MessageDialog(
message_type="info",
text=f"Keine Dateien mit '{search_term}' gefunden.",
@@ -718,11 +736,10 @@ class CustomFileDialog(tk.Toplevel):
item = search_tree.item(selection[0])
filename = item['text'].strip()
directory = item['values'][0]
full_path = os.path.join(directory, filename)
# Select the file and close dialog
self.selected_file = full_path
self.destroy()
# Exit search mode and navigate to the file's location
self.hide_search_bar()
self.navigate_to(directory, file_to_select=filename)
search_tree.bind("<Double-1>", on_search_double_click)
@@ -751,7 +768,7 @@ class CustomFileDialog(tk.Toplevel):
directory = item['values'][0]
# Exit search mode and navigate to the directory
self.toggle_search_mode() # To restore normal view
self.hide_search_bar()
self.navigate_to(directory)
# Select the file in the list
@@ -1171,7 +1188,7 @@ class CustomFileDialog(tk.Toplevel):
break
self.populate_files()
def navigate_to(self, path):
def navigate_to(self, path, file_to_select=None):
try:
real_path = os.path.realpath(
os.path.abspath(os.path.expanduser(path)))
@@ -1189,7 +1206,7 @@ class CustomFileDialog(tk.Toplevel):
if not self.history or self.history[-1] != self.current_dir:
self.history.append(self.current_dir)
self.history_pos = len(self.history) - 1
self.populate_files()
self.populate_files(item_to_select=file_to_select)
self.update_nav_buttons()
self.update_status_bar()
self.update_action_buttons_state()