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).