Page 1 of 2

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

Posted: Wed Dec 03, 2014 1:16 pm
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

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

Posted: Wed Dec 03, 2014 1:25 pm
by IdeasVacuum
Use the 2D Drawing functions, output to Window or to hidden canvas.

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

Posted: Wed Dec 03, 2014 2:56 pm
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

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

Posted: Wed Dec 03, 2014 4:52 pm
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.

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

Posted: Wed Dec 03, 2014 5:03 pm
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

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

Posted: Wed Dec 03, 2014 5:51 pm
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

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

Posted: Wed Dec 03, 2014 6:28 pm
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

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

Posted: Wed Dec 03, 2014 6:31 pm
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.

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

Posted: Wed Dec 03, 2014 6:35 pm
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


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

Posted: Wed Dec 03, 2014 7:19 pm
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

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

Posted: Wed Dec 03, 2014 11:09 pm
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

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

Posted: Thu Dec 04, 2014 8:15 am
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

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

Posted: Tue Dec 16, 2014 11:34 am
by Kukulkan
The EditorGadget() hack does not work because of this bug :-(

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

Kukulkan

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

Posted: Tue Dec 16, 2014 3:49 pm
by IdeasVacuum

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

Posted: Tue Dec 16, 2014 10:01 pm
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: