CustomCaptionButtons

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
FGK
Beiträge: 249
Registriert: 09.01.2005 14:02
Computerausstattung: i5-4430 CPU / 8GB RAM
GeForce GT630
Windows 10 Home / 64-bit
Wohnort: Augsburg

CustomCaptionButtons

Beitrag von FGK »

Hallo Leute,

hatte ein bischen Zeit übrig um ein Subclassing für
"CustomCaptionButtons" zu programmieren.
Mit der AddCaptionButton-Funktion könnt ihr zusätzliche
Buttons in der Titelleiste unterbringen. Seht den Source durch -
sollte eigentlich selbsterklärend sein.

Screenshot:

Bild


Source:

Code: Alles auswählen

;****************************************************************************** 
;* Beispiel für zusätzliche Buttons in der Titlebar                           * 
;******************************************************************************
;* Anregung von http://www.catch22.net/tuts/titlebar.asp                      *
;* PB Portierung von FGK     04/2006                                          *
;******************************************************************************                                                                   

#MAX_CAPTION_BUTTONS = 8
#B_EDGE = 2
Global CCPropName.s = "FGK_Subclass"
Dim BM_Handle(#MAX_CAPTION_BUTTONS)

Structure CaptionButton
    lpProc.l        ;Adresse zu der Gesprungen wird!
    nRightBorder.w  ;Pixels between this button and buttons to the right
    hBmp.l          ;Bitmap to display
    fPressed.l      ;Is the button pressed in or out?.
EndStructure    

Structure CustomCaption
    nButtons.CaptionButton[#MAX_CAPTION_BUTTONS+1];
    nNumButtons.w   ;
    fMouseDown.l    ;is the mouse button being clicked?
    wpOldProc.l     ;old window procedure
    iActiveButton.l ;the button index being clicked.
EndStructure
  
Declare.l Caption_NCPaint(*ctp.CustomCaption,hwnd,HRGN)

Procedure DebugRect(*r.RECT)
  With *r
    Debug "ButtonRect..............................."
    Debug "L:"+Str(\left)+ " T:"+Str(\top)+ " R:"+ Str(\right)+ " B:"+Str(\bottom)
    Debug "........................................."
  EndWith
EndProcedure

Procedure.l MakeLong(low,high)
  ProcedureReturn low + (high << 16) 
EndProcedure

Procedure.l HIWORD(Value) 
  ProcedureReturn (Value >> 16) & $FFFF 
EndProcedure 

Procedure.l LOWORD(Value) 
  ProcedureReturn Value & $FFFF 
EndProcedure

Procedure.l GetCustomCaption(hwnd)
  ProcedureReturn GetProp_(hwnd,CCPropName)
EndProcedure

Procedure.l CalcTopEdge(hwnd)
  dwStyle = GetWindowLong_(hwnd, #GWL_STYLE)
  If (dwStyle & #WS_THICKFRAME)
    ProcedureReturn GetSystemMetrics_(#SM_CYSIZEFRAME);
  Else
    ProcedureReturn GetSystemMetrics_(#SM_CYFIXEDFRAME);
  EndIf  
EndProcedure
  
Procedure.l CalcRightEdge(hwnd)
	dwStyle = GetWindowLong_(hwnd, #GWL_STYLE);
  If (dwStyle & #WS_THICKFRAME)
		ProcedureReturn GetSystemMetrics_(#SM_CXSIZEFRAME);
  Else
		ProcedureReturn GetSystemMetrics_(#SM_CXFIXEDFRAME);
  EndIf
EndProcedure    
 
Procedure.l GetRightEdgeOffset(*ctp.CustomCaption,hwnd)

  dwStyle   = GetWindowLong_(hwnd, #GWL_STYLE);
  dwExStyle = GetWindowLong_(hwnd, #GWL_EXSTYLE);

  nButSize = 0;
  nSysButSize.l;

  If(dwExStyle & #WS_EX_TOOLWINDOW)
    nSysButSize = GetSystemMetrics_(#SM_CXSMSIZE) - #B_EDGE;
    If(dwStyle & #WS_SYSMENU)	
        nButSize + nSysButSize + #B_EDGE;
        ProcedureReturn nButSize + CalcRightEdge(hwnd);
    EndIf  
  Else
    nSysButSize = GetSystemMetrics_(#SM_CXSIZE) - #B_EDGE;
    
		;// Window has close [x] button. This button has a 2-pixel
		;// border on either Size
		If(dwStyle & #WS_SYSMENU)	
			nButSize + nSysButSize + #B_EDGE;
    EndIf
    If(dwStyle & (#WS_MINIMIZEBOX | #WS_MAXIMIZEBOX) )
      nButSize + #B_EDGE + nSysButSize * 2
    EndIf  
    ;// a Window can have a question-mark button, but only
    ;// If it doesn't have any min/max buttons
    If(dwExStyle & #WS_EX_CONTEXTHELP)
      nButSize + #B_EDGE + nSysButSize;
    EndIf
    ;// Now calculate the Size of the border...aggghh!
    ProcedureReturn nButSize + CalcRightEdge(hwnd);
  EndIf
EndProcedure

Procedure CenterBitmap(hdc,*rc.RECT,hBitmap.l)
; //
; //	If hBitmap is MONOCHROME then 
  ; //	whites will be drawn transparently, 
  ; //  blacks will be drawn normally
  ; //	So, it will look just like a caption button
  ; //
  ; //	If hBitmap is > 2 colours, then no transparent
    ; //  drawing will take place....i.e. DIY!
    ; //
    ; {
    bm.BITMAP
    rcDest.RECT   
    With rcDest
      \left = *rc\left
      \top  = *rc\top
      \right = *rc\right
      \bottom = *rc\bottom
    EndWith
    
    p.POINT ;
    ; int cx;
    ; int cy;   
    ; hdc memdc;
    ; hBitmap hOldBM;
    ; Size  delta;
    colorOld.l  ;COLORREF
    ; 
    If hBitmap 
     
      ; // center BITMAP in caller's rectangle   
      GetObject_(hBitmap, SizeOf(bm),bm)   
      cx = bm\bmWidth
      cy = bm\bmHeight
      delta_cx = (*rc\right-*rc\left - cx) / 2;
      delta_cy = (*rc\bottom-*rc\top - cy) / 2;
      If *rc\right-*rc\left > cx
         SetRect_(rcDest, *rc\left+delta_cx, *rc\top + delta_cy, 0, 0);   
         rcDest\right = rcDest\left + cx;
         rcDest\bottom = rcDest\top + cy;
         p\x = 0;
         p\y = 0;
      Else
        p\x = -delta_cx;   
        p\y = -delta_cy;
      EndIf 
      ; // Select checkmark into memory DC
      memdc = CreateCompatibleDC_(hdc)
      hOldBM = SelectObject_(memdc, hBitmap)
      colorOld = SetBkColor_(hdc, GetSysColor_(#COLOR_3DFACE))
      BitBlt_(hdc, rcDest\left, rcDest\top, rcDest\right-rcDest\left, rcDest\bottom-rcDest\top, memdc, p\x, p\y, #SRCCOPY)
      SetBkColor_(hdc, colorOld)
      SelectObject_(memdc, hOldBM)
      DeleteDC_(memdc)
    EndIf
EndProcedure          
  
Procedure GetButtonRect(*ctp.CustomCaption,hwnd,idx,*rect.RECT,fWindowRelative.l)
    If(GetWindowLong_(hwnd, #GWL_EXSTYLE) & #WS_EX_TOOLWINDOW)
      cxBut = GetSystemMetrics_(#SM_CXSMSIZE);
      cyBut = GetSystemMetrics_(#SM_CYSMSIZE);
    Else
      cxBut = GetSystemMetrics_(#SM_CXSIZE);
      cyBut = GetSystemMetrics_(#SM_CYSIZE);
    EndIf
    re_start = GetRightEdgeOffset(*ctp, hwnd);
    GetWindowRect_(hwnd, *rect);
    If(fWindowRelative)=#True
      OffsetRect_(*rect, -*rect\left, -*rect\top);
    EndIf  
    For i = 0 To idx
      re_start+*ctp\nButtons[i]\nRightBorder + cxBut - #B_EDGE
    Next
    With *rect
          \left   = \right  - re_start
          \top    = \top  + CalcTopEdge(hwnd) +  #B_EDGE
          \right  = \left + cxBut - #B_EDGE
          \bottom = \top  + cyBut - #B_EDGE*2
    EndWith
EndProcedure
      
Procedure RedrawNC(hwnd)
    SetWindowPos_(hwnd, 0, 0, 0, 0, 0, #SWP_NOMOVE | #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE | #SWP_DRAWFRAME)
EndProcedure

Procedure.l Caption_Wrapper(*ctp.CustomCaption,hwnd,msg,wParam,lParam)
  ;//
  ;//	This is a generic message handler used by WM_SETTEXT And WM_NCACTIVATE.
  ;//	It works by turning off the WS_VISIBLE style, calling
  ;//	the original window Procedure, then turning WS_VISIBLE back on.
  ;//
  ;//	This prevents the original wndproc from redrawing the caption.
  ;//	Last of all, we paint the caption ourselves With the inserted buttons
  
  ret.l
  dwStyle.l
  
  dwStyle = GetWindowLong_(hwnd, #GWL_STYLE);
  
  ;//Turn off WS_VISIBLE, so that WM_NCACTIVATE does Not
  ;//paint our window caption...
  SetWindowLong_(hwnd, #GWL_STYLE, dwStyle & ~#WS_VISIBLE);
  ;//Do the Default thing..
  ret = CallWindowProc_(*ctp\wpOldProc, hwnd, msg, wParam, lParam)
  ;//Restore the original style
  SetWindowLong_(hwnd, #GWL_STYLE, dwStyle)
  ;//paint the whole window frame + caption
  Caption_NCPaint(*ctp, hwnd,1);
  ProcedureReturn ret;
EndProcedure

Procedure.l Caption_NCLButtonDown(*ctp.CustomCaption,hwnd,msg,wParam,lParam)
 rect.RECT
 pt.POINT  
 If wParam = #HTBORDER
    pt\x = LOWORD(lParam);
    pt\y = HIWORD(lParam);
    For i = 0 To *ctp\nNumButtons-1
      GetButtonRect(*ctp, hwnd, i, @rect, #False)
      InflateRect_(rect, 0,#B_EDGE)
       If PtInRect_(rect, pt\x,pt\y)
          With *ctp
             \iActiveButton = i
             \nButtons[i]\fPressed = #True
             \fMouseDown = #True
          EndWith  
          RedrawNC(hwnd)
          ProcedureReturn 0
       EndIf
    Next 
 EndIf 
 ProcedureReturn CallWindowProc_(*ctp\wpOldProc, hwnd, msg, wParam, lParam)
EndProcedure

Procedure.l Caption_LButtonUp(*ctp.CustomCaption ,hwnd,wParam,lParam)
 rect. rect
 pt.POINT 
 pt\x = LOWORD(lParam)
 pt\y = HIWORD(lParam)
  ;ClientToScreen_(hwnd, pt)

 If *ctp\fMouseDown
  GetButtonRect(*ctp, hwnd, *ctp\iActiveButton,@rect, #False);
  InflateRect_(rect, 0, 2);
  If PtInRect_(rect, pt\x,pt\y)
    *lpProc = *ctp\nButtons[*ctp\iActiveButton]\lpProc
  EndIf
  *ctp\nButtons[*ctp\iActiveButton]\fPressed = #False
  *ctp\fMouseDown = #False
  RedrawNC(hwnd)
  If *lpProc 
    CallFunctionFast(*lpProc)
  EndIf  
  ProcedureReturn 0
EndIf  
ProcedureReturn CallWindowProc_(*ctp\wpOldProc, hwnd, #WM_LBUTTONUP, wParam, lParam)
EndProcedure      

Procedure.l Caption_MouseMove(*ctp.CustomCaption,hwnd,wParam,lParam)
rect.RECT
pt.POINT 
fPressed.l
pt\x =LOWORD(lParam)
pt\y =HIWORD(lParam)
ClientToScreen_(hwnd, pt)
If *ctp\fMouseDown
  GetButtonRect(*ctp, hwnd, *ctp\iActiveButton,@rect, #False)
  InflateRect_(rect, 0, 2)
  fPressed = PtInRect_(rect, pt\x,pt\y)
  If Not(fPressed) = *ctp\nButtons[*ctp\iActiveButton]\fPressed
    *ctp\nButtons[*ctp\iActiveButton]\fPressed =#False
    RedrawNC(hwnd);
	EndIf
  ProcedureReturn 0;
EndIf
ProcedureReturn CallWindowProc_(*ctp\wpOldProc, hwnd, #WM_MOUSEMOVE, wParam, lParam)
EndProcedure
    
Procedure.l Caption_NCActivate(*ctp.CustomCaption ,hwnd,wParam,lParam)
  ProcedureReturn Caption_Wrapper(*ctp, hwnd, #WM_NCACTIVATE, wParam, lParam)
EndProcedure

Procedure.l Caption_SetText(*ctp.CustomCaption ,hwnd,wParam,lParam)
  ProcedureReturn Caption_Wrapper(*ctp, hwnd, #WM_SETTEXT, wParam, lParam)
EndProcedure

Procedure.l Caption_NCPaint(*ctp.CustomCaption,hwnd,HRGN)
  rect.RECT
  rect1.RECT
	fRegionOwner.l = #False;
	i.l
	hdc.l
	uButType.l;
	hrgn1.l
  ; 
	GetWindowRect_(hwnd, @rect)
   
	x = rect\left;
	y = rect\top;
  ; 
  If(HRGN = 1 );|| HRGN = 0)
    HRGN = CreateRectRgnIndirect_(rect)
    fRegionOwner = #True;
  EndIf
  For i = 0 To *ctp\nNumButtons-1
    GetButtonRect(*ctp, hwnd, i, @rect1, #False)
    hrgn1 = CreateRectRgnIndirect_(rect1)
    CombineRgn_(HRGN, HRGN, hrgn1, #RGN_XOR)
    DeleteObject_(hrgn1);
  Next i
  
  CallWindowProc_(*ctp\wpOldProc, hwnd, #WM_NCPAINT, HRGN, 0)
  
  hdc = GetWindowDC_(hwnd);
  For i = 0 To *ctp\nNumButtons-1
    GetButtonRect(*ctp, hwnd, i, @rect1, #True)
    uButType = #DFCS_BUTTONPUSH;
      If *ctp\nButtons[i]\fPressed
        DrawFrameControl_(hdc, rect1, #DFC_BUTTON,  uButType | #DFCS_PUSHED)
      Else
        DrawFrameControl_(hdc, rect1, #DFC_BUTTON, uButType)
      EndIf
      If *ctp\nButtons[i]\hBmp
        InflateRect_(rect1, -2, -2);
        rect1\right-1
        rect1\bottom-1
        If *ctp\nButtons[i]\fPressed
          OffsetRect_(rect1, 1, 1)
        EndIf
        CenterBitmap(hdc, rect1, *ctp\nButtons[i]\hBmp)
      EndIf
  Next i 
  ReleaseDC_(hwnd, hdc);
  If(fRegionOwner)
   DeleteObject_(HRGN);
  EndIf 
  ProcedureReturn 0;
EndProcedure 

Procedure.l Caption_NCHitTest(*ctp.CustomCaption ,hwnd, wParam,lParam)
	rect.RECT
	pt.POINT
	i.l
	ret.l
  ret = CallWindowProc_(*ctp\wpOldProc, hwnd, #WM_NCHITTEST, wParam, lParam);
  If ret = #HTCAPTION
    pt\x = LOWORD(lParam)
    pt\y = HIWORD(lParam)
    For i = 0 To *ctp\nNumButtons-1
        GetButtonRect(*ctp, hwnd, i, @rect, #False);
        InflateRect_(rect, 0, #B_EDGE);
        If PtInRect_(rect, pt\x,pt\y)
          ;Debug "Button "+Str(i)
          ProcedureReturn #HTBORDER
        EndIf
    Next 
  EndIf
  ProcedureReturn ret
EndProcedure
         
Procedure.l CustomCaptionProc(hwnd,msg,wParam,lParam) 
    *ctp.CustomCaption = GetCustomCaption(hwnd)
    Select msg 
      Case #WM_NCDESTROY 
        SetWindowLong_(hwnd,#GWL_WNDPROC,*ctp\wpOldProc) 
        FreeMemory(*ctp)
       ProcedureReturn
      Case #WM_NCHITTEST 
        ProcedureReturn Caption_NCHitTest(*ctp,hwnd, wParam,lParam)
      Case #WM_NCACTIVATE
        ProcedureReturn Caption_NCActivate(*ctp, hwnd, wParam, lParam)
      Case #WM_SETTEXT
        ProcedureReturn Caption_SetText(*ctp, hwnd, wParam, lParam)
      Case #WM_NCPAINT
        ProcedureReturn Caption_NCPaint(*ctp, hwnd, wParam)
      Case #WM_NCLBUTTONDOWN
        ProcedureReturn Caption_NCLButtonDown(*ctp, hwnd, msg, wParam, lParam)
      Case #WM_NCLBUTTONUP
        ProcedureReturn Caption_LButtonUp(*ctp, hwnd, wParam, lParam)
      Case #WM_MOUSEMOVE
        ProcedureReturn Caption_MouseMove(*ctp, hwnd, wParam, lParam)
    EndSelect 
    ProcedureReturn CallWindowProc_(*ctp\wpOldProc,hwnd,msg,wParam,lParam)
EndProcedure 

Procedure.l AddCaptionButton(hwnd.l,*Proc.l,nBorder.l,hBmp.l)
*ctp.CustomCaption = GetCustomCaption(hwnd)
Result=#False
If *ctp=0                                           ;Erster Button? Dann Speicher holen und init!
  *ctp=AllocateMemory(SizeOf(CustomCaption))
  If *ctp
    With *ctp
      \nNumButtons=0
      \fMouseDown = #False
      \wpOldProc=SetWindowLong_(hwnd,#GWL_WNDPROC,@CustomCaptionProc()) 
    EndWith    
    SetProp_(hwnd,CCPropName,*ctp)
    Result=#True
  EndIf
EndIf
  With *ctp
    idx = \nNumButtons
    If idx<#MAX_CAPTION_BUTTONS
      \nButtons[idx]\hBmp = hBmp
      \nButtons[idx]\nRightBorder = nBorder
      \nButtons[idx]\lpProc = *Proc
      \nButtons[idx]\fPressed = fPressed
      \nNumButtons+1
      Result=#True
    EndIf
  EndWith
  RedrawNC(hwnd)
  ProcedureReturn Result
EndProcedure

Procedure.l RemoveCaptionButton(hwnd.l)

  *ctp.CustomCaption  = GetCustomCaption(hwnd)
  If(*ctp = 0)
    ProcedureReturn #False
  EndIf
	If *ctp\nNumButtons > 0
		*ctp\nNumButtons-1
		RedrawNC(hwnd)
  EndIf
  ProcedureReturn #True
EndProcedure

Procedure.l DummyProc_0()
  MessageRequester("CustomCaption","CaptionButton 1")
EndProcedure

Procedure.l DummyProc_1()
  MessageRequester("CustomCaption","CaptionButton 2")
EndProcedure

Procedure.l DummyProc_2()
  MessageRequester("CustomCaption","CaptionButton 3")
EndProcedure

*hwnd=OpenWindow(0,100,300,400,200,"Testfenster",#PB_Window_SystemMenu|#PB_Window_MaximizeGadget) 
  
  If CreateGadgetList(WindowID(0))
    ButtonGadget  (1, 10, 10, 200, 20, "Click me")
    CheckBoxGadget(2, 10, 40, 200, 20, "Check me")
  EndIf

  BM_Handle(0)=Loadbitmap_(0,#OBM_CHECK)
  AddCaptionButton(WindowID(0),@DummyProc_0(),2,BM_Handle(0))
  BM_Handle(1)=Loadbitmap_(0,#OBM_LFARROW)
  AddCaptionButton(WindowID(0),@DummyProc_1(),2,BM_Handle(1))
  BM_Handle(2)=Loadbitmap_(0,#OBM_COMBO	)
  AddCaptionButton(WindowID(0),@DummyProc_2(),2,BM_Handle(2))
  

  Repeat 
  Event = WaitWindowEvent()
    Delay(1)
  Until Event = #PB_Event_CloseWindow

  For i = 0 To #MAX_CAPTION_BUTTONS
    If BM_Handle(i)>0
      RemoveCaptionButton(WindowID(0))
      DeleteObject_(BM_Handle(i))
    EndIf  
  Next i
  
End

Hoffe es kann jemand brauchen

Grüße

FGK
Benutzeravatar
Leonhard
Beiträge: 602
Registriert: 01.03.2006 21:25

Beitrag von Leonhard »

Suuper :mrgreen: .

Klappt das auch mit Window-XP-Style (ich mein das Fenster)?
Benutzeravatar
Arrag0n
Beiträge: 32
Registriert: 24.06.2005 20:49
Wohnort: Austria
Kontaktdaten:

Beitrag von Arrag0n »

@FGK Suuper Sache :allright:

Mit dem Original WindowsXP-Style kommt es nicht ganz zurecht...
Aber trotzedem weiss ich schon eine Anwendung.
Danke

Arrag0n
Aus den Steinen, die einem in den Weg gelegt werden, kann man Schönes bauen.
Antworten