diff --git a/modules/launcher/main.py b/modules/launcher/main.py
index 97bc8de..08cc695 100644
--- a/modules/launcher/main.py
+++ b/modules/launcher/main.py
@@ -1,15 +1,15 @@
from typing import List, Optional, Tuple
+from gi.repository import Gdk, GLib
+
from fabric.core.service import Property
from fabric.widgets.box import Box
from fabric.widgets.entry import Entry
-from fabric.widgets.scrolledwindow import ScrolledWindow
-from gi.repository import Gdk, GLib
-
from modules.launcher.plugin_manager import PluginManager
from modules.launcher.result import Result
from modules.launcher.result_item import ResultItem
from modules.launcher.trigger_config import TriggerConfig
+from widgets.smartscrolled import SmartScrolledBox
from widgets.wayland import WaylandWindow as Window
# Constants
@@ -44,26 +44,15 @@ class Launcher(Window):
keyboard_mode="exclusive",
**kwargs,
)
-
- # Initialization flag to prevent callbacks during setup
self._initializing = True
-
- # Flag to prevent recursion when automatically adding spaces
self._auto_adding_space = False
-
- # Flag to prevent search change handler interference during backspace processing
self._processing_backspace = False
-
- # Initialize plugin manager
self.plugin_manager = PluginManager()
-
- # Initialize trigger configuration
self.trigger_config = TriggerConfig()
- # Current results and selection
self.results: List[Result] = []
self.selected_index = 0
- self.max_results = 8 # Spotlight typically shows fewer results
+ self.max_results = 5 # Show 4 applications by default instead of list
# Trigger system
self.triggered_plugin = None # Currently active triggered plugin
@@ -75,6 +64,16 @@ class Launcher(Window):
# Focus management
self.focus_mode = "search" # "search", "results"
+ # Mouse activity tracking
+ self._mouse_active = False
+ self._last_mouse_activity = 0
+ self._mouse_activity_timeout = 1000 # 1 second timeout
+ self._keyboard_used_recently = False
+ self._last_keyboard_activity = 0
+ self._keyboard_activity_timeout = 2000 # 2 second timeout
+ self._launcher_just_opened = False
+ self._mouse_interaction_delay = 500 # 500ms delay after opening
+
# Setup UI
main_box = Box(
name="launcher",
@@ -107,28 +106,29 @@ class Launcher(Window):
main_box.add(self.header_box)
- # Results container with increased height for Spotlight-style
- self.results_scroll = ScrolledWindow(
- name="launcher-results-scroll",
- h_scrollbar_policy="never",
- min_content_size=(LAUNCHER_WIDTH, LAUNCHER_HEIGHT),
- max_content_size=(LAUNCHER_WIDTH, LAUNCHER_HEIGHT),
- propagate_width=False,
- propagate_height=False,
- )
+ self.results_scroll = SmartScrolledBox(max_height=LAUNCHER_HEIGHT)
self.results_box = Box(
name="launcher-results",
orientation="v",
spacing=0,
)
- self.results_scroll.add(self.results_box)
+ self.results_scroll.append(self.results_box)
main_box.add(self.results_scroll)
- # Initially hide the results container
- self.results_scroll.hide()
+ # Keep the results container visible initially
self.connect("key-press-event", self._on_key_press)
+
+ # Connect mouse events to track activity
+ self.results_scroll.connect("button-press-event", self._on_mouse_activity)
+ self.results_scroll.connect("motion-notify-event", self._on_mouse_activity)
+ self.results_scroll.connect("scroll-event", self._on_mouse_activity)
+
+ # Also track mouse activity on the main window for better coverage
+ self.connect("button-press-event", self._on_mouse_activity)
+ self.connect("motion-notify-event", self._on_mouse_activity)
+
self.hide()
# Mark initialization as complete
@@ -144,6 +144,20 @@ class Launcher(Window):
trigger_keyword: Optional trigger keyword to activate immediately (e.g., "google", "calc", "app")
external: If True, execute the command without showing the launcher UI
"""
+ # Reset mouse activity tracking when launcher is shown
+ self._mouse_active = False
+ self._last_mouse_activity = 0
+ self._keyboard_used_recently = False
+ self._last_keyboard_activity = 0
+ self._launcher_just_opened = True
+
+ # Schedule to allow mouse interactions after a delay
+ def enable_mouse_interactions():
+ self._launcher_just_opened = False
+ return False
+
+ GLib.timeout_add(self._mouse_interaction_delay, enable_mouse_interactions)
+
if external and trigger_keyword:
# Execute command externally without showing launcher
return self._execute_external_command(trigger_keyword)
@@ -180,10 +194,11 @@ class Launcher(Window):
self.search_entry.set_text("")
self._clear_results()
else:
- # Normal launcher opening - clear everything
+ # Normal launcher opening - show applications
self.opened_with_trigger = False
self.search_entry.set_text("")
- self._clear_results()
+ # Trigger initial search to show applications immediately
+ self._perform_search("")
# Reset focus mode to search
self.focus_mode = "search"
@@ -305,8 +320,8 @@ class Launcher(Window):
# Debounce search to avoid too many queries
GLib.timeout_add(SEARCH_DEBOUNCE_MS, self._perform_search, query)
else:
- # Hide results when query is empty
- self._clear_results()
+ # Show applications when query is empty
+ self._perform_search("")
def _perform_search(self, query: str) -> bool:
"""Perform search across all plugins."""
@@ -315,10 +330,25 @@ class Launcher(Window):
return False
if not query:
- # Empty query - reset trigger mode and hide results
+ # Empty query - show popular applications
self.triggered_plugin = None
self.active_trigger = ""
- self._clear_results()
+
+ # Get applications plugin and show popular apps
+ applications_plugin = self._get_applications_plugin()
+ if applications_plugin:
+ try:
+ # Query with empty string to get popular/frequently used applications
+ all_results = applications_plugin.query("")
+ # Limit to max_results for empty query
+ self.results = all_results[: self.max_results]
+ self.selected_index = 0
+ self._update_results_display()
+ except Exception as e:
+ print(f"Error getting applications: {e}")
+ self._clear_results()
+ else:
+ self._clear_results()
return False
# Check if we're already in trigger mode
@@ -365,13 +395,24 @@ class Launcher(Window):
print(f"Error in triggered plugin {triggered_plugin.name}: {e}")
all_results = []
else:
- # No trigger detected - only show trigger suggestions
+ # No trigger detected - search applications and show trigger suggestions
self.triggered_plugin = None
self.active_trigger = ""
- # Show trigger suggestions if query matches trigger prefixes
+ all_results = []
+
+ # Search applications directly without trigger
+ applications_plugin = self._get_applications_plugin()
+ if applications_plugin:
+ try:
+ app_results = applications_plugin.query(query)
+ all_results.extend(app_results)
+ except Exception as e:
+ print(f"Error searching applications: {e}")
+
+ # Also show trigger suggestions if query matches trigger prefixes
trigger_suggestions = self._get_trigger_suggestions(query)
- all_results = trigger_suggestions
+ all_results.extend(trigger_suggestions)
# Sort results by relevance score
all_results.sort(key=lambda r: r.relevance, reverse=True)
@@ -387,8 +428,19 @@ class Launcher(Window):
# In trigger mode - show all results from the triggered plugin
self.results = all_results
elif not has_bypass:
- # Global search or trigger suggestions - apply max_results limit
- self.results = all_results[: self.max_results]
+ # Global search - check if it's an application search
+ applications_plugin = self._get_applications_plugin()
+ is_application_search = applications_plugin and any(
+ hasattr(r, "plugin_name") and r.plugin_name == "Applications"
+ for r in all_results
+ )
+
+ if is_application_search:
+ # Don't limit application search results
+ self.results = all_results
+ else:
+ # Apply max_results limit for other global searches and trigger suggestions
+ self.results = all_results[: self.max_results]
else:
# Has bypass flag - show all results
self.results = all_results
@@ -462,6 +514,16 @@ class Launcher(Window):
return None, ""
+ def _get_applications_plugin(self):
+ """Get the applications plugin instance."""
+ for plugin in self.plugin_manager.get_active_plugins():
+ if (
+ hasattr(plugin, "display_name")
+ and plugin.display_name == "Applications"
+ ):
+ return plugin
+ return None
+
def _get_trigger_suggestions(self, query: str) -> List[Result]:
"""
Get trigger suggestions based on the current query.
@@ -515,7 +577,7 @@ class Launcher(Window):
return Result(
title=f"{trigger_clean}",
subtitle=f"{description} - {', '.join(examples[:max_examples])}",
- icon_markup=icon_name,
+ icon_name=icon_name,
action=lambda t=trigger_clean: self._activate_trigger(t),
# Shorter triggers get higher relevance
relevance=100 - len(trigger_clean),
@@ -678,11 +740,11 @@ class Launcher(Window):
self.results_box.show_all()
- # Show/hide scroll container based on results
- if self.results:
- self.results_scroll.show()
- else:
- self.results_scroll.hide()
+ # Force height constraints after adding results
+ self.results_scroll.force_height_constraint()
+
+ # Always show scroll container
+ self.results_scroll.show()
def _update_input_action_text(self):
"""Update the input field with action text (Spotlight-style)."""
@@ -716,8 +778,6 @@ class Launcher(Window):
# Not in trigger mode - show Spotlight-style help
if current_text == ":":
self.search_entry.set_placeholder_text("Available search triggers")
- elif current_text:
- self.search_entry.set_placeholder_text("Spotlight Search")
else:
self.search_entry.set_placeholder_text("Spotlight Search")
@@ -727,7 +787,7 @@ class Launcher(Window):
self.selected_index = 0
for child in self.results_box.get_children():
self.results_box.remove(child)
- self.results_scroll.hide()
+ # Keep the results scroll visible even when empty
def _handle_escape_key(self) -> bool:
"""Handle escape key press."""
@@ -816,6 +876,13 @@ class Launcher(Window):
def _on_key_press(self, _widget, event):
"""Handle key press events."""
+ # Track keyboard activity
+ import time
+
+ current_time = int(time.time() * 1000)
+ self._last_keyboard_activity = current_time
+ self._keyboard_used_recently = True
+
keyval = event.keyval
# Escape - handle password entry, exit trigger mode, or hide launcher
@@ -1011,16 +1078,58 @@ class Launcher(Window):
def _on_result_clicked(self, _result_item, index):
"""Handle result item click."""
- self.selected_index = index
- self._activate_selected()
+ # Only allow clicks when mouse is active or keyboard hasn't been used recently
+ if self._should_allow_mouse_interaction():
+ self.selected_index = index
+ self._activate_selected()
def _on_result_hovered(self, index):
"""Handle result item hover."""
- # Update selection when mouse hovers over a result (without scrolling)
- if 0 <= index < len(self.results):
- self.selected_index = index
- self.focus_mode = "results" # Switch to results focus mode
- self._update_selection_visual_only()
+ # Only allow hover selection when mouse is active and keyboard hasn't been used recently
+ if self._should_allow_mouse_interaction():
+ if 0 <= index < len(self.results):
+ self.selected_index = index
+ self.focus_mode = "results" # Switch to results focus mode
+ self._update_selection_visual_only()
+
+ def _on_mouse_activity(self, widget, event):
+ """Track mouse activity to determine when mouse interactions should be enabled."""
+ import time
+
+ current_time = int(time.time() * 1000)
+ self._last_mouse_activity = current_time
+ self._mouse_active = True
+
+ # Schedule a check to disable mouse activity after timeout
+ def check_mouse_timeout():
+ if current_time - self._last_mouse_activity >= self._mouse_activity_timeout:
+ self._mouse_active = False
+ return False
+
+ GLib.timeout_add(self._mouse_activity_timeout, check_mouse_timeout)
+ return False
+
+ def _should_allow_mouse_interaction(self):
+ """Determine if mouse interactions should be allowed."""
+ import time
+
+ current_time = int(time.time() * 1000)
+
+ # Don't allow mouse interactions immediately after launcher opens
+ if self._launcher_just_opened:
+ return False
+
+ # Allow mouse interaction if:
+ # 1. Mouse is currently active (recent activity)
+ # 2. OR keyboard hasn't been used recently (2 seconds)
+ mouse_recently_active = (
+ current_time - self._last_mouse_activity
+ ) < self._mouse_activity_timeout
+ keyboard_not_recently_used = (
+ current_time - self._last_keyboard_activity
+ ) > self._keyboard_activity_timeout
+
+ return mouse_recently_active or keyboard_not_recently_used
def _is_mouse_over_results(self):
"""Check if mouse is over the results area."""
diff --git a/modules/launcher/plugins/applications.py b/modules/launcher/plugins/applications.py
index 1e904f9..50503fe 100644
--- a/modules/launcher/plugins/applications.py
+++ b/modules/launcher/plugins/applications.py
@@ -4,7 +4,6 @@ from typing import List
from fabric.utils import DesktopApp
from fabric.utils.helpers import get_desktop_applications, get_relative_path
-
from modules.dock import Dock
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -17,8 +16,7 @@ class ApplicationsPlugin(PluginBase):
self.description = "Search and launch desktop applications"
def initialize(self):
- # Set up triggers for applications - both with and without spaces
- self.set_triggers(["app"])
+ pass
def cleanup(self):
pass
@@ -71,9 +69,8 @@ class ApplicationsPlugin(PluginBase):
def query(self, query_string: str) -> List[Result]:
"""Search applications based on query."""
if not query_string.strip():
- return []
+ return self._get_all_applications()
- # Get fresh applications list each time (like examples/app-launcher)
try:
applications = get_desktop_applications(include_hidden=False)
except Exception as e:
@@ -86,9 +83,8 @@ class ApplicationsPlugin(PluginBase):
for app in applications:
relevance = self._calculate_relevance(app, query)
if relevance > 0:
- # Truncate description to prevent overflow beyond 550px window
description = app.description or app.generic_name or ""
- if len(description) > 80: # Limit to ~80 characters to fit in 550px
+ if len(description) > 80:
description = description[:70] + "..."
result = Result(
@@ -161,3 +157,35 @@ class ApplicationsPlugin(PluginBase):
def _launch_application(self, app: DesktopApp):
app.launch()
+
+ def _get_all_applications(self) -> List[Result]:
+ """Get a list of all available applications."""
+ try:
+ applications = get_desktop_applications(include_hidden=False)
+ except Exception as e:
+ print(f"Failed to load applications: {e}")
+ return []
+
+ results = []
+
+ for app in applications:
+ # Truncate description
+ description = app.description or app.generic_name or ""
+ if len(description) > 80:
+ description = description[:70] + "..."
+
+ result = Result(
+ title=app.display_name or app.name,
+ subtitle=description,
+ icon=app.get_icon_pixbuf(size=48),
+ action=lambda a=app: self._launch_application(a),
+ relevance=0.5, # Default relevance for all apps
+ plugin_name=self.display_name,
+ data={
+ "app": app,
+ "pin_action": lambda a=app: self._pin_application(a),
+ },
+ )
+ results.append(result)
+
+ return results
diff --git a/modules/launcher/plugins/bash_scripts.py b/modules/launcher/plugins/bash_scripts.py
index dff923b..04aae0e 100644
--- a/modules/launcher/plugins/bash_scripts.py
+++ b/modules/launcher/plugins/bash_scripts.py
@@ -4,10 +4,8 @@ import threading
import time
from typing import Dict, List
-from fabric.utils import exec_shell_command_async
-
import config.data as data
-import utils.icons as icons
+from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -38,17 +36,17 @@ class BashScriptsPlugin(PluginBase):
"pm balanced": {
"description": "Switch to balanced power profile",
"profile": "balanced",
- "icon": icons.power_balanced,
+ "icon": "power-balanced",
},
"pm performance": {
"description": "Switch to performance power profile",
"profile": "performance",
- "icon": icons.power_performance,
+ "icon": "power-performance",
},
"pm saver": {
"description": "Switch to power saver profile",
"profile": "power-saver",
- "icon": icons.power_saving,
+ "icon": "power-saving",
},
}
@@ -134,12 +132,16 @@ class BashScriptsPlugin(PluginBase):
if actual_profile:
self._battery_service._profile_proxy.ActiveProfile = actual_profile
print(
- f"BashScriptsPlugin: Successfully set power profile to {actual_profile}"
+ f"BashScriptsPlugin: Successfully set power profile to {
+ actual_profile
+ }"
)
return True
else:
print(
- f"BashScriptsPlugin: No matching profile found for '{profile}' in available profiles: {available_profiles}"
+ f"BashScriptsPlugin: No matching profile found for '{
+ profile
+ }' in available profiles: {available_profiles}"
)
return False
@@ -186,7 +188,6 @@ class BashScriptsPlugin(PluginBase):
current_time - self._last_cache_update > self._cache_update_interval
or not self._scripts_cache
):
-
if not self._cache_building:
self._cache_building = True
self._cache_thread = threading.Thread(
@@ -197,7 +198,6 @@ class BashScriptsPlugin(PluginBase):
def _build_scripts_cache_background(self):
"""Build scripts cache in background thread."""
try:
-
new_cache = {}
# Scan Modus scripts directory for discovered scripts
@@ -208,7 +208,9 @@ class BashScriptsPlugin(PluginBase):
self._scan_directory_for_scripts(self.modus_scripts_dir, new_cache)
except (PermissionError, FileNotFoundError, OSError) as e:
print(
- f"BashScriptsPlugin: Error scanning Modus scripts directory: {e}"
+ f"BashScriptsPlugin: Error scanning Modus scripts directory: {
+ e
+ }"
)
# Update cache atomically
@@ -423,13 +425,12 @@ class BashScriptsPlugin(PluginBase):
or query_lower in cmd_lower
or query_lower in description_lower
):
-
relevance = 1.0 if query_lower == cmd_lower else 0.9
result = Result(
title=cmd,
subtitle=info["description"],
- icon_markup=info["icon"],
+ icon_name=info["icon"],
action=self._create_power_action(info["profile"]),
relevance=relevance,
plugin_name=self.display_name,
@@ -451,7 +452,7 @@ class BashScriptsPlugin(PluginBase):
result = Result(
title=cmd,
subtitle=info["description"],
- icon_markup=info["icon"],
+ icon_name=info["icon"],
action=self._create_power_action(info["profile"]),
relevance=0.8,
plugin_name=self.display_name,
@@ -473,9 +474,10 @@ class BashScriptsPlugin(PluginBase):
if success:
# Clear the search query after successful execution
try:
- from fabric import Application
from gi.repository import GLib
+ from fabric import Application
+
app = Application.get_default()
if app and hasattr(app, "launcher"):
launcher = app.launcher
@@ -517,16 +519,16 @@ class BashScriptsPlugin(PluginBase):
# Choose icon based on script type and status
if not executable:
- icon_markup = icons.file
+ icon_name = "gtk-file"
elif script_type == "custom":
- icon_markup = icons.star
+ icon_name = "folder-script-symbolic"
else:
- icon_markup = icons.terminal
+ icon_name = "terminalc"
return Result(
title=script_name,
subtitle=subtitle,
- icon_markup=icon_markup,
+ icon_name=icon_name,
action=self._create_script_action(script_name, script_info),
relevance=relevance,
plugin_name=self.display_name,
@@ -556,7 +558,7 @@ class BashScriptsPlugin(PluginBase):
variant_result = Result(
title=f"{script_name} {arg}",
subtitle=f"{desc} | [scripts]",
- icon_markup=icons.terminal,
+ icon_name="terminal-symbolic",
action=self._create_script_action_with_args(
script_name, script_info, [arg]
),
diff --git a/modules/launcher/plugins/bookmarks.py b/modules/launcher/plugins/bookmarks.py
index 4885938..ee55139 100644
--- a/modules/launcher/plugins/bookmarks.py
+++ b/modules/launcher/plugins/bookmarks.py
@@ -6,10 +6,9 @@ from pathlib import Path
from typing import Dict, List, Optional
from urllib.parse import urlparse
-from fabric.utils.helpers import get_relative_path
from thefuzz import fuzz
-import utils.icons as icons
+from fabric.utils.helpers import get_relative_path
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -268,7 +267,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="No Bookmarks Found",
subtitle="Use 'add
' to add first bookmark",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -298,7 +297,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="No Bookmarks Yet",
subtitle="Use 'add ' to add first bookmark",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -307,7 +306,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Example: Add Google",
subtitle="add Google https://google.com",
- icon_markup=icons.bulb,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -334,7 +333,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Add Bookmark",
subtitle="Usage: add [description]",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -343,7 +342,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Example",
subtitle="add Google https://google.com Search engine",
- icon_markup=icons.bulb,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -358,7 +357,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Invalid Format",
subtitle="Usage: add [description]",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -387,7 +386,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Bookmark Already Exists",
subtitle=f"URL '{display_url}' already exists",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -407,7 +406,7 @@ class BookmarksPlugin(PluginBase):
# Truncate description to prevent launcher resize
max_desc_len = 35 - len(domain) # Account for domain + separator
if len(description) > max_desc_len:
- description = description[:max_desc_len-3] + "..."
+ description = description[: max_desc_len - 3] + "..."
subtitle += f" • {description}"
# Truncate title for display
@@ -419,7 +418,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"Add bookmark '{display_title}'",
subtitle=subtitle,
- icon_markup=icons.plus,
+ icon_name="plus",
action=lambda: self._add_bookmark_action(title, url, description),
relevance=1.0,
plugin_name=self.display_name,
@@ -451,7 +450,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Bookmark Not Found",
subtitle=f"No bookmark found matching '{identifier}'",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -477,7 +476,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"Remove '{display_title}'?",
subtitle=f"Click to confirm: {display_domain}",
- icon_markup=icons.trash,
+ icon_name="trash",
action=lambda: self._remove_bookmark_action(identifier),
relevance=1.0,
plugin_name=self.display_name,
@@ -492,7 +491,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Remove Bookmark",
subtitle="Usage: remove ",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -506,7 +505,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Available Bookmarks:",
subtitle=f"{len(bookmarks)} bookmarks available to remove",
- icon_markup=icons.bookmark,
+ icon_name="bookmarks-organize",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -532,7 +531,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"remove {display_title}",
subtitle=f"Click to remove: {display_title} ({display_domain})",
- icon_markup=icons.trash,
+ icon_name="trash",
action=lambda t=title: self._remove_bookmark_action(t),
relevance=0.8,
plugin_name=self.display_name,
@@ -643,9 +642,10 @@ class BookmarksPlugin(PluginBase):
if description:
# Truncate description to prevent launcher resize
- max_desc_len = 50 - len(domain) # Account for domain + separator
+ # Account for domain + separator
+ max_desc_len = 50 - len(domain)
if len(description) > max_desc_len:
- description = description[:max_desc_len-3] + "..."
+ description = description[: max_desc_len - 3] + "..."
subtitle = f"{domain} • {description}"
else:
subtitle = domain
@@ -657,7 +657,7 @@ class BookmarksPlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
- icon_markup=icons.bookmark,
+ icon_name="bookmark-organize",
action=lambda u=url: self._open_bookmark(u),
relevance=relevance,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/caffeine.py b/modules/launcher/plugins/caffeine.py
index 433d0c1..decde97 100644
--- a/modules/launcher/plugins/caffeine.py
+++ b/modules/launcher/plugins/caffeine.py
@@ -3,11 +3,10 @@ from threading import Timer
from typing import List
import gi
-from fabric.utils import get_relative_path
-from fabric.utils.helpers import exec_shell_command_async
import config.data as data
-import utils.icons as icons
+from fabric.utils import get_relative_path
+from fabric.utils.helpers import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -173,7 +172,7 @@ class CaffeinePlugin(PluginBase):
result = Result(
title=f"Custom: {query}",
subtitle="Set custom duration",
- icon_markup=icons.coffee,
+ icon_name="caffeine",
action=self._create_inhibit_action(query),
relevance=1.0,
plugin_name=self.display_name,
@@ -194,7 +193,7 @@ class CaffeinePlugin(PluginBase):
result = Result(
title=description,
subtitle=subtitle,
- icon_markup=icons.coffee,
+ icon_name="caffeine",
action=self._create_inhibit_action(duration),
relevance=0.9 if query == duration else 0.7,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/calculator.py b/modules/launcher/plugins/calculator.py
index e90e468..f97dddb 100644
--- a/modules/launcher/plugins/calculator.py
+++ b/modules/launcher/plugins/calculator.py
@@ -4,7 +4,6 @@ import subprocess
import time
from typing import List
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
from utils.conversion import Conversion
@@ -99,7 +98,7 @@ class CalculatorPlugin(PluginBase):
Result(
title=f"{result:.6g} {to_unit}",
subtitle=subtitle,
- icon_markup=icons.calculator,
+ icon_name="calculator-symbolic",
action=lambda r=f"{result:.6g}": self._copy_to_clipboard(r),
relevance=1.0,
plugin_name=self.display_name,
@@ -111,7 +110,7 @@ class CalculatorPlugin(PluginBase):
Result(
title="Invalid conversion",
subtitle=str(e),
- icon_markup=icons.calculator,
+ icon_name="calculator-symbolic",
relevance=0.0,
plugin_name=self.display_name,
)
@@ -127,7 +126,7 @@ class CalculatorPlugin(PluginBase):
Result(
title=f"{result:.6g}",
subtitle=f"{query} = {result:.6g}",
- icon_markup=icons.calculator,
+ icon_name="calculator-symbolic",
action=lambda r=f"{result:.6g}": self._copy_to_clipboard(r),
relevance=1.0,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/emoji.py b/modules/launcher/plugins/emoji.py
index 834d673..8842e80 100644
--- a/modules/launcher/plugins/emoji.py
+++ b/modules/launcher/plugins/emoji.py
@@ -5,9 +5,8 @@ import time
from collections import OrderedDict
from typing import Dict, List
-from fabric.utils import get_relative_path
-
import config.data as data
+from fabric.utils import get_relative_path
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
diff --git a/modules/launcher/plugins/otp.py b/modules/launcher/plugins/otp.py
index 86362f9..7ab0b6d 100644
--- a/modules/launcher/plugins/otp.py
+++ b/modules/launcher/plugins/otp.py
@@ -6,8 +6,6 @@ from pathlib import Path
from typing import Dict, List, Optional
from fabric.utils import get_relative_path
-
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
from services.auth import (
@@ -354,7 +352,7 @@ class OTPPlugin(PluginBase):
Result(
title="Invalid format",
subtitle="Usage: add or add ``````",
- icon_markup=icons.question,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -373,7 +371,7 @@ class OTPPlugin(PluginBase):
Result(
title="Error adding account",
subtitle=f"Debug: {str(e)}",
- icon_markup=icons.cancel,
+ icon_name="cancel",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -390,7 +388,7 @@ class OTPPlugin(PluginBase):
Result(
title="No OTP accounts configured",
subtitle="Use 'add ' to add your first account",
- icon_markup=icons.key,
+ icon_name="gtk-authentication-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -401,7 +399,7 @@ class OTPPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add | remove | qr ",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -425,7 +423,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"{display_name} • {
time_display
} remaining • Shift+Enter: remove",
- icon_markup=icons.key,
+ icon_name="gtk-authentication-symbolic",
action=lambda code=totp_code: self._copy_to_clipboard(code),
relevance=1.0,
plugin_name=self.display_name,
@@ -444,7 +442,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Error: {account_name}",
subtitle="Invalid secret or configuration",
- icon_markup=icons.cancel,
+ icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -485,7 +483,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"{display_name} • {
time_display
} remaining • Shift+Enter: remove",
- icon_markup=icons.key,
+ icon_name="gtk-authentication-symbolic",
action=lambda code=totp_code: self._copy_to_clipboard(code),
relevance=1.0,
plugin_name=self.display_name,
@@ -505,7 +503,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"No accounts found for '{query}'",
subtitle="Use 'add ' to create new account",
- icon_markup=icons.magnifier,
+ icon_name="edit-find-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -516,7 +514,7 @@ class OTPPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add | remove | qr ",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.4,
plugin_name=self.display_name,
@@ -533,7 +531,7 @@ class OTPPlugin(PluginBase):
Result(
title="Enter account name",
subtitle="Usage: add ",
- icon_markup=icons.question,
+ icon_name="dialog-question-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -545,7 +543,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"To add '{account_name}':",
subtitle=f"Type: add {account_name} ",
- icon_markup=icons.bulb,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -558,7 +556,7 @@ class OTPPlugin(PluginBase):
Result(
title="Base32 Secret Format:",
subtitle="Example: add gmail JBSWY3DPEHPK3PXP",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -567,7 +565,7 @@ class OTPPlugin(PluginBase):
Result(
title="otpauth URI Format:",
subtitle="Example: add github otpauth://totp/GitHub:user?secret=JBSWY3DPEHPK3PXP",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -576,7 +574,7 @@ class OTPPlugin(PluginBase):
Result(
title="Remove Account:",
subtitle="Example: remove gmail",
- icon_markup=icons.trash,
+ icon_name="trash",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -591,7 +589,7 @@ class OTPPlugin(PluginBase):
Result(
title="Scan QR Code",
subtitle="Click to scan QR code from screen",
- icon_markup=icons.connect,
+ icon_name="view-barcode-qr-symbolic",
action=lambda: self._scan_qr_and_add_account(""),
relevance=1.0,
plugin_name=self.display_name,
@@ -600,7 +598,7 @@ class OTPPlugin(PluginBase):
Result(
title="QR Scan Instructions:",
subtitle="Use 'qr ' to specify account name",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -612,7 +610,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Scan QR Code for '{account_name}'",
subtitle="Click to scan QR code from screen",
- icon_markup=icons.connect,
+ icon_name="view-barcode-qr-symbolic",
action=lambda name=account_name: self._scan_qr_and_add_account(
name
),
@@ -658,7 +656,7 @@ class OTPPlugin(PluginBase):
Result(
title="No OTP accounts to remove",
subtitle="Use 'add ' to add accounts first",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -670,7 +668,7 @@ class OTPPlugin(PluginBase):
Result(
title="Select account to remove:",
subtitle="Type: remove to remove an account",
- icon_markup=icons.trash,
+ icon_name="user-trash-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -697,7 +695,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"Press Enter to remove • {
time_display
} remaining",
- icon_markup=icons.trash,
+ icon_name="user-trash-symbolic",
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
@@ -716,7 +714,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Error: {acc_name}",
subtitle="Press Enter to remove (Invalid secret)",
- icon_markup=icons.trash,
+ icon_name="user-trash-symbolic",
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
@@ -738,7 +736,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Account '{account_name}' not found",
subtitle="Use 'remove' to see all available accounts",
- icon_markup=icons.cancel,
+ icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -756,7 +754,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Remove '{display_name}'?",
subtitle="Press Enter to confirm removal",
- icon_markup=icons.trash,
+ icon_name="user-trash-symbolic",
action=lambda acc=account_name: self._remove_account_and_refresh(acc),
relevance=1.0,
plugin_name=self.display_name,
@@ -777,7 +775,7 @@ class OTPPlugin(PluginBase):
Result(
title="Invalid Base32 secret",
subtitle=result["error"],
- icon_markup=icons.cancel,
+ icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -800,7 +798,7 @@ class OTPPlugin(PluginBase):
subtitle=f"OTP account added successfully (secret: {
result['secret'][:4]
}...)",
- icon_markup=icons.check,
+ icon_name="emblem-ok-symbolic",
action=lambda: self._trigger_refresh(),
relevance=1.0,
plugin_name=self.display_name,
@@ -817,7 +815,7 @@ class OTPPlugin(PluginBase):
Result(
title="Error parsing otpauth URI",
subtitle=result["error"],
- icon_markup=icons.cancel,
+ icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -843,7 +841,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"✓ Added '{display_name}'",
subtitle="OTP account added from URI",
- icon_markup=icons.check,
+ icon_name="emblem-ok-symbolic",
action=lambda: self._trigger_refresh(),
relevance=1.0,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/password.py b/modules/launcher/plugins/password.py
index 18f1f65..1551b20 100644
--- a/modules/launcher/plugins/password.py
+++ b/modules/launcher/plugins/password.py
@@ -8,8 +8,6 @@ from pathlib import Path
from typing import Dict, List, Optional
from fabric.utils import get_relative_path
-
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -242,7 +240,7 @@ class PasswordPlugin(PluginBase):
Result(
title="No passwords stored",
subtitle="Use 'pass add ' to add your first password",
- icon_markup=icons.key,
+ icon_name="password-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -253,7 +251,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add | remove | (to search)",
- icon_markup=icons.info,
+ icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -284,7 +282,7 @@ class PasswordPlugin(PluginBase):
Result(
title=title,
subtitle=subtitle,
- icon_markup=icons.key,
+ icon_name="key",
action=lambda n=name: self._copy_password_to_clipboard(n),
relevance=1.0,
plugin_name=self.display_name,
@@ -311,7 +309,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Add Password - Invalid format",
subtitle="Usage: add [description]",
- icon_markup=icons.cancel,
+ icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -330,7 +328,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Update password for '{name}'?",
subtitle="Password already exists. Click to update it.",
- icon_markup=icons.key,
+ icon_name="key",
action=lambda: self._add_password_action(
name, password, description, update=True
),
@@ -345,7 +343,7 @@ class PasswordPlugin(PluginBase):
title=f"Add password for '{name}'",
subtitle="Click to save password"
+ (f" | {description}" if description else ""),
- icon_markup=icons.plus,
+ icon_name="plus",
action=lambda: self._add_password_action(
name, password, description
),
@@ -367,7 +365,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Remove Password - Invalid format",
subtitle="Usage: remove ",
- icon_markup=icons.cancel,
+ icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -383,7 +381,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Password '{name}' not found",
subtitle="Check the name and try again",
- icon_markup=icons.cancel,
+ icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -395,7 +393,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Remove password '{name}'?",
subtitle="Click to confirm deletion (this cannot be undone)",
- icon_markup=icons.trash,
+ icon_name="trash",
action=lambda: self._remove_password_action(name),
relevance=1.0,
plugin_name=self.display_name,
@@ -420,7 +418,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"No passwords found matching '{query}'",
subtitle="Try a different search term or use 'pass' to see all passwords",
- icon_markup=icons.magnifier,
+ icon_name="magnifier",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -462,7 +460,7 @@ class PasswordPlugin(PluginBase):
Result(
title=title,
subtitle=subtitle,
- icon_markup=icons.key,
+ icon_name="key",
action=lambda n=name: self._copy_password_to_clipboard(n),
relevance=get_relevance(name),
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/power.py b/modules/launcher/plugins/power.py
index ffdc919..38b9cd8 100644
--- a/modules/launcher/plugins/power.py
+++ b/modules/launcher/plugins/power.py
@@ -1,8 +1,6 @@
from typing import List
from fabric.utils import exec_shell_command_async
-
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -19,27 +17,27 @@ class PowerPlugin(PluginBase):
self.commands = {
"shutdown": {
"description": "Shutdown the system",
- "icon": icons.shutdown,
+ "icon": "system-shutdown-symbolic",
"action": self.shutdown,
},
"restart": {
"description": "Restart the system",
- "icon": icons.reboot,
+ "icon": "system-reboot-symbolic",
"action": self.restart,
},
"lock": {
"description": "Lock the screen",
- "icon": icons.lock,
+ "icon": "system-lock-screen-symbolic",
"action": self.lock,
},
"suspend": {
"description": "Suspend the system",
- "icon": icons.suspend,
+ "icon": "system-suspend-symbolic",
"action": self.suspend,
},
"logout": {
"description": "Logout from current session",
- "icon": icons.logout,
+ "icon": "system-log-out-symbolic",
"action": self.logout,
},
}
@@ -63,7 +61,7 @@ class PowerPlugin(PluginBase):
result = Result(
title=cmd.capitalize(),
subtitle=info["description"],
- icon_markup=info["icon"],
+ icon_name=info["icon"],
action=info["action"],
relevance=1.0,
plugin_name=self.display_name,
@@ -77,7 +75,7 @@ class PowerPlugin(PluginBase):
result = Result(
title=cmd.capitalize(),
subtitle=info["description"],
- icon_markup=info["icon"],
+ icon_name=info["icon"],
action=info["action"],
relevance=1.0 if query == cmd else 0.7,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/reminders.py b/modules/launcher/plugins/reminders.py
index 947f1bd..ded8c20 100644
--- a/modules/launcher/plugins/reminders.py
+++ b/modules/launcher/plugins/reminders.py
@@ -4,7 +4,6 @@ import threading
from datetime import datetime, timedelta
from typing import Dict, List, Optional
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -258,7 +257,7 @@ class RemindersPlugin(PluginBase):
subtitle=f"Active reminders: {
active_count
} | Usage: remind 5m Take a break",
- icon_markup=icons.timer_on,
+ icon_name="alarm-timer",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -279,7 +278,7 @@ class RemindersPlugin(PluginBase):
Result(
title=example,
subtitle=desc,
- icon_markup=icons.timer_on,
+ icon_name="alarm-timer",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -296,7 +295,7 @@ class RemindersPlugin(PluginBase):
Result(
title="No Active Reminders",
subtitle="Use 'remind 5m message' to set a reminder",
- icon_markup=icons.timer_off,
+ icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -314,7 +313,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"#{reminder.id}: {reminder.message}",
subtitle=f"In {time_remaining} (at {target_time})",
- icon_markup=icons.timer_on,
+ icon_name="alarm-timer",
action=lambda rid=reminder.id: self._cancel_reminder(rid),
relevance=1.0,
plugin_name=self.display_name,
@@ -334,7 +333,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"Cancelled {count} Reminders",
subtitle="All active reminders have been cancelled",
- icon_markup=icons.timer_off,
+ icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -351,7 +350,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"Cancelled Reminder #{reminder_id}",
subtitle="Reminder has been cancelled",
- icon_markup=icons.timer_off,
+ icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -363,7 +362,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Reminder Not Found",
subtitle=f"No reminder with ID #{reminder_id}",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -375,7 +374,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Invalid Reminder ID",
subtitle="Please provide a valid reminder ID number",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -407,7 +406,7 @@ class RemindersPlugin(PluginBase):
subtitle=f"Will remind in {time_remaining} (at {
target_time_str
})",
- icon_markup=icons.timer_on,
+ icon_name="alarm-timer",
action=lambda ts=time_str, msg=message: self._create_and_confirm_reminder(
ts, msg
),
@@ -425,7 +424,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Time is in the Past",
subtitle="Please specify a future time",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -438,7 +437,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Invalid Time Format",
subtitle="Use formats like: 5m, 30s, 2h, 14:30, or '5 minutes'",
- icon_markup=icons.alert,
+ icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/screencapture.py b/modules/launcher/plugins/screencapture.py
index 8cbea99..05253b6 100644
--- a/modules/launcher/plugins/screencapture.py
+++ b/modules/launcher/plugins/screencapture.py
@@ -2,10 +2,8 @@ import subprocess
from typing import List
from fabric.utils import get_relative_path
-
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
-from utils.icons import screenrecord, screenshots, ssfull, ssregion, stop
class ScreencapturePlugin(PluginBase):
@@ -17,7 +15,6 @@ class ScreencapturePlugin(PluginBase):
super().__init__()
self.display_name = "Screencapture"
self.description = "Take screenshots and screen recordings"
- # Path to the screen-capture script
self.script_path = get_relative_path("../../../scripts/screen-capture.sh")
def initialize(self):
@@ -121,290 +118,290 @@ class ScreencapturePlugin(PluginBase):
"screenshot": (
"Take Screenshot (eDP-1)",
"Capture the main display",
- ssfull,
+ "camera-photo-symbolic",
lambda: self._run_script("screenshot", "eDP-1"),
),
"ss": (
"Take Screenshot (eDP-1)",
"Capture the main display",
- ssfull,
+ "camera-photo-symbolic",
lambda: self._run_script("screenshot", "eDP-1"),
),
"screenshot-region": (
"Take Region Screenshot",
"Capture a selected region",
- ssregion,
+ "camera-photo-symbolic",
lambda: self._run_script("screenshot", "selection"),
),
"ss-region": (
"Take Region Screenshot",
"Capture a selected region",
- ssregion,
+ "camera-photo-symbolic",
lambda: self._run_script("screenshot", "selection"),
),
"screenshot-both": (
"Take Screenshot (Both Displays)",
"Capture both displays combined",
- screenshots,
+ "video-joined-displays-symbolic",
lambda: self._run_script("screenshot", "both"),
),
"ss-both": (
"Take Screenshot (Both Displays)",
"Capture both displays combined",
- screenshots,
+ "video-joined-displays-symbolic",
lambda: self._run_script("screenshot", "both"),
),
"screenshot-hdmi": (
"Take Screenshot (HDMI-A-1)",
"Capture HDMI display",
- ssfull,
+ "video-display-symbolic",
lambda: self._run_script("screenshot", "HDMI-A-1"),
),
"ss-hdmi": (
"Take Screenshot (HDMI-A-1)",
"Capture HDMI display",
- ssfull,
+ "video-display-symbolic",
lambda: self._run_script("screenshot", "HDMI-A-1"),
),
# Recording commands (with audio)
"record": (
"Start Recording (eDP-1)",
"Record the main display with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "eDP-1"),
),
"rec": (
"Start Recording (eDP-1)",
"Record the main display with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "eDP-1"),
),
"record-region": (
"Start Region Recording",
"Record a selected region with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "selection"),
),
"rec-region": (
"Start Region Recording",
"Record a selected region with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "selection"),
),
"record-hdmi": (
"Start Recording (HDMI-A-1)",
"Record HDMI display with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "HDMI-A-1"),
),
"rec-hdmi": (
"Start Recording (HDMI-A-1)",
"Record HDMI display with audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record", "HDMI-A-1"),
),
# Recording commands (no audio)
"record-noaudio": (
"Start Recording No Audio (eDP-1)",
"Record the main display without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "eDP-1"),
),
"rec-noaudio": (
"Start Recording No Audio (eDP-1)",
"Record the main display without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "eDP-1"),
),
"record-noaudio-region": (
"Start Region Recording No Audio",
"Record a selected region without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "selection"),
),
"rec-noaudio-region": (
"Start Region Recording No Audio",
"Record a selected region without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "selection"),
),
"record-noaudio-hdmi": (
"Start Recording No Audio (HDMI-A-1)",
"Record HDMI display without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "HDMI-A-1"),
),
"rec-noaudio-hdmi": (
"Start Recording No Audio (HDMI-A-1)",
"Record HDMI display without audio",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-noaudio", "HDMI-A-1"),
),
# High-quality recording commands
"record-hq": (
"Start HQ Recording (eDP-1)",
"High-quality recording for YouTube",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "eDP-1"),
),
"rec-hq": (
"Start HQ Recording (eDP-1)",
"High-quality recording for YouTube",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "eDP-1"),
),
"record-hq-region": (
"Start HQ Region Recording",
"High-quality region recording",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "selection"),
),
"rec-hq-region": (
"Start HQ Region Recording",
"High-quality region recording",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "selection"),
),
"record-hq-hdmi": (
"Start HQ Recording (HDMI-A-1)",
"High-quality HDMI recording",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "HDMI-A-1"),
),
"rec-hq-hdmi": (
"Start HQ Recording (HDMI-A-1)",
"High-quality HDMI recording",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-hq", "HDMI-A-1"),
),
# GIF recording commands
"record-gif": (
"Start GIF Recording (eDP-1)",
"Record as optimized GIF",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-gif", "eDP-1"),
),
"rec-gif": (
"Start GIF Recording (eDP-1)",
"Record as optimized GIF",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-gif", "eDP-1"),
),
"record-gif-region": (
"Start GIF Region Recording",
"Record selected region as GIF",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-gif", "selection"),
),
"rec-gif-region": (
"Start GIF Region Recording",
"Record selected region as GIF",
- screenrecord,
+ "media-record-symbolic",
lambda: self._run_script("record-gif", "selection"),
),
# Control commands
"stop": (
"Stop Recording",
"Stop the current screen recording",
- stop,
+ "media-playback-stop-symbolic",
lambda: self._run_script("record", "stop"),
),
# Conversion commands
"convert-webm": (
"Convert Latest to WebM",
"Convert latest MKV recording to WebM format",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "webm"),
),
"conv-webm": (
"Convert Latest to WebM",
"Convert latest MKV recording to WebM format",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "webm"),
),
"convert-iphone": (
"Convert Latest for iPhone",
"Convert latest MKV recording for iPhone compatibility",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "iphone"),
),
"conv-iphone": (
"Convert Latest for iPhone",
"Convert latest MKV recording for iPhone compatibility",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "iphone"),
),
"convert-youtube": (
"Convert Latest for YouTube",
"Convert latest recording for YouTube upload",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "youtube"),
),
"conv-youtube": (
"Convert Latest for YouTube",
"Convert latest recording for YouTube upload",
- screenrecord,
+ "video-x-generic-symbolic",
lambda: self._run_script("convert", "youtube"),
),
"convert-gif": (
"Convert Latest to GIF",
"Convert latest recording to GIF format",
- screenrecord,
+ "image-x-generic-symbolic",
lambda: self._run_script("convert", "gif"),
),
"conv-gif": (
"Convert Latest to GIF",
"Convert latest recording to GIF format",
- screenrecord,
+ "image-x-generic-symbolic",
lambda: self._run_script("convert", "gif"),
),
# File-based conversion commands (these will be handled specially)
"convert-webm-file": (
"Convert File to WebM",
"Type filename after command (e.g., convert-webm-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-webm-file": (
"Convert File to WebM",
"Type filename after command (e.g., conv-webm-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-iphone-file": (
"Convert File for iPhone",
"Type filename after command (e.g., convert-iphone-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-iphone-file": (
"Convert File for iPhone",
"Type filename after command (e.g., conv-iphone-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-youtube-file": (
"Convert File for YouTube",
"Type filename after command (e.g., convert-youtube-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-youtube-file": (
"Convert File for YouTube",
"Type filename after command (e.g., conv-youtube-file video.mkv)",
- screenrecord,
+ "video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-gif-file": (
"Convert File to GIF",
"Type filename after command (e.g., convert-gif-file video.mkv)",
- screenrecord,
+ "image-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-gif-file": (
"Convert File to GIF",
"Type filename after command (e.g., conv-gif-file video.mkv)",
- screenrecord,
+ "image-x-generic-symbolic",
None, # Will be handled in query method
),
}
@@ -415,7 +412,7 @@ class ScreencapturePlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
- icon_markup=icon,
+ icon_name=icon,
action=action,
relevance=1.0,
plugin_name=self.display_name,
@@ -424,7 +421,7 @@ class ScreencapturePlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
- icon_markup=icon,
+ icon_name=icon,
action=lambda: None, # No action for instruction
relevance=1.0,
plugin_name=self.display_name,
@@ -465,7 +462,11 @@ class ScreencapturePlugin(PluginBase):
Result(
title=f"Convert {file_param} to {format_type.upper()}",
subtitle=f"Convert specified file to {format_type} format",
- icon_markup=screenrecord,
+ icon_name=(
+ "video-x-generic-symbolic"
+ if format_type != "gif"
+ else "image-x-generic-symbolic"
+ ),
action=lambda fp=file_param, ft=format_type: self._run_script_with_file(
ft, fp
),
@@ -488,7 +489,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Stop Recording",
subtitle="Stop the current screen recording",
- icon_markup=stop,
+ icon_name="media-playback-stop-symbolic",
action=lambda: self._run_script("record", "stop"),
relevance=2.0, # Highest relevance to appear at top
plugin_name=self.display_name,
@@ -501,7 +502,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot",
subtitle="Capture the entire screen (eDP-1)",
- icon_markup=ssfull,
+ icon_name="camera-photo-symbolic",
action=lambda: self._run_script("screenshot", "eDP-1"),
relevance=1.0,
plugin_name=self.display_name,
@@ -509,7 +510,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Region Screenshot",
subtitle="Capture a selected region",
- icon_markup=ssregion,
+ icon_name="camera-photo-symbolic",
action=lambda: self._run_script("screenshot", "selection"),
relevance=0.9,
plugin_name=self.display_name,
@@ -517,7 +518,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot (Both Displays)",
subtitle="Capture both displays combined",
- icon_markup=screenshots,
+ icon_name="video-joined-displays-symbolic",
action=lambda: self._run_script("screenshot", "both"),
relevance=0.8,
plugin_name=self.display_name,
@@ -525,7 +526,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot (HDMI-A-1)",
subtitle="Capture HDMI display",
- icon_markup=ssfull,
+ icon_name="video-display-symbolic",
action=lambda: self._run_script("screenshot", "HDMI-A-1"),
relevance=0.7,
plugin_name=self.display_name,
@@ -539,7 +540,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording (eDP-1)",
subtitle="Record the main display with audio",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "eDP-1"),
relevance=0.7,
plugin_name=self.display_name,
@@ -547,7 +548,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Region Recording",
subtitle="Record a selected region",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "selection"),
relevance=0.6,
plugin_name=self.display_name,
@@ -555,7 +556,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording (HDMI-A-1)",
subtitle="Record HDMI display with audio",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "HDMI-A-1"),
relevance=0.5,
plugin_name=self.display_name,
@@ -569,7 +570,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording No Audio (eDP-1)",
subtitle="Record the main display without audio",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "eDP-1"),
relevance=0.65,
plugin_name=self.display_name,
@@ -577,7 +578,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Region Recording No Audio",
subtitle="Record a selected region without audio",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "selection"),
relevance=0.55,
plugin_name=self.display_name,
@@ -585,7 +586,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording No Audio (HDMI-A-1)",
subtitle="Record HDMI display without audio",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "HDMI-A-1"),
relevance=0.45,
plugin_name=self.display_name,
@@ -599,7 +600,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Recording (eDP-1)",
subtitle="High-quality recording for YouTube",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "eDP-1"),
relevance=0.4,
plugin_name=self.display_name,
@@ -607,7 +608,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Region Recording",
subtitle="High-quality region recording",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "selection"),
relevance=0.3,
plugin_name=self.display_name,
@@ -615,7 +616,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Recording (HDMI-A-1)",
subtitle="High-quality HDMI recording",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "HDMI-A-1"),
relevance=0.2,
plugin_name=self.display_name,
@@ -629,7 +630,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start GIF Recording (eDP-1)",
subtitle="Record as optimized GIF",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-gif", "eDP-1"),
relevance=0.1,
plugin_name=self.display_name,
@@ -637,7 +638,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start GIF Region Recording",
subtitle="Record selected region as GIF",
- icon_markup=screenrecord,
+ icon_name="media-record-symbolic",
action=lambda: self._run_script("record-gif", "selection"),
relevance=0.05,
plugin_name=self.display_name,
@@ -651,7 +652,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest to WebM",
subtitle="Convert latest MKV recording to WebM format",
- icon_markup=screenrecord,
+ icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "webm"),
relevance=0.01,
plugin_name=self.display_name,
@@ -659,7 +660,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest for iPhone",
subtitle="Convert latest MKV recording for iPhone compatibility",
- icon_markup=screenrecord,
+ icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "iphone"),
relevance=0.01,
plugin_name=self.display_name,
@@ -667,7 +668,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest for YouTube",
subtitle="Convert latest recording for YouTube upload",
- icon_markup=screenrecord,
+ icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "youtube"),
relevance=0.01,
plugin_name=self.display_name,
@@ -675,7 +676,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest to GIF",
subtitle="Convert latest recording to GIF format",
- icon_markup=screenrecord,
+ icon_name="image-x-generic-symbolic",
action=lambda: self._run_script("convert", "gif"),
relevance=0.01,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/system.py b/modules/launcher/plugins/system.py
index c8bb04d..bc7dba0 100644
--- a/modules/launcher/plugins/system.py
+++ b/modules/launcher/plugins/system.py
@@ -5,10 +5,8 @@ import threading
import time
from typing import List, Set, Union
-from fabric.utils import exec_shell_command_async
-
import config.data as data
-import utils.icons as icons
+from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -91,7 +89,6 @@ class SystemPlugin(PluginBase):
current_time - self._last_bin_update > self._bin_update_interval
or not self._bin_cache
):
-
if not self._cache_building:
self._cache_building = True
self._cache_thread = threading.Thread(
@@ -213,7 +210,7 @@ class SystemPlugin(PluginBase):
result = Result(
title=display_command,
subtitle=f"Execute: {display_command}",
- icon_markup=icons.terminal,
+ icon_name="terminal",
action=self._create_action(command_to_execute),
relevance=relevance,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/tmux.py b/modules/launcher/plugins/tmux.py
index ed9c3b6..5c93fad 100644
--- a/modules/launcher/plugins/tmux.py
+++ b/modules/launcher/plugins/tmux.py
@@ -3,10 +3,8 @@ import threading
import time
from typing import List
-from fabric.utils import exec_shell_command_async
-
import config.data as data
-import utils.icons as icons
+from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -24,13 +22,15 @@ class TmuxPlugin(PluginBase):
# Cache for sessions to avoid repeated subprocess calls
self._sessions_cache = []
self._last_cache_update = 0
- self._cache_ttl = 10 # Cache sessions for 10 seconds (increased from 2)
+ # Cache sessions for 10 seconds (increased from 2)
+ self._cache_ttl = 10
# Threading for auto-refresh - only when actively used
self.refresh_thread = None
self.stop_refresh = threading.Event()
self._last_query_time = 0
- self._active_refresh_timeout = 30 # Stop refreshing after 30 seconds of inactivity
+ # Stop refreshing after 30 seconds of inactivity
+ self._active_refresh_timeout = 30
def initialize(self):
"""Initialize the tmux plugin."""
@@ -67,7 +67,7 @@ class TmuxPlugin(PluginBase):
self._sessions_cache = self._get_tmux_sessions()
self._last_cache_update = current_time
- self.stop_refresh.wait(5)
+ self.stop_refresh.wait(5)
except Exception as e:
print(f"TmuxPlugin: Error in refresh thread: {e}")
self.stop_refresh.wait(10) # Wait longer on error
@@ -166,7 +166,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Attach to '{session_name}'",
subtitle=f"Connect to tmux session: {session_name}",
- icon_markup=icons.terminal,
+ icon_name="terminal",
action=lambda: self._attach_to_session(session_name),
relevance=0.9,
data={"type": "attach", "session": session_name},
@@ -177,8 +177,10 @@ class TmuxPlugin(PluginBase):
display_name = session_name if session_name else "new session"
return Result(
title=f"Create '{display_name}'",
- subtitle=f"Create new tmux session{f': {session_name}' if session_name else ''}",
- icon_markup=icons.plus,
+ subtitle=f"Create new tmux session{
+ f': {session_name}' if session_name else ''
+ }",
+ icon_name="plus",
action=lambda: self._create_session(session_name),
relevance=0.8,
data={"type": "create", "session": session_name},
@@ -189,7 +191,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Kill '{session_name}'",
subtitle=f"Terminate tmux session: {session_name}",
- icon_markup=icons.trash,
+ icon_name="trash",
action=lambda: self._kill_session(session_name),
relevance=0.7,
data={"type": "kill", "session": session_name},
@@ -200,7 +202,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Rename '{old_name}' to '{new_name}'",
subtitle=f"Rename tmux session from {old_name} to {new_name}",
- icon_markup=icons.config,
+ icon_name="config",
action=lambda: self._rename_session(old_name, new_name),
relevance=0.6,
data={"type": "rename", "old_session": old_name, "new_session": new_name},
diff --git a/modules/launcher/plugins/wallpaper.py b/modules/launcher/plugins/wallpaper.py
index 64deea1..bae0be5 100644
--- a/modules/launcher/plugins/wallpaper.py
+++ b/modules/launcher/plugins/wallpaper.py
@@ -8,12 +8,11 @@ import threading
import time
from typing import Dict, List, Optional
-from fabric.utils.helpers import exec_shell_command_async
from gi.repository import GdkPixbuf
from PIL import Image
import config.data as data
-import utils.icons as icons
+from fabric.utils.helpers import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -124,9 +123,10 @@ class WallpaperPlugin(PluginBase):
"""Clear the launcher search query and reset to trigger."""
try:
# Try to access the launcher through the fabric Application
- from fabric import Application
from gi.repository import GLib
+ from fabric import Application
+
app = Application.get_default()
if app and hasattr(app, "launcher"):
@@ -467,16 +467,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
- icon_markup=random.choice(
- [
- icons.dice_1,
- icons.dice_2,
- icons.dice_3,
- icons.dice_4,
- icons.dice_5,
- icons.dice_6,
- ]
- ),
+ icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=1.0,
plugin_name=self.display_name,
@@ -493,16 +484,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
- icon_markup=random.choice(
- [
- icons.dice_1,
- icons.dice_2,
- icons.dice_3,
- icons.dice_4,
- icons.dice_5,
- icons.dice_6,
- ]
- ),
+ icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=0.9,
plugin_name=self.display_name,
@@ -552,10 +534,9 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Apply with {scheme_name} scheme • {
status_text
}",
- icon_markup=icons.palette,
- action=lambda c=hex_color, s=scheme: self._apply_hex_color_direct(
- c, s
- ),
+ icon_name="color-picker-symbolic",
+ action=lambda c=hex_color,
+ s=scheme: self._apply_hex_color_direct(c, s),
relevance=1.0,
plugin_name=self.display_name,
data={
@@ -575,7 +556,7 @@ class WallpaperPlugin(PluginBase):
indicator_text
}",
subtitle="Matugen is enabled • Disable matugen to use hex colors",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -594,7 +575,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Apply with {scheme_name} scheme • {
status_text
}",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda c=hex_color, s=scheme: (
self._apply_hex_color_direct(c, s)
if not matugen_enabled
@@ -617,7 +598,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Hex Color (incomplete): {hex_color}",
subtitle="Complete the 6-digit hex color to apply",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda: None,
relevance=0.7,
plugin_name=self.display_name,
@@ -647,7 +628,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Generate and apply random color with {
scheme_name
} scheme • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda s=scheme: self._apply_hex_color_direct(
self._generate_random_hex_color(), s
),
@@ -667,7 +648,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Cannot Apply Random Color{indicator_text}",
subtitle="Matugen is enabled • Disable matugen to use hex colors",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -683,7 +664,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Hex Color{indicator_text}",
subtitle=f"Generate and apply random color • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda s=scheme: (
self._apply_hex_color_direct(
self._generate_random_hex_color(), s
@@ -707,7 +688,7 @@ class WallpaperPlugin(PluginBase):
Result(
title="Hex Color Commands",
subtitle="Use: color #FF5733, hex #00FF00, color random",
- icon_markup=icons.palette,
+ icon_name="color-picker-symbolic",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -761,7 +742,7 @@ class WallpaperPlugin(PluginBase):
if not matugen_enabled
else ""
),
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda s=scheme_id: self._set_current_scheme(s),
relevance=1.0 if scheme_id == current_scheme else 0.8,
plugin_name=self.display_name,
@@ -785,7 +766,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Enable Matugen{indicator_text}",
subtitle=f"Turn on dynamic color generation • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(True),
relevance=0.9,
plugin_name=self.display_name,
@@ -802,7 +783,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Disable Matugen{indicator_text}",
subtitle=f"Turn off dynamic color generation • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(False),
relevance=0.9,
plugin_name=self.display_name,
@@ -824,7 +805,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Switch matugen to {
'enabled' if new_state else 'disabled'
} • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(new_state),
relevance=0.9,
plugin_name=self.display_name,
@@ -843,7 +824,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Enable Matugen{indicator_text}",
subtitle=f"Turn on dynamic color generation • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(True),
relevance=0.8,
plugin_name=self.display_name,
@@ -862,7 +843,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Disable Matugen{indicator_text}",
subtitle=f"Turn off dynamic color generation • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(False),
relevance=0.8,
plugin_name=self.display_name,
@@ -879,7 +860,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Toggle Matugen{indicator_text}",
subtitle=f"Switch matugen state • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(not current_state),
relevance=0.8,
plugin_name=self.display_name,
@@ -898,7 +879,7 @@ class WallpaperPlugin(PluginBase):
indicator_text
}",
subtitle=f"Dynamic colors • {status_text}",
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -912,7 +893,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Wallpaper System Status{indicator_text}",
subtitle=status_text,
- icon_markup=icons.palette,
+ icon_name="color-management-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -925,16 +906,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
- icon_markup=random.choice(
- [
- icons.dice_1,
- icons.dice_2,
- icons.dice_3,
- icons.dice_4,
- icons.dice_5,
- icons.dice_6,
- ]
- ),
+ icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=0.9,
plugin_name=self.display_name,
@@ -981,7 +953,7 @@ class WallpaperPlugin(PluginBase):
' • ' + status_text if not query else ''
}",
icon=icon,
- icon_markup=icons.wallpapers if not icon else None,
+ icon_name="image-x-generic-symbolic" if not icon else None,
action=lambda w=wallpaper: self._set_wallpaper(w),
relevance=relevance,
plugin_name=self.display_name,
diff --git a/modules/launcher/plugins/websearch.py b/modules/launcher/plugins/websearch.py
index 575492a..80f992b 100644
--- a/modules/launcher/plugins/websearch.py
+++ b/modules/launcher/plugins/websearch.py
@@ -2,7 +2,6 @@ import subprocess
import urllib.parse
from typing import List
-import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -23,49 +22,49 @@ class WebSearchPlugin(PluginBase):
"google": {
"name": "Google",
"url": "https://www.google.com/search?q={}",
- "icon": icons.google,
+ "icon": "google-symbolic",
"description": "Search with Google",
},
"duckduckgo": {
"name": "DuckDuckGo",
"url": "https://duckduckgo.com/?q={}",
- "icon": icons.duckduckgo,
+ "icon": "preferences-desktop-search",
"description": "Search with DuckDuckGo (privacy-focused)",
},
"youtube": {
"name": "YouTube",
"url": "https://www.youtube.com/results?search_query={}",
- "icon": icons.youtube,
+ "icon": "youtube-symbolic",
"description": "Search videos on YouTube",
},
"github": {
"name": "GitHub",
"url": "https://github.com/search?q={}",
- "icon": icons.github,
+ "icon": "github-desktop-symbolic",
"description": "Search repositories on GitHub",
},
"stackoverflow": {
"name": "Stack Overflow",
"url": "https://stackoverflow.com/search?q={}",
- "icon": icons.stackoverflow,
+ "icon": "stack",
"description": "Search programming questions on Stack Overflow",
},
"wikipedia": {
"name": "Wikipedia",
"url": "https://en.wikipedia.org/wiki/Special:Search?search={}",
- "icon": icons.wikipedia,
+ "icon": "search-menus-symbolic",
"description": "Search articles on Wikipedia",
},
"reddit": {
"name": "Reddit",
"url": "https://www.reddit.com/search/?q={}",
- "icon": icons.reddit,
+ "icon": "reddit-symbolic",
"description": "Search discussions on Reddit",
},
"linkedin": {
"name": "LinkedIn",
"url": "https://www.linkedin.com/search/results/all/?keywords={}",
- "icon": icons.linkedin,
+ "icon": "link",
"description": "Search professionals and jobs on LinkedIn",
},
}
@@ -166,7 +165,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"Open {url}",
subtitle="Open this URL in your default browser",
- icon_markup=icons.world,
+ icon_name="link",
action=lambda u=url: self._open_url(u),
relevance=1.0,
plugin_name=self.display_name,
@@ -190,7 +189,7 @@ class WebSearchPlugin(PluginBase):
result = Result(
title=engine_info["name"],
subtitle=engine_info["description"],
- icon_markup=engine_info["icon"],
+ icon_name=engine_info["icon"],
action=lambda e=engine_id: self._show_engine_help(e),
relevance=1.0 if engine_id == self.default_engine else 0.8,
plugin_name=self.display_name,
@@ -207,7 +206,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"Search '{query}' on {engine_info['name']}",
subtitle=f"{engine_info['description']} - {query}",
- icon_markup=engine_info["icon"],
+ icon_name=engine_info["icon"],
action=lambda e=engine_id, q=query: self._perform_search(e, q),
relevance=1.0 if engine_id == self.default_engine else 0.9,
plugin_name=self.display_name,
@@ -221,7 +220,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"{engine_info['name']} Search",
subtitle=f"{engine_info['description']} - Type your search query",
- icon_markup=engine_info["icon"],
+ icon_name=engine_info["icon"],
action=lambda: None, # No action for info result
relevance=1.0,
plugin_name=self.display_name,
diff --git a/modules/launcher/result_item.py b/modules/launcher/result_item.py
index e18fd9e..baaaa43 100644
--- a/modules/launcher/result_item.py
+++ b/modules/launcher/result_item.py
@@ -62,7 +62,7 @@ class ResultItem(EventBox):
icon_widget = Image(pixbuf=self.result.icon, name="result-item-icon")
elif self.result.icon_name:
icon_widget = Image(
- icon_name=self.result.icon_name, icon_size=32, name="result-item-icon"
+ icon_name=self.result.icon_name, icon_size=48, name="result-item-icon"
)
elif self.result.icon_markup:
icon_widget = Label(
@@ -71,8 +71,8 @@ class ResultItem(EventBox):
else:
# Default icon
icon_widget = Image(
- icon_name="application-x-executable",
- icon_size=32,
+ icon_name="application-default-icon",
+ icon_size=48,
name="result-item-icon",
)
diff --git a/modules/launcher/trigger_config.py b/modules/launcher/trigger_config.py
index cb67687..3a568c4 100644
--- a/modules/launcher/trigger_config.py
+++ b/modules/launcher/trigger_config.py
@@ -4,8 +4,6 @@ from typing import Any, Dict, List
from fabric.utils import get_relative_path
-import utils.icons as icons
-
class TriggerConfig:
def __init__(self, config_path: str = None):
@@ -29,7 +27,7 @@ class TriggerConfig:
# Initialize settings with defaults
default_settings = {
"max_examples_shown": 2,
- "default_icon": "apps",
+ "default_icon": "application-default-icon",
"fallback_example_template": "{trigger} ",
"config_version": "1.0",
}
@@ -45,10 +43,10 @@ class TriggerConfig:
return examples
def get_trigger_icon(self, trigger: str) -> str:
- icon_name = self.launcher_config.get(trigger, {}).get(
- "icon", self.settings.get("default_icon", "apps")
+ icon = self.launcher_config.get(trigger, {}).get(
+ "icon", self.settings.get("default_icon", "application-default-icon")
)
- return getattr(icons, icon_name, icons.apps)
+ return icon
def get_trigger_description(self, trigger: str) -> str:
return self.launcher_config.get(trigger, {}).get(
diff --git a/widgets/smartscrolled.py b/widgets/smartscrolled.py
new file mode 100644
index 0000000..c1a48a8
--- /dev/null
+++ b/widgets/smartscrolled.py
@@ -0,0 +1,76 @@
+from gi.repository import Gtk, GLib
+
+from fabric.widgets.box import Box
+from fabric.widgets.scrolledwindow import ScrolledWindow
+
+
+class SmartScrolledBox(Box):
+ def __init__(
+ self,
+ max_height: int = 300,
+ spacing: int = 6,
+ **kwargs,
+ ):
+ super().__init__(orientation="v", spacing=spacing, **kwargs)
+
+ self._max_height = max_height
+
+ # The actual content box that holds children
+ self.content_box = Box(orientation="v", spacing=spacing)
+
+ # Viewport → for scrolling non-scrollable widgets like Box
+ self.viewport = Gtk.Viewport()
+ self.viewport.add(self.content_box)
+
+ # ScrolledWindow → wrapper for vertical scrolling
+ self.scroller = ScrolledWindow()
+ self.scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
+ self.scroller.add(self.viewport)
+
+ self.add(self.scroller)
+
+ # Initial size check
+ GLib.idle_add(self._apply_height_constraint)
+
+ def append(self, widget: Gtk.Widget):
+ self.content_box.add(widget)
+ self._apply_height_constraint()
+
+ def remove(self, widget: Gtk.Widget):
+ self.content_box.remove(widget)
+ self._apply_height_constraint()
+
+ def clear(self):
+ for child in self.content_box.get_children():
+ self.content_box.remove(child)
+ self._apply_height_constraint()
+
+ def update_height(self):
+ """Manually trigger height update - useful when content is added externally."""
+ self._apply_height_constraint()
+
+ def force_height_constraint(self):
+ """Force height constraint immediately."""
+ self._apply_height_constraint()
+
+ def _apply_height_constraint(self):
+ """Apply height constraint immediately without delay."""
+ _, nat_height = self.content_box.get_preferred_height()
+ if nat_height > self._max_height:
+ # Force height constraint and show scrollbar
+ self.set_size_request(-1, self._max_height)
+ self.scroller.set_size_request(-1, self._max_height)
+ self.scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
+ else:
+ # Allow natural height, no size request
+ self.set_size_request(-1, -1)
+ self.scroller.set_size_request(-1, -1)
+ self.scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
+
+ def get_vadjustment(self):
+ """Get the vertical adjustment from the scrolled window."""
+ return self.scroller.get_vadjustment()
+
+ def get_hadjustment(self):
+ """Get the horizontal adjustment from the scrolled window."""
+ return self.scroller.get_hadjustment()