Want to use TextGadget with multiline. How to get height?

Everything else that doesn't fall into one of the other PB categories.
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Want to use TextGadget with multiline. How to get height?

Post by Kukulkan »

Hello,

I'm generating multiple TextGadgets() during runtime. Sadly, their content varies in size and therefore some need to be higher than others to cover the text.

The TextGadget does word-wrap - what is fine for me. But it looks like I have to know the height before setting the text. I tried it by using #PB_Ignore for the gadget height but this also does not work (error).

Is there a way to find out, how big the gadget height has to be to cover all the text? I need it cross-platform! And no, counting line-breaks is not a solution ;-)

Best,

Kukulkan
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by IdeasVacuum »

Use the 2D Drawing functions, output to Window or to hidden canvas.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by Kukulkan »

Hello IdeasVacuum

Thank you, but sadly this does mean to start a drawing context and do the wordwrap by myself by using LoadFont(), StartDrawing(), TextWidth() and TextHeight() functions etc. I have to display a few hundred items like this and there are several cons on this:

1) I did this before in other projects. I believe it is way to slow!
2) In such case I also have to store that much images in the background (for redrawing).
3) The operating systems are obviously able to do this much faster (the Gadget already does fast WordWrap! I simply do not know the size needed).

Isn't there some functionality available to get the size needed or auto-size the gadget height? I remember in VB4, VB5 and VB6 I simply assigned the text to some label gadget with AutoSize=True and the height automatically adjusted to the content while keeping the width. After this, I simply needed to check the label height. :-(

Kukulkan
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by IdeasVacuum »

2) In such case I also have to store that much images in the background (for redrawing).
That shouldn't be necessary at all, only one output target is required?

If you use Danilo's gDrawing lib, you can define the width and height of a text gadget more easily.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by Kukulkan »

You are right, I can store the height in a list, array or map structure. The initial time is needed.

Danilo's code it is only Windows... :-( Need it for Mac and Linux, too.

I just started to write some quick code that uses 2D drawing (again) but also use some caching. I will see...

Kukulkan
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by IdeasVacuum »

oops, forgot the x-platform requirement. :oops:

What about having a hidden TextGadget, for which you specify a height that is way higher than ever needed. You could then set it to the width of the active TextGadget, set the text, count the lines (word-wrapped), calc the minimum TextGadget Height (You will know the height of an individual line via the 2D drawing trick).

Code: Select all

Procedure.i GetTxtLineHgt(iWinID.i, iFontID.i)
;---------------------------------------------
Protected iHgt.i = 0

              If StartDrawing(WindowOutput(iWinID))

                      DrawingMode(#PB_2DDrawing_Default)
                      DrawingFont(iFontID)
                        iHgt = TextHeight("My")
                      StopDrawing()
              EndIf

              ProcedureReturn(iHgt)
EndProcedure
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Want to use TextGadget with multiline. How to get height

Post by wilbert »

Does this help you on OS X ?

Code: Select all

; OS X

Procedure ResizeTextGadget(TextGadget)
  Protected.i TextField, Cell, Rect.NSRect
  TextField = GadgetID(TextGadget)
  Cell = CocoaMessage(0, TextField, "cell")
  CocoaMessage(@Rect, TextField, "bounds")
  Rect\size\height = Infinity()
  CocoaMessage(@Rect\size, Cell, "cellSizeForBounds:@", @Rect)
  CocoaMessage(0, TextField, "setFrameSize:@", @Rect\size)
EndProcedure

If OpenWindow(0, 0, 0, 270, 160, "TextGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  TextGadget(0, 10, 10, 250, 20, "This is a word wrapping textfield with a border. We will set the height to adjust for the amount of text", #PB_Text_Border)
  ResizeTextGadget(0)
  
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Want to use TextGadget with multiline. How to get height

Post by BasicallyPure »

I almost have it but there is one problem.
Run the code and you will see the text gadget on the right
is missing the last word (sand.).
The problem is caused by the extra spaces that are inserted
when words wrap in the text gadget.

If the number of extra spaces that are added because of
word wrapping could be calculated then it could be made
to work.

Code: Select all

Edit: code deleted.
See my working example below.
Last edited by BasicallyPure on Thu Dec 04, 2014 6:18 pm, edited 1 time in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4944
Joined: Sun Apr 12, 2009 6:27 am

Re: Want to use TextGadget with multiline. How to get height

Post by RASHAD »

Next is a very effective method for any text ,any font,any font size,any TextGadget() width and maybe more
But it is for windows
You can ask Danilo and Shardik to put you on the track for MAC and Linux
Good luck

Code: Select all

Text$ = "  Lorem ipsum dolor sit amet, consectetur adipisici elit, sed" +
" eiusmod tempor incidunt ut labore et dolore magna aliqua." +
 " Ut enim ad minim veniam, quis nostrud exercitation ullamco" +
" laboris nisi ut aliquid ex ea commodi consequat." +
" Quis aute iure reprehenderit in voluptate velit esse" +
 " cillum dolore eu fugiat nulla pariatur." +
" Excepteur sint obcaecat cupiditat non proident, sunt" +
" in culpa qui officia deserunt mollit anim id est laborum."

LoadFont(0,"Georgia",14)

If OpenWindow(0, 0, 0, 400, 300, "EditorGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    EditorGadget(0, -400,10, 380,300)
    TextGadget(1,10,10,380,20,"",#WS_BORDER)
    SetGadgetFont(0,FontID(0))
    SetGadgetFont(1,FontID(0))
    SetGadgetColor(1,#PB_Gadget_BackColor,$E6FEFE)
    SetGadgetColor(1,#PB_Gadget_FrontColor,#Red)
    SendMessage_(GadgetID(0), #EM_SETTARGETDEVICE, #Null, 0)
    ClearGadgetItems(0)
    SetGadgetText(0,Text$)
    
    nLines = CountGadgetItems(0)
    StartDrawing(WindowOutput(0))
      DrawingFont(FontID(0))
      hLine = TextHeight("W") + GetSystemMetrics_(#SM_CYBORDER)
    StopDrawing()
   
    ResizeGadget(1,10,10,380,hLine*nLines)
    ClearClipboard()
    SendMessage_(GadgetID(0),#EM_SETSEL,0,-1)
    SendMessage_(GadgetID(0),#WM_COPY,0,0)
    
    SetGadgetText(1,GetClipboardText())
Repeat
     Select WaitWindowEvent()
         Case #PB_Event_CloseWindow
                 Quit = 1
         Case #PB_Event_Gadget
                Select EventGadget()
                        Case 1
                 EndSelect
     EndSelect                             
Until Quit = 1
EndIf

Egypt my love
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by IdeasVacuum »

Cross-Platform!

Code: Select all

Enumeration
#DummyWin
#MainWin
#TxtSize
#BtnExit
#BtnGet01
#BtnGet02
#BtnGet03
#Txt01
#Txt02
#Txt03
EndEnumeration

Global    igDpi.i = GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)
Global   igFS10.i = ((10 * 100) / igDpi)
Global   igFS14.i = ((14 * 100) / igDpi)
Global   igFS16.i = ((16 * 100) / igDpi)
Global igFont10.i = LoadFont(0, "Microsoft Sans Serif", igFS10, #PB_Font_HighQuality + 2)
Global igFont14.i = LoadFont(1, "Microsoft Sans Serif", igFS14, #PB_Font_HighQuality + 2)
Global igFont16.i = LoadFont(2, "Microsoft Sans Serif", igFS16, #PB_Font_HighQuality + 2)

Procedure FakeMainWin()
;----------------------

              If OpenWindow(#DummyWin, -10, -10, 500, 500, "fake parent", #PB_Window_Invisible)
             
                       EditorGadget(#TxtSize, 0, 0, 200, 500, #PB_Editor_WordWrap)
              EndIf
EndProcedure

Procedure.i GetTxtLineHgt(iWin.i, iFont.i)
;-----------------------------------------
Protected iLineHgt.i = 0

              If StartDrawing(WindowOutput(iWin))

                      DrawingMode(#PB_2DDrawing_Default)
                      DrawingFont(iFont)
                      iLineHgt = TextHeight("MyĚ")
                      StopDrawing()
              EndIf

              ProcedureReturn(iLineHgt)
EndProcedure

Procedure.i GetTxtGdtHgt(iGdtW.i, iLineHgt.i, iFont.i, sTxt.s)
;-------------------------------------------------------------
Protected iGdtHgt.i = 0, iLineTotal.i

                 ResizeGadget(#TxtSize, #PB_Ignore, #PB_Ignore, iGdtW, #PB_Ignore)
                SetGadgetFont(#TxtSize, iFont)
                SetGadgetText(#TxtSize, sTxt)

                           iLineTotal = CountGadgetItems(#TxtSize)
                              iGdtHgt = iLineHgt * iLineTotal 
              ProcedureReturn(iGdtHgt)
EndProcedure

Procedure GetText(iTargetGdtID.i, iFont.i)
;-----------------------------------------
Protected    sTest.s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
               sTest = sTest + "Nullam molestie commodo magna, id rutrum enim suscipit ac. "
               sTest = sTest + "Nunc tempor faucibus magna, vel volutpat tellus luctus et. "

Protected    iGdtW.i = GadgetWidth(iTargetGdtID)
Protected iLineHgt.i = GetTxtLineHgt(#DummyWin, iFont)
Protected    iGdtH.i = GetTxtGdtHgt(iGdtW, iLineHgt, iFont, sTest)

                 ResizeGadget(iTargetGdtID, #PB_Ignore, #PB_Ignore, #PB_Ignore, iGdtH)
                SetGadgetText(iTargetGdtID, sTest)
EndProcedure

Procedure MainWin()
;------------------
Protected iFlags.i = #PB_Window_BorderLess|#PB_Window_ScreenCentered

              If OpenWindow(#MainWin, 0, 0, 800, 600, "MainWin", iFlags, WindowID(#DummyWin))

                     SetWindowColor(#MainWin, RGB(056,056,056))
                     
                       ButtonGadget(#BtnExit,  740,  0,  60,  20, "EXIT")
                     
                       ButtonGadget(#BtnGet01,  10, 10, 200,  20, "Get Text")
                         TextGadget(#Txt01,     10, 30, 200,  40, "Txt01")
                     
                       ButtonGadget(#BtnGet02, 220, 10, 200,  20, "Get Text")
                         TextGadget(#Txt02,    220, 30, 200,  40, "Txt02")
                     
                       ButtonGadget(#BtnGet03, 440, 10, 200,  20, "Get Text")
                         TextGadget(#Txt03,    440, 30, 200,  40, "Txt03")
                     
                      SetGadgetFont(#Txt01, igFont10)
                      SetGadgetFont(#Txt02, igFont14)
                      SetGadgetFont(#Txt03, igFont16)
              EndIf
EndProcedure

Procedure WaitForUser()
;----------------------
Protected iEvent.i, iExit.i = False

  Repeat
              iEvent = WaitWindowEvent()
       Select iEvent
                       Case #PB_Event_Gadget

                            Select EventGadget()

                                       Case #BtnGet01: GetText(#Txt01, igFont10)
                                       Case #BtnGet02: GetText(#Txt02, igFont14)
                                       Case #BtnGet03: GetText(#Txt03, igFont16)
                                       Case  #BtnExit: iExit = #True : CloseWindow(#MainWin)
                            EndSelect
       EndSelect

  Until iExit = #True

EndProcedure

FakeMainWin()
    MainWin()
WaitForUser()

End
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Want to use TextGadget with multiline. How to get height

Post by BasicallyPure »

Using an idea I got from Rashad's code I gave it another try.
Should be cross platform but I have only tested on Windows.

Note the adjustment for Mac OS, see help for GetGadgetFont().
I don't know if this adjustment works but I think is should.

For some reason I had to arbitrarily increase the width of the editorGadget
by 7 pixels to produce the correct height for all of the examples below.
It seemed to work for other font sizes that I tried.

Code: Select all

EnableExplicit

#MyWin = 0

Procedure SetTextGadgetHeight(gadget, text.s)
   ; Adjusts the height of a text gadget to match the
   ; text height of multi-line text.
   ; Use in place of SetGadgetText().
   ; Returns the new height of the text gadget
   
   Static secretEditGad, firstRun = #True
   Protected gad_w = GadgetWidth(gadget) + 7
   Protected fontID, txt_h, lineCount, result = #False
   
   If IsGadget(gadget) 
      If firstRun
         firstRun = #False
         ; locate EditorGadget off window
         secretEditGad = EditorGadget(#PB_Any, -1000, 0, gad_w, 1000, #PB_Editor_WordWrap)
      Else
         ResizeGadget(secretEditGad, #PB_Ignore, #PB_Ignore, gad_w, #PB_Ignore)
      EndIf
      
      FontID = GetGadgetFont(gadget)
      
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
         If FontID = 0 ; see help entry for GetGadgetFont()
            FontID = GetGadgetFont(#PB_Default)
         EndIf
      CompilerEndIf
      
      If StartDrawing(WindowOutput(#MyWin))
            DrawingFont(FontID)
            txt_h = TextHeight(text)
         StopDrawing()
         
         SetGadgetFont(secretEditGad, FontID)
         
         SetGadgetText(secretEditGad, text)
         lineCount = CountGadgetItems(secretEditGad)
         
         result = txt_h * lineCount
         ResizeGadget(gadget, #PB_Ignore, #PB_Ignore, #PB_Ignore, result)
         SetGadgetText(gadget, text)
      EndIf
   EndIf
   
   ProcedureReturn result
EndProcedure


; example
OpenWindow(#MyWin,0,0,600,300,"",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
   SetWindowColor(#MyWin, $FFDB85)
   
; load some fonts you wish to use
Define font_1 = LoadFont(#PB_Any, "Arial", 16)
Define font_2 = LoadFont(#PB_Any, "Times New Roman", 10)


; first create and position some text gadgets, height does not matter
Define tg_1 = TextGadget(#PB_Any, 010, 10, 100, 25, "")
Define tg_2 = TextGadget(#PB_Any, 120, 10, 100, 25, "")
Define tg_3 = TextGadget(#PB_Any, 230, 10, 100, 25, "")
Define tg_4 = TextGadget(#PB_Any, 340, 10, 100, 25, "")

; add text to the text gadgets, height will be adjusted by procedure

Define t$ = "The quick brown fox jumps over the lazy dog"
; use default font
SetTextGadgetHeight(tg_1, t$)

t$ = "One plus one equals two"
SetGadgetFont(tg_2, FontID(font_1)) ; use font_1
SetTextGadgetHeight(tg_2, t$)

t$ = "Merry Christmas!"
SetGadgetFont(#PB_Default, FontID(font_2)) ; make font_2 default font
SetTextGadgetHeight(tg_3, t$)

t$ = "Let's all fly down south and hide our heads in the sand."
SetTextGadgetHeight(tg_4, t$)


While WaitWindowEvent() <> #PB_Event_CloseWindow : Wend
Last edited by BasicallyPure on Thu Dec 04, 2014 6:35 pm, edited 2 times in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by Kukulkan »

The hack by using an EditorGadget seems to work fine (at least for Windows). I will try this on different machines. If it really works, I'm very happy with this solution.

Anyway, some #PB_Text_AutoSize flag for the Textgadget would be very handy. If set, the gadget keeps the width and position but adapts the height automatically to fit the text. Later on, you can use GadgetHeight() to find out what happened.

I will make a feature request :-)

THANK YOU ALL!

Kukulkan
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by Kukulkan »

The EditorGadget() hack does not work because of this bug :-(

http://www.purebasic.fr/english/viewtop ... 23&t=61249

Kukulkan
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Want to use TextGadget with multiline. How to get height

Post by IdeasVacuum »

IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Want to use TextGadget with multiline. How to get height

Post by Shardik »

IdeasVacuum wrote:Cross-Platform!
You should never claim cross-platform capability without testing on all 3 platforms... :wink:

Your code utilizes the Windows only API function GetDeviceCaps_():

Code: Select all

Global    igDpi.i = GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)
Kukulkan wrote:The EditorGadget() hack does not work because of this bug :-(

http://www.purebasic.fr/english/viewtop ... 23&t=61249
I have posted a workaround for Linux in that thread... :wink:
Post Reply