Spaces:
Paused
style(ui): π reduce spacing and font sizes for compact model filter layout
Browse filesSystematically reduced padding, margins, font sizes, and component dimensions throughout the ModelFilterGUI to create a more compact, space-efficient interface.
- Reduced title font from FONT_SIZE_NORMAL to FONT_SIZE_SMALL in RulePanel
- Decreased padding values (12β10, 8β6, etc.) across all UI components
- Reduced input entry and button heights from 36px to 28px, and button from 26px to smaller sizes
- Set minimum height constraint (70px) on rule list to prevent collapse
- Implemented grid-based responsive layout with proportional row weights (3:1 ratio for models vs rules)
- Prevented input frame height changes using pack_propagate(False)
- Refactored UI creation into _create_main_layout() with grid system replacing individual pack() calls
- Updated all components to use content_frame.grid() instead of self.pack() for better layout control
The changes maintain all functionality while significantly improving vertical space utilization, allowing more content to be visible without scrolling.
|
@@ -2191,14 +2191,14 @@ class RulePanel(ctk.CTkFrame):
|
|
| 2191 |
|
| 2192 |
def _create_content(self):
|
| 2193 |
"""Build panel content."""
|
| 2194 |
-
# Title
|
| 2195 |
title_label = ctk.CTkLabel(
|
| 2196 |
self,
|
| 2197 |
text=self.title,
|
| 2198 |
-
font=(FONT_FAMILY,
|
| 2199 |
text_color=TEXT_PRIMARY,
|
| 2200 |
)
|
| 2201 |
-
title_label.pack(anchor="w", padx=
|
| 2202 |
|
| 2203 |
# Virtual rule list (replaces CTkScrollableFrame + RuleChips)
|
| 2204 |
self.rule_list = VirtualRuleList(
|
|
@@ -2207,24 +2207,28 @@ class RulePanel(ctk.CTkFrame):
|
|
| 2207 |
on_rule_click=self.on_rule_clicked,
|
| 2208 |
on_rule_delete=self._on_rule_delete,
|
| 2209 |
)
|
| 2210 |
-
self.rule_list.pack(fill="both", expand=True, padx=
|
|
|
|
|
|
|
|
|
|
| 2211 |
|
| 2212 |
-
# Input frame
|
| 2213 |
-
input_frame = ctk.CTkFrame(self, fg_color="transparent")
|
| 2214 |
-
input_frame.pack(fill="x", padx=
|
|
|
|
| 2215 |
|
| 2216 |
# Pattern input
|
| 2217 |
self.input_entry = ctk.CTkEntry(
|
| 2218 |
input_frame,
|
| 2219 |
placeholder_text="pattern1, pattern2*, ...",
|
| 2220 |
-
font=(FONT_FAMILY,
|
| 2221 |
fg_color=BG_TERTIARY,
|
| 2222 |
border_color=BORDER_COLOR,
|
| 2223 |
text_color=TEXT_PRIMARY,
|
| 2224 |
placeholder_text_color=TEXT_MUTED,
|
| 2225 |
-
height=
|
| 2226 |
)
|
| 2227 |
-
self.input_entry.pack(side="left", fill="x", expand=True, padx=(0,
|
| 2228 |
self.input_entry.bind("<Return>", self._on_add_clicked)
|
| 2229 |
self.input_entry.bind("<KeyRelease>", self._on_input_key)
|
| 2230 |
|
|
@@ -2232,11 +2236,11 @@ class RulePanel(ctk.CTkFrame):
|
|
| 2232 |
add_btn = ctk.CTkButton(
|
| 2233 |
input_frame,
|
| 2234 |
text="+ Add",
|
| 2235 |
-
font=(FONT_FAMILY,
|
| 2236 |
fg_color=ACCENT_BLUE,
|
| 2237 |
hover_color="#3a8aee",
|
| 2238 |
-
width=
|
| 2239 |
-
height=
|
| 2240 |
command=self._on_add_clicked,
|
| 2241 |
)
|
| 2242 |
add_btn.pack(side="right")
|
|
@@ -2342,13 +2346,8 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2342 |
self._fetch_in_progress: bool = False
|
| 2343 |
self._preview_after_id: Optional[str] = None
|
| 2344 |
|
| 2345 |
-
# Build UI
|
| 2346 |
-
self.
|
| 2347 |
-
self._create_search_bar()
|
| 2348 |
-
self._create_model_lists()
|
| 2349 |
-
self._create_rule_panels()
|
| 2350 |
-
self._create_status_bar()
|
| 2351 |
-
self._create_action_buttons()
|
| 2352 |
|
| 2353 |
# Context menu
|
| 2354 |
self._create_context_menu()
|
|
@@ -2365,6 +2364,30 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2365 |
# Focus and raise window after it's fully loaded
|
| 2366 |
self.after(100, self._activate_window)
|
| 2367 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2368 |
def _activate_window(self):
|
| 2369 |
"""Activate and focus the window."""
|
| 2370 |
self.lift()
|
|
@@ -2373,69 +2396,69 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2373 |
self.after(200, lambda: self.attributes("-topmost", False))
|
| 2374 |
|
| 2375 |
def _create_header(self):
|
| 2376 |
-
"""Create the header with provider selector and buttons."""
|
| 2377 |
-
header = ctk.CTkFrame(self, fg_color="transparent")
|
| 2378 |
-
header.
|
| 2379 |
|
| 2380 |
-
# Title
|
| 2381 |
title = ctk.CTkLabel(
|
| 2382 |
header,
|
| 2383 |
text="π― Model Filter Configuration",
|
| 2384 |
-
font=(FONT_FAMILY,
|
| 2385 |
text_color=TEXT_PRIMARY,
|
| 2386 |
)
|
| 2387 |
title.pack(side="left")
|
| 2388 |
|
| 2389 |
-
# Help button
|
| 2390 |
help_btn = ctk.CTkButton(
|
| 2391 |
header,
|
| 2392 |
text="?",
|
| 2393 |
-
font=(FONT_FAMILY,
|
| 2394 |
fg_color=BG_SECONDARY,
|
| 2395 |
hover_color=BG_HOVER,
|
| 2396 |
border_width=1,
|
| 2397 |
border_color=BORDER_COLOR,
|
| 2398 |
-
width=
|
| 2399 |
-
height=
|
| 2400 |
-
corner_radius=
|
| 2401 |
command=self._show_help,
|
| 2402 |
)
|
| 2403 |
-
help_btn.pack(side="right", padx=(
|
| 2404 |
ToolTip(help_btn, "Help (F1)")
|
| 2405 |
|
| 2406 |
-
# Refresh button
|
| 2407 |
refresh_btn = ctk.CTkButton(
|
| 2408 |
header,
|
| 2409 |
text="π Refresh",
|
| 2410 |
-
font=(FONT_FAMILY,
|
| 2411 |
fg_color=BG_SECONDARY,
|
| 2412 |
hover_color=BG_HOVER,
|
| 2413 |
border_width=1,
|
| 2414 |
border_color=BORDER_COLOR,
|
| 2415 |
-
width=
|
| 2416 |
-
height=
|
| 2417 |
command=self._refresh_models,
|
| 2418 |
)
|
| 2419 |
-
refresh_btn.pack(side="right", padx=(
|
| 2420 |
ToolTip(refresh_btn, "Refresh models (Ctrl+R)")
|
| 2421 |
|
| 2422 |
-
# Provider selector
|
| 2423 |
provider_frame = ctk.CTkFrame(header, fg_color="transparent")
|
| 2424 |
provider_frame.pack(side="right")
|
| 2425 |
|
| 2426 |
provider_label = ctk.CTkLabel(
|
| 2427 |
provider_frame,
|
| 2428 |
text="Provider:",
|
| 2429 |
-
font=(FONT_FAMILY,
|
| 2430 |
text_color=TEXT_SECONDARY,
|
| 2431 |
)
|
| 2432 |
-
provider_label.pack(side="left", padx=(0,
|
| 2433 |
|
| 2434 |
self.provider_dropdown = ctk.CTkComboBox(
|
| 2435 |
provider_frame,
|
| 2436 |
values=["Loading..."],
|
| 2437 |
-
font=(FONT_FAMILY,
|
| 2438 |
-
dropdown_font=(FONT_FAMILY,
|
| 2439 |
fg_color=BG_SECONDARY,
|
| 2440 |
border_color=BORDER_COLOR,
|
| 2441 |
button_color=BORDER_COLOR,
|
|
@@ -2443,25 +2466,25 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2443 |
dropdown_fg_color=BG_SECONDARY,
|
| 2444 |
dropdown_hover_color=BG_HOVER,
|
| 2445 |
text_color=TEXT_PRIMARY,
|
| 2446 |
-
width=
|
| 2447 |
-
height=
|
| 2448 |
state="readonly",
|
| 2449 |
command=self._on_provider_changed,
|
| 2450 |
)
|
| 2451 |
self.provider_dropdown.pack(side="left")
|
| 2452 |
|
| 2453 |
def _create_search_bar(self):
|
| 2454 |
-
"""Create the search bar."""
|
| 2455 |
-
search_frame = ctk.CTkFrame(self, fg_color="transparent")
|
| 2456 |
-
search_frame.
|
| 2457 |
|
| 2458 |
search_icon = ctk.CTkLabel(
|
| 2459 |
search_frame,
|
| 2460 |
text="π",
|
| 2461 |
-
font=(FONT_FAMILY,
|
| 2462 |
text_color=TEXT_MUTED,
|
| 2463 |
)
|
| 2464 |
-
search_icon.pack(side="left", padx=(0,
|
| 2465 |
|
| 2466 |
self.search_entry = ctk.CTkEntry(
|
| 2467 |
search_frame,
|
|
@@ -2471,7 +2494,7 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2471 |
border_color=BORDER_COLOR,
|
| 2472 |
text_color=TEXT_PRIMARY,
|
| 2473 |
placeholder_text_color=TEXT_MUTED,
|
| 2474 |
-
height=
|
| 2475 |
)
|
| 2476 |
self.search_entry.pack(side="left", fill="x", expand=True)
|
| 2477 |
self.search_entry.bind("<KeyRelease>", self._on_search_changed)
|
|
@@ -2480,12 +2503,12 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2480 |
clear_btn = ctk.CTkButton(
|
| 2481 |
search_frame,
|
| 2482 |
text="Γ",
|
| 2483 |
-
font=(FONT_FAMILY,
|
| 2484 |
fg_color="transparent",
|
| 2485 |
hover_color=BG_HOVER,
|
| 2486 |
text_color=TEXT_MUTED,
|
| 2487 |
-
width=
|
| 2488 |
-
height=
|
| 2489 |
command=self._clear_search,
|
| 2490 |
)
|
| 2491 |
clear_btn.pack(side="left")
|
|
@@ -2494,22 +2517,23 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2494 |
"""Create the synchronized model list panel."""
|
| 2495 |
# Use the virtual list implementation for performance
|
| 2496 |
self.model_list_panel = VirtualSyncModelLists(
|
| 2497 |
-
self,
|
| 2498 |
on_model_click=self._on_model_clicked,
|
| 2499 |
on_model_right_click=self._on_model_right_clicked,
|
| 2500 |
)
|
| 2501 |
-
self.model_list_panel.
|
| 2502 |
|
| 2503 |
def _create_rule_panels(self):
|
| 2504 |
"""Create the ignore and whitelist rule panels."""
|
| 2505 |
-
rules_frame = ctk.CTkFrame(self, fg_color="transparent")
|
| 2506 |
-
rules_frame.
|
| 2507 |
-
rules_frame.grid_columnconfigure(0, weight=1)
|
| 2508 |
-
rules_frame.grid_columnconfigure(1, weight=1)
|
|
|
|
| 2509 |
|
| 2510 |
# Ignore panel
|
| 2511 |
self.ignore_panel = RulePanel(
|
| 2512 |
-
rules_frame,
|
| 2513 |
title="π« Ignore Rules",
|
| 2514 |
rule_type="ignore",
|
| 2515 |
on_rules_changed=self._on_rules_changed,
|
|
@@ -2522,7 +2546,7 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2522 |
|
| 2523 |
# Whitelist panel
|
| 2524 |
self.whitelist_panel = RulePanel(
|
| 2525 |
-
rules_frame,
|
| 2526 |
title="β Whitelist Rules",
|
| 2527 |
rule_type="whitelist",
|
| 2528 |
on_rules_changed=self._on_rules_changed,
|
|
@@ -2534,16 +2558,16 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2534 |
self.whitelist_panel.set_delete_callback(self._remove_whitelist_pattern)
|
| 2535 |
|
| 2536 |
def _create_status_bar(self):
|
| 2537 |
-
"""Create the status bar showing available count and action buttons."""
|
| 2538 |
# Combined status bar and action buttons in one row
|
| 2539 |
-
self.status_frame = ctk.CTkFrame(self, fg_color="transparent")
|
| 2540 |
-
self.status_frame.
|
| 2541 |
|
| 2542 |
-
# Status label (left side)
|
| 2543 |
self.status_label = ctk.CTkLabel(
|
| 2544 |
self.status_frame,
|
| 2545 |
text="Select a provider to begin",
|
| 2546 |
-
font=(FONT_FAMILY,
|
| 2547 |
text_color=TEXT_SECONDARY,
|
| 2548 |
)
|
| 2549 |
self.status_label.pack(side="left")
|
|
@@ -2555,33 +2579,33 @@ class ModelFilterGUI(ctk.CTk):
|
|
| 2555 |
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2556 |
text_color=ACCENT_YELLOW,
|
| 2557 |
)
|
| 2558 |
-
self.unsaved_label.pack(side="left", padx=(
|
| 2559 |
|
| 2560 |
-
# Buttons (right side)
|
| 2561 |
# Discard button
|
| 2562 |
discard_btn = ctk.CTkButton(
|
| 2563 |
self.status_frame,
|
| 2564 |
text="β©οΈ Discard",
|
| 2565 |
-
font=(FONT_FAMILY,
|
| 2566 |
fg_color=BG_SECONDARY,
|
| 2567 |
hover_color=BG_HOVER,
|
| 2568 |
border_width=1,
|
| 2569 |
border_color=BORDER_COLOR,
|
| 2570 |
-
width=
|
| 2571 |
-
height=
|
| 2572 |
command=self._discard_changes,
|
| 2573 |
)
|
| 2574 |
-
discard_btn.pack(side="right", padx=(
|
| 2575 |
|
| 2576 |
# Save button
|
| 2577 |
save_btn = ctk.CTkButton(
|
| 2578 |
self.status_frame,
|
| 2579 |
text="πΎ Save",
|
| 2580 |
-
font=(FONT_FAMILY,
|
| 2581 |
fg_color=ACCENT_GREEN,
|
| 2582 |
hover_color="#27ae60",
|
| 2583 |
-
width=
|
| 2584 |
-
height=
|
| 2585 |
command=self._save_changes,
|
| 2586 |
)
|
| 2587 |
save_btn.pack(side="right")
|
|
|
|
| 2191 |
|
| 2192 |
def _create_content(self):
|
| 2193 |
"""Build panel content."""
|
| 2194 |
+
# Title (compact)
|
| 2195 |
title_label = ctk.CTkLabel(
|
| 2196 |
self,
|
| 2197 |
text=self.title,
|
| 2198 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL, "bold"),
|
| 2199 |
text_color=TEXT_PRIMARY,
|
| 2200 |
)
|
| 2201 |
+
title_label.pack(anchor="w", padx=10, pady=(6, 3))
|
| 2202 |
|
| 2203 |
# Virtual rule list (replaces CTkScrollableFrame + RuleChips)
|
| 2204 |
self.rule_list = VirtualRuleList(
|
|
|
|
| 2207 |
on_rule_click=self.on_rule_clicked,
|
| 2208 |
on_rule_delete=self._on_rule_delete,
|
| 2209 |
)
|
| 2210 |
+
self.rule_list.pack(fill="both", expand=True, padx=6, pady=(0, 3))
|
| 2211 |
+
|
| 2212 |
+
# Set minimum height for rule list to ensure it's visible
|
| 2213 |
+
self.rule_list.frame.configure(height=70)
|
| 2214 |
|
| 2215 |
+
# Input frame with fixed height (won't squish on resize)
|
| 2216 |
+
input_frame = ctk.CTkFrame(self, fg_color="transparent", height=32)
|
| 2217 |
+
input_frame.pack(fill="x", padx=6, pady=(0, 5))
|
| 2218 |
+
input_frame.pack_propagate(False) # Prevent children from changing frame height
|
| 2219 |
|
| 2220 |
# Pattern input
|
| 2221 |
self.input_entry = ctk.CTkEntry(
|
| 2222 |
input_frame,
|
| 2223 |
placeholder_text="pattern1, pattern2*, ...",
|
| 2224 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2225 |
fg_color=BG_TERTIARY,
|
| 2226 |
border_color=BORDER_COLOR,
|
| 2227 |
text_color=TEXT_PRIMARY,
|
| 2228 |
placeholder_text_color=TEXT_MUTED,
|
| 2229 |
+
height=28,
|
| 2230 |
)
|
| 2231 |
+
self.input_entry.pack(side="left", fill="x", expand=True, padx=(0, 6))
|
| 2232 |
self.input_entry.bind("<Return>", self._on_add_clicked)
|
| 2233 |
self.input_entry.bind("<KeyRelease>", self._on_input_key)
|
| 2234 |
|
|
|
|
| 2236 |
add_btn = ctk.CTkButton(
|
| 2237 |
input_frame,
|
| 2238 |
text="+ Add",
|
| 2239 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2240 |
fg_color=ACCENT_BLUE,
|
| 2241 |
hover_color="#3a8aee",
|
| 2242 |
+
width=55,
|
| 2243 |
+
height=28,
|
| 2244 |
command=self._on_add_clicked,
|
| 2245 |
)
|
| 2246 |
add_btn.pack(side="right")
|
|
|
|
| 2346 |
self._fetch_in_progress: bool = False
|
| 2347 |
self._preview_after_id: Optional[str] = None
|
| 2348 |
|
| 2349 |
+
# Build UI with grid layout for responsive sizing
|
| 2350 |
+
self._create_main_layout()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2351 |
|
| 2352 |
# Context menu
|
| 2353 |
self._create_context_menu()
|
|
|
|
| 2364 |
# Focus and raise window after it's fully loaded
|
| 2365 |
self.after(100, self._activate_window)
|
| 2366 |
|
| 2367 |
+
def _create_main_layout(self):
|
| 2368 |
+
"""Create the main layout with grid for responsive sizing."""
|
| 2369 |
+
# Main content frame using grid layout
|
| 2370 |
+
# This allows proportional sizing between model lists and rule panels
|
| 2371 |
+
self.content_frame = ctk.CTkFrame(self, fg_color="transparent")
|
| 2372 |
+
self.content_frame.pack(fill="both", expand=True, padx=20, pady=(8, 10))
|
| 2373 |
+
|
| 2374 |
+
# Configure grid weights for responsive layout
|
| 2375 |
+
# Using 3:1 ratio so models get significantly more space than rules
|
| 2376 |
+
self.content_frame.grid_columnconfigure(0, weight=1)
|
| 2377 |
+
self.content_frame.grid_rowconfigure(0, weight=0) # Header - fixed
|
| 2378 |
+
self.content_frame.grid_rowconfigure(1, weight=0) # Search - fixed
|
| 2379 |
+
self.content_frame.grid_rowconfigure(2, weight=3) # Model lists - expands most
|
| 2380 |
+
self.content_frame.grid_rowconfigure(3, weight=1) # Rule panels - expands less
|
| 2381 |
+
self.content_frame.grid_rowconfigure(4, weight=0) # Status bar - fixed
|
| 2382 |
+
|
| 2383 |
+
# Create all sections
|
| 2384 |
+
self._create_header()
|
| 2385 |
+
self._create_search_bar()
|
| 2386 |
+
self._create_model_lists()
|
| 2387 |
+
self._create_rule_panels()
|
| 2388 |
+
self._create_status_bar()
|
| 2389 |
+
self._create_action_buttons()
|
| 2390 |
+
|
| 2391 |
def _activate_window(self):
|
| 2392 |
"""Activate and focus the window."""
|
| 2393 |
self.lift()
|
|
|
|
| 2396 |
self.after(200, lambda: self.attributes("-topmost", False))
|
| 2397 |
|
| 2398 |
def _create_header(self):
|
| 2399 |
+
"""Create the header with provider selector and buttons (compact)."""
|
| 2400 |
+
header = ctk.CTkFrame(self.content_frame, fg_color="transparent")
|
| 2401 |
+
header.grid(row=0, column=0, sticky="ew", pady=(0, 4))
|
| 2402 |
|
| 2403 |
+
# Title (smaller font)
|
| 2404 |
title = ctk.CTkLabel(
|
| 2405 |
header,
|
| 2406 |
text="π― Model Filter Configuration",
|
| 2407 |
+
font=(FONT_FAMILY, FONT_SIZE_LARGE, "bold"),
|
| 2408 |
text_color=TEXT_PRIMARY,
|
| 2409 |
)
|
| 2410 |
title.pack(side="left")
|
| 2411 |
|
| 2412 |
+
# Help button (smaller)
|
| 2413 |
help_btn = ctk.CTkButton(
|
| 2414 |
header,
|
| 2415 |
text="?",
|
| 2416 |
+
font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
|
| 2417 |
fg_color=BG_SECONDARY,
|
| 2418 |
hover_color=BG_HOVER,
|
| 2419 |
border_width=1,
|
| 2420 |
border_color=BORDER_COLOR,
|
| 2421 |
+
width=26,
|
| 2422 |
+
height=26,
|
| 2423 |
+
corner_radius=13,
|
| 2424 |
command=self._show_help,
|
| 2425 |
)
|
| 2426 |
+
help_btn.pack(side="right", padx=(8, 0))
|
| 2427 |
ToolTip(help_btn, "Help (F1)")
|
| 2428 |
|
| 2429 |
+
# Refresh button (smaller)
|
| 2430 |
refresh_btn = ctk.CTkButton(
|
| 2431 |
header,
|
| 2432 |
text="π Refresh",
|
| 2433 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2434 |
fg_color=BG_SECONDARY,
|
| 2435 |
hover_color=BG_HOVER,
|
| 2436 |
border_width=1,
|
| 2437 |
border_color=BORDER_COLOR,
|
| 2438 |
+
width=80,
|
| 2439 |
+
height=26,
|
| 2440 |
command=self._refresh_models,
|
| 2441 |
)
|
| 2442 |
+
refresh_btn.pack(side="right", padx=(8, 0))
|
| 2443 |
ToolTip(refresh_btn, "Refresh models (Ctrl+R)")
|
| 2444 |
|
| 2445 |
+
# Provider selector (compact)
|
| 2446 |
provider_frame = ctk.CTkFrame(header, fg_color="transparent")
|
| 2447 |
provider_frame.pack(side="right")
|
| 2448 |
|
| 2449 |
provider_label = ctk.CTkLabel(
|
| 2450 |
provider_frame,
|
| 2451 |
text="Provider:",
|
| 2452 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2453 |
text_color=TEXT_SECONDARY,
|
| 2454 |
)
|
| 2455 |
+
provider_label.pack(side="left", padx=(0, 6))
|
| 2456 |
|
| 2457 |
self.provider_dropdown = ctk.CTkComboBox(
|
| 2458 |
provider_frame,
|
| 2459 |
values=["Loading..."],
|
| 2460 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2461 |
+
dropdown_font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2462 |
fg_color=BG_SECONDARY,
|
| 2463 |
border_color=BORDER_COLOR,
|
| 2464 |
button_color=BORDER_COLOR,
|
|
|
|
| 2466 |
dropdown_fg_color=BG_SECONDARY,
|
| 2467 |
dropdown_hover_color=BG_HOVER,
|
| 2468 |
text_color=TEXT_PRIMARY,
|
| 2469 |
+
width=160,
|
| 2470 |
+
height=26,
|
| 2471 |
state="readonly",
|
| 2472 |
command=self._on_provider_changed,
|
| 2473 |
)
|
| 2474 |
self.provider_dropdown.pack(side="left")
|
| 2475 |
|
| 2476 |
def _create_search_bar(self):
|
| 2477 |
+
"""Create the search bar (compact version)."""
|
| 2478 |
+
search_frame = ctk.CTkFrame(self.content_frame, fg_color="transparent")
|
| 2479 |
+
search_frame.grid(row=1, column=0, sticky="ew", pady=(0, 5))
|
| 2480 |
|
| 2481 |
search_icon = ctk.CTkLabel(
|
| 2482 |
search_frame,
|
| 2483 |
text="π",
|
| 2484 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2485 |
text_color=TEXT_MUTED,
|
| 2486 |
)
|
| 2487 |
+
search_icon.pack(side="left", padx=(0, 6))
|
| 2488 |
|
| 2489 |
self.search_entry = ctk.CTkEntry(
|
| 2490 |
search_frame,
|
|
|
|
| 2494 |
border_color=BORDER_COLOR,
|
| 2495 |
text_color=TEXT_PRIMARY,
|
| 2496 |
placeholder_text_color=TEXT_MUTED,
|
| 2497 |
+
height=28,
|
| 2498 |
)
|
| 2499 |
self.search_entry.pack(side="left", fill="x", expand=True)
|
| 2500 |
self.search_entry.bind("<KeyRelease>", self._on_search_changed)
|
|
|
|
| 2503 |
clear_btn = ctk.CTkButton(
|
| 2504 |
search_frame,
|
| 2505 |
text="Γ",
|
| 2506 |
+
font=(FONT_FAMILY, FONT_SIZE_NORMAL),
|
| 2507 |
fg_color="transparent",
|
| 2508 |
hover_color=BG_HOVER,
|
| 2509 |
text_color=TEXT_MUTED,
|
| 2510 |
+
width=28,
|
| 2511 |
+
height=28,
|
| 2512 |
command=self._clear_search,
|
| 2513 |
)
|
| 2514 |
clear_btn.pack(side="left")
|
|
|
|
| 2517 |
"""Create the synchronized model list panel."""
|
| 2518 |
# Use the virtual list implementation for performance
|
| 2519 |
self.model_list_panel = VirtualSyncModelLists(
|
| 2520 |
+
self.content_frame,
|
| 2521 |
on_model_click=self._on_model_clicked,
|
| 2522 |
on_model_right_click=self._on_model_right_clicked,
|
| 2523 |
)
|
| 2524 |
+
self.model_list_panel.grid(row=2, column=0, sticky="nsew", pady=(0, 5))
|
| 2525 |
|
| 2526 |
def _create_rule_panels(self):
|
| 2527 |
"""Create the ignore and whitelist rule panels."""
|
| 2528 |
+
self.rules_frame = ctk.CTkFrame(self.content_frame, fg_color="transparent")
|
| 2529 |
+
self.rules_frame.grid(row=3, column=0, sticky="nsew", pady=(0, 5))
|
| 2530 |
+
self.rules_frame.grid_columnconfigure(0, weight=1)
|
| 2531 |
+
self.rules_frame.grid_columnconfigure(1, weight=1)
|
| 2532 |
+
self.rules_frame.grid_rowconfigure(0, weight=1)
|
| 2533 |
|
| 2534 |
# Ignore panel
|
| 2535 |
self.ignore_panel = RulePanel(
|
| 2536 |
+
self.rules_frame,
|
| 2537 |
title="π« Ignore Rules",
|
| 2538 |
rule_type="ignore",
|
| 2539 |
on_rules_changed=self._on_rules_changed,
|
|
|
|
| 2546 |
|
| 2547 |
# Whitelist panel
|
| 2548 |
self.whitelist_panel = RulePanel(
|
| 2549 |
+
self.rules_frame,
|
| 2550 |
title="β Whitelist Rules",
|
| 2551 |
rule_type="whitelist",
|
| 2552 |
on_rules_changed=self._on_rules_changed,
|
|
|
|
| 2558 |
self.whitelist_panel.set_delete_callback(self._remove_whitelist_pattern)
|
| 2559 |
|
| 2560 |
def _create_status_bar(self):
|
| 2561 |
+
"""Create the status bar showing available count and action buttons (compact)."""
|
| 2562 |
# Combined status bar and action buttons in one row
|
| 2563 |
+
self.status_frame = ctk.CTkFrame(self.content_frame, fg_color="transparent")
|
| 2564 |
+
self.status_frame.grid(row=4, column=0, sticky="ew", pady=(3, 3))
|
| 2565 |
|
| 2566 |
+
# Status label (left side, smaller font)
|
| 2567 |
self.status_label = ctk.CTkLabel(
|
| 2568 |
self.status_frame,
|
| 2569 |
text="Select a provider to begin",
|
| 2570 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2571 |
text_color=TEXT_SECONDARY,
|
| 2572 |
)
|
| 2573 |
self.status_label.pack(side="left")
|
|
|
|
| 2579 |
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2580 |
text_color=ACCENT_YELLOW,
|
| 2581 |
)
|
| 2582 |
+
self.unsaved_label.pack(side="left", padx=(10, 0))
|
| 2583 |
|
| 2584 |
+
# Buttons (right side, smaller)
|
| 2585 |
# Discard button
|
| 2586 |
discard_btn = ctk.CTkButton(
|
| 2587 |
self.status_frame,
|
| 2588 |
text="β©οΈ Discard",
|
| 2589 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL),
|
| 2590 |
fg_color=BG_SECONDARY,
|
| 2591 |
hover_color=BG_HOVER,
|
| 2592 |
border_width=1,
|
| 2593 |
border_color=BORDER_COLOR,
|
| 2594 |
+
width=85,
|
| 2595 |
+
height=26,
|
| 2596 |
command=self._discard_changes,
|
| 2597 |
)
|
| 2598 |
+
discard_btn.pack(side="right", padx=(8, 0))
|
| 2599 |
|
| 2600 |
# Save button
|
| 2601 |
save_btn = ctk.CTkButton(
|
| 2602 |
self.status_frame,
|
| 2603 |
text="πΎ Save",
|
| 2604 |
+
font=(FONT_FAMILY, FONT_SIZE_SMALL, "bold"),
|
| 2605 |
fg_color=ACCENT_GREEN,
|
| 2606 |
hover_color="#27ae60",
|
| 2607 |
+
width=75,
|
| 2608 |
+
height=26,
|
| 2609 |
command=self._save_changes,
|
| 2610 |
)
|
| 2611 |
save_btn.pack(side="right")
|