FireFly 3 (High DPI aware progress)

Started by Paul Squires, February 23, 2012, 10:08:52 AM

Previous topic - Next topic

Paul Squires

Hi Everyone,

Of all the issues surrounding FireFly 3, the most pressing at this time is ensuring that FireFly is High DPI aware and programs created by FireFly are also High DPI aware. With the help of Jose Roca, I now have a pretty good understanding of the issues involved. Using Jose's cWindow class for code generation and also for internal code to the FireFly program itself, this is making the transition easier.

I have started this thread in order to post the progress I am making in this area. I will update this every time an advancement in High DPI awareness is made.

As you can imagine, the FireFly program itself is HUGE. There are thousands of lines of source code. Even though FireFly itself is developed and compiled using FireFly, there are still many internals that were created outside of the cWindow class. All of those create windows and fonts need to be changed to use cWindow, etc. Pretty big task but at least now I have the motivation to make it happen.  :)

Paul Squires
PlanetSquires Software

Paul Squires

For code generation, all fonts that are created flow through the internal function FLY_SetControlData. That function has now been successfully modified to pass the pWindow interface pointer as a parameter. The CreateFont method of the pWindow class is now used to create the necessary fonts. This means that fonts are now automatically scaled correctly based on the current DPI settings.
Paul Squires
PlanetSquires Software

Paul Squires

I have now gone through all Forms internally to FireFly and manually adjusted Label widths, ComboBox widths, CommandButton widths, etc... in order to accommodate potential space issues that DPI adjusted fonts would cause. In some cases I had to increase the physical size of the Forms themselves in order to ensure that the increase in control sizes did not result in overly cluttered looking Forms.

I have also added the High DPI xml code to the Manifests:

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

Paul Squires
PlanetSquires Software

Paul Squires

More progress to report today. I was finally able to get the design form in FireFly to match the high dpi code generated forms that cWindow produces. It was nice to finally see the sizes of the forms, controls and fonts finally mirror each other.

Still lots to do but I am implementing the fixes faster than I thought I could.

Eventually I will post some type of test version of FireFly35.exe for you guys to test on your systems.
Paul Squires
PlanetSquires Software

Paul Squires

The tab control that displays in the FireFly design environment is actually a custom control that I wrote (this is the tab control that displays the form/module names across the top of the designer, and the various tabs along the bottom like "Handles and ID's", "Functions Library", "Image Library", etc.). It is used on several dialogs within the FireFly environment.

I have added code to that custom control to make it high dpi aware.

I basically cut out code from cWindow. Here is what I did - I am posting it here because I will probably have to reuse this info when modifying other custom controls.

I added 3 new entries into the dynamically allocated TYPE memory for the control:

Type VSTAB_DATA       
   ...
   ...
   ...
   m_rx          As Single ' high DPI horizontal scaling ratio
   m_ry          As Single ' high DPI vertical scaling ratio
   m_DPI         As Single ' DPI returned by operating system
End Type


During the creation of the control, after the data pointer is allocated, I check for DPI data and set the normal and bold fonts:

       ' // Default DPI ratios/value
       @ed.m_rx  = 1
       @ed.m_ry  = 1
       @ed.m_DPI = 96

       If AfxGetUseDpiScaling = 0 Then
          SetDPI ed, 96
       Else
          SetDPI ed, -1
       End If
       

       If AfxGetWindowsVersion => 6 Then nPointSize = 9 Else nPointSize = 8

       hDC = GetDC(%Null)
       GetObject GetStockObject(%DEFAULT_GUI_FONT), SizeOf(lf), ByVal VarPtr(lf)
       nPointSize = (nPointSize * @ed.m_DPI) \ GetDeviceCaps(hDC, %LOGPIXELSY)
       lf.lfHeight   = -MulDiv( nPointSize, GetDeviceCaps( hDC, %LOGPIXELSY), 72 )
       @ed.hFont     = CreateFontIndirect(lf)
       lf.lfWeight   = %FW_BOLD
       @ed.hFontBold = CreateFontIndirect(lf)
       ReleaseDC %Null, hDC


I include a simple version of the cWindow DPI method as a function in order to do the heavy stuff.

' =====================================================================================
' Sets the DPI (dots per pixel) to be used by the application.
' Pass -1 to use the value returned by the GetDeviceCaps API function.
' Note: Set this value if you want to make your application High-DPI aware.
' The main window, controls and fonts will be scaled if the user changes the DPI setting.
' =====================================================================================
Function SetDPI( ByVal ed  As VSTAB_DATA Ptr, _
                 ByVal dpi As Single _
                 ) As Long
                 
   Local hDC As Dword
   @ed.m_DPI = dpi
   ' // Get the screen device context
   hDC = GetDC(%Null)
   If @ed.m_DPI < 0 Then
      @ed.m_DPI = GetDeviceCaps(hDC, %LOGPIXELSX)
      dpi = @ed.m_DPI
   End If
   ' // Resolution ratio = current resolution / 96
   If dpi < 0 Then
      @ed.m_rx = (GetDeviceCaps(hDC, %LOGPIXELSX) / 96)
      @ed.m_ry = (GetDeviceCaps(hDC, %LOGPIXELSY) / 96)
   Else
      @ed.m_rx = dpi / 96
      @ed.m_ry = dpi / 96
   End If
   ReleaseDC %Null, hDC
   
End Function


I will test this tonight on my laptop using different DPI settings to see how the custom tab control responds.
Paul Squires
PlanetSquires Software

Paul Squires

Update to the last post.... turns out that I am overly complicating things. The cWindow class is always available to me within FireFly so I can modify the vsTab custom control to create a temporary class and generate the fonts that way.


    Local pWindow As iWindow
    pWindow = Class "cWindow"
    If IsNothing(pWindow) Then Exit Function
   
    @ed.hFont     = pWindow.CreateFont("Tahoma", pWindow.DefaultFontSize, %FW_NORMAL, %FALSE, %FALSE, %FALSE, %DEFAULT_CHARSET)
    @ed.hFontBold = pWindow.CreateFont("Tahoma", pWindow.DefaultFontSize, 700, %FALSE, %FALSE, %FALSE, %DEFAULT_CHARSET)
       
    pWindow = Nothing


Much easier.
Paul Squires
PlanetSquires Software

Paul Squires

Noticed a problem with the values being displayed in the ChooseFont api call that FireFly uses to allow the user to select a font. At times it is possible that the Point Size value is not set thus causing a strangely sized font to get created. This only seemed to happen when the dpi sizes were changed back and forth. I decided to change to using PB's built in DIALOG FONT function. This change meant that the way FireFly stores font information had to change. Code is now built in to automatically handle the older LOGFONT style data to the newer PB style that only uses 3 values (font name, point size, style value).

Testing has proven that the change was worth the effort. The Point Size is now always correctly set even when DPI settings are changed back and forth.
Paul Squires
PlanetSquires Software

Paul Squires

I learned another valuable lesson about fonts and scaling. A fair number of internal FireFly dialogs used "MS Sans Serif" for its font. That font does not scale well at different DPI settings (this is an older font not to be confused with the newer Microsoft Sans Serif that should scale well).

Even though Vista/7 use Segoe as its default font, I can not be 100% sure that the font will exist on WinXP and even Win2000 that FireFly still supports. Therefore, I am changing all internal fonts to Tahoma. That was the standard font for WinXP and Vista. I have also noticed that using a point size of 9 as the minimum works well at most DPI settings.

So, for your applications you should also be aware of the font that you use could have a big effect on how your application looks when it is scaled. Better off changing your fonts now rather than later.
Paul Squires
PlanetSquires Software

Paul Squires

Another lesson learned....

Sometimes you want to position a window/control to an absolute position. You need to scale that pixel position because if your DPI is not 100% then the positioning of the window will display other than you where you expect it to.


      SetWindowPos ghWndIcon, %HWND_TOP, ScaleX(445), ScaleY(175), ScaleX(64), ScaleY(64), %SWP_SHOWWINDOW


Jose's cWindow class has a SetWindowPos method built into it so you can use that to make things easier.


      pWindow.SetWindowPos ghWndIcon, %HWND_TOP, 445, 175, 64, 64, %SWP_SHOWWINDOW

Paul Squires
PlanetSquires Software

Paul Squires

Likewise (and obvious), if you are manually creating child windows/controls then you need to manually scale them as well. If you are using Jose's cWindow then the scaling of windows and child controls happens automatically.
Paul Squires
PlanetSquires Software

Paul Squires

Things are still progressing along nicely. New projects are working well. I still need to test Tab Controls and External Custom Controls. I am thinking of modifying Tab Control code generation to use cWindow.

I am now testing loading of existing projects. I need to do some modifications to the size/position properties of existing project's forms/controls mainly due to the way that FireFly loads projects, assigns property values, and then applies those properties. Hopefully it won't be as problematic as I think it will be.
Paul Squires
PlanetSquires Software

Paul Squires

#11
Funny how much of an idiot I can be. I have been looking at the same problem for two days straight and not understanding why everything on the designer form looks perfectly scaled but the resulting compiled EXE for a project would not be. Turns out I had a "High DPI" option switch built into the designer and hard coded the value to %TRUE which essentially disables scaling when outputted to the cWindow class. Arrgh - my logic was backwards.

Now the compiled programs look amazing at High DPI. Much better than the virtualized versions Windows produces.

Next, the external custom controls and Tab Controls. That should be it.

(Edit: Also need to look at OCX's with the view of using the built in cWindow code. Also need to review Rebars/ToolBars to ensure that they will be scaled properly).
Paul Squires
PlanetSquires Software

Paul Squires

External custom controls - done.
OCX controls - done. (They now use cWindows .AddOCX method)
Paul Squires
PlanetSquires Software

Paul Squires

StatusBars - done.
ToolBars - done.

TabControls are next...
Paul Squires
PlanetSquires Software

Paul Squires

TabControls appear to be working fine. The control itself is scaling and the child pages are scaling as well.
Paul Squires
PlanetSquires Software