PlanetSquires Forums

Support Forums => José Roca Software => Topic started by: José Roca on November 14, 2025, 11:37:56 PM

Title: AfxNova progress
Post by: José Roca on November 14, 2025, 11:37:56 PM
I have done much work with GDI+.

GDI+ Flat API

As the FreeBasic headers for GID+ are a mess, with different headers for 32 and 64 bit, with many naming conflicts and import libraries partially broken, I have writen my own headers: AfxGdiPlus.bi.

All the 600+ functions are documented here:
https://github.com/JoseRoca/AfxNova/tree/main/docs/Graphics%20/GdiPlus%20Flat%20%20Api

And there are hundreds of examples here:
https://github.com/JoseRoca/AfxNova/tree/main/Examples/GdiPlus%20Flat%20API

GDI+ classes

These classes replicate the C++ ones.

hey are documented here:
https://github.com/JoseRoca/AfxNova/tree/main/docs/Graphics%20/GdiPlus%20Classes

And there are hundreds of examples here:
https://github.com/JoseRoca/AfxNova/tree/main/Examples/GdiPlus%20Classes
Title: Re: AfxNova progress
Post by: José Roca on November 14, 2025, 11:46:43 PM
Currently, I'm working in a class, called CWebView2, that allows to embed the WebView2 control in a FreeBasic application.

he main part is already implemented and working. There is still much work to do to write the many event's classes.

With this short code you can create an instance of WebView2 and navigate to a site.

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

   DIM pWindow AS CWindow
   DIM hWin AS HWND = pWindow.Create(NULL, "WebView2", @WndProc)
   pWindow.SetClientSize(1050, 500)
   pWindow.Center
'   DIM pWebView2 AS CWebView2 = hWin
   DIM pWebView2 AS CWebView2 = CWebView2(hWin, ExePath)
   IF pWebView2.IsReady(5000) THEN
      ' // Navigate to the web page
      pWebView2.Navigate("https://www.planetsquires.com/protect/forum/index.php")
   ELSE
      ' // Timeout
   END IF

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

END FUNCTION
Title: Re: AfxNova progress
Post by: Paul Squires on November 15, 2025, 09:25:52 AM
Incredible work, thanks a million for this, I am amazed at the amount of work you've put into GDI+ and WebView2
Title: Re: AfxNova progress
Post by: José Roca on November 15, 2025, 03:48:01 PM
It is needed to deal with the high resolution monitors. The old WebBrowser control is obsolete and we need WebView2. And regading GDI+, I have found solutions to make graphics DPI aware. And DWSTRING provides optional support to fix broken surrogate pairs and allows to use utf-8. I do the same trick that Windows uses with the "A" functions: convert utf-8 to utf-16 on input, allowing to use all the string functions and operators, and optionally convert to utf-8 on output. It only requires a constructor and two properties.

Afx needed a modernization.
Title: Re: AfxNova progress
Post by: José Roca on November 17, 2025, 05:22:15 AM
The events are solved. I will provide overridable classes that the user can override like this:

' ########################################################################################
' CWebView2NavigationCompletedEventHandlerImpl class
' Implementation of the ICoreWebView2NavigationCompletedEventHandler callback interface.
' ########################################################################################
TYPE CWebView2NavigationCompletedEventHandlerImpl EXTENDS CWebView2NavigationCompletedEventHandler

   ' ICoreWebView2NavigationCompletedEventHandler
   DECLARE FUNCTION Invoke (BYVAL sender AS Afx_ICoreWebView2 PTR, BYVAL args AS Afx_ICoreWebView2NavigationCompletedEventArgs PTR) AS HRESULT

   DECLARE CONSTRUCTOR (BYVAL pWebView2 AS CWebView2 PTR)
   DECLARE DESTRUCTOR

   m_pWebView2 AS CWebView2 PTR
   m_token AS EventRegistrationToken

END TYPE
' ########################################################################################

' ========================================================================================
CONSTRUCTOR CWebView2NavigationCompletedEventHandlerImpl (BYVAL pWebView2 AS CWebView2 PTR)
   CWV2_DP("")
   IF pWebView2 THEN
      m_pWebView2 = pWebView2
      pWebView2->AddNavigationCompleted(cast(ANY PTR, @this), @m_token)
   END IF
END CONSTRUCTOR
' ========================================================================================
' ========================================================================================
DESTRUCTOR CWebView2NavigationCompletedEventHandlerImpl
   CWV2_DP("")
   IF m_pWebView2 THEN m_pWebView2->RemoveNavigationCompleted(m_token)
END DESTRUCTOR
' ========================================================================================
' ========================================================================================
FUNCTION CWebView2NavigationCompletedEventHandlerImpl.Invoke (BYVAL sender AS Afx_ICoreWebView2 PTR, BYVAL args AS Afx_ICoreWebView2NavigationCompletedEventArgs PTR) AS HRESULT
   CWV2_DP("*** Navigation completed ***")
   RETURN S_OK
END FUNCTION
' ========================================================================================

And will set as follows:

DIM pEvt AS CWebView2NavigationCompletedEventHandlerImpl PTR
pEvt = NEW CWebView2NavigationCompletedEventHandlerImpl(@pWebView2)

Now I only have to implement classes for all the events (there are many) and we will have a great control ready to use.
Title: Re: AfxNova progress
Post by: José Roca on November 28, 2025, 04:16:08 PM
All the essential is done and tested. What remains is to wrap the latest interfaces and events, most of which aren't essential unless you intend to write a full web browser. What we have is a very useful control that can do all that a web browser can do: maps, charts, images, graphics, videos, audio, web pages, etc. It can be embedded in any window, the most suitable being a label control.

The attached capture shows a WebView control embedded into a label displaying a Goodle map.
Title: Re: AfxNova progress
Post by: José Roca on December 08, 2025, 04:24:52 PM
I have been playing with dark mode. Here you will find templates for most of the standard Windows controls except Menus: https://github.com/JoseRoca/AfxNova/tree/main/Templates/SDK%20Templates

For easy identification, these examples have "_DarkMode" as the suffix.
Title: Re: AfxNova progress
Post by: José Roca on December 09, 2025, 05:07:42 PM
I also have started to post WebView2 examples:

https://github.com/JoseRoca/AfxNova/tree/main/Examples/CWebView2

The WebView2Loader.dll that I'm using is them one for 64 bit. It you want to try 32 bit, rename WebView2Loader_32.dll as WebView2Loader.dll.

A good place where to find examples that can be adapted to work with WebView2 is WC3 Schools: https://www.w3schools.com/html/default.asp
Title: Re: AfxNova progress
Post by: Johan Klassen on December 09, 2025, 06:18:28 PM
thank you José Roca  :)
Title: Re: AfxNova progress
Post by: hajubu on December 10, 2025, 05:18:51 PM
HI,jose

!great work!
- dark modes are fine
- just tested the webview2 samples - worked fine.  Thank's for your effort done.
----hajubu---

Just a small hint:
samples (*.bas) in utf16LE like the both "GoogleCircularChart.bas"  and "Chartjs_CircularChart.bas"
are getting mixed up , when you use the the full zip download.
' ########################################################################################
�✀ 䴀椀挀爀漀猀漀昀琀 圀椀渀搀漀眀猀ഀഀ
' File: Google circular chart.bas

Download as single raw file selection keeps the UTF16LE correct format.
' ########################################################################################
' Microsoft Windows
' File: Google circular chart.bas

Is there a real need to use UTF16LE instead of UTF8 ?
(mho : Tiko is save for UTF16 and UTF8 , which is not true for the zips in Gith.)


Title: Re: AfxNova progress
Post by: José Roca on December 11, 2025, 12:24:41 AM
Thanks very much. I have reuploaded them. There is not need to use UTF-16, except that I tried to see if it worked with these examples. I used Ελιές, (Olives in Greek). It worked, but forgot to set Tiko back to Ansi. Sorry for the inconvenience.
Title: Re: AfxNova progress
Post by: docroger on December 12, 2025, 12:20:55 PM
Hello José,

With last version afxnova, i cant compile cw_menu_icon_01... : compiling resource failed.

Seems icons are missing in resources dir (into templates).

Am i missing something ?
Title: Re: AfxNova progress
Post by: hajubu on December 12, 2025, 01:31:24 PM
hi  docroger
hi jose

yes here some icons missing - even some are doubled in DDT_Templates resources.
my fast analysis for the several SDK CW_xxx.rc files references for the SDK_Templates, especially for the menu_icon_01
home_32,home_64, save_32, save_64

just in case ...
here a shared file with the missing icons inside:

sdk_resources_save+home.7z (https://limewire.com/d/QdFUd#TiAvQeXJ4a)
Title: Re: AfxNova progress
Post by: José Roca on December 12, 2025, 04:25:52 PM
Yes, some icons were missing. I have reuploaded them.

Seems that I uploaded them to the Resources folder of the DDT templates and forgot to do the same in the Resources folder of the SK Templates. I'm sorry. I've been so busy with GDI++, WebView and Dark Mode that I'm being a bit forgetful.
Title: Re: AfxNova progress
Post by: José Roca on December 12, 2025, 04:43:53 PM
Microsoft is developing WebView2 very rapidly. There are 27 new interfaces and 18 new events in ICoreWebView (unless they've implemented more since my last check). Every time they add one or two methods, they implement a new interface, which inherits from the existing ones. The reason there are so many event interfaces is that WebView2 works asynchronously, so you generally don't get the result directly when you call a method, but rather in the event callbacks. Most of the new methods aren't essential, but rather refinements.
Title: Re: AfxNova progress
Post by: docroger on December 16, 2025, 05:22:05 AM
Hello José,

On the docs, windows, windows gui, menu procedures, there are lines in double and maybe some mistakes with check menu and append menu :

Quote| **AppendMenu** | Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu. |
| **CheckMenuItem** | Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu. |
| **CheckMenuItem** | Sets the state of the specified menu item's check-mark attribute to either selected or clear. |
| **CheckMenuRadioItem** | Checks a specified menu item and makes it a radio item. |
| **CheckMenuRadioItem** | Checks a specified menu item and makes it a radio item. |
| **CreateMenu** | Creates a menu. |
| **CreatePopupMenu** | Creates a drop-down menu, submenu, or shortcut menu. |
| **CreatePopupMenu** | Creates a drop-down menu, submenu, or shortcut menu. |
....
....
| **GetMenuString** | Copies the text string of the specified menu item into the specified buffer. |
| **GetSubMenu** | Retrieves a handle to the drop-down menu or submenu activated by the specified menu item. |
| **GetSubMenu** | Retrieves a handle to the drop-down menu or submenu activated by the specified menu item. |
| **GetSystemMenu** |Enables the application to access the window menu (also known as the system menu or the control menu) for copying and modifying. |


The docs files are very useful and afxnova very good !

Thanx for hard work.
Title: Re: AfxNova progress
Post by: José Roca on December 16, 2025, 07:46:14 PM
Thanks very much. I will check it. It is a problem of copy and paste. You copy a line to keep the fomatting and change the text, and sometimes you get distracted and forget to change it. Documentation is the worst part. Nobody likes to do it, but I make the effort of documenting all my code. A documentation like the one for GDI+, with 600+ functions, is very hard to do without doing some mistakes.
Title: Re: AfxNova progress
Post by: José Roca on December 16, 2025, 07:54:56 PM
I have removed the duplicated lines. Thanks again.
Title: Re: AfxNova progress
Post by: José Roca on December 16, 2025, 07:58:06 PM
I'm wrapping now XmlLite, to have a fast parser and writer for the XML format. I'm now in the phase of the documentation.
Title: Re: AfxNova progress
Post by: José Roca on December 18, 2025, 07:57:34 AM
Finished the work with the CXmlLite class.

Headers

https://github.com/JoseRoca/AfxNova/blob/main/AfxNova/AfxXmlLite.bi

CXmlLite class

https://github.com/JoseRoca/AfxNova/blob/main/AfxNova/CXmlLite.inc


Documentation

https://github.com/JoseRoca/AfxNova/blob/main/docs/File%20Management%20/CXmlLite%20Class.md

Examples

https://github.com/JoseRoca/AfxNova/tree/main/Examples/XML
Title: Re: AfxNova progress
Post by: José Roca on January 25, 2026, 02:29:56 PM
Static classes

A practical design choice rooted in how Win32 actually works.

Windows standard controls — BUTTON, EDIT, LISTBOX, etc. — are not object‑oriented. They are handle‑based, message‑driven window classes created by the OS.

This means:

- Windows owns the control
- You interact with it through an HWND
- All operations happen through messages and APIs
- The control can be accessed from anywhere as long as you have the handle
- Trying to wrap this model in strict OOP creates more problems than it solves.

AfxNova's static classes embrace the Win32 model instead of fighting it.

Classic OOP pain points:

- Where do I store the object?
- How do I access the control from a callback?
- How do I map HWND → object?
- What happens when the control is destroyed?"

A static class in AfxNova:

- does not need an instance
- does not track lifetime
- does not require storing objects
- works directly with the HWND
- can be called from anywhere
- matches the Win32 philosophy exactly

Example:

CButton.SetIcon(hButton, hIcon)
CButton.Enable(hButton)
CButton.SetText(hButton, "OK")

This is clean, predictable, and 100% compatible with how Windows expects you to work.

Why static classes are beneficial

Static classes let you group all related functionality in one place, instead of scattering macros and procedures across multiple files. They also allow the use of overloaded methods, making the API easier and more intuitive to use.

As a proof-of-concept, I have implemented CButton, a static class that wraps the functionality of a Button control. It is available at https://github.com/JoseRoca/AfxNova/blob/main/AfxNova/CButton.inc

This shows how a static class can group all related functionality in one place, provide overloads for ease of use, and remain fully compatible with the native Win32 handle‑based model.

These classes aren't limited to Windows controls — the same approach can be used to wrap other WinAPI technologies as well.
Title: Re: AfxNova progress
Post by: José Roca on February 21, 2026, 07:08:23 AM
With the help of my OLE Container (CAxHost), I have managed to get several of the old VB6 ActiveX controls (OCXs) to work with CWindow.

One of the most interesting is the Microsoft Hyerarchical Grid Control. It works wonderfully with CWindow, my ADO classes and the Jet Database, that comes pre-installed in Windows.

The only limitation is that it is 32-bit only.

This is one of my tests:

' ########################################################################################
' Microsoft Windows
' Contents: Embedded Microsoft Hierarchical Grid Control
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2026 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
'#define _CAXH_DEBUG_ 1
#INCLUDE ONCE "AfxNova/AfxCOM.inc"
#INCLUDE ONCE "AfxNova/CAxHost.inc"
#INCLUDE ONCE "AfxNova/MSHFlexGrid.inc"
#INCLUDE ONCE "AfxNova/CADODB.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)

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

CONST IDC_GRID = 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
   ' // The recommended way is to use a manifest file
   AfxSetProcessDPIAware

   ' // Creates the main window
   DIM pWindow AS CWindow
   ' -or- DIM pWindow AS CWindow = "MyClassName" (use the name that you wish)
   DIM hwndMain AS HWND = pWindow.Create(NULL, "Microsoft Hierarchical Flex Grid", @WndProc)
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(800, 450)
   ' // Centers the window
   pWindow.Center

   DIM wszLibName AS WSTRING * 260 = ExePath & "\MSHFLXGD.OCX"
   DIM pHost AS CAxHost = CAxHost(@pWindow, IDC_GRID, wszLibName, AFX_CLSID_MSHFlexGrid, _
       AFX_IID_IMSHFlexGrid, RTLKEY_MSHFlexGrid, 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pWindow.AnchorControl(IDC_GRID, AFX_ANCHOR_HEIGHT_WIDTH)
   SetFocus pHost.hWindow

   DIM pGrid AS CMSHFlexGrid = pHost.OcxDispObj
   ' // Set the width of the columns (in twips)
   pGrid.ColWidth(0) = 300
   pGrid.ColWidth(1) = 1100
   pGrid.ColWidth(2) = 3000
   pGrid.ColWidth(3) = 2000
   pGrid.ColWidth(4) = 2000
   pGrid.ColWidth(5) = 3000
   pGrid.ColWidth(6) = 1500
   pGrid.ColWidth(7) = 700
   pGrid.ColWidth(8) = 1200
   pGrid.ColWidth(9) = 1200
   pGrid.ColWidth(10) = 1500
   pGrid.ColWidth(11) = 1500
   ' Change the foreground and background colors
   pGrid.ForeColor = BGR(0, 0, 0)
   pGrid.BackColor = BGR(255,255,235)

   ' Open an ADO connection
   DIM pConnection AS CAdoConnection PTR = NEW CAdoConnection
   pConnection->ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & ExePath & $"\nwind.mdb"
   pConnection->Open
   ' Open a recordset
   DIM pRecordset AS CAdoRecordset PTR = NEW CAdoRecordset
   DIM dvSource AS DVARIANT = "SELECT * FROM Customers"
   pRecordset->Open(dvSource, pConnection, adOpenKeyset, adLockOptimistic, adCmdText)
   ' Set the Datasource property of the recordset
   pGrid.DataSource = cast(ANY PTR, pRecordset->DataSource)
   ' Close the recordset
   pRecordset->Close
   ' Close the connection
   pConnection->Close
   ' // Delete the recordset
   Delete pRecordset
   ' // Delete the connection
   Delete pConnection

   ' // Display the window
   ShowWindow(hwndMain, nCmdShow)
   UpdateWindow(hwndMain)

   ' // Dispatch Windows messages
   DIM uMsg AS MSG
   WHILE (GetMessageW(@uMsg, NULL, 0, 0) <> FALSE)
      IF AfxCAxHostForwardMessage(GetFocus, @uMsg) = FALSE THEN
         IF IsDialogMessageW(hwndMain, @uMsg) = 0 THEN
            TranslateMessage(@uMsg)
            DispatchMessageW(@uMsg)
         END IF
      END IF
   WEND
   FUNCTION = uMsg.wParam

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)
         RETURN 0

      ' // Theme has changed
      CASE WM_THEMECHANGED
         AfxEnableDarkModeForWindow(hwnd)
         RETURN 0

      CASE WM_COMMAND
         SELECT CASE LOWORD(wParam)
            CASE IDCANCEL
               ' // If ESC key pressed, close the application by sending an WM_CLOSE message
               IF HIWORD(wParam) = 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
' ========================================================================================

Notice that instead of pWindow.DoEvents I'm using a message pump:

   ' // Dispatch Windows messages
   DIM uMsg AS MSG
   WHILE (GetMessageW(@uMsg, NULL, 0, 0) <> FALSE)
      IF AfxCAxHostForwardMessage(GetFocus, @uMsg) = FALSE THEN
         IF IsDialogMessageW(hwndMain, @uMsg) = 0 THEN
            TranslateMessage(@uMsg)
            DispatchMessageW(@uMsg)
         END IF
      END IF
   WEND

In it, AfxCAxHostForwardMessage gives the hosted grid to process keyboard messages.
Title: Re: AfxNova progress
Post by: José Roca on February 21, 2026, 09:02:32 AM
With some tweaking, we can simulate the fashionable dark mode:

   ' // Scroll bars dark theme
   SetWindowTheme(pGrid.hwnd, "DarkMode_Explorer", NULL)
   ' // Change the foreground and background colors
   pGrid.ForeColor = RGB_WHITE
   pGrid.BackColor = RGB_BLACK
   ' // change the color of the background of the grid
   pGrid.BackColorBkg = RGB_BLACK
   ' // Change the colors of the fixed parts of the grid
   pGrid.ForeColorFixed = RGB_WHITE
   pGrid.BackColorFixed = BGR(90, 90, 90)
   ' // Color of the grid
   pGrid.GridColor = RGB_GRAY
   pGrid.GridLines = flexGridFlat
   pGrid.GridColorHeader = RGB_GRAY
   pGrid.GridLinesFixed = flexGridFlat
Title: Re: AfxNova progress
Post by: José Roca on May 18, 2026, 07:43:19 PM
I have started to write headers to add support for Direct2D in AfxNova. One of the main goals was to allow to use this technology with my graphic control, that now supports GDI, GDI+, OpenGL (classic) and Direct2D. I will also write headers for Direct Write and WIC (Windows Image Component).

This is a preliminary test drawing a circle, using Direct2D, in an instance of the grapic control.

' ########################################################################################
' Microsoft Windows
' Contents: D2D1 - Draw ellipse
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2026 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.
' ########################################################################################

#INCLUDE ONCE "AfxNova/CWindow.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#include once "AfxNova/AfxD2D1.bi"
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

' ========================================================================================
' This function draws Direct2D content to a GDI HDC.
' hMemDC = Handle of the memory device context of the graphic control
' nWidth = Width of the virtual buffer of the graphic control
' nHeight = Height of the virtual buffer of the graphic control
' ========================================================================================
FUNCTION RenderScene (BYVAL hMemDC AS HDC, BYVAL nWidth AS LONG, BYVAL nHeight AS LONG) AS HRESULT

   DIM hr AS HRESULT
   DIM pFactory AS ID2D1Factory PTR                ' // ID2D1Factory interface
   DIM pRenderTarget AS ID2D1DCRenderTarget PTR    ' // ID2D1DCRenderTarget interface
   DIM pBrush AS ID2D1SolidColorBrush PTR          ' // ID2D1SolidColorBrush interface

   ' // Create an instance of the ID2D1Factory interface
   hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, BYVAL NULL, pFactory)
   IF hr <> S_OK THEN RETURN hr

   ' // Get the DPI values
   DIM AS SINGLE dpix, dpiY
   pFactory->GetDesktopDpi(dpiX, dpiY)

   ' // Poperties of the tender target
   DIM props AS D2D1_RENDER_TARGET_PROPERTIES = D2D1_RenderTargetProperties ( _
       D2D1_RENDER_TARGET_TYPE_DEFAULT, _
       D2D1_PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), _
       dpiX, dpiY, _
       D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT)

   ' // Create a DC render target.
   hr = pFactory->CreateDCRenderTarget(props, pRenderTarget)
   IF hr <> S_OK THEN
      pFactory->Release
      RETURN hr
   END IF

   ' // Get the dimensions of the graphic control drawing area.
   DIM rc AS RECT = (0, 0, nWidth, nHeight)

   ' // Bind the DC to the DC render target.
   hr = pRenderTarget->BindDC(hMemDC, rc)

   ' // Create a brush.
   hr = pRenderTarget->CreateSolidColorBrush(D2D1_ColorF(D2D1_Blue), BYVAL NULL, pBrush)

   ' // The ID2D1RenderTarget.BeginDraw method signals the start of drawing.
   pRenderTarget->BeginDraw

      ' // The ID2D1RenderTarget.Clear method fills the entire render target with a
      ' // solid color. The color is given as a D2D1_COLOR_F structure.
      pRenderTarget->Clear(D2D1_ColorF(D2D1_White))

      ' // Sample code: Draws an ellipse (replace it with your drawing operations)
      DIM tEllipse AS D2D1_ELLIPSE = (D2D1_Point2F(150, 150), 100, 100)
      pRenderTarget->DrawEllipse(tEllipse, cast(ID2D1Brush PTR, pBrush), 3)

   ' // The BeginDraw, Clear, and DrawEllipse methods all have a void return type.
   ' // If an error occurs during the execution of any of these methods, the error
   ' // is signaled through the return value of the EndDraw method.
   ' // The EndDraw method signals the completion of drawing for this frame.
   ' //  All drawing operations must be placed between calls to BeginDraw and EndDraw.

   hr = pRenderTarget->EndDraw

   ' // Clean resources
   D2D1_SafeRelease(pBrush)
   D2D1_SafeRelease(pRenderTarget)
   D2D1_SafeRelease(pFactory)

   FUNCTION = hr

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

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

   ' // Initializa the COM library
   CoInitialize NULL

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

   ' // Creates the main window
   DIM pWindow AS CWindow = "MyClassName"   ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "D2D1 - Draw ellipse", @WndProc)
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(300, 300)
   ' // Centers the window
   pWindow.Center
   ' // Set the main window background color
   pWindow.SetBackColor(RGB_WHITE)

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

   ' // Draw the graphics
   RenderScene(pGraphCtx.GetMemDc, pGraphCtx.GetVirtualBufferWidth, pGraphCtx.GetVirtualBufferHeight)

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

   ' // Uninitialize the COM library
   CoUninitialize

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)
         RETURN 0

      ' // Theme has changed
      CASE WM_THEMECHANGED
         AfxEnableDarkModeForWindow(hwnd)
         RETURN 0

      ' // Sent when the user selects a command item from a menu, when a control sends a
      ' // notification message to its parent window, or when an accelerator keystroke is translated.
      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)
         END SELECT
         RETURN 0

      CASE WM_DESTROY
         ' // End the application by sending an WM_QUIT message
         PostQuitMessage(0)
         RETURN 0

   END SELECT

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

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

The graphic control features persistence, double buffering and automatic scrolling.
Title: Re: AfxNova progress
Post by: José Roca on May 20, 2026, 03:42:10 PM
Direct2D offers higher quality and speed than GDI+, and is better suited for working with high-resolution monitors. So it seems the time has come to transition to a more advanced technology.

' ########################################################################################
' Microsoft Windows
' Contents: D2D1 - Radial gradient brush
' Compiler: FreeBasic 32 & 64 bit
' Copyright (c) 2026 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.
' ########################################################################################

#INCLUDE ONCE "AfxNova/CWindow.inc"
#INCLUDE ONCE "AfxNova/CGraphCtx.inc"
#include once "AfxNova/AfxD2D1.bi"
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

' ========================================================================================
' This function draws Direct2D content to a GDI HDC.
' hMemDC = Handle of the memory device context of the graphic control
' nWidth = Width of the virtual buffer of the graphic control
' nHeight = Height of the virtual buffer of the graphic control
' ========================================================================================
FUNCTION RenderScene (BYVAL hMemDC AS HDC, BYVAL nWidth AS LONG, BYVAL nHeight AS LONG) AS HRESULT

   DIM hr AS HRESULT
   DIM pFactory AS ID2D1Factory PTR                ' // ID2D1Factory interface
   DIM pRenderTarget AS ID2D1DCRenderTarget PTR    ' // ID2D1DCRenderTarget interface
   DIM pBrush AS ID2D1SolidColorBrush PTR          ' // ID2D1SolidColorBrush interface

   ' // Create an instance of the ID2D1Factory interface
   hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, BYVAL NULL, pFactory)
   IF hr <> S_OK THEN RETURN hr

   ' // Get the DPI values
   DIM AS SINGLE dpix, dpiY
   pFactory->GetDesktopDpi(dpiX, dpiY)

   ' // Poperties of the tender target
   DIM props AS D2D1_RENDER_TARGET_PROPERTIES = D2D1_RenderTargetProperties ( _
       D2D1_RENDER_TARGET_TYPE_DEFAULT, _
       D2D1_PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), _
       dpiX, dpiY, _
       D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT)

   ' // Create a DC render target.
   hr = pFactory->CreateDCRenderTarget(props, pRenderTarget)
   IF hr <> S_OK THEN
      pFactory->Release
      RETURN hr
   END IF

   ' // Get the dimensions of the graphic control drawing area.
   DIM rc AS RECT = (0, 0, nWidth, nHeight)

   ' // Bind the DC to the DC render target.
   hr = pRenderTarget->BindDC(hMemDC, rc)

   ' // Create a brush.
   hr = pRenderTarget->CreateSolidColorBrush(D2D1_ColorF(D2D1_Blue), BYVAL NULL, pBrush)

   ' // The ID2D1RenderTarget.BeginDraw method signals the start of drawing.
   pRenderTarget->BeginDraw

      ' // The ID2D1RenderTarget.Clear method fills the entire render target with a
      ' // solid color. The color is given as a D2D1_COLOR_F structure.
      pRenderTarget->Clear(D2D1_ColorF(D2D1_White))

      ' ============================================================
      ' Bézier heart with radial gradient fill + glowing border + gold outline
      ' ============================================================

      ' // 1) Radial gradient for the heart fill
      DIM pFillBrush  AS ID2D1RadialGradientBrush PTR
      DIM pFillStops  AS ID2D1GradientStopCollection PTR

      DIM fillStops(3) AS D2D1_GRADIENT_STOP
      fillStops(0).position = 0.0
      fillStops(0).color    = D2D1_ColorF(1.0, 0.95, 0.6, 1.0)   ' warm highlight
      fillStops(1).position = 0.33
      fillStops(1).color    = D2D1_ColorF(1.0, 0.6, 0.2, 1.0)    ' orange
      fillStops(2).position = 0.66
      fillStops(2).color    = D2D1_ColorF(0.4, 0.3, 1.0, 1.0)    ' violet
      fillStops(3).position = 1.0
      fillStops(3).color    = D2D1_ColorF(0.1, 0.1, 0.3, 1.0)    ' deep blue
      pRenderTarget->CreateGradientStopCollection (fillStops(0), 4, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, pFillStops)

      DIM fillProps AS D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES
      fillProps.center = D2D1_Point2F(175, 150)
      fillProps.gradientOriginOffset = D2D1_Point2F(-60, -40)
      fillProps.radiusX = 180
      fillProps.radiusY = 120
      pRenderTarget->CreateRadialGradientBrush (fillProps, BYVAL NULL, pFillStops, pFillBrush)

      ' // 2) Glow border (soft halo)
      DIM pGlowBrush AS ID2D1RadialGradientBrush PTR
      DIM pGlowStops AS ID2D1GradientStopCollection PTR

      DIM glowStops(2) AS D2D1_GRADIENT_STOP
      glowStops(0).position = 0.0
      glowStops(0).color    = D2D1_ColorF(1.0, 1.0, 0.9, 1.0)   ' bright glow
      glowStops(1).position = 0.6
      glowStops(1).color    = D2D1_ColorF(1.0, 0.6, 0.2, 0.4)   ' soft orange
      glowStops(2).position = 1.0
      glowStops(2).color    = D2D1_ColorF(0.0, 0.0, 0.0, 0.0)   ' fade to transparent

      pRenderTarget->CreateGradientStopCollection( _
         glowStops(0), 3, _
         D2D1_GAMMA_2_2, _
         D2D1_EXTEND_MODE_CLAMP, _
         pGlowStops)

      DIM glowProps AS D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES
      glowProps.center = D2D1_Point2F(175, 150)
      glowProps.gradientOriginOffset = D2D1_Point2F(0, 0)
      glowProps.radiusX = 210
      glowProps.radiusY = 210
      pRenderTarget->CreateRadialGradientBrush (glowProps, BYVAL NULL, pGlowStops, pGlowBrush)

      ' // 3 Gold outline (solid brush)
      DIM pGoldBrush AS ID2D1SolidColorBrush PTR
      pRenderTarget->CreateSolidColorBrush( _
         D2D1_ColorF(1.0, 0.85, 0.2, 1.0), _   ' gold color
         BYVAL NULL, pGoldBrush)

      ' // 4) Bézier heart geometry
      DIM pGeoHeart AS ID2D1PathGeometry PTR
      DIM pSink     AS ID2D1GeometrySink PTR

      pFactory->CreatePathGeometry(pGeoHeart)
      pGeoHeart->Open(pSink)

      DIM AS SINGLE topX,topY : topX = 175 : topY =  90
      DIM AS SINGLE lcX,lcY   : lcX  =  70 : lcY  =  40
      DIM AS SINGLE lmX,lmY   : lmX  =  60 : lmY  = 150
      DIM AS SINGLE rcX,rcY   : rcX  = 280 : rcY  =  40
      DIM AS SINGLE rmX,rmY   : rmX  = 290 : rmY  = 150
      DIM AS SINGLE bx,by     : bx   = 175 : by   = 240

      pSink->BeginFigure(D2D1_Point2F(topX, topY), D2D1_FIGURE_BEGIN_FILLED)

      DIM L AS D2D1_BEZIER_SEGMENT
      L.point1 = D2D1_Point2F(lcX, lcY)
      L.point2 = D2D1_Point2F(lmX, lmY)
      L.point3 = D2D1_Point2F(bx,  by)
      pSink->AddBezier(L)

      DIM R AS D2D1_BEZIER_SEGMENT
      R.point1 = D2D1_Point2F(rmX, rmY)
      R.point2 = D2D1_Point2F(rcX, rcY)
      R.point3 = D2D1_Point2F(topX, topY)
      pSink->AddBezier(R)

      pSink->EndFigure(D2D1_FIGURE_END_CLOSED)
      pSink->Close
      pSink->Release

      ' ============================================================
      ' Metallic loop above the heart
      ' ============================================================
      ' // Metallic fill brush (radial gradient)
      DIM pLoopBrush AS ID2D1RadialGradientBrush PTR
      DIM pLoopStops AS ID2D1GradientStopCollection PTR

      DIM loopStops(2) AS D2D1_GRADIENT_STOP
      loopStops(0).position = 0.0
      loopStops(0).color    = D2D1_ColorF(1.0, 0.95, 0.6, 1.0)   ' bright gold center
      loopStops(1).position = 0.5
      loopStops(1).color    = D2D1_ColorF(0.9, 0.7, 0.2, 1.0)    ' warm gold
      loopStops(2).position = 1.0
      loopStops(2).color    = D2D1_ColorF(0.4, 0.3, 0.1, 1.0)    ' darker gold edge
      pRenderTarget->CreateGradientStopCollection (loopStops(0), 3, D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, pLoopStops)

      DIM loopProps AS D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES
      loopProps.center = D2D1_Point2F(175, 60)         ' center of the loop
      loopProps.gradientOriginOffset = D2D1_Point2F(-10, -10)
      loopProps.radiusX = 40
      loopProps.radiusY = 40
      pRenderTarget->CreateRadialGradientBrush (loopProps, BYVAL NULL, pLoopStops, pLoopBrush )

      ' // Loop geometry (rounded arc)
      DIM pGeoLoop AS ID2D1PathGeometry PTR
      DIM pSink2   AS ID2D1GeometrySink PTR

      pFactory->CreatePathGeometry(pGeoLoop)
      pGeoLoop->Open(pSink2)

      ' Loop coordinates (small arc above the heart)
      DIM AS SINGLE lx1, ly1 : lx1 = 145 : ly1 =  80   ' left base
      DIM AS SINGLE lx2, ly2 : lx2 = 205 : ly2 =  80   ' right base
      DIM AS SINGLE cx,  cy  : cx  = 175 : cy  =  40   ' top of arc

      pSink2->BeginFigure(D2D1_Point2F(lx1, ly1), D2D1_FIGURE_BEGIN_FILLED)

      DIM arcseg AS D2D1_BEZIER_SEGMENT
      arcseg.point1 = D2D1_Point2F(150, 30)   ' left control
      arcseg.point2 = D2D1_Point2F(200, 30)   ' right control
      arcseg.point3 = D2D1_Point2F(lx2, ly2)  ' right base
      pSink2->AddBezier(arcseg)

      ' Close the bottom of the loop
      pSink2->AddLine(D2D1_Point2F(lx1, ly1))

      pSink2->EndFigure(D2D1_FIGURE_END_CLOSED)
      pSink2->Close
      pSink2->Release

      ' ============================================================
      ' // Projected shadow for the èndant
      ' ============================================================
      '  Create shadow brush (soft black)
      DIM pShadowBrush AS ID2D1SolidColorBrush PTR
      pRenderTarget->CreateSolidColorBrush( _
          D2D1_ColorF(0.0, 0.0, 0.0, 0.35), _   ' translucent black
          BYVAL NULL, _
          pShadowBrush )
      '  Save current transform
      DIM oldTransform AS D2D1_MATRIX_3X2_F
      pRenderTarget->GetTransform(oldTransform)
     
      ' Build shadow transform
      DIM shadow AS D2D1_MATRIX_3X2_F

      ' Shadow pivot (same as pendant rotation pivot)
      DIM pivotX AS SINGLE : pivotX = 175
      DIM pivotY AS SINGLE : pivotY =  40

      ' Shadow tilt angle (stronger than pendant tilt)
      DIM angle AS SINGLE
      angle = -22.0   ' degrees

      ' // Build transform:
      ' 1) Move to pivot
      ' 2) Rotate
      ' 3) Squash vertically (fake blur)
      ' 4) Move downward
      shadow = _
          D2D1_MatrixTranslation(-pivotX, -pivotY) * _
          D2D1_MatrixRotation(angle, D2D1_Point2F(0, 0)) * _
          D2D1_MatrixScale(D2D1_SizeF(1.0, 0.35)) * _
          D2D1_MatrixTranslation(pivotX, pivotY + 160)

      ' Apply shadow transform
      pRenderTarget->SetTransform(shadow)

      ' // Draw shadow using the same geometries
      ' Heart shadow
      pRenderTarget->FillGeometry(pGeoHeart, pShadowBrush)

      ' Loop shadow
      pRenderTarget->FillGeometry(pGeoLoop, pShadowBrush)

      ' Chain shadow (ellipses)
      DIM AS SINGLE baseY      : baseY      = 35
      DIM AS SINGLE startX     : startX     = 115
      DIM AS SINGLE linkWidth  : linkWidth  = 22
      DIM AS SINGLE linkHeight : linkHeight = 10
      DIM AS SINGLE spacing    : spacing    = 18
      DIM AS INTEGER i

      DIM link AS D2D1_ELLIPSE
      FOR i = 0 TO 6
          DIM cx AS SINGLE : cx = startX + i * spacing
          DIM cy AS SINGLE : cy = baseY + 3 * SIN((i - 3) * 3.14159 / 6.0)
          link.point.x = cx
          link.point.y = cy
          link.radiusX = linkWidth  / 2.0
          link.radiusY = linkHeight / 2.0
          pRenderTarget->FillEllipse(link, pShadowBrush)
      NEXT

      '  // Restore original transform
      pRenderTarget->SetTransform(oldTransform)

      ' // Release shadow brush
      IF pShadowBrush THEN pShadowBrush->Release

      ' ============================================================
      ' ======= Draw everything else =======
      ' ============================================================

      ' // Fill the heart
      pRenderTarget->FillGeometry(pGeoHeart, pFillBrush)
      ' // Glow border (soft halo)
      pRenderTarget->DrawGeometry(pGeoHeart, pGlowBrush, 14.0)
      ' // Gold outline (sharp edge)
      pRenderTarget->DrawGeometry(pGeoHeart, pGoldBrush, 3.0)
      pGeoHeart->Release

      ' // Draw loop (fill + gold outline)
      pRenderTarget->FillGeometry(pGeoLoop, pLoopBrush)
      pRenderTarget->DrawGeometry(pGeoLoop, pGoldBrush, 3.0)
      pGeoLoop->Release

      ' // Release the loop resources
      D2D1_SafeRelease(pLoopBrush)
      D2D1_SafeRelease(pLoopStops)

      ' // Restore original transform
      pRenderTarget->SetTransform(oldTransform)

   ' // The BeginDraw, Clear, and DrawEllipse methods all have a void return type.
   ' // If an error occurs during the execution of any of these methods, the error
   ' // is signaled through the return value of the EndDraw method.
   ' // The EndDraw method signals the completion of drawing for this frame.
   ' //  All drawing operations must be placed between calls to BeginDraw and EndDraw.

   hr = pRenderTarget->EndDraw

   ' // Clean resources
   D2D1_SafeRelease(pFillBrush)
   D2D1_SafeRelease(pFillStops)
   D2D1_SafeRelease(pGoldBrush)
   D2D1_SafeRelease(pGlowBrush)
   D2D1_SafeRelease(pGlowStops)
   D2D1_SafeRelease(pRenderTarget)
   D2D1_SafeRelease(pFactory)

   FUNCTION = hr

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

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

   ' // Initializa the COM library
   CoInitialize NULL

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

   ' // Creates the main window
   DIM pWindow AS CWindow = "MyClassName"   ' Use the name you wish
   DIM hWin AS HWND = pWindow.Create(NULL, "D2D1 - Radial gradient brush", @WndProc)
   ' // Sizes it by setting the wanted width and height of its client area
   pWindow.SetClientSize(350, 300)
   ' // Centers the window
   pWindow.Center
   ' // Set the main window background color
   pWindow.SetBackColor(RGB_WHITE)

   ' // Add a graphic control
   DIM pGraphCtx AS CGraphCtx = CGraphCtx(@pWindow, IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight)
   pGraphCtx.Clear RGB_FLORALWHITE
   'pGraphCtx.Stretchable = TRUE
   ' // Anchor the control
   pWindow.AnchorControl(pGraphCtx.hWindow, AFX_ANCHOR_HEIGHT_WIDTH)

   ' // Draw the graphics
   RenderScene(pGraphCtx.GetMemDc, pGraphCtx.GetVirtualBufferWidth, pGraphCtx.GetVirtualBufferHeight)

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

   ' // Uninitialize the COM library
   CoUninitialize

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)
         RETURN 0

      ' // Theme has changed
      CASE WM_THEMECHANGED
         AfxEnableDarkModeForWindow(hwnd)
         RETURN 0

      ' // Sent when the user selects a command item from a menu, when a control sends a
      ' // notification message to its parent window, or when an accelerator keystroke is translated.
      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)
         END SELECT
         RETURN 0

      CASE WM_DESTROY
         ' // End the application by sending an WM_QUIT message
         PostQuitMessage(0)
         RETURN 0

   END SELECT

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

END FUNCTION
' ========================================================================================
Title: Re: AfxNova progress
Post by: Johan Klassen on May 21, 2026, 07:00:41 AM
good day José  :)
that example looks great
I recommended your work to Patrice Terrier who asked if webview2 worked with FB x64 https://www.freebasic.net/forum/viewtopic.php?p=311135
Title: Re: AfxNova progress
Post by: José Roca on May 21, 2026, 08:13:30 AM
Yes, I have seen that post. Thanks very much.

Regarding Direct2D, I'm currently writing small didactic templates. I already have 66.

They're useful for me to learn how to use this technology and to test if my headers are correct. Now I'll try Direct Write and then WIC. This technology is a whole ecosystem.
Title: Re: AfxNova progress
Post by: José Roca on May 28, 2026, 02:33:22 AM
The old WebBrowser control used to navigate to HTTPS sites, although modern pages rendered incorrectly due to the outdated MSHTML engine. Older HTML pages still displayed fine.

After recent Windows updates, HTTPS support has effectively been removed from the legacy Internet Explorer engine, so the WebBrowser control can no longer load secure sites.

It still works with HTTP pages and local HTML files.

This change is permanent and expected, as Microsoft has fully deprecated Internet Explorer. Anyone still relying on the old WebBrowser control should migrate to WebView2 as soon as possible.

Fortunately, AfxNova provides classes for WebView2, which is the new standard and fully supports modern web technologies.
Title: Re: AfxNova progress
Post by: hajubu on May 29, 2026, 10:19:41 AM
Hi, I just update my previous Html-Help for Tiko 1.3 , which is based on your md-DOCS-folder.

enclosed are two zip containing the changes , the instructions, and Readme
                    and the ready-to-use AfxNova_Web.

Readme_first_if_you_like.

Have Fun !


b.r. Hans (Hajubu)

# AfxNova_Web_2026

Readme for  updating  CWebview2.bas to AfxNova_webview2_2026

+ Hi, just updated the  'AfxNova-WebView2' , to integrate a Help Tool for the Docs for 'AfxNova' as html.

+ taken from examples latest in AfxNova-Main\AfxNova\WebView2\ dated ..._260528_1945 sha 998d7f1 ...

-  Changes
  - CWebview2.inc (UserDatafolder, now possible in "User-temp\..."
        - i.e. %userappdata%\\local\\temp\\...) or in ...
       
1. Markdown to html is done with pandoc - ( fence code rules needing an editor w. R E)

```
  * i.e. 1rst:  regex (```\r\n---\r\n)# -> (```\r\n\r\n---\r\n\r\n)#
  *      2nd:  regex (---\r\n)#        -> (---\r\n\r\n)#
  * and adapting  "Docs" folder names  i.e. Webview2 : - docs\WebWiev2 -> docs\Webview2
  * { A copy of AfxNova-Main-Readme is also available in html-folder }
 
  * Even when .md-docs are updated, the maintenance is an easy-peasy task (took me only 15 minutes)
 
  * The converted .Md-to-.html can just exchanged inside the folder structure.
```

2. When using 'pandoc' as a batch job - actual 137 - by keeping the folder structure the same as Jose's doc-folder

```
- it may be used any Browser locally

* OR on a Webserver (if you like)

* OR also within  IKO 1.3  TOOLS

* ... without or with an adapted Jose's CW_Webview2.bas

* Fitting dir_name_idx.html as page index in this structure let me use it as an offline Help

* Integrating in Tiko is no rocket science.  * It works a simple tool , but it helps a lot.

* File structure : html,css only ( no java,.js no php * no need !)
```

```
3. Optional
- Compiling sample  of AfxNova_WebView2_2026.bas sample
  - Creating/adapting some lines of code in the AfxNovaWeb.ini-file
  - Adapting some lines of the org. CW_Webviev2.bas for updated AfxNova_webview2.bas  ,
        - changing the pWebView2.Navigate(weburl) + start-index could be called from the app.
  - i.e. Instruction are in the CW_Snippet{1,2,3}_WebView2.txt for the CW_WebView2_2026.bas.
```

4. Epilog:

```
Archive:
    * The '_TestPan_AfxNovaWeb2_2026.zip' archive contains
      -  AfxNova_webview2_2026.bas
    * The zipped Readme
          {org_CW_WebView2.bas + snippets.txt(3)) + AfxNovaWeb.ini + this Readme.}
      -  and the AfxNova_Web as html structured folder
Info:
    * As the comiled Webview2-app is to large for upload ,
      attached is only the sources (*.bas,*.ini) in a zip.
    The names of the ini-file :AfxNovaWeb.ini is hardcoded in the source.
   
    * Please do not forget to adapt it to your needs at least in the ini-file.

    *  here the template for the ini-file :
    [AfxNovaWeb]
    Root="V:\_testpan\_AfxNovaWeb14\AfxNova_idx.html"
    Puri="file:///"
   
    *  Comiling with Tiko and AfxNova of _260528_1945 sha 998d7f1... needs
   
    * proj.folder : (work.tiko) new project: manifest.xml,resource.rc
   
    .\AfxNova_WebView2_2026.bas (sample)
    .\loader.js
    .\WebView2Loader.dll
    .\AfxNovaWeb.ini
    and UserDatafolder (applicable, if default is not %TEMP%)
   
    * and if testing at least:
    .\AfxNova_WebView2_2026.exe + .\AfxNovaWeb.ini + Userdatafolder/Temp
   
```
Have Fun !
Title: Re: AfxNova progress
Post by: José Roca on May 29, 2026, 04:51:23 PM
Thanks very much for your efforts.

I've been experimenting with WebView2, and I'm genuinely impressed. This control gives desktop applications access to the full power of a modern browser, which means we can now integrate high‑quality images, animations, graphics, audio, video, printing, and much more—directly inside a native Windows program.

What makes this especially exciting is that the integration with FreeBasic is now seamless. The heavy lifting—initialization, DPI handling, resizing, message routing, and all the low‑level details—has already been solved. From this point on, everything becomes simple experimentation: write some HTML, add a bit of CSS, drop in a JavaScript library, and watch it come alive inside your application.

WebView2 also includes a fully localized context menu with built‑in features like copy, save, zoom, rotate, and print, all without writing any additional code. And because it uses the Chromium engine, we automatically get perfect DPI scaling, smooth zooming, crisp rendering, and support for the latest web standards.

There is an enormous ecosystem of JavaScript libraries available—charts, maps, UI frameworks, image processors, PDF generators, animation engines—and every one of them becomes instantly usable from FreeBasic through WebView2. With the help of AI, generating HTML, CSS, and JavaScript becomes even easier, making it possible to prototype and integrate advanced features in minutes.

In short, WebView2 gives us superpowers.

And now that it's fully integrated into FreeBasic, the only limit is our imagination. I'll be sharing examples so everyone can explore what this control can do. I'm just beginning to learn HTML, CSS, and JavaScript myself, but even at this early stage, the possibilities already look incredible.
Title: Re: AfxNova progress
Post by: Paul Squires on June 07, 2026, 07:22:25 PM
Hi José,

I have gotten back to updating Tiko code and am just about ready to post the version 1.3.1 update. I can grab your most recent AfxNova repository files now, or I can wait a bit longer if you'd like to add anything before I publish the Tiko update. Just let me know.

Thanks,
Paul
Title: Re: AfxNova progress
Post by: José Roca on June 07, 2026, 08:07:41 PM
Hi Paul,

Everything I have uploaded is working fine as far as I know, so there is no need to delay the release of the new version of Tiko.

As you can see, I'm working to provide access to the latest technologies, such as WebView2 and Direct2D. Direct2D, DirectWrite, and WIC can already be used if you manage the object lifetimes yourself and call the methods directly. I'm currently working on wrapper classes to make them easier to use by providing constructors, automatic memory management, and overloaded methods.

I have already finished wrapping the most important interfaces, but wrapping the entire set will take time, since Direct2D is a whole ecosystem. The advantages of Direct2D are that it is faster, offers higher quality, and is fully DPI‑aware — all essential qualities for modern 4K displays.
Title: Re: AfxNova progress
Post by: Paul Squires on June 08, 2026, 06:56:23 AM
Excellent, thanks! I will use your latest repository files. Very exciting new code you're working on! I look forward to using it when you are finished. AfxNova makes coding in FB so much easier and these new technologies will make it even better.
Title: Re: AfxNova progress
Post by: José Roca on June 08, 2026, 08:27:41 AM
My next step will be to take full advantage of the WebView2 control and turn it into a true "super control".
 
I'm planning to write a canvas class that allows drawing anything you need: graphics, images, text, shapes, and more. The idea is to expose a classic, easy‑to‑use API on the outside, while internally generating the required HTML, CSS, and JavaScript to perform the rendering.

This approach will make it possible to build all kinds of document‑related features:

- invoices and reports
- barcodes and QR codes
- embedded images
- tables and layouts
- previews and printouts
- charts and visualizations
- even animations

Basically, anything you can do on a modern web page will be available through a simple, traditional API.


The WebView2 control also has a localized context menu and/or shortcuts that allow to save, copy, rotate images, zoom and printing.
Title: Re: AfxNova progress
Post by: José Roca on June 09, 2026, 03:08:08 AM
Mi idea is working.

I'm currently developing a new graphics engine based on Microsoft WebView2, wrapped inside a custom class that exposes a clean and easy‑to‑use API.

The goal is simple: give you a powerful 2D drawing system without requiring you to learn HTML, CSS, or JavaScript

Under the hood, the engine uses an HTML5 <canvas> element and JavaScript to perform all rendering. This means you automatically benefit from:


- GPU‑accelerated drawing
- smooth rendering with no flicker
- high‑resolution text and shapes
- automatic scrolling when the canvas is larger than the control
- modern browser‑level performance

But you never have to deal with any of that complexity.

Instead, you work with a very simple and intuitive API, for example:

pCanvas->DrawText("Hello World", 100, 100, "red", 40)
pCanvas->DrawLine(50, 50, 300, 200, "blue", 4)
pCanvas->DrawCircle(200, 200, 50, "purple", 3)

No fonts to create.
No pens or brushes.
No device contexts.
No GDI or Direct2D setup.
Just straightforward drawing commands.

If you do know HTML, CSS, or JavaScript, you can extend the system even further. But if you don't, the API will give you what you need to draw text, shapes, images, and more.

In short, this project aims to provide a modern, lightweight, and extremely easy graphics language, built on top of WebView2 but completely hiding the web technologies behind a clean and friendly interface.
Title: Re: AfxNova progress
Post by: Johan Klassen on June 09, 2026, 10:27:42 AM
Good day José Roca  :)
this is great stuff, I can see that creating a GUI applications using your WebView2 class will be easy, even for me, thank you ;D
Title: Re: AfxNova progress
Post by: Paul Squires on June 09, 2026, 11:54:01 AM
Reminds me a lot of the Tauri project and what they've accomplished as an alternative to Electron.
https://v2.tauri.app/

Tauri is cross platform and written in Rust.
Title: Re: AfxNova progress
Post by: José Roca on June 09, 2026, 09:29:24 PM
The idea is simple. I already have a class, CWebView2, to allow to embed an instance of WebView2 in any window. CWebView2Canvas extends CWebView2, calls the BASE constructor to create the instance of WebView2 and...

Uses this tiny html script to create a canvas:


  DIM s AS STRING = $"<html>"
  s += "<body style='margin:0; padding:0; overflow:auto;'>"
  s += $"<canvas id='cv' width='" & STR(cvWidth) & "' height='" & STR(cvHeight) & "'></canvas>"
  s += $"</body>"
  s += $"</html>"

Uses this tiny JavaScript function to get the context when the web page is loaded:

  DIM JS_INIT AS STRING = $"var cv, ctx;"
  JS_INIT += "window.onload = function() {"
  JS_INIT += "    cv = document.getElementById('cv');"
  JS_INIT += "    ctx = cv.getContext('2d');"
  JS_INIT += "};"

If the WebView2 control is ready, adds the script to the control and navigats to our tiny web page.

  IF this.IsReady THEN
      this.AddScriptToExecuteOnDocumentCreated(JS_INIT, NULL)
      this.NavigateToString(s)
  END IF

With the reference to CWebView2Canvas class we can call our wrapper functions, e.g.

DIM pCanvas AS CWebView2Canvas = CWebView2Canvas(hWin, dwsUserDataFolder)
...
...
pCanvas->DrawText("Hello World", 100, 100, "red", 40)
pCanvas->DrawLine(50, 50, 300, 200, "blue", 4)
pCanvas->DrawCircle(200, 200, 50, "purple", 3)

As WebView2 is asynchronous, the safest way is to use the CWebView2NavigationCompletedEventHandler. I provide overridable classes to implement your custom event handlers.

For the wrapper methods, I use JavaScript inside them, e.g.

' ========================================================================================
' Draws a circle.
' x, y : coordinates
' radius; Radius
' clr: The color of the pen
' nWidth : The width of the pen
' ========================================================================================
PRIVATE SUB CWebView2Canvas.DrawCircle( _
        BYVAL x AS SINGLE, BYVAL y AS SINGLE, _
        BYVAL radius AS SINGLE, _
        BYREF clr AS STRING, _
        BYVAL nWidth AS LONG = 1)

  ' // Make sure that the JS function exists
  IF m_hasDrawCircle = FALSE THEN
      DIM js AS STRING
      js = _
      "function drawCircle(x, y, radius, color, width) {" _
      & " ctx.strokeStyle = color;" _
      & " ctx.lineWidth = width;" _
      & " ctx.beginPath();" _
      & " ctx.arc(x, y, radius, 0, Math.PI * 2);" _
      & " ctx.stroke();" _
      & "}"
      this.ExecuteScript(js, NULL)
      m_hasDrawCircle = TRUE
    END IF

  ' // Call the JS function
  DIM js AS STRING
  js = "drawCircle(" _
      & STR(x) & "," & STR(y) & "," _
      & STR(radius) & ",'" _
      & clr & "'," & STR(nWidth) & ");"

  this.ExecuteScript(js, NULL)

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

If the JavaScript function does not exist, the wrapper adds it to the web page and then calls it with ExecuteScript.

As I still don't have much expertise with JavaScript, I ask the IA to write it for me.

It is not crossplatform, because WebView2 is a control for Windows, but the size of the executable of one small example with a CWindow's GUI, the CWebView2 and CWebView2Canvas classes, the event's class, DWSTRING and the wrappers for AfxNova, weights around 67 KB, not 600. As a simple example of CWindow with a button, weights 54.5 KB, this means that my WebView2 classes add 12.5 KB.

As the javascript functions are added on demand, the size of the application will grow depending on how many different ones you use. The size of the executable of the example that produces the capture below weights 82.5 KB and it is using 14 different functions.

We are not limited to graphics functions, but we can do everything: images, video, audio... and  even to use external libraries.


Title: Re: AfxNova progress
Post by: José Roca on June 09, 2026, 09:56:14 PM
The embeded WebView2 control also has contextual localized menus that allow to save or copy the image, and shortcut keys. For example, you can zoom the image with Ctrl+/-, or using Ctrl and the mouswheel. Scrollbars are added automatically if needed. And Ctrl+P activates the browser printer dalog, so we have printing and print preview for free. We can generate PDF files if we choose a driver like Microsoft Print to PDF or any other that you prefer.

We can also drop images, pdf files and other objects in the control.
Title: Re: AfxNova progress
Post by: José Roca on June 11, 2026, 01:24:00 PM
I have added methods to draw text that can also be used with effects to, for example, draw logos.

Title: Re: AfxNova progress
Post by: José Roca on June 13, 2026, 04:18:11 PM
The canvas can now display images, loaded from disk or a URL. With automatic scrolling, zoom, save and copy, print and preview, and with my "on-demand" function system, I can create a massive library without bloat. The 64-bit executable is 81 KB.
Title: Re: AfxNova progress
Post by: hajubu on June 14, 2026, 06:25:03 AM
Hi, I just updated my previous Html-Help for Tiko 1.3 , which is based on your md-DOCS-folder.

enclosed are two zip containing the changes , the instructions, and Readme
                    and the ready-to-use AfxNova_Web.

Readme_first_if_you_like. - Have Fun !

br. Hans (hajubu)

P.S:
ref_to_: details post May 29 - AfxNova progess  (https://www.planetsquires.com/protect/forum/index.php?msg=36589)

downloads available - Testpan_AfxNova_Web_260613__3fa6207 ->
   -> Planetsquires Software -> Tiko 1.3 and AfxNova Docs as integrated HelpTool (https://www.planetsquires.com/protect/forum/index.php?msg=36608)

Title: Re: AfxNova progress
Post by: hajubu on June 16, 2026, 04:07:08 PM
hi, just updated  available - Testpan_AfxNova_Web_260616_690f2a3->related to the Array Macros ( Jose made a commit today )

see for available upd
Updated AfxNova_Web_260616_0310 (https://www.planetsquires.com/protect/forum/index.php?msg=36608)

see also Jose repo at Github
https://github.com/JoseRoca/AfxNova (https://github.com/JoseRoca/AfxNova)
Title: Re: AfxNova progress
Post by: José Roca on June 20, 2026, 10:08:58 AM
Using JavaScript Libraries from FreeBasic via WebView2: A Powerful Hybrid Technique

One of the most exciting capabilities unlocked by embedding WebView2 into a FreeBasic application is the ability to combine native code with the full power of modern JavaScript libraries. In the attached example, I demonstrate how to draw shapes, Unicode text, and even emojis by using a class derived from CWebView2 that executes JavaScript commands from the Konva.js graphics framework.

This technique is far more than a simple trick. It represents a clean, extensible, and extremely powerful architectural pattern.

Why this technique is so effective

1. You get a modern 2D graphics engine "for free"

Konva.js is a high‑performance HTML5 canvas framework designed for shapes, layers, animations, and interactive graphics. By calling its API from FreeBasic through WebView2, you instantly gain:

* smooth vector rendering
* layers and groups
* hit‑testing and interactivity
* animations and tweens
* pixel‑perfect text rendering
* full Unicode support, including emojis

All of this without writing a single line of low‑level canvas code in FreeBasic.

2. Unicode "just works" — including emojis

Because the rendering is handled by the browser engine, you automatically inherit:

* full UTF‑8 support
* complex scripts
* emoji rendering
* font fallback
* anti‑aliasing and subpixel smoothing

This eliminates the usual headaches of Unicode text in native GUI frameworks.

I have added a property called JScript to my DWSTRING class to allow the use utf-16 strings instead of utf-8.


3. Clean separation of responsibilities

The derived classes will handle:

* generating the JavaScript code
* sending it to WebView2
* managing the canvas state
* abstracting away the browser details

Meanwhile, the JavaScript library handles:

* drawing
* layout
* animation
* event handling

This keeps the FreeBasic side clean and focused.

4. No global variables, no manual cleanup

Because the event handlers are implemented as real COM objects, they:

* register themselves
* unregister themselves in the destructor
* are released automatically when the WebView2 instance is destroyed

This means the user does not need to store pointers, call Delete, or manage lifetimes manually. The system is robust and self‑contained.

What else can we do with this technique?

The beauty of this architecture is that Konva.js is only the beginning. Any JavaScript library that runs in a browser can be controlled from FreeBasic simply by deriving a new class and exposing methods that generate the appropriate JavaScript.

Here are some possibilities.

1. Charts and data visualization

You can create a CWebView2Charts class that wraps libraries such as:

Chart.js
ECharts
D3.js
Plotly
ApexCharts

This would allow FreeBasic applications to display:

* bar charts
* line charts
* pie charts
* heatmaps
* real‑time data visualizations
* interactive dashboards

All with modern styling and smooth animations.

2. UI components and widgets

By wrapping libraries like:

* Bootstrap
* Material UI
* jQuery UI

you could embed:

* dialogs
* sliders
* tabs
* accordions
* responsive layouts

directly inside a FreeBasic application.

3. Maps and geospatial visualization

Using libraries such as:

* Leaflet
* Mapbox GL
* OpenLayers

you could display:

* interactive maps
* markers
* routes
* heatmaps
* GPS data

all controlled from FreeBasic.

4. Game‑style rendering

With libraries like:

* PixiJS
* Phaser

you could build:

* 2D games
* simulations
* particle effects
* sprite animations

inside a native FreeBasic window.

A flexible, extensible architecture

The key idea is simple:

Derive a class from CWebView2, and implement methods that generate JavaScript for the library you want to use.

Each derived class becomes a bridge between FreeBasic and a modern JavaScript framework.

This approach gives FreeBasic developers access to:

* modern graphics
* interactive UI components
* advanced visualization
* high‑level animation
* rich text and Unicode
* High DPI rendering consistency

All without rewriting existing libraries or reinventing the wheel.

Conclusion

By combining FreeBasic with WebView2 and JavaScript libraries like Konva.js, we unlock a hybrid development model that is both powerful and elegant. Native code handles performance‑critical logic, while JavaScript libraries provide modern rendering and UI capabilities.

This technique is not only practical — it is extensible. New derived classes can wrap any JavaScript framework, enabling FreeBasic applications to incorporate charts, maps, animations, widgets, and much more.

It's a modern approach that dramatically expands what FreeBasic applications can do.

P.S. I have added automatic scrollbars to the example.
Title: Re: AfxNova progress
Post by: José Roca on June 20, 2026, 10:16:05 AM
In the example attached in the previous post, clicking the mouse right button provides a localized menu with options to save or copy the content of the canvas. Ctrl+mouse wheel allows zooming, and Ctrl+P activates the Print/Preview dialog.
Title: Re: AfxNova progress
Post by: Paul Squires on June 22, 2026, 09:12:47 PM
Wow, that looks seriously impressive!

I am just thinking out loud - how would a FreeBasic programmer get FB data into the Javascript and then from the Javascript back to FB? It would be very convenient to be able to transfer data between the languages because you could use FB for the serious data intensive work and then Javascript for the visual stuff.
Title: Re: AfxNova progress
Post by: José Roca on June 23, 2026, 01:45:06 PM
This is done using JSON. WebView2 has methods to send and receive JSON strings. I already have JSON writer and reader classes that work with DWSTRING, BSTRING and DSafeArray. I'm procedeeing step by step because all this is new to me: asynchronous events, html, javascript, JSon... However, my wrapper classes will allow to use a javascript library like Konva as if it was a FreeBasic library, e.g. m_pCanvas->DrawText("text1", 10, 10, "This is a test string", 48, "Arial", "red", "transparent", 0, TRUE). Inside, the function does this:

' ========================================================================================
' Draws a text string on the Konva canvas.
' Parameters:
'   id          : Optional identifier for the text (case-sensitive)
'   x, y        : Coordinates of the text position (in pixels). Use -1, -1 to center the text.
'   dwsText     : The text string to display (Unicode aware)
'   fontSize    : Font size in pixels
'   fontFamily  : Font family name (e.g., "Arial", "Verdana")
'   fillColor   : Text fill color (HTML/CSS color string)
'   strokeColor : Outline color of the text (HTML/CSS color string)
'   strokeWidth : Width of the outline (in pixels)
'   draggable   : If TRUE, the text can be dragged with the mouse
' Description:
'   Creates a new Konva.Text object and adds it to the main layer.
'   If an id is provided, it is assigned to the shape so it can be
'   modified later (e.g., SetShadow, MoveTo, etc.)
' Usage example:
'   m_pCanvas->DrawText("txt1", 100, 150, "Hello world", 24, "Arial", "black", "transparent", 0, TRUE)
' ========================================================================================
PRIVATE SUB CWV2CanvasKonva.DrawText ( _
   BYREF id AS STRING = "", _
   BYVAL x AS LONG, _
   BYVAL y AS LONG, _
   BYREF dwsText AS DWSTRING, _
   BYVAL fontSize AS LONG = 20, _
   BYREF fontFamily AS STRING = "Arial", _
   BYREF fillColor AS STRING = "black", _
   BYREF strokeColor AS STRING = "transparent", _
   BYVAL strokeWidth AS LONG = 0, _
   BYVAL draggable AS BOOLEAN = FALSE)

   DIM idPart AS STRING
   IF id <> "" THEN idPart = "id:'" & id & "',"

   DIM js AS STRING
   js = _
      "var txt = new Konva.Text({" _
      & idPart _
      & "x:0," _
      & "y:0," _
      & "text:" & dwsText.JScript & "," _
      & "fontSize:" & STR(fontSize) & "," _
      & "fontFamily:'" & fontFamily & "'," _
      & "fill:'" & fillColor & "'," _
      & "stroke:'" & strokeColor & "'," _
      & "strokeWidth:" & STR(strokeWidth) & "," _
      & "draggable:" & IIF(draggable, "true", "false") _
      & "});" _
      & "layer.add(txt);" _
      & "var finalX = " & STR(x) & ";" _
      & "var finalY = " & STR(y) & ";" _
      & "var w = txt.width();" _
      & "var h = txt.height();" _
      & "if(finalX == -1){" _
      & "   finalX = (stage.width() - w) / 2;" _
      & "}" _
      & "if(finalY == -1){" _
      & "   finalY = (stage.height() - h) / 2;" _
      & "}" _
      & "txt.x(finalX);" _
      & "txt.y(finalY);" _
      & "layer.draw();"
   this.ExecuteScript(js, NULL)
END SUB
' ========================================================================================
Title: Re: AfxNova progress
Post by: José Roca on June 23, 2026, 02:21:52 PM
dwsText.JScript. JScript This is a property that I have added to DWSTRING to convert its content into a safe JavaScript expression for use with WebView2 ExecuteScript. You know, in the modern web browsers, all is javascript and utf-8.

' ========================================================================================
' JScript (PROPERTY)
' ------------------
' Converts the DWSTRING content into a safe JavaScript expression for use with
' WebView2 ExecuteScript.
'
' WebView2 requires UTF-8 text when passing strings to JavaScript. This property
' takes the internal UTF-8 byte sequence of the DWSTRING and generates a JS
' expression of the form:
'
'     new TextDecoder('utf-8').decode(Uint8Array.from([b1,b2,b3,...]))
'
' This guarantees that any Unicode text (including accents, symbols and emojis)
' is transmitted correctly to JavaScript without escaping issues, broken quotes,
' or encoding mismatches.
'
' The user can therefore pass DWSTRING values directly to ExecuteScript without
' worrying about UTF-8 handling or manual escaping.
'
' Usage example:
' m_pWebView->ExecuteScript("console.log(" & myString.JScript & ");")
'
' Notes:
' - The returned value is valid JavaScript code, not a literal string.
' - Ideal for passing arbitrary text safely from FreeBasic to JavaScript.
' ========================================================================================
' ========================================================================================
PRIVATE PROPERTY DWSTRING.JScript () AS STRING
   DWSTRING_DP("")
   DIM utf8Text AS STRING = this.utf8
   DIM p AS UBYTE PTR = STRPTR(utf8Text)
   DIM js AS STRING = "new TextDecoder('utf-8').decode(Uint8Array.from(["
   DIM L AS LONG = LEN(utf8Text)
   FOR i AS LONG = 0 TO L - 1
      js += STR(p[i])
      IF i < L - 1 THEN js += ","
   NEXT
   js += "]))"
   RETURN js
END PROPERTY
' ========================================================================================
Title: Re: AfxNova progress
Post by: hajubu on June 30, 2026, 12:10:52 PM
hi, just updated  available - Testpan_AfxNova_Web_260629_a0d4caa->related to the Webview2  ( Jose made a commit today ) (+doc 'CRegexClass.md' )

see for available updates in the Forum ...

... Updated AfxNova_Web_260629_a0d4caa (https://www.planetsquires.com/protect/forum/index.php?action=dlattach;attach=3824)

... Tiko 1.3 and AfxNova Docs as integrated HelpTool (https://www.planetsquires.com/protect/forum/index.php?msg=36608)

PS: if you are you using "AfxNova_webview2_2026.bas" as basis,
please,  do not forget to adapt "AfxNovaWeb.ini".

... ReadMe_AfxNovaWeb2_2026 (https://www.planetsquires.com/protect/forum/index.php?action=dlattach;attach=3812)


see also Jose repo at Github for the latest changes
https://github.com/JoseRoca/AfxNova

Title: Re: AfxNova progress
Post by: José Roca on July 03, 2026, 03:50:25 AM
Besides Konva, I'm also adding support for SVG, HTML5 and CSS. We can use HTML5 elemens like buttons, labels, images, videos, audio...
Title: Re: AfxNova progress
Post by: Paul Squires on July 03, 2026, 08:08:18 AM
I have to start thinking of what new application that I can build in order to dive into this new world of webview based applications. Maybe some sort of stock/finance tracker... something that I'd use daily so it'd give be incentive to keep building it. I am back deep into coding Tiko, so another application would be a good distraction so that I don't burn out just coding Tiko.  :-)
Title: Re: AfxNova progress
Post by: José Roca on July 03, 2026, 02:06:48 PM
I don't usually write applications anymore. What I enjoy now is building tools that integrate new technologies.

WebView2 offers plenty of technical challenges, and that's exactly what makes it interesting.

The library I'm working on behaves like a synchronous, standard Win32 library — without requiring the developer to write HTML or JavaScript directly.

For events such as click, mousemove, etc., I'm working on a mechanism inside the internal CWebView2 class to register and receive them.

These events will be translated into WM_NOTIFY messages, so they can be handled just like any other native notification in a traditional Win32 application.