I have several blocks of text that can each be long or short or anywhere in between. I want to display them as a list, but with the ability to show each block as a small 2 or 3 line preview or expand it to show the entire text. I want the text to be selectable, but not editable.
I am currently using a DesktopLabel to display the text, and calculating the height of the label for the preview is easy and accurate, but getting the correct height for the expanded mode is proving to be frustrating. I am currently using this function:
Public Function Height(Extends val As String, wrapWidth As Double, fontName As String, fontSize As Double, isBold As Boolean = False, isItalic As Boolean = False) As Double
Dim p As New Picture(1, 1)
p.Graphics.FontName = fontName
p.Graphics.FontSize = fontSize
p.Graphics.Bold = isBold
p.Graphics.Italic = isItalic
Return p.Graphics.TextHeight(val, wrapWidth)
End Function
The calculated height is generally too short, but by how much seems to change with how many EndOfLines there are and how long the text is. I haven’t really found a pattern. I’m thinking that the label has some padding that the Graphics object doesn’t, and it could possibly wrap different than a graphics object?
Any ideas? Or another approach? A different control to display the text in, but still be able to do the expand/collapse action?
I have been playing with accounting for padding in the label and it seems to be mostly accurate now - it will sometimes be an extra line too tall but it’s not cutting off the text anymore.
I get the width of the label and subtract from that the padding, and then pass that number into the height function as the wrap width. Then I add the padding to the calculated height and set Label.Height to that. FWIW a padding value of 16 is working pretty well.
I am still open to other suggestions if there is a better way.
It’s in the category of those framework bugs that last for several years and are impacting basic functionalities, like DesktopTextArea.LineNumAtCharPos which has never been reliable since at least Cocoa was a target: https://tracker.xojo.com/xojoinc/xojo/-/work_items/43874
I’m really tired of them.
On macOS you should be able to do this using Declares or the MBS plugins. Here is the code we use in a v1 API Label control subclass method.
Dim nsTextFieldObj As NSTextFieldMBS
Dim nsTextFieldCellObj As NSTextFieldCellMBS
Dim boundsNSRectObj As NSRectMBS
Dim boundsCellSizeObj As NSSizeMBS
'retrieve the nstextfield and nstextfieldcell for the control
nsTextFieldObj = Self.NSTextFieldMBS
nsTextFieldCellObj = nsTextFieldObj.cell
'define the bounds which is the nstextfield bounds width (which may be different to the control width) and a large height
boundsNSRectObj = New NSRectMBS
boundsNSRectObj.Width = nsTextFieldObj.bounds.Width
boundsNSRectObj.Height = 99999
'retrieve the cell size that fits the text and update the height
boundsCellSizeObj = nsTextFieldCellObj.cellSizeForBounds(boundsNSRectObj)
Self.Height = Ceil(boundsCellSizeObj.Height)
On MS-Windows, things are more difficult. Xojo Graphics use Direct2D (DirectWrite) but the UI controls when not using WinUI use GDI / GDI+ which are two completely different technologies. We solved this using the Win32 API via Declares. Unfortunately, I cannot share this code but you should start by looking at the DrawTextW API call.