Autor Tema: Libreria grafica con widgets  (Leído 3792 veces)

0 Usuarios y 2 Visitantes están viendo este tema.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Libreria grafica con widgets
« en: 29 de Diciembre de 2011, 22:00:41 »
Hola a todos,

Hace unos meses hice en C una liberia grafica que incorporaba widgets. Los widgets son elementos de la interfaz grafica que pueden interactuar con el usuario. Los ejemplos tipicos pueden ser: botones, sliders, barras de progreso... Ahora quiero reescribir esa libreria para mejorarla, y necesito vuestra colaboración para revisar y mejorar mi trabajo. Estas son dos fotos de la version anterior sobre un TFT de 320*240:

El funcionamiento de las librerías de este tipo es siempre el mismo y se puede dividir en 4 pasos:
 1 Se definen los parámetros de cada widget ( tipo, posición, tamaño, CallBack/s...)
 2 Se crea un árbol con toda la estructura de los widgets.
 3 Se envían eventos al árbol de widgets, con la acción realizada por el usuario como( se presionó el TouchScreen en el punto x=25 e y=100 )
 4 Los eventos recorren el árbol hasta llegar al widget correspondiente. Este procesa el evento para cambiar su estado, su representación en pantalla o ejecutar una acción definida por el usuario mediante callbacks.
Los pasos 1 y 2 se suelen realizar en tiempo de compilacion, y si es posible antes, desde un GUI builder.  

La parte mas importante de esta libreria es la estructura generica sWidget. Esta define por completo un widget generico y por tanto debe contener al menos posicion, tamaño, y tipo y punteros a otros widgets para crear un arbol.... De esta estructura de widget generica derivan el resto de widgets como boton, textbox, slider...cada uno añadiendo los parametros que necesite como callbacks o un valor numerico.
Código: C
  1. struct sWidget{
  2.         struct sWidget *pParent, *pChild, *pPrev, *pNext;
  3.         uint16_t x, y, w, h;
  4.         enum eWidgetType ID;
  5. };
  6.  
  7. struct sWidgetButton{
  8.         struct sWidget wgdt;
  9.         uint8_t state;
  10.         struct sFont *pFont;
  11.         char *pText;
  12.         void (*pCBPress)( struct sWidgetButton *pButton );
  13. };

En tiempo de compilacion, a mano, o con un GUI builder, se define el arbol que contendrá a todos los widges ya ordenados.

Código: C
  1. struct sWidgetButton sBtnLedON;
  2. struct sWidgetButton sBtnLedOFF;
  3.  
  4. struct sWidgetButton sBtnLedON = {
  5.         {
  6.                 &sBtnLedOFF, NULL,
  7.                 10, 10, 20, 20,
  8.                 WIDGET_TYPE_BUTTON
  9.         },
  10.         0,
  11.         &sFontArial_12,
  12.         "ON",
  13.         &TurnOn
  14. };
  15.  
  16.  
  17. struct sWidgetButton sBtnLedOF = {
  18.         {
  19.                 NULL, NULL,
  20.                 40, 10, 20, 20,
  21.                 WIDGET_TYPE_BUTTON
  22.         },
  23.         0,
  24.         &sFontArial_12,
  25.         "OFF",
  26.         &TurnOff
  27. };

Cuando el usuario realiza una accion se genera el evento correspondiente y se envia al arbol de widgets.

Código: C
  1. enum eEventSource{      EVENT_SRC_TOUCH = 0,    EVENT_SRC_UI,   EVENT_SRC_MAX};
  2. enum eEventType{        EVENT_TP_PRESS = 0,             EVENT_TP_HOLD,  EVENT_TP_RELEASE,       EVENT_TP_REDRAW,        EVENT_TP_MAX};
  3.  
  4. struct sWidgetEvent{
  5.         enum eEventSource source;
  6.         enum eEventType type;
  7.         int16_t x, y;
  8. };
  9.  
  10. void main( void ){     
  11.         uint8_t press, pressOld;
  12.         struct sEvent event;
  13.         struct sWidget *pWidgetTree = (struct sWidget*)&sBtnLedON;
  14.        
  15.         while(1){
  16.                 pressOld = press;
  17.                 press = TouchIsPressed();
  18.                
  19.                 if( !pressOld && press ){
  20.                         event.src = EVENT_SRC_TOUCH;
  21.                         event.type = EVENT_TP_PRESS;
  22.                         event.data.x = TouchGetX();
  23.                         event.data.y = TouchGetY();
  24.                         WidgetEvent( pWidgetTree, &event );
  25.                 }
  26.                 if( pressOld && !press ){
  27.                         event.src = EVENT_SRC_TOUCH;
  28.                         event.type = EVENT_TP_RELEASE;
  29.                         WidgetEvent( pWidgetTree, &event );            
  30.                 }
  31.                 delayMs( 100 );
  32.         }
  33. }

Los eventos recorren el arbol para actuar sobre los widgets. En este caso se utiliza una funcion recursiva para recorrer el arbol:

Código: C
  1. void WidgetEvent( struct sWidget *pWidget, const struct sEvent *pEvent ){
  2.         switch( pWidget->ID ){
  3.                 case WIDGET_ID_TEXT:    WidgetTextEvent( pWidget, pEvent );             break;
  4.                 case WIDGET_ID_BUTTON:  WidgetButtonEvent( pWidget, pEvent );   break;
  5.                 default:        break;
  6.         }
  7.        
  8.         if( NULL != pWidget->pChild ){
  9.                 WidgetEvent( pWidget->pChild, pEvent );
  10.         }
  11.        
  12.         if( NULL != pWidget->pNext ){
  13.                 WidgetEvent( pWidget->pNext, pEvent );
  14.         }
  15. }

Los widgets se procesan en una función distinta según el tipo. Esta función, dependiendo del evento, cambia el estado del widget, lo dibuja en pantalla, ejecuta un callback...

Código: C
  1. void TurnOn( struct sWidgetButton *pButton ){
  2.         PinWrite( &sPinLed, PIN_HIGH );
  3. }
  4. void TurnOff( struct sWidgetButton *pButton ){
  5.         PinWrite( &sPinLed, PIN_LOW );
  6. }
  7.  
  8.  
  9. void WidgetButtonEvent( struct sWidgetButton *pButton, const struct sEvent *pEvent ){
  10.  
  11.         switch( pEvent->source ){
  12.                 case EVENT_SRC_TOUCH:
  13.                         switch( pEvent->type ){
  14.                                 case EVENT_TP_PRESS:    
  15.                                         if( Inside() ){ //#define Inside()      ((widget.x <= event.x && event.x <= widget.x + widget.w) && (widget.y <= event.y && event.y <= widget.y + widget.h))
  16.                                                 // Comprueba si el boton esta apretado, lo aprieta, lo repinta y ejecuta la funcion definida por el usuario.
  17.                                                 if( !WidgetFlagTest( &pButton->widget, WIDGET_FL_PRESSED ) ){
  18.                                                         WidgetFlagSet( &pButton->widget, WIDGET_FL_PRESSED );
  19.                                                         WidgetButtonEvent( pButton, &(const struct sEvent){EVENT_SRC_UI, EVENT_TP_DRAW, 0, 0} );
  20.                                                         if( NULL != pButton->pCBPress ){
  21.                                                                 pButton->pCBPress( pButton );
  22.                                                         }
  23.                                                 }
  24.                                         }
  25.                                         break;
  26.                                 case EVENT_TP_RELEASE:  
  27.                                         WidgetFlagClear( &pButton->widget, WIDGET_FL_PRESSED );
  28.                                         break;
  29.  
  30.                                 default:
  31.                                         break;
  32.                         }
  33.                         break;
  34.                 case EVENT_SRC_UI:
  35.                                 // Borramos y pintamos el widget en la pantalla segun si esta presionado, si tiene texto o una imagen.
  36.                                 GLCDRect( pWidget->pGLCD,
  37.                                         pButton->widget.x,
  38.                                         pButton->widget.y,
  39.                                         pButton->widget.x + pButton->widget.w,
  40.                                         pButton->widget.y + pButton->widget.h,
  41.                                         GLCD_FILL, GLCD_COLOR_WHITE );
  42.                                 GLCDRect( pButton->widget.pGLCD,
  43.                                         pButton->widget.x,
  44.                                         pButton->widget.y,
  45.                                         pButton->widget.x + pButton->widget.w,
  46.                                         pButton->widget.y + pButton->widget.h,
  47.                                         GLCD_FILL, GLCD_COLOR_BLACK );                         
  48.                                 if( WidgetFlagTest( &pButton->widget, WIDGET_FL_PRESSED ) ){
  49.                                         GLCDRect( pButton->widget.pGLCD,
  50.                                                 pButton->widget.x,
  51.                                                 pButton->widget.y,
  52.                                                 pButton->widget.x + pButton->widget.w,
  53.                                                 pButton->widget.y + pButton->widget.h,
  54.                                                 GLCD_FILL, GLCD_COLOR_BLACK );
  55.                                 }
  56.                                 if( NULL != pButton->pFont && NULL != pButton->pStr ){
  57.                                         GLCDText( pButton->widget.pGLCD, pButton->pFont, (char*)pButton->pStr,
  58.                                                 pButton->widget.x + pButton->widget.w/2 - GLCDTextWidth( pButton->pFont, pButton->pStr )/2,
  59.                                                 pButton->widget.y + pButton->widget.h/2 - pButton->pFont->height/2,
  60.                                                 GLCD_NO_INVERT, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  61.                                 }else if( NULL != pButton->pBMP ){
  62.                                         GLCDDrawBMP( pButton->widget.pGLCD, pButton->pBMP,
  63.                                                 pButton->widget.x + pButton->widget.w - pButton->pBMP->width/2,
  64.                                                 pButton->widget.y + pButton->widget.h - pButton->pBMP->height/2 );
  65.                                 }
  66.                         break;
  67.                 default:
  68.                         break;
  69.  
  70.                
  71.         }
  72.         return;
  73. }

y con esto tenemos toda la estructura de la librería. Después es cuestión de añadir mas tipos de widgets y/o más tipos de eventos.

Si queremos realizar interfaces graficas con muchos elementos se hace muy recomendable la utilizacion de un GUI builder para generar todos los widgets. Algunas librerias ya traen el suyo propio, pero no todas, asi que tambien esta bien saber como se puede hacer uno. Netbeans y Microsoft Visual Studio tienen GUI builders muy completos para crear UI para PCs. Estos se pueden aprobechar de varias maneras:
 1 Podemos construir la interfaz, capturar la pantalla y despues tomar medidas de pixeles con el paint... un poco cutre.
 2 Podemos copiar el propio codigo que generan estos GUI builder, pero en el caso de Netbeans esta en java, y en el caso de MVS los widgets son mucho mas complejos y no "casan" bien con los de esta libreria.
 3 Crear un codigo que en tiempo de ejecucion lea de manera recursiva todos los widgets y escriba en un archivo todos sus parametros. Despues este archivo se compila como un .c mas.
Código: C
  1. private void Form1_Load(object sender, EventArgs e){
  2.         int i;
  3.  
  4.         fUIsrc = new System.IO.StreamWriter(".\\UIsrc.txt");
  5.  
  6.         for (i = 0; i < this.Controls.Count; i++){
  7.                
  8.                 if (i - 1 >= 0){
  9.                         StrParent = "&" + this.Controls[i - 1].Name + ".widget";
  10.                 }else{
  11.                         StrParent = "NULL";
  12.                 }
  13.                 if (i + 1 < this.Controls.Count){
  14.                         StrChild = "&" + this.Controls[i + 1].Name + ".widget";
  15.                 }else{
  16.                         StrChild = "NULL";
  17.                 }
  18.                
  19.                 if (pnl.Controls[i] is Button){
  20.                         Button cntrl = (Button)pnl.Controls[i];
  21.                         fUIsrc.WriteLine("struct sWidgetButton {0} = {{", cntrl.Name);
  22.                         fUIsrc.WriteLine("      {");
  23.                         fUIsrc.WriteLine("              WIDGET_ID_BUTTON,");
  24.                         fUIsrc.WriteLine("              WIDGET_FL_BOX,");
  25.                         fUIsrc.WriteLine("              0,");
  26.                         fUIsrc.WriteLine("              {{ {0}, {1}, {2}, {3} }},", x + cntrl.Left, y + cntrl.Top, cntrl.Width, cntrl.Height);
  27.                         fUIsrc.WriteLine("              &myGLCD,");
  28.                         fUIsrc.WriteLine("              {0}, {1}, {2}, {3}", StrParent, StrChild );
  29.                         fUIsrc.WriteLine("      },");
  30.                         fUIsrc.WriteLine("      &{0},", cntrl.Font.Name);
  31.                         fUIsrc.WriteLine("      \"{0}\",", cntrl.Text);
  32.                         fUIsrc.WriteLine("      NULL,");
  33.                         fUIsrc.WriteLine("      NULL/*{0}CallBackPress*/,", cntrl.Name);
  34.                         fUIsrc.WriteLine("};");
  35.                 }
  36.         }
  37.  
  38.         fUIsrc.Close();
  39.  
  40.         return;
  41. }

Despues de todo este codigo, y bastante mas, asi queda una UI creada con el Microsoft Visual Studio 2008 en C# y ejecutada en un PIC24+GLCD+KEYB.

Por ultimo comentarios que ya existen unas cuantas librerías de este tipo, pero yo creo que puede ser interesante hacerse unas propias, aunque sean mas básicas y rudimentarias. Ejemplos:
« Última modificación: 30 de Enero de 2012, 20:36:31 por jgpeiro06 »

Desconectado Vtronic

  • PIC16
  • ***
  • Mensajes: 121
Re: Libreria grafica con widgets
« Respuesta #1 en: 29 de Diciembre de 2011, 23:44:36 »
Me impresiono mucho tu librería, Yo estoy trabajando en una pero todavía esta bien cruda, que compilador estas usando? Yo estoy usando C30, ya tengo la librería para el texto (una modificación ya que la original estaba hecha para ccs)y para los iconos.

Saludos.

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Libreria grafica con widgets
« Respuesta #2 en: 30 de Diciembre de 2011, 04:18:07 »
Como siempre, tus trabajos son para quedarse boquiabierto. Me suscribo al hilo. Gracias por compartirlo

Desconectado MGLSOFT

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 7912
Re: Libreria grafica con widgets
« Respuesta #3 en: 30 de Diciembre de 2011, 08:50:22 »
Siempre igual, por suerte, ya que tus trabajos son magníficos!!
Suscrito!!
Todos los dias aprendo algo nuevo, el ultimo día de mi vida aprenderé a morir....
Mi Abuelo.

Desconectado Suky

  • Moderador Local
  • DsPIC33
  • *****
  • Mensajes: 6758
Re: Libreria grafica con widgets
« Respuesta #4 en: 30 de Diciembre de 2011, 11:28:13 »
Me interesaría participar, por el trabajo mucho tiempo no dispongo pero tengo lo necesario como para probar interfaces en un PIC32 con pantalla 320x240.


Saludos!
No contesto mensajes privados, las consultas en el foro

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Libreria grafica con widgets
« Respuesta #5 en: 31 de Diciembre de 2011, 09:25:40 »
Citar
...para quedarse boquiabierto...magníficos...
jejeje, gracias...

Citar
Que compilador estas usando?
De momento estoy usando el compilador C30.

Citar
dispongo pero tengo lo necesario
Te agradecería que cuando tengas tiempo pruebala y comenta aquí como se podría mejorar...

Ahora estoy liado con un problema sobre el árbol de widgets. Este árbol se recorre con un algoritmo recursivo para llevar los eventos a todos los nodos. Un árbol tiene esta forma:
Código: C
  1. a
  2.     b
  3.         f
  4.         g
  5.         h
  6.     c
  7.         i
  8.         j
  9.     d
  10.         k
  11.     e

Donde:
a es la raiz.
a,b,c,d,e son contenedores.
j,g,h,i,j,k son widgets.

Para refrescar la pantalla,  se puede recorrer el nodo recursivamente en preorden y se pintarian en el siguiente orden: a,b,f,g,h,c,i,j,d,k,e. Esto es perfecto ya que pintaria un contenedor y despues todos sus widgets encima... ¿pero que pasa si un contenedor se solapa con otro? pues que siempre seria visible el que este mas abajo en el arbol, ya que se pinta despues.
Esto nos obliga a cambiar el orden en el que recorremos los widgets, dejando para el final el contenedor seleccionado, o a cambiar la posicion en el arbol del widget seleccionado, dejandolo al final.

He escrito un par de rutinas para añadir, mover y quitar nodos del árbol... ¿pero que pasa si estoy recorriendo un árbol recursivamente y muevo los nodos de lugar?...

Código: C
  1. void addWidget( struct sWidget *pWidget, struct sWidget *pNew ){
  2.         struct sWidget *pTmp;
  3.         if( NULL == pWidget->child ){
  4.                 pWidget->pChild = pNew;
  5.                 for( pTmp = pNew ; pTmp != NULL ; pTmp = pTmp->pNext ){
  6.                         pTmp->pParent = pWidget;
  7.                 }
  8.                
  9.         }else{
  10.                 for( pTmp = pWidget->child ; pTmp->pNext != NULL ; pTmp = pTmp->pNext );
  11.                 pTmp->pNext = pNew;
  12.                 pNew->pPrev = pTmp;
  13.                 for( pTmp = pNew ; pTmp != NULL ; pTmp = pTmp->pNext ){
  14.                         pTmp->pParent = pWidget;
  15.                 }              
  16.         }
  17.         return;
  18. }
  19.  
  20. void delWidget( struct sWidget *pWidget ){
  21.         if( NULL != pWidget->pPrev )
  22.                 pWidget->pPrev->pNext = pWidget->pNext;
  23.         if( NULL != pWidget->pNext )
  24.                 pWidget->pNext->pPrev = pWidget->pPrev;
  25.         return;
  26. }
  27.  
  28. void moveWidget( struct sWidget *pDest, struct sWidget *pWidget ){
  29.         delWidget( pWidget );
  30.         addWidget( pDest, pWidget );
  31.         return;
  32. }


También tengo dudas en como saber a que contenedor se debe enviar un evento en caso de que se solapen los dos contenedores.  En principio el usuario quiere interactuar con el widget que esta pintado en la pantalla, que es el ultimo del arbol, ¿pero como sabe un widget que no debe hacer caso al evento porque hay otro widget detrás de él que si lo  hará?....
Esto me hace pensar que cada nodo debe tener una variable que sea "seleccionado" y que solo se envien eventos a los widgets que la tengan a 1. Pero entonces, ¿como cambiamos de widget?
Entonces el evento de los widgets podria quedar así:

Código: C
  1. void WidgetEvent( struct sWidget *pWidget, const struct sEvent *pEvent ){
  2.         if( WidgetTestFlag( pWidget, WIDGET_FL_SELECTED ){
  3.                 switch( pWidget->ID ){
  4.                         case WIDGET_ID_TEXT:    WidgetTextEvent( pWidget, pEvent );             break;
  5.                         case WIDGET_ID_BUTTON:  WidgetButtonEvent( pWidget, pEvent );   break;
  6.                         default:        break;
  7.                 }
  8.        
  9.                 if( NULL != pWidget->pChild ){
  10.                         WidgetEvent( pWidget->pChild, pEvent );
  11.                 }
  12.         }else if( isInside() ){
  13.                 WidgetFlagsClearRecursive( pTop, WIDGET_FL_SELECTED );
  14.                 WidgetFlagSet( pWidget, WIDGET_FL_SELECTED );
  15.         }
  16.         if( NULL != pWidget->pNext ){
  17.                 WidgetEvent( pWidget->pNext, pEvent );
  18.         }
  19. }

bueno, todo esto no me cuadra demasiado, me parecia mas sencilla la implementacion de WidgetEvent que di en el post anterior...En fin, creo que esto es mucho trabajo para un solo hombre, así que necesito vuestros puntos de vista!!!

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Libreria grafica con widgets
« Respuesta #6 en: 31 de Diciembre de 2011, 14:22:34 »
Ciertamente es interesante el problema que planteas, y habría que pensar en distintas soluciones, pero no alcanzo a comprender en qué casos habría dos widgets sensibles al usuario ocupando el mismo espacio de la interface. Cada vez que lo pienso se me vienen a la cabeza botones solapados y no lo entiendo.
En caso de no ser viable, ¿no sería mejor una regla de validación en el GUI builder que impida este solapamiento?

Desconectado Geo

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 922
    • Mexchip
Re: Libreria grafica con widgets
« Respuesta #7 en: 31 de Diciembre de 2011, 17:48:42 »
Yo no he diseñado GUI ni en PC, mucho menos en microcontroladores, pero si he utilizado en PC :). Así que, sabiendo esto, me atrevo a opinar :).

¿Por qué manejas una jerarquía de árbol de contenedores? Creo que con un solo contenedor sería suficiente, ordenas la lista de widgets de acuerdo al orden en que los quieres dibujar, así es más fácil mandar un widget por sobre todos los demás.

La última biblioteca GUI que estoy usando se llama guichan, en esta cada widget puede tener o no el "foco", cuando el usuario interactúa con el widget lo primero que hace es ganar el foco y quitárselo a los demás, de acuerdo a si tiene o no el foco, es como atiende o no los eventos del usuario.
La imaginación es el límite.
Visita mi blog, en inglés o en español :).
Mini curso de introducción a VHDL en MEXCHIP :-/

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Libreria grafica con widgets
« Respuesta #8 en: 01 de Enero de 2012, 08:54:19 »
Citar
...pero no alcanzo a comprender en qué casos habría dos widgets sensibles al usuario ocupando el mismo espacio...
en ningún caso. Los dos sensibles no. Pero si puede haber uno en una ventana seleccionada(sensible) sobre otro en una ventana posterior(no sensible).

Citar
¿Por qué manejas una jerarquía de árbol de contenedores?
Bueno, mi idea era poder agrupar los widgets dentro de un contenedor y así poder manejarlos como si en una ventana.

Creo que me estoy haciendo un lío yo solo y mezclando dos conceptos distintos: "widgets" y "window manager". Voy a dejar de lado el tema de las ventanas e intentar hacer la librería como si cada grupo de widgets esta en una "screen" distinta. Después, cuando esto este bien terminado, intentaré añadir lo de las ventanas en una capa superior...

Por cierto, Feliz 2012!

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Libreria grafica con widgets
« Respuesta #9 en: 03 de Enero de 2012, 08:18:04 »
bueno, al final parece que esto va tomando forma. Despues de darle mil vueltas y hacer media docena de implementaciones, creo que ya va saliendo... He optado por recorrer los elementos de dos maneras distintas dependiendo del evento. Si es de tipo DRAW se recorren todos los elementos en un orden, si es de otro tipose recorren en orden inverso y cuando uno "acepta" el evento ya no se recorren mas elementos.


Código: C
  1. uint8_t WidgetEvent( struct sWidget *pWidget, struct sEvent *pEvent ){
  2.         if( WIDGET_SRC_UI == pEvent->src ){
  3.                 WidgetEventID( pWidget, pEvent );
  4.        
  5.                 if( NULL != pWidget->pChild ){
  6.                         WidgetEvent( pWidget->pChild, pEvent );
  7.                 }
  8.                 if( NULL != pWidget->pNext ){
  9.                         WidgetEvent( pWidget->pNext, pEvent );
  10.                 }
  11.                 return 1;
  12.         }else{
  13.                 if( NULL != pWidget->pNext ){
  14.                         if( WidgetEvent( pWidget->pNext, pEvent ) ){
  15.                                 return 1;
  16.                         }
  17.                 }
  18.                 if( NULL != pWidget->pChild ){
  19.                         if( WidgetEvent( pWidget->pChild, pEvent ) ){
  20.                                 return 1;
  21.                         }
  22.                 }
  23.                
  24.                 if( WidgetEventID( pWidget, pEvent ) ){
  25.                         return 1;
  26.                 }      
  27.         }
  28.         return 0;
  29. }
  30.  
  31. uint8_t WidgetEventID( struct sWidget *pWidget, struct sEvent *pEvent ){
  32.         switch( pWidget->ID ){
  33.                 case WIDGET_ID_CONTAINER:       return WidgetEventContainer( pWidget, pEvent );
  34.                 case WIDGET_ID_TEXT:            return WidgetEventText( pWidget, pEvent );
  35.                 case WIDGET_ID_BUTTON:          return WidgetEventButton( pWidget, pEvent );
  36.                 default: return 0;
  37.         }
  38.         return 0;
  39. }
  40.  
  41. uint8_t WidgetEventContainer( struct sWidget *pWidget, struct sEvent *pEvent ){
  42.         if( WIDGET_SRC_UI == pEvent->src && WIDGET_TP_DRAW == pEvent->type ){
  43.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_FILL, GLCD_COLOR_WHITE );
  44.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  45.                 return 1;
  46.         }else if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_RELEASE == pEvent->type && EVENT_DATA_A == pEvent->data1 ){
  47.                 // Next Widget
  48.                 return 1;
  49.         }else if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_HOLD == pEvent->type && EVENT_DATA_A == pEvent->data1 ){
  50.                 // Next Screen
  51.                 return 1;
  52.         }else if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_RELEASE == pEvent->type && EVENT_DATA_C == pEvent->data1 ){
  53.                 // Prev Widget
  54.                 return 1;
  55.         }else if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_HOLD == pEvent->type && EVENT_DATA_C == pEvent->data1 ){
  56.                 // Prev Screen
  57.                 return 1;
  58.         }else if( WIDGET_SRC_TOUCH == pEvent->src && WIDGET_TP_PRESS == pEvent->type ){
  59.                 struct sPoint point = { pEvent->data1, pEvent->data2 };
  60.                 if( isInside( pWidget->rect, point ) ){
  61.                         // FocusGain();
  62.                         return 1;
  63.                 }else{
  64.                         return 0;
  65.                 }
  66.         }
  67.         return 0;
  68. }
  69.  
  70. uint8_t WidgetEventText( struct sWidget *pWidget, struct sEvent *pEvent ){
  71.         if( WIDGET_SRC_UI == pEvent->src && WIDGET_TP_DRAW == pEvent->type ){
  72.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_FILL, GLCD_COLOR_WHITE );
  73.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  74.                 GLCDsText( pWidget->pGLCD, pWidget->rect, pWidget->pfont, pWidget->str, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  75.                 return 1;
  76.         }
  77.         return 0;
  78. }
  79.  
  80. uint8_t WidgetEventButton( struct sWidget *pWidget, struct sEvent *pEvent ){
  81.         if( WIDGET_SRC_UI == pEvent->src && WIDGET_TP_DRAW == pEvent->type ){
  82.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_FILL, GLCD_COLOR_WHITE );
  83.                 GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  84.                 if( WidgetFlagTest( pWidget, WIDGET_FLAG_PRESSED ) ){
  85.                         GLCDsRect( pWidget->pGLCD, pWidget->rect, GLCD_FILL, GLCD_COLOR_BLACK );
  86.                 }
  87.                 GLCDsImage( pWidget->pGLCD, pWidget->rect, pWidget->pBmp );    
  88.                 GLCDsText( pWidget->pGLCD, pWidget->rect, pWidget->pfont, pWidget->str, GLCD_NO_FILL, GLCD_COLOR_BLACK );
  89.                 return 1;
  90.         }
  91.  
  92.         if( WidgetFlagTest( pWidget->state, WIDGET_FLAG_PRESSED ) ){
  93.                 if( WIDGET_SRC_TOUCH == pEvent->src && WIDGET_TP_PRESS == pEvent->type ){
  94.                         struct sPoint point = { pEvent->data1, pEvent->data2 };
  95.                         if( isInside( pWidget->rect, point ) ){
  96.                                 // FocusGain();
  97.                                 WidgetFlagClear( pWidget->state, WIDGET_FLAG_PRESSED );
  98.                                 WidgetEventButton( pWidget, WIDGET_EVENT_DRAW );
  99.                                 return 1;
  100.                         }else{
  101.                                 return 0;
  102.                         }
  103.                 }
  104.                
  105.                 if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_RELEASE == pEvent->type && EVENT_DATA_B == pEvent->data1 ){
  106.                         WidgetFlagClear( pWidget->state, WIDGET_FLAG_PRESSED );
  107.                         WidgetEventButton( pWidget, WIDGET_EVENT_DRAW );
  108.                         return 1;
  109.                 }
  110.         }else{
  111.                 if( WIDGET_SRC_TOUCH == pEvent->src && WIDGET_TP_PRESS == pEvent->type ){
  112.                         struct sPoint point = { pEvent->data1, pEvent->data2 };
  113.                         if( isInside( pWidget->rect, point ) ){
  114.                                 // FocusGain();
  115.                                 WidgetFlagSet( pWidget->state, WIDGET_FLAG_PRESSED );
  116.                                 WidgetEventButton( pWidget, WIDGET_EVENT_DRAW );
  117.                                 if( NULL != pButton->pfCallBack ){
  118.                                         pButton->pfCallBack( pWidget );
  119.                                 }
  120.  
  121.                                 return 1;
  122.                         }else{
  123.                                 return 0;
  124.                         }
  125.                 }
  126.  
  127.                 if( WIDGET_SRC_KEYB == pEvent->src && WIDGET_TP_PRESS == pEvent->type && EVENT_DATA_B == pEvent->data1 ){
  128.                         WidgetFlagSet( pWidget->state, WIDGET_FLAG_PRESSED );
  129.                         WidgetEventButton( pWidget, WIDGET_EVENT_DRAW );
  130.                         if( NULL != pButton->pfCallBack ){
  131.                                 pButton->pfCallBack( pWidget );
  132.                         }
  133.                         return 1;
  134.                 }
  135.         }
  136.         return 0;
  137. }

Cuando un elemento gana el foco, se pone el primero de la lista, así sera el primero en recibir el evento y si lo acepta ya no se propagará.
El codigo para añadir, mover y quitar elementos seria algo asi:
Código: C
  1. void addNext( struct sNode *pNode, struct sNode *pNew ){
  2.         struct sNode *pTmp;
  3.         for( pTmp = pNode ; pTmp->pNext != NULL ; pTmp = pTmp->pNext )
  4.                 ;
  5.         pTmp->pNext = pNew;
  6. }
  7.  
  8. void addChild( struct sNode *pNode, struct sNode *pNew ){
  9.         if( NULL == pNode->pChild ){
  10.                 pNode->pChild = pNew;
  11.         }else{
  12.                 addNext( pNode->pChild, pNew );
  13.         }
  14.         printf( "added %c to %c\n", pNew->name, pNode->name );
  15. }
  16.  
  17. void delNext(struct sNode *pNode, struct sNode *pOld ){
  18.         struct sNode *pTmp;
  19.         for( pTmp = pNode ; pOld != pTmp->pNext && NULL != pTmp ; pTmp = pTmp->pNext )
  20.                 ;
  21.         if( NULL == pTmp )
  22.                 return;
  23.         pTmp->pNext = pOld->pNext;
  24. }
  25. void delChild(struct sNode *pNode, struct sNode *pOld ){
  26.         if( NULL != pNode->pChild ){
  27.                 if( pOld == pNode->pChild ){
  28.                         pNode->pChild = pOld->pNext;
  29.                 }else{
  30.                         delNext( pNode, pOld );
  31.                 }
  32.                 printf( "deleted %c from %c\n", pOld->name, pNode->name );
  33.         }
  34. }
  35.  
  36. void moveChild( struct sNode *pNode, struct sNode *pMove ){
  37.         delChild( pNode, pMove );
  38.         addChild( pNode, pMove );
  39. }

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Libreria grafica con widgets
« Respuesta #10 en: 10 de Enero de 2012, 15:41:50 »
después de pelearme unos días con el código, aqui traigo novedades...Os muestro un pequeño video donde se muestran 5 contenedores con 6 botones cada uno.
Cuando el usuario pulsa el touch en el punto {x,y}, se genera el evento {TOUCH, PRESS,{x,y}} que va recorriendo el arbol de widgets hasta el primer widget que se encuentra sobre el punto {x,y}, este widget obtiene el foco, procesa el evento(cambia su estado), se repinta en pantalla y ejecuta el callback correspondiente.


Os comento los puntos mas interesantes del código:
-Cada nodo del arbol ahora tiene 4 punteros. Esto es indispensable para recorrer el arbol en distintas direcciones sin conocer el root. El puntero tiene la forma que muestro a continuacion y en el ejemplo del video queda esta estructura:
Código: C
  1. struct sNode {
  2.     struct sNode *pParent, *pPrev;
  3.     struct sNode *pChild, *pNext;
  4. };
            Parent      Child      Prev      Next      Position      Size      Focus
R            -         c1         -         -         0,0            320,240      y
   c1         R         btn1      -         c2         25,10         160,110      y
      btn1   c1         -         -         btn2      10,10         40,40      y
      btn2   c1         -         btn1      btn3      60,10         40,40      n
      ...
   c2         R         btn7      c1         c3         50,20         160,110      n
      btn7   c2         -         -         btn8      10,10         40,40      n
      btn8   c2         -         btn7      btn9      60,10         40,40      n
      ...
   c3         R         btn13      c2         c4         75,30         160,110      n
      ...
   c4         R         btn19      c3         c5         100,40         160,110      n
      ...
   c5         R         btn25      c4         -         125,50         160,110      n
      ...

-La estructura del arbol determina el widget seleccionado. Siempre será el primer widget del primer contenedor del primer... Esto permite cambiar el "foco" fácilmente reordenando el árbol y mantener la coherencia con la informacion que se muestra en pantalla.
Código: C
  1. uint8_t WidgetHasFocus( struct sWidget *pWidget ){
  2.         struct sNode *pNTmp;
  3.         for( pNTmp = NodeGetFirstParent( &pWidget->node ) ; pNTmp != NULL ; pNTmp = NodeGetChild( pNTmp ) ){
  4.                 if( pNTmp == &pWidget->node ){
  5.                         return 1;
  6.                 }
  7.         }
  8.         return 0;
  9. }
  10.  
  11. void WidgetGetFocus( struct sWidget *pWidget ){
  12.         struct sNode *pNTmp;
  13.         struct sWidget *pWFocusOut, *pWFocusIn;
  14.         if( WidgetHasFocus( pWidget ) ){
  15.                 return;
  16.         }
  17.         for( pNTmp = &pWidget->node ; NodeHasParent( pNTmp ) ; pNTmp = NodeGetParent( pNTmp ) ){
  18.                 pWFocusOut = (struct sWidget*)NodeGetChild( NodeGetParent( pNTmp ) );
  19.                 pWFocusIn = (struct sWidget*)pNTmp;
  20.                 if( pWFocusOut != pWFocusIn ){
  21.                         WidgetMovePrev( pWFocusIn );
  22.                         WidgetEvent( pWFocusOut, WIDGET_EVENT_DRAW );  
  23.                         WidgetEvent( pWFocusIn, WIDGET_EVENT_DRAW );
  24.                 }
  25.         }
  26.         return;
  27. }

-Las coordenadas de un widget son relativas a las del padre, y las de este su padre... De esta manera podremos desplazar una ventana y repintarla correctamente( cuando la libreria permita mover ventanas).
Código: C
  1. struct sPoint WidgetGetOffset( struct sWidget *pWidget ){
  2.         struct sPoint p = {0,0};
  3.         struct sNode *pNTmp;
  4.         struct sWidget *pWTmp;
  5.         for( pNTmp = &pWidget->node ; NULL != pNTmp ; pNTmp = NodeGetParent( pNTmp ) ){
  6.                 pWTmp = (struct sWidget*) pNTmp;
  7.                 p.x += pWTmp->rect.x;
  8.                 p.y += pWTmp->rect.y;
  9.         }
  10.         return p;
  11. }

-Un widget no puede pintarse fuera de su padre. Antes de pintar el widget obtienen su área en pantalla {x,y,alto,ancho} y la de su padre. Con esos datos se obtiene el área común a ambos. Después se obtiene el área común con el padre del padre...hasta llegar al widget raíz. El área resultante es sobre la que se puede representar el widget, y de la que podrá recibir eventos del touch.
Código: C
  1. struct sRect RectInser( struct sRect rTop, struct sRect rBot ){
  2.         struct sPoint pBot[2], pTop[2], pClip[2];
  3.         struct sRect rClip;
  4.        
  5.         pTop[0].x = rTop.x;
  6.         pTop[0].y = rTop.y;
  7.         pTop[1].x = rTop.x + rTop.w;
  8.         pTop[1].y = rTop.y + rTop.h;
  9.        
  10.         pBot[0].x = rBot.x;
  11.         pBot[0].y = rBot.y;
  12.         pBot[1].x = rBot.x + rBot.w;
  13.         pBot[1].y = rBot.y + rBot.h;
  14.        
  15.         if( pBot[0].x < pTop[0].x ){
  16.                 pClip[0].x = pTop[0].x;
  17.         }else if( pTop[0].x <= pBot[0].x && pBot[0].x <= pTop[1].x ){
  18.                 pClip[0].x = pBot[0].x;
  19.         }else{
  20.                 pClip[0].x = pTop[1].x;
  21.         }
  22.         if( pBot[1].x < pTop[0].x ){
  23.                 pClip[1].x = pTop[0].x;
  24.         }else if( pTop[0].x <= pBot[1].x && pBot[1].x <= pTop[1].x ){
  25.                 pClip[1].x = pBot[1].x;
  26.         }else{
  27.                 pClip[1].x = pTop[1].x;
  28.         }
  29.  
  30.         if( pBot[0].y < pTop[0].y ){
  31.                 pClip[0].y = pTop[0].y;
  32.         }else if( pTop[0].y <= pBot[0].y && pBot[0].y <= pTop[1].y ){
  33.                 pClip[0].y = pBot[0].y;
  34.         }else{
  35.                 pClip[0].y = pTop[1].y;
  36.         }
  37.         if( pBot[1].y < pTop[0].y ){
  38.                 pClip[1].y = pTop[0].y;
  39.         }else if( pTop[0].y <= pBot[1].y && pBot[1].y <= pTop[1].y ){
  40.                 pClip[1].y = pBot[1].y;
  41.         }else{
  42.                 pClip[1].y = pTop[1].y;
  43.         }
  44.        
  45.         rClip.x = pClip[0].x;
  46.         rClip.y = pClip[0].y;
  47.         rClip.w = abs( pClip[1].x - pClip[0].x );
  48.         rClip.h = abs( pClip[1].y - pClip[0].y );
  49.        
  50.         return rClip;
  51. }
  52. struct sRect WidgetGetClip( struct sWidget *pWidget ){
  53.         struct sRect rect, clip;
  54.         struct sPoint pOffset;
  55.         struct sWidget *pWTmp;
  56.         struct sNode *pNTmp;
  57.  
  58.         pOffset = WidgetGetOffset( pWidget );
  59.         clip = pWidget->rect;
  60.         clip.x = pOffset.x;
  61.         clip.y = pOffset.y;
  62.        
  63.         for( pNTmp = &pWidget->node ; NULL != pNTmp ; pNTmp = NodeGetParent( pNTmp ) ){
  64.                 pWTmp = (struct sWidget*) pNTmp;
  65.                 pOffset = WidgetGetOffset( pWTmp );
  66.                 rect = pWTmp->rect;
  67.                 rect.x = pOffset.x;
  68.                 rect.y = pOffset.y;
  69.                 clip = RectInser( rect, clip );
  70.         }
  71.         return clip;   
  72. }

De momento ya se pueden añadir más widgets sencillos como slider, textbox... y reordenar el código. Pero tambien tengo que mejorar alguna cosa, como el manejo con botones. Se puede controlar el ultimo y penultimo nivel con botones, pero los superiores no. Aun no tengo claro como hacerlo.
Tambien tengo un problema en la representacion del BMPs. Tanto las fuentes como los BMPs los creo con el "graphics resources converter" de michochip, una pequeña aplicacion que viene con las librerias graficas de microchip. El caso es que tengo problemas si el ancho o el alto de la imagen no es una potencia de 2.
Código: C
  1. void GLCDDraw_BW_BMP( struct sGLCD *this, const struct sBMP *pBMP, int16_t x, int16_t y ){
  2.         int16_t w, h;
  3.         int16_t a,b;
  4.         color_t color;
  5.         for( w = 0 ; w < pBMP->width ; w++ ){
  6.                 for( h = 0 ; h < pBMP->height ; h++ ){
  7.                         a = (pBMP->width-w-1)/8;
  8.                         b = (h*pBMP->width)/8;
  9.                         color = ( pBMP->pData[ a + b ] >> (w&0x07)) & 0x01;
  10.                         GLCDPixel( this, x+(pBMP->width-w-1), y+h, color );
  11.                 }
  12.         }
  13.         return;
  14. }





Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18286
    • MicroPIC
Re: Libreria grafica con widgets
« Respuesta #11 en: 10 de Enero de 2012, 15:56:24 »
¡¡¡Genial!!!

Desconectado jukinch

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 608
Re: Libreria grafica con widgets
« Respuesta #12 en: 11 de Enero de 2012, 12:02:51 »
Buenísimo! :)
"Divide las dificultades que examinas en tantas partes como sea posible para su mejor solución." -René Descartes

Desconectado max252

  • PIC10
  • *
  • Mensajes: 14
Re: Libreria grafica con widgets
« Respuesta #13 en: 16 de Enero de 2012, 08:23:03 »
Si señor muy bueno , animo
Saludos desde Las Palmas de G.C. Islas Canarias


 

anything