InicioInfoTodo lo que tenes que saber de JavaFX

Todo lo que tenes que saber de JavaFX

Info12/4/2008



Java FX Script, primeros pasos


¿Qué es JavaFX Script? Parte 1:

JavaFX Script es un lenguaje de scripting declarativo estáticamente tipado, llamado anteriormente F3 (Form Follows Function) que fue desarrollado en su mayoría por Chris Oliver, quien trabaja para Sun microsystems desde la adquisición de SeeBeyond Technology por parte de esta a fines de 2005.

El compilador de JavaFX, así como gran parte de las bibliotecas gráficas y herramientas se encuentran liberadas con la licencia GPL2, a través del proyecto OpenJFX.

JFXS Forma parte del conjunto de tecnologías JavaFX, las cuales tienen el propósito de facilitar la creación de Rich Internet Applications (RIAs) con gran cantidad de contenido multimedia, respetando siempre la idea de Java de poder luego ejecutarlas en forma independiente de la plataforma. Lo último que pudo verse de esta tecnología son las "draggable applets", que son aplicaciones (que como su nombre indica) pueden arrastrarse desde el navegador al escritorio, en donde se convierten en una especie de "widgets" (similares a aquellos del Dashboard en Mac OS X o los del Sidebar de Windows Vista), estas fueron presentadas en JavaOne 2008, y les dedicamos un post hace tiempo en el que pueden obtener más info.

La idea de esta serie de artículos es introducir las características de este novedoso lenguaje a través de la realización de pequeños ejemplos. En esta primera parte solo utilizaremos el Pad de JavaFX (click en el link para ejecutarlo), que es una pequeña aplicación multiplataforma que podrán ejecutar desde Internet utilizando Java Web Start (sólo requiere tener instalado el JRE de Java).
¿Lenguaje declarativo?, ¿Estáticamente tipado?

Para comenzar, la característica principal de JFXS es que es un lenguaje de tipo declarativo, esto significa que, en vez de explicar "como" realizar algo (el algoritmo, en los lenguajes imperativos), deberemos describir "que" es ese algo. Por ejemplo, en HTML, se declara el contenido de la pagina, pero no se explica como este será mostrado en la pantalla. En general, se consideran declarativos aquellos programas escritos en un lenguaje puramente funcional, lógico o basado en restricciones, formando un paradigma que contrasta con el imperativo.

Para ver a que me refiero con "declarativo", podemos ver el siguiente programa JFXS:

import javafx.ui.*;

class HelloWorldModel {
attribute saying: String;
}

var model = HelloWorldModel {
saying: "Hello World"
};

var win = Frame {
title: bind "{model.saying} JavaFX"
width: 200
content: TextField {
value: bind model.saying
}
visible: true
};


En el mismo podemos observar como aparecen una debajo de la otra las descripciones de los elementos que formaran parte de, en este caso, un frame con el mensaje "hello world". Nótese como, en ningún momento aparece un punto de entrada al programa (un método main, por ejemplo), en vez de esto, el motor de JFXS deberá leer todo el contenido del programa, para que luego el interprete decida que hacer con toda esta información.



La segunda característica importante de JFXS es que es estáticamente tipado, esto quiere decir que la verificación de tipos se realiza en tiempo de compilación. Por lo tanto, si bien es un lenguaje de scripting, no podremos hacer cosas típicas de estos lenguajes como cambiar el tipo de una variable en tiempo de ejecución.

Veamos el siguiente ejemplo:

var myVariable = "Hello"; //linea 1
myVariable = 12345; // linea 2



En la linea 1, creamos la variable "myVariable" y le asignamos el String "Hello". En la linea 2, intentamos asignarle el valor 12345, sin las comillas, lo cual implica que el valor será interpretado como un integer, resultando en un error.



Resultado de intentar realizar esta operación en JavaFX Pad


Elementos del lenguaje

Lamentablemente, y como en todos los tutoriales de un nuevo lenguaje, no podemos escaparnos de la introducción a los elementos del mismo. Puede que sea aburrido, pero es mejor conocerlos desde un principio que perder tiempo recurriendo a la ayuda luego, cuando estemos trabajando en ejemplos más interesantes.
Tipos primitivos

Para comenzar, veremos los tipos primitivos de JFXS, que se corresponden a tipos del lenguaje Java, de la siguiente forma:
JavaFX Java
String java.lang.String
Boolean java.lang.Boolean
Number java.lang.Number
Integer byte,short,int,long,BigInteger

Podemos utilizar en ellos los mismos métodos que utilizamos en programas Java, por ejemplo:

var s = "que aburrido";
s.toUpperCase(); // retorna "QUE ABURRIDO";
s.substring(4); // retorna "aburrido";
var n = 4.5;
n.intValue(); // retorna 4
(1.5).intValue(); // retorna 1
s.substring(n); // retorna "aburrido"
var b = true;
b instanceof Boolean; // retorna true



Adicionalmente, los valores numéricos se truncan automáticamente cuando interactúan con métodos Java así como cuando se convierten Numbers a Integers.

En JFXS podemos importar clases Java, crear objetos y llamar sus métodos. Por ejemplo:

import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.lang.System;

var frame = new JFrame();
var button = new JButton("Apretame";
frame.getContentPane().add(button);
button.addActionListener(new ActionListener() {
operation actionPerformed(event) {
System.out.println("Me apretaste";
}
});
frame.pack();
frame.setVisible(true);



Esto dará como resultado lo siguiente:






Sin embargo, utilizar JFSX de esa forma rompe totalmente con la idea de sencillez propuesta por el lenguaje, por lo que una manera más apropiada de escribir el mismo programa sería:

import javafx.ui.*;
import java.lang.System;
Frame {
content: Button {
text: "Apretame"
action: operation() {
System.out.println("Me apretaste";
}
}
visible: true
}



Pueden verificar el resultado copiando y pegando los códigos en el pad de JavaFX.
Variables

En JFXS la palabra reservada "var" encabeza la declaración de una nueva variable. Si no se especifica un tipo para la misma, el interprete lo inferirá de acuerdo al uso que le demos. Una declaración de variable en JFXS toma la siguiente forma:

var nombreDeLaVariable : tipoDeLaVarible [?,+,*] = valor;

Donde tanto los operadores "?", "+", "*" como el tipo de la variable (e incluso el valor) son opcionales. El signo de pregunta implica que la variable puede ser null, el asterisco que puede estar compuesta de cero o más ocurrencias y el signo de suma obliga a que al menos este compuesta de una ocurrencia. Por ejemplo:

var numeros : Number* = [1,2,3]; //declara la variable "numeros" como un arreglo de 0 o más ocurrencias del tipo Number.

Pero, dado que tanto el operador como el valor y el tipo son opcionales, el siguiente código logrará el mismo efecto:

var nums = [1,2,3];



Por hoy esto es todo, pero en la próxima entrega veremos el uso de funciones, arreglos, expresiones, operadores y quizás algo de queries para arreglos, dependiendo de la longitud que vaya tomando el/ los articulos a medida que los escriba.

Antes de dejarlos, les paso los datos para configurar el uso de la consola con programas JFXS, ya que la misma no viene configurada por defecto en ningún sistema operativo (esto es muy útil para ver el resultado de algunos programas utilizando System.out.println). Nota: deberán importar java.lang.System para hacer uso de la consola.

* En Windows XP o Vista, vayan al panel de control, click en el icono de Java, y en el tab "Avanzado", seleccionen "Mostrar consola".

* En Solaris, click en el icono Java en el tab de preferencias, y seleccionen "Mostrar consola" en el tab "Avanzado". Si no tienen un icono de Java en las preferencias, ejecuten la aplicación "ControlPanel" (o jcontrol) en el directorio bin de su distribución Java.

* En Linux, busquen la aplicación ControlPanel (o jcontrol) dentro del directorio bin de su distribución Java. Ejecútenlo, y luego aparecerá el icono de Java en las preferencias, vayan al tab "Avanzado" y tilden "Mostrar Consola".

* En Mac OS X, vayan a Aplications/Utilities/Java/ y ejecuten "preferencias de Java". En el tab "Avanzado", seleccionen "Ver consola". Al parecer, luego de hacer esto en algunos Mac con procesador Intel, puede surgir un error que hace que Java Web Start deje de funcionar. Para Solucionarlo vayan a Library/Caches/Java/deplyment.properties y cambien todos los valores osarch de "i386" (o "X86_64" a "ppc". En mi caso, esto no sucedió y todo funcionó correctamente.




Panel de preferencias de Java


Finalmente, en el Pad de JavaFX desactiven la opción "Run Automatically" y vacien la consola antes de ejecutar alguna aplicación. Para ejecutar las aplicaciones manualmente, utilicen la opción Run del menú homonimo del Pad.


Programa que imprime "pepe" en la consola Java


Java FX Script, primeros pasos - Parte 2

Antes de comenzar con esta segunda parte, quiero agradecerle a toda la gente que durante esta semana estuvo dejando comentarios y criticas sobre el curso, tanto en la sección "comentarios" del blog como aquellos que se comunicaron por email. Me alegra saber que hay tanta gente enganchada, ya que, como escribí en los comentarios, nunca pensé que esto (que pretendía ser una introducción a JFX que pasara casi desapercibida) iba a tener el nivel de respuesta que tuvo.

Para aquellos que no estuvieron siguiendo los comentarios de la parte 1, les cuento como he decidido que siga esto: primero vamos a terminar de cubrir los aspectos básicos de la versión interpretada de JavaFX Script, para luego marcar las diferencias entre esta y la nueva versión de JFXS, JavaFX Script compilado. Una vez completo esto, vamos a empezar con ejemplos más avanzados.

Sin más introducción, retomemos donde habíamos dejado en la parte 1:

Nota: al igual que en la parte 1, sólo utilizaremos el pad de JavaFX para los ejemplos de esta parte.
Expresiones

Esta parte además de aburrida es mera sintaxis, a la que van a tener que recurrir una y otra vez hasta que la memoricen, así que me voy a limitar a copiar la tabla de operadores de la referencia, y a mostrarles un par de ejemplos (aquellos que estén acostumbrados a Java noten especialmente que el operador de "⁞no igualdad" y los booleanos cambian, lo cual puede ser un dolor de cabeza en sus primeros pasos por JFXS):



Ejemplos varios:

import java.lang.System;
import java.lang.Math;

var x = 2;
var y = 4;
var a = true;
var b = false;
System.out.println(x == y); // imprime false
System.out.println(x <> y); // imprime true
System.out.println(x < y); // imprime true
System.out.println(x > y); // imprime true
System.out.println(x >= y); // imprime false
System.out.println(x <= y); // imprime true
System.out.println(x + y); // imprime 6
System.out.println(x - y); // imprime -2
System.out.println(x * y); // imprime 8
System.out.println(x / y); // imprime 0.5
System.out.println(x % y); // imprime 2
System.out.println(a and b); // imprime false
System.out.println(a or b); // imprime true
System.out.println(not a); // imprime false
System.out.println(sizeof [x,y]); // imprime 2
System.out.println([x,y][indexof . == 0]); // imprime 2
System.out.println(if a then x else y); // imprime 2
System.out.println(select q from q in [x, y] where q > 3); // imprime 4
System.out.println(foreach(q in [x, y] where q < 3) q); // imprime 2
System.out.println(Math.max(x, y)); // imprime 4
System.out.println("abc".toUpperCase()); // imprime ABC
System.out.println(x instanceof Number); // imprime true
x = 10;
System.out.println(x); // imprime 10


Pasada la parte más pesada, volvamos a los mecanismos propios de JFXS:
Literales y expresiones con Strings

En JavaFX las cadenas de caracteres pueden ser declaradas usando comillas simples o dobles:

var s = 'Hola';
var s = "Hola";


Es importante destacar que dentro de estas declaraciones, pueden incluirse referencias a variables (o incluso porciones de código) encerrándolas entre llaves ('{', '}'):

var nombre = 'Pepe';
var s = "Hola {nombre}"; // s = 'Hola Pepe'

var respuesta = true;
var s = "La respuesta es {if respuesta then "Si" else "No"}";
// s = 'La respuesta es Si'


A diferencia de Java, en JFXS los Strings declarados con comillas dobles pueden contener saltos de línea:

var s = "Esto
tiene
varias lineas";


Arreglos

Una de las estructuras más utilizadas en JFXS son los arreglos, los cuales representan una secuencia de elementos que, al igual que en Java, deben ser del mismo tipo. En este lenguaje, los arreglos poseen muchas funcionalidades, lo cual da lugar a un infinito número de posibilidades al momento de trabajar con ellos. Comencemos por ver como crear una variable de este tipo:

var diasLaborales = ["Lun","Mar","Mie","Jue","Vie"];
var dias = [weekdays, ["Sab","Dom"]];


Cómo puede observarse en el ejemplo de arriba, los arreglos se crean utilizando corchetes y comas, y pueden crearse en base a otros arreglos. Sin embargo, es importante saber que los arreglos no son objetos en sí mismos y, además, que las expresiones que producen arreglos anidados (como la declaración de "dias" del ejemplo) son aplanadas automáticamente. Esto último implica que el valor de "dias" será en realidad '["Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom"]' y no '["Lun", "Mar", "Mie", "Jue", "Vie", ["Sab", "Dom"]]' (si bien la diferencia es sutil, puede llevar a errores).

Si intentamos imprimir el valor del arreglo con "System.out.println(dias)" sólo veremos el primer elemento del arreglo, para obtener todos los elementos, tendremos que usar un índice. Además, cabe destacar que en JFXS no obtendremos una excepción "ArrayIndexOutOfBoundsException" como en Java al intentar acceder a un índice fuera de rango, en lugar de eso, obtendremos un cero.

Para saber el tamaño de un arreglo, podemos usar el operador "sizeof":

var n = sizeof dias; // n = 7


Pueden utilizarse dos puntos seguidos '..' para crear arreglos cuyos elementos formas series aritméticas, por ejemplo:

var unoACien = [1..100];
var tamanio = sizeof unoACien; // tamanio == 100
var porTres = [1*3, 2*3 .. 10*3]; // [3,6,9,12,15,18,21,24,27,30]
tamanio = sizeof porCien; // tamanio == 10


El Operador "[]"

En JFXS el operador "[]" se utiliza para expresar una selección utilizando predicados Xquery-Update (similares a los de XPath). Este mecanismo se basa en la utilización de una variable de contexto, con la cual el interprete se encargará de localizar aquellos valores que cumplen con la condición planteada. Es importante destacar que es el valor y no el índice a lo que estaremos haciendo referencia. Para obtener el índice, debemos utilizar el operador "indexof":

var nums = [1,2,3,4];
var numsMayoreQueDos = nums[n|n > 2]; // resulta en [3,4]
var numsMenosLosDosPrimeros = nums[n|indexof n > 1]; // resulta en [3,4]


El significado de la primera expresión es "los n, contenidos en num tal que ('|') n sea mayor a 2" mientras que el de la segunda es "los n tales que el índice de dichos n sea mayor que 1". Nota: En versiones anteriores de JFXS se aceptaba no declarar n y utilizar un punto en lugar de "n|n", por lo que es probable que lo encuentren en algún ejemplo más viejo. Bastará con intercambiar dicho punto por "n|n" para hacer que estos funcionen correctamente.



Insertar y Borrar elementos en un arreglo

Para realizar inserciones y borrados en los arreglos, se utilizan los operadores "insert" y "delete", los cuales soportan los siguientes modificadores:

insert Expression1 [as first | as last] into Expression2: inserta la primera expresión
al comienzo o al final de la segunda (la segunda debe ser un atributo o variable).

insert Expression1 before Expression2: inserta la primera expresión
antes de la segunda (la segunda debe ser una selección sobre un atributo o variable)

insert Expression1 after Expression2: inserta la segunda expresión
luego de la segunda (la segunda debe ser una selección sobre un atributo o variable)

Para mayor claridad, veamos varios ejemplos varios de posibles usos de insert:

var x = [1,2,3];
insert 12 into x; // resulta en [1,2,3,12]
insert 10 as first into x; // resulta en [10,1,2,3,12]
insert [99,100] as last into x; // resulta en [10,1,2,3,12,99,100]

var y = [1,2,3];
insert 10 after y[n|n == 3]; // resulta en [1,2,3,10]
insert 12 before y[1]; // resulta en [1,12,2,3,10]
insert 13 after y[n|n == 2]; // resulta en [1, 12, 2, 13, 3, 10];


El operador "delete" funciona en forma similar, nótese que en caso de no especificar una expresión se borrará todo el arreglo:

var x = [1,2,3];
insert 10 into x; // resulta en [1,2,3,10]
insert 12 before x[1]; // resulta en [1,12,2,3,10]
delete x[n|n == 12]; // resulta en [1,2,3,10]
delete x[n|n >= 3]; // resulta en [1,2]
insert 5 after x[n|n == 1]; // resulta en [1,5,2];
insert 13 as first into x; // resulta en [13, 1, 5, 2];
delete x; // borra el arreglo resulta en []


Queries en arreglos

Además de estas operaciones, pueden realizarse Consultas (mejor conocidas como Queries) más complejas sobre los arreglos utilizando el mecanismo llamado "List Compehensions" a través de los operadores "select" y "foreach". Algunos ejemplos a continuación:

var a:Integer* = select n*n from n in [1..10];
// resulta en [1,4,9,16,25,36,49,64,81,100]


Lo que estamos diciendo es algo así como: "recorré los números de 1 a 10, asignando el número de turno a la variable 'n', elevalo al cuadrado y agregalo al arreglo 'a'".

Pueden agregarse filtros utilizando el operador "where":

var a:Integer* = select n*n from n in [1..10] where (n%2 == 0);
// resulta en [4,16,36,64,100]


Aquí estariamos diciendo: "recorré los numeros de 1 a 10, y asigná el número de turno a la variable 'n' sólo si es par, elevalo al cuadrado y agregalo al arreglo 'a'".

Podemos realizar bucles sobre más de una variable resultando en, por ejemplo, el siguiente producto cartesiano:

var a:Integer* = select n*m from n in [1..4], m in [100,200] where (n%2 == 0);
// resulta en [200, 400, 400, 800]


Aquí, decimos "recorré los números de 1 a 4, asigna el número a 'n' si es par, luego por cada n recorré el arreglo que contiene 100 y 200, asigná el elemento de turno a la variable 'm' y multiplicalo por n y agregalo al arreglo 'a'".

Podríamos completarlo aún más agregando finalmente una condición sobre el segundo arreglo:

var a:Integer* = select n*m from n in [1..4], m in [100,200] where (n%2 == 0) and (m==100);
// resulta en [200, 400]


Utilizando el operador "foreach", se pueden obtener los mismos resultados:

var a:Integer* =
foreach(n in [1..4], m in [100,200] where (n%2 == 0) )
n*m; // resulta en [200, 400, 400, 800]


Formateado

Finalmente, para cerrar esta parte veremos el operador "format as" que permite (como su nombre lo indica) dar formatos especificos a ciertos valores permitiendo, por ejemplo, su homogeneidad a la hora de almacenarlos, imprimirlos, etc.

Para empezar, la sintaxis de format as es:

expresión format as directiva

En donde la directiva indica cual es la clase que se utilizará para dar el formato. Más especificamente, si la directiva comienza con '%' se utilizará la clase java.util.Formatter, en caso contrario si la expresión es de tipo Number se utilizará java.util.DecimalFormat, sino, si la expresión es de tipo Date se utilizará java.text.SimpleDataFormat.

Algunos ejemplos:

import java.util.Date;

100.896 format as <<%f>>; // resulta en '100.896000'
31.intValue() format as <<%02X>>; // resulta en '1F'
var d = new Date();
d format as <<yyyy-MM-dd'T'HH:mm:ss.SSSZ>>; // resulta en '2005-10-31T08:04:31.323-0800'
0.00123 format as <<00.###E0>>; // resulta en '12.3E-4'


El uso de las comillas francesas ('<<' y '>>') hace que JavaFX reconozca cualquier secuencia de caracteres encerrada en estas como un identificador (incluso los espacios en blanco). Esto permite utilizar las palabras reservadas de JFXS u otros identificadores ilegales como nombres de variables, clases, atributos o funciones. Lo que es más importante, permite llamar a métodos Java que poseen nombres de palabras reservadas en JavaFX, por ejemplo:

import javax.swing.JTextArea;

var textArea = new JTextArea();
textArea.<<insert>>("Hello", 0);


Hasta aquí llegamos hoy. Del contenido básico sólo queda por cubrir: funciones, clases, sentencias de control y triggers (y algún que otro detalle), con lo cual es muy posible que quede completo con la próxima clase, que posiblemente esté publicando a mitad de la semana próxima. Hasta entonces, una buena practica puede ser la de comenzar a probar estos ejemplos, y modificarlos para ver toda la gama de posibilidades que provee JavaFX Script y, de paso, irse acostumbrando a la sintaxis del lenguaje.


Java FX Script, primeros pasos - Parte 3

Antes que nada, pido perdón por no respetar la frecuencia de publicación que prometí, pero esta semana tuve un problema familiar y se me atrasó todo el trabajo. Sin más excusas, ahora si, la última entrega de la "referencia" de JavaFX script. En la primera y la segunda entrega en conjunto, ya conocimos:

* Las características y el propósito de este lenguaje.
* Los tipos primitivos
* Declaración de variables
* Expresiones
* Manejo de cadenas de caracteres
* Arreglos
* El operador de selección
* Queries sobre arreglos
* Formateo de cadenas de caracteres

En esta entrega, terminaremos entonces de cubrir los tópicos de la referencia oficial de JFX Script, comenzando por la declaración de clases:

En JavaFX, la declaración de clases consiste en colocar la palabra reservada "class" seguida del nombre que deseamos darle. Finalmente, se coloca la lista de atributos, operaciones y funciones entre llaves, separadas por punto y coma. Si nuestra clase hereda funcionalidad de una o más clases, se coloca luego de su nombre la palabra reservada "extends", seguida de la lista de clases "padre" separadas por comas (a diferencia de Java, en JFXS sí existe la herencia múltiple):

class Persona {

attribute nombre: String;

attribute padre: Persona inverse Person.hijos;
attribute hijos: Persona* inverse Person.padre;

function obtenerIngresosFamiliares(): Number;
function obtenerCantidadDeHijos(): Number;

operation casarseCon(pareja: Persona): Boolean;
}

Atributos, funciones y operaciones:

En JavaFX un atributo se declara utilizando la palabra reservada "attribute" seguida del nombre deseado y, opcionalmente, el tipo, la cardinalidad ('?', '*' o '+') y una clausula "inverse" continuación. Declarar un atributo como "inverse" de otro implica que el interprete de JFXS realizará actualizaciones (inserción, borrado o reemplazo) en el atributo inverso ante una modificación en el atributo original.

Los atributos multivaluados (es decir, los declarados con '*' o '+') son tratados como arreglos y, por ende, puede accederse a la información contenida en los mismos utilizando el operador de selección ("[]".

La contraparte en JavaFX de los métodos en Java son las funciones y operaciones. Las primeras representan un subconjunto puramente funcional del lenguaje, se declaran utilizando la palabra reservada "function" y solo pueden contener declaraciones de variables y una sentencia de retorno. Son especialmente útiles para realizar operaciones matemáticas sencillas, por ejemplo:

function z(a,b) {
var x = a + b;
var y = a - b;
return sq(x) / sq (y);
}

function sq(n) {return n * n; }


Las operaciones, por su parte, pueden contener cualquier cantidad de sentencias de control: condicionales, bucles, try and catch, etc Es decir, son bastante más similares a un método Java tradicional que las funciones. Se declaran utilizando la palabra clave "operation" y, además del nombre, debe colocarse la lista de parámetros entre paréntesis y el tipo de retorno deseado utilizando ": tipo de retorno", por ejemplo:

operation substring(s:String, n:Number): String {
try {
return s.substring(n);
} catch (e:StringIndexOutOfBoundsException) {
throw "indice fuera de rango";
}
}


A diferencia de los métodos Java, en JFX el cuerpo de las funciones y operaciones debe definirse fuera de la clase:

function Persona.obtenerCantidadDeHijos() {
return sizeof this.hijos;
}

Así mismo, si deseamos darle valores iniciales a los atributos, debemos hacerlo fuera de la definición de la clase:

import java.lang.System;

class X {
attribute a: Number;
attribute b: Number;
}

attribute X.a = 10;
attribute X.b = -1;

var x = new );
System.out.println(x.a); // imprime 10
System.out.println(x.b); // imprime -1


Una vez definida nuestra clase, lo lógico es que creemos una o más instancias de la misma, para hacer esto JFXS soporta dos mecanismos: el primero, la sintaxis clásica de Java y, el segundo, la sintaxis declarativa propia de este lenguaje. La sintaxis clásica de Java consiste en realizar una llamada al constructor de la clase, pasando los valores de los atributos en el orden en que fueron declarados. La sintaxis declarativa consiste en: abrir llaves, para cada atributo colocar el nombre, dos puntos y el valor pretendido y finalizar cerrando llaves. Veamos un ejemplo utilizando la clase "Date" de Java:

import java.util.Date;
import java.lang.System;

var date1 = new Date(95, 4, 23); // llamada a un constructor Java
var date2 = Date { // creación de un "object literal" usando sintaxis declarativa
month: 4
date: 23
year: 95
};


Triggers

A diferencia de Java, las clases en JFXS no poseen constructores y, en general no se utilizan "setters" para los atributos. En vez de eso, el lenguaje provee un conjunto de triggers (similares a los de SQL) que permiten la manipulación de estos datos. En pocas palabras un trigger (que en castellano vendría siendo algo como "gatillo" es un conjunto de instrucciones que se programan para "dispararse" en respuesta a un evento dado. Es decir, la ocurrencia del evento presiona el gatillo que inicia la ejecución de las instrucciones. En JFXS pueden programarse triggers que respondan a eventos de creación de objetos, inserción, reemplazo y borrado de valores de atributos multivaluados.
Triggers de creación de objetos

los triggers de este tipo responden ante la creación de un objeto del tipo declarado en la sentencia "on new nombreDeLaClase". Por ejemplo:

import java.lang.System;

class X {
attribute nums: Number*;
}

trigger on new X {
insert [3,4] into this.nums;
}

var x = new );
System.out.println(x.nums == [3,4]); // imprime true


Este trigger inserta los elementos 3 y 4 dentro del atributo multivaluado "numbs" de cada instancia de la clase X, al momento de su creación. Nótese la utilización de la palabra reservada "this" para indicar que nos referimos a la instancia actual.
Triggers de inserción

Un trigger de este tipo se disparará al momento de realizar una inserción en un atributo multivaluado. Por ejemplo:

import java.lang.System;

class X {
attribute nums: Number*;
}

trigger on insert num into X.nums {
System.out.println("acaba de insertar {num} en X.nums en la posición {indexof num}";
}

var x = new );
insert 12 into x.nums;
// imprime "acaba de insertar 12 en X.nums en la posición 0"
insert 13 into x.nums;
// imprime "acaba de insertar 13 en X.nums en la posición 1"


Nótese el uso del mecanismo de inserción de inclusión de referencias a variables encerrándolas entre llaves, así como el uso de la instrucción "indexof ".
Triggers de borrado

Complementariamente a los triggers de inserción, existen los triggers que se disparan al borrarse un elemento de un atributo multivaluado:

import java.lang.System;

class X {
attribute nums: Number*;
}

trigger on delete num from X.nums {
System.out.println("acaba de borrar {num} de X.nums en la posición {indexof num}";
}

var x = X {
nums: [12, 13]
};

delete x.nums[1];
// imprime "acaba de borrar 13 de X.nums en la posición 1"
delete x.nums[0];
// imprime "acaba de borrar 13 de X.nums en la posición 0"


Nota: "num" es sólo un nombre elegido al azar, la referencia a la variable que será insertada (o borrada) puede tomar cualquier nombre.
Triggers de reemplazo

Estos triggers se disparan al reemplazar el valor de un atributo por uno nuevo. En el ejemplo, "valorViejo" y "valorNuevo" son nombres elegidos al azar para las variables que tomaran los valores respectivos del elemento actual. Al igual que el nombre "num" de los ejemplos anteriores, estos nombres pueden ser cambiados a voluntad.

import java.lang.System;

class X {
attribute nums: Number*;
attribute num: Number?;
}

trigger on X.nums[valorViejo] = valorNuevo {
System.out.println("X.nums: se reemplazo {valorViejo} con {valorNuevo} en la posición {indexof valorNuevo}";
}

trigger on X.num[valorViejo] = valorNuevo {
System.out.println("X.num: se reemplazo {valorViejo} con {valorNuevo}";
}

var x = X {
nums: [12, 13]
num: 100
};

x.nums[1] = 5;
// imprime "X.nums: se reemplazo 13 con 5 en la posición 1"
x.num = 3;
// imprime"X.num: se reemplazo 100 con 3"
x.num = null;
// "X.num: se reemplazo 3 con null"

Sentencias

JFXS soporta versiones similares de aquellas de Java. A saber:

if/ else: igual a la de Java, solo que requiere el uso obligatorio de llaves:

if (condition1) {
System.out.println("Condition 1";
} else if (condition2) {
System.out.println("Condition2";
} else {
System.out.println("not Condition 1 or Condition 2";
}

while: igual a la de Java, también requiere del uso de llaves obligatorio:

var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
System.out.println("i = {i}";
i += 1;
}


try, catch y throw: iguales a las de Java, pero con la declaración de variables propia de JFXS. Es importante notar que en JFXS cualquier objeto puede ser "arrojado" y/o "atrapado", no sólo aquellos que extienden "java.lang.Throwable"

try {
throw "Hola";
} catch (s:String) {
System.out.println("atrape un String: {s}";
} catch (any) {
System.out.println("atrape algo que no es un Sting: {any}";⁞
} finally {
System.out.println("finally...";
}

for: en JFXS, la sentencia for utiliza la misma sintaxis que "foreach" (una de las "list comprehensions" que vimos en la segunda parte del curso):

for (i in [0..10]) {
System.out.println("i = {i}";⁞
}

// imprime solo los números pares
for (i in [0..10] where (i%2 == 0) ) {
System.out.println("i = {i}";
}

// imprime solo los números impares utilizando un rango
for (i in [1,3..10]) {
System.out.println("i = {i}";
}

// imprime el producto cartesiano
for (i in [0..10], j in [0..10]) {
System.out.println(i);
System.out.println(j);
}

return: identica a la de java:

operation add(x, y) {
return x + y;
}


break y continue: identicas a sus pares Java, solo que en JFXS no soportan el uso de etiquetas. Deben aparecer dentro de un bucle for o while:

operation foo() {
for (i in [0..10]) {
if (i > 5) {
break;
}
if (i % 2 == 0) {
continue;
}
System.out.println(i);
}
}

operation bar() {
var i = 0;
while (i < 10) {
if (i > 5) {
break;
}
if (i % 2 == 0) {
continue;
}
System.out.println(i);
i += 1;
}
}


do y do later: Permite ejecutar un bloque de código JFX en un hilo en segundo plano. Normalmente, todo el código JFX se ejecuta en un único hilo (en particular en el hilo AWT Event Dispatch), unicamente el código contenido dentro de una sentencia "do" puede ejecutarse en otro hilo:

import java.net.URL;
import java.lang.StringBuffer;
import java.lang.System;
import java.io.InputStreamReader;
import java.io.BufferedReader;

// En el hilo AWT Event Dispatch
var result = new StringBuffer();

do {
// En un nuevo hilo en segundo plano
var url = new URL("http://www.foo.com/abc.xml";
var is = url.openStream();
var reader = new BufferedReader(new InputStreamReader(is));
var line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
result.append(line);
result.append("\n";
}
}

// Nuevamente en el hilo AWT Event Dispatch
System.out.println("result = {result}";


El ejemplo anterior vemos como es posible mandar operaciones a segundo plano mientras continuamos con la ejecución de nuestra aplicación. En general esto es muy útil para no interrumpir la ejecución de las instrucciones propias de la interfaz gráfica, rompiendo así con la fluidez de la aplicación. Sin embargo, el código dentro de una sentencia "do" sólo puede acceder a objetos Java que, de ser necesario, deberán hacerse cargo de su sincronización.

La sentencia "do later", en cambio, permite una ejecución asincronica del código que envuelve dentro del hilo AWD, en vez de una ejecución sincronica fuera del mismo (como lo hace "do":

import java.lang.System;
var saying1 = "Hola Mundo";
var saying2 = "Adios Mundo Cruel";
do later {
System.out.println(saying1);
}
System.out.println(saying2);


//imprime:
Adios Mundo Cruel
Hola Mundo

Evaluación incremental

Finalmente, la cereza de la torta: el mecanismo de evaluación incremental de JFXS. Este mecanismo permite que el valor de dos atributos se vincule utilizando la instrucción "bind", creando así en una especie de "Observer - Observable" entre ellos. De esta forma, el valor de un atributo cambiará cuando el atributo vinculado a este lo haga. Utilizando este mecanismo es posible encadenar los vínculos para generar interfaces gráficas que respondan en formas complejas a distintos eventos.

Veamos un ejemplo sencillo:

import java.lang.System;

class X {
attribute a: Number;
attribute b: Number;
}

var x1 = X {
a: 1
b: 2
};

var x2 = X {
a: x1.a // no incremental
b: bind x1.b // incremental
};

System.out.println(x2.a); // imprime 1

System.out.println(x2.b); // imprime 2

x1.a = 5;
x1.b = 5;

System.out.println(x2.a); // imprime 1
System.out.println(x2.b); // imprime 5


Es fácil ver que el vinculo se da entre X1.b y X2.b, nótese que cuando el primero se actualiza, el segundo lo hace también. En el caso de las funciones, los valores que se modifican dentro de las mismas son evaluados incrementalmente (es decir, si estaban vinculados, los vínculos tendrán efecto). Por el contrario, dentro de las operaciones los valores no son evaluados incrementalmente.

Otra versión de la evaluación incremental es la "perezosa" (lazy). En concordancia con su nombre, esta evaluación espera que el valor sea sometido a una lectura para verificar si debe actualizarse. Es común el uso de este mecanismo para manipular estructuras recursivas de datos. Modificando el ejemplo anterior podemos ver la evaluación perezosa en acción:

import java.lang.System;

class X {
attribute a: Number;
}

var x1 = X {
a: 1
};

var x2 = X {
a: bind lazy x1.a
// aún no se asignará ningún valor
};

System.out.println(x2.a);
// al leerlo, el valor se actualiza, e imprime 1


Java FX Script, primeros pasos - Parte 4

Cuando Sun presentó JavaFX en JavaOne 2007, JFX Script era aún un lenguaje interpretado. Sin embargo, desde un primer momento se aclaró que esto era sólo temporal, mientras el lenguaje pasaba de ser el resultado de la investigación personal (F3, de Chris Oliver) a un producto a ser introducido en el mercado. Es así como, después de un lapso de tiempo, JFX Script pasó de ser un lenguaje interpretado, a un lenguaje compilado, principalmente porque uno de las características principales que Sun pretende para las aplicaciones escritas en este lenguaje es un nivel de performance mayor que aquellas escritas en Javascript o ActionScript (de hecho, en los benchmarks realizados por Chris Oliver, el rendimiento de JFX Script fue 12 veces mayor que el de ActionScript.

Al cambio de categoría del lenguaje se sumaron algunos cambios en la sintaxis, los cuales revisaremos hoy, siguiendo la guia de migración de Planet JFX (simplemente porque fue una de las más completas que conseguí), como para dar por terminada esta parte, y comenzar luego con ejemplos algo más complejos y aggiornados.
Operaciones

Antes: el lenguaje diferenciaba entre funciones y operaciones.

class Foo {
function times2(x) { return x * 2; }
operation print(s) { System.out.println(s); }
}

Ahora: La sintaxis actual fusiona los dos conceptos en uno único: el de función, y utiliza la palabra reservada "function" para declararlas. Es decir, el único cambio que debemos realizar es el de renombrar las operaciones a funciones.

class Foo {
function times2(x) { return x * 2; }
function print(s) { System.out.println(s); }
}


Inicialización de atributos

Antes: los valores iniciales de los atributos debían declararse fuera del cuerpo de la clase.

class Foo {
attribute bar: Boolean;
}

attribute Foo.bar = true;

Ahora: esto se hace igual que en Java, es decir, dentro de la clase.

class Foo {
attribute bar: Boolean = true;
}

Triggers de reemplazo

Antes: los triggers de reemplazo se definían fuera de la clase, utilizando la sintaxis "trigger on atributo = valor"

Sin inicialización:

class Foo {
attribute bar: Boolean;
}

trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}

Con inicialización:

class Foo {
attribute bar: Boolean = true;
}

trigger on Foo.bar = value {
if (bar == true) {
beep();
}
}



Ahora: se definen dentro de la clase, como parte de la declaración del atributo, utilizando la sintaxis "on replace"

Sin inicialización:

class Foo {
attribute bar: Boolean on replace {
if (bar == true) {
beep();
}
};
}


Con inicialización:

class Foo {
attribute bar: Boolean = true on replace {
if (bar == true) {
beep();
}
};
}

Cardinalidad

Antes: el operador de cardinalidad era el asterisco ('*').

class Foo {
attribute names :String*;
}

attribute names = ["Monica", "Rachel", "Phoebe"];

Ahora: se utilizan en lugar del asterisco, los corchetes ("[]".

class Foo {
attribute names :String[] = ["Monica", "Rachel", "Phoebe"];
}

Literales objeto sin atributos

Antes: podíamos referirnos a literales objeto sin atributos utilizando solo el nombre de su clase.

Frame {
title: "Show MenuSeparator"
height: 180
width: 320
menubar: MenuBar {
menus: Menu {
text: "File"
items: [MenuItem {
text: "New"
}, MenuItem {
text: "Open"
}, MenuItem {
text: "Save"
}, MenuSeparator, MenuItem {
text: "Import"
}]
}
}
visible: true
}


Ahora: adicionalmente, debemos utilizar llaves ("{}".

Frame {
title: "Show MenuSeparator"
height: 180
width: 320
menubar: MenuBar {
menus: Menu {
text: "File"
items: [MenuItem {
text: "New"
}, MenuItem {
text: "Open"
}, MenuItem {
text: "Save"
}, MenuSeparator {
}, MenuItem {
text: "Import"
}]
}
}
visible: true
}


Instancias con nombre

Antes: era posible utilizar algunas instancias con nombre, que representaban constantes predefinidas.

Frame {
title: "White Frame"
background: white
...
}


Ahora: es necesario utilizar las constantes predefinidas, o literales objeto.

Frame {
title: "White Frame"
background: Color.WHITE
...
}

O también:

Frame {
title: "White Frame"
background: Color {
red: 1
green: 1
blue: 1
opacity: 1
}
...
}



Literales objeto anónimo

Antes: podíamos utilizar literales anónimos y dejar que el interprete infiriese el tipo de ese bloque de código.

...
accelerator: {
modifier: CTRL
keyStroke: O
}
....

Ahora: ante la ausencia de interprete, es obligatorio declarar el tipo de cada bloque.

...
accelerator: Accelerator {
modifier: KeyModifier.CTRL
keyStroke: KeyStroke.O
}
...

Sobreescribir funciones

Antes: podíamos sobreescribir funciones sin necesidad de colocar el tipo de retorno en la declaración de las mismas.

class MyWidget extends CompositeNode {
...
function composeNode() {
...
}
}


Ahora: es obligatorio escribirlo.

class MyWidget extends CompositeNode {
...
function composeNode() :Node {
...
}
}


Binding bidireccional

Antes: bastaba con colocar la palabra reservada "bind".

...
TextField {
value: bind model.firstName
}
...


Ahora: la sintaxis de JFX Script compilado requiere del uso de la clausula "with inverse".

...
TextField {
value: bind model.firstName with inverse
}
...

Casting de Number a Integer

Antes: el casting era automático.

...
var real :Number;
num = 6.42;
var integer :Integer;
integer = real;
...


Ahora: para evitar perdida de precisión en la compilación, debe utilizarse la función "intValue".

...
var real :Number;
num = 6.42;
var integer :Integer = real.intValue();
...


Herencia
Al menos hasta la versión del compilador de JavaFX de marzo de 2008, puede que sea necesario utilizar la palabra reservada "as" para evitar algunos problemas de herencia. Es decir, lo que antes hubiesemos hecho de esta forma:

class Foo extends Rectangle {
...
}

...

...
content: Canvas {
content: Foo {
...
}
...
}
...
}

Ahora debemos hacerlo así:

class Foo extends Rectangle {
...
}

var foo oo = Foo {
...
};

...
content: Canvas {
content: foo as Node
...
}
...
}


Bucles

Antes: existían dos bucles, "for" y "foreach".

...

for (Integer i = 0; i < element.length; i++) {
System.out.println(element);
}

...

foreach (element in group) {
System.out.println(element);
}
...


Ahora: al igual que con las funciones y operaciones, estos dos conceptos se fusionaron en uno solo que utiliza la palabra reservada "for" en su declaración. Este puede ser utilizado tanto como el "for" de Java así como el "foreach" de JFX Script.

...

for (Integer i = 0; i < element.length; i++) {
System.out.println({element});
}

...

for (element in group where element.length() < 4) {
System.out.println({element});
}
...


Java FX Script, primeros pasos - Parte 5- El mas interesante


Hoy finalmente vamos a comenzar a trabajar en algunos proyectos algo más entretenidos que aquellos de la referencia. Además vamos a dejar de lado el pad de JavaFX y comenzaremos a usar un ambiente de desarrollo integrado, que nos dará la posibilidad de administrar nuestros proyectos de una forma más amigable, nos permitirá arrastrar y soltar componentes en nuestros scripts y resaltará la sintaxis del lenguaje aumentando la claridad y la legibilidad del código.

Utilizaremos el IDE NetBeans 6.1 (Nota: el plug-in aún no funciona con la beta 6.5), un ambiente de desarrollo modular, multiplataforma, gratuito y libre (GPL v2 y CDDL), el cual pueden obtener aquí. Además, necesitaremos Java SE Update 7 o mayor, que puede descargarse aquí.

El ejemplo que realizaremos está tomado de javapassion, pueden encontrar la versión original aquí.
Paso 1: instalar el plugin de JavaFX en NetBeans

Empezaremos entonces por instalar el plugin de JavaFX en NetBeans, lo cual ampliará la funcionalidad original del IDE, agregando las funciones necesarias para desarrollar proyectos en el lenguaje JavaFX Script (si no poseemos NetBeans, podemos bajar una versión que incluye JavaFX aquí). Nota: para usar el update center de NetBeans, es necesario poseer una conexión a Internet.

1.1 - Abrir el update center. Abrimos NetBeans, vamos al menú herramientas, lo desplegamos y seleccionamos la opción "plugins".

1.2 - Obtener los plug-ins de interés. Dentro de la ventana "plugins" seleccionamos la pestaña "plugins disponibles", y buscamos aquellos que poseen la palabra "javafx" en su nombre. Seleccionamos los tres que el IDE encuentra ("JavaFX Wheater Sample" no es estrictamente necesario, pero es un ejemplo muy completo para observar y aprender algunas cosas) y procedemos a instalarlos. Finalmente reiniciamos el IDE para culminar la instalación.




Paso 2: construir la aplicación "Hola Mundo"

2.1 - Crear un nuevo proyecto JavaFX. Para esto, vamos al menú "archivo" y seleccionamos la opción "nuevo proyecto". Dentro de la ventana correspondiente, seleccionamos "JavaFX" en la columna correspondiente a la categoría y "aplicación JavaFX script" en la columna "proyectos", a continuación, hacemos click en siguiente y completamos los datos del proyecto colocando como nombre del mismo "JavaFXHolaMundo" y dejando las otras opciones con sus valores por defecto. Finalmente, hacemos click en "finalizar", y dejamos que el IDE prepare nuestro ambiente de trabajo.



2.2 - Agregar un frame para alojar todos nuestros elementos. Esto podemos hacerlo arrastrando desde la paleta al código (donde dice "//place your code here" el elemento Frame, o escribiendo a mano la descripción del literal y sus propiedades (fijense que el caret les indica donde quedará el código una vez que suelten el botón del mouse).

Dentro del código agregado podemos observar las propiedades titulo (title), ancho (width), alto (height), si es visible o no y la acción a realizar cuando el frame se cierre ("closeAction", en este caso, una llamada al método estático "close" de la clase "System", que hará que el programa finalice su ejecución).

2.3 - Modificar las propiedades del frame. Colocamos el valor de la propiedad height en 100, el de width en 400, y el titulo del frame lo cambiamos por "HolaMundoJavaFX".

2.4 - Construir y ejecutar el proyecto. Sencillo, simplemente hacemos click en el botón correspondiente, o podemos hacer click derecho sobre el proyecto y seleccionar la opción "ejecutar" del menú despegable.

Nota: en la versión del plug-in para algunos sistemas operativos se encuentra disponible una característica llamada "preview" que nos permite previsualizar el resultado de los cambios que vayamos realizando en nuestro código. Para activarla solo deben hacer click en el icono correspondiente, que se encuentra en el extremo izquierdo de la barra de acciones justo encima del editor.
Paso 3: agregar una etiqueta de texto a nuestro frame

3.1 - Agregar la etiqueta. Para esto, arrastramos el componente "text" desde la paleta hacia nuestro código, específicamente dentro de los contents del "stage" del frame.

3.2 - Editar el contenido del texto y su tamaño. Cambiamos el valor de la propiedad "content" por "Hola Mundo", y el de la propiedad "size" de la fuente por 24.

3.3 - Cambiar el color de relleno. Agregamos la propiedad "fill" con el valor "color.BLUE" en el componente "text". Necesitaran además agregar el "import" correspondiente para "Color", el mismo es "import javafx.scene.paint.Color". Nota: para ver una lista de propiedades de este y todos los componentes en el API de JavaFX, hacemos click en la opción "ayuda" de la barra de menús, expandimos la opción "referencias javadoc" y, seleccionamos la opción "documentación JavaFX Script". Luego vamos a la página correspondiente al API de JavaFX Script y buscamos los componentes y propiedades que nos interesen.




Paso 4: Agregar un circulo al frame

4.1 - Preparar el terreno. Primero, agregamos una coma luego de la descripción del "text" en el "stage" (entre la llave y el corchete), en pos de separar las descripciones de los distintos componentes.

4.2 - Agregar el circulo. Ahora, arrastramos el componente "circle" desde el panel, y lo ubicamos luego de la coma que acabamos de colocar.

4.3 - Ubicar el circulo y ajustar sus propiedades. Damos a la propiedad "CenterX" (coordenada X del centro) el valor 200, a "CenterY" (coordenada Y del centro) y a "radius" (radio) el valor 30, y a "fill" (relleno), "Color.PINK".



Paso 5: agregar un efecto al circulo

5.1 - Agregar el efecto lighting. Para agregar este efecto al circulo, debemos añadir la propiedad "effect" y darle el valor "lighting". La luz de este efecto la describiremos como de tipo "DistantLight", y al acimut (el ángulo de incidencia de la luz en el plano XY - experimenten cambiándole los valores para ver cual es el efecto que genera) de esta luz le daremos el valor 60. Además debemos agregarle 2 imports al código, que debería quedar de la siguiente forma (en negrita el código que tendríamos que agregar):

package javafxholamundo;

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;
import javafx.scene.Font;
import javafx.scene.FontStyle;
import javafx.scene.geometry.Circle;
import javafx.scene.paint.Color;
import javafx.scene.effect.Lighting;
import javafx.scene.effect.light.DistantLight;
import javafx.input.MouseEvent;

/**
* @author ezequiel
*/

// place your code here
Frame {
title: "JavaFXHolaMundo"
width: 400
height: 100
closeAction: function() {
java.lang.System.exit( 0 );
}
visible: true

stage: Stage {
content: [Text {
font: Font {
size: 24
style: FontStyle.PLAIN
}
fill: Color.BLUE
x: 10, y: 30
content: "HelloWorld"
},Circle {
centerX: 200, centerY: 30
radius: 30
fill: Color.PINK
effect: Lighting {
light: DistantLight {
azimuth: 60
}
}

}]
}
}




5.2 - Hacer que la iluminación se encienda y apague al hacer click en el circulo. Para hacer esto, agregamos un efecto "onMousePressed" arrastrando y soltando el mismo debajo de la última propiedad del circulo. Adicionalmente, para agregar el comportamiento deseado, agregamos el siguiente código (bastante sencillo de comprender):

package javafxholamundo;

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.text.Text;
import javafx.scene.Font;
import javafx.scene.FontStyle;
import javafx.scene.geometry.Circle;
import javafx.scene.paint.Color;
import javafx.scene.effect.Lighting;
import javafx.scene.effect.light.DistantLight;
import javafx.input.MouseEvent;

/**
* @author ezequiel
*/

// place your code here
Frame {
title: "JavaFXHolaMundo"
width: 400
height: 100
closeAction: function() {
java.lang.System.exit( 0 );
}
visible: true
var counter: Integer=0

stage: Stage {
content: [Text {
font: Font {
size: 24
style: FontStyle.PLAIN
}
fill: Color.BLUE
x: 10, y: 30
content: "HelloWorld"
},Circle {
centerX: 200, centerY: 30
radius: 30
fill: Color.PINK
effect: Lighting {
light: DistantLight {
azimuth: 60
}
}
onMousePressed: function( e: MouseEvent ):Void {
if (counter mod 2 == 0) {
e.node.effect = null
} else {
e.node.effect = Lighting {
light: DistantLight {
azimuth: 60
}
}
}
counter++
}
}]
}
}



Espero que les sea de utilidad, yo por mi parte no he tenido el tiempo de leerloa todo pero por las charlas que he visto del tema me parece que esta muy bueno ... Saludos.




Datos archivados del Taringa! original
0puntos
215visitas
0comentarios
Actividad nueva en Posteamelo
0puntos
1visitas
0comentarios
Dar puntos:

Dejá tu comentario

0/2000

No hay comentarios nuevos todavía

Autor del Post

f
Usuario
Puntos0
Posts6
Ver perfil →
PosteameloArchivo Histórico de Taringa! (2004-2017). Preservando la inteligencia colectiva de la internet hispanohablante.

CONTACTO

18 de Septiembre 455, Casilla 52

Chillán, Región de Ñuble, Chile

Solo correo postal

© 2026 Posteamelo.com. No afiliado con Taringa! ni sus sucesores.

Contenido preservado con fines históricos y culturales.