Announcement

Collapse
No announcement yet.

Drawing a specially painted button

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Drawing a specially painted button

    Hi All

    I have cobbled up a program which has a specially painted button, which will only look nice ONLY AFTER we hover our mouse
    over it, as its rectangle frame is not painted at the initial stage.

    How do I get the button's rectangular frame to be displayed at the start of the program run?
    Thanks in advance!

    The caption of the button is "Add countries to combobox"

    Code:
    'Combobox Test.bas
    ' https://forum.powerbasic.com/forum/user-to-user-discussions/programming/797495-can-we-append-arrays-using-array-assign-statements?p=797505#post797505
     ' Thanks to Dave Biggs
    
    #DEBUG DISPLAY ON       ' < especially useful while developing apps that use arrays !!
    #COMPILE EXE
    #INCLUDE "win32api.inc"
    
    
    #RESOURCE MANIFEST, 1, "XPTheme.xml"
    '
    
       GLOBAL hBtnAdd , hDlg AS DWORD
       GLOBAL CountryArr() AS STRING
    
       %Idc_Cmb = 100
       %Idc_Lab = 108
       %Idc_BtnAdd = 110
    
    '=================================
       FUNCTION PBMAIN() AS LONG
          LOCAL hfb AS DWORD
          LOCAL UCbuttonChar AS WSTRING
    
    
          FONT NEW "Fbold" ,12,1 TO hfb
          DIALOG NEW PIXELS, 0, "Please select a Country", _
                 300, 300, 500, 300, %WS_SYSMENU, 0 TO hDlg
          DIALOG SET COLOR  hDlg, %RGB_MEDIUMBLUE , %RGB_AZURE
    
          CONTROL ADD LABEL , hDlg, %Idc_Lab,"Country  ", 30,90, 60, 20 ,  %SS_CENTER
          CONTROL SET COLOR  hDlg, %Idc_Lab, %RGB_DARKBLUE , %RGB_AZURE
          CONTROL SET FONT hDlg,%Idc_Lab, hfb
    
        ' create a label as a button
          UCbuttonChar = CHR$$(1002)
          CONTROL ADD LABEL, hDlg, %Idc_BtnAdd, "Add countries to combobox    "+ _
             UCbuttonChar,30,18,170,18, %SS_CENTER OR %SS_CENTERIMAGE
          CONTROL SET COLOR  hDlg, %Idc_BtnAdd, %RGB_MAGENTA , %RGB_CORNSILK
          hBtnAdd = GetDlgItem(hDlg, %Idc_BtnAdd)
    
          DIALOG SHOW MODAL hDlg CALL DlgProc
          FONT END hfb
       END FUNCTION
    
    
    
    
    
    
     '===============================================
       CALLBACK FUNCTION DlgProc() AS LONG
         LOCAL  cbTxt AS STRING
         LOCAL cbItem AS LONG
    
          SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                    BuildCarr
                    ' Add ComboBox complete with array..    vvvvvvvvvvv
                    CONTROL ADD COMBOBOX, CBHNDL, %Idc_Cmb, CountryArr() ,_
                     120, 90, 175, 225, %CBS_DROPDOWN OR %WS_VSCROLL
    
    
             CASE %WM_LBUTTONDOWN, %WM_LBUTTONUP, %WM_MOUSEMOVE
                 ' when the add button is clicked or mouse hover it
                   PaintButton(CBMSG)
    
    
            CASE  %WM_COMMAND
    
                 SELECT CASE CB.CTL
                  CASE %Idc_Cmb
                    IF CB.CTLMSG = %CBN_SELENDOK THEN
                       COMBOBOX GET TEXT   CBHNDL, %Idc_Cmb TO cbTxt
                       COMBOBOX GET SELECT CBHNDL, %Idc_Cmb TO cbItem
                       ? " selected : " + cbTxt,,"Item" + STR$(cbItem)
                    END IF
    
                 END SELECT
          END SELECT
    
       END FUNCTION
    
    
    '================
    ' build the array for the combobox
    ' Note that Array Assign starts with LBound(Array())
    ' ie FIRST Element, not neccessarily 'element 0'
    SUB BuildCarr
       LOCAL jj AS LONG
       REDIM CountryArr(0 TO 11)            ' Create array with 12 elements
       ARRAY ASSIGN CountryArr()="Cabo Verde", "Cambodia" , "Cameroon" , "Canada", "CAR" , _
          "Cayman Islands" , "Chad" , "Chile", "Colombia", "Comoros" , "Congo", "Costa Rica"
    END SUB
    
    
    
    '================
    ' Add more countries into the combobox
    ' Note that Array Assign starts with LBound(Array())
      ' ie FIRST Element, not neccessarily 'element 0'
    SUB BuildAddCarr
     ' retain the old countries name
     ' Change array to 22 elements
       REDIM PRESERVE CountryArr(0 TO 21)
     ' fill additional items
     ' NB DIM ..AT.. creates an 'absolute array'
       DIM AddOn(12 TO 21) AS STRING AT VARPTR(CountryArr(12))
        ARRAY ASSIGN AddOn()= "c13","c14","c15","c16","c17","c18","c19","c20","c21","c22"
       CONTROL KILL hDlg, %Idc_Cmb
    
       CONTROL ADD COMBOBOX, hDlg, %Idc_Cmb, CountryArr(), _
         120, 90, 175, 225, %CBS_DROPDOWN OR %WS_VSCROLL
    
    END SUB
    
    
    
    '======================================
    ' Repaint button and test whether
    ' button is clicked or not
    FUNCTION PaintButton(BYVAL wMsg AS DWORD) AS LONG
      LOCAL Pt AS POINTAPI, Rc AS RECT, Result AS DWORD, hDc AS DWORD
      STATIC OnButton AS DWORD, MouseDown AS DWORD
    
    
    
      GetCursorPos(Pt)
      ScreenToClient hDlg, Pt
      Result = ChildWindowFromPoint (hDlg, BYVAL Pt)
      hDc = GetDc (hBtnAdd)
      GetClientRect(hBtnAdd,Rc)
      IF (wMsg = %WM_LBUTTONDOWN) OR (wMsg = %WM_LBUTTONUP) THEN
          OnButton = 0
      END IF
      IF hBtnAdd = Result THEN
        IF OnButton = 0 THEN
          OnButton = 1
          IF wMsg = %WM_LBUTTONDOWN THEN
       '       when left mouse button is clicked
               DrawEdge(hdc, Rc, %BDR_SUNKENINNER OR %BDR_SUNKENOUTER, %BF_RECT)
           '   add more countries to combobox
               BuildAddCarr
          ELSE
            DrawEdge(hdc, Rc, %BDR_RAISEDINNER OR %BDR_RAISEDOUTER, %BF_RECT)
          END IF
        END IF
      ELSE
        IF OnButton = 1 THEN
            OnButton = 0
            InValidateRect hBtnAdd, BYVAL 0, BYVAL 0
        END IF
    
      END IF
      ReleaseDc hBtnAdd, hDc
      ' Draws the outer rectangle frame around the button
      paint_ButtonRectFrame  hBtnAdd
    END FUNCTION
    
    
    
    '===================================
     ' Routine to paint the Button rect frame
     ' to make it look stunning
     SUB paint_ButtonRectFrame( hBtn AS DWORD)
    
             LOCAL hdgBrush, hdgPen AS DWORD
             LOCAL rcdg AS RECT, psdg AS PAINTSTRUCT
             ' get button and statusbar height to "frame"
              GetClientRect hBtn, rcdg
    
              BeginPaint hBtn, psdg
               '  create pen with desired color       ' cyan , magenta and yellow are ok
               '  the pen width will increase the width of the border
                  hdgPen   = CreatePen(%PS_SOLID, 2, %RGB_CYAN)
                ' select pen into control's dc
                  hdgPen   = SelectObject(psdg.hDc, hdgPen)
                 'prepare for hollow rect
                  hdgBrush = SelectObject(psdg.hDc, GetStockObject(%NULL_BRUSH))
    
                  ' frame button  area and draw its rectangle
                  ' slightly smaller than the client area
                  Rectangle psdg.hDC, rcdg.nLeft+1  , rcdg.nTop+1 , _
                                      rcdg.nRight-1  , rcdg.nBottom-1
    
                 'return original brush
                  SelectObject psdg.hDc, hdgBrush
                '  return original pen and delete the one we created
                  DeleteObject SelectObject(psdg.hDc, hdgPen)
              EndPaint hBtn, psdg
    
     END SUB

  • #2
    Code:
      IF hBtnAdd = Result THEN
        IF OnButton = 0 THEN
          OnButton = 1
          IF wMsg = %WM_LBUTTONDOWN THEN
       '       when left mouse button is clicked
               DrawEdge(hdc, Rc, %BDR_SUNKENINNER OR %BDR_SUNKENOUTER, %BF_RECT)
           '   add more countries to combobox
               BuildAddCarr
          ELSE
            DrawEdge(hdc, Rc, %BDR_RAISEDINNER OR %BDR_RAISEDOUTER, %BF_RECT)
            '<<<<<< Draw the rest of the button here
          END IF
        END IF
      ELSE
        IF OnButton = 1 THEN
            OnButton = 0
            InValidateRect hBtnAdd, BYVAL 0, BYVAL 0
        END IF
    or

    draw the rest of the button in paint_ButtonRectFrame

    Comment


    • #3
      Thanks Jim, still not able to draw the frame even at CASE %WM_INITDIALOG

      Comment


      • #4
        I see what you mean. How did they do it in the example links I gave you? At least the button is being painted with corn silk an magenta text.

        Perhaps a %WM_PAINT is where you need to concentrate your general purpose painting of the button/label control. I did this ...

        Code:
                CASE %WM_LBUTTONDOWN, %WM_LBUTTONUP, %WM_MOUSEMOVE
                     ' when the add button is clicked or mouse hover it
                       PaintButton(CBMSG)
        
                CASE %WM_PAINT
                    PaintButton(CBMSG)
        and was able to display the button's rectangular frame at the start; however, it lost the text so you will have to paint the text yourself in the paint routine.

        DrawText is your friend.

        Comment


        • #5
          Thanks Jim, I got another idea by drawing a box over the button label and now it looks good
          Here's the initial display before moving a mouse over the button label ....


          Click image for larger version

Name:	Initial combo.png
Views:	87
Size:	3.0 KB
ID:	797531

          Comment


          • #6
            here's the screen shot of the label button after the mouse had hover over the label button

            Click image for larger version

Name:	After mouse hover.png
Views:	86
Size:	4.7 KB
ID:	797533

            Looks cool right?

            Comment


            • #7
              here's the program code for any one who wants to improve on it

              Code:
              'Combobox Test.bas
              ' https://forum.powerbasic.com/forum/user-to-user-discussions/programming/797495-can-we-append-arrays-using-array-assign-statements?p=797505#post797505
               ' Thanks to Dave Biggs
              
              #DEBUG DISPLAY ON       ' < especially useful while developing apps that use arrays !!
              #COMPILE EXE
              #INCLUDE "win32api.inc"
              
              
              #RESOURCE MANIFEST, 1, "XPTheme.xml"
              '
              
                 GLOBAL hBtnAdd , hDlg AS DWORD
                 GLOBAL CountryArr() AS STRING
              
                 %Idc_Cmb = 100
                 %Idc_Lab = 108
                 %Idc_BtnAdd = 110
                 %ID_Lin = 112
                 %ID_LinBtn = 122
              
              
              '=================================
                 FUNCTION PBMAIN() AS LONG
                    LOCAL hfb AS DWORD
                    LOCAL UCbuttonChar AS WSTRING
                    LOCAL LinThk AS LONG
              
                    FONT NEW "Fbold" ,12,1 TO hfb
                    DIALOG NEW PIXELS, 0, "Please select a Country", _
                           300, 300, 500, 300, %WS_SYSMENU, 0 TO hDlg
                    DIALOG SET COLOR  hDlg, %RGB_MEDIUMBLUE , %RGB_AZURE
              
                    ' draws a long and thick line ( at the Top of dialog ) to
                    ' demarcate caption from dialog. It is long enough
                    ' so that resizing the dialog would have no effect.
                    ' LinThk is the line thickness
                      LinThk = 4
                      CONTROL ADD LINE, hDlg, %ID_Lin, "", 0, 1, 2000, LinThk,%SS_GRAYFRAME
              
              
                    CONTROL ADD LABEL , hDlg, %Idc_Lab,"Country  ", 30,90, 60, 20 ,  %SS_CENTER
                    CONTROL SET COLOR  hDlg, %Idc_Lab, %RGB_DARKBLUE , %RGB_AZURE
                    CONTROL SET FONT hDlg,%Idc_Lab, hfb
              
                  ' create a label as a button
                    UCbuttonChar = CHR$$(1002)
                    CONTROL ADD LABEL, hDlg, %Idc_BtnAdd, "Add countries to combobox    "+ _
                       UCbuttonChar,30,18,170,22, %SS_CENTER OR %SS_CENTERIMAGE
                    CONTROL SET COLOR  hDlg, %Idc_BtnAdd, %RGB_MAGENTA , %RGB_CORNSILK
                    hBtnAdd = GetDlgItem(hDlg, %Idc_BtnAdd)
              
                  ' draw a box line over the button label
                    CONTROL ADD LINE, hDlg, %ID_LinBtn, "", 30, 18, 170,22
              
              
              
              
                    DIALOG SHOW MODAL hDlg CALL DlgProc
                    FONT END hfb
                 END FUNCTION
              
              
              
              
              
              
               '===============================================
                 CALLBACK FUNCTION DlgProc() AS LONG
                   LOCAL  cbTxt AS STRING
                   LOCAL cbItem AS LONG
              
                    SELECT CASE AS LONG CBMSG
                      CASE %WM_INITDIALOG
                              BuildCarr
                              ' Add ComboBox complete with array..
                              CONTROL ADD COMBOBOX, CBHNDL, %Idc_Cmb, CountryArr() ,_
                               120, 90, 175, 225, %CBS_DROPDOWN OR %WS_VSCROLL
              
              
                       CASE %WM_LBUTTONDOWN, %WM_LBUTTONUP, %WM_MOUSEMOVE
                           ' when the add button is clicked or mouse hover it
                             PaintButton(CBMSG)
              
              
              
                      CASE  %WM_COMMAND
              
                           SELECT CASE CB.CTL
                            CASE %Idc_Cmb
                              IF CB.CTLMSG = %CBN_SELENDOK THEN
                                 COMBOBOX GET TEXT   CBHNDL, %Idc_Cmb TO cbTxt
                                 COMBOBOX GET SELECT CBHNDL, %Idc_Cmb TO cbItem
                                 ? " Selected Country : " + cbTxt,,"Item #" + STR$(cbItem)
                              END IF
              
                           END SELECT
                    END SELECT
              
                 END FUNCTION
              
              
              '================
              ' build the array for the combobox
              ' Note that Array Assign starts with LBound(Array())
              ' ie FIRST Element, not neccessarily 'element 0'
              SUB BuildCarr
                 LOCAL jj AS LONG
                 REDIM CountryArr(0 TO 11)            ' Create array with 12 elements
                 ARRAY ASSIGN CountryArr()="Cabo Verde", "Cambodia" , "Cameroon" , "Canada", "CAR" , _
                    "Cayman Islands" , "Chad" , "Chile", "Colombia", "Comoros" , "Congo", "Costa Rica"
              END SUB
              
              
              
              '================
              ' Add more countries into the combobox
              ' Note that Array Assign starts with LBound(Array())
                ' ie FIRST Element, not neccessarily 'element 0'
              SUB BuildAddCarr
               ' retain the old countries name
               ' Change array to 22 elements
                 REDIM PRESERVE CountryArr(0 TO 21)
               ' fill additional items
               ' NB DIM ..AT.. creates an 'absolute array'
                 DIM AddOn(12 TO 21) AS STRING AT VARPTR(CountryArr(12))
                  ARRAY ASSIGN AddOn()= "c13","c14","c15","c16","c17","c18","c19","c20","c21","c22"
                 CONTROL KILL hDlg, %Idc_Cmb
              
                 CONTROL ADD COMBOBOX, hDlg, %Idc_Cmb, CountryArr(), _
                   120, 90, 175, 225, %CBS_DROPDOWN OR %WS_VSCROLL
              
              END SUB
              
              
              
              
              
              
              '======================================
              ' Repaint button and test whether
              ' button is clicked or not
              FUNCTION PaintButton(BYVAL wMsg AS DWORD) AS LONG
                LOCAL Pt AS POINTAPI, Rc AS RECT, Result AS DWORD, hDc AS DWORD
                STATIC OnButton AS DWORD, MouseDown AS DWORD
              
                GetCursorPos(Pt)
                ScreenToClient hDlg, Pt
                Result = ChildWindowFromPoint (hDlg, BYVAL Pt)
                hDc = GetDc (hBtnAdd)
                GetClientRect(hBtnAdd,Rc)
                IF (wMsg = %WM_LBUTTONDOWN) OR (wMsg = %WM_LBUTTONUP) THEN
                    OnButton = 0
                END IF
                IF hBtnAdd = Result THEN
                  IF OnButton = 0 THEN
                    OnButton = 1
                    IF wMsg = %WM_LBUTTONDOWN THEN
                 '       when left mouse button is clicked
                         DrawEdge(hdc, Rc, %BDR_SUNKENINNER OR %BDR_SUNKENOUTER, %BF_RECT)
                     '   add more countries to combobox
                         BuildAddCarr
                    ELSE
                      DrawEdge(hdc, Rc, %BDR_RAISEDINNER OR %BDR_RAISEDOUTER, %BF_RECT)
                      paint_ButtonRectFrame  hBtnAdd
                    END IF
                  END IF
                ELSE
                  IF OnButton = 1 THEN
                      OnButton = 0
                      InValidateRect hBtnAdd, BYVAL 0, BYVAL 0
                  END IF
              
                END IF
                ReleaseDc hBtnAdd, hDc
                ' Draws the outer rectangle frame around the button
                paint_ButtonRectFrame  hBtnAdd
              END FUNCTION
              
              
              
              '===================================
               ' Routine to paint the Button rect frame
               ' to make it look stunning
               SUB paint_ButtonRectFrame( hBtn AS DWORD)
              
                       LOCAL hdgBrush, hdgPen AS DWORD
                       LOCAL rcdg AS RECT, psdg AS PAINTSTRUCT
                       ' get button and statusbar height to "frame"
                        GetClientRect hBtn, rcdg
              
                        BeginPaint hBtn, psdg
                         '  create pen with desired color       ' cyan , magenta and yellow are ok
                         '  the pen width will increase the width of the border
                            hdgPen   = CreatePen(%PS_SOLID, 2, %RGB_CYAN)
                          ' select pen into control's dc
                            hdgPen   = SelectObject(psdg.hDc, hdgPen)
                           'prepare for hollow rect
                            hdgBrush = SelectObject(psdg.hDc, GetStockObject(%NULL_BRUSH))
              
                            ' frame button  area and draw its rectangle
                            ' slightly smaller than the button area
                            Rectangle psdg.hDC, rcdg.nLeft+1  , rcdg.nTop+1 , _
                                                rcdg.nRight-1  , rcdg.nBottom-1
              
                           'return original brush
                            SelectObject psdg.hDc, hdgBrush
                          '  return original pen and delete the one we created
                            DeleteObject SelectObject(psdg.hDc, hdgPen)
                        EndPaint hBtn, psdg
              
               END SUB

              Comment


              • #8
                Tim,

                One simply does not paint a button control in the

                %WM_LBUTTONDOWN, %WM_LBUTTONUP, %WM_MOUSEMOVE

                events (messages).

                It does not provide persistence. When you paint a control it is not persistent. Move another window over and it and you will see it does not repaint.

                All painting should be done in WM_PAINT.

                That said, to accomplish what you are doing it is best to make the button ownerdraw and paint the control in the WM_DRAWITEM message. This message actually occurs during WM_PAINT. When the control gets the WM_PAINT message, it will forward the WM_DRAWITEM message to owner window (your dialog) of the control, so you are actually drawing during WM_PAINT.

                The beauty of the WM_DRAWITEM message is that it passes a structure which provides the controls current state, such as is the button up or down, is it disabled, etc. This allows you to draw the control based on its current state/

                Also by using ownerdraw your controls appearance is persistent. Move another window over it and it will redraw when visible again.

                Drawing outside of WM_PAINT is at best just a "hack" and does not produce the effects you desire. This is why Windows provides ownerdraw (and customdraw).

                Chris Boss
                Computer Workshop
                Developer of "EZGUI"
                http://cwsof.com
                http://twitter.com/EZGUIProGuy

                Comment


                • #9
                  A common way to handle startup painting is to post (PostMessage(), DIALOG POST) a private message to your window during the processing of WM_INITDIALOG, then handle the painting when processing that private message.

                  Code:
                  %PWM_DRAWBUTTON =    %WM_USER + 1
                  
                  ......
                  
                  SELECT CASE  wMsg/CBMSG
                       CASE %WM_INITDIALOG
                             stuff here
                             PostMessage or DIALOG POST  hwnd/CBHNDL, %PWM_DRAWBUTTON, %NULL, %NULL
                  
                       CASE %PWM_DRAWBUTTON     ' will be first message after dialog is shown
                              draw here
                  
                   .....
                  This also guarantees the user will see the drawing right after the screen is first shown.



                  MCM
                  Michael Mattias
                  Tal Systems Inc. (retired)
                  Racine WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment

                  Working...
                  X