Este es uno de los capítulos del tutorial Crea tu propia extensión para GNOME Shell. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.
¿Te gustaría crear una configuración para tu aplicación?. Antes de continuar con las extensiones e indicadores para Ubuntu, vamos a ver como dar opciones al usuario pueda personalizar una aplicación.
Seguro que a ti también te pasa que te encanta toquetear la configuración de cualquier aplicación. Cuando abres la configuración de la aplicación y te encuentras con una decena de pestañas y una docena de opciones por pestaña, seguro que entras en éxtasis. A mi antes también me pasaba. Pero no te preocupes, esto pasa con los años.
Sin embargo, dar opciones al usuario para dejar la aplicación a su gusto es muy importante. Al final, hay que tener en cuenta que dar opciones para personalizar una extensión, siempre es mejor que hacer tantas extensiones como preferencias de usuario.
En este artículo, verás como puedes añadir opciones para que cualquier usuario pueda personalizar una aplicación.
Crear e implementar una configuración para tu aplicación
Como he comentado en la introducción, aunque yo me he rehabilitado de la adicción de la personalización, creo que es muy importante dar opciones al usuario.
Es imposible hacer una aplicación que esté a gusto de todos los usuarios. Hay quien le gusta uno u otro tipo de tipografía, o colores, o que al abrirse la aplicación quede centrada, o… tantas y tantas opciones que se pueden configurar.
Una posibilidad para resolver este problema es dando al usuario la posibilidad de configurar ciertos aspectos de la aplicación. De esta forma, GNOME ha facilitado enormemente a los desarrolladores la posibilidad de la configuración.
La configuración con GNOME
Cuando comencé a escribir este artículo, lo orienté a las extensiones de GNOME Shell. Sin embargo, dado que GNOME recomienda a los desarrolladores utilizar JavaScript, para implementar sus aplicaciones, lo que voy a comentar, no solo vale para una extensión de GNOME Shell, sino que es válido para cualquier otra aplicación.
Como verás a continuación, utilizar el sistema predeterminado de GNOME para crear la configuración, es realmente sencillo. Pero no solo es que sea sencillo, sino que además se integra perfectamente con el entorno de escritorio, como no podía ser de otra forma, y le da un aspecto muy profesional.
Schema
Para definir las opciones que nuestro usuario puede configurar se utiliza un archivo xml
. Este archivo, para un par de opciones tiene, un aspecto como el que puedes ver a continuación,
<?xml version="1.0" encoding="UTF-8"?> <schemalist gettext-domain="touchpad-manager@atareao.es"> <schema path="/es/atareao/touchpad-manager/" id="es.atareao.touchpad-manager"> <key name="first-time" type="b"> <default>true</default> <summary>First time</summary> <description>The first time the app is executed</description> </key> <key name="version" type="s"> <default>""</default> <summary>Version</summary> <description>Version</description> </key> </schema> </schemalist>
Como puedes ver, cada opción queda dentro de la clave key
, y tiene cinco parámetros a configurar. Estos parámetros son los siguientes,
name
. Se corresponde con el nombre de la variable. Este nombre es el que utilizaremos en nuestra aplicación, pero no es visible al usuario,type
. Este el tipo de la variable. El tipo puede serb
si es booleano,s
si es una cadena de caracteres,d
si es un número real,i
si es un entero ….default
. Es el valor por defecto que toma la variable.summary
. Se corresponde con el nombre de la opción tal y como lo ve el usuario.description
. Tal y como te puedes imaginar, esto se corresponde con la descripción de la variable.
Como puedes ver, es realmente sencillo definir las opciones que podrá configurar el usuario.
Ahora bien, para que el sistema pueda utilizarlo es necesario compilarlo. Sin embargo, no te tienes que preocupar, porque esto es realmente sencillo.
Tan solo tienes que ejecutar en el directorio padre de schemas
la siguiente orden,
glib-compile-schemas schemas
Convenience
Para facilitar el uso de las opciones conviene que utilices un módulo de JavaScript que puedes encontrar en diferentes proyectos de extensiones de GNOME Shell. Por supuesto, también puedes utilizar, el que se encuentra en el repositorio de la extensión que estamos creando.
Una vez ya la tienes en tu proyecto, lo llamas como a cualquier otro módulo,
const ExtensionUtils = imports.misc.extensionUtils; const Extension = ExtensionUtils.getCurrentExtension(); const Convenience = Extension.imports.convenience;
Para utilizar la configuración, solo tienes que añadir esta línea de código,
var settings = Convenience.getSettings();
Vinculando opciones
Una de las características mas interesantes que tiene este sistema de configurar opciones es que permite vincular acciones con el evento que se produce al cambiar una opción. Es decir, que nosotros podemos establecer que cuando se cambie una opción se ejecute una función o método.
Así para obtener el valor de una opción, solo tenemos que utilizar la siguiente línea de código,
this.settingName = Convenience.getSettings(name);
Donde name
es el nombre que hemos asignado a la opción que puede configurar el usuario.
De esta manera, ahora podemos asignar una acción al evento que se produce al modificar la dicha opción. Para ello, solo necesitarás las siguientes líneas de código,
this.settingName.connect("changed", () => function() { //aquí va lo que quieras que haga al cambiar la opción });
De esta forma, no te tienes que preocupar de nada. Puesto que cada vez que el usuario cambie la opción se ejecutará la función que has definido.
Mostrar la configuración
Evidentemente una vez definidas las opciones que el usuario puede configurar es necesario mostrarlas. Para mostrarlas de forma que quede integrada con el sistema de configuración de GNOME Shell, en la aplicación Retoques
, es necesario añadir un nuevo archivo prefs.js
.
En este archivo se define una clase que es la que se encarga de mostrar las preferencias y una función. Esta función es la que es llamada por GNOME Shell. Así, por ejemplo, nuestra función podría tener un aspecto como el que vemos a continuación,
let tipw = new PowerCommandsPreferencesWidget(); GLib.timeout_add(GLib.PRIORITY_DEFAULT, 0, () => { let prefsWindow = tipw.get_toplevel() prefsWindow.get_titlebar().custom_title = tipw.switcher; prefsWindow.connect("destroy", () => { tipw.daemon.discovering = false; }); return false; }); tipw.show_all(); return tipw;
Pero, ¿Como definimos nuestra clase? Para definir nuestra clase vamos a apoyarnos en otro módulo. Este módulo define una serie de clases, de forma que, en función del tipo de opción tengamos una clase. Por ejemplo, para una elemento de la configuración tipo booleano tendremos un objeto del tipo interruptor, es decir Gtk.Switch
. En el caso el elemento de la configuración tenga varas opciones tendremos un combo, es decir, un Gtk.ComboBoxText
.
Esto nos permite simplificar nuestra trabajo considerablemente. Simplemente tenemos que utilizar este módulo en cada una de nuestras aplicaciones para tener definido.
La magia de este módulo viene definida en el siguiente método de la clase Section
,
addGSetting(settings, keyName, widget) { let key = settings.settings_schema.get_key(keyName); let range = key.get_range().deep_unpack()[0]; let type = key.get_value_type().dup_string(); type = (range !== "type") ? range : type; if (widget !== undefined) { widget = new widget(settings, keyName); } else if (type === "b") { widget = new BoolSetting(settings, keyName); } else if (type === "enum") { widget = new EnumSetting(settings, keyName); } else if (type === "flags") { widget = new FlagsSetting(settings, keyName); } else if (type === "mb") { widget = new MaybeSetting(settings, keyName); } else if (type.length === 1 && "ynqiuxthd".indexOf(type) > -1) { widget = new NumberSetting(settings, keyName, type); } else if (type === "range") { widget = new RangeSetting(settings, keyName); } else if (type.length === 1 && "sog".indexOf(type) > -1) { widget = new StringSetting(settings, keyName); } else { widget = new OtherSetting(settings, keyName); } return this.addSetting( key.get_summary(), key.get_description(), widget ); }
Como ves en función del tipo se elige la clase que mostrará la opción. Se trata de una forma muy interesante de simplificar el trabajo de crear la configuración de nuestra extensión.
Mostrando la configuración
Como hemos visto anteriormente, para mostrar la configuración hacemos uso del archivo prefs.js
. En el definimos la clase PowerCommandsPreferencesWidget
que hereda de PreferencesWidget.Stack
y que contiene dos elementos del tipo Page
. El primero de los elementos contiene las preferencias, mientras que el segundo contiene un Acerca de.
El importante es el primero, el que contiene las preferencias. Para añadir las preferencias creamos una sección utilizando el método definido en el módulo preferenceswidget
, y añadimos las opciones que necesitemos. Por ejemplo,
let appearanceSection = preferencesPage.addSection(_("Select buttons"), null, {}); appearanceSection.addGSetting(settings, "show-screensaver");
Como show-screensaver
la hemos definido del tipo booleano, nos mostrará un elemento del tipo interrruptor, un Gtk.Switch
. Así es muy sencillo añadir todos los elementos que necesitemos de nuestra configuración. No te tienes que preocupar de darle forma o estilo, ni de que tipo es. Simplemente se trata de añadir elementos.
El resultado es el que puedes ver en la siguiente captura de pantalla…
Conclusiones
Como has podido ver, crear opciones para personalizar una aplicación, es algo realmente sencillo. Por supuesto que existen otras opciones para guardar la configuración. Pero esta es, sin duda, de las mas sencillas, pero sobre todo, y lo mas importante, es que se integra perfectamente con el entorno de escritorio.
He actualizado el repositorio de GitHub donde se encuentra esta extensión con la configuración. De esta manera, con clonar el repositorio puedes partir de una extensión con su correspondiente configuración para que hagas la tuya. Recordarte las versiones están etiquetadas, de forma que la versión 1.0 corresponde al capítulo 1, la versión 2.0 al capítulo dos y así sucesivamente…
En el siguiente capítulo del tutorial, veremos como puedes internacionalizar tu extensión de forma sencilla.