""" Main Window Main GUI window that integrates all frames. """ import customtkinter as ctk from ui.login_frame import LoginFrame from ui.subscription_selection_frame import SubscriptionSelectionFrame from ui.app_selection_frame import AppSelectionFrame from ui.secret_generation_frame import SecretGenerationFrame from ui.result_frame import ResultFrame from typing import Callable, Dict, List class MainWindow(ctk.CTk): """Main application window.""" def __init__( self, on_connect: Callable = None, on_subscription_selected: Callable = None, on_app_selected: Callable = None, on_generate_secret: Callable = None, on_generate_another: Callable = None ): """ Initialize the main window. Args: on_connect: Callback for authentication on_subscription_selected: Callback when subscription is selected on_app_selected: Callback when app is selected on_generate_secret: Callback when generate secret is clicked on_generate_another: Callback when generate another is clicked """ super().__init__() # Configure window self.title("Azure Key Vault Secret Manager") # Center window on screen window_width = 950 window_height = 850 # Get screen dimensions screen_width = self.winfo_screenwidth() screen_height = self.winfo_screenheight() # Calculate center position center_x = int((screen_width - window_width) / 2) center_y = int((screen_height - window_height) / 2) # Set geometry with center position self.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") # Set application icon (if available) self._set_icon() # Configure grid self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) # Title self.title_label = ctk.CTkLabel( self, text="Azure Key Vault Secret Manager", font=ctk.CTkFont(size=28, weight="bold") ) self.title_label.grid(row=0, column=0, padx=20, pady=20, sticky="n") # Scrollable frame for content with optimized scrolling self.scroll_frame = ctk.CTkScrollableFrame( self, scrollbar_button_color=("gray75", "gray25"), scrollbar_button_hover_color=("gray65", "gray35") ) self.scroll_frame.grid(row=1, column=0, padx=20, pady=(0, 20), sticky="nsew") self.scroll_frame.grid_columnconfigure(0, weight=1) # Optimize scroll step for smoother scrolling (40px = 2x faster than default 20px) self.scroll_frame._parent_canvas.configure(yscrollincrement=40) # Add smooth mouse wheel scrolling def smooth_scroll(event): """Handle smooth mouse wheel scrolling.""" # Platform-specific delta handling if event.delta > 0: delta = -1 # Scroll up else: delta = 1 # Scroll down self.scroll_frame._parent_canvas.yview_scroll(delta, "units") return "break" # Prevent event propagation # Bind mouse wheel event (Windows/Mac) self.scroll_frame._parent_canvas.bind_all("", smooth_scroll, add="+") # Login Frame self.login_frame = LoginFrame(self.scroll_frame, on_connect=on_connect) self.login_frame.grid(row=0, column=0, padx=0, pady=(0, 15), sticky="ew") # Subscription Selection Frame self.subscription_selection_frame = SubscriptionSelectionFrame( self.scroll_frame, on_subscription_selected=on_subscription_selected ) self.subscription_selection_frame.grid(row=1, column=0, padx=0, pady=(0, 15), sticky="ew") self.subscription_selection_frame.set_enabled(False) # App Selection Frame self.app_selection_frame = AppSelectionFrame( self.scroll_frame, on_app_selected=on_app_selected ) self.app_selection_frame.grid(row=2, column=0, padx=0, pady=(0, 15), sticky="ew") self.app_selection_frame.set_enabled(False) # Secret Generation Frame self.secret_generation_frame = SecretGenerationFrame( self.scroll_frame, on_generate=on_generate_secret ) self.secret_generation_frame.grid(row=3, column=0, padx=0, pady=(0, 15), sticky="ew") self.secret_generation_frame.set_enabled(False) # Result Frame (initially hidden) self.result_frame = ResultFrame( self.scroll_frame, on_generate_another=on_generate_another ) self.result_frame.grid(row=4, column=0, padx=0, pady=(0, 15), sticky="ew") self.result_frame.hide() def set_authenticated(self, authenticated: bool): """ Update UI after authentication. Args: authenticated: Whether authentication succeeded """ self.login_frame.set_authenticated(authenticated) if authenticated: self.subscription_selection_frame.set_enabled(True) def set_connecting(self): """Set UI to connecting state.""" self.login_frame.set_connecting() def enable_connect_button(self): """Enable connect button for retry.""" self.login_frame.enable_button() def set_subscriptions(self, subscriptions: List[Dict[str, str]]): """ Set the list of subscriptions. Args: subscriptions: List of subscription dictionaries """ self.subscription_selection_frame.set_subscriptions(subscriptions) def set_subscription_selected(self): """Enable UI after subscription is selected.""" self.app_selection_frame.set_enabled(True) self.secret_generation_frame.set_enabled(True) def set_apps(self, apps: List[Dict[str, str]]): """ Set the list of applications. Args: apps: List of app dictionaries """ self.app_selection_frame.set_apps(apps) def set_vaults(self, vaults: List[Dict[str, str]]): """ Set the list of Key Vaults. Args: vaults: List of vault dictionaries """ self.secret_generation_frame.set_vaults(vaults) def set_loading_subscriptions(self, loading: bool): """ Set loading subscriptions state. Args: loading: Whether currently loading """ self.subscription_selection_frame.set_loading(loading) def set_loading_apps(self, loading: bool): """ Set loading apps state. Args: loading: Whether currently loading """ self.app_selection_frame.set_loading(loading) def set_loading_vaults(self, loading: bool): """ Set loading vaults state. Args: loading: Whether currently loading """ self.secret_generation_frame.set_loading_vaults(loading) def set_generating(self, generating: bool): """ Set generating secret state. Args: generating: Whether currently generating """ self.secret_generation_frame.set_generating(generating) def show_result( self, secret_name: str, vault_name: str, secret_value: str, removed_count: int = 0 ): """ Show secret generation result. Args: secret_name: The sanitized secret name vault_name: The Key Vault name secret_value: The secret value removed_count: Number of old secrets removed """ self.result_frame.show_result(secret_name, vault_name, secret_value, removed_count) def reset_form(self): """Reset the secret generation form.""" self.secret_generation_frame.reset() self.result_frame.hide() def get_selected_app(self) -> Dict[str, str]: """Get the currently selected app.""" return self.app_selection_frame.get_selected_app() def get_description(self) -> str: """Get the secret description.""" return self.secret_generation_frame.get_description() def get_selected_vault(self) -> Dict[str, str]: """Get the currently selected vault.""" return self.secret_generation_frame.get_selected_vault() def get_remove_old_secrets(self) -> bool: """Get whether to remove old secrets.""" return self.secret_generation_frame.get_remove_old_secrets() def _set_icon(self): """Set the application icon if available.""" import os from pathlib import Path # Get the directory where the script is located script_dir = Path(__file__).parent.parent # Try common icon file names and locations icon_paths = [ script_dir / "icon.ico", script_dir / "assets" / "icon.ico", script_dir / "icon.png", script_dir / "assets" / "icon.png", ] for icon_path in icon_paths: if icon_path.exists(): try: if icon_path.suffix == '.ico': # Use .ico file directly (Windows) self.iconbitmap(str(icon_path)) print(f"Icon loaded: {icon_path}") return elif icon_path.suffix == '.png': # Use .png file with iconphoto (cross-platform) import tkinter as tk from PIL import Image, ImageTk img = Image.open(icon_path) photo = ImageTk.PhotoImage(img) self.iconphoto(True, photo) print(f"Icon loaded: {icon_path}") return except Exception as e: print(f"Failed to load icon from {icon_path}: {str(e)}") # No icon found - use default print("No custom icon found. Using default application icon.")