Page 1 sur 2

Subclassing a window

Publié : sam. 14/mai/2005 20:42
par Progi1984
Dans ce post,
http://forums.purebasic.com/english/vie ... hp?t=15090

elchoni me conseille de 'subclassing the window' mais je ne connais pas cette technique, quelqu'un peut m'aider ?

Publié : sam. 14/mai/2005 21:01
par nico
Le subclassing est un moyen d'interfacer notre propre procédure avant l'appel de la procédure de traitement originale et ainsi pouvoir intervenir sur des messages auxquelles on aurait pas accès.

En voici un exemple que j'avais posté:

Code : Tout sélectionner

Procedure NouvelleProc( hWnd, Msg,  wParam, lParam) 
  ;Ici on récupère l'adresse d'origine de la procédure grâce à la 
  ;chaine qui l'identifie: "OriginProc" et le handle de la fenêtre 
  ;voir la fonction SetProp. 
  OriginProc.l= GetProp_(hWnd, "OriginProc") 
  Select Msg 
    Case #wm_char 
      If wParam=$0D 
        ;Simulation de la touche Tab 
        keybd_event_($09, 0, 0, 0) 
        keybd_event_($09, 0, #KEYEVENTF_KEYUP, 0) 
        ProcedureReturn 0 
      EndIf 
  EndSelect 
  ;On renvoie tous les autres évènements à la procédure d'origine. 
  ProcedureReturn CallWindowProc_(OriginProc,hWnd,Msg,wParam,lParam) 
EndProcedure    

If OpenWindow(0,0,0,322,275,#PB_Window_SystemMenu|#PB_Window_ScreenCentered,"StringGadget Flags") And CreateGadgetList(WindowID(0)) 
  StringGadget(0,8, 10,306,20,"Normal StringGadget...") 
  StringGadget(1,8, 35,306,20,"1234567",#PB_String_Numeric) 
  StringGadget(2,8, 60,306,20,"Readonly StringGadget",#PB_String_ReadOnly) 
  
  ;Avec cette fonction, on récupère l'adresse d'origine de la procédure 
  ;pour ensuite la restituer, une fois le traitement terminé. 
  OriginProc = SetWindowLong_(GadgetID(0), #GWL_WNDPROC, @NouvelleProc()) 
  
  ;Cette fonction est très utile car elle permet d'associer une nouvelle donnée 
  ;à n'importe quelles fenêtres crées en utlisant une chaine de caractère pour 
  ;l'identification; ça évite d'utiliser une valeur globale. 
  ;Ici on associe la valeur OriginProc identifiée par la chaine "OriginProc" 
  ;au StringGadget 
  SetProp_(GadgetID(0), "OriginProc", OriginProc) 
  
  Repeat 
    EventID = WaitWindowEvent() 
    Select EventID 
      Case  #PB_EventGadget 
        Select EventGadgetID() 
          Case 0 
            Debug "Normal StringGadget" 
            
        EndSelect 
        
      Case #WM_CLOSE 
        ;Supprimer avant fermeture du programme la donnée associée à la fenêtre. 
        RemoveProp_(GadgetID(0),"OriginProc") 
        Quit=1 
        
    EndSelect 
    
  Until Quit=1 
EndIf 

Publié : sam. 14/mai/2005 23:14
par erix14
Merci nico pour ton exemple, je ne connaissais pas SetProp_(...), GetProp_(...), RemoveProp_(...)
C'est en fait une simplification de ce genre de code

Code : Tout sélectionner

          Addr = GlobalAlloc_(#GMEM_FIXED,8) 
          OldCB = SetWindowLong_(hBouton,#GWL_WNDPROC,@BoutonRX14_CallBack()) 
          PokeL(Addr,OldCB) 
          PokeL(Addr+4,number) 
          SetWindowLong_(hBouton,#GWL_USERDATA,Addr) 
qui peut s'écrire

Code : Tout sélectionner

          OldCB = SetWindowLong_(hBouton,#GWL_WNDPROC,@BoutonRX14_CallBack()) 
          SetProp_(hBouton, "OldCB", OldCB)
          SetProp_(hBouton,"number", number)
C'est bien ça nico ?

Publié : sam. 14/mai/2005 23:22
par nico
Oui, pour moi ça revient à ça.

:)

Publié : dim. 15/mai/2005 6:23
par Anonyme2
Je suis comme erix14,
je ne connaissais pas SetProp_(...), GetProp_(...), RemoveProp_(...)

Merci Nico

Publié : sam. 11/juin/2005 22:43
par gansta93
Salut,

Cool ! :-D Mais comment intégrer ça dans une lib ?

Publié : jeu. 18/août/2005 13:07
par Dr. Dri
Je viens de lire ce post et je vois une question sans réponse ^^
à partir du code de nico je t'ai fait un exemple "général"

Code : Tout sélectionner

Declare WindowCallback(WindowID.l, Message.l, wParam.l, lParam.l)
Declare WindowSetMinMaxSize(WindowID.l, MinW.l, MinH.l, MaxW.l, MaxH.l)
Declare MinMax(Value.l, OldValue.l)

Procedure WindowCallback(WindowID.l, Message.l, wParam.l, lParam.l)
  Protected WndProc.l, Result.l, *MinMax.MinMaxInfo
  
  If Message = #WM_GetMinMaxInfo
    
    *MinMax = lParam
    *MinMax\ptMinTrackSize\x = MinMax(GetProp_(WindowID, "MinTrackWidth"),  *MinMax\ptMinTrackSize\x)
    *MinMax\ptMinTrackSize\y = MinMax(GetProp_(WindowID, "MinTrackHeight"), *MinMax\ptMinTrackSize\y)
    *MinMax\ptMaxTrackSize\x = MinMax(GetProp_(WindowID, "MaxTrackWidth"),  *MinMax\ptMaxTrackSize\x)
    *MinMax\ptMaxTrackSize\y = MinMax(GetProp_(WindowID, "MaxTrackHeight"), *MinMax\ptMaxTrackSize\y)
    
  EndIf
  
  WndProc = GetProp_(WindowID, "WndProc")
  If WndProc
    Result = CallWindowProc_(WndProc, WindowID, Message, wParam, lParam)
  EndIf
  ProcedureReturn Result
EndProcedure

Procedure WindowSetMinMaxSize(WindowID.l, MinW.l, MinH.l, MaxW.l, MaxH.l)
  Protected WndProc.l
  
  SetProp_(WindowID, "MinTrackWidth",  MinW)
  SetProp_(WindowID, "MinTrackHeight", MinH)
  SetProp_(WindowID, "MaxTrackWidth",  MaxW)
  SetProp_(WindowID, "MaxTrackHeight", MaxH)
  
  If GetProp_(WindowID, "WndProc") = #Null
    WndProc = SetWindowLong_( WindowID, #GWL_WndProc, @WindowCallback() )
    SetProp_(WindowID, "WndProc", WndProc)
  EndIf
EndProcedure

Procedure MinMax(Value.l, OldValue.l)
  If Value <= 0
    Value = OldValue
  EndIf
  ProcedureReturn Value
EndProcedure

OpenWindow(0, 0, 0, 480, 360, #PB_Window_MinimizeGadget|#PB_Window_Sizegadget, "MinMax")
WindowSetMinMaxSize(WindowID(0), 320, 240, 640, 480)

OpenWindow(1, 100, 100, 400, 300, #PB_Window_MinimizeGadget|#PB_Window_Sizegadget, "Min")
WindowSetMinMaxSize(WindowID(1), 320, 240, 0, 0)

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
en gros dans ta lib tu auras la fonction callback (que l'utilisateur n'est pas censé connaître) et la fonction WindowSetMinMaxSize qu'il pourra utiliser autant de fois qu'il voudra pour autant de fenêtre...

Dri ;)

Publié : jeu. 18/août/2005 13:16
par Progi1984
Une petite question au passage : puisque tu parles de userlibs, est il possible de créer plusieurs callbacks pour une meme fenetre ?

Publié : jeu. 18/août/2005 13:23
par Dr. Dri
plusieurs callback, pourquoi pas ? mais l'intérêt c'est zéro...
avec une callback tu gères toutes les fenêtres concernées et tous les messages que tu veux...

Dri ;)

Publié : jeu. 18/août/2005 14:20
par gansta93
Ah OK, je comprand... merci. :-D

Publié : jeu. 18/août/2005 15:21
par Progi1984
Disons que par exemple, j'ai un callback qui active ceci et un autre qui active cela. Comment faire pour en lancer un des deux ou les deux avec un seul callback ?

Publié : jeu. 18/août/2005 15:38
par Dr. Dri
bah je te ferais un exemple de code dès que j'ai le temps ;)

Dri

Publié : jeu. 18/août/2005 20:33
par Progi1984
Bah Merci Dri ;)

Publié : jeu. 18/août/2005 23:11
par Dr. Dri
voila un exemple avec deux fonctions pour une seule callback...

Code : Tout sélectionner

Declare WindowCallback(WindowID.l, Message.l, wParam.l, lParam.l)

Declare WindowSetMinMaxSize(WindowID.l, MinW.l, MinH.l, MaxW.l, MaxH.l)
Declare WindowLockPosition(WindowID.l, x.w, y.w)

Declare MinMax(Value.l, OldValue.l)

Procedure WindowCallback(WindowID.l, Message.l, wParam.l, lParam.l)
  Protected WndProc.l, Result.l, *MinMax.MinMaxInfo, *Pos.WindowPos, x.l, y.l
  
  If Message = #WM_GETMINMAXINFO
    
    *MinMax = lParam
    *MinMax\ptMinTrackSize\x = MinMax(GetProp_(WindowID, "MinTrackWidth"),  *MinMax\ptMinTrackSize\x)
    *MinMax\ptMinTrackSize\y = MinMax(GetProp_(WindowID, "MinTrackHeight"), *MinMax\ptMinTrackSize\y)
    *MinMax\ptMaxTrackSize\x = MinMax(GetProp_(WindowID, "MaxTrackWidth"),  *MinMax\ptMaxTrackSize\x)
    *MinMax\ptMaxTrackSize\y = MinMax(GetProp_(WindowID, "MaxTrackHeight"), *MinMax\ptMaxTrackSize\y)
    
  ElseIf Message = #WM_WINDOWPOSCHANGING
    
    *Pos = lParam
    x = GetProp_(WindowID, "PositionLockX")
    y = GetProp_(WindowID, "PositionLockY")
    
    If x & $10000 : *Pos\x = x & $FFFF : EndIf
    If y & $10000 : *Pos\y = y & $FFFF : EndIf
    
  EndIf
 
  WndProc = GetProp_(WindowID, "WndProc")
  If WndProc
    Result = CallWindowProc_(WndProc, WindowID, Message, wParam, lParam)
  EndIf
  ProcedureReturn Result
EndProcedure

Procedure WindowSetMinMaxSize(WindowID.l, MinW.l, MinH.l, MaxW.l, MaxH.l)
  Protected WndProc.l
 
  SetProp_(WindowID, "MinTrackWidth",  MinW)
  SetProp_(WindowID, "MinTrackHeight", MinH)
  SetProp_(WindowID, "MaxTrackWidth",  MaxW)
  SetProp_(WindowID, "MaxTrackHeight", MaxH)
 
  If GetProp_(WindowID, "WndProc") = #Null
    WndProc = SetWindowLong_( WindowID, #GWL_WNDPROC, @WindowCallback() )
    SetProp_(WindowID, "WndProc", WndProc)
  EndIf
EndProcedure

Procedure WindowLockPosition(WindowID.l, x.w, y.w)
  Protected WndProc.l, xx.l, yy.l
 
  If x >= 0 : xx = $10000 | x : EndIf
  If y >= 0 : yy = $10000 | y : EndIf
  
  SetProp_(WindowID, "PositionLockX" ,xx)
  SetProp_(WindowID, "PositionLockY" ,yy)
 
  If GetProp_(WindowID, "WndProc") = #Null
    WndProc = SetWindowLong_( WindowID, #GWL_WNDPROC, @WindowCallback() )
    SetProp_(WindowID, "WndProc", WndProc)
  EndIf
EndProcedure

Procedure MinMax(Value.l, OldValue.l)
  If Value <= 0
    Value = OldValue
  EndIf
  ProcedureReturn Value
EndProcedure

OpenWindow(0, 0, 0, 480, 360, #PB_Window_MinimizeGadget|#PB_Window_SizeGadget, "MinMax + Lock")
WindowSetMinMaxSize(WindowID(0), 320, 240, 640, 480)
WindowLockPosition(WindowID(0), #PB_Default, 0)

OpenWindow(1, 400, 100, 400, 300, #PB_Window_MinimizeGadget|#PB_Window_SizeGadget, "Min")
WindowSetMinMaxSize(WindowID(1), 320, 240, #Null, #Null)

Repeat
  Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
si la propriété n'existe pas, la fonction GetProp_() renvoie 0... donc faut s'arranger si c'est un long pour que 0 soit une valeur incorrecte... Ou alors on peut pointer vers une structure LONG si toutes les valeurs sont acceptées...

Dri

Publié : ven. 19/août/2005 8:07
par Progi1984
Ptite question à quoi sert getProp_(), Setprop_() ?