# TextView (JavaScript)

Taryn Fox
jewelfox@fursona.net 2012
Un editor de texto multilínea
Daniel Mustieles
daniel.mustieles@gmail.com 2011 - 2020
Nicolás Satragno
nsatragno@gmail.com 2012 - 2013
Jorge González
jorgegonz@svn.gnome.org 2011

# TextView

Un «TextView» es realmente (o al menos generalmente) un conjunto anidado de tres objetos.

En la parte inferior hay un Búfer de texto. Este almacena el texto.

En el medio está el TextView, que es un widget que le permite ver y editar el texto en el búfer. Cambia su tamaño automáticamente dependiendo de cuánto texto hay.

Dado que el cambio de tamaño automático puede hacer un «TextView» difícil de manejar, normalmente se ubica dentro de una «ScrolledWindow». A pesar de su nombre, no es una ventana real en términos de tener una barra de título y un botón «X»; es un widget en el que pone la aplicación que está haciendo, que actúa como una ventana sobre un pedazo más manejable de un «TextView». Si el texto en el búfer es demasiado grande para entrar, aparecerán barras de desplazamiento.

Si quiere cambiar el texto que se muestra en el «TextView», actúe en el búfer de texto, dado que es lo que realmente contiene al texto. Es lo mismo si quiere ver lo que se ha introducido en él. Esta aplicación de ejemplo le permite hablar con un pingüino de fantasía, y verifica el búfer de texto para ver si ha introducido la palabra «fish» en él.

Las poblaciones de pingüinos reales están decayendo rápidamente, porque el cambio climático está derritiendo el hielo en el que viven y matando a los peces que comen. Si le interesa jugar a un juego de GNOME algo tonto basado en esta premisa, échele un vistazo a Pingus.

# Bibliotecas que importar

#!/usr/bin/gjs

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 TextViewExample = new Lang.Class ({ Name: 'TextView Example',

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

// 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 «TextViewExample». El código anterior crea una Gtk.Application para poner 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: "Talk to a Penguin", default_height: 400, default_width: 440, border_width: 20 });

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 «TextView»

// Create a label for the penguin to talk to you this._penguin = new Gtk.Label ({ height_request: 180, width_request: 400, label: "Squaaaak?", wrap: true });

El primer paso en este ejemplo es crear la Label que el pingüino usará para hablarle. Se configura para que tenga ajuste de línea estableciendo su propiedad «wrap» a «true», pero usaremos un método distinto en el «TextView» propio que permite un control más preciso.

// Create a textview for you to talk to the penguin this.buffer = new Gtk.TextBuffer(); this._textView = new Gtk.TextView ({ buffer: this.buffer, editable: true, wrap_mode: Gtk.WrapMode.WORD });

El primer paso es crear un «TextBuffer» para poner dentro las palabras. Después de eso se crea el «TextView», y se le dice que use el «TextBuffer» que se creó como su búfer. También se configura para que sea editable, dado que se quiere poder introducir cosas nuevas dentro.

La propiedad «wrap_mode» le permite elegir entre cuatro Modos de ajuste de línea diferentes. «Gtk.WrapMode.CHAR», por ejemplo, salta de línea en el medio de una palabra si sigue escribiendo cuando llega al borde. La mayoría de la gente está probablemente acostumbrada a «Gtk.WrapMode.WORD», que pondrá la palabra que está escribiendo en la línea siguiente automáticamente si es demasiado larga.

// Create a "scrolled window" to put your textview in so it will scroll this._scrolled = new Gtk.ScrolledWindow ({ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, shadow_type: Gtk.ShadowType.ETCHED_IN, height_request: 180, width_request: 400, });

// Put the textview into the scrolled window this._scrolled.add_with_viewport (this._textView);

Aquí se crea una «ScrolledWindow», y se configura para que se desplace automáticamente si se vuelve demasiado grande horizontal o verticalmente. También se le da un borde bonito ETCHED_IN. Después de eso, se pone el «TextView» dentro, y se hace que la «ScrolledWindow» dé un punto de vista sobre él.

# Crear el resto de la interfaz de usuario

// Create a grid to organize them in this._grid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Put the label and textview in the grid one on top of the other this._grid.attach (this._penguin, 0, 0, 1, 1); this._grid.attach (this._scrolled, 0, 1, 1, 1);

La primera Rejilla que se crea sólo tiene dentro la etiqueta y la «ScrolledWindow».

// Create a button to send your message to the penguin this._send = new Gtk.Button ({ halign: Gtk.Align.END, margin_top: 20, label: "Send" }); this._send.connect ('clicked', Lang.bind (this, this._chat));

// Create a grid that will have the other grid on top and the button on bottom this._mainGrid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Add the other grid and the button to the main grid this._mainGrid.attach (this._grid, 0, 0, 1, 1); this._mainGrid.attach (this._send, 0, 1, 1, 1);

Se crea un Botón para enviar su mensaje al pingüino, y una rejilla nueva que tiene la otra en su parte superior y el botón en la parte inferior. El botón tiene un margen en su parte superior, para que no se aplaste contra la «ScrolledWindow».

// Attach the main grid to the window this._window.add (this._mainGrid);

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

Finalmente, se adjunta la rejilla principal a la ventana, después se le dice a la ventana y a todo lo que tiene dentro de ella que se vuelva visible cuando se ejecute la aplicación.

# Función que maneja la respuesta del pingüino

_chat: function () {

// Create a random number to determine what the penguin says this.number = Math.floor ((Math.random() * 3) + 1);

// Did you actually say anything? if (this.buffer.text) {

// Did you mention fish? if (this.buffer.text.match (/fish/gi)) {

// Have the penguin squaak about fish if (this.number == 1) this._penguin.set_label ("FISH!");

else if (this.number == 2) this._penguin.set_label ("Fish fish fish fish. Fish!");

else this._penguin.set_label ("Fish? Fish fish fish. Fish fish. FISH!");

}

// I guess you didn't mention fish else {

// Have the penguin talk about penguinny stuff if (this.number == 1) this._penguin.set_label ("SQUAAK!");

else if (this.number == 2) this._penguin.set_label ("Ork ork ork ork squaak. Squaak squaak! *waves flippers*");

else this._penguin.set_label ("Ork ork ork ork ork?");

}

}

// Clear the buffer this.buffer.text = "";

// Give focus back to the textview so you don't have to click it again this._textView.has_focus = true;

}

});

Aquí se usan algunas funciones básicas de JavaScript para que el pingüino diga algo aleatorio. Sin embargo, a los pingüinos les gusta el pescado, por lo que se quiere que si se menciona «fish», el pingüino responda. Para hacer eso, se usa el método «match» del objeto de cadena de JavaScript sobre this.buffer.text, que devuelve el contenido del búfer de texto.

Dado que se quiere limpiar el búfer de texto después de cada vez que pulsa «Send», se configura this.buffer.text para que contenga una cadena vacía justo entonces. Después, se devuelve el foco al «TextView», para poder seguir escribiendo sin tener que pulsar en él antes.

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

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

# Código de ejemplo completo

#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk;

class TextViewExample {

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

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

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

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

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

// Create the application window this._window = new Gtk.ApplicationWindow ({ application: this.application, window_position: Gtk.WindowPosition.CENTER, title: "Talk to a Penguin", default_height: 400, default_width: 440, border_width: 20 });

// Create a label for the penguin to talk to you this._penguin = new Gtk.Label ({ height_request: 180, width_request: 400, label: "Squaaaak?", wrap: true });

// Create a textview for you to talk to the penguin this.buffer = new Gtk.TextBuffer(); this._textView = new Gtk.TextView ({ buffer: this.buffer, editable: true, wrap_mode: Gtk.WrapMode.WORD });

// Create a "scrolled window" to put your textview in so it will scroll this._scrolled = new Gtk.ScrolledWindow ({ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, shadow_type: Gtk.ShadowType.ETCHED_IN, height_request: 180, width_request: 400, });

// Put the textview into the scrolled window this._scrolled.add_with_viewport (this._textView);

// Create a grid to organize them in this._grid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Put the label and textview in the grid one on top of the other this._grid.attach (this._penguin, 0, 0, 1, 1); this._grid.attach (this._scrolled, 0, 1, 1, 1);

// Create a button to send your message to the penguin this._send = new Gtk.Button ({ halign: Gtk.Align.END, margin_top: 20, label: "Send" }); this._send.connect ('clicked', this._chat.bind(this));

// Create a grid that will have the other grid on top and the button on bottom this._mainGrid = new Gtk.Grid ({ halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER });

// Add the other grid and the button to the main grid this._mainGrid.attach (this._grid, 0, 0, 1, 1); this._mainGrid.attach (this._send, 0, 1, 1, 1);

// Attach the main grid to the window this._window.add (this._mainGrid);

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

_chat() {

// Create a random number to determine what the penguin says this.number = Math.floor ((Math.random() * 3) + 1);

// Did you actually say anything? if (this.buffer.text) {

// Did you mention fish? if (this.buffer.text.match (/fish/gi)) {

// Have the penguin squaak about fish if (this.number == 1) this._penguin.set_label ("FISH!");

else if (this.number == 2) this._penguin.set_label ("Fish fish fish fish. Fish!");

else this._penguin.set_label ("Fish? Fish fish fish. Fish fish. FISH!");

}

// I guess you didn't mention fish else {

// Have the penguin talk about penguinny stuff if (this.number == 1) this._penguin.set_label ("SQUAAK!");

else if (this.number == 2) this._penguin.set_label ("Ork ork ork ork squaak. Squaak squaak! *waves flippers*");

else this._penguin.set_label ("Ork ork ork ork ork?");

}

}

// Clear the buffer this.buffer.text = "";

// Give focus back to the textview so you don't have to click it again this._textView.has_focus = true; } };

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

# Documentación en profundidad

Gtk.Application

Gtk.ApplicationWindow

Gtk.Button

Gtk.Grid

Gtk.Label

Gtk.RadioButton

Gtk.ScrolledWindow

Gtk.TextBuffer

Gtk.TextView