Author Topic: [SDK] 02 - Take control of your window(s) [SUPERCLASS]  (Read 6049 times)

0 Members and 1 Guest are viewing this topic.

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2022
    • www.zapsolution.com
[SDK] 02 - Take control of your window(s) [SUPERCLASS]
« on: August 07, 2007, 11:11:59 PM »
WORK IN PROGRESS
This is the second post of a serie, where I shall try to explain how to take complete control over SDI window.

1 - SUPERCLASS.
This is close to the oop concept where a new class can inherit from the properties of another one, to take advange of its existing features, while adding new properties or changing existing behaviors.

In this example we will create a new "ZBUTIMAGE" button class, that inherits from the default "BUTTON" class. 

Code: [Select]
'// Create an image button control
FUNCTION zButImage (BYVAL hOwner AS LONG, zFullpathImageName AS ASCIIZ, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL ButID AS LONG) AS LONG
    LOCAL wc  AS WNDCLASSEX
    LOCAL zClass AS ASCIIZ * 10
    LOCAL hBut, IsInitialized AS LONG

    zClass = "ZBUTIMAGE"
    wc.cbSize = SIZEOF(wc)
    IsInitialized = GetClassInfoEx(zInstance, zClass, wc)
    IF IsInitialized = 0 THEN
       IsInitialized = GetClassInfoEx(%NULL, "BUTTON", wc)
       IF IsInitialized THEN
          CALL zButOldProc(wc.lpfnWndProc, 1)
          CALL zButOldExtra(wc.cbWndExtra, 1)
          wc.lpfnWndProc   = CODEPTR(zImageButProc)
          '// %EXTEND_EXTRA
          '// We reserve %EXTEND_EXTRA bytes to easily store pivate properties
          '// that can be retrieved using: zSetButProperty and zGetButProperty
          wc.cbWndExtra    = wc.cbWndExtra + (%EXTEND_EXTRA * 4)
          wc.hInstance     = zInstance
          wc.lpszClassName = VARPTR(zClass)
          IsInitialized =  RegisterClassEx(wc)
       END IF
    END IF

    IF IsInitialized THEN
       LOCAL Img AS LONG
'      // Create GDIPLUS image from file
       Img = zCreateImageFromFile(zFullpathImageName)
       IF Img THEN
          LOCAL btW, btH, imgW, imgH AS LONG
'         // Get the GDIPLUS image size
          CALL zGetImageSize(Img, imgW, imgH)
          btW = imgW \ 5: btH = imgH
          Style& = %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP ' OR %BS_OWNERDRAW
          hBut = CreateWindowEx(%WS_EX_TRANSPARENT, zClass, "", Style&, x, y, btW, btH, hOwner, ButID, zInstance, BYVAL %NULL)
          IF hBut THEN
'            // Save Img handle as a property
             CALL zSetButProperty(hBut, %IMAGE_EXTRA, Img)
             FUNCTION = hBut
          ELSE
'            // Delete image
             CALL zDisposeImage(Img)
          END IF
       END IF
    END IF
END FUNCTION

Note that we extend the size of the wc.cbWndExtra member in order to use it to store private properties for each new ZBUTIMAGE.

Code: [Select]
'// Get button's property.
FUNCTION zGetButProperty (BYVAL hWnd AS LONG, BYVAL Item AS LONG) AS LONG
    IF Item > 0 AND Item < %EXTEND_EXTRA + 1 AND IsWindow(hWnd) THEN
       FUNCTION = GetWindowLong(hWnd, (zButOldExtra(0, 0) + Item - 1) * 4)
    END IF
END FUNCTION

'// Set button's property.
SUB zSetButProperty (BYVAL hWnd AS LONG, BYVAL Item AS LONG, BYVAL V AS LONG)
    IF Item > 0 AND Item < %EXTEND_EXTRA + 1 AND IsWindow(hWnd) THEN
       CALL SetWindowLong(hWnd, (zButOldExtra(0, 0) + Item - 1) * 4, V)
    END IF
END SUB

2 - FIVE-STATE button.
Also because the ultimate purpose of this project is to show you how to create a complete skinned window, we use PNG files to customize the look of the button with the help of GDIPLUS.

Note: that each of the button uses 5 states like this one

State 1 - Normal
State 2 - Pressed
State 3 - Disabled
State 4 - Focus
State 5 - Hover

3 - DOUBLE BUFFER and WM_PRINT message.
Most programmers do not handle the WM_PRINT and WM_PRINCLIENT messages, however they are very powerful, and we use them to paint the control in our private DC, aka DOUBLE BUFFER, to boost the display and suppress flickering while moving child controls during resize of the main window.

'// Double buffer
Code: [Select]
FUNCTION zPaintToScreen (BYVAL hWnd AS LONG, BYVAL hDC AS LONG, BYVAL X AS LONG, BYVAL Y AS LONG, BYVAL Action AS LONG) AS LONG
    LOCAL rc AS RECT
    LOCAL bmW, bmH, ExistBitmap, hDCTemp, hPaintBitmap AS LONG
   
    CALL GetClientRect(hWnd, rc)
    IF Action THEN
     ' Create OFF screen bitmap to avoid flickering and allow smooth display
     ' ---------------------------------------------------------------------
       ExistBitmap = zGetPaintBitmap(hWnd)
       IF ExistBitmap THEN
          CALL zGetBitmapSize(ExistBitmap, bmW, bmH)
          IF MAX&(rc.nRight, X) < bmW + 1 AND MAX&(rc.nBottom, Y) < bmH + 1 THEN
             FUNCTION = zGetPaintDC(hWnd): EXIT FUNCTION
          END IF
          CALL zDeleteObject(ExistBitmap)
          CALL DeleteDC(zGetPaintDC(hWnd))
       END IF
       hDCTemp = CreateCompatibleDC(hDC)

       hPaintBitmap = zCreateDIBSection(hDC, MAX&(rc.nRight, X), MAX&(rc.nBottom, Y), 24)

       CALL SelectObject(hDCTemp, hPaintBitmap)
       CALL zSetProperty(hWnd, %Z_PaintDC, hDCTemp)
       CALL zSetProperty(hWnd, %Z_PaintBitmap, hPaintBitmap)
       FUNCTION = hDCTemp
    ELSE
     ' End of OFF screen drawing, fast BitBlt to the target display Window
     ' -------------------------------------------------------------------
     ' Draw final result to the target window
       CALL BitBlt(hDC, X, Y, rc.nRight, rc.nBottom, zGetPaintDC(hWnd), 0, 0, %SRCCOPY)
    END IF

END FUNCTION

CALL SendMessage(hMain, %WM_PRINT, hDCmem, lParam&)
Code: [Select]
'// Draw our custom background there
SUB zDrawBackground()
    LOCAL hMain AS LONG, hIC AS LONG, hDCmem AS LONG, rc AS RECT
    hMain = zMainWindow(0)
    CALL GetClientRect(hMain, rc)
    hIC = zDisplayDC()
    hDCmem = zPaintToScreen(hMain, hIC, rc.nRight, rc.nBottom, 1)
    CALL zFillRect(hDCmem, 0, 0, rc.nRight, rc.nBottom, RGB(27,29,41))
    CALL DrawEdge(hDCmem, rc, %EDGE_RAISED, %BF_RECT)

'   // WP_PRINT and WM_PRINCLIENT is very valuable for double buffering technic
    'lParam& = %PRF_CLIENT OR %PRF_CHILDREN OR %PRF_NONCLIENT OR %PRF_ERASEBKGND OR %PRF_CHECKVISIBLE OR %PRF_OWNED
    lParam& = %PRF_CLIENT OR %PRF_CHILDREN OR %PRF_NONCLIENT OR %PRF_CHECKVISIBLE 'OR %PRF_OWNED
    CALL SendMessage(hMain, %WM_PRINT, hDCmem, lParam&)

    CALL DeleteDC(hIC)
END SUB

CASE %WM_PAINT, %WM_PRINT
Code: [Select]
    CASE %WM_PAINT, %WM_PRINT
         Img = zGetButProperty(hWnd, %IMAGE_EXTRA)
         IF Img THEN
            LOCAL ps AS PAINTSTRUCT
            IF Msg = %WM_PAINT THEN
               hDC = BeginPaint(hWnd, ps)
            ELSE ' // WM_PRINT
               hDC = wParam
            END IF
            IF GdipCreateFromHDC(hDC, graphics) = 0 THEN
               CALL GetWindowRect(hWnd, rw)
               CALL GetClientRect(hWnd, rc)
               lp.x = rw.nLeft: lp.y = rw.nTop
               CALL ScreenToClient(GetParent(hWnd), lp)
               CALL zGetImageSize(Img, ImgW, ImgH)
               ImgW = ImgW \ 5
               IF IsWindowEnabled(hWnd) THEN
                  UseState = 1: IF hWnd = GetFocus() THEN UseState = 4
                  CALL GetCursorPos(lp)
                  CALL ScreenToClient(hWnd, lp)
                  IF PtInRect(rc, lp.x, lp.y) THEN
                     IF zIsLButtonDown() THEN
                        IF hWnd = GetFocus() THEN
                           IF GetCursor() = LoadCursor(%NULL, BYVAL %IDC_ARROW) THEN UseState = 2
                        END IF
                     ELSE
                        UseState = 5
                     END IF
                  END IF
               ELSE
                  UseState = 3
               END IF
               CALL GdipDrawImageRectRectI(graphics, Img, _
                                           0, 0, ImgW, ImgH, _
                                           ImgW * UseState - ImgW, 0, ImgW, ImgH, %UnitPixel, _
                                           ImgAttr)
               CALL GdipDeleteGraphics(graphics)
            END IF
            IF Msg = %WM_PAINT THEN
               CALL EndPaint(hWnd, ps)
            END IF

            FUNCTION = 0: EXIT FUNCTION
         END IF

So far the window itself doesn't do much, however it has everything to resize, drag, iconize, maximize, restore, close.

Next step:
we will add the caption bar, and a new SUPERCLASS for PUSBUTTON and perhaps ...
start skinning the form (borders)  :)

To be continued...

Patrice Terrier
www.zapsolution.com



« Last Edit: November 15, 2013, 06:34:15 PM by Patrice Terrier »
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Offline Patrice Terrier

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2022
    • www.zapsolution.com
Re: [SDK] 02 - Take control of your window(s) [SUPERCLASS]
« Reply #1 on: August 10, 2011, 06:20:27 PM »
The corrupted ZIP file attached to the first post of this thread has been removed (because of the "Server Collapse").

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com