Scopes tutorials - adding settings to your scope
A scope can provide persistent settings for simple customizations, such as allowing the user to configure an email address or select a distance unit as metric or imperial.
Defining settings
You can define such settings in a configuration file. The file must be placed
into the same directory as the scope’s normal configuration file, with the
name <scope>-settings.ini
.
For example, for a scope with ID "myscope", the normal configuration file is
myscope.ini
, and the settings definition file is myscope-settings.ini
. Both
files must be installed in the same directory (together with the scope’s .so
file).
The shell constructs a user interface from the settings definitions. The user can change settings via that UI. The scope can then retrieve actual settings values at run time.
Types
The following types are supported for settings:
- string: a string value
- number: a numeric value (integer or floating point)
- boolean: true or false
- list: a list of alternatives to choose from (single-choice)
Note: Please see Scope settings - what does “list” typereturns? from Ask Ubuntu
It is possible to optionally define a default value for each setting.
UI Examples
number, string
These types provide text inputs.
boolean
This type provides a checkbox.
list
This type provides an expandable list.
-settings.ini
The file must contain a group for each setting. The order of the groups determines the display order for the user interface that is constructed by the shell. The group name is the ID of the corresponding setting.
Each setting definition must contain at least the following mandatory definitions:
- type: Defines the type of the setting (string, number, boolean, or list).
- displayName: Defines a display name that is shown for this setting by the shell.
The defaultValue field is optional. If present, it defines a default value that is provided to the scope if the user has not changed anything (or has never used the settings UI before using the scope). It is possible to test for settings that do not have a default value and were never set by the user.
For settings of type list, the displayValues field is mandatory. It must contain an array that lists the available choices. If you provide a default value, it must be in the range 0..max-1 (where max is the number of choices).
The displayName and displayValues fields can be localized by appending a locale identifier in square brackets. If no entry can be found that matches the current locale, the non-localized value is used.
Here is an example of a <scope>-settings.ini
file using all the settings types:
[location] type = string defaultValue = London displayName = Location [distanceUnit] type = list defaultValue = 1 displayName = Distance Unit displayName[de] = Entfernungseinheit displayValues = Kilometers;Miles displayValues[de] = Kilometer;Meilen [age] type = number defaultValue = 23 displayName = Age [enabled] type = boolean defaultValue = true displayName = Enabled # Setting without a default value [color] type = string displayName = Color
Accessing settings, step by step
Let’s add some settings to our default scope template and make them interact with our results model. Create a new scope project and select the HTTP+JSON API template. This template provides a basic weather scope that we will use for this tutorial. You can retrieve all the files of our final scope with its settings in this branch.
Edit the settings file
First, open the settings file in the src/data
folder, and declare the
following settings:
[location] type = string defaultValue = London displayName = Location [forecast] type = boolean defaultValue = true displayName = Forecast
The “location” setting will be used to allow the user to provide a default search for current weather and “forecast” will allow the user to turn on and off forecast results.
query.h
In src/query.h, in the Query class, we need to declare 3 things:
initScope()
: A function that will send the current settings state each time the scope is querieds_location
ands_forecast
: two variables to store the output of our settings.
Add the following lines at the end of the Query class, or paste the content of this file into yours.
private: void initScope(); std::string s_location; bool s_forecast; };
query.cpp
Retrieve settings value
We are now going to create our function exposing settings value to the scope. At the bottom of src/query.cpp, let’s add a few lines that :
- Request settings data
- Check if the config is empty
- Fill our "s_" variables with the location (string) and forecast (double) settings values.
void Query::initScope() { unity::scopes::VariantMap config = settings(); if (config.empty()) cerr << "CONFIG EMPTY!" << endl; s_location = config["location"].get_string(); s_forecast = config["forecast"].get_bool(); }
Use settings to change the results model
We are now going to call our new function to make the data available at query time.
Just add it at the beginning of the Query::run
method:
void Query::run(sc::SearchReplyProxy const& reply) { try { initScope(); (...)
Location value
By default, this example scope does a “London” query to OpenWeatherMap when the user doesn’t provide a query string. Let’s change that .
We want our location value to be used by default. Since our scope does two queries (one for the current weather and one for the forecast) we need to fix the default query in two places. Replace “London” by our s_location variable in both client_.weather() calls that are done when the query is empty :
if(query_string.empty()) { // If the string is empty, get the current weather for the location in settings current = client_.weather(s_location); } else { // otherwise, get the current weather for the search string current = client_.weather(query_string); } if (query_string.empty()) { // If there is no search string, get the forecast for the location in settings forecast = client_.forecast_daily(s_location); } else { // otherwise, get the forecast for the search string forecast = client_.forecast_daily(query_string); }
Forecast value
The scope also provides weather forecast for seven days. For the sake of the example, the forecast setting is going to give the option to turn off forecast results.
At the top of the Forecast class, let’s do a check for the state of this setting and ignore everything coming from it when s_forecast is false.
Simply add :
if(!s_forecast){ return; }
That’s it!
Our scope now has working settings and should look like this: