FF3 with PB10 ... it works !

Started by Jean-pierre Leroy, March 30, 2011, 12:39:23 PM

Previous topic - Next topic

Jean-pierre Leroy

Dear all,

I know that Paul will release probably very soon a new FireFly version (FF4 ?) to be fully compatible with PB10.

But in the meantime if you are eager (like me) to test and use the new PB10 statements and functions with the current version of FireFly (version 3.10), there are only a few steps to follow; the good news is that FF3 is almost fully compatible with PB10.

Step1 - Download and install PB10
Step2 - Download and install the new Windows API Headers v.2.0 Build 1 made by Jose Roca; they are available here http://www.jose.it-berater.org/smfforum/index.php?topic=4054.0
Don't forget to download as well the very complete and informative help file, made by Jose.
Step3 - Set the environment inside FireFly (see below the enclosed screenshot)
Step4 - 5 controls need to be updated to be fully compatible with the new include files; before updating these .inc files, do not forget to make a copy of the original files.

4.1 In /Program Files/FireFly3 Visual Designer/CustomControls/Borje Hagsten/RRButton/RRBUTTON.INC
Replace the line
If PtInRect(rc, pt.x, pt.y) Then
by
If PtInRect(rc, pt) Then

4.2 In /Program Files/FireFly3 Visual Designer/CustomControls/Jose Roca/TB_XPButton/TB_XPBUTTON.inc
Replace the line
IF PtInRect(rc, pt.x, pt.y) THEN
By
IF PtInRect(rc, pt) THEN

4.3 In /Program Files/FireFly3 Visual Designer/CustomControls/PlanetSquires/FireImage/FireImage.inc
Replace the line
If PtInRect(rc, pt.x, pt.y) = %FALSE Then
By
If PtInRect(rc, pt) = %FALSE Then

4.4 In /Program Files/FireFly3 Visual Designer/CustomControls/PlanetSquires/FireLink/FireLink.inc
Replace the line
Function = PtInRect(rc, pt.x, pt.y)
By
Function = PtInRect(rc, pt)

4.5 In /Program Files/FireFly3 Visual Designer/CustomControls/PlanetSquires/FireTextBox/FireTextBox.inc
Replace the line
Function = PtInRect(@ed.SelectorRECT, pt.x, pt.y)
By
Function = PtInRect(@ed.SelectorRECT, pt)

Obvioulsy, you have to update these include files only if you use one of these control; other than that most of my FF3 projects are fully compatible with PB10; don't forget to read also the useful recommandations made by Theo Gottwald on the PB Forum: http://www.powerbasic.com/support/pbforums/showthread.php?t=46933

LIMITATIONS REGARDING UNICODE WITH FF 3.10 and PB10

Even if we add the line
%UNICODE = 1 in the FF_AppStart module, we will get an error message (see below the enclosed screenshot)
So to work with unicode transparently with FireFly, we have to wait for the new FF4 from Paul.

Hope that helps.
Jean-Pierre



Marc van Cauwenberghe


Haakon Birkeland

Great service Jean-Pierre â€" and Jose!

Now I'm gonna look for that discussion; SEARCH donation NEAR Jose ...
Haakon 8o)

Haakon Birkeland

Darn. Anyone participate in that?

I just can't seem to find the thread. Donate/donation(s) seems to be a scarcely used word on the forums ...
Haakon 8o)

José Roca

Quote
Even if we add the line

%UNICODE = 1

in the FF_AppStart module, we will get an error message (see below the enclosed screenshot)
So to work with unicode transparently with FireFly, we have to wait for the new FF4 from Paul.

This is because szClassName must be declared as WSTRINGZ instead of ASCIIZ, e.g..


#IF %DEF(%UNICODE)
   LOCAL szClassName AS ASCIIZ * 260
#ELSE
   LOCAL szClassName AS WSTRINGZ * 260
#ENDIF


José Roca

I have provided alternate functions to the functions that use a POINT structure. So, instead of


Function = PtInRect(rc, pt)


You can use


Function = PtInRectXY(rc, pt.x, pt.y)


if you prefer it.

Indeed, my new headers are more compatible with FF that the new PB ones.

I also have used, whenever possible, some compatibility tricks. For example, RECT is now an union:


#IF NOT %DEF(%RECT_DEFINED)
%RECT_DEFINED = 1
' // Size = 16 bytes
TYPE OLD_RECT_STRUCT DWORD   ' Old PB definition
   nLeft   AS LONG   ' LONG left
   nTop    AS LONG   ' LONG top
   nRight  AS LONG   ' LONG right
   nBottom AS LONG   ' LONG bottom
END TYPE

' // Size = 16 bytes
TYPE tagRECT DWORD
   Left   AS LONG   ' LONG left
   Top    AS LONG   ' LONG top
   Right  AS LONG   ' LONG right
   Bottom AS LONG   ' LONG bottom
END TYPE

' // GDI+ uses x, y, Width and Height as members instead of Left, Right, Top and Bottom
TYPE GDIP_RECT_STRUCT DWORD
   x      AS LONG   ' LONG x
   y      AS LONG   ' LONG y
   Width  AS LONG   ' LONG Width
   Height AS LONG   ' LONG Height
END TYPE

' // To allow the use of both nLeft, etc, and Left, etc.
' // Size = 16 bytes
UNION RECT
   OLD_RECT_STRUCT
   tagRECT
   GDIP_RECT_STRUCT
END UNION
#ENDIF


This allows to use rc.nLeft or rc.Left or rc.x.

Paul Squires

Jose, I have been looking at your CWindow class. I am considering re-working FF4 to only accept PB10 compiler, only use your Includes, and output CWindow code. (Maybe those are too strict limitations(?))

From my side, I would first modify the FF3 code generator to produce CWindow code, and then re-compile FF3 using FF3 itself to produce an FF4 built on the FF3/CWindow code output. FF4 would then be dpi aware and fully unicode enabled.

Anyway, it is a good theory that may work....  :)
Paul Squires
PlanetSquires Software

José Roca

#7
It's worth a try. If the class lacks something needed, it can be added easily.

We have to face the fact that things have changed a lot with Windows 7. I wasn't aware until I have bought a 20" monitor and used high resolution (until now I was working at 600x800). If a program isn't High DPI aware, Windows 7 virtualizes it. At a first look, everything looks normal, although I noticed that the fonts and controls didn't looked the same as the ones of the operating system and the ones of applications such Office. But with time, you notice artifacts and other problems (for example, drag and drop doesn't work properly). And many third party applications become unusable or almost.

Since DDT uses the Windows Dialog Engine, DDT applications work properly if you make them High DPI aware, because Windows takes care of it. My class is more versatile: if you do nothing, the size is the one that corresponds to the screen resolution (see attached picture 1), if you use pWindow.DPI = -1, then the size is scaled according the DPI setting (see attached picture 2), and any size higher or lower than the DPI setting increases or decreases the size. And the controls and fonts look the same as the ones of the operating system, and there are no artifacts and drag and drop works properly.

Compare the fonts of the High DPI aware program with the ones of the editor, that is working virtualized... Also forget to use stock fonts, True Type fonts must be used for best results.

Unicode is also very important. It is not only a problem with the Chinese, as many North Americans seems to think. 2/3 or more of the world population needs it, including North Eurpeans, East Europeans and Greeks.

I have designed the CWindow class as a way to provide a sort of DDT for SDK programmers, but without the limitations of the Windows Dialog Engine.

The future is Windows 7 and the next versions of Windows, not XP, and we have to be ready for it.

José Roca


José Roca

#9
This is the GUI code that produces the High DPI aware screen in the above attached pictures:


' // Create an instance of the class
LOCAL pWindow AS IWindow
pWindow = CLASS "CWindow"
IF ISNOTHING(pWindow) THEN EXIT FUNCTION

pWindow.SetProcessDPIAware
pWindow.DPI = -1

' // Create the main window
LOCAL hwnd AS DWORD
hwnd = pWindow.CreateWindow(%NULL, $PROGNAME, 0, 0, 0, 0, _
   %WS_OVERLAPPED OR %WS_VISIBLE OR %WS_MINIMIZEBOX OR %WS_SYSMENU, _
   %WS_EX_WINDOWEDGE, CODEPTR(WndProc))
pWindow.SetClientSize 692, 420
' // Center the window
pWindow.CenterWindow hwnd

' // Set the icons
pWindow.BigIcon = LoadIcon(hInstance, BYVAL %IDI_BACKUPMGR32)
pWindow.SmallIcon = LoadIcon(hInstance, BYVAL %IDI_BACKUPMGR16)

' // Load the RichEdit library
LOCAL hRichEdit AS DWORD
hRichEdit = LoadLibrary("RICHED32.DLL")
IF ISFALSE hRichEdit THEN
   MessageBox hwnd, "Unable to load RICHED32.DLL", "Error!", %MB_ICONERROR OR %MB_OK OR %MB_APPLMODAL
   EXIT FUNCTION
END IF

' // Add the controls
LOCAL hCtl AS DWORD

pWindow.AddStatusBar(hwnd, %IDC_STATUSBAR, "", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR %WS_CLIPCHILDREN OR %WS_CLIPSIBLINGS OR %CCS_BOTTOM)
' // Update the size of the statusbar
SendMessage GetDlgItem(hwnd, %IDC_STATUSBAR), %WM_SIZE, 0, 0

pWindow.AddLabel(hwnd, -1, "Records", 15, 14, 215, 15, -1, -1)

pWindow.AddListBox(hwnd, %IDC_LBITEMS, "", 0, 0, 0, 0, %WS_CHILD OR %WS_VISIBLE OR %WS_HSCROLL OR %WS_VSCROLL OR %WS_BORDER OR %WS_TABSTOP OR _
   %LBS_EXTENDEDSEL OR %LBS_HASSTRINGS OR %LBS_SORT OR %LBS_NOTIFY, -1)
pWindow.SetWindowPos GetDlgItem(hwnd, %IDC_LBITEMS), %NULL, 15, 30, 215, 350, %SWP_NOZORDER
SendMessage GetDlgItem(hwnd, %IDC_LBITEMS), %LB_SETHORIZONTALEXTENT, 600, 0

pWindow.AddLabel(hwnd, -1, "Code", 240, 14, 440, 15, -1, -1)
hRichEdit = pWindow.AddRichEdit(hwnd, %IDC_RICHEDIT, "", 240, 30, 440, 311, -1, -1)

pWindow.AddButton(hwnd, %IDB_EXPORT, "&Export", 294, 352, 65, 26, -1, -1)
pWindow.AddButton(hwnd, %IDB_DELETE, "&Delete", 374, 352, 65, 26, -1, -1)
pWindow.AddButton(hwnd, %IDB_DELETEALL, "Delete &All", 454, 352, 65, 26, -1, -1)
pWindow.AddButton(hwnd, %IDB_COPY, "&Copy", 534, 352, 65, 26, -1, -1)
pWindow.AddButton(hwnd, %IDB_EXIT, "E&xit", 614, 352, 65, 26, -1, -1)

' // Default message pump
pWindow.DoEvents(nCmdShow)

' // Free the Rich Edit library
IF hRichEdit THEN FreeLibrary hRichEdit


Instead of calling pWindow.SetProcessDPIAware in the program, it is advisable to use a manifest, e.g.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >

      <assemblyIdentity version="1.0.0.0"
         processorArchitecture="X86"
         name="CSED_BACKUPMGR"
         type="win32"/>
      <description>CSED_BACKUPMGR - Utility for the CSED editor</description>

      <!-- Compatibility section -->
      <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
         <application>
            <!--The ID below indicates application support for Windows Vista -->
            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
            <!--The ID below indicates application support for Windows 7 -->
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
         </application>
       </compatibility>

      <!-- Trustinfo section -->
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
         <security>
            <requestedPrivileges>
               <requestedExecutionLevel
                  level="asInvoker"
                  uiAccess="false"/>
               </requestedPrivileges>
         </security>
      </trustInfo>

      <dependency>
         <dependentAssembly>
            <assemblyIdentity
               type="win32"
               name="Microsoft.Windows.Common-Controls"
               version="6.0.0.0"
               processorArchitecture="X86"
               publicKeyToken="6595b64144ccf1df"
               language="*" />
         </dependentAssembly>
      </dependency>

      <asmv3:application>
         <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>true</dpiAware>
         </asmv3:windowsSettings>
      </asmv3:application>

   </assembly>


Windows XP and below aren't High DPI aware, but allow the use of large and custom fonts. Although I haven't tested my class with XP, it should work.

José Roca

#10
I was having a problem with the CreateWindow method when using High DPI and %CW_USEDEFAULT, but it has been easy to fix.

I have replaced


      m_hwnd = CreateWindowEx(dwExStyle, BYVAL dwClass, BYCOPY strTitle, dwStyle, _
               x * m_rx, y * m_ry, nWidth * m_rx, nHeight * m_ry, hParent, %NULL, m_hInstance, tCreateParams)


with


      ' // CW_USEDEFAULT is a special value. Therefore, don't multiply it by the scaling factor.
      m_hwnd = CreateWindowEx(dwExStyle, BYVAL dwClass, BYCOPY strTitle, dwStyle, _
               IIF&(x = %CW_USEDEFAULT, %CW_USEDEFAULT, x * m_rx), _
               IIF&(y = %CW_USEDEFAULT, %CW_USEDEFAULT, y * m_ry), _
               IIF&(nWidth = %CW_USEDEFAULT, %CW_USEDEFAULT, nWidth * m_rx), _
               IIF&(nHeight = %CW_USEDEFAULT, %CW_USEDEFAULT, nHeight * m_ry), _
               hParent, %NULL, m_hInstance, tCreateParams)


and is working fine now.

José Roca

#11
While Bob is adding obsolete GDI graphic statements, we can use the latest technologies, such Direct2D.

Here is a CWindow template that produces the result displayed in the attached picture. Notice the smoothness of the circle.


#COMPILE EXE
#DIM ALL

' // Include files for external files
#INCLUDE ONCE "CWindow.inc"            ' // CWindow class
#INCLUDE ONCE "d2d1Helper.inc"         ' // Helper class

GLOBAL g_pD2DFactory AS ID2D1Factory   ' // ID2D1Factory interface
GLOBAL g_pD2DHelper AS ID2D1Helper     ' // ID2D1Helper interface

' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG

   ' // Create an instance of the CWindow class
   LOCAL pWindow AS IWindow
   pWindow = CLASS "CWindow"
   IF ISNOTHING(pWindow) THEN EXIT FUNCTION

   ' // Create D2D factory
   D2D1CreateFactory2(%D2D1_FACTORY_TYPE_SINGLE_THREADED, g_pD2DFactory)
   IF ISNOTHING(g_pD2DFactory) THEN EXIT FUNCTION

   ' // Create an instance of the CD2D1Helper class
   g_pD2DHelper = CLASS "CD2D1Helper"
   IF ISNOTHING(g_pD2DHelper) THEN EXIT FUNCTION

   ' // Create the application window.
   pWindow.CreateWindow(%NULL, "Direct2D Demo App", %CW_USEDEFAULT, %CW_USEDEFAULT, _
      320, 340, -1, -1, CODEPTR(WindowProc))

   ' // Default message pump (you can replace it with your own)
   pWindow.DoEvents(nCmdShow)

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

' ========================================================================================
' Main callback function.
' ========================================================================================
FUNCTION WindowProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG

   ' // Process window mesages
   SELECT CASE uMsg

      CASE %WM_COMMAND
         SELECT CASE LO(WORD, wParam)
            CASE %IDCANCEL
               ' // If the Escape key has been pressed...
               IF HI(WORD, wParam) = %BN_CLICKED THEN
                  ' // ... close the application by sending a WM_CLOSE message
                  SendMessage hwnd, %WM_CLOSE, 0, 0
                  EXIT FUNCTION
               END IF
         END SELECT

      ' // Because the render target is a window (as opposed to a bitmap or other
      ' // offscreen surface), drawing is done in response to WM_PAINT messages.
      CASE %WM_PAINT, %WM_DISPLAYCHANGE
         ' // Render the scene
         LOCAL ps AS PAINTSTRUCT
         BeginPaint hwnd, ps
         RenderScene hwnd, ps
         EndPaint hwnd, ps
         EXIT FUNCTION

      CASE %WM_DESTROY
         ' // End the application
         PostQuitMessage 0
         EXIT FUNCTION

   END SELECT

   ' // Pass unprocessed messages to Windows
   FUNCTION = DefWindowProc(hwnd, uMsg, wParam, lParam)

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

' ========================================================================================
' This function draws Direct2D content to a GDI HDC.
' This function will automatically discard device-specific resources if the D3D device
' disappears during function invocation, and will recreate the resources the next time
' it's invoked.
' ========================================================================================
FUNCTION RenderScene (BYVAL hwnd AS DWORD, BYREF ps AS PAINTSTRUCT) AS LONG

   LOCAL hr AS LONG
   STATIC pRenderTarget AS ID2D1DCRenderTarget
   STATIC pBlackBrush AS ID2D1SolidColorBrush

   ' // Create a DC render target.
   IF ISNOTHING(pRenderTarget) THEN
      LOCAL props AS D2D1_RENDER_TARGET_PROPERTIES
      props = g_pD2DHelper.RenderTargetProperties(%D2D1_RENDER_TARGET_TYPE_DEFAULT, _
              g_pD2DHelper.PixelFormat(%DXGI_FORMAT_B8G8R8A8_UNORM, %D2D1_ALPHA_MODE_IGNORE), _
              0, 0, %D2D1_RENDER_TARGET_USAGE_NONE, %D2D1_FEATURE_LEVEL_DEFAULT)
      hr = g_pD2DFactory.CreateDCRenderTarget(props, pRenderTarget)
      IF SUCCEEDED(hr) THEN
         ' // Create a black brush.
         hr = pRenderTarget.CreateSolidColorBrush(g_pD2DHelper.ColorF_3(%D2D1_Black), BYVAL %NULL, pBlackBrush)
      END IF
   END IF

   IF SUCCEEDED(hr) THEN
      ' // Get the dimensions of the client drawing area.
      LOCAL rc AS RECT
      GetClientRect(hwnd, rc)
      ' // Bind the DC to the DC render target.
      hr = pRenderTarget.BindDC(ps.hdc, rc)
      ' // 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(g_pD2DHelper.ColorF_3(%D2D1_White))
      ' // Sample code: Draws an ellipse (replace it with your drawing operations)
      pRenderTarget.DrawEllipse(g_pD2DHelper.Ellipse(g_pD2DHelper.Point2F(150.0!, 150.0!), 100.0!, 100.0!), pBlackBrush, 3.0!)
     
      ' // 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 ID2D1RenderTarget::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
   END IF

   ' // Direct2D signals a lost device by returning the error code D2DERR_RECREATE_TARGET
   ' // from the EndDraw method. If you receive this error code, you must re-create the
   ' // render target and all device-dependent resources.
   IF hr = %D2DERR_RECREATE_TARGET THEN
      ' // To discard a resource, simply release the interface for that resource.
      pRenderTarget = NOTHING
      pBlackBrush = NOTHING
      hr = %S_OK
   END IF

   FUNCTION = hr

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


José Roca

#12
The only problem that I had to develop extensive classes was the bloat they added to the application. Fortunately, I was able to convince Bob that it was possible to strip unused methods and properties from internal classes without any ill effect.

Now, nothing can stop me to extend the language. The new headers already have a CVarUtils class with about 120 methods that adds all the missing functionality to deal with variants still not implemented in PB, an ODBC class with hundreds of methods, a class to host the .NET runtime in a PB application, a class with 1,720 methods for OpenGL extensions, etc., etc., etc.

We are going to have fun!

José Roca

Subclassing is as easy as to pass the address of the callback procedure with CODEPTR.


pWindow.AddButton(pWindow.hwnd, %IDCANCEL, "&Close", 100, 100, 75, 23, -1, -1, CODEPTR(TextBtn_SubclassProc))


Adding tooltips is as easy as


pWindow.AddTooltip(hButton, "I'm a button")


And you can change the text of the tooltip without deleting and recreating it with the SetTooltipText method. And works with unicode.

And adding an Active control is as easy as


hCtl = pWindow.AddOCX(pWindow.hwnd, %IDC_OCX, "MSCAL.Calendar", "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight, -1, -1)


And the graphic control is now GDI+ aware, i.e. no need for GdiPlusStartup and GdipShutdown.


hCtl = pWindow.AddGdipGraphCtx(pWindow.hwnd, %IDC_GRCTX, "", 0, 0, pWindow.ClientWidth, pWindow.ClientHeight, -1, -1)


And I also have a MDI template.

And I have to stop now because I have to go to sleep :)

Jean-pierre Leroy

#14
Quote from: TechSupport on March 30, 2011, 10:02:15 PM
Jose, I have been looking at your CWindow class. I am considering re-working FF4 to only accept PB10 compiler, only use your Includes, and output CWindow code. (Maybe those are too strict limitations(?))

From my side, I would first modify the FF3 code generator to produce CWindow code, and then re-compile FF3 using FF3 itself to produce an FF4 built on the FF3/CWindow code output. FF4 would then be dpi aware and fully unicode enabled.

Anyway, it is a good theory that may work....  :)


Paul,

Do you think you can organize FireFly updates in 2 steps:

First update - To upgrade FF3.10 with the current functionalities but make the necessary modifications to be fully compatible with PB10; I'm thinking of :


  • Adding a check box "Unicode project" in the Project Properties; the %UNICODE=1 will then allow us to generate immediately fully compliant Unicode applications with the new Jose Roca include files
  • Update the controls include files that need to be modified to be fully compatible with the new Jose Roca include files
  • Adding the new PB10 keywords
  • ...

Second update - probably a major upgrade with the generation of "C Window code" and creation of applications that are High DPI aware.

The first update could be a kind of "Quick win" and everything FireFly users could benefit relatively quickly from the new PB10 statements and functions.
The second update with the generation of "C Window code" and the creation of applications that are High DPI aware will be probably a more complex step with a lot of tests, beta versions etc ...

What do you think ? Are they reasonable and feasible requests ?

FireFly, always a step ahead.

Regards,
Jean-Pierre