Skip to content

Commit

Permalink
perf: lazy load slow modules that we don't need for showing the window (
Browse files Browse the repository at this point in the history
#1393)

* perf: start lazy loading the interactive parts we don't need just to show the window

* perf: lazy load mode_handler too (required more workarounds)
  • Loading branch information
friday committed May 12, 2024
1 parent b61c6f5 commit 3a73e6f
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 40 deletions.
3 changes: 2 additions & 1 deletion ulauncher/internals/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from ulauncher.internals.query import Query
from ulauncher.utils.basedataclass import BaseDataClass
from ulauncher.utils.fuzzy_search import get_score

DEFAULT_ACTION = True # keep window open and do nothing

Expand Down Expand Up @@ -58,4 +57,6 @@ def get_searchable_fields(self) -> list[tuple[str, float]]:
def search_score(self, query: str) -> float:
if not self.searchable:
return 0
from ulauncher.utils.fuzzy_search import get_score

return max(get_score(query, field) * weight for field, weight in self.get_searchable_fields() if field)
3 changes: 2 additions & 1 deletion ulauncher/modes/file_browser/file_browser_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from ulauncher.modes.base_mode import BaseMode
from ulauncher.modes.file_browser.file_browser_result import FileBrowserResult
from ulauncher.utils.fuzzy_search import get_score


class FileBrowserMode(BaseMode):
Expand Down Expand Up @@ -57,6 +56,8 @@ def handle_query(self, query: str) -> list[FileBrowserResult]:
if not query.startswith("."):
file_names = self.filter_dot_files(file_names)

from ulauncher.utils.fuzzy_search import get_score

sorted_files = sorted(file_names, key=lambda fn: get_score(query, fn), reverse=True)
filtered = list(filter(lambda fn: get_score(query, fn) > self.THRESHOLD, sorted_files))[: self.LIMIT]
results = [FileBrowserResult(os.path.join(closest_parent, name)) for name in filtered]
Expand Down
14 changes: 9 additions & 5 deletions ulauncher/ui/ulauncher_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@

from gi.repository import Gio, Gtk

import ulauncher
from ulauncher import config
from ulauncher.config import APP_ID, FIRST_RUN
from ulauncher.internals.query import Query
from ulauncher.ui.tray_icon import TrayIcon
from ulauncher.ui.windows.preferences_window import PreferencesWindow
from ulauncher.ui.windows.ulauncher_window import UlauncherWindow
from ulauncher.utils.eventbus import EventBus
from ulauncher.utils.hotkey_controller import HotkeyController
from ulauncher.utils.settings import Settings
from ulauncher.utils.singleton import get_instance

Expand All @@ -27,8 +25,8 @@ class UlauncherApp(Gtk.Application):
# So all methods except __init__ runs on the main app
_query = ""
_window: weakref.ReferenceType[UlauncherWindow] | None = None
_preferences: weakref.ReferenceType[PreferencesWindow] | None = None
_tray_icon: TrayIcon | None = None
_preferences: weakref.ReferenceType[ulauncher.ui.windows.preferences_window.PreferencesWindow] | None = None
_tray_icon: ulauncher.ui.tray_icon.TrayIcon | None = None

def __call__(self, *args: Any, **kwargs: Any) -> UlauncherApp:
return cast(UlauncherApp, get_instance(super(), self, *args, **kwargs))
Expand Down Expand Up @@ -83,6 +81,8 @@ def setup(self) -> None:
self.toggle_tray_icon(True)

if FIRST_RUN or settings.hotkey_show_app:
from ulauncher.utils.hotkey_controller import HotkeyController

if HotkeyController.is_supported():
hotkey = "<Primary>space"
if settings.hotkey_show_app and not HotkeyController.is_plasma():
Expand Down Expand Up @@ -128,6 +128,8 @@ def show_preferences(self, page: str | None = None) -> None:
if preferences:
preferences.present(page)
else:
from ulauncher.ui.windows.preferences_window import PreferencesWindow

preferences = PreferencesWindow(application=self)
self._preferences = weakref.ref(preferences)
preferences.show(page)
Expand All @@ -139,6 +141,8 @@ def activate_query(self, query: str) -> None:
@events.on
def toggle_tray_icon(self, enable: bool) -> None:
if not self._tray_icon:
from ulauncher.ui.tray_icon import TrayIcon

self._tray_icon = TrayIcon()
self._tray_icon.switch(enable)

Expand Down
32 changes: 22 additions & 10 deletions ulauncher/ui/windows/ulauncher_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
import logging
from typing import Any, Sequence

from gi.repository import Gdk, Gtk
from gi.repository import Gdk, GLib, Gtk

import ulauncher
from ulauncher.config import paths
from ulauncher.internals.query import Query
from ulauncher.internals.result import Result
from ulauncher.modes import mode_handler
from ulauncher.modes.apps.app_result import AppResult
from ulauncher.ui import layer_shell
from ulauncher.ui.item_navigation import ItemNavigation
from ulauncher.ui.result_widget import ResultWidget
from ulauncher.utils.eventbus import EventBus
from ulauncher.utils.load_icon_surface import load_icon_surface
from ulauncher.utils.load_icon_surface import load_icon_surface # TODO: investigate why this takes ~35-40ms to load
from ulauncher.utils.settings import Settings
from ulauncher.utils.theme import Theme
from ulauncher.utils.wm import get_monitor
Expand All @@ -25,7 +22,7 @@

class UlauncherWindow(Gtk.ApplicationWindow):
_css_provider: Gtk.CssProvider | None = None
results_nav: ItemNavigation | None = None
results_nav: ulauncher.ui.item_navigation.ItemNavigation | None = None
is_dragging = False
layer_shell_enabled = False
settings = Settings.load()
Expand Down Expand Up @@ -147,16 +144,21 @@ def __init__(self, **kwargs: Any) -> None: # noqa: PLR0915
self.present_with_time(Gdk.CURRENT_TIME)
self.position_window()

mode_handler.refresh_triggers()

if self.query:
self.set_input(str(self.query))
mode_handler.on_query_change(self.query)
else:
# this will trigger to show frequent apps if necessary
self.show_results([])

super().show()
GLib.idle_add(self.init_listeners)

def init_listeners(self) -> None:
from ulauncher.modes import mode_handler

mode_handler.refresh_triggers()
if self.query:
mode_handler.on_query_change(self.query)

######################################
# GTK Signal Handlers
Expand All @@ -179,6 +181,8 @@ def on_input_changed(self) -> None:
Triggered by user input
"""
events.emit("app:set_query", self.input.get_text(), update_input=False)
from ulauncher.modes import mode_handler

mode_handler.on_query_change(self.query)

def on_input_key_press(self, entry_widget: Gtk.Entry, event: Gdk.EventKey) -> bool: # noqa: PLR0911
Expand Down Expand Up @@ -213,6 +217,8 @@ def on_input_key_press(self, entry_widget: Gtk.Entry, event: Gdk.EventKey) -> bo
and not entry_widget.get_selection_bounds()
and entry_widget.get_position() == len(self.query)
):
from ulauncher.modes import mode_handler

new_query = mode_handler.on_query_backspace(self.query)
if new_query is not None:
events.emit("app:set_query", new_query)
Expand Down Expand Up @@ -329,14 +335,20 @@ def show_results(self, results: Sequence[Result]) -> None:

limit = len(self.settings.get_jump_keys()) or 25
if not self.input.get_text() and self.settings.max_recent_apps:
from ulauncher.modes.apps.app_result import AppResult

results = AppResult.get_most_frequent(self.settings.max_recent_apps)

if results:
from ulauncher.ui.result_widget import ResultWidget

result_widgets: list[ResultWidget] = []
for index, result in enumerate(results[:limit]):
result_widget = ResultWidget(result, index, self.query)
result_widgets.append(result_widget)
self.result_box.add(result_widget)
from ulauncher.ui.item_navigation import ItemNavigation

self.results_nav = ItemNavigation(result_widgets)
self.results_nav.select_default(self.query)

Expand Down
44 changes: 21 additions & 23 deletions ulauncher/utils/wm.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@
from ulauncher.utils.environment import IS_X11

logger = logging.getLogger()
if IS_X11:
try:
# Import will fail if Xlib is not installed
from Xlib.display import Display as XlibDisplay

from ulauncher.utils.ewmh import EWMH

except ModuleNotFoundError:
XlibDisplay = None


def get_monitor(use_mouse_position: bool = False) -> Gdk.Monitor | None:
Expand Down Expand Up @@ -46,18 +37,25 @@ def try_raise_app(app_id: str) -> bool:
Try to raise an app by id (str) and return whether successful
Currently only supports X11 via EWMH/Xlib
"""
if IS_X11 and XlibDisplay:
ewmh = EWMH()
for win in reversed(ewmh.getClientListStacking()):
class_id, class_name = win.get_wm_class()
win_app_id = (class_id or "").lower()
if win_app_id == "thunar" and win.get_wm_name().startswith("Bulk Rename"):
# "Bulk Rename" identify as "Thunar": https://gitlab.xfce.org/xfce/thunar/-/issues/731
# Also, note that get_wm_name is unreliable, but it works for Thunar https://github.com/parkouss/pyewmh/issues/15
win_app_id = "thunar --bulk-rename"
if app_id == win_app_id or app_id == class_name.lower():
logger.info("Raising application %s", app_id)
ewmh.setActiveWindow(win)
ewmh.display.flush()
return True
if IS_X11:
try:
from ulauncher.utils.ewmh import EWMH

ewmh = EWMH()
for win in reversed(ewmh.getClientListStacking()):
class_id, class_name = win.get_wm_class()
win_app_id = (class_id or "").lower()
if win_app_id == "thunar" and win.get_wm_name().startswith("Bulk Rename"):
# "Bulk Rename" identify as "Thunar": https://gitlab.xfce.org/xfce/thunar/-/issues/731
# Also, note that get_wm_name is unreliable, but it works for Thunar https://github.com/parkouss/pyewmh/issues/15
win_app_id = "thunar --bulk-rename"
if app_id == win_app_id or app_id == class_name.lower():
logger.info("Raising application %s", app_id)
ewmh.setActiveWindow(win)
ewmh.display.flush()
return True

except ModuleNotFoundError:
pass

return False

0 comments on commit 3a73e6f

Please sign in to comment.