viernes, 28 de agosto de 2009

Migrando a JavaFX 1.2: de UiStub --> FXDNode


Ayer retomé JavaFX para migrar el proyecto memeFX (código abierto) a la versión JavaFX 1.2 y este es básicamente el único inconveniente que encontré (además de un bug algo extraño que tengo que verificar).

Estas clases se utilizan para obtener recursos gráficos para la aplicación, como las imágenes generadas con Adobe Illustrator:

En JavaFX 1.1 se usaba UiStub del paquete javafx.fxd.UiStub:


import javafx.fxd.UiStub;

public class myClass extends UiStub {

override protected function update() {
...
}

}

Ahora, es necesario utilizar FXDNode también en el paquete javafx.fxd. Sólo hay que hacer este cambio:

import javafx.fxd.FXDNode;

public class BasicKnob extends FXDNode {

override protected function contentLoaded() {
...
}

}

martes, 16 de junio de 2009

Pastello... JavaFX crayon phyics


Me suscribi para seguir el Twitter de JavaFX y el primer post que encontré me pareció entretenido. Se llama Pastello. Es un juego de "puzzle" en que debes dibujar la solución para que una bolita llegue hasta donde está la estrella.

Lo interesante del proyecto es la FISICA de los elementos. Así, por ejemplo, puedes dejar caer bolitas sobre la bola principal, de manera que esta gire subiendo por una rampa que dibujaste con un triangulo. También puedes agregar contrapesos, etc. Un tanto adictivo! ;D

Jugar Pastello

Sitio del proyecto Pastello

viernes, 5 de junio de 2009

JavaFX 1.2 incluye APIs para gráficos (charts)


Este es el código para agregar un gráfico de Pie en JavaFX

package piechartsample;

import javafx.scene.chart.*;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

var name = ["Java", "JavaFX", "MySQL", "Netbeans", "Others"];
var val = [20, 12, 25, 22, 30];


var r : PieChart.Data[] = [];
for(i in [0..4]) {
insert PieChart.Data {
action: function() {
print("Name :: {name[i]}");
}
label : name[i] value : val[i] } into r; }


var chart = PieChart3D {
data : r
pieLabelFont: Font.font("dialog", FontWeight.REGULAR, 8);
pieLabelVisible: true
pieValueVisible: true
pieToLabelLineOneLength: 3
pieToLabelLineTwoLength : 6

}

var st = Stage {
height: 420
width: 480
title:"Pie Chart 3D"
scene: Scene {
content: [
chart
]
}
}

Este es el código para un gráfico de "burbujas":


package bubblechart;

import javafx.scene.chart.*;
import javafx.scene.chart.part.*;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.stage.Stage;
var x: Number[] = [30, 60, 10, 100,50];
var y: Number[] = [40,20,90,40,23];
var radius: Number[] = [10,13,7,10,5];

var x1: Number[] = [13, 20, 100, 30,50];
var y1: Number[] = [100,80,60,40,20];
var radius1: Number[] = [7,13,10,6,12];

var r : BubbleChart.Data[] = [];
var r1: BubbleChart.Data[] = [];
for(i in [0..4]) {
insert BubbleChart.Data {fill: Color.YELLOWGREEN xValue : x[i] yValue : y[i] radius: radius[i] }into r;
insert BubbleChart.Data {fill: Color.RED xValue : x1[i] yValue : y1[i] radius: radius1[i] }into r1;

}
var s1 : BubbleChart.Series = BubbleChart.Series{data: [r,r1]};
var BubbleChartData : BubbleChart.Series[] = [s1];
var chart = BubbleChart {
title : "BubbleChart"
xAxis : NumberAxis{
lowerBound: 0
upperBound : 150
label: "SomeX"
visible: true
axisStrokeWidth: 1
tickUnit : 20
tickLabelsVisible: true

}
yAxis : NumberAxis{
lowerBound: 0
upperBound : 150
tickUnit: 20
label: "SomeY"
visible: true
tickLabelsVisible: true
}
data : BubbleChartData
}

var st = Stage {
height: 520
width: 640
title:"Bubble Chart"
scene: Scene {
content: [
chart
]
}
}


Aquí hay algunos demos con JavaFX


A partir del minuto 20 de este video hay unas demos con JavaFX, incluyendo (minuto 30) la herramienta para generar JavaFX (como la utilizada hoy para generar Flash). Se ve bastante interesante, especialmente la capacidad de generar versiones para distintos dispositivos, en el mismo proyecto. Esto quiere decir que, mientras desarrollas una aplicación JavaFX para el escritorio, puedes ir creando una versión para un aparato móvil, un netbook o un TV con JavaFX (con distintos elementos o distribución de esos elementos en la pantalla de cada dispositivo).

Video: El show de los "Juguetes" Java

viernes, 8 de mayo de 2009

Mejores prácticas para el desarrollo de JavaFX móvil


Un interesante artículo que describe como desarrollar aplicaciones con el mejor rendimiento posible en aparatos móviles, usando JavaFX Script:

- Evitar bindings innecesarios
- Mantener el scenegraph lo más pequeño posible
- Usar Integer en vez de Number
- Usar figuras simples en vez de imágenes
- Usar funcionalidad de pre-escalado
- Usar background loading ("descarga de fondo")
- Usar funciones de clase Sequences

Aquí está el artículo completo. Más adelante revisaré cada recomendación, para compartir los "descubrimientos" ;)

http://java.sun.com/developer/technicalArticles/javafx/mobile/

miércoles, 29 de abril de 2009

MemeFX en artículo de Sun.com


Los articulistas de Sun seleccionaron 3 proyectos JavaFX para mostrar lo que la comunidad está desarrollando con el nuevo lenguaje. Uno de los desarrollos seleccionados fue mi proyecto de código abierto MemeFX :D

.... ¡está bien!, no es lo mismo que aparecer en la portada del Times jajaja.... pero no deja de ser bueno para el ego hacer algo que para otros puede resultar interesante... ;D ... así que me disculpan que me sienta un poco más "importante" que ayer jajajaja... ;D

Bueno, el artículo incluye otro dos proyectos bastante buenos. Un cliente para Twitter, desarrollado en JavaFX y una aplicación de Widgets para el escritorio (similar al Windows Sidebar de Vista).

Por cierto, el autor de TwitterFX me contacto para agradecer por el componente TextHTML, porque hasta ahora el no habia podido desarrollar uno ni había otro en existencia en la internet, así que por lo menos el proyecto de componentes que estoy desarrollando comienza a ser UTIL -de verdad y no sólo una curiosidad técnica- que es lo que realmente importa.

Aquí está el link al artículo en java.sun.com:

http://java.sun.com/developer/technicalArticles/javafx/AppORama/index.html

viernes, 24 de abril de 2009

Demo de TextHTML (componente de memeFX)


Aquí hay una pequeña demo del componente TextHTML en acción. Incluido el uso de LINKS en los textos de una aplicación JavaFX. También pueden ver AQUI una demo Java Web Start.



La biblioteca memeFX está AQUI

jueves, 23 de abril de 2009

Cache:true ... otro detalle para mejorar el rendimiento gráfico


Si, por ejemplo, utilizas una figura con una gradiente -o sombra- como fondo de un área, es mejor agregar a esa figura el parámetro cache:true, de manera de evitar que el objeto sea re-dibujado con cada cambio que se realiza por encima de él (en el caso de una gradiente, debe volver a recalcular y redibujar cada franja de color que conforma la transición entre los colores... lo que resulta sumamente "caro" en términos de procesamiento).



El código al pie del artículo

Al agregar cache:true se le indica al sistema que debe generar la imagen una vez y guardarla en cache, de esta menera, cuando se realiza un cambio sobre ella, sólo recupera la imagen desde una copia almacenada en memoria (como bytes) y pinta los cambios encima, lo que resulta sumamente rápido y menos "costoso" en ciclos de procesamiento.

En todo caso, no debes utilizar cache:true cuando la imagen va a cambiar con frecuencia, porque esto sólo producirá que sea re-generada con cada cambio, pero además almacenada, lo que da por resultado un proceso mucho más "costoso". El cache:true debe ser utilizado en imágenes que NO cambian frecuentemente.




import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.scene.effect.DropShadow;

var px:Number;
var py:Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse:true
keyFrames : [
KeyFrame {
time : 0s
values: [px=>100, py=>100]
}
KeyFrame {
time : 3s
canSkip : true
values: [px=>1180, py=>680]
}
]
}.play();

var angle:Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse:true
keyFrames : [
KeyFrame {
time : 0s
values: [angle=>0]
}
KeyFrame {
time : 1.5s
canSkip : true
values: [angle=>360]
}
]
}.play();

Stage {
title: "Application title"
width: 1024
height: 768
scene: Scene {
content: [
Rectangle {
cache:true
x: 0, y: 0
width: 1280, height: 768
fill: LinearGradient {
startX : 0.0
startY : 0.0
endX : 1.0
endY : 0.0
stops: [
Stop {
color : Color.BLACK
offset: 0.0
},
Stop {
color : Color.BLUE
offset: 1.0
},

]
}
effect: DropShadow { offsetX:10, offsetY:10 }
}
Rectangle {
x: bind px, y: bind py
width: 200, height: 200
fill: Color.RED
rotate: bind angle
}

]
}
}

lunes, 20 de abril de 2009

HTML en JavaFX


Componente TextHTML

Ya está disponible el componente TextHTML en la biblioteca memeFX.

Este control -aunque básico- permite incluir contenido mucho mas rico que el permitido por el componente Text, standard en JavaFX.

Por ahora, básicamente permite cambiar los fonts (fuentes), color del texto, la alineación de parrafos, agregar LIKS (enlaces) e incluir algunos caracteres especiales (< > € etc). Para más información visita el sitio del proyecto y mira la documentación.

TextHTML {
x: 20, y: 20, wrappingWidth:300

content:
"<p align=justify><font size=24 color=#ffff00>"
"Easter Island</font> <i>(Rapa Nui)</i> is a "
"Polynesian island in the southeastern <font "
"size=24 face=Verdana color=#ff5555>Pacific "
"Ocean</font>. The island is a <font face="
"TimesRoman size=24 color=#55ffff><a href="
"algo>special</a> <i>territory</i></font> of "
"<a href=Chile>Chile</a>. Easter Island is "
"famous for its monumental statues, called "
"<font color=#00ff00 size=18>moai.</font></p>"

onLinkPressed: function(link) { println(link) }

};


En el futuro espero incluir imágenes y algun tipo de control sobre ellas (alineación de las imágenes, eventos como mouseOver, etc).

HTML en ImagesAccordion

El componente ImagesAccordion ahora incluye el componente TextHTML para las descripciones extensas de las imágenes.

CLICK AQUI para lanzar un demo Java Web Start


http://code.google.com/p/memefx/

sábado, 18 de abril de 2009

Como mejorar la partida de Java Web Start


Aquí hay un truco para mejorar la velocidad de partida de las aplicaciones JavaFX Web Start.

Todo lo que necesitas hacer es agregar una linea a tus archivos JNLP.

<update check="background">

El elemento update se usa para indicar la preferencia sobre como Java Web Start maneja las actualizaciones. El elemento update puede contener los siguientes atributos opcionales:

Atributo check: El atributo check indica la preferencia sobre cuando el cliente JNLP debe chequear por actualizaciones, y puede tener uno de los siguientes tres valores:

1. "always": Siempre chequea por actualizaciones antes de lanzar la aplicación.
2. "timeout" (default): Chequea por actualizaciones hasta alcanzar un umbral de tiempo (timeout) antes de lanzar la aplicación. Si el chequeo de actualización no se completa antes del timeout, la aplicación es lanzada, y el chequeo de actualización continua por "atras" del proceso de la aplicación.
3. "background": Lanza la aplicación mientras chequea si existen actualizaciones simultaneamente, por "atras".

La opción 3 mejorará el tiempo de partida de las aplicaciones JavaFX Web Start.

jueves, 16 de abril de 2009

Un detalle para obtener una mejor performance


Este detalle (junto con incluir canSkip:true en los timelines asociados a animaciones o transiciones gráficas de algún tipo) puede hacer una gran diferencia en la performance gráfica de JavaFX.



Tengo un CustomNode cuyo contenido son unos nodos almacenados en una secuencia:


public class myComponent extends CustomNode {

var objs:Node[];

public override function create(): Node {
Group {
content: bind objs
};
};
}

Digamos que se necesita reemplazar con frecuencia los nodos en objs y que tengo una función que realiza este trabajo:

function paint():Void {

delete objs;

for (element in someGroup) {
...
insert newNode into objs;
...
}
}

Como muestra el video, eso es ineficiente. Al estar enlazado el contenido de objs con el contenido del componente e ir cambiando el contenido de objs, se va gatillando trabajo para refrescar el componente.

Es más eficiente crear una secuencia temporal y traspasar al final todos los nodos a la secuencia asociada al componente:

function paint():Void {

var tmpObjs:Node[];

for (element in someGroup) {
...
insert newNode into tmpObjs;
...
}

objs = tmpObjs;
}

Habría que probar si esto también es aplicable cuando se realizan cambios a los elementos en la secuencia (y no sólo cuando estos se reemplazan). Es probable que ocurra lo mismo, porque al actualizar uno a uno los elementos, es muy probable que también se vaya gatillando trabajo parcial, mientras si se reemplaza toda la secuencia de una vez, ese trabajo debería ocurrir una sóla vez... ¿?

martes, 14 de abril de 2009

Un "truco" para obtener el alto de un Font


Estoy construyendo un componente de texto HTML para mi librería de JavaFX, y me encontré con el siguiente problema.

¿Cómo obtengo el alto de un font?

No confundan el obtener el alto de un TEXT con obtener el alto de un FONT.

Si uno tiene un texto y quiere obtener sus dimensiones, basta con:


var myText = Text {
content: "Mi texto"
...
Font {
size:24
...
}
}

var ancho = myText.boundsInLocal.width;
var alto = myText.boundsInLocal.height;


El asunto es que si uno pone "....." como contenido del texto, el alto es de apenas unos pixeles.

Probablemente ustedes no se vayan a encontrar con este problema con mucha frecuencia, pero para el componente que estoy creando es decisivo... entonces, aquí va una solución bastante... mmm.... "simple"... pero funciona.


var myFont = Font {
size=24
...
}

var tmpText = Text {
content: "Aj"
font: myFont
}

var myText = Text {
content: ".........."
font: myFont
...
}

var altoFont = tmpText.boundsInLocal.height;
var anchoTexto = myText.boundsInLocal.width;


No es el ideal recurrir a este tipo de "trucos", pero me pareció mejor que tener que incluir las APIs de Java para el manejo de Fonts, que si entregan todo tipo de información sobre las fuentes... espero que en próximas versiones de JavaFX la API incluya una alternativa más "elegante", es decir, que entregue directamente las medidas asociadas a las fuentes.

domingo, 12 de abril de 2009

"On Replace" no es "On Modify"...


No tengo claro si hay alguna manera de hacer que "on replace" se dispare al modificar una variable de una clase definida por el usuario (supongo que no, porque se llamaría ON MODIFY). Al parecer es necesario recrear el objeto y asignarlo a la variable para que "on replace" detecte el cambio.

Digamos que defino una clase:

public class Data {
public var attrib1:Number=0.0;
public var attrib2:String="";
}

Luego, creo una instancia de la clase:

var data1:Data = Data {
attrib1: 1.2,
attrib2: "Mauricio"
} on replace {
println("{data1.attrib1} {data1.attrib2}");
};
data1.attrib2="Omar";
data1.attrib2="Veronica";

La salida de este ejemplo es:

1.2 Mauricio

Es decir, al asignar "Omar" o "Veronica" a attrib2, el bloque "on replace" de data1 no se dispara.

La única manera (que conozco) de evitar esto es asignar una nueva instancia del objeto a data1:

data1= Data { attrib1:data1.attrib1 attrib2:"Omar"};

Quizás haya alguna forma de declarar la variable con algún tipo de binding o algo por el estilo que permita detectar los cambios de variables dentro de una instancia.

Por ahora, vale la pena tener este detalle presente para evitar problemas y pérdidas de tiempo... si uno no lo sabe, puede no ser evidente que esa parte del código no se está ejecutando y hay un bug en el código.

"On Replace" aplicado a secuencias


La clausula "on replace", si está presente al momento de definir una variable, causa que el bloque de código sea ejecutado cada vez que el valor de la variable cambia -- incluyendo la asignación inicial de su valor.

Los parametros opcionales proveen información sobre qué cambió: on replace oldValue [ firstIndex .. lastIndex ] = newElements { ... }

Todos estos parámetros son opcionales, y en la más simple de las formas, ninguno está presente:

var x = 0 on replace { println("x is ahora: {x}") }

El parámetro "oldValue" provee el valor previo de la variable.

var x = 0 on replace oldValue {
println("x was {oldValue} and is now: {x}") }

Los parámetros remanentes son utiles con las secuencias.

var seq = ['A', 'B', 'C', 'D', 'E', 'F']
on replace oldValue[firstIdx .. lastIdx] =
newElements { println(
"reemplazo {oldValue}[{firstIdx}..{lastIdx}]"
" por {newElements} produciendo {seq}")
}
seq[3] = '$';
insert '#' into seq;
delete '$' from seq;
delete seq[2];
seq[1..4] = ['X', 'Y'];
delete seq;

Lo que produce está salida:

replaced [0..-1] by ABCDEF yielding ABCDEF
replaced ABCDEF[3..3] by $ yielding ABC$EF
replaced ABC$EF[6..5] by # yielding ABC$EF#
replaced ABC$EF#[3..3] by yielding ABCEF#
replaced ABCEF#[2..2] by yielding ABEF#
replaced ABEF#[1..4] by XY yielding AXY
replaced AXY[0..2] by yielding

Nota que firstIndex, lastIndex and newElements se refieren únicamente a la porción que la secuencia que cambió.

martes, 7 de abril de 2009

Cómo compilar clases Java en JavaFX, que contienen delete o insert


Como saben, los términos "insert" y "delete" están reservados por JavaFX para insertar y borrar elementos en las secuencias. Esto puede producir un inconveniente cuando uno incluye dentro de un proyecto JavaFX una clase Java donde se utiliza llamadas a métodos denominados "insert" o "delete".

Esto ocurre, por ejemplo, si se utiliza "delete" para borrar un archivo. El compilador muestra el siguiente error:

Sorry, I was trying to understand an expression
but I got confused when I saw 'delete' which is a keyword.
new java.io.File("temp").delete();
^

La solución es simple, sólo hay que incluir el término entre << ... >>

new java.io.File("temp").<<delete>>();

Lo interesante es que esta sintaxis permite utilizar cualquier texto como un identificador:

def <<english
variable>> = "cualquier simbolo sirve como identificador";
def <<русская переменная>> = <<english
variable>>;
println(<<русская переменная>>);

La salida del script es:

cualquier simbolo sirve como identificador

Noten que el identificador "english variable" incluso incluye un salto de línea (CR/LF).

lunes, 30 de marzo de 2009

Usando FX.deferAction()


Al desarrollar el componente de acordeón de imágenes, se producía un "pestañeo extraño" al momento de solicitar a los elementos que actualizaran su posición. Le dí algunas vueltas al código hasta comprobar que: no era resultado de algo producido por un error en el código, sino por algo relacionado con el refresco de la pantalla (o algo por el estilo ¿?).

Así que recurrí un poco al instinto y a la memoria... y... recorde haber leido algo sobre deferAction. Así que incluí el código que solicita el ajuste de posición, dentro de la llamada de esta función y el problema terminó:

El siguiente código:


public-read var active: Integer on replace {
for (item in pictures) {
item.adjust();
};
};



Pasó a ser este:


public-read var active: Integer on replace {
FX.deferAction(function():Void {
for (item in pictures) {
item.adjust();
};
});
};



Sólo anidé el for dentro de FX.deferAction(function():Void { ... });

Sí descargan la biblioteca memeFX y quitan el FX.deferAction en ImagesAccordion.fx, quizás puedan ver el problema que me llevó a esta solución (no sé si el problema sea reproducible en otras máquinas). Tampoco me quedó claro que produce el problema ni por qué lo resuelve deferAction, voy a buscar información al respecto. Por ahora, sólo comparto el "tip" en caso que alguien tenga un problema similar.

http://code.google.com/p/memefx/

domingo, 29 de marzo de 2009

Por qué incluir canSkip:true en los timelines


Este video muestra la diferencia en el rendimiento de las animaciones gráficas con y sin canSkip:true. Es recomendable incluir este parametro en los KeyFrame que alteran valores (values:) asociados a animaciones y efectos (cambio de posiciones, variación de sombras, difuminado, etc... cualquier efecto o movimiento gradual).

Una persona del grupo JavafxProgramming creo una aplicación Flex que luego replicó en JavaFX. Su problema era el rendimiento de JavaFX, que hacia su aplicación inutilizable. Pidió consejos al foro, pero nada funcionó... hasta que incluyó el canSkip:true en los timelines... recién ahí la aplicación funcionó como debía.

Quizás sería bueno que Sun incluyera el canSkip con true como valor por defecto. Esto puede terminar desprestigiando la plataforma.

En todo caso, sería bueno que todos comparta este detalle o lo tengan muy presente al momento de usar los timelines o de tener problemas con el rendimiento.

domingo, 22 de marzo de 2009

memeFX 0.2.2 : ImagesAccordion con ORIENTACION


Ahora se puede cambiar la orientación del componente de "acordeón", para que las imágenes se desplacen horizontal o verticalmente. Basta con incluir en la definición del componente, un:

orientation: ImagesAccordion.VERTICAL

para que las imágenes se muevan verticalmente.

viernes, 20 de marzo de 2009

Como crear juegos para móvil con Netbeans 6.5


Realmente, es increible lo fácil que resulta crear un juego para teléfono con Netbeans 6.5. De hecho, este tutorial demuestra como crear uno en ¡10 minutos! Vale la pena verlo y tenerlo en cuenta.

Blogger no me permite incrustar el video aquí, así que ahí les va el enlance (se ve mejor a pantalla completa): CLICK AQUI

- Crea tu propio juego para móvil. Hands on Lab6400 (pdf)
- Mobile Game Project.zip
- Guía para el GameBuilder de Netbeans

memeFX v0.2


Ya está disponible la librería incluyendo una versión final de "acordeón de imágenes". La verdad que tiene muchas opciones para personalizar el componente. Además, permite enlazar algunas acciones del usuario con funciones.

Asi, por ejemplo, se podría hacer el que componente comience a tocar un MP3 distinto al pasar el mouse sobre los distintos elementos del "acordeón", y que muestre detalles sobre la música si se hace click sobre ese elemento. Pudiendo además dejar de tocar al abandonar el "acordeón" (al sacar el mouse de encima).

Proyecto memeFX y descarga código fuente, CLICK AQUI


Demo Java Web Start del ImageAccordion, CLICK AQUI

Esto me dio la idea de hacer una variación que permita crear un "acordeón de formulario", es decir, que haya varios formularios con campos, botones, etc. Y que uno pueda pasar de uno a otro igual que en el acordeón de imágenes. Voy a darle vueltas y veré que resulta.

jueves, 19 de marzo de 2009

Timelines de duración variable y el at(..)


Uno de los inconvenientes de trabajar en un ambientea tan nuevo como el JavaFX script, es que no abundan las fuentes de información.

Una de las cosas que no tenía claras (seguramente porque no investigué lo suficiente, o no miré mejor los ejemplos), era cómo se hace que un timeline tenga duración variable.

Bueno, ahora descubrí que diablos es el at(..) en los timeline y cómo se hace para que la duración sea variable.

Primera cosa, cuando uno usa at(), en realidad está usando una "abreviatura" en la manera de definir un timeline. La idea es que sólo sea una opción breve para escribir el código, pero por lo mismo, no es la forma más "potente".

Tomemos este ejemplo:

var x:Number;
Timeline {
repeatCount: 1
keyFrames: [
at (0s) { x => 0.0}
at (3s) { x => 100.0}
]
}.play();

En él, definimos una variable x, que al segundo 0s -de iniciado el timeline- asume un valor de 0.0, luego, x asumirá gradualmente los valores entre 0.0 y 100.0, alcanzando el máximo valor (100.0) en 3s segundos.

El asunto que me faltaba comprender era... ¿como cambiaba el tiempo en at(3s)?, porque no acepta ni variables ni bind dentro de los paréntesis.

Pues resulta que con at(..) no lo vas a conseguir (por lo menos eso entiendo en este momento), la forma correcta y potente de usar las lineas de tiempo es esta:


var dur=3s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
values: [x=>100]
}
]
}.play();

De hecho, esta forma de definir un timeline permite anidar timelines o agregar un action: en cada KeyFrame, de manera que una acción sea ejecutada cada vez que una etapa de la animación o transición se completa.


var dur=3s;
var dur2=5s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
canSkip: true
values: [x=>50]
action: function() {
println("completo etapa 1");
};
},
KeyFrame {
time: bind dur2
canSkip: true
values: [x=>200]
action: function() {
println("completo etapa 2");
};
}
]
}.play();

miércoles, 18 de marzo de 2009

Un "acordeón" de imágenes


Este componente aun no está terminado, pero ya lo pueden probar usando el link de más abajo.

Lanzar demo Java Web Start, CLICK AQUI

Código fuente componente y demo, CLICK AQUI


El código para crear un "Acordeón de imágenes" es el siguiente:

var accordion = ImagesAccordion{
width: 650,
height: 350,
lineWidth: 1.0,
lineColor: Color.WHITE
images: [
ImageItem {
id: "moais",
caption: "Moais"
image: Image { url: "{__DIR__}moais.jpg"
}
message: "Easter Island (Rapa Nui) is a Polynesian island in the "
"southeastern Pacific Ocean, The island is a special "
"territory of Chile. Easter Island is famous for its "
"monumental statues, called moai."
messageArea: Rectangle2D {
minX: 30,
minY: 253,
width: 350,
height: 87
}
call: click
},
ImageItem {
id: "anakena",
caption: "Anakena"
image: Image { url: "{__DIR__}anakena.jpg"
}
message: "‘Anakena is a white coral sand beach in Rapa Nui..."
messageArea: Rectangle2D {
minX: 20,
minY: 233,
width: 360,
height: 107
}
messageFont: Font { size: 12
}
call: click
},
ImageItem {
id: "glacier",
caption: "Grey glacier"
image: Image { url: "{__DIR__}grey.jpg"
}
message: "The Glacier is in the south end..."
messageArea: Rectangle2D {
minX: 30,
minY: 270,
width: 320,
height: 70
}
call: click
},
ImageItem {
id: "torres",
caption: "Torres del Paine"
image: Image { url: "{__DIR__}paine.jpg"
}
message: "Spectacular mountain group in Torres del Paine..."
messageArea: Rectangle2D {
minX: 30,
minY: 270,
width: 320,
height: 70
}
call: click
},
ImageItem {
id: "salar",
caption: "Atacama desert"
image: Image { url: "{__DIR__}salar.jpg"
}
message: "The Atacama Desert is a virtually rainless plateau in Chile..."
messageArea: Rectangle2D {
minX: 20,
minY: 237,
width: 380,
height: 103
}
// messageFont: Font { size: 12 }
call: click
},
/* ImageItem {
id: "crayons",
caption: "Crayones"
message: "Recuerdos de la niñez"
image: Image { url: "{__DIR__}crayons.jpg"}
call: click
},*/
ImageItem {
id: "chaiten",
caption: "Chaiten"
image: Image { url: "{__DIR__}chaiten.jpg"
}
message: "Hell on Earth. Chaitén is a volcanic caldera..."
messageArea: Rectangle2D {
minX: 20,
minY: 270,
width: 420,
height: 70
}
call: click
}
]
effect: Reflection { fraction: 0.1,
}

};

function click(image:ImageItem) {
println(image.id);
}

martes, 17 de marzo de 2009

Un pequeño bug en el Timeline y una solución


Estaba haciendo unas pruebas y de vez en cuando notaba un comportamiento extraño, finalmente descubrí que el problema se origina en la reutilización de un Timeline, que estaba definido como:

var timeline= Timeline {
repeatCount: 1
keyFrames: [
at (0s) { currentX => startX }
at (0.5s) { currentX => posX }
]
};

Aunque no es el ideal que haya bugs, es algo habitual en una plataforma nueva (he leído que Adobe Flex tiene demasiados bugs, así que -aunque es consuelo de tontos- me voy a conformar con este pequeño error en JavaFX).

La solución fue tan simple como... no reutilizar el Timeline una y otra vez, sino que recrearlo cuando lo voy a usar:

var timeline:Timeline;

...

timeline = Timeline {
repeatCount: 1
keyFrames: [
at (0s) { currentX => startX }
at (0.5s) { currentX => posX }
]
};
timeline.playFromStart();

El problema específico de mi programa, era que a veces asignaba currentX igual a 0 en at (0s), aunque startX no tenia ese valor.

En todo caso, no es un problema que ocurra con frecuencia (supongo que debe ser un pequeño error el compilador), así que no es necesario hacer esto cada vez que utilicen un Timeline... pero si notan algo extraño como que un elemento asociado de alguna manera a un timeline salta de manera no esperada, intenten esto (por lo menos hasta que el bug sea corregido).

lunes, 16 de marzo de 2009

for... indexof


Este es un detalle muy menor, pero UTIL... típicamente, cuando uno recorre una lista, crea una variable que sirva de puntero y que se incremente en la medida que pasa los elementos de la lista.


var names=["peter","john","james"];

var i=0;
for (name in names) {
println("{name} {i}");
i++;
}

Con JavaFX script esto no es necesario, sólo basta con un:

var names=["peter","john","james"];

for (name in names) {
println("{name} {indexof name}");
}

¿Comó aplicar más de un efecto a un nodo?


Si por ejemplo, se desea hacer difusa una imagen y al mismo tiempo reducir su brillo.

El "truco" está en que casi todos los efectos tienen un INPUT. Eso permite anidar los efectos.


var img = ImageView {
image: Image { url: "{__DIR__}crayons.jpg"},
effect:
GaussianBlur {
input:
ColorAdjust{
brightness: -0.5
}
}
};

Agregando funcionalidad a eventos, sobre la marcha


Mientras desarrollaba el componente de menú flotante, me encontré con esta posibilidad... agregar funcionalidad a un evento... sobre la marcha.

El asunto es este. Si defino un evento onMousePressed de un círculo, ¿como podría agregar funcionalidad a ese evento sin alterar el código y sin crear un nuevo tipo de clase?

Entonces pense... ¿qué tal si primero obtengo la funcionalidad en un evento (onMousePressed) de un objeto y la guardo en una variable? ... Luego, ¿qué tal si reemplazo el evento onMousePressed de ese objeto, con una nueva función en la que primero ejecute la funcion original (guardada en la variable) y luego agrego la nueva funcionalidad?

Bueno, es posible hacerlo...

Para ver una versión Java Web Start, CLICK AQUI

package addfunctions;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.input.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.util.Sequences;

var colors: Color[]=[Color.RED, Color.YELLOW,
Color.BLUE, Color.GREEN];

var mycircle = Circle {
centerX: 80, centerY: 80,
radius: 40
fill: Color.RED
onMousePressed: function(e) {
println("CLICK");
};
}

var mycircle2 = Circle {
centerX: 150, centerY: 150,
radius: 40
fill: Color.RED
onMousePressed: function(e) {
println("CLICK 2");
};
}

function addColorSwitch(node:Node) {
// obtiene la funcion original en el nodo
var origFunc: function(e:MouseEvent):Void=
node.onMousePressed;
// reemplaza evento en el nodo por nuevo evento
node.onMousePressed= function(e:MouseEvent) {
// incluye la funcion original del nodo
origFunc(e);
// agrega funcionalidad
var shape = (node as Circle);
// obtiene color actual en el circulo
var currentColor = shape.fill;
// busca la posicion del color en la secuencia
var pos = Sequences.indexOf(colors, currentColor);
// si es el ultimo colo de la secuencia, vuelve a cero
// si no, al siguiente elemento
if (pos == sizeof colors - 1) then
pos=0 else pos++;
// asigna nuevo color a circulo
shape.fill=colors[pos];
};
};

// agrega funcionalidad a
// onMousePressed en los circulos
addColorSwitch(mycircle);
addColorSwitch(mycircle2);

Stage {
width: 250, height: 250
scene: Scene {
content: [mycircle, mycircle2]
}
}

sábado, 14 de marzo de 2009

MemeFX ... con Popup Menu (experimental)


Estuve haciendo algunos arreglos al experimento del menú flotante que desarrollé hace algún tiempo, y salvo algunos detalles, funciona sin problemas.

Acabo de subir el código de la biblioteca (memeFX), incluyendo este código y un demo, donde se aprecia como usar el menú flotante.

Para ver el demo (Java Web Start), haz CLICK AQUI

Para visitar el sitio web del proyecto, CLICK AQUI

Algo de código:

Primero, se crea un tipo de menú colgante... lo que puede incluir cambios a la apariencia del menú (colores, grosores, rotación, transparencia, esquinas redondeadas, etc.).


var popupCircle = PopupMenu{
corner: 20, padding: 8, borderWidth: 4,
opacity: 0.9, animate: true


content: [
MenuItem { text: "Animar circulo!",
callNode
: node },

MenuItem { text: "Mostrar nombre y posicion",
callPosId: positionId }

]
};

El tipo de función declarada para el Item (la función que ejecuta al ser seleccionado un item del menú), tiene que ver con los parámetros que retorna el menú al ser seleccionada la opción:

call = no retorna parametros, la función es declarada como:

function algo():Void { ... };

callId = retorna el ID del objeto sobre el que se utilizó el menú, la función es declarada como:

function algo(id:String):Void { ... };

callPos = retorna el MouseEvent que originó la aparición del menú. Permite, por ejemplo, obtener la posición del mouse al lanzar el menú (¿qué otros datos se pueden obtener de MouseEvent?). La función es declarada como:

function algo(e:MouseEvent):Void { ... };

callNode = retorna el Nodo sobre el que se utilizó el menú flotante. Permite manipular directamente el objeto (en este ejemplo, se anima el tamaño del círculo seleccionado). Si se quiere utilizar sobre un tipo particular de nodo, es necesario hacer un casting al tipo de objeto apropiado... ejemplo, si el tipo de objeto es un campo de texto: (node as SwingTextField).text="{node.id}" . La función es declarada como:

function algo(node:Node):Void { ... };

callPosId = retorna el MouseEvent que originó la aparición del menu y el ID del objeto sobre el que se utilizó (puede ser útil para saber en que parte del objeto se hizo click). La función es declarada como:

function algo(e:MouseEvent, id:String) { ... };

callPosNode = retorna el MouseEvent que originó la aparición del menu y el Nodo sobre el que se utilizó (puede ser útil para saber en que parte y sobre que objeto se hizo click). La función es declarada como:

function algo(e:MouseEvent, node:Node) { ... };

En este ejemplo agregaré dos círculos:

var circle1 = Circle {
id: "Cicle 1"
centerX: 100, centerY: 100
radius: 40, fill: Color.RED
};

var circle2 = Circle {
id: "Cicle 2"
centerX: 220, centerY: 300
radius: 80, fill: Color.YELLOW
};

Los ID son opcionales, pero pueden ser usados para reconocer sobre que objeto se lanzó el menú flotante (callId y callPosId).

A continuación, enlazo el menú con los círculos:

popupCircle.to(circle1);
popupCircle.to(circle2);

Se agregan las rutinas que serán ejecutadas por las opciones del menú (el código pudo ser incluido al definir el menú colgante).

function node(node:Node):Void {
Timeline {
repeatCount: 4
autoReverse: true
keyFrames: [
at (0s) {node.scaleX => 1.0;
node.scaleY => 1.0 }

at (0.2s) {node.scaleX => 1.4;
node.scaleY => 1.4}

]
}.play();
};

function positionId(e:MouseEvent, id:String):Void {
println("Mouse at {e.x},{e.y} over {id}");
}

Finalmente, se agregan los círculos y los menús al Stage:

Stage {
scene: Scene {
content: [
circle1,
circle2
PopupMenu.activateMenus()
]
}
}

viernes, 13 de marzo de 2009

MemeFX ... mi biblioteca para JavaFX


El nombre puede parecer "curioso", pero tiene una razón "intelectualoide" de ser...

Meme (se pronuncia MIM), responde a toda una teoría sobre la capacidad de las ideas para replicarse y evolucionar por si mismas, como seres vivos. La idea es que el diseño puede surgir del caos (suena a código abierto, ¿no?).

Se supone que a través de un proceso evolutivo y se selección natural, se consigue un resultado superior al original... ahora, como este proyecto es código abierto, y por lo mismo, fácilmente replicable y modificable (puede mutar y evolucionar libremente)... entonces, podría convertirse en un meme.

Veamos si el EFECTO MEME (MemeFX) ocurre con él... ;)

En este momento la biblioteca contiene sólo tres componentes, pero, no por eso no son útiles ;D ... la idea es que con el tiempo vaya creciendo. Tengo algunas ideas como incorporar controles para un menu flotante gráfico (si han mirado el blog, por ahí ya hice un intento), un componente de acordeón, componentes que permitan fácilmente asignar drag and drop a los elementos y definir areas para ser arrojados, un componente para simplificar que los applets sean arrastrables desde el navegador al escritorio (básicamente que genere el área de arrastre solo, porque de por sí es simple habilitar esa funcionalidad), etc. Las ideas son bienvenidas.

Por ahora, aquí está el código fuente de la biblioteca, demos Java Web Start con sus respectivos códigos fuentes y la documentación sobre como utilizarlos y personalizarlos.

Ojalá les sean de utilidad y puedan unirse al proyecto quienes se sientan motivados.

http://code.google.com/p/memefx/

Aquí hay más info sobre los Memes, si alguien tiene curiosidad.

miércoles, 11 de marzo de 2009

Como guardar datos persistentes en una aplicación JavaFX Web Start


Esto permite que incluso una aplicación JWS -sin autorización para acceder a los recursos locales- almacene objetos y datos localmente, utilizando un mecanismo parecido a los "cookies" de los navegadores web.

Para el desarrollo del proyecto, hay que incluir el archivo javaws.jar entre las bibliotecas del proyecto, de manera de poder compilar el código. El archivo javaws.jar es standard en las distribuciones de Java 6 y posteriores, así que no es necesario incluirlo en la distribución de tu aplicación.

Comenzamos incluyendo los import en la aplicación:

import java.io.*;
import java.net.URL;
import javax.jnlp.*;

A continuación declaramos dos variables:

var persistenceService: PersistenceService = null;
var codebase: URL = null;

En el bloque INIT de la aplicación JavaFX, incluimos:

init {
try {
var basicService: BasicService =
ServiceManager.lookup("javax.jnlp.BasicService")
as BasicService;
codebase = basicService.getCodeBase();
persistenceService =
ServiceManager.lookup("javax.jnlp.PersistenceService")
as PersistenceService;
} catch (use:Exception) {
}

};

Eso inicializa codebase y persistenceService.

En este ejemplo, declaro una clase Java que contendrá los datos que deseo almacenar persistentemente en el computador del usuario:

import java.io.Serializable;

class persistentData implements Serializable {

public float x;
public float y;
public float width;
public float height;

persistentData (float x, float y, float width, float height) {
this.x=x;
this.y=y;
this.width=width;
this.height=height;
};
}

Ahora, puedo incluir el código para almacenar en el repositorio local una instancia de mi clase persistentData:


var cache = new persistentData (x, y, width, height);
try {
var fc: FileContents =
persistenceService.get(codebase);
var oos: ObjectOutputStream =
new ObjectOutputStream
(fc.getOutputStream(true));
oos.writeObject( cache );
oos.flush();
oos.close();
} catch (e:Exception) {
}


Y para recuperar el objeto desde el repositorio:

var cache:persistentData;
try {

var appSettings: FileContents = null;
appSettings = persistenceService.get(codebase);
var ois: ObjectInputStream = new ObjectInputStream
( appSettings.getInputStream() );
cache = ois.readObject() as persistentData;
ois.close();
} catch (fnfe: FileNotFoundException ) {
try {
var size = persistenceService.create(codebase, 1024);
} catch (ioe:IOException) {
}
} catch (e:Exception) {
}


Voila!

Nota: el número 1024 en la línea persistenceService.create(codebase, 1024) indica el espacio (en bytes) que la aplicación está solicitando al repositorio para almacenar datos.

Si vas a ejecutar más de una aplicación Java Web Start desde una misma ruta (el mismo directorio en las URLs), es necesario agregar algo al codebase de manera de distinguir a ambas aplicaciones, porque si no, van a "pisarse" los datos almacenados.