diff --git a/modules/about.py b/modules/about.py index 0511da2..46c7f1c 100644 --- a/modules/about.py +++ b/modules/about.py @@ -54,34 +54,34 @@ def get_executable_path(exec_string): """Extract and find the actual executable path from Exec field""" if not exec_string: return None - + # Remove common exec modifiers and arguments exec_parts = exec_string.split() if not exec_parts: return None - + executable = exec_parts[0] - + # Remove common prefixes - prefixes_to_remove = ['env', 'bash', 'sh', '/usr/bin/env'] + prefixes_to_remove = ["env", "bash", "sh", "/usr/bin/env"] while executable in prefixes_to_remove and len(exec_parts) > 1: exec_parts.pop(0) executable = exec_parts[0] - + # If it's already an absolute path, check if it exists - if executable.startswith('/'): + if executable.startswith("/"): if os.path.exists(executable): return executable return None - + # Search in PATH try: - result = subprocess.run(['which', executable], capture_output=True, text=True) + result = subprocess.run(["which", executable], capture_output=True, text=True) if result.returncode == 0: return result.stdout.strip() except Exception: pass - + return None @@ -96,81 +96,88 @@ def get_app_info(wmclass): "exec": "", "location": "", "categories": "", - "desktop_file": "" + "desktop_file": "", } - + desktop_paths = [ "/usr/share/applications", "/var/lib/flatpak/exports/share/applications", os.path.expanduser("~/.local/share/applications"), - "/usr/local/share/applications" + "/usr/local/share/applications", ] - + for path in desktop_paths: if not os.path.exists(path): continue - + # Try exact match first - exact_matches = [f for f in os.listdir(path) - if f.lower() == f"{wmclass.lower()}.desktop"] - + exact_matches = [ + f for f in os.listdir(path) if f.lower() == f"{wmclass.lower()}.desktop" + ] + # Then try starts with - startswith_matches = [f for f in os.listdir(path) - if f.startswith(wmclass.lower()) and f.endswith('.desktop')] - + startswith_matches = [ + f + for f in os.listdir(path) + if f.startswith(wmclass.lower()) and f.endswith(".desktop") + ] + # Finally try contains - contains_matches = [f for f in os.listdir(path) - if wmclass.lower() in f.lower() and f.endswith('.desktop')] - + contains_matches = [ + f + for f in os.listdir(path) + if wmclass.lower() in f.lower() and f.endswith(".desktop") + ] + # Process matches in order of preference for matches in [exact_matches, startswith_matches, contains_matches]: for filename in matches: desktop_file = os.path.join(path, filename) try: - with open(desktop_file, 'r', encoding='utf-8') as f: + with open(desktop_file, "r", encoding="utf-8") as f: content = f.read() - + name = wmclass.title() version = "" comment = "" icon = wmclass.lower() exec_cmd = "" categories = "" - + # Parse desktop file in_desktop_entry = False - for line in content.split('\n'): + for line in content.split("\n"): line = line.strip() - if line == '[Desktop Entry]': + if line == "[Desktop Entry]": in_desktop_entry = True continue - elif line.startswith('[') and line.endswith(']'): + elif line.startswith("[") and line.endswith("]"): in_desktop_entry = False continue - - if not in_desktop_entry or '=' not in line: + + if not in_desktop_entry or "=" not in line: continue - - key, value = line.split('=', 1) - if key == 'Name': + + key, value = line.split("=", 1) + if key == "Name": name = value - elif key == 'Version': + elif key == "Version": version = value - elif key == 'Comment': + elif key == "Comment": comment = value - elif key == 'GenericName' and not comment: + elif key == "GenericName" and not comment: # Use GenericName as fallback description comment = value - elif key == 'Icon': + elif key == "Icon": icon = value - elif key == 'Exec': + elif key == "Exec": exec_cmd = value - elif key == 'Categories': + elif key == "Categories": categories = value - + # Get executable location location = get_executable_path(exec_cmd) - + return { "name": name, "version": version, @@ -179,20 +186,22 @@ def get_app_info(wmclass): "exec": exec_cmd, "location": location or "", "categories": categories, - "desktop_file": desktop_file + "desktop_file": desktop_file, } except Exception: continue - + # Fallback: try to find executable in PATH location = "" try: - result = subprocess.run(['which', wmclass.lower()], capture_output=True, text=True) + result = subprocess.run( + ["which", wmclass.lower()], capture_output=True, text=True + ) if result.returncode == 0: location = result.stdout.strip() except Exception: pass - + return { "name": wmclass.title() if wmclass else "Unknown Application", "version": "", @@ -201,7 +210,7 @@ def get_app_info(wmclass): "exec": "", "location": location, "categories": "", - "desktop_file": "" + "desktop_file": "", } @@ -211,9 +220,9 @@ class AboutApp(Gtk.Window): self.app_name = app_name self.wmclass = wmclass self.icon_resolver = IconResolver() - - self.set_default_size(400, 500) - self.set_size_request(400, 480) + + self.set_default_size(300, 500) + self.set_size_request(300, 480) self.set_resizable(False) self.set_title(f"About {app_name}") self.set_name("about-app") @@ -223,12 +232,12 @@ class AboutApp(Gtk.Window): def setup_ui(self): app_info = get_app_info(self.wmclass) - + main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) main_box.set_margin_top(20) main_box.set_margin_bottom(20) - main_box.set_margin_start(25) - main_box.set_margin_end(25) + main_box.set_margin_start(15) + main_box.set_margin_end(15) # App Icon logo_box = Gtk.Box(halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER) @@ -242,7 +251,9 @@ class AboutApp(Gtk.Window): except Exception: # Fallback: try direct file path if it's an absolute path try: - if app_info["icon"].startswith('/') and os.path.exists(app_info["icon"]): + if app_info["icon"].startswith("/") and os.path.exists( + app_info["icon"] + ): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( app_info["icon"], 128, 128, preserve_aspect_ratio=True ) @@ -253,25 +264,28 @@ class AboutApp(Gtk.Window): # Final fallback: emoji logo = Gtk.Label() logo.set_markup("📱") - + logo_box.pack_start(logo, False, False, 0) logo_box.set_margin_bottom(15) # App Name app_name_label = Gtk.Label() - app_name_label.set_markup(f"{app_info['name']}") + app_name_label.set_markup( + f"{app_info['name']}" + ) app_name_label.set_halign(Gtk.Align.CENTER) app_name_label.set_margin_bottom(5) # Version (if available) - version_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) if app_info["version"]: - version_label = Gtk.Label(label=f"Version {app_info['version']}") + version_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + version_label = Gtk.Label( + label=f"Version {app_info.get('version', 'Unknown')}" + ) version_label.set_name("version-label") version_label.set_halign(Gtk.Align.CENTER) version_box.pack_start(version_label, False, False, 0) - - version_box.set_margin_bottom(15) + version_box.set_margin_bottom(5) # Description/Comment - Make it more prominent description_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) @@ -279,9 +293,9 @@ class AboutApp(Gtk.Window): # Create a frame for the description to make it stand out desc_frame = Gtk.Frame() desc_frame.set_shadow_type(Gtk.ShadowType.IN) - + description_label = Gtk.Label() - description_label.set_markup(f"{app_info['comment']}") + description_label.set_markup(f"{app_info['comment']}") description_label.set_justify(Gtk.Justification.CENTER) description_label.set_halign(Gtk.Align.CENTER) description_label.set_line_wrap(True) @@ -290,23 +304,30 @@ class AboutApp(Gtk.Window): description_label.set_margin_bottom(8) description_label.set_margin_start(12) description_label.set_margin_end(12) - + desc_frame.add(description_label) description_box.pack_start(desc_frame, False, False, 0) else: # Show a placeholder if no description is available placeholder_label = Gtk.Label() - placeholder_label.set_markup("No description available") + placeholder_label.set_markup( + "No description available" + ) placeholder_label.set_halign(Gtk.Align.CENTER) description_box.pack_start(placeholder_label, False, False, 0) - + description_box.set_margin_bottom(15) # Information Grid + # Create a label with markup info_frame = Gtk.Frame() - info_frame.set_label("Application Information") + label = Gtk.Label() + label.set_markup("Application Information") + label.set_margin_bottom(5) + info_frame.set_label_widget(label) + info_frame.set_label_align(0.5, 0.5) - + info_grid = Gtk.Grid() info_grid.set_row_spacing(8) info_grid.set_column_spacing(15) @@ -315,50 +336,51 @@ class AboutApp(Gtk.Window): info_grid.set_margin_start(15) info_grid.set_margin_end(15) info_grid.set_valign(Gtk.Align.CENTER) - info_grid.set_halign(Gtk.Align.FILL) + info_grid.set_halign(Gtk.Align.CENTER) def make_info_row(label_text, value_text, row): """Create a row in the info grid""" label = Gtk.Label(label=f"{label_text}:") label.set_halign(Gtk.Align.END) label.set_markup(f"{label_text}:") - - value = Gtk.Label(label=value_text) + + value = Gtk.Label() + value.set_markup(f'{value_text}') value.set_halign(Gtk.Align.START) value.set_line_wrap(True) value.set_max_width_chars(30) value.set_name("info-value-label") - + info_grid.attach(label, 0, row, 1, 1) info_grid.attach(value, 1, row, 1, 1) row = 0 - + # Application Name make_info_row("Name", app_info["name"], row) row += 1 - + # Version if app_info["version"]: make_info_row("Version", app_info["version"], row) row += 1 - + # Executable Location if app_info["location"]: make_info_row("Location", app_info["location"], row) row += 1 - + # Window Class if self.wmclass: make_info_row("Window Class", self.wmclass, row) row += 1 - + # Categories if app_info["categories"]: categories = app_info["categories"].replace(";", ", ").strip(", ") make_info_row("Categories", categories, row) row += 1 - + # Desktop File if app_info["desktop_file"]: desktop_file_name = os.path.basename(app_info["desktop_file"]) @@ -370,7 +392,8 @@ class AboutApp(Gtk.Window): # Layout main_box.pack_start(logo_box, False, False, 0) main_box.pack_start(app_name_label, False, False, 0) - main_box.pack_start(version_box, False, False, 0) + if app_info["version"]: + main_box.pack_start(version_box, False, False, 0) main_box.pack_start(description_box, False, False, 0) main_box.pack_start(info_frame, False, False, 0)