Efectos con el texto

Ya se han introducido los objetos de tipo campo de texto (field) en la la sección de nombre Campos de texto (field) en Capítulo 4, así que aquí abordaremos la utilización del texto pero como media: esto es, como soporte de elementos de efecto en la comunicación y como soporte de la información (documentos) y acceso a la misma.

Empecemos por algo sencillo. Este es un pequeño ejercicio práctico para introducir algunas propiedades del texto, como las fuentes de letra, el tamaño y otras propiedades del mismo. Se trata de mostrar el texto que contiene un campo de texto (el mío se llama "campDeText") más o menos centrado en la pantalla y sobre el que se podría montar unos créditos de cierre de una aplicación con un cierto aire de película haciéndolos desplazarse de arriba a abajo.

Bueno, pues simplifiquémoslo más aún: que los contenidos del campo de texto vayan rotando por filas en el campo de texto. Al fin y al cabo, nuestro interés se centra en mostrar la propiedad htmlText de un campo de texto como un posible constructor de apariencias para el texto. Esto es debido y de ahí su sencillez de aprendizaje, por la utilización de las familiares etiquetas de HTML utilizadas (algunas en demasía y fuera de sitio) en las páginas Web.

Bueno, queremos hacer algo similar a la Figura 7-1, en la que se ve un botón y, se intuye, un campo de texto. Todo ello en blanco sobre negro para que no se vea la ruptura con el fondo. Así se verá obligado a tocar alguna propiedad más ...

Figura 7-1. Tratamiento de las propiedades del texto: tamaño y desplazamiento.

Todo el trabajo está en el código del botón, quien cada vez que es pulsado:

#
# Botón "superText"
#
on mouseUp
  # Preparat, que vaig
  set the htmlText of field "campDeText" to empty
  
  # Pega-li la volteta al que hi ha
  set the contingut of field "campDeText" to \
      the last line of the contingut of field "campDeText" & return & \
      line 1 to (the number of lines of the contingut of field "campDeText" -1) of the contingut of field "campDeText"
  
  # Arregla tamanys
  put empty into field "valors"
   lock screen
  repeat with linea = 1 to the number of lines of the contingut of field "campDeText"
    set the htmlText of field "campDeText" to \
        the htmlText of field "campDeText" & \
        "<p><font size=" & quote &  (linea + 8) + 3 & quote &">" & \
        line linea of the contingut of field "campDeText" & \
        "</font></p>" & return
  end repeat
   unlock screen
end mouseUp
                      

Así nuestra aplicación podría ir enviando los mensajes de mouseUp al botón superText que podría estar oculto y además ... Ejercicio, lo acabo de proponer allí, lo siento.

Ya tenemos el primero, veamos otra posibilidad para altererar la apariencia del texto. Siguiendo con la vena cinematográfica vamos a intentar reproducir el efecto de una película un poco más reciente que las que utilizaban el anterior efecto. En este caso ...

Figura 7-2. Tratamiento de las propiedades del texto (2on. part).

#
# La tarjeta
#
global parar, ncols, nfiles

on resizeStack
  set the lockLocation of field "campDeText" to true
  set the width of field "campDeText" to \
        (the width of this card - ( 2 *the left of field "campDeText") )
  set the height of field "campDeText" to \
        (the height of this card - ( 2 *the top of field "campDeText") )
    
  put  round(  5 / 3 *(the height of field "campDeText" / \
                                    (the effective textSize of field "campDeText" +\
                                      the textHeight of field "campDeText"))) into nfiles
  
  put  round ( 5 / 3 *(the effective width of field "campDeText" / \
                                     the effective textSize of field "campDeText") )  into ncols
    
  set the htmlText of field "campDeText" to empty
  put empty into linea
  send "mouseUp" to button "botoRedimensionar" of this card
end resizeStack

#
# fld "campDeText"
#
global parar, ncols, nfiles, volta

on comença
  if parar = true
  then
    put 1 into volta
    exit "comença"
  else
    put volta  + 1 into volta
  end if
  
  # Avançar alguna lletra blanca
  -- En  lineOffset localitze la primera linea i, d'esta, el primer caracter blanc
  -- El patro es algo com
  -- "<font color=" & quote & "#FFFFFF" & quote & "><b>" & CARACTER & "</b></font>"
  -- NO, NO., NO SERVEIX: LA SEGUENT LINEA NO SAPS SI EN TE O NO, ...
  
  
  -- ACABARE LLEVANT-HO QUE EM DIFICULTA FER AVANÇAR LES LLETRES BLANQUES
  put the number of lines of  the htmlText of me into lineaEscollida
  
  lock screen
  set the htmlText of me to \
      line lineaEscollida of the htmlText of me & \
      the line 1 to  (lineaEscollida -1) of the htmlText of me &\
      the line (lineaEscollida+1) to (the number of lines of the htmlText of me) of the htmlText of me
  unlock screen
  if (volta < 20) then send "comença" to me in (1000 - (volta * 50)) milliseconds
end comença
                      

Otro lugar donde el texto puede se de gran utilidad es en la presentación de ideas complejas u opciones en un programa. Vamos a crear un sencillo menú, de forma que al pasar el ratón por encima de las opciones de este, se produzca alguna modificación en la opción implicada. Esta deber ser tal que la haga resaltar sobre las demás y así el usuario reciba la realimentación de qué opción está a punto de escoger.

Figura 7-3. Creación de un menú muy visual sólo con texto.

La interfaz propuesta (Figura 7-3y) está compuesta por un campo de textom en la parte superior, no editable y un grupo de botones que contienen las opciones del índice. En estos está el manejo del texto: cuando el botón recibe el foco (bien por que se ha activado con el teclado o bien por que el ratón pasa por encima de él) aprovechamos para modifcar el tamaño y el color del texto. Sólo muestro el código de uno de los botones por que todos hacen la misma tarea.

#
#Botó Llançar els experiments de la tarjeta inicial
#
on focusIn
  repeat with nControl = 1 to (the number of buttons of the group "menuInici")
    if not( the short name of button nControl is the short name of me ) \
        then send "focusOut" to button nControl of group "menuInici"
  end repeat
  set the textColor of me to green
  set the textSize of me to 34
end focusIn

on focusOut
  set the textColor of me to black
  set the textSize of me to 14
end focusOut

on mouseUp
  go to card (the short name of me)
end mouseUp
            

No podemos aprovechar, en esta aplicación del menú, para concentrar el código en el grupo, puesto que necesitamos saber cuándo entra a un botón para resaltarlo y cuándo sale para restaurar su estado habitual.

Para terminar esta introducción, emprezaremos con un sencillo juego: replicando la estructura de un teletipo. Es, seguramente, la opción más clara para utilizar la orden type, aprovechando el código de ejemplo de la propia orden que se puede encontrar en MetaCard.

Figura 7-4. Enviando texto a un field con la orden type: un momento de la ejecución (izquierda) y final (derecha).

El interfaz puede responder a diseño que muestar la Figura 7-4. La idea central es ofrecer una ayuda visual a la utilización de una aplicación. En el caso actual, y sólo es un ejemplo, cuando se llega a esta tarjeta (se abre la pila o se circula hasta llegar a ella) se le recuerda la utilidad de aquel campo con un mensaje.

Cuando un campo de texto recibe el foco, se hace el objeto activo. Recibe un mensaje openFieldy, si es editable, permitirá las típicas acciones de edición habituales de estos. De manera general (es decir se aplica a cualquier objeto o a la tarjeta actual si no hay ninguno activo) el evento generado es focusIn, este es que recibiría un campo de texto bloquejado con locked fields. Veámoslo sobre un ejemplo del propio MetaCard:

on openField
  send "enterText" to me
end openField


on enterText
  choose browse tool
  focus on fld "Shape"
  
  type "Introduzca la 'contraseña'"
end enterText
                    

Nota: Si ha puesto en marcha el ejemplo, habrá observado que aunque seleccione el campo de texto para teclear algún texto no puede hacerlo. Esto es debido a que el control activo ha habido que cambiarlo al campo de texto que muestra el aviso. Para poder "actuar" en el campo de texto habrá que hacer algo más ...

Si se añade, al final del "enterText" una de las dos líneas comentadas en el código anterior entonces se generará un openField y se volverá a ejecutar este código, que volverá a enviar el foco al campo de texto y volvemos a empezar. Así que puede utilizar la orden lock para evitar que la siguiente orden genere ningún mensaje.

...
  lock messages
  focus on me
...
                    

Por supuesto, la acción que hace type se podría haber hecho igualmente con put y ahí habría cabida para un número mayor de efectos. Pero vamos a dejarlo ahí, de momento, que para el poco código que nos ha costado no está mal y no hemos hecho uso de propiedades como typingRate que permitiría variar el efecto.

Manejo de texto

Como muestra de utilización de ficheros de texto y procesado de los mismos, vamos a realizar un acceso a un fichero que contenga la descripción de una serie de nombres de aplicaciones y su utilidad. Como ejemplo de esto se utilizarán los nombres y la funcionalidad de los programas que componen las utilidades Netpbm (creado a partir del Pbmplus de Jef Poskanzer) que (como dice en su README.gz) incluyen tanto la posibilidad de conversión entre un buen número de formatos gráficos, así como el empleo de unas cuantas operaciones básicas de procesado de imágenes.

El elevado número de estas utilidades, junto a la discontinua utilización de este conjunto de programas, me hace difícil tener siempre presente qué opciones están ya realizadas y si puedo simplemente encadenarlas con tuberías para realizar una acción más compleja. Además la posibilidad de ser utilizado por quien las desconoce aún más que yo me animó a crear un interfaz para encontrar la aplicación que realiza una cierta tarea y obtener la necesaria lista de parámetros con que invocarla.

Figura 7-5. Ejemplo de interfaz en funcionamiento.

Con lo que se propone hacer un interfaz que lliste todas las opciones y que se vaya reduciendo el número de estas conforme se defina la búsqueda del usuario. Decidida una opción se presentará esta con todo detalle. Esta última es la expresión de lo que aparece en las páginas del manual de cada orden. Así que la primera parte es conseguir ese fichero de texto, para ello ...

Aviso

... para ello ...

Y lo siguiente es probar, antes de ir demasiado lejos, así que vamos suponer que ya tenemos un fichero con líneas como:

pbmclean-flip isolated pixels in portable bitmap
pbmlife-apply  Conway's  rules  of  Life to a portable bitmap
pbmmake-create a blank bitmap of a specified size
pbmmask-create a mask bitmap from a regular bitmap
pbmpscale-enlarge a portable bitmap with edge smoothing
pbmreduce-read a portable bitmap and reduce it N times
pbmtext-render text into a bitmap
pbmto10x-convert  a  portable  bitmap  into Gemini 10X printer graphics
pbmto4425-Display PBM images on an AT&T 4425 terminal
pbmtoascii-convert a portable bitmap into ASCII graphics
pbmtoatk-convert  portable  bitmap  to  Andrew Toolkit raster object
pbmtobbnbg-convert a portable bitmap into BitGraph graphics
pbmtocmuwm-convert a portable bitmap into a CMU window manager bitmap
pbmtoepsi-convert a portable bitmap into an encapsulated PostScript style preview bitmap
...
                    

donde en cada línea se tiene la descripción de una utilidad separando su nombre de su descripción por un carácter dado, en nuestro caso el guión. El elemento que hará posible la selección de la utilidad correspondiente es una variación de la que permite buscar una orden o propiedad en el diccionario de MetaTalk. Copiado este elemento (esta subpila), el código tendrá que ser algo parecido a:

#
# la pila 'tutorialNetPGM'
#
on openCard
  if the short name of this card is "inici" \
      then
       send "mouseUp" to btn "carregarFitxerDescripcio"
       put empty into fld "cadenaABuscar" of group "buscar"
 end if

end openCard


on resizeStack
  send  "resizeStack" to group "menuGroup"
  send  "resizeStack" to group "barraDeMensatges"
end resizeStack


#
# button "arregarFitxerDescripcio"
on mouseUp
  hide group "buscar"
  set the loc of image "splash" to the loc of this card
  show image "splash"
  
  if there is a file "netPBM.txt"
  then put url "file:netPBM.txt" into fld "copia-netPBM.txt"
  else put the descripcions of this stack into fld "copia-netPBM.txt"
  
  lock screen
  send ("crear" && 1) to button crearTarjeta
  
  hide image "splash"
  show group "buscar"
end mouseUp


# card "inici"
on openCard
  if the short name of this card is "inici"
  then
    hide group "paginaDeManual"
    show group "buscar"
  else
    --    hide group "buscar"
    show group "paginaDeManual"
  end if
end openCard


#
# button "buscar"
#
cadenaABuscar
on backspaceKey
  delete last char of me
  send "keyUp" to me
--  pass "backspaceKey"
end backspaceKey


on keyUp
  lock screen
  put the descripcions of this stack into fld "resultatsBusqueda"
  filter fld "resultatsBusqueda" with ( "*" & the text of me & "*")
  set the vScrollBar of fld "resultatsBusqueda" to (the formattedHeight of fld "resultat
sBusqueda" > the height of fld "resultatsBusqueda" )
  set the hScrollBar of fld "resultatsBusqueda" to (the formattedWidth of fld "resultats
Busqueda" > the width of fld "resultatsBusqueda" )
  unlock screen
end keyUp

#
# field "resultatsBusqueda"
#
on mouseDoubleUp
  if (the number of cards of this stack > 1) and \
      (there is a card (the number of cards of this stack - \
                                      (lineOffset(the selectedText, \
                                       fld "copia-netPBM.txt" of card "Inici") - 1)))
  then
    put the first item of the selectedText
    go to card  (the number of cards of this stack -\
                         (lineOffset(the selectedText, fld "copia-netPBM.txt" of card "Inici") -1))
  else
    answer error \
       "Recarrega la configuració des de el menu 'Fitxer' i reintenta esta operació" \
        titled "[" & the short name of me & "]"
  end if
end mouseDoubleUp

on returnInField --enterInField
  send "mouseDoubleUp" to me
end returnInField --enterInField



#
# button "crearTarjeta"
#
on crear i
  if i > the number of lines of fld "copia-NetPBM.txt"  of card "inici"
  then
    unlock screen
  else
    set itemDelimiter to "-"
    put the first item of (line i of fld "copia-netPBM.txt" of card "inici") into \
            fld "textBarraDeMensatges"
    create card
    put the first item of (line i of fld "copia-netPBM.txt" of card "inici") into fld "NAME"
    put the second item of (line i of fld "copia-netPBM.txt" of card "inici") into\
            fld "SYNOPSIS" of group "paginaDeManual"
    set the script of this card to the script of card "inici"
    go to card "Inici"
    send ("crear" && i + 1) to me --in 10 milliseconds
  end if
end crear
                       

Buscando en el texto

Este punto sirve para demostrar l'ús de les funcions de búsqueda en el texto. Además, hay que verlo como un paso previo al tratamiento de ficheros XML, desde MetaCard

La situación que vamos a abordar primero viene dada por aprovechar la escritura de la ayuda de una aplicación que realizaemos en un formato que haga posible su fácil conversión a cuantos formatos sea de interés ... En este caso hemos elegido HTML, del cual aprovecharemos su vertiente de estructurador de contenidos para localizar las partes que componen el documento y crear de manera dinámica un índice del documento (la ayuda en este caso).

Un documento HTML se puede ver como un árbol, en nuestro caso formado por apartados que vienen determinados por la aparición de ciertas etiquetas. Vamos a recorrer el archivo buscando etiquetas "h2" para crear de forma dinámica , en tiempo de ejecución, un índice de las secciones que forma la ajuda.

Figura 7-6. Ejemplo de recorrido de un fichero HTML para crear un índice de acceso a los apartados.

#
# Pila
#
on openStack
  put url ("file:" & the mainStack of this stack & ".html") into fitxerAjuda
  if matchText( fitxerAjuda, ".<head>.<title>(.*)</title>.</head>.", varAux2 )
  then
    set the title of this stack to varAux2
  else
    set the title of this stack to (the mainStack of this stack & ".html")
  end if

  if matchText( fitxerAjuda, ".<body>(.*)</body>.", varAux2 )
  then
    set the htmlText of field "contingutAjuda" to varAux2
    if (the formattedHeight of field "contingutAjuda" > the height of field "contingutAjuda") \
        then
      set the vScrollBar of field "contingutAjuda" to true
    end if
    send "carregarItems" to this card of this stack
  else
    answer error "Error al carregar el fitxer d'ajuda: ajudaPpal.html"
  end if
end openStack

on closeStack
  set the vScollBar of field "contingutAjuda" to false
end closeStack
                    

Y cuando ya es posible hablar de un fichero sólo queda ir buscando el inicio de las secciones que hemos decido marcan las etiquetas "h2" del fichero HTML.

#
# Tarjeta
#
on carregarItems
  put url ("file:" & "ajudaPpal.html") into fitxerAjuda
  put empty into button "buscaText"
  repeat while matchText( fitxerAjuda, ".[^<]*<h2>(.[^<]*)</h2>(.*)", itemH2, restoFitxer )
    put (itemH2 & return) after button "buscaText"
    put restoFitxer into fitxerAjuda
  end repeat
end carregarItems
                    

Para ir a cada uno de estos apartados, el botón desplegable que guarda el índice de las secciones del fichero de ayuda calculará el incremento que tiene que mover la barra de desplazamiento para situarse en el punto correspondiente, por ejemplo, de esta forma:

#
# Botón "buscaText"
#
on menuPick quin
  set the scroll of field "contingutAjuda" to \
      lineOffset( quin, the text of field "contingutAjuda") * the textHeight of field "contingutAjuda"
end menuPick
                    

Figura 7-7. Ejemplo de recorrido de un fichero XML sencillo

Era antes XML

La descripción que corresponda.

Examinaremos en este apartado la utilización de dos objetos desarrollados para el uso de esta tecnología como son XMLLibrary .

Figura 7-8. XML Library

Y Altuit XML TreeView List Control.

Figura 7-9. XML Library