commit 55
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
"""App configuration for Custom File Dialog"""
|
"""App configuration for Custom File Dialog"""
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os
|
import os
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
@@ -13,8 +14,7 @@ class AppConfig:
|
|||||||
This class serves as a singleton-like container for all global configuration data,
|
This class serves as a singleton-like container for all global configuration data,
|
||||||
including paths, UI settings, localization, versioning, and system-specific resources.
|
including paths, UI settings, localization, versioning, and system-specific resources.
|
||||||
It ensures that required directories, files, and services are created and configured
|
It ensures that required directories, files, and services are created and configured
|
||||||
before the application starts. Additionally, it provides tools for managing translations,
|
before the application starts. Additionally, it provides tools for managing translations.
|
||||||
default settings, and autostart functionality to maintain a consistent user experience.
|
|
||||||
|
|
||||||
Key Responsibilities:
|
Key Responsibilities:
|
||||||
- Centralizes all configuration values (paths, UI preferences, localization).
|
- Centralizes all configuration values (paths, UI preferences, localization).
|
||||||
@@ -35,15 +35,6 @@ class AppConfig:
|
|||||||
BASE_DIR: Path = Path.home()
|
BASE_DIR: Path = Path.home()
|
||||||
CONFIG_DIR: Path = BASE_DIR / ".config/cfiledialog"
|
CONFIG_DIR: Path = BASE_DIR / ".config/cfiledialog"
|
||||||
|
|
||||||
# Configuration files
|
|
||||||
SETTINGS_FILE: Path = CONFIG_DIR / "settings"
|
|
||||||
DEFAULT_SETTINGS: Dict[str, str] = {
|
|
||||||
"# Configuration": "on",
|
|
||||||
"# Theme": "dark",
|
|
||||||
"# Tooltips": True,
|
|
||||||
"# Autostart": "off",
|
|
||||||
}
|
|
||||||
|
|
||||||
# UI configuration
|
# UI configuration
|
||||||
UI_CONFIG: Dict[str, Any] = {
|
UI_CONFIG: Dict[str, Any] = {
|
||||||
"window_size": (1050, 850),
|
"window_size": (1050, 850),
|
||||||
@@ -53,23 +44,6 @@ class AppConfig:
|
|||||||
"resizable_window": (True, True),
|
"resizable_window": (True, True),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def ensure_directories(cls) -> None:
|
|
||||||
"""Ensures that all required directories exist"""
|
|
||||||
if not cls.CONFIG_DIR.exists():
|
|
||||||
cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_default_settings(cls) -> None:
|
|
||||||
"""Creates default settings if they don't exist"""
|
|
||||||
if not cls.SETTINGS_FILE.exists():
|
|
||||||
content = "\n".join(
|
|
||||||
f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items()
|
|
||||||
)
|
|
||||||
cls.SETTINGS_FILE.write_text(content)
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
# here is initializing the class for translation strings
|
# here is initializing the class for translation strings
|
||||||
_ = Translate.setup_translations("custom_file_fialog")
|
_ = Translate.setup_translations("custom_file_fialog")
|
||||||
@@ -85,7 +59,10 @@ class CfdConfigManager:
|
|||||||
"search_icon_pos": "left", # 'left' or 'right'
|
"search_icon_pos": "left", # 'left' or 'right'
|
||||||
"button_box_pos": "left", # 'left' or 'right'
|
"button_box_pos": "left", # 'left' or 'right'
|
||||||
"window_size_preset": "1050x850", # e.g., "1050x850"
|
"window_size_preset": "1050x850", # e.g., "1050x850"
|
||||||
"default_view_mode": "icons" # 'icons' or 'list'
|
"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
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
203
cfd_ui_setup.py
203
cfd_ui_setup.py
@@ -104,7 +104,8 @@ class StyleManager:
|
|||||||
style.map("Bottom.TButton.Borderless.Round",
|
style.map("Bottom.TButton.Borderless.Round",
|
||||||
background=[('active', self.hover_extrastyle)])
|
background=[('active', self.hover_extrastyle)])
|
||||||
style.layout("Bottom.TButton.Borderless.Round",
|
style.layout("Bottom.TButton.Borderless.Round",
|
||||||
style.layout("Header.TButton.Borderless.Round"))
|
style.layout("Header.TButton.Borderless.Round")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WidgetManager:
|
class WidgetManager:
|
||||||
@@ -125,12 +126,12 @@ class WidgetManager:
|
|||||||
top_bar = ttk.Frame(
|
top_bar = ttk.Frame(
|
||||||
main_frame, style='Accent.TFrame', padding=(0, 5, 0, 5))
|
main_frame, style='Accent.TFrame', padding=(0, 5, 0, 5))
|
||||||
top_bar.grid(row=0, column=0, columnspan=2, sticky="ew")
|
top_bar.grid(row=0, column=0, columnspan=2, sticky="ew")
|
||||||
# Make path entry column expandable
|
|
||||||
top_bar.grid_columnconfigure(2, weight=1)
|
|
||||||
|
|
||||||
# Left navigation buttons
|
# Left navigation buttons
|
||||||
left_nav_container = ttk.Frame(top_bar, style='Accent.TFrame')
|
left_nav_container = ttk.Frame(top_bar, style='Accent.TFrame')
|
||||||
left_nav_container.grid(row=0, column=0, sticky="w")
|
left_nav_container.grid(row=0, column=0, sticky="w")
|
||||||
|
# Prevent this container from changing size
|
||||||
|
left_nav_container.grid_propagate(False)
|
||||||
|
|
||||||
self.back_button = ttk.Button(left_nav_container, image=self.dialog.icon_manager.get_icon(
|
self.back_button = ttk.Button(left_nav_container, image=self.dialog.icon_manager.get_icon(
|
||||||
'back'), command=self.dialog.go_back, state=tk.DISABLED, style="Header.TButton.Borderless.Round")
|
'back'), command=self.dialog.go_back, state=tk.DISABLED, style="Header.TButton.Borderless.Round")
|
||||||
@@ -142,85 +143,105 @@ class WidgetManager:
|
|||||||
self.forward_button.pack(side="left", padx=5)
|
self.forward_button.pack(side="left", padx=5)
|
||||||
Tooltip(self.forward_button, "Vorwärts")
|
Tooltip(self.forward_button, "Vorwärts")
|
||||||
|
|
||||||
|
self.up_button = ttk.Button(left_nav_container, image=self.dialog.icon_manager.get_icon(
|
||||||
|
'up'), command=self.dialog.go_up_level, style="Header.TButton.Borderless.Round")
|
||||||
|
self.up_button.pack(side="left", padx=5)
|
||||||
|
Tooltip(self.up_button, "Eine Ebene höher")
|
||||||
|
|
||||||
self.home_button = ttk.Button(left_nav_container, image=self.dialog.icon_manager.get_icon(
|
self.home_button = ttk.Button(left_nav_container, image=self.dialog.icon_manager.get_icon(
|
||||||
'home'), command=lambda: self.dialog.navigate_to(os.path.expanduser("~")), style="Header.TButton.Borderless.Round")
|
'home'), command=lambda: self.dialog.navigate_to(os.path.expanduser("~")), style="Header.TButton.Borderless.Round")
|
||||||
self.home_button.pack(side="left", padx=(5, 10))
|
self.home_button.pack(side="left", padx=(5, 10))
|
||||||
Tooltip(self.home_button, "Home")
|
Tooltip(self.home_button, "Home")
|
||||||
|
|
||||||
# Search button (left position)
|
# Path and search widgets container
|
||||||
search_icon_pos = self.settings.get("search_icon_pos", "left")
|
path_search_container = ttk.Frame(top_bar, style='Accent.TFrame')
|
||||||
if search_icon_pos == 'left':
|
path_search_container.grid(row=0, column=1, sticky="ew")
|
||||||
search_container_left = ttk.Frame(top_bar, style='Accent.TFrame')
|
|
||||||
search_container_left.grid(row=0, column=1, sticky="w")
|
|
||||||
self.search_button = ttk.Button(search_container_left, 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", padx=5)
|
|
||||||
Tooltip(self.search_button, "Suchen")
|
|
||||||
|
|
||||||
self.recursive_search = tk.BooleanVar(value=True)
|
# Right-side controls container
|
||||||
self.recursive_button = ttk.Button(search_container_left, image=self.dialog.icon_manager.get_icon(
|
right_controls_container = ttk.Frame(top_bar, style='Accent.TFrame')
|
||||||
'recursive_small'), command=self.dialog.toggle_recursive_search, style="Header.TButton.Active.Round")
|
right_controls_container.grid(row=0, column=2, sticky="e")
|
||||||
self.recursive_button.pack(side="left", padx=2)
|
|
||||||
self.recursive_button.pack_forget() # Initially hidden
|
# Make the middle column (path_search_container) expand
|
||||||
Tooltip(self.recursive_button, "Rekursive Suche ein/ausschalten")
|
top_bar.grid_columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
search_icon_pos = self.settings.get("search_icon_pos", "left")
|
||||||
|
self.recursive_search = tk.BooleanVar(value=True)
|
||||||
|
|
||||||
# Path entry
|
# Path entry
|
||||||
self.path_entry = ttk.Entry(top_bar)
|
self.path_entry = ttk.Entry(path_search_container)
|
||||||
self.path_entry.grid(row=0, column=2, sticky="ew")
|
|
||||||
self.path_entry.bind(
|
self.path_entry.bind(
|
||||||
"<Return>", lambda e: self.dialog.navigate_to(self.path_entry.get()))
|
"<Return>", lambda e: self.dialog.navigate_to(self.path_entry.get()))
|
||||||
|
|
||||||
# Right-side controls
|
# Function to create search widgets
|
||||||
right_controls_container = ttk.Frame(top_bar, style='Accent.TFrame')
|
def create_search_widgets(parent_frame):
|
||||||
right_controls_container.grid(row=0, column=3, sticky="e")
|
container = ttk.Frame(parent_frame, style='Accent.TFrame')
|
||||||
|
self.search_button = ttk.Button(container, image=self.dialog.icon_manager.get_icon(
|
||||||
# Search button (right position)
|
|
||||||
if search_icon_pos == 'right':
|
|
||||||
search_container_right = ttk.Frame(
|
|
||||||
right_controls_container, style='Accent.TFrame')
|
|
||||||
search_container_right.pack(side="left", padx=5)
|
|
||||||
self.search_button = ttk.Button(search_container_right, image=self.dialog.icon_manager.get_icon(
|
|
||||||
'search_small'), command=self.dialog.toggle_search_mode, style="Header.TButton.Borderless.Round")
|
'search_small'), command=self.dialog.toggle_search_mode, style="Header.TButton.Borderless.Round")
|
||||||
self.search_button.pack(side="left")
|
self.search_button.pack(side="left")
|
||||||
Tooltip(self.search_button, "Suchen")
|
Tooltip(self.search_button, "Suchen")
|
||||||
|
|
||||||
self.recursive_search = tk.BooleanVar(value=True)
|
self.recursive_button = ttk.Button(container, image=self.dialog.icon_manager.get_icon(
|
||||||
self.recursive_button = ttk.Button(search_container_right, image=self.dialog.icon_manager.get_icon(
|
|
||||||
'recursive_small'), command=self.dialog.toggle_recursive_search, style="Header.TButton.Active.Round")
|
'recursive_small'), command=self.dialog.toggle_recursive_search, style="Header.TButton.Active.Round")
|
||||||
self.recursive_button.pack(side="left", padx=2)
|
self.recursive_button.pack(side="left", padx=2)
|
||||||
self.recursive_button.pack_forget() # Initially hidden
|
self.recursive_button.pack_forget() # Initially hidden
|
||||||
Tooltip(self.recursive_button, "Rekursive Suche ein/ausschalten")
|
Tooltip(self.recursive_button, "Rekursive Suche ein/ausschalten")
|
||||||
|
return container
|
||||||
|
|
||||||
# Other right-side buttons
|
# Place search and path entry based on settings
|
||||||
self.new_folder_button = ttk.Button(right_controls_container, image=self.dialog.icon_manager.get_icon(
|
if search_icon_pos == 'left':
|
||||||
|
path_search_container.grid_columnconfigure(1, weight=1)
|
||||||
|
search_container = create_search_widgets(path_search_container)
|
||||||
|
search_container.grid(row=0, column=0, sticky="w", padx=(0, 5))
|
||||||
|
self.path_entry.grid(row=0, column=1, sticky="ew")
|
||||||
|
else: # right
|
||||||
|
path_search_container.grid_columnconfigure(0, weight=1)
|
||||||
|
search_container = create_search_widgets(path_search_container)
|
||||||
|
search_container.grid(row=0, column=1, sticky="e", padx=(5, 0))
|
||||||
|
self.path_entry.grid(row=0, column=0, sticky="ew")
|
||||||
|
|
||||||
|
# --- Responsive Buttons ---
|
||||||
|
self.responsive_buttons_container = ttk.Frame(
|
||||||
|
right_controls_container, style='Accent.TFrame')
|
||||||
|
self.responsive_buttons_container.pack(side="left")
|
||||||
|
|
||||||
|
self.new_folder_button = ttk.Button(self.responsive_buttons_container, image=self.dialog.icon_manager.get_icon(
|
||||||
'new_folder_small'), command=self.dialog.create_new_folder, style="Header.TButton.Borderless.Round")
|
'new_folder_small'), command=self.dialog.create_new_folder, style="Header.TButton.Borderless.Round")
|
||||||
self.new_folder_button.pack(side="left", padx=5)
|
self.new_folder_button.pack(side="left", padx=5)
|
||||||
Tooltip(self.new_folder_button, "Neuen Ordner erstellen")
|
Tooltip(self.new_folder_button, "Neuen Ordner erstellen")
|
||||||
|
|
||||||
self.new_file_button = ttk.Button(right_controls_container, image=self.dialog.icon_manager.get_icon(
|
self.new_file_button = ttk.Button(self.responsive_buttons_container, image=self.dialog.icon_manager.get_icon(
|
||||||
'new_document_small'), command=self.dialog.create_new_file, style="Header.TButton.Borderless.Round")
|
'new_document_small'), command=self.dialog.create_new_file, style="Header.TButton.Borderless.Round")
|
||||||
self.new_file_button.pack(side="left", padx=5)
|
self.new_file_button.pack(side="left", padx=5)
|
||||||
Tooltip(self.new_file_button, "Neues Dokument erstellen")
|
Tooltip(self.new_file_button, "Neues Dokument erstellen")
|
||||||
|
|
||||||
view_switch = ttk.Frame(right_controls_container,
|
if self.dialog.dialog_mode == "open":
|
||||||
padding=(5, 0), style='Accent.TFrame')
|
self.new_folder_button.config(state=tk.DISABLED)
|
||||||
view_switch.pack(side="left")
|
self.new_file_button.config(state=tk.DISABLED)
|
||||||
|
|
||||||
self.icon_view_button = ttk.Button(view_switch, image=self.dialog.icon_manager.get_icon(
|
self.view_switch = ttk.Frame(self.responsive_buttons_container,
|
||||||
|
padding=(5, 0), style='Accent.TFrame')
|
||||||
|
self.view_switch.pack(side="left")
|
||||||
|
|
||||||
|
self.icon_view_button = ttk.Button(self.view_switch, image=self.dialog.icon_manager.get_icon(
|
||||||
'icon_view'), command=self.dialog.set_icon_view, style="Header.TButton.Active.Round")
|
'icon_view'), command=self.dialog.set_icon_view, style="Header.TButton.Active.Round")
|
||||||
self.icon_view_button.pack(side="left", padx=5)
|
self.icon_view_button.pack(side="left", padx=5)
|
||||||
Tooltip(self.icon_view_button, "Kachelansicht")
|
Tooltip(self.icon_view_button, "Kachelansicht")
|
||||||
|
|
||||||
self.list_view_button = ttk.Button(view_switch, image=self.dialog.icon_manager.get_icon(
|
self.list_view_button = ttk.Button(self.view_switch, image=self.dialog.icon_manager.get_icon(
|
||||||
'list_view'), command=self.dialog.set_list_view, style="Header.TButton.Borderless.Round")
|
'list_view'), command=self.dialog.set_list_view, style="Header.TButton.Borderless.Round")
|
||||||
self.list_view_button.pack(side="left")
|
self.list_view_button.pack(side="left")
|
||||||
Tooltip(self.list_view_button, "Listenansicht")
|
Tooltip(self.list_view_button, "Listenansicht")
|
||||||
|
|
||||||
self.hidden_files_button = ttk.Button(right_controls_container, image=self.dialog.icon_manager.get_icon(
|
self.hidden_files_button = ttk.Button(self.responsive_buttons_container, image=self.dialog.icon_manager.get_icon(
|
||||||
'hide'), command=self.dialog.toggle_hidden_files, style="Header.TButton.Borderless.Round")
|
'hide'), command=self.dialog.toggle_hidden_files, style="Header.TButton.Borderless.Round")
|
||||||
self.hidden_files_button.pack(side="left", padx=10)
|
self.hidden_files_button.pack(side="left", padx=10)
|
||||||
Tooltip(self.hidden_files_button, "Versteckte Dateien anzeigen")
|
Tooltip(self.hidden_files_button, "Versteckte Dateien anzeigen")
|
||||||
|
|
||||||
|
# "More" button for responsive UI
|
||||||
|
self.more_button = ttk.Button(right_controls_container, text="...",
|
||||||
|
command=self.dialog.show_more_menu, style="Header.TButton.Borderless.Round", width=3)
|
||||||
|
# self.more_button is managed by _handle_responsive_buttons
|
||||||
|
|
||||||
# Horizontal separator
|
# Horizontal separator
|
||||||
separator_color = "#000000" if self.style_manager.is_dark else "#9c9c9c"
|
separator_color = "#000000" if self.style_manager.is_dark else "#9c9c9c"
|
||||||
tk.Frame(main_frame, height=1, bg=separator_color).grid(
|
tk.Frame(main_frame, height=1, bg=separator_color).grid(
|
||||||
@@ -374,71 +395,67 @@ class WidgetManager:
|
|||||||
content_frame.grid_columnconfigure(0, weight=1)
|
content_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
self.file_list_frame = ttk.Frame(
|
self.file_list_frame = ttk.Frame(
|
||||||
content_frame, style="AccentBottom.TFrame")
|
# Use Content.TFrame for consistent bg color
|
||||||
|
content_frame, style="Content.TFrame")
|
||||||
self.file_list_frame.grid(row=0, column=0, sticky="nsew")
|
self.file_list_frame.grid(row=0, column=0, sticky="nsew")
|
||||||
self.dialog.bind("<Configure>", self.dialog.on_window_resize)
|
self.dialog.bind("<Configure>", self.dialog.on_window_resize)
|
||||||
|
|
||||||
bottom_controls_frame = ttk.Frame(
|
# This frame will contain the action buttons and status bar
|
||||||
|
self.action_status_frame = ttk.Frame(
|
||||||
content_frame, style="AccentBottom.TFrame")
|
content_frame, style="AccentBottom.TFrame")
|
||||||
bottom_controls_frame.grid(row=1, column=0, sticky="ew", pady=(5, 0))
|
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")
|
button_box_pos = self.settings.get("button_box_pos", "left")
|
||||||
action_buttons_col = 0 if button_box_pos == 'left' else 2
|
|
||||||
action_buttons_sticky = "w" if button_box_pos == 'left' else "e"
|
|
||||||
|
|
||||||
if self.dialog.dialog_mode == "save":
|
# Configure columns for the action_status_frame
|
||||||
# Give most of the weight to the action buttons frame (which contains the entry)
|
if button_box_pos == 'left':
|
||||||
bottom_controls_frame.grid_columnconfigure(action_buttons_col, weight=10)
|
self.action_status_frame.grid_columnconfigure(1, weight=1)
|
||||||
bottom_controls_frame.grid_columnconfigure(1, weight=1) # status_bar is in col 1
|
|
||||||
else:
|
else:
|
||||||
# Original behavior for open mode
|
self.action_status_frame.grid_columnconfigure(1, weight=1)
|
||||||
bottom_controls_frame.grid_columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
action_buttons_frame = ttk.Frame(
|
|
||||||
bottom_controls_frame, style="AccentBottom.TFrame")
|
|
||||||
action_buttons_frame.grid(
|
|
||||||
row=0, column=action_buttons_col, rowspan=2, sticky="nsew", pady=(5, 10))
|
|
||||||
|
|
||||||
|
# Status bar will be placed inside the action_status_frame
|
||||||
self.status_bar = ttk.Label(
|
self.status_bar = ttk.Label(
|
||||||
bottom_controls_frame, text="", anchor="w", style="AccentBottom.TLabel")
|
self.action_status_frame, text="", anchor="w", style="AccentBottom.TLabel")
|
||||||
status_bar_col = 1 if button_box_pos == 'left' else 1
|
|
||||||
status_bar_sticky = "w" if button_box_pos == 'left' else "e"
|
|
||||||
self.status_bar.grid(row=0, column=status_bar_col,
|
|
||||||
sticky=status_bar_sticky, padx=10)
|
|
||||||
|
|
||||||
self.settings_button = ttk.Button(bottom_controls_frame, image=self.dialog.icon_manager.get_icon(
|
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")
|
'settings-2_small'), command=self.dialog.open_settings_dialog, style="Bottom.TButton.Borderless.Round")
|
||||||
self.settings_button.grid(
|
|
||||||
row=0, column=3, sticky="ne", padx=(0, 5), pady=(2, 0))
|
self.trash_button = ttk.Button(self.action_status_frame, image=self.dialog.icon_manager.get_icon(
|
||||||
Tooltip(self.settings_button, "Einstellungen")
|
'trash_small2'), command=self.dialog.delete_selected_item, style="Bottom.TButton.Borderless.Round")
|
||||||
|
Tooltip(self.trash_button, "Ausgewähltes Element löschen/verschieben")
|
||||||
|
|
||||||
if self.dialog.dialog_mode == "save":
|
if self.dialog.dialog_mode == "save":
|
||||||
self.filename_entry = ttk.Entry(action_buttons_frame)
|
self.filename_entry = ttk.Entry(self.action_status_frame)
|
||||||
save_button = ttk.Button(
|
save_button = ttk.Button(
|
||||||
action_buttons_frame, text="Speichern", command=self.dialog.on_save)
|
self.action_status_frame, text="Speichern", command=self.dialog.on_save)
|
||||||
cancel_button = ttk.Button(
|
cancel_button = ttk.Button(
|
||||||
action_buttons_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||||
self.filter_combobox = ttk.Combobox(action_buttons_frame, values=[
|
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[
|
||||||
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||||
|
|
||||||
if button_box_pos == 'left':
|
if button_box_pos == 'left':
|
||||||
action_buttons_frame.grid_columnconfigure(1, weight=1)
|
save_button.grid(row=0, column=0, sticky="w", padx=(0, 10))
|
||||||
self.filename_entry.grid(
|
self.filename_entry.grid(
|
||||||
row=0, column=1, sticky="ew", padx=(10, 0))
|
row=0, column=1, sticky="ew", padx=(0, 5))
|
||||||
save_button.grid(row=0, column=0, sticky="e", padx=(10, 5))
|
|
||||||
cancel_button.grid(row=1, column=0, sticky="w",
|
cancel_button.grid(row=1, column=0, sticky="w",
|
||||||
padx=(10, 0), pady=(10, 0))
|
pady=(5, 0), padx=(0, 10))
|
||||||
self.filter_combobox.grid(
|
self.filter_combobox.grid(
|
||||||
row=1, column=1, sticky="w", padx=(10, 0), pady=(10, 0))
|
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))
|
||||||
else: # right
|
else: # right
|
||||||
action_buttons_frame.grid_columnconfigure(0, weight=1)
|
self.trash_button.grid(
|
||||||
save_button.grid(row=0, column=1, sticky="w", padx=(5, 5))
|
row=1, column=0, sticky="sw", padx=(0, 5))
|
||||||
self.filename_entry.grid(
|
self.filename_entry.grid(
|
||||||
row=0, column=0, sticky="ew", padx=(0, 10))
|
row=0, column=1, sticky="ew", padx=(0, 5))
|
||||||
self.filter_combobox.grid(
|
self.filter_combobox.grid(
|
||||||
row=1, column=0, sticky="e", padx=(0, 10), pady=(10, 0))
|
row=1, column=1, sticky="e", pady=(5, 0), padx=(0, 5))
|
||||||
cancel_button.grid(row=1, column=1, sticky="e",
|
save_button.grid(row=0, column=2, sticky="e", padx=5)
|
||||||
padx=(0, 5), pady=(10, 0))
|
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.filter_combobox.bind(
|
self.filter_combobox.bind(
|
||||||
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||||
@@ -446,24 +463,28 @@ class WidgetManager:
|
|||||||
|
|
||||||
else: # Open mode
|
else: # Open mode
|
||||||
open_button = ttk.Button(
|
open_button = ttk.Button(
|
||||||
action_buttons_frame, text="Öffnen", command=self.dialog.on_open)
|
self.action_status_frame, text="Öffnen", command=self.dialog.on_open)
|
||||||
cancel_button = ttk.Button(
|
cancel_button = ttk.Button(
|
||||||
action_buttons_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
self.action_status_frame, text="Abbrechen", command=self.dialog.on_cancel)
|
||||||
self.filter_combobox = ttk.Combobox(action_buttons_frame, values=[
|
self.filter_combobox = ttk.Combobox(self.action_status_frame, values=[
|
||||||
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
ft[0] for ft in self.dialog.filetypes], state="readonly")
|
||||||
|
|
||||||
if button_box_pos == 'left':
|
if button_box_pos == 'left':
|
||||||
open_button.grid(row=0, column=0, sticky="e", padx=(10, 5))
|
open_button.grid(row=0, column=0, sticky="w", padx=(0, 5))
|
||||||
cancel_button.grid(row=1, column=0, sticky="w",
|
self.status_bar.grid(row=0, column=1, sticky="w", padx=5)
|
||||||
padx=(10, 0), pady=(10, 0))
|
cancel_button.grid(row=1, column=0, sticky="w", pady=(5, 0))
|
||||||
self.filter_combobox.grid(
|
self.filter_combobox.grid(
|
||||||
row=1, column=1, sticky="w", padx=(10, 0), pady=(10, 0))
|
row=1, column=1, sticky="w", pady=(5, 0), padx=(5, 0))
|
||||||
|
self.settings_button.grid(row=0, column=2, sticky="e")
|
||||||
|
|
||||||
else: # right
|
else: # right
|
||||||
open_button.grid(row=0, column=1, sticky="w", padx=(5, 5))
|
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",
|
cancel_button.grid(row=1, column=1, sticky="e",
|
||||||
padx=(0, 5), pady=(10, 0))
|
padx=5, pady=(5, 0))
|
||||||
self.filter_combobox.grid(
|
self.filter_combobox.grid(
|
||||||
row=1, column=0, sticky="e", padx=(0, 5), pady=(10, 0))
|
row=1, column=0, sticky="e", pady=(5, 0))
|
||||||
|
self.settings_button.grid(row=0, column=2, sticky="e")
|
||||||
|
|
||||||
self.filter_combobox.bind(
|
self.filter_combobox.bind(
|
||||||
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
"<<ComboboxSelected>>", self.dialog.on_filter_change)
|
||||||
|
@@ -10,15 +10,22 @@ from shared_libs.common_tools import IconManager, Tooltip, ConfigManager, LxTool
|
|||||||
from cfd_app_config import AppConfig, CfdConfigManager
|
from cfd_app_config import AppConfig, CfdConfigManager
|
||||||
from cfd_ui_setup import StyleManager, WidgetManager, get_xdg_user_dir
|
from cfd_ui_setup import StyleManager, WidgetManager, get_xdg_user_dir
|
||||||
|
|
||||||
|
try:
|
||||||
|
import send2trash
|
||||||
|
SEND2TRASH_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
SEND2TRASH_AVAILABLE = False
|
||||||
|
|
||||||
|
|
||||||
class SettingsDialog(tk.Toplevel):
|
class SettingsDialog(tk.Toplevel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent, dialog_mode="save"):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
self.title("Einstellungen")
|
self.title("Einstellungen")
|
||||||
|
|
||||||
self.settings = CfdConfigManager.load()
|
self.settings = CfdConfigManager.load()
|
||||||
|
self.dialog_mode = dialog_mode
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
self.search_icon_pos = tk.StringVar(
|
self.search_icon_pos = tk.StringVar(
|
||||||
@@ -29,6 +36,12 @@ class SettingsDialog(tk.Toplevel):
|
|||||||
value=self.settings.get("window_size_preset", "1050x850"))
|
value=self.settings.get("window_size_preset", "1050x850"))
|
||||||
self.default_view_mode = tk.StringVar(
|
self.default_view_mode = tk.StringVar(
|
||||||
value=self.settings.get("default_view_mode", "icons"))
|
value=self.settings.get("default_view_mode", "icons"))
|
||||||
|
self.search_hidden_files = tk.BooleanVar(
|
||||||
|
value=self.settings.get("search_hidden_files", False))
|
||||||
|
self.use_trash = tk.BooleanVar(
|
||||||
|
value=self.settings.get("use_trash", False))
|
||||||
|
self.confirm_delete = tk.BooleanVar(
|
||||||
|
value=self.settings.get("confirm_delete", False))
|
||||||
|
|
||||||
# --- UI Elements ---
|
# --- UI Elements ---
|
||||||
main_frame = ttk.Frame(self, padding=10)
|
main_frame = ttk.Frame(self, padding=10)
|
||||||
@@ -70,6 +83,39 @@ class SettingsDialog(tk.Toplevel):
|
|||||||
ttk.Radiobutton(view_mode_frame, text="Liste",
|
ttk.Radiobutton(view_mode_frame, text="Liste",
|
||||||
variable=self.default_view_mode, value="list").pack(side="left", padx=5)
|
variable=self.default_view_mode, value="list").pack(side="left", padx=5)
|
||||||
|
|
||||||
|
# Search Hidden Files
|
||||||
|
search_hidden_frame = ttk.LabelFrame(
|
||||||
|
main_frame, text="Sucheinstellungen", padding=10)
|
||||||
|
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")
|
||||||
|
|
||||||
|
# Deletion Settings
|
||||||
|
delete_frame = ttk.LabelFrame(
|
||||||
|
main_frame, text="Löscheinstellungen", padding=10)
|
||||||
|
delete_frame.pack(fill="x", pady=5)
|
||||||
|
|
||||||
|
self.use_trash_checkbutton = ttk.Checkbutton(delete_frame, text="Dateien in den Papierkorb verschieben (empfohlen)",
|
||||||
|
variable=self.use_trash)
|
||||||
|
self.use_trash_checkbutton.pack(anchor="w")
|
||||||
|
|
||||||
|
if not SEND2TRASH_AVAILABLE:
|
||||||
|
self.use_trash_checkbutton.config(state=tk.DISABLED)
|
||||||
|
ttk.Label(delete_frame, text="(send2trash-Bibliothek nicht gefunden)",
|
||||||
|
font=("TkDefaultFont", 9, "italic")).pack(anchor="w", padx=(20, 0))
|
||||||
|
|
||||||
|
self.confirm_delete_checkbutton = ttk.Checkbutton(delete_frame, text="Löschen/Verschieben ohne Bestätigung",
|
||||||
|
variable=self.confirm_delete)
|
||||||
|
self.confirm_delete_checkbutton.pack(anchor="w")
|
||||||
|
|
||||||
|
# Disable deletion options in "open" mode
|
||||||
|
if self.dialog_mode == "open":
|
||||||
|
self.use_trash_checkbutton.config(state=tk.DISABLED)
|
||||||
|
self.confirm_delete_checkbutton.config(state=tk.DISABLED)
|
||||||
|
info_label = ttk.Label(delete_frame, text="(Löschoptionen sind nur im Speichern-Modus verfügbar)",
|
||||||
|
font=("TkDefaultFont", 9, "italic"))
|
||||||
|
info_label.pack(anchor="w", padx=(20, 0))
|
||||||
|
|
||||||
# --- Action Buttons ---
|
# --- Action Buttons ---
|
||||||
button_frame = ttk.Frame(main_frame)
|
button_frame = ttk.Frame(main_frame)
|
||||||
button_frame.pack(fill="x", pady=(10, 0))
|
button_frame.pack(fill="x", pady=(10, 0))
|
||||||
@@ -86,7 +132,10 @@ class SettingsDialog(tk.Toplevel):
|
|||||||
"search_icon_pos": self.search_icon_pos.get(),
|
"search_icon_pos": self.search_icon_pos.get(),
|
||||||
"button_box_pos": self.button_box_pos.get(),
|
"button_box_pos": self.button_box_pos.get(),
|
||||||
"window_size_preset": self.window_size_preset.get(),
|
"window_size_preset": self.window_size_preset.get(),
|
||||||
"default_view_mode": self.default_view_mode.get()
|
"default_view_mode": self.default_view_mode.get(),
|
||||||
|
"search_hidden_files": self.search_hidden_files.get(),
|
||||||
|
"use_trash": self.use_trash.get(),
|
||||||
|
"confirm_delete": self.confirm_delete.get()
|
||||||
}
|
}
|
||||||
CfdConfigManager.save(new_settings)
|
CfdConfigManager.save(new_settings)
|
||||||
self.master.reload_config_and_rebuild_ui()
|
self.master.reload_config_and_rebuild_ui()
|
||||||
@@ -98,6 +147,9 @@ class SettingsDialog(tk.Toplevel):
|
|||||||
self.button_box_pos.set(defaults["button_box_pos"])
|
self.button_box_pos.set(defaults["button_box_pos"])
|
||||||
self.window_size_preset.set(defaults["window_size_preset"])
|
self.window_size_preset.set(defaults["window_size_preset"])
|
||||||
self.default_view_mode.set(defaults["default_view_mode"])
|
self.default_view_mode.set(defaults["default_view_mode"])
|
||||||
|
self.search_hidden_files.set(defaults["search_hidden_files"])
|
||||||
|
self.use_trash.set(defaults["use_trash"])
|
||||||
|
self.confirm_delete.set(defaults["confirm_delete"])
|
||||||
|
|
||||||
|
|
||||||
class CustomFileDialog(tk.Toplevel):
|
class CustomFileDialog(tk.Toplevel):
|
||||||
@@ -141,13 +193,54 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.original_path_text = "" # Store original path text
|
self.original_path_text = "" # Store original path text
|
||||||
self.items_to_load_per_batch = 250
|
self.items_to_load_per_batch = 250
|
||||||
self.item_path_map = {}
|
self.item_path_map = {}
|
||||||
|
self.responsive_buttons_hidden = None # State for responsive buttons
|
||||||
|
|
||||||
self.icon_manager = IconManager()
|
self.icon_manager = IconManager()
|
||||||
self.style_manager = StyleManager(self)
|
self.style_manager = StyleManager(self)
|
||||||
self.widget_manager = WidgetManager(self, self.settings)
|
self.widget_manager = WidgetManager(self, self.settings)
|
||||||
|
|
||||||
self._update_view_mode_buttons()
|
self._update_view_mode_buttons()
|
||||||
self.navigate_to(self.current_dir)
|
|
||||||
|
# Defer initial navigation until the window geometry is calculated
|
||||||
|
# to ensure the icon view gets the correct initial width.
|
||||||
|
def initial_load():
|
||||||
|
# Force layout update to get correct widths
|
||||||
|
self.update_idletasks()
|
||||||
|
self.last_width = self.widget_manager.file_list_frame.winfo_width()
|
||||||
|
self._handle_responsive_buttons(self.winfo_width())
|
||||||
|
self.navigate_to(self.current_dir)
|
||||||
|
|
||||||
|
# Using after(10) gives the window manager a moment to process
|
||||||
|
# the initial window drawing and sizing.
|
||||||
|
self.after(10, initial_load)
|
||||||
|
|
||||||
|
# Bind the intelligent return handler
|
||||||
|
self.widget_manager.path_entry.bind("<Return>", self.handle_path_entry_return)
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
def load_settings(self):
|
def load_settings(self):
|
||||||
self.settings = CfdConfigManager.load()
|
self.settings = CfdConfigManager.load()
|
||||||
@@ -179,10 +272,20 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.style_manager = StyleManager(self)
|
self.style_manager = StyleManager(self)
|
||||||
self.widget_manager = WidgetManager(self, self.settings)
|
self.widget_manager = WidgetManager(self, self.settings)
|
||||||
self._update_view_mode_buttons()
|
self._update_view_mode_buttons()
|
||||||
|
|
||||||
|
# Reset responsive button state and re-evaluate
|
||||||
|
self.responsive_buttons_hidden = None
|
||||||
|
self.update_idletasks()
|
||||||
|
self._handle_responsive_buttons(self.winfo_width())
|
||||||
|
|
||||||
|
# If search was active, reset it to avoid inconsistent state
|
||||||
|
if self.search_mode:
|
||||||
|
self.toggle_search_mode() # This will correctly reset the UI
|
||||||
|
|
||||||
self.navigate_to(self.current_dir)
|
self.navigate_to(self.current_dir)
|
||||||
|
|
||||||
def open_settings_dialog(self):
|
def open_settings_dialog(self):
|
||||||
SettingsDialog(self)
|
SettingsDialog(self, dialog_mode=self.dialog_mode)
|
||||||
|
|
||||||
def get_file_icon(self, filename, size='large'):
|
def get_file_icon(self, filename, size='large'):
|
||||||
ext = os.path.splitext(filename)[1].lower()
|
ext = os.path.splitext(filename)[1].lower()
|
||||||
@@ -196,7 +299,8 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
if ext in ['.mp3', '.wav', '.ogg', '.flac']:
|
if ext in ['.mp3', '.wav', '.ogg', '.flac']:
|
||||||
return self.icon_manager.get_icon(f'audio_{size}')
|
return self.icon_manager.get_icon(f'audio_{size}')
|
||||||
if ext in ['.mp4', '.mkv', '.avi', '.mov']:
|
if ext in ['.mp4', '.mkv', '.avi', '.mov']:
|
||||||
return self.icon_manager.get_icon(f'video_{size}') if size == 'large' else self.icon_manager.get_icon('video_small_file')
|
return self.icon_manager.get_icon(f'video_{size}') if size == 'large' else self.icon_manager.get_icon(
|
||||||
|
'video_small_file')
|
||||||
if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg']:
|
if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg']:
|
||||||
return self.icon_manager.get_icon(f'picture_{size}')
|
return self.icon_manager.get_icon(f'picture_{size}')
|
||||||
if ext == '.iso':
|
if ext == '.iso':
|
||||||
@@ -218,12 +322,73 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.populate_files()
|
self.populate_files()
|
||||||
|
|
||||||
def on_window_resize(self, event):
|
def on_window_resize(self, event):
|
||||||
new_width = self.widget_manager.file_list_frame.winfo_width()
|
# This check is to prevent the resize event from firing for child widgets
|
||||||
if self.view_mode.get() == "icons" and abs(new_width - self.last_width) > 50:
|
if event.widget is self:
|
||||||
if self.resize_job:
|
# Handle icon view redraw on width change, but not in search mode
|
||||||
self.after_cancel(self.resize_job)
|
if self.view_mode.get() == "icons" and not self.search_mode:
|
||||||
self.resize_job = self.after(200, self.populate_files)
|
new_width = self.widget_manager.file_list_frame.winfo_width()
|
||||||
self.last_width = new_width
|
if abs(new_width - self.last_width) > 50:
|
||||||
|
if self.resize_job:
|
||||||
|
self.after_cancel(self.resize_job)
|
||||||
|
|
||||||
|
def repopulate_icons():
|
||||||
|
# Ensure all pending geometry changes are processed before redrawing
|
||||||
|
self.update_idletasks()
|
||||||
|
self.populate_files()
|
||||||
|
|
||||||
|
self.resize_job = self.after(150, repopulate_icons)
|
||||||
|
self.last_width = new_width
|
||||||
|
|
||||||
|
# Handle responsive buttons in the top bar
|
||||||
|
self._handle_responsive_buttons(event.width)
|
||||||
|
|
||||||
|
def _handle_responsive_buttons(self, window_width):
|
||||||
|
# This threshold might need adjustment based on your layout and button sizes
|
||||||
|
threshold = 850
|
||||||
|
container = self.widget_manager.responsive_buttons_container
|
||||||
|
more_button = self.widget_manager.more_button
|
||||||
|
|
||||||
|
should_be_hidden = window_width < threshold
|
||||||
|
|
||||||
|
# Only change the layout if the state is different from the current one
|
||||||
|
if should_be_hidden != self.responsive_buttons_hidden:
|
||||||
|
if should_be_hidden:
|
||||||
|
# Hide individual buttons and show the 'more' button
|
||||||
|
container.pack_forget()
|
||||||
|
more_button.pack(side="left", padx=5)
|
||||||
|
else:
|
||||||
|
# Show individual buttons and hide the 'more' button
|
||||||
|
more_button.pack_forget()
|
||||||
|
container.pack(side="left")
|
||||||
|
self.responsive_buttons_hidden = should_be_hidden
|
||||||
|
|
||||||
|
def show_more_menu(self):
|
||||||
|
# Create and display the dropdown menu for hidden buttons
|
||||||
|
more_menu = tk.Menu(self, tearoff=0, background=self.style_manager.header, foreground=self.style_manager.color_foreground,
|
||||||
|
activebackground=self.style_manager.selection_color, activeforeground=self.style_manager.color_foreground, relief='flat', borderwidth=0)
|
||||||
|
|
||||||
|
more_menu.add_command(label="Neuer Ordner", command=self.create_new_folder,
|
||||||
|
image=self.icon_manager.get_icon('new_folder_small'), compound='left')
|
||||||
|
more_menu.add_command(label="Neues Dokument", command=self.create_new_file,
|
||||||
|
image=self.icon_manager.get_icon('new_document_small'), compound='left')
|
||||||
|
more_menu.add_separator()
|
||||||
|
more_menu.add_command(label="Kachelansicht", command=self.set_icon_view,
|
||||||
|
image=self.icon_manager.get_icon('icon_view'), compound='left')
|
||||||
|
more_menu.add_command(label="Listenansicht", command=self.set_list_view,
|
||||||
|
image=self.icon_manager.get_icon('list_view'), compound='left')
|
||||||
|
more_menu.add_separator()
|
||||||
|
|
||||||
|
# Toggle hidden files option
|
||||||
|
hidden_files_label = "Versteckte Dateien ausblenden" if self.show_hidden_files.get() else "Versteckte Dateien anzeigen"
|
||||||
|
hidden_files_icon = self.icon_manager.get_icon('unhide') if self.show_hidden_files.get() else self.icon_manager.get_icon('hide')
|
||||||
|
more_menu.add_command(label=hidden_files_label, command=self.toggle_hidden_files,
|
||||||
|
image=hidden_files_icon, compound='left')
|
||||||
|
|
||||||
|
# Position and show the menu
|
||||||
|
more_button = self.widget_manager.more_button
|
||||||
|
x = more_button.winfo_rootx()
|
||||||
|
y = more_button.winfo_rooty() + more_button.winfo_height()
|
||||||
|
more_menu.tk_popup(x, y)
|
||||||
|
|
||||||
def on_sidebar_resize(self, event):
|
def on_sidebar_resize(self, event):
|
||||||
current_width = event.width
|
current_width = event.width
|
||||||
@@ -271,11 +436,10 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.original_path_text = self.widget_manager.path_entry.get()
|
self.original_path_text = self.widget_manager.path_entry.get()
|
||||||
self.widget_manager.path_entry.delete(0, tk.END)
|
self.widget_manager.path_entry.delete(0, tk.END)
|
||||||
self.widget_manager.path_entry.insert(0, "Suchbegriff eingeben...")
|
self.widget_manager.path_entry.insert(0, "Suchbegriff eingeben...")
|
||||||
self.widget_manager.path_entry.select_range(0, tk.END)
|
# Use after() to ensure the focus is set after the UI has updated
|
||||||
# Set focus reliably
|
self.after(10, lambda: self.widget_manager.path_entry.focus_set())
|
||||||
self.after(50, 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(
|
|
||||||
"<Return>", self.execute_search)
|
|
||||||
self.widget_manager.path_entry.bind(
|
self.widget_manager.path_entry.bind(
|
||||||
"<FocusIn>", self.clear_search_placeholder)
|
"<FocusIn>", self.clear_search_placeholder)
|
||||||
|
|
||||||
@@ -286,8 +450,6 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.search_mode = False
|
self.search_mode = False
|
||||||
self.widget_manager.path_entry.delete(0, tk.END)
|
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.insert(0, self.original_path_text)
|
||||||
self.widget_manager.path_entry.bind(
|
|
||||||
"<Return>", lambda e: self.navigate_to(self.widget_manager.path_entry.get()))
|
|
||||||
self.widget_manager.path_entry.unbind("<FocusIn>")
|
self.widget_manager.path_entry.unbind("<FocusIn>")
|
||||||
|
|
||||||
# Hide search options
|
# Hide search options
|
||||||
@@ -381,11 +543,12 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
|
|
||||||
# Build find command based on recursive setting (use . for current directory)
|
# Build find command based on recursive setting (use . for current directory)
|
||||||
if self.widget_manager.recursive_search.get():
|
if self.widget_manager.recursive_search.get():
|
||||||
find_cmd = ['find', '.', '-iname',
|
# Find both files and directories
|
||||||
f'*{search_term}*', '-type', 'f']
|
find_cmd = ['find', '.', '-iname', f'*{search_term}*']
|
||||||
else:
|
else:
|
||||||
|
# Find both files and directories, but only in the current level
|
||||||
find_cmd = ['find', '.', '-maxdepth', '1',
|
find_cmd = ['find', '.', '-maxdepth', '1',
|
||||||
'-iname', f'*{search_term}*', '-type', 'f']
|
'-iname', f'*{search_term}*']
|
||||||
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
find_cmd, capture_output=True, text=True, timeout=30)
|
find_cmd, capture_output=True, text=True, timeout=30)
|
||||||
@@ -398,7 +561,8 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
if f and f.startswith('./'):
|
if f and f.startswith('./'):
|
||||||
abs_path = os.path.join(
|
abs_path = os.path.join(
|
||||||
search_dir, f[2:]) # Remove './' prefix
|
search_dir, f[2:]) # Remove './' prefix
|
||||||
if os.path.isfile(abs_path):
|
# Check if the path exists, as it might be a broken symlink or deleted
|
||||||
|
if os.path.exists(abs_path):
|
||||||
directory_files.append(abs_path)
|
directory_files.append(abs_path)
|
||||||
all_files.extend(directory_files)
|
all_files.extend(directory_files)
|
||||||
|
|
||||||
@@ -413,12 +577,21 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
seen.add(file_path)
|
seen.add(file_path)
|
||||||
unique_files.append(file_path)
|
unique_files.append(file_path)
|
||||||
|
|
||||||
# Filter based on currently selected filter pattern
|
# Filter based on currently selected filter pattern and hidden file setting
|
||||||
self.search_results = []
|
self.search_results = []
|
||||||
|
search_hidden = self.settings.get("search_hidden_files", False)
|
||||||
|
|
||||||
for file_path in unique_files:
|
for file_path in unique_files:
|
||||||
filename = os.path.basename(file_path)
|
# Check if path contains a hidden component (e.g., /.config/ or /some/path/to/.hidden_file)
|
||||||
if self._matches_filetype(filename):
|
if not search_hidden:
|
||||||
self.search_results.append(file_path)
|
if any(part.startswith('.') for part in file_path.split(os.sep)):
|
||||||
|
continue # Skip hidden files/files in hidden directories
|
||||||
|
|
||||||
|
# Check if the path exists (it might have been deleted during the search)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
filename = os.path.basename(file_path)
|
||||||
|
if self._matches_filetype(filename) or os.path.isdir(file_path):
|
||||||
|
self.search_results.append(file_path)
|
||||||
|
|
||||||
# Show search results in TreeView
|
# Show search results in TreeView
|
||||||
if self.search_results:
|
if self.search_results:
|
||||||
@@ -494,7 +667,11 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
modified_time = datetime.fromtimestamp(
|
modified_time = datetime.fromtimestamp(
|
||||||
stat.st_mtime).strftime('%d.%m.%Y %H:%M')
|
stat.st_mtime).strftime('%d.%m.%Y %H:%M')
|
||||||
|
|
||||||
icon = self.get_file_icon(filename, 'small')
|
if os.path.isdir(file_path):
|
||||||
|
icon = self.icon_manager.get_icon('folder_small')
|
||||||
|
else:
|
||||||
|
icon = self.get_file_icon(filename, 'small')
|
||||||
|
|
||||||
search_tree.insert("", "end", text=f" {filename}", image=icon,
|
search_tree.insert("", "end", text=f" {filename}", image=icon,
|
||||||
values=(directory, size, modified_time))
|
values=(directory, size, modified_time))
|
||||||
except (FileNotFoundError, PermissionError):
|
except (FileNotFoundError, PermissionError):
|
||||||
@@ -675,16 +852,17 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.all_items, error, warning = self._get_sorted_items()
|
self.all_items, error, warning = self._get_sorted_items()
|
||||||
self.currently_loaded_count = 0
|
self.currently_loaded_count = 0
|
||||||
|
|
||||||
canvas = tk.Canvas(self.widget_manager.file_list_frame,
|
self.icon_canvas = tk.Canvas(self.widget_manager.file_list_frame,
|
||||||
highlightthickness=0, bg=self.style_manager.icon_bg_color)
|
highlightthickness=0, bg=self.style_manager.icon_bg_color)
|
||||||
v_scrollbar = ttk.Scrollbar(
|
v_scrollbar = ttk.Scrollbar(
|
||||||
self.widget_manager.file_list_frame, orient="vertical", command=canvas.yview)
|
self.widget_manager.file_list_frame, orient="vertical", command=self.icon_canvas.yview)
|
||||||
canvas.pack(side="left", fill="both", expand=True)
|
self.icon_canvas.pack(side="left", fill="both", expand=True)
|
||||||
v_scrollbar.pack(side="right", fill="y")
|
v_scrollbar.pack(side="right", fill="y")
|
||||||
container_frame = ttk.Frame(canvas, style="Content.TFrame")
|
container_frame = ttk.Frame(self.icon_canvas, style="Content.TFrame")
|
||||||
canvas.create_window((0, 0), window=container_frame, anchor="nw")
|
self.icon_canvas.create_window(
|
||||||
container_frame.bind("<Configure>", lambda e: canvas.configure(
|
(0, 0), window=container_frame, anchor="nw")
|
||||||
scrollregion=canvas.bbox("all")))
|
container_frame.bind("<Configure>", lambda e: self.icon_canvas.configure(
|
||||||
|
scrollregion=self.icon_canvas.bbox("all")))
|
||||||
|
|
||||||
def _on_mouse_wheel(event):
|
def _on_mouse_wheel(event):
|
||||||
if event.num == 4:
|
if event.num == 4:
|
||||||
@@ -693,12 +871,12 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
delta = 1
|
delta = 1
|
||||||
else:
|
else:
|
||||||
delta = -1 * int(event.delta / 120)
|
delta = -1 * int(event.delta / 120)
|
||||||
canvas.yview_scroll(delta, "units")
|
self.icon_canvas.yview_scroll(delta, "units")
|
||||||
# Check if scrolled to the bottom and if there are more items to load
|
# 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:
|
if self.currently_loaded_count < len(self.all_items) and self.icon_canvas.yview()[1] > 0.9:
|
||||||
self._load_more_items_icon_view(container_frame)
|
self._load_more_items_icon_view(container_frame)
|
||||||
|
|
||||||
for widget in [canvas, container_frame]:
|
for widget in [self.icon_canvas, container_frame]:
|
||||||
widget.bind("<MouseWheel>", _on_mouse_wheel)
|
widget.bind("<MouseWheel>", _on_mouse_wheel)
|
||||||
widget.bind("<Button-4>", _on_mouse_wheel)
|
widget.bind("<Button-4>", _on_mouse_wheel)
|
||||||
widget.bind("<Button-5>", _on_mouse_wheel)
|
widget.bind("<Button-5>", _on_mouse_wheel)
|
||||||
@@ -709,23 +887,42 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
ttk.Label(container_frame, text=error).pack(pady=20)
|
ttk.Label(container_frame, text=error).pack(pady=20)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._load_more_items_icon_view(
|
widget_to_focus = self._load_more_items_icon_view(
|
||||||
container_frame, item_to_rename, item_to_select)
|
container_frame, item_to_rename, item_to_select)
|
||||||
|
|
||||||
|
if widget_to_focus:
|
||||||
|
def scroll_to_widget():
|
||||||
|
self.update_idletasks()
|
||||||
|
if not widget_to_focus.winfo_exists():
|
||||||
|
return
|
||||||
|
y = widget_to_focus.winfo_y()
|
||||||
|
canvas_height = self.icon_canvas.winfo_height()
|
||||||
|
scroll_region = self.icon_canvas.bbox("all")
|
||||||
|
if not scroll_region:
|
||||||
|
return
|
||||||
|
scroll_height = scroll_region[3]
|
||||||
|
if scroll_height > canvas_height:
|
||||||
|
fraction = y / scroll_height
|
||||||
|
self.icon_canvas.yview_moveto(fraction)
|
||||||
|
|
||||||
|
self.after(100, scroll_to_widget)
|
||||||
|
|
||||||
def _load_more_items_icon_view(self, container, item_to_rename=None, item_to_select=None):
|
def _load_more_items_icon_view(self, container, item_to_rename=None, item_to_select=None):
|
||||||
start_index = self.currently_loaded_count
|
start_index = self.currently_loaded_count
|
||||||
end_index = min(len(self.all_items), start_index +
|
end_index = min(len(self.all_items), start_index +
|
||||||
self.items_to_load_per_batch)
|
self.items_to_load_per_batch)
|
||||||
|
|
||||||
if start_index >= end_index:
|
if start_index >= end_index:
|
||||||
return # All items loaded
|
return None # All items loaded
|
||||||
|
|
||||||
item_width, item_height = 125, 100
|
item_width, item_height = 125, 100
|
||||||
frame_width = self.widget_manager.file_list_frame.winfo_width()
|
frame_width = self.widget_manager.file_list_frame.winfo_width()
|
||||||
col_count = max(1, frame_width // item_width - 1)
|
col_count = max(1, frame_width // item_width - 1)
|
||||||
|
|
||||||
row = start_index // col_count
|
row = start_index // col_count if col_count > 0 else 0
|
||||||
col = start_index % col_count
|
col = start_index % col_count if col_count > 0 else 0
|
||||||
|
|
||||||
|
widget_to_focus = None
|
||||||
|
|
||||||
for i in range(start_index, end_index):
|
for i in range(start_index, end_index):
|
||||||
name = self.all_items[i]
|
name = self.all_items[i]
|
||||||
@@ -743,6 +940,7 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
|
|
||||||
if name == item_to_rename:
|
if name == item_to_rename:
|
||||||
self.start_rename(item_frame, path)
|
self.start_rename(item_frame, path)
|
||||||
|
widget_to_focus = item_frame
|
||||||
else:
|
else:
|
||||||
icon = self.icon_manager.get_icon(
|
icon = self.icon_manager.get_icon(
|
||||||
'folder_large') if is_dir else self.get_file_icon(name, 'large')
|
'folder_large') if is_dir else self.get_file_icon(name, 'large')
|
||||||
@@ -753,12 +951,7 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
name, 14), anchor="center", style="Item.TLabel")
|
name, 14), anchor="center", style="Item.TLabel")
|
||||||
name_label.pack(fill="x", expand=True)
|
name_label.pack(fill="x", expand=True)
|
||||||
|
|
||||||
tooltip_text = name
|
Tooltip(item_frame, name)
|
||||||
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)
|
|
||||||
|
|
||||||
for widget in [item_frame, icon_label, name_label]:
|
for widget in [item_frame, icon_label, name_label]:
|
||||||
widget.bind("<Double-Button-1>", lambda e,
|
widget.bind("<Double-Button-1>", lambda e,
|
||||||
@@ -772,12 +965,17 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
|
|
||||||
if name == item_to_select:
|
if name == item_to_select:
|
||||||
self.on_item_select(path, item_frame)
|
self.on_item_select(path, item_frame)
|
||||||
|
widget_to_focus = item_frame
|
||||||
|
|
||||||
col = (col + 1) % col_count
|
if col_count > 0:
|
||||||
if col == 0:
|
col = (col + 1) % col_count
|
||||||
|
if col == 0:
|
||||||
|
row += 1
|
||||||
|
else:
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
self.currently_loaded_count = end_index
|
self.currently_loaded_count = end_index
|
||||||
|
return widget_to_focus
|
||||||
|
|
||||||
def populate_list_view(self, item_to_rename=None, item_to_select=None):
|
def populate_list_view(self, item_to_rename=None, item_to_select=None):
|
||||||
self.all_items, error, warning = self._get_sorted_items()
|
self.all_items, error, warning = self._get_sorted_items()
|
||||||
@@ -815,7 +1013,8 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
def _on_scroll(*args):
|
def _on_scroll(*args):
|
||||||
# Check if scrolled to the bottom and if there are more items to load
|
# 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:
|
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)
|
# On-scroll loading should not trigger rename or select.
|
||||||
|
self._load_more_items_list_view()
|
||||||
v_scrollbar.set(*args)
|
v_scrollbar.set(*args)
|
||||||
self.tree.configure(yscrollcommand=_on_scroll)
|
self.tree.configure(yscrollcommand=_on_scroll)
|
||||||
|
|
||||||
@@ -886,7 +1085,7 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
child.state(['selected'])
|
child.state(['selected'])
|
||||||
self.selected_item_frame = item_frame
|
self.selected_item_frame = item_frame
|
||||||
self.selected_file = path
|
self.selected_file = path
|
||||||
self.update_status_bar()
|
self.update_status_bar(path) # Pass selected path
|
||||||
self.bind("<F2>", lambda e, p=path,
|
self.bind("<F2>", lambda e, p=path,
|
||||||
f=item_frame: self.on_rename_request(e, p, f))
|
f=item_frame: self.on_rename_request(e, p, f))
|
||||||
if self.dialog_mode == "save" and not os.path.isdir(path):
|
if self.dialog_mode == "save" and not os.path.isdir(path):
|
||||||
@@ -899,8 +1098,9 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
return
|
return
|
||||||
item_id = self.tree.selection()[0]
|
item_id = self.tree.selection()[0]
|
||||||
item_text = self.tree.item(item_id, 'text').strip()
|
item_text = self.tree.item(item_id, 'text').strip()
|
||||||
self.selected_file = os.path.join(self.current_dir, item_text)
|
path = os.path.join(self.current_dir, item_text)
|
||||||
self.update_status_bar()
|
self.selected_file = path
|
||||||
|
self.update_status_bar(path) # Pass selected path
|
||||||
if self.dialog_mode == "save" and not os.path.isdir(self.selected_file):
|
if self.dialog_mode == "save" and not os.path.isdir(self.selected_file):
|
||||||
self.widget_manager.filename_entry.delete(0, tk.END)
|
self.widget_manager.filename_entry.delete(0, tk.END)
|
||||||
self.widget_manager.filename_entry.insert(0, item_text)
|
self.widget_manager.filename_entry.insert(0, item_text)
|
||||||
@@ -1006,13 +1206,19 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.update_status_bar()
|
self.update_status_bar()
|
||||||
self.update_action_buttons_state()
|
self.update_action_buttons_state()
|
||||||
|
|
||||||
|
def go_up_level(self):
|
||||||
|
"""Navigates one directory level up."""
|
||||||
|
new_path = os.path.dirname(self.current_dir)
|
||||||
|
if new_path != self.current_dir: # Avoid getting stuck at the root
|
||||||
|
self.navigate_to(new_path)
|
||||||
|
|
||||||
def update_nav_buttons(self):
|
def update_nav_buttons(self):
|
||||||
self.widget_manager.back_button.config(
|
self.widget_manager.back_button.config(
|
||||||
state=tk.NORMAL if self.history_pos > 0 else tk.DISABLED)
|
state=tk.NORMAL if self.history_pos > 0 else tk.DISABLED)
|
||||||
self.widget_manager.forward_button.config(state=tk.NORMAL if self.history_pos < len(
|
self.widget_manager.forward_button.config(state=tk.NORMAL if self.history_pos < len(
|
||||||
self.history) - 1 else tk.DISABLED)
|
self.history) - 1 else tk.DISABLED)
|
||||||
|
|
||||||
def update_status_bar(self):
|
def update_status_bar(self, selected_path=None):
|
||||||
try:
|
try:
|
||||||
total, used, free = shutil.disk_usage(self.current_dir)
|
total, used, free = shutil.disk_usage(self.current_dir)
|
||||||
free_str = self._format_size(free)
|
free_str = self._format_size(free)
|
||||||
@@ -1021,10 +1227,19 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
self.widget_manager.storage_bar['value'] = (used / total) * 100
|
self.widget_manager.storage_bar['value'] = (used / total) * 100
|
||||||
|
|
||||||
status_text = ""
|
status_text = ""
|
||||||
if self.dialog_mode == "open" and self.selected_file and os.path.exists(self.selected_file) and not os.path.isdir(self.selected_file):
|
if selected_path and os.path.exists(selected_path):
|
||||||
size = os.path.getsize(self.selected_file)
|
if os.path.isdir(selected_path):
|
||||||
size_str = self._format_size(size)
|
# Display item count for directories
|
||||||
status_text = f"'{os.path.basename(self.selected_file)}' Größe: {size_str}"
|
content_count = self._get_folder_content_count(selected_path)
|
||||||
|
if content_count is not None:
|
||||||
|
status_text = f"'{os.path.basename(selected_path)}' ({content_count} Einträge)"
|
||||||
|
else:
|
||||||
|
status_text = f"'{os.path.basename(selected_path)}'"
|
||||||
|
else:
|
||||||
|
# Display size for files
|
||||||
|
size = os.path.getsize(selected_path)
|
||||||
|
size_str = self._format_size(size)
|
||||||
|
status_text = f"'{os.path.basename(selected_path)}' Größe: {size_str}"
|
||||||
self.widget_manager.status_bar.config(text=status_text)
|
self.widget_manager.status_bar.config(text=status_text)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.widget_manager.status_bar.config(
|
self.widget_manager.status_bar.config(
|
||||||
@@ -1050,6 +1265,48 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
def get_selected_file(self):
|
def get_selected_file(self):
|
||||||
return self.selected_file
|
return self.selected_file
|
||||||
|
|
||||||
|
def delete_selected_item(self, event=None):
|
||||||
|
"""Deletes or moves the selected item to trash based on settings."""
|
||||||
|
if not self.selected_file or not os.path.exists(self.selected_file):
|
||||||
|
return
|
||||||
|
|
||||||
|
use_trash = self.settings.get("use_trash", False) and SEND2TRASH_AVAILABLE
|
||||||
|
confirm = self.settings.get("confirm_delete", False)
|
||||||
|
|
||||||
|
action_text = "in den Papierkorb verschieben" if use_trash else "endgültig löschen"
|
||||||
|
item_name = os.path.basename(self.selected_file)
|
||||||
|
|
||||||
|
if not confirm:
|
||||||
|
dialog = MessageDialog(
|
||||||
|
master=self,
|
||||||
|
title="Bestätigung erforderlich",
|
||||||
|
text=f"Möchten Sie '{item_name}' wirklich {action_text}?",
|
||||||
|
message_type="question"
|
||||||
|
)
|
||||||
|
if not dialog.show():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if use_trash:
|
||||||
|
send2trash.send2trash(self.selected_file)
|
||||||
|
else:
|
||||||
|
if os.path.isdir(self.selected_file):
|
||||||
|
shutil.rmtree(self.selected_file)
|
||||||
|
else:
|
||||||
|
os.remove(self.selected_file)
|
||||||
|
|
||||||
|
self.populate_files()
|
||||||
|
self.widget_manager.status_bar.config(
|
||||||
|
text=f"'{item_name}' wurde erfolgreich entfernt.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
MessageDialog(
|
||||||
|
master=self,
|
||||||
|
title="Fehler",
|
||||||
|
text=f"Fehler beim Entfernen von '{item_name}':\n{e}",
|
||||||
|
message_type="error"
|
||||||
|
).show()
|
||||||
|
|
||||||
def create_new_folder(self):
|
def create_new_folder(self):
|
||||||
self._create_new_item(is_folder=True)
|
self._create_new_item(is_folder=True)
|
||||||
|
|
||||||
@@ -1164,7 +1421,19 @@ class CustomFileDialog(tk.Toplevel):
|
|||||||
entry.bind("<Escape>", cancel_rename)
|
entry.bind("<Escape>", cancel_rename)
|
||||||
|
|
||||||
def _start_rename_list_view(self, item_id):
|
def _start_rename_list_view(self, item_id):
|
||||||
x, y, width, height = self.tree.bbox(item_id, column="#0")
|
# First, ensure the item is visible by scrolling to it.
|
||||||
|
self.tree.see(item_id)
|
||||||
|
# Force the UI to process the scrolling and other pending events.
|
||||||
|
self.tree.update_idletasks()
|
||||||
|
|
||||||
|
# Now, get the bounding box. It should be available since the item is visible.
|
||||||
|
bbox = self.tree.bbox(item_id, column="#0")
|
||||||
|
|
||||||
|
# If bbox is still empty (e.g., view is not focused), abort to prevent crash.
|
||||||
|
if not bbox:
|
||||||
|
return
|
||||||
|
|
||||||
|
x, y, width, height = bbox
|
||||||
entry = ttk.Entry(self.tree)
|
entry = ttk.Entry(self.tree)
|
||||||
# Set a fixed width for the entry widget to prevent it from expanding too much
|
# Set a fixed width for the entry widget to prevent it from expanding too much
|
||||||
entry_width = self.tree.column("#0", "width")
|
entry_width = self.tree.column("#0", "width")
|
||||||
|
@@ -33,7 +33,7 @@ class GlotzMol(tk.Tk):
|
|||||||
dialog = CustomFileDialog(self,
|
dialog = CustomFileDialog(self,
|
||||||
initial_dir=os.path.expanduser("~"),
|
initial_dir=os.path.expanduser("~"),
|
||||||
filetypes=[("All Files", "*.*")
|
filetypes=[("All Files", "*.*")
|
||||||
], dialog_mode="save")
|
])
|
||||||
|
|
||||||
# This is the crucial part: wait for the dialog to be closed
|
# This is the crucial part: wait for the dialog to be closed
|
||||||
self.wait_window(dialog)
|
self.wait_window(dialog)
|
||||||
@@ -55,7 +55,7 @@ if __name__ == "__main__":
|
|||||||
style = ttk.Style(root)
|
style = ttk.Style(root)
|
||||||
root.tk.call('source', f"{theme_path}/water.tcl")
|
root.tk.call('source', f"{theme_path}/water.tcl")
|
||||||
try:
|
try:
|
||||||
root.tk.call('set_theme', 'light')
|
root.tk.call('set_theme', 'dark')
|
||||||
except tk.TclError:
|
except tk.TclError:
|
||||||
pass
|
pass
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
Reference in New Issue
Block a user