Speed optimizations for the menu.

This commit is contained in:
Storm Dragon 2025-06-07 22:47:42 -04:00
parent fdc2fdc41d
commit 0368d764c6

View File

@ -22,10 +22,15 @@ from gi.repository import Gtk, Gdk, GLib, Atk
def read_desktop_files(paths):
desktopEntries = []
for path in paths:
if not Path(path).exists():
continue
for file in Path(path).rglob('*.desktop'):
config = configparser.ConfigParser(interpolation=None)
config.read(file)
desktopEntries.append(config)
try:
config = configparser.ConfigParser(interpolation=None)
config.read(file, encoding='utf-8')
desktopEntries.append(config)
except (UnicodeDecodeError, configparser.Error):
continue
return desktopEntries
userApplicationsPath = Path.home() / '.local/share/applications'
@ -93,34 +98,24 @@ subcategories = defaultdict(set)
for entry in desktopEntries:
try:
# Check if NoDisplay=true is set
try:
noDisplay = entry.getboolean('Desktop Entry', 'NoDisplay', fallback=False)
if noDisplay:
continue
except:
pass
if entry.getboolean('Desktop Entry', 'NoDisplay', fallback=False):
continue
name = entry.get('Desktop Entry', 'Name', fallback=None)
execCommand = entry.get('Desktop Entry', 'Exec', fallback=None)
if not name or not execCommand:
continue
name = entry.get('Desktop Entry', 'Name')
execCommand = entry.get('Desktop Entry', 'Exec')
entryCategories = entry.get('Desktop Entry', 'Categories', fallback='').split(';')
# For applications with categories
mainCategory = None
for category in entryCategories:
if category: # Skip empty strings
mappedCategory = categoryMap.get(category, category)
if mainCategory is None:
mainCategory = mappedCategory
# Check if this might be a subcategory
for other in entryCategories:
if other and other != category:
mappedOther = categoryMap.get(other, other)
if mappedCategory != mappedOther:
subcategories[mappedOther].add(mappedCategory)
validCategories = [cat for cat in entryCategories if cat.strip()]
# If we found a category, add the application
if mainCategory:
if validCategories:
# Use first valid category as main
mainCategory = categoryMap.get(validCategories[0], validCategories[0])
categoryApps[mainCategory][name] = execCommand
else:
# If no category was found, add to "Other"
@ -194,58 +189,17 @@ class I38_Tab_Menu(Gtk.Window):
sortedCategories.remove("All Applications")
sortedCategories.insert(0, "All Applications")
# Create tabs
for category in sortedCategories:
# Create tabs - defer TreeView creation for performance
self.tabCategories = sortedCategories
self.createdTabs = set() # Track which tabs have been created
for i, category in enumerate(sortedCategories):
if not categoryApps[category]: # Skip empty categories
continue
# Create a TreeStore for this category
store = Gtk.TreeStore(str, str) # Columns: Name, Exec
self.stores[category] = store
# Add applications to this category's store
sortedApps = sorted(categoryApps[category].items())
# Check for potential subcategories within this category
categorySubcategories = {}
for appName, appExec in sortedApps:
subcatFound = False
for subcat in subcategories.get(category, []):
if appName in categoryApps.get(subcat, {}):
if subcat not in categorySubcategories:
categorySubcategories[subcat] = []
categorySubcategories[subcat].append((appName, appExec))
subcatFound = True
break
if not subcatFound:
# Add directly to the category's root
store.append(None, [appName, appExec])
# Add any subcategories
for subcat, subcatApps in sorted(categorySubcategories.items()):
subcatIter = store.append(None, [subcat, None])
for appName, appExec in sorted(subcatApps):
store.append(subcatIter, [appName, appExec])
# Create TreeView for this category
treeView = Gtk.TreeView(model=store)
treeView.set_headers_visible(False)
self.treeViews[category] = treeView
# Add column for application names
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Applications", renderer, text=0)
treeView.append_column(column)
# Set up scrolled window
# Create placeholder scrolled window
scrolledWindow = Gtk.ScrolledWindow()
scrolledWindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolledWindow.add(treeView)
# Connect signals
treeView.connect("row-activated", self.on_row_activated)
treeView.connect("key-press-event", self.on_key_press)
# Create tab label
tabLabel = Gtk.Label(label=category)
@ -254,13 +208,14 @@ class I38_Tab_Menu(Gtk.Window):
self.notebook.append_page(scrolledWindow, tabLabel)
# Set tab accessibility properties for screen readers
tabChild = self.notebook.get_nth_page(self.notebook.get_n_pages() - 1)
# Get the accessible object and set properties on it instead
accessible = tabChild.get_accessible()
accessible = scrolledWindow.get_accessible()
accessible.set_name(f"{category} applications")
# Use Atk role instead of Gtk.AccessibleRole which isn't available in GTK 3.0
accessible.set_role(Atk.Role.LIST)
# Create first tab immediately
if sortedCategories:
self.create_tab_content(0)
# Connect notebook signals
self.notebook.connect("switch-page", self.on_switch_page)
@ -276,6 +231,48 @@ class I38_Tab_Menu(Gtk.Window):
windowAccessible.set_name("I38 Application Menu")
windowAccessible.set_description("Tab-based application launcher menu. Press slash to search, type app name and use down arrow to navigate results. Type letters to incrementally navigate to matching applications.")
def create_tab_content(self, tabIndex):
"""Create TreeView content for a tab on demand"""
if tabIndex in self.createdTabs or tabIndex >= len(self.tabCategories):
return
category = self.tabCategories[tabIndex]
if not categoryApps[category]:
return
# Create a TreeStore for this category
store = Gtk.TreeStore(str, str) # Columns: Name, Exec
self.stores[category] = store
# Add applications to this category's store (simplified - no subcategories)
sortedApps = sorted(categoryApps[category].items())
for appName, appExec in sortedApps:
store.append(None, [appName, appExec])
# Create TreeView for this category
treeView = Gtk.TreeView(model=store)
treeView.set_headers_visible(False)
self.treeViews[category] = treeView
# Add column for application names
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Applications", renderer, text=0)
treeView.append_column(column)
# Get the scrolled window for this tab
scrolledWindow = self.notebook.get_nth_page(tabIndex)
scrolledWindow.add(treeView)
# Connect signals
treeView.connect("row-activated", self.on_row_activated)
treeView.connect("key-press-event", self.on_key_press)
# Show the new content
treeView.show()
# Mark this tab as created
self.createdTabs.add(tabIndex)
def populate_completion_store(self):
"""Populate completion store with all available applications"""
self.completionStore.clear()
@ -286,6 +283,9 @@ class I38_Tab_Menu(Gtk.Window):
self.completionStore.append([appName, execCommand])
def on_switch_page(self, notebook, page, pageNum):
# Create tab content if not already created
self.create_tab_content(pageNum)
# Focus the TreeView in the newly selected tab
tab = notebook.get_nth_page(pageNum)
for child in tab.get_children():