Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Weird textfield behavior (with layout) — Gideros Forum

Weird textfield behavior (with layout)

rrraptorrrraptor Member
edited October 2019 in General questions


The red rect represents textfields position and size using getPosition() and getSize(). The blue rectangle - getBounds(stage). Black line is just a 100px offset from top.

First, why textfield one line above black line? Second, why getPosition() returns incorrect results? Well, its not realy incorrect because i didnt set position of the textfield, but it looks weird.
What a was expecting? Text at (0, 0) (or in my case in (0, 100)).

app @ application
 
local width = app:getContentWidth()
local str = "FIRST LINE!\nSecond line\nThird...hmmm\nsomething went wrong..."
local font = TTFont.new("font.otf", 15)
local tf = TextField.new(font, str)
tf:setLayout({flags=FontBase.TLF_CENTER, w = width})
tf:setY(100)
stage:addChild(tf)
 
local x, y = tf:getPosition()
local w, h = tf:getSize()
 
local px1 = Pixel.new(0xff0000, 1, w, h)
px1:setAlpha(.5)
px1:setPosition(x, y)
stage:addChild(px1)
 
x,y,w,h = tf:getBounds(stage)
local px2 = Pixel.new(0x0000ff, 1, w, h)
px2:setAlpha(.5)
px2:setPosition(x, y)
stage:addChild(px2)
 
--x,y,w,h = tf:getBounds(stage) <- forgot to delete when ctr+c - ctrl+v ¯\_(ツ)_/¯
local px3 = Pixel.new(0, 1, width, 2)
px3:setY(100)
stage:addChild(px3)


Project attached

Comments

  • rrraptorrrraptor Member
    edited October 2019
    Ok, magic setSample("Aq") solve all problems.
    Maybe set sample to something similar to "Aq" by default (instead of empty string) to prevent this weird things?

    Likes: Apollo14

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    Well I don’t see any problem here, maybe about getSize() when the layout is centered but it is arguable. TextFields reference points are, by default, the text baseline, so you’ll see displayed text as if you were hand writing it on line located at setPosition(). That’s why you see letters going above that line.
    As for any sprite, setPosition gives the reference point, not necessarily the upper left corner.
  • rrraptorrrraptor Member
    edited October 2019
    getSize() w,h and getBounds() w,h are equal, so thats not a problem at all :smile:
    hgy29 said:

    Well I don’t see any problem here

    Em, so you dont see anything wrong on my screenshot/sample project?
    I mean, technically it may be correct behavior, but visualy its wrong.
    hgy29 said:

    TextFields reference points are, by default, the text baseline, so you’ll see displayed text as if you were hand writing it on line located at setPosition().

    Ok, i understand. But why? I dont know any engine that uses same technic...I belive, in most engines textfields looks like a boxes with text inside and the anchor point is in top left corner, correct me if I wrong.
    hgy29 said:

    As for any sprite, setPosition gives the reference point, not necessarily the upper left corner.

    But whe you setSample() it goes to upper left corner. And its not realy documented. It just says that this method will take a string and determine text's line height.

    And how about this?
    rrraptor said:

    Maybe set sample to something similar to "Aq" by default (instead of empty string) to prevent this weird things?

  • hgy29hgy29 Maintainer
    Why baseline is used instead of top ref ? because of legacy. Textfield used to work on the baseline before layout (in a box) was introduced. I thought it was better to keep consistency with old behaviour as much as possible, it may have been a bad move at that time, and it could be changed by that could break some existing apps. There is layout flag you can use to switch to top ref though: FontBase.TLF_REF_TOP. With this flag added text will appear as you first expected.

    About setSample, it was, in my mind, supposed to give a way to replace the font line height with the height of a specific sample of text. I understand its benefits, but I the way it is implemented doesn't fit well with layout. I admit I have never been in favor of that option, and the guy who added it to gideros hasn't been seen here in a while, so unless someone finds it extremely useful and wants to maintain it, I think it should be dropped completely. But that's my opinion.

    Back to TLF_REF_TOP vs TLF_REF_BASELINE, I totally agree with you that REF_TOP should have been the default. The question is how to make it the default without breaking existing projects too much ?
  • rrraptorrrraptor Member
    edited October 2019
    hgy29 said:

    keep consistency with old behaviour

    Fair enough.
    hgy29 said:

    There is layout flag you can use to switch to top ref though: FontBase.TLF_REF_TOP.

    Yes, but Its not very accurate, and still doesnt solve the problem I had initialy.


    But when i use sample, im getting expected result:


    And combination will give us even more inaccurate result:

    hgy29 said:

    someone finds it extremely useful

    I found it extremply useful to fix my problem :smiley:
    hgy29 said:

    The question is how to make it the default without breaking existing projects too much ?

    We can always use older verison of gideros, so maybe "just do it"? :smile:
    Or keep "old" TextField as is, but add new one called like "TextBox" and later totaly remove TextField and leave only TextBox? Create some notifications on forum, and in docs like "This object will be removed after N years/months/weeks". And maybe in IDE, idk)))

    1.png
    711 x 361 - 22K
    2.png
    606 x 364 - 30K
    32.png
    523 x 358 - 20K
    1.png 21.7K
    2.png 30.1K
    32.png 19.9K
  • hgy29hgy29 Maintainer
    I understand your point, but the fact is that using setSample() in combination with layout doesn't give the expected results, so you shouldn't rely on that. What you want to achieve should have required TLF_REF_TOP and setSample(): REF_TOP to use top of the text as reference, and setSample() to specify which characters contributes to computing the line metrics.

    I am very much in favor of assuming TLF_REF_TOP when you give a bouding box such as in layout, but baseline is actually a better choice if not. I am asking everyone in the forum here: would you mind letting TextField assume top anchoring if a bounding box (or just width or height) is supplied ?

    If so I can change that for future gideros versions, and possibly fix setSample to work in combination with layout.
  • hgy29hgy29 Maintainer
    Just for those reading since that don't get everything about baseline, height, ascent and descent:
    http://mirror.informatimago.com/next/developer.apple.com/documentation/Cocoa/Conceptual/FontHandling/Tasks/GettingFontMetrics.html

    TextField sprite suppose its position is at the 'Origin' point, which is, I think, correct.
  • hgy29hgy29 Maintainer
    edited October 2019
    So I dug into the rendering code to understand better what setSample was doing and things are a bit messy currently.
    1) setSample works by computing the top-left position of the supplied sample text relative to its origin and offseting the real textfield content according to it.
    2) layout on the other hand uses global font metrics (ascender/height/descender) and offsets text by ascender value when selecting REF_TOP

    If both are used, both offsets are applied. This is consistent with you see @rrraptor, and yes it is difficult to understand if you don't know what it does. I didn't know myself that setSample would offset the text. Its name doesn't imply this behaviour. Still being able to use metrics of a specific set of characters instead of font metrics is useful, but must find a way to merge both behaviours somehow.

    I would:
    1) remove setSample() API
    2) add a sample field in the layout parameters
    3) have layout honour that sample field when computing the metrics
    4) have layout use ref_top by default if a layout parameter table is supplied

    EDIT:
    5) have layout interprets ref_top as whatever is correct for the currently rendered text, irrespective of the font metrics or sample. (This makes sample almost useless I think)

    What do you all think ?
  • hgy29 said:

    What do you all think ?

    Remove setSample(), make sample property for layout that only sets line's height, add new anchor property (same as sprite), but with additional baseline parameter like TextFiled.LAYOUT_ANCHOR_BASELINE which will be used by default.
    ?

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    rrraptor said:

    hgy29 said:

    What do you all think ?

    Remove setSample(), make sample property for layout that only sets line's height, add new anchor property (same as sprite), but with additional baseline parameter like TextFiled.LAYOUT_ANCHOR_BASELINE which will be used by default.
    ?
    Actually height is not enough, the layout engine needs to differentiate ascent/descent and height parameters, but all three can be computed at the same time luckily.
    I am not sure about the anchor call, you may want to anchor the TextField sprite by a specific position. But a textfield specific version could be added (setTextAnchor ?)

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • In an app where I use textfield, I must admit it was quite a bit of pain to set the text where I wanted. I had to test every kind of combination to make it work!
    For example I had to test all of these combinations to get what I wanted:
    function xCenteredTextFieldW:init(ptext, pfont, pcolor, pw, ph, pline)
    	self.anchorpointx = 0.5
    --	self.anchorpointy = 0.5
    	self.anchorpointy = 0
    	self.mytextfield = TextField.new(pfont, ptext, ptext)
    	self.mytextfield:setSample(ptext)
     
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_BOTTOM} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_BOTTOM} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_BREAKWORDS} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_CENTER} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_CENTER} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_JUSTIFIED} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_LEFT} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_LEFT} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_NOWRAP} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_REF_BASELINE} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_REF_BASELINE} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_REF_BOTTOM} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_REF_BOTTOM} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_REF_MIDDLE} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_REF_MIDDLE} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_REF_TOP} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_REF_TOP} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_RIGHT} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_TOP} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_TOP} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = 7, flags=FontBase.TLF_VCENTER} )
    --	self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_VCENTER} )
     
    	if pline == 1 then -- arab center
    		self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = -32, flags=FontBase.TLF_CENTER} )
    	elseif pline == 2 then -- tr center
    		self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_CENTER} )
    	elseif pline == 3 then -- tr left
    		self.mytextfield:setLayout( {w = pw, h = ph, lineSpacing = -8, flags=FontBase.TLF_LEFT} )
    	elseif pline == 4 then -- tr justified
    		self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_JUSTIFIED} )
    	else -- grammar
    		self.mytextfield:setLayout( {w = pw, h = ph, flags=FontBase.TLF_REF_BASELINE} )
    	end
    	self.mytextfield:setTextColor(pcolor or 0xff0000)
    	self.mytextfield:setAnchorPoint(self.anchorpointx, self.anchorpointy)
    	self:addChild(self.mytextfield)
    end

    Likes: Yan

    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
    +1 -1 (+1 / -0 )Share on Facebook
  • Apollo14Apollo14 Member
    edited October 2019
    hgy29 said:


    I would:
    1) remove setSample() API
    2) add a sample field in the layout parameters
    3) have layout honour that sample field when computing the metrics
    4) have layout use ref_top by default if a layout parameter table is supplied

    EDIT:
    5) have layout interprets ref_top as whatever is correct for the currently rendered text, irrespective of the font metrics or sample. (This makes sample almost useless I think)

    What do you all think ?

    Imo it's cool.
    Imho there's no reason to worry about legacy projects.
    There're very few legacy classes that may be useful in future (gtween, easing, scenemanager, crappy good old "button.lua", etc.)
    They're not using textfields, so they won't get broken.

    Though I'm not sure about 'Widget Candy" and "Layout" framework.
    I hope "Layout" won't get broken?
    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    “The more you do coding stuff, the better you get at it.” - Aristotle (322 BC)
  • hgy29hgy29 Maintainer
    Apollo14 said:

    Though I'm not sure about 'Widget Candy" and "Layout" framework.
    I hope "Layout" won't get broken?

    Don't worry about widget candy, setSample didn't exist at that time, but Layout may be using setSample since setSample was indeed added by @n1cke , the author of Layout.

    So maybe don't remove it yet and just deprecate it.

    Likes: Apollo14

    +1 -1 (+1 / -0 )Share on Facebook
  • rrraptorrrraptor Member
    edited October 2019
    Apollo14 said:

    "Layout" framework


    It used when you create the Layout object (so you can use just image with text or any other displayable thing). There is no use of TextFiled in main Layout.lua file. And as you can see, he uses "|" as sample most of the time, which sets origin to top left corner. I think it safe to completly remove this thing :smile:


    layout.png
    658 x 499 - 39K

    Likes: MoKaLux, Apollo14

    +1 -1 (+2 / -0 )Share on Facebook
  • Here we go again xd

    Here, I generated a 9 combinations of text alignment, and as you can see in most cases last character always out of bounds.

    I assume that this is a bug, that must be fixed? @hgy29 what do you think?)
    As a work around I just need to add space char at the end of the string.

    P.S. Project attached
    asz.png
    772 x 819 - 26K
    rar
    rar
    TextFiled_test.rar
    509K

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • I think textfield needs some love.
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • hgy29hgy29 Maintainer
    Definitely a bug, it looks like the last line's width isn't computed correctly. I am on it.

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
  • hgy29hgy29 Maintainer
    Fixed: https://github.com/gideros/gideros/commit/a0f0348eb3a39b8ebae1f182ad13d54180181e7d

    Also I suggest you use TLF_REF_LINETOP instead of TLF_REF_TOP, it looks better in most cases.
    20200608_090118_349_800x1280.png
    799 x 765 - 19K

    Likes: MoKaLux, rrraptor

    +1 -1 (+2 / -0 )Share on Facebook
Sign In or Register to comment.