# ComboBox (JavaScript)

Taryn Fox
jewelfox@fursona.net 2012
Un menú desplegable personalizable.
Daniel Mustieles
daniel.mustieles@gmail.com 2011 - 2014
Nicolás Satragno
nsatragno@gmail.com 2012 - 2013
Jorge González
jorgegonz@svn.gnome.org 2011

# ComboBox

Un «ComboBox» es un menú desplegable extremadamente personalizable. Contiene el equivalente a un widget TreeView que aparece cuando lo pulsa, completo con un «ListStore» (básicamente una hoja de cálculo) que dice qué está en las filas y columnas. En este ejemplo, el «ListStore» tiene el nombre de cada opción en una columna, y el nombre de un elemento del almacén en la otra, que el «ComboBox» convierte en un icono para cada opción.

Puede seleccionar una fila horizontal a la vez, por lo que los iconos no se tratan como opciones separadas. Ellos y su texto forman una opción que puede pulsar.

Trabajar con un «ListStore» puede llevar tiempo. Si sólo quiere un menú desplegable de solo texto simple, échele un vistazo al ComboBoxText. No toma tanto tiempo configurarlo, y es más fácil trabajar con él.

# Bibliotecas que importar

#!/usr/bin/gjs

const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang;

Estas son las bibliotecas que necesita importar para que esta aplicación se ejecute. Recuerde que la línea que le dice a GNOME que está usando Gjs siempre tiene que ir al principio.

# Crear la ventana de la aplicación

const ComboBoxExample = new Lang.Class ({ Name: 'ComboBox Example',

// Create the application itself _init: function () { this.application = new Gtk.Application ({ application_id: 'org.example.jscombobox'});

// Connect 'activate' and 'startup' signals to the callback functions this.application.connect('activate', Lang.bind(this, this._onActivate)); this.application.connect('startup', Lang.bind(this, this._onStartup)); },

// Callback function for 'activate' signal presents windows when active _onActivate: function () { this._window.present (); },

// Callback function for 'startup' signal builds the UI _onStartup: function () { this._buildUI (); },

Todo el código de este ejemplo va en la clase «ComboBoxExample». El código anterior crea una Gtk.Application para que vayan los widgets y la ventana.

// Build the application's UI _buildUI: function () {

// Create the application window this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Welcome to GNOME", default_width: 200, border_width: 10 });

La función _buildUI es donde se pone todo el código que crea la interfaz de usuario de la aplicación. El primer paso es crear una Gtk.ApplicationWindow nueva para poner dentro todos los widgets.

# Crear el ListStore

// Create the liststore to put our options in this._listStore = new Gtk.ListStore(); this._listStore.set_column_types ([ GObject.TYPE_STRING, GObject.TYPE_STRING]);

Este «ListStore» funciona como el que se usó en el ejemplo del TreeView. Se le dan dos columnas, ambas cadenas, porque una contendrá los nombres de los iconos del almacén de GTK+.

Si quisiera usar sus propios iconos que no están incluidos en GNOME, tendría que usar el tipo gtk.gdk.Pixbuf en su lugar. Aquí hay algunos tipos más que puede usar:

GObject.TYPE_BOOLEAN: verdadero o falso

GObject.TYPE_FLOAT: un número de coma flotante (uno con coma decimal)

GObject.TYPE_STRING: una cadena de letras y números

Necesita poner la línea const GObject = imports.gi.GObject; al principio del código de su aplicación, como se hizo en este ejemplo, si quiere poder usar tipos de GObject.

// This array holds our list of options and their icons let options = [{ name: "Select" }, { name: "New", icon: Gtk.STOCK_NEW }, { name: "Open", icon: Gtk.STOCK_OPEN }, { name: "Save", icon: Gtk.STOCK_SAVE }];

// Put the options in the liststore for (let i = 0; i < options.length; i++ ) { let option = options[i]; let iter = this._listStore.append(); this._listStore.set (iter, [0], [option.name]); if ('icon' in option) this._listStore.set (iter, [1], [option.icon]); }

Aquí se crea una matriz de las opciones de texto y sus iconos correspondientes, después se ponen en el «ListStore» de forma parecida a cOmo se haría para un «ListStore» de un TreeView. Sólo se quiere poner un icono si hay uno en la matriz de opciones, por lo que primero hay que asegurarse de verificar esto.

«Select» no es realmente una opción, sino una invitación a pulsar en el «ComboBox», por lo que no necesita un icono.

# Crear el ComboBox

// Create the combobox this._comboBox = new Gtk.ComboBox({ model: this._listStore});

Cada «ComboBox» tiene un «modelo» subyacente del que toma todas sus opciones. Puede usar un «TreeStore» si quiere tener un «ComboBox» con opciones de bifurcación. En este caso, solo se está usando el «ListStore» que ya se creó.

// Create some cellrenderers for the items in each column let rendererPixbuf = new Gtk.CellRendererPixbuf(); let rendererText = new Gtk.CellRendererText();

// Pack the renderers into the combobox in the order we want to see this._comboBox.pack_start (rendererPixbuf, false); this._comboBox.pack_start (rendererText, false);

// Set the renderers to use the information from our liststore this._comboBox.add_attribute (rendererText, "text", 0); this._comboBox.add_attribute (rendererPixbuf, "stock_id", 1);

Esta parte, nuevamente, funciona de forma similar a crear «CellRenderer» y empaquetarlos en columnas de un TreeView. La principal diferencia es que no necesita crear las columnas del «ComboBox» como objetos separados. Sólo se empaquetan los «CellRenderer» en el orden en el que quiere que se muestren, y se les dice que obtengan información del «ListStore» (y qué tipo de información tienen que esperar).

Se usa un «CellRendererText» para mostrar el texto, y un «CellRendererPixbuf» para mostrar los iconos. Se pueden almacenar los nombres de los tipos del almacén de iconos como cadenas, pero cuando se muestran se necesita un «CellRenderer» diseñado para imágenes.

Al igual que con un «TreeView», el «modelo» (en este caso un «ListStore») y la «vista» (en este caso el «ComboBox») están separados. Es por esto que se pueden hacer cosas como tener las columnas en un orden en el «ListStore», y después empaquetar los «CellRenderer» que les corresponden en el «ComboBox» en un orden diferente. Incluso se puede crear un «TreeView» u otro widget que muestre la información en el «ListStore» de una manera diferente, sin afectar el «ComboBox».

// Set the first row in the combobox to be active on startup this._comboBox.set_active (0);

// Connect the combobox's 'changed' signal to our callback function this._comboBox.connect ('changed', Lang.bind (this, this._onComboChanged));

Se quiere que el texto «Select» sea la parte que la gente ve al principio, que les haga pulsar el «ComboBox»; por lo que se establece como entrada activa. También se conecta la señal changed del «ComboBox» a una función de retorno de llamada, para que siempre que alguien pulse una opción nueva suceda algo. En este caso, sólo se va a mostrar una ventana emergente con un pequeño «haiku».

// Add the combobox to the window this._window.add (this._comboBox);

// Show the window and all child widgets this._window.show_all(); },

Finalmente, se añade el «ComboBox» a la ventana, y se le dice que se muestre con todo lo que contiene.

# Función que maneja su selección

_selected: function () {

// The silly pseudohaiku that we'll use for our messagedialog let haiku = ["", "You ask for the new\nwith no thought for the aged\nlike fallen leaves trod.", "Like a simple clam\nrevealing a lustrous pearl\nit opens for you.", "A moment in time\na memory on the breeze\nthese things can't be saved."];

Se va a crear un MessageDialog emergente, que muestra un «haiku» tonto de acuerdo a qué distribución seleccione. Primero, se crea la matriz de «haiku». Dado que la primera cadena en el «ComboBox» es sólo el mensaje «Select», la primera cadena en la matriz se hace vacía.

// Which combobox item is active? let activeItem = this._comboBox.get_active();

// No messagedialog if you choose "Select" if (activeItem != 0) { this._popUp = new Gtk.MessageDialog ({ transient_for: this._window, modal: true, buttons: Gtk.ButtonsType.OK, message_type: Gtk.MessageType.INFO, text: haiku[activeItem]});

// Connect the OK button to a handler function this._popUp.connect ('response', Lang.bind (this, this._onDialogResponse));

// Show the messagedialog this._popUp.show(); }

},

Antes de mostrar un «MessageDialog», primero se verifica que no se eligió el mensaje «Select». Después de eso, se establece su texto al «haiku» en la matriz que le corresponde a la entrada activa en el «ComboBoxText». Esto se hace usando el método get_active, que devuelve la identificación numérica de su selección.

Otros métodos que puede usar incluyen get_active_id, que devuelve la identificación de texto que asignó append, y get_active_text, que devuelve el texto completo de la cadena que seleccionó.

Después de crear el «MessageDialog», se conecta su señal «response» a la función «onDialogResponse», y se le dice que se muestre.

_onDialogResponse: function () {

this._popUp.destroy ();

}

});

Dado que el único botón que tiene el «MessageDialog» es un botón aceptar, no se necesita verificar su «response_id» para ver qué botón se pulsó. Todo lo que se hace aquí es destruir la ventana emergente.

// Run the application let app = new ComboBoxExample (); app.application.run (ARGV);

Finalmente, se crea una instancia nueva de la clase «ComboBoxExample» terminada, y se ejecuta la aplicación.

# Código de ejemplo completo

#!/usr/bin/gjs

const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang;

const ComboBoxExample = new Lang.Class ({ Name: 'ComboBox Example',

// Create the application itself _init: function () { this.application = new Gtk.Application ({ application_id: 'org.example.jscombobox'});

// Connect 'activate' and 'startup' signals to the callback functions this.application.connect('activate', Lang.bind(this, this._onActivate)); this.application.connect('startup', Lang.bind(this, this._onStartup)); },

// Callback function for 'activate' signal presents windows when active _onActivate: function () { this._window.present (); },

// Callback function for 'startup' signal builds the UI _onStartup: function () { this._buildUI (); },

// Build the application's UI _buildUI: function () {

// Create the application window this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Welcome to GNOME", default_width: 200, border_width: 10 });

// Create the liststore to put our options in this._listStore = new Gtk.ListStore(); this._listStore.set_column_types ([ GObject.TYPE_STRING, GObject.TYPE_STRING]);

// This array holds our list of options and their icons let options = [{ name: "Select" }, { name: "New", icon: Gtk.STOCK_NEW }, { name: "Open", icon: Gtk.STOCK_OPEN }, { name: "Save", icon: Gtk.STOCK_SAVE }];

// Put the options in the liststore for (let i = 0; i < options.length; i++ ) { let option = options[i]; let iter = this._listStore.append(); this._listStore.set (iter, [0], [option.name]); if ('icon' in option) this._listStore.set (iter, [1], [option.icon]); }

// Create the combobox this._comboBox = new Gtk.ComboBox({ model: this._listStore});

// Create some cellrenderers for the items in each column let rendererPixbuf = new Gtk.CellRendererPixbuf(); let rendererText = new Gtk.CellRendererText();

// Pack the renderers into the combobox in the order we want to see this._comboBox.pack_start (rendererPixbuf, false); this._comboBox.pack_start (rendererText, false);

// Set the renderers to use the information from our liststore this._comboBox.add_attribute (rendererText, "text", 0); this._comboBox.add_attribute (rendererPixbuf, "stock_id", 1);

// Set the first row in the combobox to be active on startup this._comboBox.set_active (0);

// Connect the combobox's 'changed' signal to our callback function this._comboBox.connect ('changed', Lang.bind (this, this._onComboChanged));

// Add the combobox to the window this._window.add (this._comboBox);

// Show the window and all child widgets this._window.show_all(); },

_onComboChanged: function () {

// The silly pseudohaiku that we'll use for our messagedialog let haiku = ["", "You ask for the new\nwith no thought for the aged\nlike fallen leaves trod.", "Like a simple clam\nrevealing a lustrous pearl\nit opens for you.", "A moment in time\na memory on the breeze\nthese things can't be saved."];

// Which combobox item is active? let activeItem = this._comboBox.get_active();

// No messagedialog if you choose "Select" if (activeItem != 0) { this._popUp = new Gtk.MessageDialog ({ transient_for: this._window, modal: true, buttons: Gtk.ButtonsType.OK, message_type: Gtk.MessageType.INFO, text: haiku[activeItem]});

// Connect the OK button to a handler function this._popUp.connect ('response', Lang.bind (this, this._onDialogResponse));

// Show the messagedialog this._popUp.show(); }

},

_onDialogResponse: function () {

this._popUp.destroy ();

}

});

// Run the application let app = new ComboBoxExample (); app.application.run (ARGV);

# Documentación en profundidad

En este ejemplo se usa lo siguiente:

Gtk.Application

Gtk.ApplicationWindow

Gtk.CellRendererPixbuf

Gtk.CellRendererText

Gtk.ComboBox

Gtk.ListStore

Gtk.MessageDialog

Gtk.TreeIter