Resolution-Independent Window

Share your advanced PureBasic knowledge/code with the community.
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Resolution-Independent Window

Post by TI-994A »

the application window and contents remain unchanged in size or position regardless of the display scaling...
Image

While experimenting with the DPI-Aware feature of PureBasic, I cobbled together three examples that will display almost exactly the same on any resolution screen; if the need ever arises. :lol:

This first one compares the resolution of the executing machine against the resolution of the development machine and scales all the window, gadget, and content measurements accordingly. As it relies on a pre-initialised, invisible, maximised window to obtain the metrics of the display, it is not affected by any applied scaling or zooming. However, as Linux does not support the #PB_Window_Maximize directive well, it does not work reliably on Linux.

Code: Select all

;=========================================================
;
;   for apps to maintain their original window and
;   content sizings and gadget placements regardless
;   of the display resolutions of the executing display
;   including scaled and zoomed displays
;
;   tested & working on: 
;   i. Windows XP to Windows 10 (PureBasic v5.0 ++)
;   ii. macOS High Sierra to Sonoma (PureBasic v5.70 ++)
;
;   please disable the DPI-Aware option on later 
;   versions of PureBasic that support the feature
; 
;=========================================================

; this must be set to the resolution of the display
; that was used when designing the application
#devMachineWidth = 1920
#devMachineHeight = 1000

Global.f xScale, yScale

Macro x(width)
  width * xScale
EndMacro

Macro y(height)
  height * yScale
EndMacro

Procedure getDisplayMetrics()  
  OpenWindow(0, 0, 0, 10, 10, "",
             #PB_Window_Invisible |
             #PB_Window_Maximize)
  Define winWidth = WindowWidth(0)
  Define winHeight = WindowHeight(0)
  CloseWindow(0) 
  xScale = (winWidth / #devMachineWidth)
  yScale = (winHeight / #devMachineHeight)  
  aspectRatio.f = winWidth / winHeight
  If aspectRatio < 1.5
    yScale / aspectRatio
  EndIf   
EndProcedure

getDisplayMetrics()


;###### sample GUI ######

image = LoadImage(#PB_Any, #PB_Compiler_Home + "examples/sources/Data/Geebee2.bmp")
smallFont = LoadFont(#PB_Any, "Arial", y(12))
mediumFont = LoadFont(#PB_Any, "Arial", y(16))
largeFont = LoadFont(#PB_Any, "Arial", y(22))

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(0, 0, 0, x(530), y(480), "Responsive Desktop Example", wFlags)           
ButtonGadget(0, x(80), y(30), x(120), y(40), "Small Button")
SetGadgetFont(0, FontID(smallFont))
ButtonGadget(1, x(60), y(100), x(160), y(50), "Medium Button")
SetGadgetFont(1, FontID(mediumFont))
ButtonGadget(2, x(40), y(180), x(200), y(60), "Large Button")
SetGadgetFont(2, FontID(largeFont))
TextGadget(3, x(80), y(280), x(120), y(30), "Small Text", #PB_Text_Center)
SetGadgetFont(3, FontID(smallFont))
TextGadget(4, x(70), y(340), x(140), y(50), "Medium Text", #PB_Text_Center)
SetGadgetFont(4, FontID(mediumFont))
TextGadget(5, x(60), y(400), x(160), y(60), "Large Text", #PB_Text_Center)
SetGadgetFont(5, FontID(largeFont))

ResizeImage(image, x(100), y(100))
ImageGadget(6, x(290), y(30), x(100), y(100), ImageID(image))
ResizeImage(image, x(200), y(200))
ImageGadget(7, x(290), y(240), x(200), y(200), ImageID(image))
ProgressBarGadget(8, x(290), y(170), x(200), y(40), 0, 100)
SetGadgetState(8, 60)

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

;########################

This second one does exactly the same thing, except that it utilises the ExamineDesktops() function to obtain the display metrics. The values returned by this function do not factor in any applied scaling or zooming, so the windows will look bigger if scaling or zooming is applied. This approach works on Windows, macOS, and Linux as well.

Code: Select all

;=========================================================
;
;   for apps to maintain their original window and
;   content sizings and gadget placements regardless
;   of the display resolutions of the executing display
;
;   tested & working on: 
;   i. Windows XP to Windows 10 (PureBasic v5.0 ++)
;   ii. macOS High Sierra to Sonoma (PureBasic v5.70 ++)
;   iii. Ubuntu Linux 18.04.2 (PureBasic 6.11)
;
;   does not support scaled or zoomed displays -
;   please disable the DPI-Aware option on later 
;   versions of PureBasic that support the feature
; 
;=========================================================

; this must be set to the resolution of the display
; that was used when designing the application
#devMachineWidth = 1920
#devMachineHeight = 1000

Global.f xScale, yScale

Macro x(width)
  width * xScale
EndMacro

Macro y(height)
  height * yScale
EndMacro

Procedure getDisplayMetrics()
  ExamineDesktops()          
  winWidth = DesktopWidth(0)
  winHeight = DesktopHeight(0)
  xScale = (winWidth / #devMachineWidth)
  yScale = (winHeight / #devMachineHeight)  
  aspectRatio.f = winWidth / winHeight
  If aspectRatio < 1.5
    yScale / aspectRatio
  EndIf 
EndProcedure

getDisplayMetrics()


;###### sample GUI ######

image = LoadImage(#PB_Any, #PB_Compiler_Home + "examples/sources/Data/Geebee2.bmp")
smallFont = LoadFont(#PB_Any, "Arial", y(12))
mediumFont = LoadFont(#PB_Any, "Arial", y(16))
largeFont = LoadFont(#PB_Any, "Arial", y(22))

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(0, 0, 0, x(530), y(480), "Responsive Desktop App Example", wFlags)           
ButtonGadget(0, x(80), y(30), x(120), y(40), "Small Button")
SetGadgetFont(0, FontID(smallFont))
ButtonGadget(1, x(60), y(100), x(160), y(50), "Medium Button")
SetGadgetFont(1, FontID(mediumFont))
ButtonGadget(2, x(40), y(180), x(200), y(60), "Large Button")
SetGadgetFont(2, FontID(largeFont))
TextGadget(3, x(80), y(280), x(120), y(30), "Small Text", #PB_Text_Center)
SetGadgetFont(3, FontID(smallFont))
TextGadget(4, x(70), y(340), x(140), y(50), "Medium Text", #PB_Text_Center)
SetGadgetFont(4, FontID(mediumFont))
TextGadget(5, x(60), y(400), x(160), y(60), "Large Text", #PB_Text_Center)
SetGadgetFont(5, FontID(largeFont))

ResizeImage(image, x(100), y(100))
ImageGadget(6, x(290), y(30), x(100), y(100), ImageID(image))
ResizeImage(image, x(200), y(200))
ImageGadget(7, x(290), y(240), x(200), y(200), ImageID(image))
ProgressBarGadget(8, x(290), y(170), x(200), y(40), 0, 100)
SetGadgetState(8, 60)

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

;########################

The final example is a duplicate of the second one, but it applies the new DesktopScaledX/Y() functions to obtain any applied scaling or zooming. This will maintain the original size of the window, gadgets, and contents even on scaled or zoomed displays. It works only on Windows with PureBasic v5.70++, and on macOS with PureBasic v6.10++, with the compiler's DPI-Aware option enabled.

Code: Select all

;=========================================================
;
;   for apps to maintain their original window and
;   content sizings and gadget placements regardless
;   of the display resolutions of the executing display
;   including scaled and zoomed displays
;
;   tested & working on: 
;   i. Windows XP to Windows 10 (PureBasic v5.70 ++)
;   ii. macOS High Sierra to Sonoma (PureBasic v6.10 ++)
;
;   please enable the DPI-Aware compiler option
; 
;=========================================================

CompilerIf (#PB_Compiler_OS = #PB_OS_MacOS And #PB_Compiler_Version < 610) Or 
           (#PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Version < 570) Or
           #PB_Compiler_OS = #PB_OS_Linux
  CompilerError "Platform/Version not supported!"
CompilerEndIf


; this must be set to the resolution of the display
; that was used when designing the application
#devMachineWidth = 1920
#devMachineHeight = 1000

Global.f xScale, yScale

Macro x(width)
  width * xScale
EndMacro

Macro y(height)
  height * yScale
EndMacro

Procedure getDisplayMetrics()
  ExamineDesktops()          
  winWidth = DesktopWidth(0)
  winHeight = DesktopHeight(0)
  aspectRatio.f = winWidth / winHeight
  dtScaleX.f = DesktopScaledX(100) / 100
  dtScaleY.f = DesktopScaledY(100) / 100  
  xScale = (winWidth / #devMachineWidth) / dtScaleX
  yScale = (winHeight / #devMachineHeight) / dtScaleY
  If aspectRatio < 1.5
    yScale / aspectRatio
  EndIf 
EndProcedure

getDisplayMetrics()


;###### sample GUI ######

imgRes.s = #PB_Compiler_Home + 
           "examples/sources/Data/Geebee2.bmp"
image = LoadImage(#PB_Any, imgRes)
image2 = CopyImage(image, #PB_Any)
smallFont = LoadFont(#PB_Any, "Arial", y(12))
mediumFont = LoadFont(#PB_Any, "Arial", y(16))
largeFont = LoadFont(#PB_Any, "Arial", y(22))

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  ResizeImage(image, x(100), y(100))
  ResizeImage(image2, x(200), y(200))
CompilerElse
  ResizeImage(image, 100, 100)
  ResizeImage(image2, 200, 200)
CompilerEndIf

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(0, 0, 0, x(530), y(480), "Responsive Desktop App Example", wFlags)           
ButtonGadget(0, x(80), y(30), x(120), y(40), "Small Button")
SetGadgetFont(0, FontID(smallFont))
ButtonGadget(1, x(60), y(100), x(160), y(50), "Medium Button")
SetGadgetFont(1, FontID(mediumFont))
ButtonGadget(2, x(40), y(180), x(200), y(60), "Large Button")
SetGadgetFont(2, FontID(largeFont))
TextGadget(3, x(80), y(280), x(120), y(30), "Small Text", #PB_Text_Center)
SetGadgetFont(3, FontID(smallFont))
TextGadget(4, x(70), y(340), x(140), y(50), "Medium Text", #PB_Text_Center)
SetGadgetFont(4, FontID(mediumFont))
TextGadget(5, x(60), y(400), x(160), y(60), "Large Text", #PB_Text_Center)
SetGadgetFont(5, FontID(largeFont))
ImageGadget(6, x(290), y(30), x(100), y(100), ImageID(image))
ImageGadget(7, x(290), y(240), x(200), y(200), ImageID(image2))
ProgressBarGadget(8, x(290), y(170), x(200), y(40), 0, 100)
SetGadgetState(8, 60)

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

;########################

These are just conceptualised examples to demonstrate the scaling concepts, and in no way bulletproof. Hopefully, they might still provide some inherent value.
Last edited by TI-994A on Fri Aug 30, 2024 7:46 am, edited 1 time in total.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
ZX80
Enthusiast
Enthusiast
Posts: 361
Joined: Mon Dec 12, 2016 1:37 pm

Re: Resolution-Independent Window

Post by ZX80 »

TI-994A, good time !

Thanks for sharing.

I can't say how it's correct, but in one of my old programs I did something like this:

Code: Select all

Structure WinProp
  x.i
  y.i
  w.i
  h.i
EndStructure


Global dpi.d = GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)/120

Define wflags.i
Define MainWindow.WinProp
...

wflags = #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget

If OpenWindow(0, MainWindow\x * dpi, MainWindow\y * dpi, MainWindow\w * dpi, MainWindow\h * dpi, #WindowTitle, wflags)
  ...
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Resolution-Independent Window

Post by Caronte3D »

TI-994A wrote: Sat Aug 24, 2024 7:58 pm While experimenting with the DPI-Aware feature of PureBasic, I cobbled together three examples that will display almost exactly the same on any resolution screen; if the need ever arises. :lol:
Thanks for sharing it!
Would be good if PB do these things automatically when DPI-Aware is set 8)
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Resolution-Independent Window

Post by TI-994A »

Caronte3D wrote: Sun Aug 25, 2024 9:57 amWould be good if PB do these things automatically when DPI-Aware is set 8)
Yes; DPI awareness is an important factor in software development. However, while it might seem ideal to maintain an application's form factor independent of the device, it's not always so. Most users adjust the scaling because they want to enlarge everything, for visual accessibility. So, it would be a disservice to pull away from the system metrics in this sense.

A better approach would be to design a responsive app, like web and mobile apps, where the content sizing and positioning dynamically complement the device form factor. A little tougher to do. :lol:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Resolution-Independent Window

Post by Caronte3D »

TI-994A wrote: Fri Aug 30, 2024 8:02 am A better approach would be to design a responsive app, like web and mobile apps, where the content sizing and positioning dynamically complement the device form factor.
+1000000 :D
Post Reply