diff options
Diffstat (limited to 'extensions/vertical-workspaces/lib/search.js')
-rw-r--r-- | extensions/vertical-workspaces/lib/search.js | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/extensions/vertical-workspaces/lib/search.js b/extensions/vertical-workspaces/lib/search.js new file mode 100644 index 0000000..8540626 --- /dev/null +++ b/extensions/vertical-workspaces/lib/search.js @@ -0,0 +1,206 @@ +/** + * V-Shell (Vertical Workspaces) + * search.js + * + * @author GdH <G-dH@github.com> + * @copyright 2022 - 2023 + * @license GPL-3.0 + * + */ + +'use strict'; +const { Shell, Gio, St, Clutter } = imports.gi; +const Main = imports.ui.main; + +const AppDisplay = imports.ui.appDisplay; +const Search = imports.ui.search; + +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const _Util = Me.imports.lib.util; + +const _ = Me.imports.lib.settings._; +const shellVersion = _Util.shellVersion; + +let opt; +let _overrides; +let _firstRun = true; + +let SEARCH_MAX_WIDTH; + +function update(reset = false) { + opt = Me.imports.lib.settings.opt; + const moduleEnabled = opt.get('searchModule', true); + reset = reset || !moduleEnabled; + + // don't even touch this module if disabled + if (_firstRun && reset) + return; + + _firstRun = false; + + if (_overrides) + _overrides.removeAll(); + + _updateSearchViewWidth(reset); + + if (reset) { + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.FILL; + opt = null; + _overrides = null; + return; + } + + _overrides = new _Util.Overrides(); + + _overrides.addOverride('AppSearchProvider', AppDisplay.AppSearchProvider.prototype, AppSearchProvider); + _overrides.addOverride('SearchResult', Search.SearchResult.prototype, SearchResult); + _overrides.addOverride('SearchResultsView', Search.SearchResultsView.prototype, SearchResultsView); + + // Don't expand the search view vertically and align it to the top + // this is important in the static workspace mode when the search view bg is not transparent + // also the "Searching..." and "No Results" notifications will be closer to the search entry, with the distance given by margin-top in the stylesheet + Main.overview._overview._controls.layoutManager._searchController.y_align = Clutter.ActorAlign.START; +} + +function _updateSearchViewWidth(reset = false) { + const searchContent = Main.overview._overview._controls.layoutManager._searchController._searchResults._content; + if (!SEARCH_MAX_WIDTH) { // just store original value; + const themeNode = searchContent.get_theme_node(); + const width = themeNode.get_max_width(); + SEARCH_MAX_WIDTH = width; + } + + if (reset) { + searchContent.set_style(''); + } else { + let width = Math.round(SEARCH_MAX_WIDTH * opt.SEARCH_VIEW_SCALE); + searchContent.set_style(`max-width: ${width}px;`); + } +} + +// AppDisplay.AppSearchProvider +const AppSearchProvider = { + getInitialResultSet(terms, callback, _cancellable) { + // Defer until the parental controls manager is initialized, so the + // results can be filtered correctly. + if (!this._parentalControlsManager.initialized) { + let initializedId = this._parentalControlsManager.connect('app-filter-changed', () => { + if (this._parentalControlsManager.initialized) { + this._parentalControlsManager.disconnect(initializedId); + this.getInitialResultSet(terms, callback, _cancellable); + } + }); + return; + } + + + const pattern = terms.join(' '); + let appInfoList = Shell.AppSystem.get_default().get_installed(); + + let weightList = {}; + appInfoList = appInfoList.filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + + let string = ''; + let name; + let shouldShow = false; + if (appInfo.get_display_name) { + // show only launchers that should be visible in this DE + shouldShow = appInfo.should_show() && this._parentalControlsManager.shouldShowApp(appInfo); + + if (shouldShow) { + let dispName = appInfo.get_display_name() || ''; + let gName = appInfo.get_generic_name() || ''; + let description = appInfo.get_description() || ''; + let categories = appInfo.get_string('Categories') || ''; + let keywords = appInfo.get_string('Keywords') || ''; + name = dispName; + string = `${dispName} ${gName} ${description} ${categories} ${keywords}`; + } + } + + let m = -1; + if (shouldShow && opt.SEARCH_FUZZY) { + m = _Util.fuzzyMatch(pattern, name); + m = (m + _Util.strictMatch(pattern, string)) / 2; + } else if (shouldShow) { + m = _Util.strictMatch(pattern, string); + } + + if (m !== -1) + weightList[appInfo.get_id()] = m; + + return shouldShow && (m !== -1); + }); + + appInfoList.sort((a, b) => weightList[a.get_id()] > weightList[b.get_id()]); + + const usage = Shell.AppUsage.get_default(); + // sort apps by usage list + appInfoList.sort((a, b) => usage.compare(a.get_id(), b.get_id())); + // prefer apps where any word in their name starts with the pattern + appInfoList.sort((a, b) => _Util.isMoreRelevant(a.get_display_name(), b.get_display_name(), pattern)); + + let results = appInfoList.map(app => app.get_id()); + + results = results.concat(this._systemActions.getMatchingActions(terms)); + + if (shellVersion < 43) + callback(results); + else + return new Promise(resolve => resolve(results)); + }, + + // App search result size + createResultObject(resultMeta) { + if (resultMeta.id.endsWith('.desktop')) { + const icon = new AppDisplay.AppIcon(this._appSys.lookup_app(resultMeta['id']), { + expandTitleOnHover: false, + }); + icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + return icon; + } else { + const icon = new AppDisplay.SystemActionIcon(this, resultMeta); + icon.icon._setSizeManually = true; + icon.icon.setIconSize(opt.SEARCH_ICON_SIZE); + return icon; + } + }, +}; + +const SearchResult = { + activate() { + this.provider.activateResult(this.metaInfo.id, this._resultsView.terms); + + if (this.metaInfo.clipboardText) { + St.Clipboard.get_default().set_text( + St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText); + } + // don't close overview if Shift key is pressed - Shift moves windows to the workspace + if (!_Util.isShiftPressed()) + Main.overview.toggle(); + }, +}; + +const SearchResultsView = { + _updateSearchProgress() { + let haveResults = this._providers.some(provider => { + let display = provider.display; + return display.getFirstResult() !== null; + }); + + this._scrollView.visible = haveResults; + this._statusBin.visible = !haveResults; + + if (!haveResults) { + if (this.searchInProgress) + this._statusText.set_text(_('Searching…')); + else + this._statusText.set_text(_('No results.')); + } + }, +}; |