Ooops
July 09, 2020, 10:47:35 AM

Author Topic: Methods to gather pixel-length of GUI strings  (Read 266 times)

Offline hackball

  • Jr. Member
  • **
  • Posts: 36
Methods to gather pixel-length of GUI strings
« on: June 27, 2020, 08:56:51 PM »
Hello folks,while trying out stuff it bothered me that i had to set a specific size to a gadget or label even i don't know the size of it yet because it is determined at runtime.It seems there is no automatic layout in BMax so this is what i came up with:
Code: BlitzMax
  1. 'calculating string length in pxels for label/gadget creation
  2.  
  3. Import MaxGui.Drivers
  4.  
  5. 'getting our gui windows' fonts
  6. Global winSysFont:TGUIFont = LookupGuiFont (GUIFONT_SYSTEM,        24)
  7. Global winFixFont:TGUIFont = LookupGuiFont (GUIFONT_MONOSPACED,    24)
  8. Global winSysFontSize:Int
  9. If winSysFont
  10.     winSysFontSize = Int(FontSize (winSysFont))        'is Double
  11.     Print "Sys Font Size = " + String(winSysFontSize)
  12. Else
  13.     End
  14. EndIf
  15. Global winFixFontSize:Int
  16. If winFixFont
  17.     winFixFontSize = Int(FontSize (winFixFont))        'is Double
  18.     Print "Fixed Font Size = " + String(winFixFontSize)
  19. Else
  20.     End
  21. EndIf
  22.  
  23. t$ = "Hello BMax Forum!"
  24. Print;Print
  25. Print TextLength (t$, GUIFONT_SYSTEM)
  26. Print TextLength (t$, GUIFONT_MONOSPACED)
  27.  
  28. Function TextLength:Int (test:String, mode:Int)
  29.     Local outlen:Int = 0
  30.     Select mode                        'debug output
  31.         Case GUIFONT_SYSTEM;        Print "TextLength() mode = Prop Font!"
  32.         Case GUIFONT_MONOSPACED;    Print "TextLength() mode = Fix Font!"
  33.     End Select
  34.    
  35.     If test
  36.         tl% = Len(test)
  37.         Print "String: " + test + " ( " + String(tl) + " )"
  38.         For i% = 1 To tl
  39.             c$ = Mid (test,i,1)
  40.             ol% = 0
  41.             Select mode
  42.                 Case GUIFONT_SYSTEM
  43.                     ol = winSysFont.CharWidth(Asc(c$))
  44.                     Print c$ + " > " + String(ol)
  45.                    
  46.                     outlen :+ ol
  47.                 Case GUIFONT_MONOSPACED
  48.                     ol = winFixFont.CharWidth(Asc(c$))
  49.                     Print c$ + " > " + String(ol)
  50.                    
  51.                     outlen :+ ol
  52.             End Select    
  53.         Next
  54.         Return outlen
  55.     Else
  56.         Return 0
  57.     EndIf
  58. End Function
  59.  
Is there any better or smarter way to determine the length of a string for GUI purposes?
Edit:Of course i should add the TGUIFont as an arguement to not rely on globals, but that aside:
Code: BlitzMax
  1. Import MaxGui.Drivers
  2.  
  3. 'getting our gui windows' fonts
  4. Global winSysFont:TGUIFont = LookupGuiFont (GUIFONT_SYSTEM        )',        24)
  5. Global winFixFont:TGUIFont = LookupGuiFont (GUIFONT_MONOSPACED    )',    24)
  6. Global winSysFontSize:Int
  7. If winSysFont
  8.     winSysFontSize = Int(FontSize (winSysFont))        'is Double
  9.     Print "Sys Font Size = " + String(winSysFontSize)
  10. Else
  11.     End
  12. EndIf
  13. Global winFixFontSize:Int
  14. If winFixFont
  15.     winFixFontSize = Int(FontSize (winFixFont))        'is Double
  16.     Print "Fix Font Size = " + String(winFixFontSize)
  17. Else
  18.     End
  19. EndIf
  20.  
  21. t$ = "Hello BMax Forum!"
  22. Print;Print
  23. Print TextLength (t$, winSysFont) + " (prop font)"
  24. Print TextLength (t$, winFixFont) + " (fix font)"
  25.  
  26. 'Print TextLength (t$, 0)
  27. 'Print TextLength ("", winFixFont)
  28.  
  29. Function TextLength:Int (test:String, font:TGUIFont)
  30.     Local outlen:Int = 0
  31.     If Not(test) Then Return -1
  32.     If Not(font) Then Return -2
  33.    
  34.     tl% = Len(test)
  35.     Print "String: " + test + " ( " + String(tl) + " )"
  36.     For i% = 1 To tl
  37.         c$ = Mid (test,i,1)
  38.         ol% = 0
  39.        
  40.         ol = font.CharWidth(Asc(c$))
  41.         'Print c$ + " > " + String(ol)
  42.                
  43.         outlen :+ ol    
  44.     Next
  45.    
  46.     Return outlen
  47.    
  48. End Function
  49.  

Offline hackball

  • Jr. Member
  • **
  • Posts: 36
Re: Methods to gather pixel-length of GUI strings
« Reply #1 on: June 28, 2020, 02:14:30 PM »
Just spoiling here while waiting for tips or solutions:
It doesn't work properly, as you can see in the example output above.
Maybe the CharWidth() method is doing something else than what i think it does or it cannot use ASCII values for unicode glyphs or i did use it wrong - who knows?
The resulting values are not correct, so this is not useful at all.

Online col

  • Sr. Member
  • ****
  • Posts: 444
Re: Methods to gather pixel-length of GUI strings
« Reply #2 on: July 02, 2020, 03:09:01 AM »
Not sure if it will help or not but on Windows10 I'm getting:

Code: [Select]
Sys Font Size = 24
Fixed Font Size = 24


TextLength() mode = Prop Font!
String: Hello BMax Forum! ( 17 )
H > 23
e > 17
l > 8
l > 8
o > 19
  > 9
B > 18
M > 29
a > 16
x > 15
  > 9
F > 16
o > 19
r > 11
u > 18
m > 28
! > 9
272
TextLength() mode = Fix Font!
String: Hello BMax Forum! ( 17 )
H > 18
e > 18
l > 18
l > 18
o > 18
  > 18
B > 18
M > 18
a > 18
x > 18
  > 18
F > 18
o > 18
r > 18
u > 18
m > 18
! > 18
306
To be is to be perceived.

https://github.com/davecamp

Offline hackball

  • Jr. Member
  • **
  • Posts: 36
Re: Methods to gather pixel-length of GUI strings
« Reply #3 on: July 03, 2020, 07:36:16 PM »
Thanks, i didn't test on Windows yet. I tried to find whats going on and found Charwidth() uses NSCharWidth() from the Cocoa framework (MacOSX). Unfortunately this does not exist. I have an old compendium on Cocoa and 'NSCharWidth()' could not be found in it. ??? Anyway, it looks ok on Windows.

Offline DerFetteElch

  • Jr. Member
  • **
  • Posts: 3
Re: Methods to gather pixel-length of GUI strings
« Reply #4 on: July 05, 2020, 08:58:12 PM »
NSCharWidth is a custom callback function defined in /maxgui.mod/cocoamaxgui.mod/cocoa.macos.m and not part of the Cocoa frameworks.
It is simply calling NSFont's advancementForGlyph method and casts the width of the resulting NSSize struct into an Integer value before returning it.

You have to dig a bit deeper inside Cocoas guts to find a propper way. I can have a look at it tomorrow, if you like.

EDIT: 06.07.2020

Save the following code as "PrecalcTextWidth.m"...
Code:  (Unknown Language)
  1. #import <AppKit/AppKit.h>
  2.  
  3. float BMXPrecalcTextWidth(NSString *string, NSFont *font){
  4.     //create attributes for the font.
  5.     NSDictionary *attributes = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
  6.     //let NSString calculate the size, taking the font into it's calculation.
  7.     NSSize size = [string sizeWithAttributes:attributes];
  8.     //return the resulting width only
  9.     return size.width;
  10. }
(erm... Is there any list of the supported languages for bbcode 'code' tag? ObjectiveC, ObjC and Objective-C are not working...  ;D)


...and save this code as "PrecalcTextWidth.bmx" next to "PrecalcTextWidth.m".
Code: BlitzMax
  1. Import brl.blitz
  2. Import maxgui.maxgui
  3. ?macos
  4. Import pub.macos
  5. Import "PrecalcTextWidth.m"
  6. ?
  7.  
  8. Rem
  9. bbdoc: Precalculate the width of a string with a given font.
  10. EndRem
  11. Function PrecalcTextWidth:Int(s:String, font:TGuiFont)
  12. ?macos
  13.         'Define the callback for ObjectiveC
  14.         Extern
  15.                 Function BMXPrecalcTextWidth:Float(s:Byte Ptr, font:Byte Ptr)
  16.         End Extern
  17.         'Return the result of the callback with a given string and font.
  18.         Return Int(BMXPrecalcTextWidth(NSStringFromBBString(s), font.handle))
  19. ?
  20.         Return 0
  21. End Function


The next code is the example to proof the functionality:
Code: BlitzMax
  1. SuperStrict
  2.  
  3. Framework maxgui.drivers
  4. Import brl.standardio
  5.  
  6. 'import the extension
  7. Import "PrecalcTextWidth.bmx"
  8.  
  9.  
  10. Global systemFont:TGuiFont = LookupGuiFont(GUIFONT_SYSTEM, 24.0, 0)
  11. If systemFont
  12.         Print("Sys Font Size = "+Int(FontSize(systemFont)))
  13.         Print(PrecalcTextWidth("Hello BMax Forum!", systemFont))
  14. EndIf
  15.  
  16. Global fixedFont:TGuiFont = LookupGuiFont(GUIFONT_MONOSPACED, 24.0, 0)
  17. If fixedFont
  18.         Print("Sys Font Size = "+Int(FontSize(fixedFont)))
  19.         Print(PrecalcTextWidth("Hello BMax Forum!", fixedFont))
  20. EndIf

Quote from: Output
[  1%] Compiling:PrecalcTextWidth.m
[ 28%] Processing:PrecalcTextWidth.bmx
[ 28%] Processing:Example.bmx
[ 83%] Compiling:PrecalcTextWidth.bmx.debug.macos.x64.c
[ 84%] Compiling:Example.bmx.gui.debug.macos.x64.c
[100%] Linking:Example
Executing:Example
Sys Font Size = 24
195
Sys Font Size = 24
244

Hope that helped.

Cheers

Offline hackball

  • Jr. Member
  • **
  • Posts: 36
Re: Methods to gather pixel-length of GUI strings
« Reply #5 on: July 06, 2020, 07:58:24 PM »
Thank you very much! I will try as soon as possible! ;D

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal