diff --git a/__pycache__/cfd_app_config.cpython-312.pyc b/__pycache__/cfd_app_config.cpython-312.pyc new file mode 100644 index 0000000..5d59b46 Binary files /dev/null and b/__pycache__/cfd_app_config.cpython-312.pyc differ diff --git a/__pycache__/cfd_ui_setup.cpython-312.pyc b/__pycache__/cfd_ui_setup.cpython-312.pyc new file mode 100644 index 0000000..c1acd7c Binary files /dev/null and b/__pycache__/cfd_ui_setup.cpython-312.pyc differ diff --git a/__pycache__/custom_file_dialog.cpython-312.pyc b/__pycache__/custom_file_dialog.cpython-312.pyc new file mode 100644 index 0000000..b70a49b Binary files /dev/null and b/__pycache__/custom_file_dialog.cpython-312.pyc differ diff --git a/custom_file_dialog.py b/custom_file_dialog.py index 45a6bb1..75389e7 100644 --- a/custom_file_dialog.py +++ b/custom_file_dialog.py @@ -56,6 +56,8 @@ class CustomFileDialog(tk.Toplevel): self.search_results = [] # Store search results self.search_mode = False # Track if in search mode self.original_path_text = "" # Store original path text + self.items_to_load_per_batch = 250 + self.item_path_map = {} self.icon_manager = IconManager() self.style_manager = StyleManager(self) @@ -508,7 +510,42 @@ class CustomFileDialog(tk.Toplevel): except (PermissionError, FileNotFoundError): return None + def _get_item_path_from_widget(self, widget): + # Traverse up the widget hierarchy to find the item_frame + while widget and not hasattr(widget, 'item_path'): + widget = widget.master + return getattr(widget, 'item_path', None) + + def _handle_icon_click(self, event): + item_path = self._get_item_path_from_widget(event.widget) + if item_path: + item_frame = event.widget + while not hasattr(item_frame, 'item_path'): + item_frame = item_frame.master + self.on_item_select(item_path, item_frame) + + def _handle_icon_double_click(self, event): + item_path = self._get_item_path_from_widget(event.widget) + if item_path: + self.on_item_double_click(item_path) + + def _handle_icon_context_menu(self, event): + item_path = self._get_item_path_from_widget(event.widget) + if item_path: + self._show_context_menu(event, item_path) + + def _handle_icon_rename_request(self, event): + item_path = self._get_item_path_from_widget(event.widget) + if item_path: + item_frame = event.widget + while not hasattr(item_frame, 'item_path'): + item_frame = item_frame.master + self.on_rename_request(event, item_path, item_frame) + def populate_icon_view(self, item_to_rename=None, item_to_select=None): + self.all_items, error, warning = self._get_sorted_items() + self.currently_loaded_count = 0 + canvas = tk.Canvas(self.widget_manager.file_list_frame, highlightthickness=0, bg=self.style_manager.icon_bg_color) v_scrollbar = ttk.Scrollbar( @@ -521,33 +558,42 @@ class CustomFileDialog(tk.Toplevel): scrollregion=canvas.bbox("all"))) def _on_mouse_wheel(event): - # Determine the scroll direction and magnitude - if event.num == 4: # Scroll up on Linux - delta = -1 - elif event.num == 5: # Scroll down on Linux - delta = 1 - else: # MouseWheel event for Windows/macOS - delta = -1 * int(event.delta / 120) + if event.num == 4: delta = -1 + elif event.num == 5: delta = 1 + else: delta = -1 * int(event.delta / 120) canvas.yview_scroll(delta, "units") + # Check if scrolled to the bottom and if there are more items to load + if self.currently_loaded_count < len(self.all_items) and canvas.yview()[1] > 0.9: + self._load_more_items_icon_view(container_frame) - # Bind mouse wheel events to the canvas and the container frame for widget in [canvas, container_frame]: widget.bind("", _on_mouse_wheel) widget.bind("", _on_mouse_wheel) widget.bind("", _on_mouse_wheel) - items, error, warning = self._get_sorted_items() if warning: self.widget_manager.status_bar.config(text=warning) if error: ttk.Label(container_frame, text=error).pack(pady=20) return + self._load_more_items_icon_view(container_frame, item_to_rename, item_to_select) + + def _load_more_items_icon_view(self, container, item_to_rename=None, item_to_select=None): + start_index = self.currently_loaded_count + end_index = min(len(self.all_items), start_index + self.items_to_load_per_batch) + + if start_index >= end_index: return # All items loaded + item_width, item_height = 125, 100 frame_width = self.widget_manager.file_list_frame.winfo_width() col_count = max(1, frame_width // item_width - 1) - row, col = 0, 0 - for name in items: + + row = start_index // col_count + col = start_index % col_count + + for i in range(start_index, end_index): + name = self.all_items[i] if not self.show_hidden_files.get() and name.startswith('.'): continue path = os.path.join(self.current_dir, name) @@ -556,65 +602,54 @@ class CustomFileDialog(tk.Toplevel): continue item_frame = ttk.Frame( - container_frame, width=item_width, height=item_height, style="Item.TFrame") + container, width=item_width, height=item_height, style="Item.TFrame") item_frame.grid(row=row, column=col, padx=5, ipadx=25, pady=5) item_frame.grid_propagate(False) if name == item_to_rename: self.start_rename(item_frame, path) else: - icon = self.icon_manager.get_icon('folder_large') if is_dir else self.get_file_icon( - name, 'large') - icon_label = ttk.Label( - item_frame, image=icon, style="Icon.TLabel") + icon = self.icon_manager.get_icon('folder_large') if is_dir else self.get_file_icon(name, 'large') + icon_label = ttk.Label(item_frame, image=icon, style="Icon.TLabel") icon_label.pack(pady=(10, 5)) - name_label = ttk.Label(item_frame, text=self.shorten_text( - name, 14), anchor="center", style="Item.TLabel") + name_label = ttk.Label(item_frame, text=self.shorten_text(name, 14), anchor="center", style="Item.TLabel") name_label.pack(fill="x", expand=True) tooltip_text = name - if is_dir: + if is_dir and len(self.all_items) < 500: content_count = self._get_folder_content_count(path) if content_count is not None: tooltip_text += f"\n({content_count} Einträge)" Tooltip(item_frame, tooltip_text) - # Bind events to all individual widgets so scrolling works everywhere for widget in [item_frame, icon_label, name_label]: - widget.bind("", lambda e, - p=path: self.on_item_double_click(p)) - widget.bind("", lambda e, p=path, - f=item_frame: self.on_item_select(p, f)) + widget.bind("", lambda e, p=path: self.on_item_double_click(p)) + widget.bind("", lambda e, p=path, f=item_frame: self.on_item_select(p, f)) widget.bind("", lambda e, p=path: self._show_context_menu(e, p)) - widget.bind("", _on_mouse_wheel) - widget.bind("", _on_mouse_wheel) - widget.bind("", _on_mouse_wheel) - widget.bind("", lambda e, p=path, - f=item_frame: self.on_rename_request(e, p, f)) + widget.bind("", lambda e, p=path, f=item_frame: self.on_rename_request(e, p, f)) if name == item_to_select: self.on_item_select(path, item_frame) - canvas.yview_moveto(row / max(1, (len(items) // col_count))) col = (col + 1) % col_count - if col == 0: - row += 1 + if col == 0: row += 1 + + self.currently_loaded_count = end_index def populate_list_view(self, item_to_rename=None, item_to_select=None): + self.all_items, error, warning = self._get_sorted_items() + self.currently_loaded_count = 0 + tree_frame = ttk.Frame(self.widget_manager.file_list_frame) tree_frame.pack(fill='both', expand=True) tree_frame.grid_rowconfigure(0, weight=1) tree_frame.grid_columnconfigure(0, weight=1) columns = ("size", "type", "modified") - self.tree = ttk.Treeview( - tree_frame, columns=columns, show="tree headings") + self.tree = ttk.Treeview(tree_frame, columns=columns, show="tree headings") - # Tree Column (#0) self.tree.heading("#0", text="Name", anchor="w") self.tree.column("#0", anchor="w", width=250, stretch=True) - - # Other Columns self.tree.heading("size", text="Größe", anchor="e") self.tree.column("size", anchor="e", width=120, stretch=False) self.tree.heading("type", text="Typ", anchor="w") @@ -622,30 +657,43 @@ class CustomFileDialog(tk.Toplevel): self.tree.heading("modified", text="Geändert am", anchor="w") self.tree.column("modified", anchor="w", width=160, stretch=False) - v_scrollbar = ttk.Scrollbar( - tree_frame, orient="vertical", command=self.tree.yview) - h_scrollbar = ttk.Scrollbar( - tree_frame, orient="horizontal", command=self.tree.xview) - self.tree.configure(yscrollcommand=v_scrollbar.set, - xscrollcommand=h_scrollbar.set) + v_scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview) + h_scrollbar = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.tree.xview) + self.tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set) self.tree.grid(row=0, column=0, sticky='nsew') v_scrollbar.grid(row=0, column=1, sticky='ns') h_scrollbar.grid(row=1, column=0, sticky='ew') + def _on_scroll(*args): + # Check if scrolled to the bottom and if there are more items to load + if self.currently_loaded_count < len(self.all_items) and self.tree.yview()[1] > 0.9: + self._load_more_items_list_view(item_to_rename, item_to_select) + v_scrollbar.set(*args) + self.tree.configure(yscrollcommand=_on_scroll) + self.tree.bind("", self.on_list_double_click) self.tree.bind("<>", self.on_list_select) self.tree.bind("", self.on_rename_request) self.tree.bind("", self.on_list_context_menu) - items, error, warning = self._get_sorted_items() if warning: self.widget_manager.status_bar.config(text=warning) if error: self.tree.insert("", "end", text=error, values=()) return - for name in items: + self._load_more_items_list_view(item_to_rename, item_to_select) + + def _load_more_items_list_view(self, item_to_rename=None, item_to_select=None): + start_index = self.currently_loaded_count + end_index = min(len(self.all_items), start_index + self.items_to_load_per_batch) + + if start_index >= end_index: + return # All items loaded + + for i in range(start_index, end_index): + name = self.all_items[i] if not self.show_hidden_files.get() and name.startswith('.'): continue path = os.path.join(self.current_dir, name) @@ -654,28 +702,25 @@ class CustomFileDialog(tk.Toplevel): continue try: stat = os.stat(path) - modified_time = datetime.fromtimestamp( - stat.st_mtime).strftime('%d.%m.%Y %H:%M') + modified_time = datetime.fromtimestamp(stat.st_mtime).strftime('%d.%m.%Y %H:%M') if is_dir: - icon, file_type, size = self.icon_manager.get_icon( - 'folder_small'), "Ordner", "" + icon, file_type, size = self.icon_manager.get_icon('folder_small'), "Ordner", "" else: - icon, file_type, size = self.get_file_icon( - name, 'small'), "Datei", self._format_size(stat.st_size) - item_id = self.tree.insert("", "end", text=f" {name}", image=icon, values=( - size, file_type, modified_time)) + icon, file_type, size = self.get_file_icon(name, 'small'), "Datei", self._format_size(stat.st_size) + item_id = self.tree.insert("", "end", text=f" {name}", image=icon, values=(size, file_type, modified_time)) if name == item_to_rename: self.tree.selection_set(item_id) self.tree.focus(item_id) - self.tree.see(item_id) # Scroll to the item + self.tree.see(item_id) self.start_rename(item_id, path) elif name == item_to_select: self.tree.selection_set(item_id) self.tree.focus(item_id) - self.tree.see(item_id) # Scroll to the item - + self.tree.see(item_id) except (FileNotFoundError, PermissionError): continue + + self.currently_loaded_count = end_index def on_item_select(self, path, item_frame): if hasattr(self, 'selected_item_frame') and self.selected_item_frame.winfo_exists():