PlanetSquires Forums

Support Forums => José Roca Software => Topic started by: docroger on July 22, 2025, 07:05:46 AM

Title: How to draw on dialog with DDT
Post by: docroger on July 22, 2025, 07:05:46 AM
Hello José,

Can you give me a small example how to draw (line, point, box, etc) on a dialog.
I use DDT with afxnova.

On powerbasic there is graphic attach, graphic line... etc...

Thanx for any help.
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 22, 2025, 08:52:13 AM
I'm not going to replicate the GRAPHIC statements. Instead, I'm working in my own Graphic control that works with GDI, GDI+ and OpenGL. It is a SDK custom control. I will see if I can make it to work with dialogs.

To draw in  a dialog using GDI, you have to do it processing the WM_PAINT message ad, if the dialog is resizable, also the WM_ERASEBKGND message.

Here is an small example:

#define UNICODE
#define _WIN32_WINNT &h0602
#include once "AfxNova/DDT.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

CONST IDC_OK = 1001

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)

   ' // Create a new dialog using dialog units
   DIM hDlg AS HWND = DialogNewPixels(0, "DDT Dialog - GDI Line Demo", 0, 0, 400, 220, WS_POPUP OR WS_CAPTION OR WS_SYSMENU OR DS_CENTER)
   ' // Set the dialog's backgroung color
   DialogSetColor(hDlg, -1, RGB_WHITE)

   ' // Display and activate the dialog as modal
   DialogShowModal(hDlg, @DlgProc)

   RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

   STATIC cxClient AS LONG, cyClient AS LONG
   
   SELECT CASE uMsg

      CASE WM_INITDIALOG
         RETURN TRUE

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
               END IF
            CASE IDC_OK
               MsgBox("OK button pressed", MB_ICONEXCLAMATION OR MB_TASKMODAL, "Message")
         END SELECT

      CASE WM_PAINT
         DIM ps AS PAINTSTRUCT
         DIM hdc AS HDC = BeginPaint(hDlg, @ps)
         Rectangle hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8
         MoveToEx hdc, 0, 0, NULL
         LineTo hdc, cxClient, cyClient
         MoveToEx hdc, 0, cyClient, NULL
         LineTo hdc, cxClient, 0
         Ellipse hdc, cxClient / 8, cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8
         RoundRect hdc, cxClient / 4, cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, cxClient / 4, cyClient / 4
         EndPaint hDlg, @ps
         RETURN TRUE

      CASE WM_SIZE
         cxClient = LOWORD(lParam)
         cyClient = HIWORD(lParam)
         RETURN TRUE

      CASE WM_CLOSE
         ' // End the application
         DialogEnd(hDlg)

   END SELECT

   RETURN FALSE

END FUNCTION

For documentation about GDI see: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-rectangle
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 22, 2025, 08:53:57 AM
My GdiPlus classes (working on it) can also be used. You can't compile it because I'm still working in the GdiPlus classes.

#define UNICODE
#define _WIN32_WINNT &h0602
#include once "AfxNova/DDT.inc"
#include once "AfxNova/CGdiPlus.inc"
USING AfxNova

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG
   END wWinMain(GetModuleHandleW(NULL), NULL, wCommand(), SW_NORMAL)

DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

CONST IDC_OK = 1001

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)

   ' // Create a new dialog using dialog units
   DIM hDlg AS HWND = DialogNewPixels(0, "DDT Dialog - GDI Line Demo", 0, 0, 400, 220, WS_POPUP OR WS_CAPTION OR WS_SYSMENU OR DS_CENTER)
   ' // Set the dialog's backgroung color
   DialogSetColor(hDlg, -1, RGB_WHITE)

   ' // Display and activate the dialog as modal
   DialogShowModal(hDlg, @DlgProc)

   RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_Violet
   arrowPen.SetCustomEndCap(@myArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 20, 100, 20)

   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)

END SUB
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

   SELECT CASE uMsg

      CASE WM_INITDIALOG
         RETURN TRUE

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
               END IF
            CASE IDC_OK
               MsgBox("OK button pressed", MB_ICONEXCLAMATION OR MB_TASKMODAL, "Message")
         END SELECT

      CASE WM_PAINT
         DIM ps AS PAINTSTRUCT
         DIM hdc AS HDC = BeginPaint(hDlg, @ps)
         Example_GetHeight(hdc)
         EndPaint hDlg, @ps
         RETURN TRUE

      CASE WM_CLOSE
         ' // End the application
         DialogEnd(hDlg)

   END SELECT

   RETURN FALSE

END FUNCTION
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 22, 2025, 09:00:06 AM
My graphic control features persistence, so you don't have to be constantly repainting it. This is a SDK test. You can't compile it because I'm still working in the GdiPlus classes.

' ########################################################################################
' Microsoft Windows
' File: AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_Violet
   arrowPen.SetCustomEndCap(@myArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 20, 100, 20)

   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create the main window
   DIM pWindow AS CWindow = "MyClassName"
   pWindow.CreateOverlapped(NULL, "GDI+ AdjustableArrowCapGetHeight", @WndProc)
   ' // Size it by setting the wanted width and height of its client area
   pWindow.SetClientSize(400, 250)
   ' // Center the window
   pWindow.Center

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pGraphCtx.Clear RGB_FLORALWHITE
   ' // Get the memory device context of the graphic control
   DIM hdc AS HDC = pGraphCtx.GetMemDc
   ' // Draw the graphics
   Example_GetHeight(hdc)

   ' // Displays the window and dispatches the Windows messages
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main window procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   SELECT CASE uMsg

      ' // If an application processes this message, it should return zero to continue
      ' // creation of the window. If the application returns –1, the window is destroyed
      ' // and the CreateWindowExW function returns a NULL handle.
      CASE WM_CREATE
         AfxEnableDarkModeForWindow(hwnd)
         EXIT FUNCTION

      ' // Theme has changed
      CASE WM_THEMECHANGED
         AfxEnableDarkModeForWindow(hwnd)
         EXIT FUNCTION

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

    CASE WM_DESTROY
         ' // Ends the application by sending a WM_QUIT message
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hwnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================
Title: Re: How to draw on dialog with DDT
Post by: docroger on July 23, 2025, 06:03:46 AM
Hi José,

Thanx for the explanations.

I test and learn for porting my powerbasic apps.

Is the new graphic control soon available ?
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 23, 2025, 09:49:39 PM
Yes. I just have finished the integration. Using my GDI+ classes, that replicate the Microsoft ones, and setting the DPI ratios with ScaleTransform, the graphics are DPI aware and scale automatically. Contrarily to the PowerBasic graphic control, that is a subclassed static control that only works with 24-bit images, CGraphCtx is 32 bit, allowing the use of alpha blended images with transparency and, as it uses DIBs, it has not the 4 MB limitation.

Test example (the attachment contains a 64-bit executable):

' ########################################################################################
' Microsoft Windows
' File: DDT_AdjustableArrowCapGetHeight.bas
' Contents: GDI+ - AdjustableArrowCapGetHeight example
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#INCLUDE ONCE "AfxNova/DDT.inc"
#INCLUDE ONCE "AfxNova/AfxExt.bi"   ' // optional: For dark mode
USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

' ========================================================================================
' The following example creates an AdjustableArrowCap, myArrow, and sets the height of the
' cap. The code then creates a Pen, assigns myArrow as the ending line cap for the Pen,
' and draws a capped line. Next, the code gets the height of the arrow cap, creates a new
' arrow cap with height equal to the height of myArrow, assigns the new arrow cap as the
' ending line cap for the Pen, and draws another capped line.
' ========================================================================================
SUB Example_GetHeight (BYVAL hdc AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create an AdjustableArrowCap with a height of 10 pixels
   DIM myArrow AS CGpAdjustableArrowCap = CGpAdjustableArrowCap(10, 10, TRUE)
   ' // Adjust to DPI by setting the scale width
   myArrow.SetWidthScale(rxRatio)

   ' // Create a Pen, and assign myArrow as the end cap
   DIM arrowPen AS CGpPen = ARGB_Violet
   arrowPen.SetCustomEndCap(@myArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 20, 100, 20)

   ' // Create a second arrow cap using the height of the first one
   DIM AS CGpAdjustableArrowCap otherArrow = CGpAdjustableArrowCap(myArrow.GetHeight, 20, TRUE)
   otherArrow.SetWidthScale(rxRatio)

   ' // Assign the new arrow cap as the end cap for arrowPen
   arrowPen.SetCustomEndCap(@otherArrow)

   ' // Draw a line using arrowPen
   graphics.DrawLine(@arrowPen, 0, 55, 100, 55)

END SUB
' ========================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // The recommended way is to use a manifest.
   ' // Optional: Set process DPI aware.
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Optional: Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create a new custom dialog using pixels
'   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 400, 250, WS_OVERLAPPEDWINDOW OR DS_CENTER)
   ' // Alternate way: Provides an exact control of the client size
   DIM hDlg AS HWND = DialogNewPixels("MyClassName", 0, "DDT - GraphCtx Test", 0, 0, 0, 0, WS_OVERLAPPEDWINDOW)
   DialogSetClient(hDlg, 400, 250)
   DialogCenter(hDlg)
   ' // Optional: Caption dark mode
   AfxEnableDarkModeForWindow(hDlg)

   ' // Add a graphic control
   DIM nWidth AS LONG = DialogGetClientWidth(hDlg)
   DIM nHeight AS LONG = DialogGetClientHeight(hDlg)
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
   ' // Make the control resizable
   pGraphCtx.Resizable = TRUE
   ' // Optional: Clear the graphic control background
   pGraphCtx.Clear RGB_FLORALWHITE
   ' // Get the memory device context of the graphic control
   DIM hdc AS HDC = pGraphCtx.GetMemDc

   ' // Pointer version:
'   DIM pGraphCtx AS CGraphCtx = NEW CGraphCtx(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   --or--
'   DIM pGraphCtx AS CGraphCtx PTR = ControlAddGraphic(hDlg, IDC_GRCTX, "", 0, 0, nWidth, nHeight)
'   pGraphCtx->Resizable = TRUE
'   pGraphCtx->Clear RGB_FLORALWHITE
'   DIM hdc AS HDC = pGraphCtx->GetMemDc

   ' // Draw the graphics
   Example_GetHeight(hdc)

   ' // Display and activate the dialog as modal
   DialogShowModal(hDlg, @DlgProc)

   ' // Pointer version: Delete the graphic control
 '  IF pGraphCtx THEN Delete pGraphCtx

   ' // Return the result code set with DialogEnd
   RETURN DialogEndResult(hDlg)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Dialog callback procedure
' ========================================================================================
FUNCTION DlgProc (BYVAL hDlg AS HWND, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LPARAM) AS INT_PTR

   SELECT CASE uMsg

      CASE WM_INITDIALOG
         RETURN TRUE

      ' // Optional: Theme has changed
'      CASE WM_THEMECHANGED
'         AfxEnableDarkModeForWindow(hDlg)

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hDlg, WM_CLOSE, 0, 0
               END IF
         END SELECT

      CASE WM_CLOSE
         ' // End the application
         DialogEnd(hDlg)

   END SELECT

END FUNCTION
' ========================================================================================
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 23, 2025, 10:12:43 PM
It also allows to mix the use of GDI, GDI+ and OpenGL.

' ########################################################################################
' Microsoft Windows
' Contents: CWindow OpenGL Graphic Control Skeleton
' Graphic control, GDI+ classes and OpenGL working together
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2025 José Roca. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#define UNICODE
#INCLUDE ONCE "AfxNova/CWindow.inc"
#INCLUDE ONCE "AfxNova/CGdiPlus.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
USING AfxNova

CONST IDC_GRCTX = 1001

DECLARE FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                           BYVAL hPrevInstance AS HINSTANCE, _
                           BYVAL pwszCmdLine AS WSTRING PTR, _
                           BYVAL nCmdShow AS LONG) AS LONG

   END wWinMain(GetModuleHandleW(NULL), NULL, wCOMMAND(), SW_NORMAL)

' // Forward declaration
DECLARE FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

' ========================================================================================
' The following sample code draws a line.
' Change it with your own code.
' ========================================================================================
SUB GDIP_Render (BYVAL hDC AS HDC)

   ' // Create a graphics object from the window device context
   DIM graphics AS CGpGraphics = hdc
   ' // Get the DPI scaling ratios
   DIM rxRatio AS SINGLE = graphics.GetDpiX / 96
   DIM ryRatio AS SINGLE = graphics.GetDpiY / 96
   ' // Set the scale transform
   graphics.ScaleTransform(rxRatio, ryRatio)

   ' // Create a Pen object
   DIM pen AS CGpPen = CGpPen(ARGB_GREEN, 15)

   ' // Draw a line
   graphics.DrawLine(@pen, 0, 0, 200, 100)

END SUB
' ========================================================================================

' =======================================================================================
' All the setup goes here
' =======================================================================================
SUB RenderScene (BYVAL nWidth AS LONG, BYVAL nHeight AS LONG)

   ' // Specify clear values for the color buffers
   glClearColor 0.0, 0.0, 0.0, 0.0
   ' // Specify the clear value for the depth buffer
   glClearDepth 1.0
   ' // Specify the value used for depth-buffer comparisons
   glDepthFunc GL_LESS
   ' // Enable depth comparisons and update the depth buffer
   glEnable GL_DEPTH_TEST
   ' // Select smooth shading
   glShadeModel GL_SMOOTH

   ' // Prevent divide by zero making height equal one
   IF nHeight = 0 THEN nHeight = 1
   ' // Reset the current viewport
   glViewport 0, 0, nWidth, nHeight
   ' // Select the projection matrix
   glMatrixMode GL_PROJECTION
   ' // Reset the projection matrix
   glLoadIdentity
   ' // Calculate the aspect ratio of the window
   gluPerspective 45.0!, nWidth / nHeight, 0.1!, 100.0!
   ' // Select the model view matrix
   glMatrixMode GL_MODELVIEW
   ' // Reset the model view matrix
   glLoadIdentity

   ' // Clear the screen buffer
   glClear GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT
   ' // Reset the view
   glLoadIdentity

   glTranslatef -1.5!, 0.0!, -6.0!       ' Move left 1.5 units and into the screen 6.0

   glBegin GL_TRIANGLES                 ' Drawing using triangles
      glColor3f   1.0!, 0.0!, 0.0!       ' Set the color to red
      glVertex3f  0.0!, 1.0!, 0.0!       ' Top
      glColor3f   0.0!, 1.0!, 0.0!       ' Set the color to green
      glVertex3f  1.0!,-1.0!, 0.0!       ' Bottom right
      glColor3f   0.0!, 0.0!, 1.0!       ' Set the color to blue
      glVertex3f -1.0!,-1.0!, 0.0!       ' Bottom left
   glEnd                                 ' Finished drawing the triangle

   glTranslatef 3.0!,0.0!,0.0!           ' Move right 3 units

   glColor3f 0.5!, 0.5!, 1.0!            ' Set the color to blue one time only
   glBegin GL_QUADS                      ' Draw a quad
      glVertex3f -1.0!, 1.0!, 0.0!       ' Top left
      glVertex3f  1.0!, 1.0!, 0.0!       ' Top right
      glVertex3f  1.0!,-1.0!, 0.0!       ' Bottom right
      glVertex3f -1.0!,-1.0!, 0.0!       ' Bottom left
   glEnd                                 ' Done drawing the quad

   ' // Required: force execution of GL commands in finite time
   glFlush

END SUB
' =======================================================================================

' ========================================================================================
' Main
' ========================================================================================
FUNCTION wWinMain (BYVAL hInstance AS HINSTANCE, _
                   BYVAL hPrevInstance AS HINSTANCE, _
                   BYVAL pwszCmdLine AS WSTRING PTR, _
                   BYVAL nCmdShow AS LONG) AS LONG

   ' // Set process DPI aware
   SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
   ' // Enable visual styles without including a manifest file
   AfxEnableVisualStyles

   ' // Create the main window
   DIM pWindow AS CWindow
   pWindow.Create(NULL, "CWindow+CGraphCtx+CGdiPlus+OpenGL", @WndProc)
   pWindow.SetClientSize(400, 250)
   pWindow.Center

   ' // Add a graphic control with OPENGL enabled
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "OPENGL", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   ' // Anchor the control
   pWindow.AnchorControl(IDC_GRCTX, AFX_ANCHOR_HEIGHT_WIDTH)

   ' // Clear the background
   pGraphCtx.Clear RGB_WHITE
   ' // Make the control resizable
   pGraphCtx.Stretchable = TRUE
   ' // Make current the rendering context
   pGraphCtx.MakeCurrent
   ' // Render the OpenGL scene
   RenderScene pGraphCtx.GetVirtualBufferWidth, pGraphCtx.GetVirtualBufferHeight
   ' // Draw the GDI+ graphics
   GDIP_Render pGraphCtx.GetMemDC

   ' // Dispatch Windows events
   FUNCTION = pWindow.DoEvents(nCmdShow)

END FUNCTION
' ========================================================================================

' ========================================================================================
' Main window callback procedure
' ========================================================================================
FUNCTION WndProc (BYVAL hwnd AS HWND, BYVAL uMsg AS UINT, BYVAL wParam AS WPARAM, BYVAL lParam AS LPARAM) AS LRESULT

   SELECT CASE uMsg

      CASE WM_COMMAND
         SELECT CASE CBCTL(wParam, lParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF CBCTLMSG(wParam, lParam) = BN_CLICKED THEN
                  SendMessageW hwnd, WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

    CASE WM_DESTROY
         ' // End the application
         PostQuitMessage(0)
         EXIT FUNCTION

   END SELECT

   ' // Default processing of Windows messages
   FUNCTION = DefWindowProcW(hWnd, uMsg, wParam, lParam)

END FUNCTION
' ========================================================================================
Title: Re: How to draw on dialog with DDT
Post by: docroger on July 24, 2025, 05:31:10 AM
Hi, nice examples !!

After try the exe, i try to compile code... but my version afxnova is incomplete... i wait.

Good job.
Title: Re: How to draw on dialog with DDT
Post by: hajubu on July 24, 2025, 10:56:08 AM
@jose
I know your are in mid of your work - but there some findings for the graphics control
Nvertheless - thank for the great work --
' Graphic control, GDI+ classes and OpenGL working together
' DEMO FILE : GdiPlusAndOpenGL.bas
! wrong folder reference in line 1188...1198 of AfxNova\CGdiPlu.inc
! causes error 23: File not found in '#include once "AfxNova/CGdiPlus/.....inc"'
! starting with
#include once "AfxNova/CGdiPlus/CGpBitmap.inc"
#include once "AfxNova/CGdiPlus/CGpBrush.inc"
#include once "AfxNova/CGdiPlus/CGpFont.inc"
#include once "AfxNova/CGdiPlus/CGpGraphics.inc"
#include once "AfxNova/CGdiPlus/CGpImageAttributes.inc"
#include once "AfxNova/CGdiPlus/CGpLineCaps.inc"
#include once "AfxNova/CGdiPlus/CGpMatrix.inc"
#include once "AfxNova/CGdiPlus/CGpPath.inc"
#include once "AfxNova/CGdiPlus/CGpPen.inc"
#include once "AfxNova/CGdiPlus/CGpRegion.inc"
#include once "AfxNova/CGdiPlus/CGpStringFormat.inc"

! BUT using a correct reference will follow-up with "  declaration outside namespace...."
!      FOR "CGpBitmap.inc ... CGpGraphics.inc"
!  trying to correct this
!  will cause an follow-up segmentation error
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 24, 2025, 04:37:55 PM
Thanks very much for reporting it.

Modified "CGdiPlus.inc" and "CGpGraphics.inc"

and uploaded them to the AfxNova repository in GitHub.
Title: Re: How to draw on dialog with DDT
Post by: José Roca on July 24, 2025, 04:44:04 PM
For GDI+ I'm using my own declarations file, "AfxGdiplus.bi", because the ones provided with the FreeBasic compiler for 32-bit are a truly mess and incompatible with the ones for 64-bit, so I have unified them. The import 32-bit library for GDI+ is also partially broken. Paul Squires has generated a new one that works correctly and that will be included with the new Tiko release. Meanwhile, it is advisable to compile with the 64-bit compiler.