FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)
Posts: 6984
Joined: Fri Oct 07, 2005 07:07 PM

Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 06:30 PM

Dear all,

we are increasingly working with larger monitors (Full HD / 4K) and mostly via RDP sessions.

Using Windows scaling at 125% makes the whole operating system look oversized and inconsistent, so we would prefer to keep Windows at 100% DPI and scale only our FiveWin application.

Typical issue: Functions like MSGINFO() (Windows MessageBox) become difficult to read on large displays.

My question:

What is the recommended and cleanest way to implement global UI scaling in FiveWin, for example a 125% factor, without modifying every dialog manually?

Possible directions I am considering:

A global font scaling approach (e.g. central default font override)

A system-wide scale factor inside TWindow

A FiveWin-native alternative to MessageBox() that respects application scaling

A best practice pattern used by others in production environments

How are you handling modern display sizes in real-world FiveWin applications?

I would appreciate hearing practical approaches from those who have already solved this cleanly.

Kind regards, Otto

Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:12 PM

Dear Otto,

Both Alvaro Quintana and Carlos Vargas have done research about DPI support on FWH.

Lets review DeepWiki analysis about it:

# Recommended Approaches for Global UI Scaling in FiveWin

Based on the FiveWin codebase, here are the cleanest and most production-ready approaches to implement global 125% UI scaling without modifying individual dialogs:

1. Foundation: Enable DPI Awareness (Required First Step)

Add a manifest file to your application to declare DPI awareness. This is essential for Windows 10/11 to respect your application-level scaling: 0-cite-0

This tells Windows your application will handle DPI scaling itself, preventing Windows from applying bitmap scaling to your UI.

2. Recommended Approach: TLayoutManager Class (Newest & Cleanest)

FiveWin recently added the TLayoutManager class specifically for this purpose. It provides automatic scaling of sizes, positions, and fonts based on screen resolution: 0-cite-1

Key features:

  • CalcSize() - Scales control sizes proportionally
  • CalcPos() - Scales positions
  • CalcFontSize() - Scales font sizes automatically
  • Uses minimum factor to maintain aspect ratio 0-cite-2

This class was added in 2025 as a modern solution for high-resolution displays.

3. Global Font Scaling Approach (Production Pattern)

For a 125% scale factor, use the DPI detection function and apply scaled fonts globally:

Step A: Detect Current DPI 0-cite-3

This function returns the scaling factor (e.g., 1.25 for 125%, 1.5 for 150%).

Step B: Create Scaled Fonts 0-cite-4

Step C: Apply Font Globally Using TWindow Inheritance

The TWindow:GetFont() method implements recursive parent font inheritance. When you set a font on a parent window, child controls automatically inherit it: 0-cite-5

Production pattern (as shown in testfon3.prg): 0-cite-6

This pattern sets the font on the main window, then iterates through all controls to apply it globally.

4. System Default Font Override

You can override the system default font that FiveWin uses. When no font is specified, FiveWin creates one using: 0-cite-7

You can define a global scaled font at application startup and assign it to all windows/dialogs using the inheritance pattern above.

5. Alternative to MSGINFO() with Scaling Support

For message boxes, use the Alert() function instead of MSGINFO(). It creates a custom TDialog-based dialog that respects your application's font settings: 0-cite-8

Notice that Alert() uses GetSysFont() and creates a TDialog, which will inherit your global font settings, unlike the Windows MessageBox API used by MSGINFO().

6. Complete Working Example (DPI-Aware Dialog)

The scrolldlg.prg sample demonstrates a production-ready DPI-aware implementation: 0-cite-9

This shows how to:

  1. Get current DPI using GetDeviceCaps(hDC, LOGPIXELSX)
  2. Calculate scaling factor: nDpi * baseSize / 96
  3. Apply dynamically at runtime

7. Modern UI Components with Built-in DPI Scaling

The TMyTitle class (added October 2025) includes automatic DPI awareness: 0-cite-10

This demonstrates the framework's direction toward built-in DPI support in modern controls.


---

Recommended Production Strategy

For a clean 125% global scaling without modifying individual dialogs:

  1. Add the DPI-aware manifest to your executable

  2. At application startup, detect DPI and create scaled fonts:

    Code (harbour): Select all Collapse
       nDpi := GetDpiForSystem()  // or use DispFontPerSize() * 96
       nScaleFactor := 1.25  // Your desired 125%
       DEFINE FONT oAppFont NAME GetSysFont() SIZE 0, ScaleFontSize(-12, nDpi * nScaleFactor)
  3. Apply font to main window (which propagates to all child controls via inheritance)

  4. Use Alert() instead of MSGINFO() for scalable message boxes

  5. For new development, consider using TLayoutManager for automatic scaling

  6. For graphics, the GDI+ layer already supports DPI scaling through the dx variable


---

Notes

  • MSGINFO() limitation: It uses the Windows MessageBox API directly, which doesn't respect application fonts - only system DPI settings. This is why Alert() is recommended as an alternative.

  • Font inheritance: FiveWin's architecture automatically propagates fonts from parent windows to child controls, making global font scaling very effective.

  • RDP sessions: The manifest-based approach works well with RDP, as the DPI awareness is declared at the application level.

  • TLayoutManager is the most modern approach but requires refactoring to use its methods. The font scaling approach is simpler and works with existing code.

  • For truly comprehensive scaling (including control sizes, positions, spacing), you'll need to either use TLayoutManager or implement your own scaling calculations similar to the scrolldlg.prg example.

Citations

File: samples/ui/box/manifest.xml (L6-7)

Code (text): Select all Collapse
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>

File: source/classes/Tlayman.prg (L4-18)

Code (text): Select all Collapse
CLASS TLayoutManager
   DATA nScreenWidth, nScreenHeight  // Current screen resolution
   DATA nBaseWidth, nBaseHeight     // Reference resolution (es. 1280x800)
   DATA nSpacing                    // Spacing between controls
   DATA nMarginTop, nMarginLeft     // Upper and lower margins
   DATA nMarginRight, nMarginBottom // Right and lower margins

   METHOD New(nScreenWidth, nScreenHeight, nBaseWidth, nBaseHeight, nSpacing, nMarginTop, nMarginLeft, nMarginRight, nMarginBottom) CONSTRUCTOR
   METHOD CalcSize(nBaseSize)       // Calculate scaled size
   METHOD CalcPos(nBasePos)         // Calculate climbed position
   METHOD CalcGridPos(nIndex, nItemsPerRow, nBaseWidth, nBaseHeight, nBaseTop, nBaseLeft) // Grid position
   METHOD CalcFontSize(nBaseFontSize) // Calculate scaled font size
   METHOD GetUsableWidth()          //Usable width (excluding margins)
   METHOD GetUsableHeight()         // Usable height (excluding margins)
ENDCLASS

File: source/classes/Tlayman.prg (L49-51)

Code (text): Select all Collapse
METHOD CalcFontSize(nBaseFontSize) CLASS TLayoutManager
   // Calculate scaled font size
RETURN Int(nBaseFontSize * Min(::nScreenWidth / ::nBaseWidth, ::nScreenHeight / ::nBaseHeight))

File: source/function/getsysin.prg (L103-112)

Code (text): Select all Collapse
function DispFontPerSize()

   local hDC, nPixelX

   hDC     = CreateDC( "DISPLAY", "", "" )
   nPixelX = GetDeviceCaps( hDC, LOGPIXELSX )

   DeleteDC( hDC )

return ( nPixelX / 96 )

File: samples/ui/title/mytitle.prg (L91-92)

Code (text): Select all Collapse
FUNCTION ScaleFontSize( nBaseSize, nDpi )
RETURN Int( nBaseSize * nDpi / 96 )

File: source/classes/window.prg (L3182-3204)

Code (text): Select all Collapse
METHOD GetFont() CLASS TWindow

   local hFont, aInfo, oFont

/*
   if ::oFont == nil .and. ::oWnd != nil .and. ::oWnd:oFont != nil
      ::oFont = ::oWnd:oFont
      ::SendMsg( WM_SETFONT, ::oFont:hFont )
      DEFAULT ::oFont:nCount := 0
      ::oFont:nCount++
      return ::oFont
   endif
*/

   if ::oFont == nil
      if ::oWnd != nil
         ::oFont  = ::oWnd:GetFont()
         if ::oFont != nil
            ::oFont:nCount++
            ::SendMsg( WM_SETFONT, ::oFont:hFont )
         endif
      endif
   endif

File: source/classes/window.prg (L3226-3226)

Code (text): Select all Collapse
         DEFINE FONT ::oFont NAME GetSysFont() SIZE 0, -12 // BOLD

File: samples/test/testfon3.prg (L95-106)

Code (text): Select all Collapse
STATIC FUNCTION REFRESHWND( oWnd, oFont ) 

    LOCAL i 

    oWnd:SetFont( oFont ) 

    FOR i = 1 TO LEN( oWnd:aControls ) 
        oWnd:aControls[ i ]:SetFont( oFont ) 
        oWnd:aControls[ i ]:Refresh() 
    NEXT 

    RETURN NIL

File: source/function/alert.prg (L34-60)

Code (text): Select all Collapse
   function Alert( cMsg, aOptions, cTitle, nDefault, xIcon )
#endif

   Local oDlg, oFont

   DEFAULT cMsg     := "Alert dialog box" ,;
           aOptions := {"&OK"}            ,;
           cTitle   := "Attention"        ,;
           nDefault := 1

   if CenterMsgs( "?" )
      SetAsMsgBox()
   endif

   DEFINE FONT oFont NAME GetSysFont() SIZE NIL, -14

   DEFINE DIALOG oDlg TITLE cTitle FONT oFont

   oDlg:Cargo     := 0
   oDlg:lHelpIcon := .f.

   ACTIVATE DIALOG oDlg ;
      ON INIT ( Self, DlgInit(oDlg, oFont, cMsg, aOptions, cTitle, nDefault, xIcon) )

   oFont:End()

RETURN oDlg:Cargo

File: samples/ui/dialogs/scrolldlg.prg (L51-74)

Code (text): Select all Collapse
PROCEDURE Set_D_Height()

LOCAL nDpi := GetCurrentDpi() 

d_height := (nDpi * 13 / 96)

RETURN 

//============================================================================ 
// FUNCTION : GetCurrentDpi() 
// Purpose  : To Get the Windows Current DPI Setting 
//============================================================================ 

FUNCTION GetCurrentDpi()

local hDc  := GetDc(0)
local nDpi := GetDeviceCaps( hDC, LOGPIXELSX )

nDpi := GetDeviceCaps( hDC, LOGPIXELSX )
MsgInfo( LTRIM(STR(nDpi))+" DPI" )

ReleaseDC( 0, hDC )

RETURN nDpi

File: whatsnew.txt (L128-141)

Code (text): Select all Collapse
* New: Class TMyTitle developed by Carlos Vargas. Please review samples\ui\mytitle.prg
  A very nice class to create modern title bars.

  **TMyTitle Class Functionality Summary:**
  - **Gradient Backgrounds**: Supports horizontal and vertical gradient fills with customizable begin/end colors
  - **Glass Effect**: Optional glass-like visual effect for modern UI appearance
  - **Bitmap Integration**: Displays bitmaps with alpha channel support (ABPaint) and automatic DPI scaling
  - **Typography**: Custom font support with precise text positioning and centering
  - **Borders**: Optional borders with customizable colors
  - **DPI Awareness**: Automatic font and bitmap scaling for high-resolution displays
  - **Flexible Positioning**: Configurable offsets for bitmap and text placement
  - **Resource Support**: Can load bitmaps from resources or external files
  - **Color Customization**: Full control over gradient colors, text color, and border color
  - **Cross-Platform**: Works with both 32-bit and 64-bit compilation

Alvaro Quintana changes are available here:
https://github.com/FiveTechSoft/FWH_tools/blob/master/test_dpi.7z

Antigravity Implementation Plan:

Master Plan: Modern UI Scaling & DPI Awareness in FiveWin
This comprehensive strategy synthesizes the newest framework features (v2025), community-proven patterns, and modern Windows API requirements to provide a robust 125%+ scaling solution.

  1. Global Infrastructure (Setup)
    Application Manifest [MANDATORY]
    Without a proper manifest, Windows will bitmap-stretch your UI, causing blurriness.

Action: Ensure your .manifest file declares PerMonitorV2 awareness.
Effect: Enables crisp UI rendering and accurate DPI reporting via GetDpiForSystem().
DPI Context Initialization
Action: Call ActivatePerMonitorV2() at the very beginning of Main().
Effect: Sets the process-level awareness required for dynamic scaling.

  1. Tier 1: Quick Scaling (Legacy Compatibility)
    Best for existing applications requiring global scaling with minimal code changes.

Scaled Font Inheritance
Leverage FiveWin's recursive font propagation. child controls automatically inherit the font of their parent.

Action: Define a global scaled font and assign it as the default.
harbour
nDpi := GetDpiForSystem()
nScale := 1.25 // Your desired 125% factor
DEFINE FONT oAppFont NAME GetSysFont() SIZE 0, ScaleFontSize(-12, nDpi * nScale)
TWindow():oFontDefault := oAppFont
Redefining Message Boxes [RECOMMENDED]
Standard MsgInfo() uses the Windows API, which does not respect application fonts. Use Alert() as a scalable replacement.

Action: Add these global redefinitions:
harbour
#xtranslate MsgInfo( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 64 )
#xtranslate MsgAlert( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 48 )

  1. Tier 2: Precision Scaling (Modern Modules)
    Recommended for new development to ensure perfect layout at any resolution.

Automated Layout Manager
The TLayoutManager class (2025) provides proportional scaling of fonts, sizes, and positions.

Usage: Initialize with design-time coordinates and use CalcSize() / CalcPos() for dynamic control placement.
DPI-Aware Controls
Favor modern controls like TMyTitle, which include built-in automatic scaling for bitmaps and text.

  1. Tier 3: Full Automation (Community Solution)
    For high-end applications that must adapt perfectly when moving between monitors with different scaling factors.

Dynamic Class Modification
Modify core classes (TFont, TWindow, TXBrowse) to store design-level values and respond to WM_DPICHANGED.

Logic: Store noDpiScale_nInpHeight in TFont.
Automation: Implement DpiUpdate() in TWindow to iterate through all controls and recalculate coordinates/fonts using the relative factor provided by WM_DPICHANGED.
Verification Plan
Manifest Validation: Verify that the application is NOT being bitmap-scaled by Windows (text remains crisp).
Global Inheritance: Open a legacy dialog and confirm all controls (labels, buttons, browses) are using the scaled font.
Message Box Test: Verify that MsgInfo triggers a custom FiveWin dialog with scaled text.
Dynamic Resizing: Move the app between screens with different scaling and verify the UI resizes automatically (requires Tier 3 logic).

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:16 PM

Walkthrough: Tier 1 DPI Scaling Demonstration
This walkthrough demonstrates the Quick Scaling Strategy (Tier 1) implemented in FiveWin, leveraging global font inheritance and manifest-level DPI awareness.

Files Created
1.
dpi_demo.manifest
This manifest file declares PerMonitorV2 awareness. It ensures that Windows provides the correct DPI to the application and prevents the OS from applying artificial bitmap scaling (which causes blur).

2.
dpi_demo.prg
A sample application that implements the following scaling patterns:

Global Font Inheritance
Instead of styling every control, we set a default scaled font for the TWindow class:

harbour
DEFINE FONT oFont NAME GetSysFont() SIZE 0, ScaleFontSize(-12, nDpi * nScale)
TWindow():oFontDefault := oFont
Scaled Message Boxes
Standard MsgInfo() is redirected to the native Alert() function using #xtranslate. This ensures that even system messages respect the application's scaled font:

harbour
#xtranslate MsgInfo( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 64 )
How to Test
Compile: Compile
dpi_demo.prg
ensuring the
dpi_demo.manifest
is included in the resources (usually via your
.rc
file).
Observe: Upon startup, the main window and the auto-created child dialog will appear with larger fonts and controls (scaled at 150% in the demo code).
Messages: Click the "Show Scaled MsgInfo" button. Notice it displays as a full FiveWin dialog with large readable text, rather than a tiny standard Windows message box.
Inheritance: Open any child dialog or menu; you will see they automatically follow the application-level font without extra code.
NOTE

This demonstration uses a fixed scale factor of 1.5 (150%) to make the changes obvious. In a production app, you would typically use 1.25 or calculate it dynamically based on user preferences.

Conclusion
This Tier 1 approach is the most efficient way to modernize legacy FiveWin applications for high-resolution displays without refactoring the entire UI logic.

dpi_demo.prg

#include "FiveWin.ch"

// Tier 1 Redefinitions: Redirect MsgInfo to Alert for scaling support
#xtranslate MsgInfo( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 64 )
#xtranslate MsgAlert( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 48 )

static oWnd

function Main()

local nDpi, nScale
local oFont

// Initialization
ActivatePerMonitorV2()
   
nDpi   := GetDpiForSystem()
nScale := 1.5 // Scaling at 150% for high visibility in demo
   
// Create global scaled font
DEFINE FONT oFont NAME GetSysFont() SIZE 0, ScaleFontSize(-12, nDpi * nScale)
TWindow():oFontDefault := oFont

DEFINE WINDOW oWnd TITLE "DPI Scaling Demo - Tier 1" ;
    MENU BuildMenu()

@ 2, 2 BUTTON "Show Scaled MsgInfo" SIZE 200, 40 PIXEL ;
    ACTION MsgInfo( "This is a scaled message box!", "DPI Demo" )

@ 5, 2 BUTTON "Exit" SIZE 200, 40 PIXEL ;
    ACTION oWnd:End()

ACTIVATE WINDOW oWnd MAXIMIZED ;
    ON INIT CreateChild()

return nil

function CreateChild()
    local oDlg
   

// This dialog will inherit the global font automatically
DEFINE DIALOG oDlg TITLE "Inherited Font Dialog" SIZE 400, 300 PIXEL
   
@ 10, 10 SAY "This text and button inherit the font." PIXEL
   
@ 50, 10 BUTTON "A Child Button" SIZE 100, 25 PIXEL ;
    ACTION Alert( "Scaled Alert" )

ACTIVATE DIALOG oDlg CENTERED NOMODAL
return nil

function BuildMenu()
    local oMenu
    MENU oMenu
    MENUITEM "Test"
    MENU
    MENUITEM "MsgInfo Test" ACTION MsgInfo( "Hello from menu" )
    SEPARATOR
    MENUITEM "Exit" ACTION oWnd:End()
    ENDMENU
    ENDMENU
return oMenu

function ScaleFontSize( nBaseSize, nDpi )
return Int( nBaseSize * nDpi / 96 )

dpi_demo.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
      version="1.0.0.0"
      processorArchitecture="*"
      name="FiveWin.Application"
      type="win32"
  />
  <description>FiveWin Application</description>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
      />
    </dependentAssembly>
  </dependency>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
    </windowsSettings>
  </application>
</assembly>
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:20 PM

Walkthrough: Tier 2 DPI Scaling (Modern Precision)
This demonstration showcases Tier 2 (Modern Precision) using the TLayoutManager class introduced in recent FiveWin versions.

Files Created
1.
dpi_demo_tier2.prg
A sample application that demonstrates mathematical precision scaling.

TLayoutManager Integration
Unlike simple font inheritance, TLayoutManager recalculates every coordinate:

harbour
oLayout := TLayoutManager():New( nWindowWidth, nWindowHeight, 800, 500, 10, 20, 20, 20, 20 )
Here, 800, 500 is the "Base Resolution" the developer designed for. The class automatically calculates ratios if the current display is larger (e.g., a 4K monitor).

Automatic Grid Layout
The demo uses CalcGridPos() to perfectly align 4 buttons regardless of resolution:

harbour
aPosSize := oLayout:CalcGridPos( nIndex, nItemsPerRow, nBaseWidth, nBaseHeight, nBaseTop, nBaseLeft )
How to Test
Compile: Compile
dpi_demo_tier2.prg
. Ensure the manifest from Tier 1 is still linked.
Precision: Notice how the dialog perfectly maintains its "look and feel" by scaling not just text, but the gaps between controls and their absolute pixel dimensions.
Adaptive UI: Switch between resolutions or maximize the window to see how the layout manager can be used to keep controls proportionally sized.
Tier 2 Benefits
No Overlaps: By calculating sizes and positions together, you prevent text from outgrowing its button containers.
Modern Aesthetics: Essential for "pixel-perfect" designs on varied hardware.

dpi_demo_tier2.prg

#include "FiveWin.ch"

static oWnd

function Main()

local oLayout
local nDlgWidth  := 800
local nDlgHeight := 500
local nDpi       := GetDpiForSystem()
local nScale     := nDpi / 96
   
// Initialize DPI Settings
ActivatePerMonitorV2()

// Tier 2: TLayoutManager use
// Parameters: nScreenWidth, nScreenHeight, nBaseWidth, nBaseHeight, nSpacing, nTop, nLeft, nRight, nBottom
oLayout := TLayoutManager():New( nDlgWidth, nDlgHeight, 800, 500, 10, 20, 20, 20, 20 )

DEFINE WINDOW oWnd TITLE "DPI Scaling Demo - Tier 2 (LayoutManager)" ;
    MENU BuildMenu()

@ 1, 1 BUTTON "Open Precision Dialog" SIZE 250, 40 PIXEL ;
    ACTION CreatePrecisionDialog( oLayout )

@ 4, 1 BUTTON "Exit" SIZE 250, 40 PIXEL ;
    ACTION oWnd:End()

ACTIVATE WINDOW oWnd MAXIMIZED ;
    ON INIT CreatePrecisionDialog( oLayout )

return nil

function CreatePrecisionDialog( oLayout )
    local oDlg, oFont, aPosSize
    local nWidth  := oLayout:nScreenWidth
    local nHeight := oLayout:nScreenHeight

// Scaled Font using Layout Manager calculation
DEFINE FONT oFont NAME "Inter" SIZE 0, -oLayout:CalcFontSize( 12 )

DEFINE DIALOG oDlg TITLE "TLayoutManager Precision Dialog" ;
    SIZE nWidth, nHeight PIXEL TRUEPIXEL FONT oFont

// Example 1: CalcGridPos - Automatically layout 4 buttons in a grid
// CalcGridPos( nIndex, nItemsPerRow, nBaseWidth, nBaseHeight, nBaseTop, nBaseLeft )
   
// Button 1
aPosSize := oLayout:CalcGridPos( 1, 2, 180, 50, 60, 20 )
@ aPosSize[1], aPosSize[2] BUTTON "Grid Pos 1" ;
    SIZE aPosSize[3], aPosSize[4] PIXEL OF oDlg

// Button 2
aPosSize := oLayout:CalcGridPos( 2, 2, 180, 50, 60, 20 )
@ aPosSize[1], aPosSize[2] BUTTON "Grid Pos 2" ;
    SIZE aPosSize[3], aPosSize[4] PIXEL OF oDlg

// Example 2: Manual Scaling of sizes
aPosSize := { oLayout:nMarginTop + 150, oLayout:nMarginLeft, oLayout:CalcSize( 350 ), oLayout:CalcSize( 150 ) }
@ aPosSize[1], aPosSize[2] GROUP oGroup PROMPT "Scaled Layout Area" ;
    SIZE aPosSize[3], aPosSize[4] PIXEL OF oDlg

@ aPosSize[1] + 20, aPosSize[2] + 20 SAY "Note how positions and sizes" PIXEL OF oDlg
@ aPosSize[1] + 40, aPosSize[2] + 20 SAY "are calculated relative to the" PIXEL OF oDlg
@ aPosSize[1] + 60, aPosSize[2] + 20 SAY "base design resolution (800x500)." PIXEL OF oDlg

ACTIVATE DIALOG oDlg CENTERED NOMODAL
return nil

function BuildMenu()
    local oMenu
    MENU oMenu
    MENUITEM "Actions"
    MENU
    MENUITEM "Create Dialog" ACTION MsgInfo( "Check background dialog" )
    SEPARATOR
    MENUITEM "Exit" ACTION oWnd:End()
    ENDMENU
    ENDMENU
return oMenu
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:24 PM

Walkthrough: Tier 2 DPI Scaling (Dynamic Automation) This culmination demonstrates Tier 3 (Dynamic Automation). This is the "Gold Standard" for modern Windows applications, where the UI adjusts instantly as the user interacts with their hardware.

Files Created 1. dpi_demo_tier3.prg A sample application that monitors hardware events.

WM_DPICHANGED Interception The core of Tier 3 is listening for the WM_DPICHANGED message (0x02E0):

harbour oWnd:bMsg := { | nMsg, nWParam, nLParam | HandleDpiChange( nMsg, nWParam, nLParam ) } Recursive DpiUpdate When Windows signals a change (e.g., the window is dragged to a 4K monitor), the application recursively iterates through every control and recalculates its position and font size on focal points:

harbour oControl:Move( oControl:nTop * nFactor, oControl:nLeft * nFactor, ... ) How to Test Hardware Test: Run the app and drag it from your primary monitor to a secondary monitor with a different scaling factor (e.g., 100% to 150%). Software Test: Go to Windows Display Settings and change the "Scale and layout" percentage while the app is running. Observation: Notice how the window resizes itself to the new physical dimension and all buttons/text stay sharp and proportionally positioned within the window. Tier 3 Benefits Zero Interruption: Users don't need to restart the app when changing docking stations or monitors. Future-Proof: Automatically handles any future scaling factor Windows might introduce (225%, 300%, etc.). Pro Performance: This level of integration is what users expect from high-end commercial software.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:27 PM

Walkthrough: Tier 1 DPI Scaling Demonstration

This walkthrough demonstrates the Quick Scaling Strategy (Tier 1) implemented in FiveWin, leveraging global font inheritance and manifest-level DPI awareness.

Files Created

1. [dpi_demo.manifest](file:///c:/fwteam/dpi_demo.manifest)

This manifest file declares PerMonitorV2 awareness. It ensures that Windows provides the correct DPI to the application and prevents the OS from applying artificial bitmap scaling (which causes blur).

2. [dpi_demo.prg](file:///c:/fwteam/dpi_demo.prg)

A sample application that implements the following scaling patterns:

Global Font Inheritance

Instead of styling every control, we set a default scaled font for the TWindow class:

Code (harbour): Select all Collapse
DEFINE FONT oFont NAME GetSysFont() SIZE 0, ScaleFontSize(-12, nDpi * nScale)
TWindow():oFontDefault := oFont

Scaled Message Boxes

Standard MsgInfo() is redirected to the native Alert() function using #xtranslate. This ensures that even system messages respect the application's scaled font:

Code (harbour): Select all Collapse
#xtranslate MsgInfo( <cMsg>, [<cTitle>] ) => Alert( <cMsg>, {"&OK"}, <cTitle>, 1, 64 )

How to Test

  1. Compile: Compile [dpi_demo.prg](file:///c:/fwteam/dpi_demo.prg) ensuring the [dpi_demo.manifest](file:///c:/fwteam/dpi_demo.manifest) is included in the resources (usually via your [.rc](file:///c:/fwteam/dpi/source/test_dpi.rc) file).
  2. Observe: Upon startup, the main window and the auto-created child dialog will appear with larger fonts and controls (scaled at 150% in the demo code).
  3. Messages: Click the "Show Scaled MsgInfo" button. Notice it displays as a full FiveWin dialog with large readable text, rather than a tiny standard Windows message box.
  4. Inheritance: Open any child dialog or menu; you will see they automatically follow the application-level font without extra code.

[!NOTE]
This demonstration uses a fixed scale factor of 1.5 (150%) to make the changes obvious. In a production app, you would typically use 1.25 or calculate it dynamically based on user preferences.

Conclusion

This Tier 1 approach is the most efficient way to modernize legacy FiveWin applications for high-resolution displays without refactoring the entire UI logic.


---

Walkthrough: Tier 2 DPI Scaling (Modern Precision)

This demonstration showcases Tier 2 (Modern Precision) using the TLayoutManager class introduced in recent FiveWin versions.

Files Created

1. [dpi_demo_tier2.prg](file:///c:/fwteam/dpi_demo_tier2.prg)

A sample application that demonstrates mathematical precision scaling.

TLayoutManager Integration

Unlike simple font inheritance, TLayoutManager recalculates every coordinate:

Code (harbour): Select all Collapse
oLayout := TLayoutManager():New( nWindowWidth, nWindowHeight, 800, 500, 10, 20, 20, 20, 20 )

Here, 800, 500 is the "Base Resolution" the developer designed for. The class automatically calculates ratios if the current display is larger (e.g., a 4K monitor).

Automatic Grid Layout

The demo uses CalcGridPos() to perfectly align 4 buttons regardless of resolution:

Code (harbour): Select all Collapse
aPosSize := oLayout:CalcGridPos( nIndex, nItemsPerRow, nBaseWidth, nBaseHeight, nBaseTop, nBaseLeft )

How to Test

  1. Compile: Compile [dpi_demo_tier2.prg](file:///c:/fwteam/dpi_demo_tier2.prg). Ensure the manifest from Tier 1 is still linked.
  2. Precision: Notice how the dialog perfectly maintains its "look and feel" by scaling not just text, but the gaps between controls and their absolute pixel dimensions.
  3. Adaptive UI: Switch between resolutions or maximize the window to see how the layout manager can be used to keep controls proportionally sized.

Tier 2 Benefits

  • No Overlaps: By calculating sizes and positions together, you prevent text from outgrowing its button containers.
  • Modern Aesthetics: Essential for "pixel-perfect" designs on varied hardware.

---

Walkthrough: Tier 2 DPI Scaling (Dynamic Automation)

This culmination demonstrates Tier 3 (Dynamic Automation). This is the "Gold Standard" for modern Windows applications, where the UI adjusts instantly as the user interacts with their hardware.

Files Created

1. [dpi_demo_tier3.prg](file:///c:/fwteam/dpi_demo_tier3.prg)

A sample application that monitors hardware events.

WM_DPICHANGED Interception

The core of Tier 3 is listening for the WM_DPICHANGED message (0x02E0):

Code (harbour): Select all Collapse
oWnd:bMsg := { | nMsg, nWParam, nLParam | HandleDpiChange( nMsg, nWParam, nLParam ) }

Recursive DpiUpdate

When Windows signals a change (e.g., the window is dragged to a 4K monitor), the application recursively iterates through every control and recalculates its position and font size on focal points:

Code (harbour): Select all Collapse
oControl:Move( oControl:nTop * nFactor, oControl:nLeft * nFactor, ... )

How to Test

  1. Hardware Test: Run the app and drag it from your primary monitor to a secondary monitor with a different scaling factor (e.g., 100% to 150%).
  2. Software Test: Go to Windows Display Settings and change the "Scale and layout" percentage while the app is running.
  3. Observation: Notice how the window resizes itself to the new physical dimension and all buttons/text stay sharp and proportionally positioned within the window.

Tier 3 Benefits

  • Zero Interruption: Users don't need to restart the app when changing docking stations or monitors.
  • Future-Proof: Automatically handles any future scaling factor Windows might introduce (225%, 300%, etc.).
  • Pro Performance: This level of integration is what users expect from high-end commercial software.
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:28 PM

test_dpi_tier3.prg

#include "FiveWin.ch"

#define WM_DPICHANGED 0x02E0

static oWnd
static nCurrentDpi := 96

function Main()

// In production, you would include the modified classes. 
// For this demo, we'll implement the logic in the PRG.
   
ActivatePerMonitorV2()
nCurrentDpi := GetDpiForSystem()

DEFINE WINDOW oWnd TITLE "DPI Scaling Demo - Tier 3 (Dynamic Automation)" ;
    MENU BuildMenu()

@ 2, 2 BUTTON "Open Dynamic Dialog" SIZE 250, 40 PIXEL ;
    ACTION CreateDynamicDialog()

// Intercept DPI Change at the window level
oWnd:bMsg := { | nMsg, nWParam, nLParam | HandleDpiChange( nMsg, nWParam, nLParam ) }

ACTIVATE WINDOW oWnd MAXIMIZED ;
    ON INIT CreateDynamicDialog()

return nil

//----------------------------------------------------------------------------//

function HandleDpiChange( nMsg, nWParam, nLParam )
   

local nNewDpi, nFactor
local oRect

if nMsg == WM_DPICHANGED
nNewDpi := nLoWord( nWParam )
nFactor := nNewDpi / nCurrentDpi
nCurrentDpi := nNewDpi
  
// nLParam contains the suggested new window rectangle
oRect := TRect():New( nPeekShort( nLParam, 0 ), nPeekShort( nLParam, 4 ), ;
    nPeekShort( nLParam, 8 ), nPeekShort( nLParam, 12 ) )
  
oWnd:Move( oRect:nTop, oRect:nLeft, oRect:nWidth, oRect:nHeight, .T. )
  
// Recursively update all children
DpiUpdate( oWnd, nFactor )
  
MsgInfo( "DPI Changed to " + LTrim(Str(nNewDpi)) + " (" + LTrim(Str(nFactor*100)) + "%)" )
endif

return nil

//----------------------------------------------------------------------------//

function DpiUpdate( oParent, nFactor )
    local i, oControl, oFont
   

for i = 1 to Len( oParent:aControls )
oControl := oParent:aControls[ i ]
  
// Update coordinates
oControl:Move( oControl:nTop * nFactor, oControl:nLeft * nFactor, ;
    oControl:nWidth * nFactor, oControl:nHeight * nFactor, .T. )
  
// Update Font if it exists
if oControl:oFont != nil
oFont := oControl:oFont
// In Tier 3 production, TFont would have noDpiScale_nInpHeight
// Here we simulate the recalculation
oControl:SetFont( TFont():New( oFont:cFaceName,, oFont:nHeight * nFactor ) )
endif
  
// If it has children (like a Folder), recurse
if ! Empty( oControl:aControls )
DpiUpdate( oControl, nFactor )
endif
next
   
oParent:Refresh()

return nil

//----------------------------------------------------------------------------//

function CreateDynamicDialog()
    local oDlg
   

DEFINE DIALOG oDlg TITLE "Auto-Scaling Control Dialog" SIZE 500, 400 PIXEL
   
@ 20, 20 SAY "Drag the main window between" PIXEL OF oDlg
@ 40, 20 SAY "monitors with different DPI settings." PIXEL OF oDlg
   
@ 80, 20 BUTTON "Dynamic Button" SIZE 150, 40 PIXEL OF oDlg ;
    ACTION MsgInfo( "DPI: " + LTrim(Str(nCurrentDpi)) )

@ 140, 20 CHECKBOX oCheck PROMPT "Scale me!" PIXEL OF oDlg

ACTIVATE DIALOG oDlg CENTERED NOMODAL
return nil

//----------------------------------------------------------------------------//

function BuildMenu()
    local oMenu
    MENU oMenu
    MENUITEM "DPI Tools"
    MENU
    MENUITEM "Current DPI" ACTION MsgInfo( nCurrentDpi )
    SEPARATOR
    MENUITEM "Exit" ACTION oWnd:End()
    ENDMENU
    ENDMENU
return oMenu
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:29 PM

AI provides us a lot to test and think about... :D

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:41 PM

Análisis Técnico: Solución DPI de Álvaro Quintana

Esta evaluación detalla las ventajas y el funcionamiento del sistema de escalado DPI desarrollado por Álvaro Quintana para FiveWin.

Resumen Técnico

La solución de Álvaro es una implementación de nivel ingeniería que aborda el escalado desde la raíz de las clases base de FiveWin, priorizando la automatización y la precisión matemática.

Puntos Clave

1. Conservación del Diseño Original (noDpiScale_nInpHeight)

La modificación más brillante en la clase TFont.

  • Problema: El redondeo acumulativo al escalar fuentes repetidamente causa distorsiones.
  • Solución: Almacena el tamaño original del diseño en una propiedad que nunca varía. Cualquier nuevo escalado (por cambio de monitor) se calcula siempre sobre el valor original, eliminando errores de redondeo.

2. Automatización Total con DpiUpdate()

A diferencia de los métodos manuales, esta solución inyecta inteligencia en TWindow.

  • Funcionamiento: Intercepta WM_DPICHANGED y ejecuta una rutina recursiva que redimensiona todo el árbol de controles.
  • Ventaja: El desarrollador no tiene que modificar cada diálogo; la clase base maneja la infraestructura de redimensión.

3. Integración de Bajo Nivel (C)

  • Utiliza funciones en C para GetDpiForMonitor y SetProcessDpiAwarenessContext(-4).
  • Asegura que la aplicación sea "DPI Aware" a nivel de sistema, evitando que Windows aplique escalado por mapa de bits (lo que causa borrosidad).

4. Excelencia en Escenarios Multi-Monitor

Diseñada específicamente para aplicaciones profesionales que se mueven entre pantallas con diferentes densidades de píxeles (ej. Laptop 1080p y Monitor 4K). La coordinación entre el cambio de fuentes y el reposicionamiento de controles es perfecta.

Conclusión y Recomendación

EscenarioRecomendación
Proyectos NuevosUsar la clase oficial TLayoutManager ( v2025) para un control total y moderno.
Aplicaciones ExistentesImplementar la solución de Álvaro Quintana para inyectar modernidad en el código legado con el mínimo esfuerzo de re-programación.

---

Este documento sirve como referencia técnica para la toma de decisiones arquitectónicas sobre el escalado UI en FiveWin.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:44 PM

Propuesta Estratégica: Integración de Solución DPI en el Núcleo de FWH

Implementar la lógica de Álvaro Quintana directamente en el código fuente oficial de FiveWin (FWH) sería, en mi opinión, el avance más significativo para la plataforma en la última década.

Aquí presento un análisis de por qué y cómo debería hacerse:

Por qué integrarlo oficialmente

  1. Competitividad: En 2026, el soporte de alta resolución no es una "mejora", es una necesidad básica. Integrarlo nativamente pondría a FWH al nivel de frameworks modernos.
  2. Solución en la Raíz: Al modificar TFont y TWindow directamente, se soluciona el problema para todos los controles (incluyendo los de terceros o personalizados) que hereden de estas clases.
  3. Eliminación del "Código Espagueti": Actualmente, los desarrolladores tienen que parchear cada diálogo. Una integración oficial permitiría que el escalado sea una propiedad de la aplicación, no una tarea de programación manual por pantalla.

Desafíos y Soluciones (Roadmap)

Fase 1: El Flag de Activación

Para no romper aplicaciones antiguas con diseños "pixel-perfect" rígidos, la integración no debe ser obligatoria por defecto.

  • Propuesta: Crear un flag global FW_SetDpiAutoScaling( .L. ). Si el usuario lo pone en .T., se activa la maquinaria de Álvaro.

Fase 2: Robustez en Controles Complejos

Clases como TXBrowse, TFolderEx y TGraph tienen coordenadas internas complejas.

  • Acción: Auditar estas clases para asegurar que sus métodos de pintura (Paint()) usen variables relativas al DPI actual del objeto, en lugar de constantes.

Fase 3: Persistencia de Estado

Integrar SaveState() y RestoreState() nativamente en TWindow.

  • Acción: Que FiveWin recuerde automáticamente la posición y el factor de escala de cada ventana por usuario/máquina.

Opinión Final

Si el equipo de FiveWin adopta esta arquitectura:

  • Ganancia: Miles de aplicaciones legadas se verían modernas y nítidas instantáneamente con solo una recompilación y una línea de código.
  • Riesgo: Bajo, siempre que se mantenga como una característica opcional (Opt-in).

Es el paso natural para que FiveWin siga siendo relevante en la era de los monitores 8K y la movilidad extrema.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44162
Joined: Thu Oct 06, 2005 05:47 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 08:50 PM

5. FWH Core Integration (Surgical Edits)

To make DPI scaling a native part of the framework, we will modify the official source classes.

[MODIFY] [font.prg](file:///c:/fwteam/source/classes/font.prg)

  • Addition: DATA noDpiScale_nInpHeight
  • Logic: Store the original design height in New() to avoid cumulative rounding errors during multi-monitor movement.

[MODIFY] [window.prg](file:///c:/fwteam/source/classes/window.prg)

  • Addition: METHOD DpiUpdate( nFactor )
  • Logic: A recursive method that scales fonts and coordinates of all child controls.
  • Hook: Ensure WM_DPICHANGED triggers this method automatically if the feature is enabled.

---

6. Verification Plan

  1. Manifest Validation: Verify that the application is NOT being bitmap-scaled by Windows (text remains crisp).
  2. Global Inheritance: Open a legacy dialog and confirm all controls (labels, buttons, browses) are using the scaled font.
  3. Message Box Test: Verify that MsgInfo triggers a custom FiveWin dialog with scaled text.
  4. Dynamic Resizing: Move the app between screens with different scaling and verify the UI resizes automatically (requires Tier 3 logic).
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 230
Joined: Sat Apr 19, 2008 10:28 PM

Re: Best practice for global UI scaling in FiveWin (125% without changing Windows DPI)

Posted: Mon Feb 16, 2026 10:36 PM

Good afternoon,

Indeed, I believe that with the proliferation of 4K and Ultra-HD monitors, it’s unacceptable for an application to look blurry. The system I devised — without AI assistance — is probably not very rigorous, but once finished it allowed me to update my applications in barely 5 minutes each. Surely many things could be done in a simpler and more efficient way, but since I am a user of several of my own programs, I was in a hurry to eliminate the blurriness :D I have only modified the classes I use, and my applications now look perfect, although there is still a lot to do. I have just sent Antonio the classes and functions as they stand today, since there have been changes compared to what was posted above. I will continue to follow this thread closely.

Regards

Alvaro

Continue the discussion