commit 57
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -62,7 +62,8 @@ class CfdConfigManager:
|
||||
"default_view_mode": "icons", # 'icons' or 'list'
|
||||
"search_hidden_files": False, # True or False
|
||||
"use_trash": False, # True or False
|
||||
"confirm_delete": False # True or False
|
||||
"confirm_delete": False, # True or False
|
||||
"recursive_search": True
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
136
cfd_ui_setup.py
136
cfd_ui_setup.py
@@ -175,11 +175,6 @@ class WidgetManager:
|
||||
# Function to create search widgets
|
||||
def create_search_widgets(parent_frame):
|
||||
container = ttk.Frame(parent_frame, style='Accent.TFrame')
|
||||
self.search_button = ttk.Button(container, image=self.dialog.icon_manager.get_icon(
|
||||
'search_small'), command=self.dialog.toggle_search_mode, style="Header.TButton.Borderless.Round")
|
||||
self.search_button.pack(side="left")
|
||||
Tooltip(self.search_button, "Suchen")
|
||||
|
||||
self.recursive_button = ttk.Button(container, image=self.dialog.icon_manager.get_icon(
|
||||
'recursive_small'), command=self.dialog.toggle_recursive_search, style="Header.TButton.Active.Round")
|
||||
self.recursive_button.pack(side="left", padx=2)
|
||||
@@ -400,92 +395,87 @@ class WidgetManager:
|
||||
self.file_list_frame.grid(row=0, column=0, sticky="nsew")
|
||||
self.dialog.bind("<Configure>", self.dialog.on_window_resize)
|
||||
|
||||
# --- Bottom Bar ---
|
||||
# This frame will contain the action buttons and status bar
|
||||
self.action_status_frame = ttk.Frame(
|
||||
content_frame, style="AccentBottom.TFrame")
|
||||
self.action_status_frame.grid(
|
||||
row=1, column=0, sticky="ew", pady=(5, 10), padx=10)
|
||||
self.action_status_frame = ttk.Frame(content_frame, style="AccentBottom.TFrame")
|
||||
self.action_status_frame.grid(row=1, column=0, sticky="ew", pady=(5, 10), padx=10)
|
||||
|
||||
button_box_pos = self.settings.get("button_box_pos", "left")
|
||||
# Create three main containers for alignment
|
||||
self.left_container = ttk.Frame(self.action_status_frame, style="AccentBottom.TFrame")
|
||||
self.center_container = ttk.Frame(self.action_status_frame, style="AccentBottom.TFrame")
|
||||
self.right_container = ttk.Frame(self.action_status_frame, style="AccentBottom.TFrame")
|
||||
|
||||
# Configure columns for the action_status_frame
|
||||
if button_box_pos == 'left':
|
||||
self.action_status_frame.grid_columnconfigure(1, weight=1)
|
||||
else:
|
||||
self.action_status_frame.grid_columnconfigure(1, weight=1)
|
||||
# Configure the containers' expansion behavior
|
||||
self.action_status_frame.grid_columnconfigure(0, weight=0) # Left container
|
||||
self.action_status_frame.grid_columnconfigure(1, weight=1) # Center container (expands)
|
||||
self.action_status_frame.grid_columnconfigure(2, weight=0) # Right container
|
||||
|
||||
# Status bar will be placed inside the action_status_frame
|
||||
self.status_bar = ttk.Label(
|
||||
self.action_status_frame, text="", anchor="w", style="AccentBottom.TLabel")
|
||||
self.left_container.grid(row=0, column=0, sticky='w')
|
||||
self.center_container.grid(row=0, column=1, sticky='ew')
|
||||
self.right_container.grid(row=0, column=2, sticky='e')
|
||||
|
||||
self.settings_button = ttk.Button(self.action_status_frame, image=self.dialog.icon_manager.get_icon(
|
||||
'settings-2_small'), command=self.dialog.open_settings_dialog, style="Bottom.TButton.Borderless.Round")
|
||||
|
||||
self.trash_button = ttk.Button(self.action_status_frame, image=self.dialog.icon_manager.get_icon(
|
||||
'trash_small2'), command=self.dialog.delete_selected_item, style="Bottom.TButton.Borderless.Round")
|
||||
# --- Define Widgets (All belong to action_status_frame) ---
|
||||
self.status_bar = ttk.Label(self.action_status_frame, text="", anchor="w", style="AccentBottom.TLabel")
|
||||
self.search_entry = ttk.Entry(self.action_status_frame)
|
||||
self.search_status_label = ttk.Label(self.action_status_frame, text="", style="AccentBottom.TLabel")
|
||||
self.settings_button = ttk.Button(self.action_status_frame, image=self.dialog.icon_manager.get_icon('settings-2_small'),
|
||||
command=self.dialog.open_settings_dialog, style="Bottom.TButton.Borderless.Round")
|
||||
self.trash_button = ttk.Button(self.action_status_frame, image=self.dialog.icon_manager.get_icon('trash_small2'),
|
||||
command=self.dialog.delete_selected_item, style="Bottom.TButton.Borderless.Round")
|
||||
Tooltip(self.trash_button, "Ausgewähltes Element löschen/verschieben")
|
||||
|
||||
# --- Arrange Widgets based on Mode and Settings ---
|
||||
button_box_pos = self.settings.get("button_box_pos", "left")
|
||||
|
||||
if self.dialog.dialog_mode == "save":
|
||||
self.filename_entry = ttk.Entry(self.action_status_frame)
|
||||
save_button = ttk.Button(
|
||||
self.action_status_frame, text="Speichern", command=self.dialog.on_save)
|
||||
cancel_button = ttk.Button(
|
||||
self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[
|
||||
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||
save_button = ttk.Button(self.action_status_frame, text="Speichern", command=self.dialog.on_save)
|
||||
cancel_button = ttk.Button(self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||
|
||||
# Pack widgets into their respective containers
|
||||
self.filename_entry.pack(in_=self.center_container, side="top", fill="x", expand=True)
|
||||
|
||||
if button_box_pos == 'left':
|
||||
save_button.grid(row=0, column=0, sticky="w", padx=(0, 10))
|
||||
self.filename_entry.grid(
|
||||
row=0, column=1, sticky="ew", padx=(0, 5))
|
||||
cancel_button.grid(row=1, column=0, sticky="w",
|
||||
pady=(5, 0), padx=(0, 10))
|
||||
self.filter_combobox.grid(
|
||||
row=1, column=1, sticky="w", pady=(5, 0), padx=(0, 5))
|
||||
self.settings_button.grid(row=0, column=3, sticky="e")
|
||||
self.trash_button.grid(
|
||||
row=1, column=3, sticky="se", padx=(5, 0))
|
||||
save_button.pack(in_=self.left_container, side="left", padx=(0, 5))
|
||||
cancel_button.pack(in_=self.left_container, side="left")
|
||||
self.filter_combobox.pack(in_=self.center_container, side="left", pady=(5,0), padx=(5,0))
|
||||
self.search_status_label.pack(in_=self.center_container, side="left", pady=(5,0), padx=(5,0))
|
||||
self.trash_button.pack(in_=self.right_container, side="right", padx=(5,0))
|
||||
self.settings_button.pack(in_=self.right_container, side="right")
|
||||
else: # right
|
||||
self.trash_button.grid(
|
||||
row=1, column=0, sticky="sw", padx=(0, 5))
|
||||
self.filename_entry.grid(
|
||||
row=0, column=1, sticky="ew", padx=(0, 5))
|
||||
self.filter_combobox.grid(
|
||||
row=1, column=1, sticky="e", pady=(5, 0), padx=(0, 5))
|
||||
save_button.grid(row=0, column=2, sticky="e", padx=5)
|
||||
cancel_button.grid(row=1, column=2, sticky="e",
|
||||
padx=5, pady=(5, 0))
|
||||
self.settings_button.grid(row=0, column=3, sticky="e")
|
||||
self.trash_button.pack(in_=self.left_container, side="left")
|
||||
self.search_status_label.pack(in_=self.center_container, side="left", pady=(5,0), padx=(5,0))
|
||||
self.filter_combobox.pack(in_=self.center_container, side="right", pady=(5,0), padx=(0,5))
|
||||
save_button.pack(in_=self.right_container, side="left", padx=(5, 5))
|
||||
cancel_button.pack(in_=self.right_container, side="left")
|
||||
self.settings_button.pack(in_=self.right_container, side="right")
|
||||
|
||||
self.filter_combobox.bind(
|
||||
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||
self.filter_combobox.bind("<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||
self.filter_combobox.set(self.dialog.filetypes[0][0])
|
||||
|
||||
else: # Open mode
|
||||
open_button = ttk.Button(
|
||||
self.action_status_frame, text="Öffnen", command=self.dialog.on_open)
|
||||
cancel_button = ttk.Button(
|
||||
self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[
|
||||
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||
open_button = ttk.Button(self.action_status_frame, text="Öffnen", command=self.dialog.on_open)
|
||||
cancel_button = ttk.Button(self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||
|
||||
# Pack status bar and search entry into the center
|
||||
self.status_bar.pack(in_=self.center_container, side="top", fill="x")
|
||||
self.search_entry.pack(in_=self.center_container, side="top", fill="x")
|
||||
self.search_entry.pack_forget() # Initially hidden
|
||||
|
||||
if button_box_pos == 'left':
|
||||
open_button.grid(row=0, column=0, sticky="w", padx=(0, 5))
|
||||
self.status_bar.grid(row=0, column=1, sticky="w", padx=5)
|
||||
cancel_button.grid(row=1, column=0, sticky="w", pady=(5, 0))
|
||||
self.filter_combobox.grid(
|
||||
row=1, column=1, sticky="w", pady=(5, 0), padx=(5, 0))
|
||||
self.settings_button.grid(row=0, column=2, sticky="e")
|
||||
|
||||
open_button.pack(in_=self.left_container, side="left", padx=(0, 5))
|
||||
cancel_button.pack(in_=self.left_container, side="left")
|
||||
self.filter_combobox.pack(in_=self.center_container, side="left", pady=(5,0), padx=(5,0))
|
||||
self.search_status_label.pack(in_=self.center_container, side="left", pady=(5,0), padx=(5,0))
|
||||
self.settings_button.pack(in_=self.right_container, side="right")
|
||||
else: # right
|
||||
self.status_bar.grid(row=0, column=0, sticky="e", padx=10)
|
||||
open_button.grid(row=0, column=1, sticky="e", padx=5)
|
||||
cancel_button.grid(row=1, column=1, sticky="e",
|
||||
padx=5, pady=(5, 0))
|
||||
self.filter_combobox.grid(
|
||||
row=1, column=0, sticky="e", pady=(5, 0))
|
||||
self.settings_button.grid(row=0, column=2, sticky="e")
|
||||
self.search_status_label.pack(in_=self.center_container, side="left", pady=(5,0))
|
||||
self.filter_combobox.pack(in_=self.center_container, side="right", pady=(5,0), padx=(0,5))
|
||||
open_button.pack(in_=self.right_container, side="left", padx=(0, 5))
|
||||
cancel_button.pack(in_=self.right_container, side="left")
|
||||
self.settings_button.pack(in_=self.right_container, side="right")
|
||||
|
||||
self.filter_combobox.bind(
|
||||
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||
self.filter_combobox.bind("<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||
self.filter_combobox.set(self.dialog.filetypes[0][0])
|
||||
|
@@ -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)
|
||||
@@ -218,30 +225,62 @@ class CustomFileDialog(tk.Toplevel):
|
||||
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()
|
||||
|
82
layout_beschreibung.md
Normal file
82
layout_beschreibung.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Beschreibung der Layout-Struktur (Unterer Bereich)
|
||||
|
||||
Selbstverständlich. Sie haben absolut recht, eine gute Dokumentation ist wichtig, besonders nach einer solchen Umstrukturierung. Ich werde die neue Logik des unteren Bereichs detailliert aufschlüsseln.
|
||||
|
||||
### Das Grundkonzept: Drei flexible Spalten
|
||||
|
||||
Stellen Sie sich den gesamten unteren Bereich (`action_status_frame`) wie eine Tabelle mit **einer Zeile und drei Spalten** vor:
|
||||
|
||||
| `left_container` | `center_container` (flexibel) | `right_container` |
|
||||
| :--- | :--- | :--- |
|
||||
| (linksbündig) | (füllt den Platz) | (rechtsbündig) |
|
||||
|
||||
- **`action_status_frame`**: Dies ist der Haupt-Frame für den gesamten unteren Bereich. Er enthält die drei Container.
|
||||
- **`left_container`**: Alle Widgets, die linksbündig sein sollen, kommen hier hinein.
|
||||
- **`right_container`**: Alle Widgets, die rechtsbündig sein sollen, kommen hier hinein.
|
||||
- **`center_container`**: Dieser Container ist der flexible Teil. Er dehnt sich aus, um den gesamten verfügbaren Platz zwischen dem linken und rechten Container zu füllen. Hier platzieren wir die Statusleiste oder das Eingabefeld für den Dateinamen.
|
||||
|
||||
Gesteuert wird dieses Verhalten durch diese Zeilen:
|
||||
```python
|
||||
# Die drei Container werden im Haupt-Frame platziert
|
||||
left_container.grid(row=0, column=0, sticky='w')
|
||||
center_container.grid(row=0, column=1, sticky='ew')
|
||||
right_container.grid(row=0, column=2, sticky='e')
|
||||
|
||||
# Dem Grid wird gesagt, dass nur die mittlere Spalte (1) wachsen soll
|
||||
self.action_status_frame.grid_columnconfigure(1, weight=1)
|
||||
```
|
||||
|
||||
### Die zwei "Zeilen": Widgets in den Containern
|
||||
|
||||
Innerhalb dieser drei Spalten-Container organisieren wir die Widgets in zwei logischen Zeilen.
|
||||
|
||||
- **Obere Zeile**: Dies ist meist ein einzelnes, breites Widget.
|
||||
- Im **Open-Modus**: Die `status_bar` (oder das `search_entry`).
|
||||
- Im **Save-Modus**: Das `filename_entry`.
|
||||
Diese werden direkt in den `center_container` gepackt und füllen dessen Breite aus.
|
||||
|
||||
- **Untere Zeile**: Hier liegen die meisten Aktions-Widgets (Buttons, Combobox). Um die horizontale Anordnung innerhalb der drei Spalten beizubehalten, wird für diese Zeile **in jeden Haupt-Container ein weiterer kleiner Frame** gepackt:
|
||||
- `row2_left` (im `left_container`)
|
||||
- `row2_center` (im `center_container`)
|
||||
- `row2_right` (im `right_container`)
|
||||
|
||||
### Platzierung der Widgets: Wer kommt wohin?
|
||||
|
||||
Jetzt wird nur noch entschieden, welches Widget in welchen Container kommt.
|
||||
|
||||
---
|
||||
|
||||
#### **Im `Open-Modus`**
|
||||
|
||||
- **`status_bar` / `search_entry`**: Immer in der oberen Zeile des `center_container`.
|
||||
|
||||
- **Wenn `button_box_pos == 'left'` (Buttons Links):**
|
||||
- `left_container`: Enthält `open_button` und `cancel_button`.
|
||||
- `center_container`: Enthält `filter_combobox` und `search_status_label`.
|
||||
- `right_container`: Enthält den `settings_button`.
|
||||
|
||||
- **Wenn `button_box_pos == 'right'` (Buttons Rechts):**
|
||||
- `left_container`: Ist leer.
|
||||
- `center_container`: Enthält `search_status_label` (linksbündig darin) und `filter_combobox` (rechtsbündig darin).
|
||||
- `right_container`: Enthält `open_button`, `cancel_button` und `settings_button`.
|
||||
|
||||
---
|
||||
#### **Im `Save-Modus`**
|
||||
|
||||
- **`filename_entry`**: Immer in der oberen Zeile des `center_container`.
|
||||
|
||||
- **Wenn `button_box_pos == 'left'` (Buttons Links):**
|
||||
- `left_container`: Enthält `save_button`, `cancel_button` und den `trash_button`.
|
||||
- `center_container`: Enthält `filter_combobox` und `search_status_label`.
|
||||
- `right_container`: Enthält den `settings_button`.
|
||||
|
||||
- **Wenn `button_box_pos == 'right'` (Buttons Rechts):**
|
||||
- `left_container`: Enthält nur den `trash_button`.
|
||||
- `center_container`: Enthält `search_status_label` (linksbündig) und `filter_combobox` (rechtsbündig).
|
||||
- `right_container`: Enthält `save_button`, `cancel_button` und `settings_button`.
|
||||
|
||||
---
|
||||
|
||||
**Der entscheidende Vorteil ist:** Wenn Sie jetzt ein Widget verschieben oder hinzufügen wollen, müssen Sie nur noch entscheiden, in welchen der drei Haupt-Container (`left`, `center`, `right`) und in welche "Zeile" (direkt oder in den `row2`-Frame) es gehört. Das komplexe Jonglieren mit Spalten- und Zeilenindizes im Grid entfällt vollständig.
|
||||
|
||||
Ich hoffe, diese Erklärung macht die neue Struktur klarer und die Wartung für Sie einfacher.
|
Reference in New Issue
Block a user