Methods to gather pixel-length of GUI strings

Started by hackball, June 27, 2020, 20:56:51

Previous topic - Next topic

hackball

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) Select
'calculating string length in pxels for label/gadget creation

Import MaxGui.Drivers

'getting our gui windows' fonts
Global winSysFont:TGUIFont = LookupGuiFont (GUIFONT_SYSTEM,        24)
Global winFixFont:TGUIFont = LookupGuiFont (GUIFONT_MONOSPACED,    24)
Global winSysFontSize:Int
If winSysFont
    winSysFontSize = Int(FontSize (winSysFont))        'is Double
    Print "Sys Font Size = " + String(winSysFontSize)
Else
    End
EndIf
Global winFixFontSize:Int
If winFixFont
    winFixFontSize = Int(FontSize (winFixFont))        'is Double
    Print "Fixed Font Size = " + String(winFixFontSize)
Else
    End
EndIf

t$ = "Hello BMax Forum!"
Print;Print
Print TextLength (t$, GUIFONT_SYSTEM)
Print TextLength (t$, GUIFONT_MONOSPACED)

Function TextLength:Int (test:String, mode:Int)
    Local outlen:Int = 0
    Select mode                        'debug output
        Case GUIFONT_SYSTEM;        Print "TextLength() mode = Prop Font!"
        Case GUIFONT_MONOSPACED;    Print "TextLength() mode = Fix Font!"
    End Select
   
    If test
        tl% = Len(test)
        Print "String: " + test + " ( " + String(tl) + " )"
        For i% = 1 To tl
            c$ = Mid (test,i,1)
            ol% = 0
            Select mode
                Case GUIFONT_SYSTEM
                    ol = winSysFont.CharWidth(Asc(c$))
                    Print c$ + " > " + String(ol)
                   
                    outlen :+ ol
                Case GUIFONT_MONOSPACED
                    ol = winFixFont.CharWidth(Asc(c$))
                    Print c$ + " > " + String(ol)
                   
                    outlen :+ ol
            End Select   
        Next
        Return outlen
    Else
        Return 0
    EndIf
End Function

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) Select

Import MaxGui.Drivers

'getting our gui windows' fonts
Global winSysFont:TGUIFont = LookupGuiFont (GUIFONT_SYSTEM        )',        24)
Global winFixFont:TGUIFont = LookupGuiFont (GUIFONT_MONOSPACED    )',    24)
Global winSysFontSize:Int
If winSysFont
    winSysFontSize = Int(FontSize (winSysFont))        'is Double
    Print "Sys Font Size = " + String(winSysFontSize)
Else
    End
EndIf
Global winFixFontSize:Int
If winFixFont
    winFixFontSize = Int(FontSize (winFixFont))        'is Double
    Print "Fix Font Size = " + String(winFixFontSize)
Else
    End
EndIf

t$ = "Hello BMax Forum!"
Print;Print
Print TextLength (t$, winSysFont) + " (prop font)"
Print TextLength (t$, winFixFont) + " (fix font)"

'Print TextLength (t$, 0)
'Print TextLength ("", winFixFont)

Function TextLength:Int (test:String, font:TGUIFont)
    Local outlen:Int = 0
    If Not(test) Then Return -1
    If Not(font) Then Return -2
   
    tl% = Len(test)
    Print "String: " + test + " ( " + String(tl) + " )"
    For i% = 1 To tl
        c$ = Mid (test,i,1)
        ol% = 0
       
        ol = font.CharWidth(Asc(c$))
        'Print c$ + " > " + String(ol)
               
        outlen :+ ol   
    Next
   
    Return outlen
   
End Function

hackball

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.

col

Not sure if it will help or not but on Windows10 I'm getting:


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
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

hackball

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.

DerFetteElch

#4
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 (ObjectiveC) Select
#import <AppKit/AppKit.h>

float BMXPrecalcTextWidth(NSString *string, NSFont *font){
    //create attributes for the font.
    NSDictionary *attributes = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
    //let NSString calculate the size, taking the font into it's calculation.
    NSSize size = [string sizeWithAttributes:attributes];
    //return the resulting width only
    return size.width;
}

(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) Select
Import brl.blitz
Import maxgui.maxgui
?macos
Import pub.macos
Import "PrecalcTextWidth.m"
?

Rem
bbdoc: Precalculate the width of a string with a given font.
EndRem
Function PrecalcTextWidth:Int(s:String, font:TGuiFont)
?macos
'Define the callback for ObjectiveC
Extern
Function BMXPrecalcTextWidth:Float(s:Byte Ptr, font:Byte Ptr)
End Extern
'Return the result of the callback with a given string and font.
Return Int(BMXPrecalcTextWidth(NSStringFromBBString(s), font.handle))
?
Return 0
End Function



The next code is the example to proof the functionality:
Code (BlitzMax) Select
SuperStrict

Framework maxgui.drivers
Import brl.standardio

'import the extension
Import "PrecalcTextWidth.bmx"


Global systemFont:TGuiFont = LookupGuiFont(GUIFONT_SYSTEM, 24.0, 0)
If systemFont
Print("Sys Font Size = "+Int(FontSize(systemFont)))
Print(PrecalcTextWidth("Hello BMax Forum!", systemFont))
EndIf

Global fixedFont:TGuiFont = LookupGuiFont(GUIFONT_MONOSPACED, 24.0, 0)
If fixedFont
Print("Sys Font Size = "+Int(FontSize(fixedFont)))
Print(PrecalcTextWidth("Hello BMax Forum!", fixedFont))
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

hackball

Thank you very much! I will try as soon as possible! ;D

TAS1624

This uses a text area gadget to find the side of GUI Text in pixels
Text area gadget notes;
  The first character is position at position 0, line 0
  Its pixel coordinates are 1,1
  The text is appended with a Chr(10) by the system
  Any CHR(13)'s is replaced by Chr(10)'s

Searching the text to find the largest value of TextAreaCharX() (Exit after line 1 is found) is option also.
 
Import MaxGui.Drivers
   Global window:TGadget = CreateWindow( "My Window", 130, 20, 200, 200 )
   Global area:TGadget = CreateTextArea( 0, 0, ClientWidth(window), ClientHeight(window), window )

   'SetGadgetFont area,Font:TGUIFont
   text$="111222333"

   SetTextAreaText(area,text)   
   h=TextAreaCharY(area,n)
   n=TextAreaLen(area) 'System adds chr(10) to end so we now have n+1 characters (0...N)
   w=TextAreaCharX(area,n) 'if text ends with a chr(13) or chr(10) than w will equal 1
   If h=1 'need to create a second line
      AddTextAreaText(area,Chr(10))
      n=TextAreaLen(area)
      h=TextAreaCharY(area,n)
   EndIf
   L=TextAreaLine(area,n)
   
   Print "len "+n
   Print "h "+h
   Print "Line "+L  'Line count starts a 0 but system adds a forth
   Print "GUI Text width = "+w    'includes extra pixel?
   Print "Textheight= "+((TextAreaCharY(area,n)-1)/L)   'subtract Y origin
End

hackball

#7
Quote from: DerFetteElch on July 05, 2020, 20:58:12

Hope that helped.

Cheers
Thank you for providing this and sorry for my late answer, but, i could not make it to work.I cannot compile that. Maybe it is expecting NG BMax or whatever.I am really tired of that NG at all, it doesn't provide the flexibility i need, i can't even build it on my machine. My apps need to work on older systems too; what is the benefit if they run on "Big Surveillance" only?
Anyway, thanx for trying, i've got 50+hrs of OBjectivec/Cocoa tuts ahead of me.
P.S.: and this site NEEDS Google integgration for providing basic services. Bummer. A coder's forum needs Google APIs.

Derron

What kind of google integration? Maybe it needs microsoft integration too - or facebook, twitter, instagram ... who else sucks in your personal data?

Why should NG work only on Big Sur?
I am only aware of NG no longer running on Windows 98 and 2000/ME (while legacy did - this is a unicode and GCC thing)

bye
Ron

DerFetteElch

QuoteMy apps need to work on older systems too; what is the benefit if they run on "Big Surveillance" only?
NG is compatible with macOS 10.9 and above, as long as I remember. Correct me, if I'm wrong...

I guess the problem has something to do with the use of Byte pointers instead of Int pointers, wich was common in vanilla BlitzMax.
Another problem could be the use of NSStringFromBBString inside of the BlitzMax part of the code instead of including pub.mod/macos.mod/macos.h into the Objective-C part and using NSStringFromBBString from there.

I'm sorry, but I don't own a copy of vanilla BlitzMax to fix this for you.

hackball

Try using NoScript and disable googleAPIs.You cannot comfortly edit any post. This kind of integration. Why do any forum needs Google to edit a text??
Quote from: Derron on October 25, 2020, 06:50:49What kind of google integration?
Ron