Search Provider
WARNING
This documentation is for GNOME 45 and later. Please see the Legacy Documentation for previous versions.
A search provider is a mechanism by which an application can expose its search capabilities to GNOME Shell. Text from the search entry is forwarded to all search providers, which may each return a set of search results.
Applications must register search providers by exporting a D-Bus service, while GNOME Shell extensions can register search providers directly.
See Also
Imports
The following imports should be all most developers need for Search Providers:
import St from 'gi://St';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
Example Usage
GNOME Shell extensions create search providers by creating a class implementing a simple interface. This class is responsible for returning a list of results for a list of search terms.
Results are returned as unique string identifiers, which may be passed back to the search provider to request ResultMeta
objects. These are used by GNOME Shell to populate the results displayed to the user.
Search providers are constructed and then registered with GNOME Shell, before they start receiving search requests.
CreateIcon
The CreateIcon
callback is a property of a ResultMeta
, used to return a Clutter.Actor
to represent a search result. Usually this will be an St.Icon
, and the scale factor should be accounted for.
Implementations can call St.ThemeContext.get_for_stage()
on global.stage
to get the current scale and use it as a multiplier for the size
argument.
/**
* Create an icon for a search result.
*
* Implementations may want to take scaling into consideration.
*
* @callback CreateIcon
* @param {number} size - The requested size of the icon
* @returns {Clutter.Actor} An icon
*/
ResultMeta
The ResultMeta
object is a light-weight metadata object, used to represent a search result in the search view. Search providers must return objects of this type when SearchProvider.prototype.getResultMetas()
is called.
/**
* @typedef ResultMeta
* @type {object}
* @property {string} id - the unique identifier of the result
* @property {string} name - the name of the result
* @property {string} [description] - optional description of the result
* @property {string} [clipboardText] - optional clipboard content
* @property {CreateIcon} createIcon - creates an icon for the result
*/
The id
is a unique ID for the result identifier, that may be passed as an argument to SearchProvider.prototype.activateResult()
.
The description
property is optional, holding a longer description of the result that is only displayed in the list view.
The clipboardText
property is optional, holding text that will be copied to the clipboard if the result is activated.
SearchProvider
Below is a fully documented implementation of a search provider class. An extension may implement this interface as part of its default export, so long as it still implements enable()
and disable()
.
class SearchProvider {
constructor(extension) {
this._extension = extension;
}
/**
* The application of the provider.
*
* Applications will return a `Gio.AppInfo` representing themselves.
* Extensions will usually return `null`.
*
* @type {Gio.AppInfo}
*/
get appInfo() {
return null;
}
/**
* Whether the provider offers detailed results.
*
* Applications will return `true` if they have a way to display more
* detailed or complete results. Extensions will usually return `false`.
*
* @type {boolean}
*/
get canLaunchSearch() {
return false;
}
/**
* The unique ID of the provider.
*
* Applications will return their application ID. Extensions will usually
* return their UUID.
*
* @type {string}
*/
get id() {
return this._extension.uuid;
}
/**
* Launch the search result.
*
* This method is called when a search provider result is activated.
*
* @param {string} result - The result identifier
* @param {string[]} terms - The search terms
*/
activateResult(result, terms) {
console.debug(`activateResult(${result}, [${terms}])`);
}
/**
* Launch the search provider.
*
* This method is called when a search provider is activated. A provider can
* only be activated if the `appInfo` property holds a valid `Gio.AppInfo`
* and the `canLaunchSearch` property is `true`.
*
* Applications will typically open a window to display more detailed or
* complete results.
*
* @param {string[]} terms - The search terms
*/
launchSearch(terms) {
console.debug(`launchSearch([${terms}])`);
}
/**
* Create a result object.
*
* This method is called to create an actor to represent a search result.
*
* Implementations may return any `Clutter.Actor` to serve as the display
* result, or `null` for the default implementation.
*
* @param {ResultMeta} meta - A result metadata object
* @returns {Clutter.Actor|null} An actor for the result
*/
createResultObject(meta) {
console.debug(`createResultObject(${meta.id})`);
return null;
}
/**
* Get result metadata.
*
* This method is called to get a `ResultMeta` for each identifier.
*
* If @cancellable is triggered, this method should throw an error.
*
* @async
* @param {string[]} results - The result identifiers
* @param {Gio.Cancellable} cancellable - A cancellable for the operation
* @returns {Promise<ResultMeta[]>} A list of result metadata objects
*/
getResultMetas(results, cancellable) {
console.debug(`getResultMetas([${results}])`);
const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
return new Promise((resolve, reject) => {
const cancelledId = cancellable.connect(
() => reject(Error('Operation Cancelled')));
const resultMetas = [];
for (const identifier of results) {
const meta = {
id: identifier,
name: 'Result Name',
description: 'The result description',
clipboardText: 'Content for the clipboard',
createIcon: size => {
return new St.Icon({
icon_name: 'dialog-information',
width: size * scaleFactor,
height: size * scaleFactor,
});
},
};
resultMetas.push(meta);
}
cancellable.disconnect(cancelledId);
if (!cancellable.is_cancelled())
resolve(resultMetas);
});
}
/**
* Initiate a new search.
*
* This method is called to start a new search and should return a list of
* unique identifiers for the results.
*
* If @cancellable is triggered, this method should throw an error.
*
* @async
* @param {string[]} terms - The search terms
* @param {Gio.Cancellable} cancellable - A cancellable for the operation
* @returns {Promise<string[]>} A list of result identifiers
*/
getInitialResultSet(terms, cancellable) {
console.debug(`getInitialResultSet([${terms}])`);
return new Promise((resolve, reject) => {
const cancelledId = cancellable.connect(
() => reject(Error('Search Cancelled')));
const identifiers = [
'result-01',
'result-02',
'result-03',
];
cancellable.disconnect(cancelledId);
if (!cancellable.is_cancelled())
resolve(identifiers);
});
}
/**
* Refine the current search.
*
* This method is called to refine the current search results with
* expanded terms and should return a subset of the original result set.
*
* Implementations may use this method to refine the search results more
* efficiently than running a new search, or simply pass the terms to the
* implementation of `getInitialResultSet()`.
*
* If @cancellable is triggered, this method should throw an error.
*
* @async
* @param {string[]} results - The original result set
* @param {string[]} terms - The search terms
* @param {Gio.Cancellable} cancellable - A cancellable for the operation
* @returns {Promise<string[]>}
*/
getSubsearchResultSet(results, terms, cancellable) {
console.debug(`getSubsearchResultSet([${results}], [${terms}])`);
if (cancellable.is_cancelled())
throw Error('Search Cancelled');
return this.getInitialResultSet(terms, cancellable);
}
/**
* Filter the current search.
*
* This method is called to truncate the number of search results.
*
* Implementations may use their own criteria for discarding results, or
* simply return the first n-items.
*
* @param {string[]} results - The original result set
* @param {number} maxResults - The maximum amount of results
* @returns {string[]} The filtered results
*/
filterResults(results, maxResults) {
console.debug(`filterResults([${results}], ${maxResults})`);
if (results.length <= maxResults)
return results;
return results.slice(0, maxResults);
}
}
Registration
Search providers from GNOME Shell extensions must be registered before they will receive search queries. Registration should be performed in the extension's enable()
method and unregistered in disable()
.
export default class ExampleExtension extends Extension {
enable() {
this._provider = new SearchProvider(this);
Main.overview.searchController.addProvider(this._provider);
}
disable() {
Main.overview.searchController.removeProvider(this._provider);
this._provider = null;
}
}