PDA

View Full Version : Vertically Aligning multiline text



phantomwhale
June 24th, 2012, 16:11
I'm playing with a two-line multiline stringcontrol, which is defined as:


<stringcontrol name="name">
<bounds>65,5,-46,40</bounds>
<multilinespacing>20</multilinespacing>
<font>sheetlabel</font>
<static />
</stringcontrol>

Is there anyway to "center" it when it's only on one line ?

I could call the resetStaticBounds(65,15,-46,40) method to shift it down 10 pixels when I know it's only going to use one line, but how can I determine how many lines it's using for this approach ?

I'm guessing this might be impossible without being able to ask the String Control what it's current height is ? E.g. I tried the name.getSize() method, but they all report they are height 40, of course !

Any ideas?

Ikael
June 24th, 2012, 19:22
I was dealing with the same issue with SWFI extension's "allow longer module names" feature. The only way was to count words and when herrain charcount was exeeded the bounds was updated. I know this method is not error free since different chars take different width etc. Now that I think about it, you could try out using anchoring instead of static bounds. Set left and bottom anchoring to your control and maybe the expanding will start from bottom to upwards (!?) This is pure quess which can be tested in practise.

bradmcadam
June 24th, 2012, 19:24
You could try using

<anchored>
<to>Whatever your window is called</to>
<position>insideleft</position>
</anchored>

Instead of bounds. I've gotten my string controls to horizontally align that way. Also if it's being called within a subwindow you'll need to do something funky like


<stringcontrol>
<anchored>
<top><anchor>top</anchor></top>
<left><anchor>left</anchor></left>
<position>insidetop</position>
</anchored>
<center />
<font>sheetlabel</font>
<static>Inventory</static>
</stringcontrol>

See https://www.fantasygrounds.com/modguide/windowing.xcp for more info on that, it's where I got it from.
I'd be curious to see if it works.

phantomwhale
June 25th, 2012, 00:05
Ikael - It's precisely your feature(s) that I'm adapting ! Yeah, I saw the variable character thing, and to be honest that's the best solution I've found so far, but given the inconsistent display, I might opt for top-aligned text instead.

bradmcadam - alas, the object being anchored is still 40 pixels tall, it seems. So wherever you anchor it, you are anchoring the top-left corner of the new control, so it doesn't expand up and down as hoped when a new line is started.

Might have another play tomorrow, with a less tired head, but think I've tried this before, and think it defeated me then !

Moon Wizard
June 25th, 2012, 02:34
There is no support for a vertical center alignment in stringcontrols, and barely support for horizontal center alignment. Something that has been on the wish list, but lower priority.

Not sure if this will work, but something you can try. Anchor top, right and left; query the getSize function, if size < 2 lines, then move top anchor.

Cheers,
JPG

phantomwhale
June 25th, 2012, 15:01
Yuck.

I followed JPGs recommendation, anchored three sides of the control, and tried to detect when it's height had gone over one line.

To detect this, I needed to wait for the window entry to be created and layed out on the page. Just adding a call on the main window after the "setName" method (which sets the string control's content, amoungst other things) only reports the height as a single line everytime. Adding a loop after all the window elements have been created does detect the line heights - but then I need to deal with the case of new windowlist windows being created at runtime (in this case, by new Modules being added).

ASIDE: Admittedly, I'm not 100% positive under what condition the module list ever gets a new entry duing an FG session - I've not managed to "pop up a new one" during testing - they are either there or not there for me.

So to cover the case of new windows being added, I opted for a "onListRearranged" method on the windowlist, only triggering a "center()" method on every window within the list if a new entry had been added.

Then (double yuck) I noted that the FIRST time a center() method is called, the height is still one line ! But the SECOND time it's called, it is correctly set. So I ended up with this method (I'm not proud of it):



local centered = 0

function center()
if centered < 2 then
local width, height = name.getSize()
if height > 40 then
-- prevent name going over more than two lines
name.setAnchoredHeight(40)
end
if height > 20 then
-- shift control up half a line
name.setAnchor("top", "", "top", "absolute", 5)
end
centered = centered + 1
end
end


So yeah - there it is. Pretty ugly, but it performs well and does the trick. Without the centered variable, it would "look" neater, but the number of recursive calls to the center() method make the load time pretty poor on the list.

To really neaten things up, I need to capture whenever a new window has been created and is already laid out - all the capture points I tried (onInit(), after calling setValue() on the string, etc...) seemed to be too soon, and I need this capture point to occur if a module gets added after the windowlist is initially created - which means I'm really looking at methods on the windowlist class.

What I really wanted was an "afterCreate()" method on a windowlist's windowclass, or an "afterRendering()" method to capture the instance once all it's controls are positioned.

Any thoughts on doing this better ?

Moon Wizard
June 25th, 2012, 18:20
No other ideas right now. I think it would require some more client features to do better.

I've looked at doing a post-layout function call, but the issue I ran into was figuring out at which point in all the various loops that a) the window size is set and b) the layout code is being called. Since this changes depending on levels of embedding, it got very complex quickly. I might need to take a look again, since that would be a nice general solution to allow developers more flexibility in the short term.

Regards,
JPG

Moon Wizard
July 3rd, 2012, 00:15
So, I was looking at this again. It looks like the top-level controls for the windowinstance should call onInit after layout is complete. This is not true for anything embedded (i.e. subwindows and windowlists).

If you capture the onInit at the topmost window, and percolate down to the controls you are trying to align, are you able to do what you want? If so, I can create an event that gets called all the way down to mimic that behavior.

Otherwise, I'll have to look elsewhere, and I could use a good example to work with.

Thanks,
JPG

phantomwhale
July 15th, 2012, 02:22
Ok, so I've had time to address this again, and your right.

By adding the following to my windowclass script:



function onInit()
...
list.onLayout()
end


And then adding the following to my windowlist script:



function onLayout()
for _,win in ipairs(getWindows()) do
win.onLayout()
end
end


I was able to simply add the following to my windowlist instance script:



function onLayout()
center()
end


Where the center() method does the calculation for centering.

This then works, only calls the center() method the one time, and has a very low processing overhead. Perfect !

So it would be nice having an "onLayout" style method, such that control's can declare what they will do onLayout, without having to couple high-level window scripts with knowledge of exactly which subwindows need the event to be triggered on them.

For now, I'm content to have it coded for the one place I use it. Can think of a couple other places I've used (and abused) the onListRearranged() method where having an onLayout trigger might have helped reduce some of the complexity (player combat tracker, for one).

Understanding the layout event trigger certainly was the key for me here.

Thanks,
Ben