Jump to content


Photo

[MULTILENGUAJE] Ventana desplegable translúcida, una alternativa a AnimateWindow


  • Please log in to reply
28 replies to this topic

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 30 September 2010 - 12:22 PM

Para conseguir desplegar una ventana tenemos la API AnimateWindow que funciona razonablemente bien. El problema surge cuando queremos desplegar una ventana semitransparente, en este caso el efecto visual es francamente malo, al menos en Windows XP.

Pues visto lo visto he escrito una función que realiza el efecto. Despliega una ventana semitransparente con un texto en el systray, la muestra un tiempo y la repliega.

Hacer el efecto con la VCL es muy sencillo, pero nos ata a ella y a ejecutables muy pesados. Si queremos el efecto para mini ejecutables (estoy hablando de ejecutables de 5 a 10k) tenemos que bajar a la API. Eso es lo que muestro en este truco que además puede servir de ejemplo o punto de partida para modificaciones posteriores o mostrar ventanas mas complejas pasando su Handle.

Aquí muestro el código en C++



cpp
  1. void TaskBarUpMessage(char *Msg, int cX, int cY, int cW, int cH, int cTime, HINSTANCE chInstance)
  2. {
  3.   RECT RectArea;
  4.   SystemParametersInfo(SPI_GETWORKAREA, 0, &RectArea, 0);
  5.   static YMax = RectArea.bottom;
  6.   static int X = cX;
  7.   static int Y = cY;
  8.   static int W = cW;
  9.   static int H = cH;
  10.   static int Time = cTime;
  11.   static HINSTANCE hInstance = chInstance;
  12.   int Margin = 10;
  13.   SIZE Size;
  14.   HDC hDC = GetDC(0);
  15.   int nLines = 1;
  16.   int Ancho = 0;
  17.   for(char *C = Msg, *Ini = Msg; *C; C++)
  18.   if(*C=='\n') {
  19.     GetTextExtentPoint32(hDC, Ini, DWORD(C)-DWORD(Ini), &Size);
  20.     if(Ancho < Size.cx) Ancho = Size.cx;
  21.     Ini = C;
  22.     nLines++;
  23.   }
  24.   if(!W) W = Ancho + Margin*2;
  25.   if(!H) H = Size.cy*nLines + Margin*2.5;
  26.   if(!X) X = RectArea.right - W;
  27.   if(Y)  YMax = H+Y;
  28.   Y = YMax;
  29.   ReleaseDC(0, hDC);
  30.   struct Timer{
  31.     static VOID __stdcall OnTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime){
  32.       static Inc = 1;
  33.       if(YMax-Y >= H && Inc>0){
  34.         Inc = -Inc;
  35.         Sleep(Time);
  36.       }
  37.       if(YMax<=Y && Inc<0){
  38.         Inc = -Inc;
  39.         SendMessage(hWnd, WM_CLOSE, 0, 0);
  40.         ::UnregisterClass("UPMSG", hInstance);
  41.         return;
  42.       }
  43.       Y-=Inc;
  44. //      SetWindowPos(hWnd, HWND_TOPMOST, X, Y, W, YMax-Y, SWP_SHOWWINDOW);
  45.       SetWindowPos(hWnd, HWND_TOP, X, Y, W, H, SWP_SHOWWINDOW);
  46.     }
  47.   }TI;
  48.  
  49.   WNDCLASS WinClass = {0,DefWindowProc,0,0,hInstance,0,0,(HBRUSH)COLOR_BTNSHADOW,"","UPMSG"};
  50.   ::RegisterClass(&WinClass);
  51.   HANDLE hFrame = CreateWindowEx(WS_EX_LAYERED, "UPMSG", "",WS_VISIBLE | WS_POPUP |WS_THICKFRAME
  52.                                 ,X, Y, W, 0, HWND_DESKTOP, (HMENU)0, hInstance, NULL);
  53.  
  54.   HANDLE hStatic = CreateWindow("STATIC", "", WS_VISIBLE | WS_CHILD
  55.                                 ,Margin, Margin, W-Margin*2, H-Margin*2, hFrame, (HMENU)0, hInstance, NULL);
  56.  
  57.   SetWindowText(hStatic, Msg);
  58.   SetLayeredWindowAttributes(hFrame, NULL, 200, LWA_ALPHA);
  59.   SetTimer(hFrame, 0, 10, (TIMERPROC)TI.OnTimer);
  60. }



El uso es simple. Si damos los valores cero a las coordenadas de la ventana, la muestra en el systray y calcula el tamaño necesario para contener el texto según el número de líneas y tamaño.

Si damos tamaño a la ventana, el texto se tratará de ajustar a ella.
cTime es el tiempo durante el que mostrará la ventana una vez desplegada antes de comenzar a ocultarse.

Ejemplo de uso automático:


cpp
  1. TaskBarUpMessage("Mensage secundario\nMicroondas\ncocedero", 0, 0, 0, 0, 1000, 0);



Imagen Enviada

Saludos.
  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 30 September 2010 - 12:35 PM

Caramba!!!, yo he tenido problema con esos mensajes, por alguna causa no se ocultan en el tiempo que le asigno en el componente. Me parece que me viene genial esta función.

Salud OS

PD, ¿ Me esperaré a que salga la versión de Delphi ? :D :D :D
  • 0

#3 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 30 September 2010 - 12:36 PM

Ya sabéis que me gusta poner el código en C/C++ y en delphi y en este caso no iba a ser menos (aunque me toca escribir por dos ^o|)

La versión delphi es muy similar a la versión C++ y he preferido usar submétodos para compactar el código y hacer honores a Delphius en su gran esfuerzo. Creo que se entiende bien.


delphi
  1. procedure TaskBarUpMessage(Msg: string; cX, cY, cW, cH, cTime: integer; chInstance: LongWord);
  2. const
  3. {$J+}
  4.   YMax: integer = 0;
  5.   X: integer = 0;
  6.   Y: integer = 0;
  7.   W: integer = 0;
  8.   H: integer = 0;
  9.   Time: integer = 0;
  10.   hInstance: LongWord = 0;
  11.   Margin: integer = 10;
  12.  
  13. var
  14.   RectArea: TRect;
  15.   Size: TSize;
  16.   DC: HDC;
  17.   XMax: integer;
  18.   WinClass: WNDCLASS;
  19.   hFrame, hStatic: THandle;
  20.  
  21.   // El evento OnTimer
  22.   procedure OnTimer(hWnd: THandle; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;
  23.   const
  24.     Inc: integer = 2;
  25.   begin
  26.     if (YMax-Y >= H) and (Inc>0)then
  27.     begin
  28.       Inc:= -Inc;
  29.       Sleep(Time);
  30.     end;
  31.     if (YMax<=Y) and(Inc<0) then
  32.     begin
  33.       Inc:= -Inc;
  34.       SendMessage(hWnd, WM_CLOSE, 0, 0);
  35.       Windows.UnregisterClass('UPMSG', hInstance);
  36.       exit;
  37.     end;
  38.     Y:= Y-Inc;
  39.     SetWindowPos(hWnd, HWND_TOPMOST, X, Y, W, YMax-Y, SWP_SHOWWINDOW);
  40. //    SetWindowPos(hWnd, HWND_TOP, X, Y, W, H, SWP_SHOWWINDOW);
  41.   end;
  42.  
  43.   // Calcula el tamaño de ventana que contendrá el texto "Multiline"
  44.   procedure GetTextExtentML(DC: HDC; Msg: String; Len: integer; var Size: TSize);
  45.   var
  46.     Ancho, Alto, nLines, Ini, i: integer;
  47.   begin
  48.     Ancho:= 0; Alto:= 0; nLines:= 1; Ini:= 1;
  49.     for i:=1 to Length(Msg) do
  50.     begin
  51.       if Msg[i] = #13 then
  52.       begin
  53.         GetTextExtentPoint32(DC, PCHAR(@Msg[Ini]), i-Ini, Size);
  54.         if Ancho < Size.cx then Ancho:= Size.cx;
  55.         if Alto  < Size.cy then Alto:=  Size.cy;
  56.         Ini:= i;
  57.         inc(nLines);
  58.       end;
  59.     end;
  60.     Size.cx:= Ancho;
  61.     Size.cy:= Alto*nLines;
  62.   end;
  63.  
  64.  
  65. begin
  66.   SystemParametersInfo(SPI_GETWORKAREA, 0, @RectArea, 0);
  67.   YMax:= RectArea.bottom;
  68.   X:= cX;
  69.   Y:= cY;
  70.   W:= cW;
  71.   H:= cH;
  72.   Time:= cTime;
  73.   hInstance:= chInstance;
  74.   DC:= GetDC(0);
  75.   GetTextExtentML(DC, PCHAR(Msg), Length(Msg), Size);
  76.   if W = 0 then W:= Size.cx + Margin*2;
  77.   if H = 0 then H:= Size.cy + Margin*2;
  78.   if X = 0 then X:= RectArea.right - W;
  79.   if Y <>0 then YMax:= H+Y;
  80.   Y:= YMax;
  81.   ReleaseDC(0, DC);
  82.   with WinClass do
  83.   begin
  84.     style:= 0;
  85.     lpfnWndProc:= @DefWindowProc;
  86.     cbClsExtra:= 0;
  87.     cbWndExtra:= 0;
  88.     hInstance:= chInstance;
  89.     hIcon:= 0;
  90.     hCursor:= 0;
  91.     hbrBackground:= COLOR_BTNSHADOW;
  92.     lpszMenuName:= '';
  93.     lpszClassName:= 'UPMSG';
  94.   end;
  95.   Windows.RegisterClass(WinClass);
  96.   hFrame:= CreateWindowEx(WS_EX_LAYERED, 'UPMSG', '',WS_POPUP or WS_THICKFRAME
  97.                           ,X, Y, W, H, HWND_DESKTOP, HMENU(0), hInstance, nil);
  98.  
  99.   hStatic:= CreateWindow('STATIC', '', WS_VISIBLE or WS_CHILD
  100.                         ,Margin, Margin, W-Margin*2, H-Margin*2, hFrame, HMENU(0), hInstance, nil);
  101.  
  102.   SetWindowText(hStatic, PCHAR(Msg));
  103.   SetLayeredWindowAttributes(hFrame, 0, 200, LWA_ALPHA);
  104.   SetTimer(hFrame, 0, 20, @OnTimer);
  105. {$J-}
  106. end;


Un ejemplo del uso:

delphi
  1. TaskBarUpMessage('Mensage secundario'+#13+'Hola'+#13+'Tercero', 0, 0, 0, 0, 2000, 0);


Como explique en la versión C++, si damos los valores cero a las coordenadas de la ventana, la muestra en el systray y calcula el tamaño necesario para contener el texto según el número de líneas y tamaño. Si damos tamaño a la ventana, el texto se tratará de ajustar a ella. cTime es el tiempo durante el que mostrará la ventana una vez desplegada antes de comenzar a ocultarse.

Espero que sea de alguna utilidad.


Saludos.


PD: Edito por añadir una sugerencia de Desart
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 30 September 2010 - 12:38 PM

PD, ¿ Me esperaré a que salga la versión de Delphi ? :D :D :D


Parece que no vas a tener que esperar mucho... ;) :D :D :D

Saludos.
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 30 September 2010 - 12:45 PM

Lo sabía amigo, además el Prefijo [MULTILENGUAJE] lo auguraba ;)

Por supuesto que me será de utilidad :)

Salud OS
  • 0

#6 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 posts
  • LocationEspaña

Posted 30 September 2010 - 12:55 PM

Buenas,

Es genial, me ha gustado mucho!!

Una pregunta, la directiva de compilación {$J-} qué hace??

Gracias

Nos leemos

PD: el fondo de escritorio lo mejor xD
PD2: el av no me mola jejejejejje
  • 0

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 30 September 2010 - 01:02 PM

..........hacer honores a Delphius en su gran esfuerzo......


Tendrán que esperar un "poquitín" para ver ese gran esfuerzo de nuestro amigo Delphius :)

Salud OS
  • 0

#8 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 30 September 2010 - 01:08 PM

Hola

Intenté ejecutar la función y me muestra dos errores....

[DCC Error] Unit1.pas(59): E2010 Incompatible types: 'TPersistentClass' and 'string'

En la línea:      UnregisterClass('UPMSG', hInstance);


[DCC Error] Unit1.pas(119): E2010 Incompatible types: 'TPersistentClass' and 'tagWNDCLASSA'

En la línea:  RegisterClass(WinClass);


Que estoy haciendo mal :(

Salud OS

PD: Estoy usando D2007.
  • 0

#9 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14460 posts
  • LocationMéxico

Posted 30 September 2010 - 01:13 PM

Ah vaya, pues es que en la unidad classes los procedimientos son así:



delphi
  1. procedure UnRegisterClass(AClass: TPersistentClass);
  2. procedure RegisterClass(AClass: TPersistentClass);



Con que versión de Delphi lo hiciste amigo escafandra....

Salud OS

  • 0

#10 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 30 September 2010 - 01:18 PM

Bueno, esto me ha pasado por probarlo en un programa API exclusivo, sin VCL:



delphi
  1. var
  2. Msg: TMsg;
  3. begin
  4.  
  5.   TaskBarUpMessage('Mensage secundario'+#13+'Hola'+#13+'Tercero', 0, 0, 0, 0, 2000, 0);
  6.   // Bucle de mensajes:
  7.   repeat GetMessage(Msg, 0, 0, 0);
  8.   DispatchMessage(Msg);
  9.   until (Msg.Message = WM_QUIT);
  10.  
  11. end.



Es mi manía del bajo nivel.

Esas funciones de delphi se llaman como las correspondientes APIs. La solución es bien sencilla:


delphi
  1. Windows.RegisterClass(WinClass);
  2. Windows.UnregisterClass('UPMSG', hInstance);



Ya está corregido en el código delphi.

Saludos.
  • 0

#11 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 30 September 2010 - 01:25 PM

Es genial, me ha gustado mucho!!


Me alegro. :)

Una pregunta, la directiva de compilación {$J-} qué hace??

{$J+} Sirve para poder asignar valores a las constantes, es decir las trata como variables globales de la función y permite reconocer su valor en las entradas de las subfunciones, en particular en las llamadas automáticas (por el S.O.) al evento OnTimer. {$J-} Lo anula.

PD: el fondo de escritorio lo mejor xD

Busqué un motivo contrastado... *-)

PD2: el av no me mola jejejejejje


A mi tampoco me gusta nada, es el que tenemos en el curro...  un coladero de bichitos 8o| *-)

Saludos.
  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 30 September 2010 - 01:27 PM

Por la imagen se ve que funciona muy bien. No he probado el código, y lo he visto de pasada.
¡Como será que te gusta ahorrar en recursos que hasta empleaste las API de timer!

Respecto al honor por el trabajo, como ha dicho Eliseo, habrá que esperar. Si puedo colocaré algo más entre hoy y mañana. Disculpen que vaya lento.
Te agradezco el interés... ha quien hay que darle honores es a tí. No muchos se animan o pueden lograr hacer las obras maestras que expones.

Respecto a la directiva +J, si no me falla la memoria tengo entendido que es para evitar que una variable constante pueda ser alterada.

Saludos,
  • 0

#13 eduarcol

eduarcol

    Advanced Member

  • Administrador
  • 4483 posts
  • LocationVenezuela

Posted 01 October 2010 - 08:05 AM

Funciona muy bien en delphi 7  <:o)
  • 0

#14 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 posts
  • LocationEspaña

Posted 20 November 2010 - 05:49 AM

Hola escafandra, con tu permiso me gustaría incorporar, este procedimiento como función a las que uso, ademas comentarte que he añadido a las lineas de definición de la ventana en automático para que no recorte el texto, estas lineas son originalmente

delphi
  1.     Size.cx:= Ancho;        Size.cy:= (Alto*nLines);

y las he dejado así

delphi
  1.   Size.cx:= Ancho+15;        Size.cy:= (Alto*nLines)+15;


Una última cosa, por que nada más me permite usarlo una vez, ¿estoy cometiendo un error? o como se puede solucionar

  • 0

#15 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 20 November 2010 - 06:46 PM

Puedes modificar el código como gustes, de hecho ya comenté que podría servir de base para seguir su desarrollo. De hecho yo mismo he realizado alguna mejora en C++ de esa función mejorando el comportamiento y añadiendo colores. Si tengo un tiempo libre lo traduzco a delphi.

El problema que comentas radica en la siguiente parte del código en la función de tratan¡miento del evento Timer:



delphi
  1.     if (YMax<=Y) and(Inc<0) then
  2.     begin
  3.       SendMessage(hWnd, WM_CLOSE, 0, 0);
  4.       Windows.UnregisterClass('UPMSG', hInstance);
  5.       exit;
  6.     end;



Debes añadir una línea de código para cambiar de signo a la constante inc y que permita el ascenso de la ventana:



delphi
  1.     if (YMax<=Y) and(Inc<0) then
  2.     begin
  3.       Inc:= -Inc;
  4.       SendMessage(hWnd, WM_CLOSE, 0, 0);
  5.       Windows.UnregisterClass('UPMSG', hInstance);
  6.       exit;
  7.     end;




Saludos.
  • 0

#16 Desart

Desart

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 715 posts
  • LocationEspaña

Posted 21 November 2010 - 01:40 AM

Muchas gracias Escafandra funciono perfecto, y estoy seguro de que muchos quedamos a la espera de esas modificaciones.
  • 0

#17 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 22 November 2010 - 07:19 AM

Voy a publicar las modificaciones que he realizado sobre el código inicial tanto en C++ como en delphi.

He añadido el parámetro Margin para configurar el margen entre el borde de la ventana y el texto, Ese valor también separará la ventana del borde derecho del escritorio si es en ese lugar donde colocamos la ventana (posición por defecto).

He eliminado el uso de una ventana estática para colocar el texto con el fin de tener mas control sobre el pintado. Con esto consigo que se pueda elegir un color de fondo, a cambio de tener que escribir la función de tratamiento del mensaje WM_PAINT.

La forma de como estera la ventana, una vez ha ascendido, es controlada ahora por otro timer para no bloquear la aplicación como ocurría con el anterior método (Sleep).

Por último he eliminado el parámetro HInstance.

Por supuesto, el código sigue siendo válido en miniaplicaciones que prescindan de la VCL.

Seguidamente os propongo el código en C++:
 

cpp
  1. #include <Shlwapi.h>
  2. void TaskBarUpMessage(char *cMsg, int cX, int cY, int cW, int cH, int cMargin, int cTime, COLORREF cColor)
  3. {
  4.   static char* Msg;
  5.   static int X, Y, W, H, YMax, Time, Margin;
  6.   static COLORREF Color;
  7.  
  8.   struct TPopUpWindow
  9.   {
  10.     static LRESULT  __stdcall WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  11.     {
  12.       if(uMsg == WM_PAINT){
  13.         HDC hDC = GetWindowDC(hWnd);
  14.         RECT Rect = {Margin, Margin, W-Margin, H};
  15.         HBRUSH hBrush = CreateSolidBrush(Color);
  16.         SelectObject(hDC, hBrush);
  17.         Rectangle(hDC, 0, 0, W, H);
  18.         SetTextColor(hDC, RGB(0,0,0));
  19.         SetBkColor(hDC, Color);
  20.         DrawText(hDC, Msg, lstrlen(Msg), &Rect, DT_LEFT);
  21.         DeleteObject(hBrush);
  22.         ReleaseDC(hWnd, hDC);
  23.       }
  24.       return DefWindowProc(hWnd, uMsg, wParam, lParam);
  25.     }
  26.  
  27.     static void __stdcall OnTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  28.     {
  29.       if(idEvent) return;
  30.       static Inc = 2;
  31.       if(YMax-Y > H && Inc>0){
  32.           Inc = -Inc;
  33.           KillTimer(hWnd, 0);
  34.           SetTimer(hWnd, 1, Time, (TIMERPROC)OnWait);
  35.       }
  36.       if(YMax<=Y && Inc<0){
  37.           Inc = -Inc;
  38.           SendMessage(hWnd, WM_CLOSE, 0, 0);
  39.           UnregisterClass("UPMSG", 0);
  40.           return;
  41.       }
  42.       Y-=Inc;
  43.       MoveWindow(hWnd, X, Y, W, YMax-Y, true);
  44.     }
  45.  
  46.     static void __stdcall OnWait(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
  47.     {
  48.       if(idEvent == 1){
  49.         KillTimer(hWnd, 1);
  50.         SetTimer(hWnd, 0, 5, (TIMERPROC)OnTimer);
  51.       }
  52.     }
  53.  
  54.   }WPopUp;
  55.  
  56.   RECT RectArea;
  57.   SystemParametersInfo(SPI_GETWORKAREA, 0, &RectArea, 0);
  58.   YMax = RectArea.bottom;
  59.   X = cX; Y = cY; W=cW; H=cH; Time=cTime;
  60.   Msg=cMsg; Color=cColor;
  61.   // Tamaño del recuadro que contendrá el texto multiline
  62.   SIZE Size;
  63.   HDC hDC = GetDC(0);
  64.   int nLines = 0;
  65.   int Ancho = 0;
  66.   char *C = Msg, *Ini = Msg, *Fin = Msg + lstrlen(Msg);
  67.   do{
  68.     C = StrStr(C, " ");
  69.     if(!C) C = Fin;
  70.     GetTextExtentPoint32(hDC, Ini, DWORD(C)-DWORD(Ini), &Size);
  71.     if(Ancho < Size.cx) Ancho = Size.cx;
  72.     Ini = C;
  73.     nLines++;
  74.   }while(*C++);
  75.  
  76.   if(!Margin) Margin = Size.cy;
  77.   if(!W) W = Ancho + Margin*2;
  78.   if(!H) H = Size.cy*nLines + Margin*2;
  79.   if(!X) X = RectArea.right - W - Margin;
  80.   if(Y && Y<YMax-H)  YMax = H+Y;
  81.   Y = YMax;
  82.   ReleaseDC(0, hDC);
  83.  
  84.   HWND hPrev = GetFocus();
  85.   WNDCLASS WinClass = {0,(WNDPROC)WPopUp.WindowProc,0,0,0,0,0,0,"","UPMSG"};
  86.   RegisterClass(&WinClass);
  87.   HANDLE hFrame = CreateWindowEx(WS_EX_LAYERED|WS_EX_TOPMOST|WS_EX_TOOLWINDOW, "UPMSG", "",WS_VISIBLE | WS_POPUP
  88.                                 ,X, Y, W, 0, HWND_DESKTOP, (HMENU)0, 0, NULL);
  89.   SetLayeredWindowAttributes(hFrame, NULL, 200, LWA_ALPHA);
  90.   SetTimer(hFrame, 0, 5, (TIMERPROC)WPopUp.OnTimer);
  91.   SetFocus(hPrev);
  92. }

Que lo disfrutéis, os sea útil y le saquéis todo el partido posible.

Saludos.
  • 0

#18 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 22 November 2010 - 07:42 AM

Y ahora le llega el turno a delphi, lo explicado en el anterior mensaje para la versión C++ vale para esta versión.

No he variado la filosofía del código en cuanto al uso de submétodos locales. Evidentemente se puede modificar para prescindir de ellos.

Está probado en delphi 7.


delphi
  1. // La siguiente definición es para el caso de una aplicación que no use la VCL
  2. function StrStr(s1: PCHAR; s2: PCHAR): PCHAR; stdcall;
  3.         external 'Shlwapi.dll' name 'StrStrA';
  4.  
  5. procedure TaskBarUpMessage(cMsg: string; cX, cY, cW, cH, cMargin, cTime: integer; cColor: COLORREF);
  6. const
  7. {$J+}
  8.   Msg: PCHAR = nil;
  9.   YMax: integer = 0;
  10.   X: integer = 0;
  11.   Y: integer = 0;
  12.   W: integer = 0;
  13.   H: integer = 0;
  14.   Color: COLORREF = 0;
  15.   Time: integer = 0;
  16.   hInstance: LongWord = 0;
  17.   Margin: integer = 0;
  18.   Wait: Pointer = 0;
  19. var
  20.   RectArea: TRect;
  21.   Size: TSize;
  22.   CharSize: TSize;
  23.   DC: HDC;
  24.   WinClass: WNDCLASS;
  25.   hFrame, hPrev: THandle;
  26.  
  27.   function WindowProc(hWnd: THandle; uMsg, wParam, lParam: integer): LRESULT; stdcall;
  28.   var
  29.     DC: HDC;
  30.     Rect: TRect;
  31.     Brush: HBRUSH;
  32.  
  33.   begin
  34.     if uMsg = WM_PAINT then
  35.     begin
  36.       DC:= GetWindowDC(hWnd);
  37.       Rect.Left:= Margin; Rect.Top:= Margin; Rect.Right:= W; Rect.Bottom:= H;
  38.       Brush:= CreateSolidBrush(Color);
  39.       SelectObject(DC, Brush);
  40.       Rectangle(DC, 0, 0, W, H);
  41.       SetTextColor(DC, RGB(0,0,0));
  42.       SetBkColor(DC, Color);
  43.       DrawText(DC, Msg, lstrlen(Msg), Rect, DT_LEFT);
  44.       DeleteObject(Brush);
  45.       ReleaseDC(hWnd, DC);
  46.     end;
  47.     Result:= DefWindowProc(hWnd, uMsg, wParam, lParam);
  48.   end;
  49.  
  50.   procedure OnTimer(hWnd: THandle; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;
  51.   const
  52.     Inc: integer = 2;
  53.   var
  54.     Rect: TRect;
  55.     DC: HDC;
  56.     Brush: HBRUSH;
  57.   begin
  58.     if idEvent <> 0 then exit;
  59.     if (YMax-Y > H) and (Inc>0)then
  60.     begin
  61.       Inc:= -Inc;
  62.       KillTimer(hWnd, 0);
  63.       SetTimer(hWnd, 1, Time, Wait);
  64.     end;
  65.     if (YMax<=Y) and(Inc<0) then
  66.     begin
  67.       Inc:= -Inc;
  68.       SendMessage(hWnd, WM_CLOSE, 0, 0);
  69.       Windows.UnregisterClass('UPMSG', hInstance);
  70.       exit;
  71.     end;
  72.     Y:= Y-Inc;
  73.     MoveWindow(hWnd, X, Y, W, YMax-Y, true);
  74.   end;
  75.  
  76.   procedure OnWait(hWnd: THandle; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;
  77.   begin
  78.     if idEvent = 1 then
  79.     begin
  80.       KillTimer(hWnd, 1);
  81.       SetTimer(hWnd, 0, 5, @OnTimer);
  82.     end;
  83.   end;
  84.  
  85.   procedure GetTextExtentML(DC: HDC; Msg: PCHAR; Len: integer; var Size: TSize);
  86.   var
  87.     Ancho, Alto, nLines: integer;
  88.     C, Ini: PCHAR;
  89.   begin
  90.     Ancho:= 0; Alto:= 0; nLines:= 0;
  91.     Ancho:= 0; Alto:= 0; nLines:= 0;
  92.     C:= Msg; Ini:= Msg;
  93.     repeat
  94.       if ((C+1)^ = #13) or ((C+1)^ = #0) then
  95.       begin
  96.         GetTextExtentPoint32(DC, Ini, DWORD(C)-DWORD(Ini)+1, Size);
  97.         if Ancho < Size.cx then Ancho:= Size.cx;
  98.         if Alto  < Size.cy then Alto:=  Size.cy;
  99.         Ini:= C;
  100.         inc(nLines);
  101.       end;
  102.       inc(C);
  103.     until C^ = #0;
  104.     Size.cx:= Ancho;
  105.     Size.cy:= Alto*nLines;
  106.   end;
  107.  
  108. begin
  109.   Wait:= @OnWait;
  110.   SystemParametersInfo(SPI_GETWORKAREA, 0, @RectArea, 0);
  111.   Msg:= PCHAR(cMsg);
  112.   YMax:= RectArea.bottom;
  113.   X:= cX;
  114.   Y:= cY;
  115.   W:= cW;
  116.   H:= cH;
  117.   Color:= cColor;
  118.   Time:= cTime;
  119.   DC:= GetDC(0);
  120.   GetTextExtentML(DC, PCHAR(Msg), Length(Msg), Size);
  121.  
  122.   GetTextExtentPoint32(DC, 'H', 1, CharSize);
  123.   if Margin = 0 then Margin:= CharSize.cy;
  124.   if W = 0 then W:= Size.cx + Margin*2;
  125.   if H = 0 then H:= Size.cy + Margin*2;
  126.   if X = 0 then X:= RectArea.right - W - Margin;;
  127.   if (Y<>0) and (Y<YMax-H) then YMax:= H+Y;
  128.  
  129.   Y:= YMax;
  130.   ReleaseDC(0, DC);
  131.   with WinClass do
  132.   begin
  133.     style:= 0;
  134.     lpfnWndProc:= @WindowProc;
  135.     cbClsExtra:= 0;
  136.     cbWndExtra:= 0;
  137.     hInstance:= 0;
  138.     hIcon:= 0;
  139.     hCursor:= 0;
  140.     hbrBackground:= 0;
  141.     lpszMenuName:= '';
  142.     lpszClassName:= 'UPMSG';
  143.   end;
  144.   Windows.RegisterClass(WinClass);
  145.   hPrev:= GetFocus;
  146.   hFrame:= CreateWindowEx(WS_EX_LAYERED or WS_EX_TOPMOST or WS_EX_TOOLWINDOW, 'UPMSG', '',WS_VISIBLE or WS_POPUP
  147.                           ,X, Y, W, 0, HWND_DESKTOP, HMENU(0), hInstance, nil);
  148.   SetLayeredWindowAttributes(hFrame, 0, 200, LWA_ALPHA);
  149.   SetTimer(hFrame, 0, 20, @OnTimer);
  150.   SetFocus(hPrev);
  151.  
  152. {$J-}
  153. end;



Espero que sea de utilidad.

Saludos.
  • 0

#19 seoane

seoane

    Advanced Member

  • Administrador
  • 1259 posts
  • LocationEspaña

Posted 22 November 2010 - 08:06 AM

Muy bueno escafandra !!!  (y)

Yo solo cambiaría esto:


delphi
  1. hFrame:= CreateWindowEx(WS_EX_LAYERED or WS_EX_TOPMOST , 'UPMSG', '',WS_POPUP
  2.                           ,X, Y, W, H, HWND_DESKTOP, HMENU(0), hInstance, nil);



Por esto otro, para evitar que la ventana emergente aparezca en la barra de tareas:


delphi
  1. hFrame:= CreateWindowEx(WS_EX_LAYERED or WS_EX_TOPMOST or WS_EX_TOOLWINDOW, 'UPMSG', '',WS_POPUP
  2.                           ,X, Y, W, H, HWND_DESKTOP, HMENU(0), hInstance, nil);



Y por otro lado, ¿por que no usar un Ballonhint?  ^o|
  • 0

#20 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 22 November 2010 - 09:05 AM

Pues ahora que lo dices, seoane, tienes toda la razón, es mejor eliminarla de la barra de tareas.

Mostrar los mensajes con un BalloonHints es una buena opción como lo hice en el componente SysTrayIcon que muchos conocen y que publiqué en el foro. En esta ocasión trataba de conseguir otro efecto y sobre todo jugar con la API pues la idea nació para usarlo en programas sin VCL y al no convencerme del todo el comportamiento de AnimateWindow.

Saludos.

PD he incorporado en el código la sugerencia de seoane.
  • 0




IP.Board spam blocked by CleanTalk.