Speed optimizations for the menu.
This commit is contained in:
parent
fdc2fdc41d
commit
0368d764c6
152
scripts/menu.py
152
scripts/menu.py
@ -22,10 +22,15 @@ from gi.repository import Gtk, Gdk, GLib, Atk
|
|||||||
def read_desktop_files(paths):
|
def read_desktop_files(paths):
|
||||||
desktopEntries = []
|
desktopEntries = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
if not Path(path).exists():
|
||||||
|
continue
|
||||||
for file in Path(path).rglob('*.desktop'):
|
for file in Path(path).rglob('*.desktop'):
|
||||||
config = configparser.ConfigParser(interpolation=None)
|
try:
|
||||||
config.read(file)
|
config = configparser.ConfigParser(interpolation=None)
|
||||||
desktopEntries.append(config)
|
config.read(file, encoding='utf-8')
|
||||||
|
desktopEntries.append(config)
|
||||||
|
except (UnicodeDecodeError, configparser.Error):
|
||||||
|
continue
|
||||||
return desktopEntries
|
return desktopEntries
|
||||||
|
|
||||||
userApplicationsPath = Path.home() / '.local/share/applications'
|
userApplicationsPath = Path.home() / '.local/share/applications'
|
||||||
@ -93,34 +98,24 @@ subcategories = defaultdict(set)
|
|||||||
for entry in desktopEntries:
|
for entry in desktopEntries:
|
||||||
try:
|
try:
|
||||||
# Check if NoDisplay=true is set
|
# Check if NoDisplay=true is set
|
||||||
try:
|
if entry.getboolean('Desktop Entry', 'NoDisplay', fallback=False):
|
||||||
noDisplay = entry.getboolean('Desktop Entry', 'NoDisplay', fallback=False)
|
continue
|
||||||
if noDisplay:
|
|
||||||
continue
|
name = entry.get('Desktop Entry', 'Name', fallback=None)
|
||||||
except:
|
execCommand = entry.get('Desktop Entry', 'Exec', fallback=None)
|
||||||
pass
|
|
||||||
|
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(';')
|
entryCategories = entry.get('Desktop Entry', 'Categories', fallback='').split(';')
|
||||||
|
|
||||||
# For applications with categories
|
# For applications with categories
|
||||||
mainCategory = None
|
mainCategory = None
|
||||||
for category in entryCategories:
|
validCategories = [cat for cat in entryCategories if cat.strip()]
|
||||||
if category: # Skip empty strings
|
|
||||||
mappedCategory = categoryMap.get(category, category)
|
|
||||||
if mainCategory is None:
|
|
||||||
mainCategory = mappedCategory
|
|
||||||
|
|
||||||
# Check if this might be a subcategory
|
if validCategories:
|
||||||
for other in entryCategories:
|
# Use first valid category as main
|
||||||
if other and other != category:
|
mainCategory = categoryMap.get(validCategories[0], validCategories[0])
|
||||||
mappedOther = categoryMap.get(other, other)
|
|
||||||
if mappedCategory != mappedOther:
|
|
||||||
subcategories[mappedOther].add(mappedCategory)
|
|
||||||
|
|
||||||
# If we found a category, add the application
|
|
||||||
if mainCategory:
|
|
||||||
categoryApps[mainCategory][name] = execCommand
|
categoryApps[mainCategory][name] = execCommand
|
||||||
else:
|
else:
|
||||||
# If no category was found, add to "Other"
|
# If no category was found, add to "Other"
|
||||||
@ -194,58 +189,17 @@ class I38_Tab_Menu(Gtk.Window):
|
|||||||
sortedCategories.remove("All Applications")
|
sortedCategories.remove("All Applications")
|
||||||
sortedCategories.insert(0, "All Applications")
|
sortedCategories.insert(0, "All Applications")
|
||||||
|
|
||||||
# Create tabs
|
# Create tabs - defer TreeView creation for performance
|
||||||
for category in sortedCategories:
|
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
|
if not categoryApps[category]: # Skip empty categories
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create a TreeStore for this category
|
# Create placeholder scrolled window
|
||||||
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
|
|
||||||
scrolledWindow = Gtk.ScrolledWindow()
|
scrolledWindow = Gtk.ScrolledWindow()
|
||||||
scrolledWindow.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
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
|
# Create tab label
|
||||||
tabLabel = Gtk.Label(label=category)
|
tabLabel = Gtk.Label(label=category)
|
||||||
@ -254,13 +208,14 @@ class I38_Tab_Menu(Gtk.Window):
|
|||||||
self.notebook.append_page(scrolledWindow, tabLabel)
|
self.notebook.append_page(scrolledWindow, tabLabel)
|
||||||
|
|
||||||
# Set tab accessibility properties for screen readers
|
# Set tab accessibility properties for screen readers
|
||||||
tabChild = self.notebook.get_nth_page(self.notebook.get_n_pages() - 1)
|
accessible = scrolledWindow.get_accessible()
|
||||||
# Get the accessible object and set properties on it instead
|
|
||||||
accessible = tabChild.get_accessible()
|
|
||||||
accessible.set_name(f"{category} applications")
|
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)
|
accessible.set_role(Atk.Role.LIST)
|
||||||
|
|
||||||
|
# Create first tab immediately
|
||||||
|
if sortedCategories:
|
||||||
|
self.create_tab_content(0)
|
||||||
|
|
||||||
# Connect notebook signals
|
# Connect notebook signals
|
||||||
self.notebook.connect("switch-page", self.on_switch_page)
|
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_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.")
|
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):
|
def populate_completion_store(self):
|
||||||
"""Populate completion store with all available applications"""
|
"""Populate completion store with all available applications"""
|
||||||
self.completionStore.clear()
|
self.completionStore.clear()
|
||||||
@ -286,6 +283,9 @@ class I38_Tab_Menu(Gtk.Window):
|
|||||||
self.completionStore.append([appName, execCommand])
|
self.completionStore.append([appName, execCommand])
|
||||||
|
|
||||||
def on_switch_page(self, notebook, page, pageNum):
|
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
|
# Focus the TreeView in the newly selected tab
|
||||||
tab = notebook.get_nth_page(pageNum)
|
tab = notebook.get_nth_page(pageNum)
|
||||||
for child in tab.get_children():
|
for child in tab.get_children():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user