[Nifty1.3] Example of ScrollPanelBuilder. (Solved)

Hi,

I’m trying now to create a ScrollPanel dynamicly for one of my sheets.

For that I intend to use the ScrollPanelBuilder class :

[java]

private static Element createMapItemPopup(Nifty nifty, Screen screen, Element parent, CharacterItem item)

{

ScrollPanelBuilder scrollBuilder = new ScrollPanelBuilder( “” );

scrollBuilder.width( “100%” );

scrollBuilder.height( “100%” );

scrollBuilder.childLayoutVertical();

Element scrollPanel = scrollBuilder.build( nifty, screen, parent );

createHeader( nifty, screen, scrollPanel, "Item : " );

[…]

}

[/java]

I’ve got a problem when I try to add item to it.

The items are added below the scrollPanel and not inside :frowning:

In the last example, my ‘header’ “Item :” has been added below my scroll panel :



I think I need to retrieve the “childRootId” Element of the scroll Panel before added items but I don’t know how xD

So if someone as an example of how to use it :slight_smile:

( I’ve already been to this url :

http://nifty-gui.svn.sourceforge.net/viewvc/nifty-gui/nifty-default-controls-examples/trunk/src/main/java/de/lessvoid/nifty/examples/controls/scrollpanel/ScrollPanelDialogBuilder.java?revision=1351&view=markup&sortby=date

But the syntaxe used is not something I’m familiar with.

Can someone give me an example with the classic syntaxe ? :slight_smile:

Thanks in advance!

you either need to nest elements like in the example from nifty you’ve posted:



[java]control(new ScrollPanelBuilder(“scrollPanel”) {{

width("");

height("
");

image(new ImageBuilder() {{ // this will automatically resolve the childRootId and add the image directly under the childRootId

filename(“background-new.png”);

}});

}}); [/java]



or you’ll need to receive the “childRootId” element specified by the control. In case of the ScrollPanel this would work:



[java]Element scrollPanel = scrollBuilder.build( nifty, screen, parent );

Element theChildRootIdElementThatIShouldAddChildElementsToo = scrollPanel.findElementByName("#nifty-scrollpanel-child-root");

// create new elements using theChildRootIdElementThatIShouldAddChildElementsToo as the parent[/java]



This seems a bit confusing at first when working with controls but when you take a closer look at the ScrollPanel control you’ll see that it brings a bunch of other panels/controls with it. So the “childRootId” specified at the tag will specifiy the element that child elements of the will be added to automatically:



[xml]<?xml version=“1.0” encoding=“UTF-8”?>

<nifty-controls>

<controlDefinition name=“scrollPanel” style=“nifty-scrollpanel” childRootId="#nifty-scrollpanel-child-root" controller=“de.lessvoid.nifty.controls.scrollpanel.ScrollPanelControl”>

<panel childLayout=“vertical” visibleToMouse=“true”>

<panel childLayout=“horizontal” visibleToMouse=“true”>

<!-- this panel is the actual parent of all elements nested into the outer <control name=“scrollPanel” … element

<panel id="#nifty-scrollpanel-child-root" childLayout=“absolute” childClip=“true” style="#scrollpanel" visibleToMouse=“true” />

<control id="#nifty-internal-vertical-scrollbar" name=“verticalScrollbar” />

</panel>

<panel id="#nifty-internal-horizonal-panel" childLayout=“horizontal”>

<control id="#nifty-internal-horizontal-scrollbar" name=“horizontalScrollbar” />

<panel align=“right” style="#bottom-right" />

</panel>

</panel>

</controlDefinition>

</nifty-controls>[/xml]



So when using the scrollpanel you’re able to apply child elements and it will do the right thing behind your back:



[xml]<control name=“scrollPanel” …>

<image filename=“some-pic.png” />

</control>[/xml]



Well, one could argue if Nifty should automatically detect this when adding elements directly from java to controls that use the childRootId feature. I’ll need to think about that for a bit :smiley:

Thanks,

I will use the second solution ( Since I don’t really understand the first one )



It seems that #nifty-scrollpanel-child-root is giving the correct child but I still have problem using it xD

I finally understood that, by default, this child Element as an “absolute” layout.



I tried to add a vertical Panel inside it. ( One bigger that this childElement )

The panel is correctly added but … the scrollbar is not updated ( and i can’t display the bottom of my vertical panel )



Does nifty needs to recalculate the child panel size at some points ? I don’t know what I’m missing oO

Here is the code I use :

[java]

private static Element createMapItemPopup(Nifty nifty, Screen screen, Element parent, CharacterItem item)

{

Element scrollPanel = createScrollPanel( nifty, screen, parent );

Element scrollContainer = scrollPanel.findElementByName( “#nifty-scrollpanel-child-root” );



Vector<ElementBuilder> list = new Vector<ElementBuilder>();

list.add( createHeader( "Item : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );



createVerticalPanel( nifty, screen, scrollContainer, list );



scrollContainer.layoutElements();

updateScrollBars( scrollPanel );

parent.show();

return scrollPanel;

}



private static Element createScrollPanel( Nifty nifty, Screen screen, Element parent )

{

ScrollPanelBuilder scrollBuilder = new ScrollPanelBuilder( “” );

scrollBuilder.width( “100%” );

scrollBuilder.height( “100%” );

scrollBuilder.set( “vertical”, “true” );

scrollBuilder.set( “horizontal”, “false” );

return scrollBuilder.build( nifty, screen, parent );

}



private static TextBuilder createHeader( String text )

{

TextBuilder textBuilder = new TextBuilder();

textBuilder.text( " " + text );

textBuilder.width( lineWidth() + “px” );

textBuilder.height( lineHeight() + “px” );

textBuilder.font( “textButton.fnt” );

textBuilder.textHAlignLeft();

return textBuilder;

}



private static Element createVerticalPanel( Nifty nifty, Screen screen, Element parent, Vector<ElementBuilder> list )

{

PanelBuilder panelBuilder = new PanelBuilder();

panelBuilder.x( “0px” );

panelBuilder.y( “0px” );

panelBuilder.width( lineWidth() + “px” );

panelBuilder.width( lineHeight() * list.size() + “px” );

panelBuilder.alignLeft();

panelBuilder.valignTop();

panelBuilder.set( “childLayout”, “vertical” );

Element panel = panelBuilder.build( nifty, screen, parent );



for( int i = 0; i < list.size(); i++ )

list.get( i ).build( nifty, screen, panel );



return panel;

}



private static void updateScrollBars( Element elem )

{

ScrollPanel scrollPanel = elem.getNiftyControl( ScrollPanel.class );

scrollPanel.setAutoScroll( AutoScroll.TOP );

scrollPanel.setStepSizeY( lineHeight() );

//scrollPanel.setPageSizeY( lineHeight() );

//scrollPanel.setVerticalPos( 0 );

}

[/java]

there is a typo in your code:



[java]panelBuilder.width( lineHeight() * list.size() + “px” );[/java]



I suspect you want to set the height here :stuck_out_tongue:



(and you’ll need to set the height because the scrollpanel uses an absolute panel as the scroll container which requires you to set width/height and position … but from your code I think you understood that and only the typo causes the issues)

How come I didn’t see that … xD



I fixed but… it seems its not enough.

Do I need to set something inside the scrollPanel too ? ( like setPageSizeY() or setVerticalPos() )

For now, I’m only using setAutoScroll( AutoScroll.TOP );



Edit :

I tried to interact directly with the scrollbar

[java]

elem.findElementByName("#nifty-internal-vertical-scrollbar" );

[/java]

And use the Nifty controls Slider or Scroolbar … but I can’t fix my scrollbar :frowning:

did you call element.layoutElements() after you’ve changed the height? when you change constraints you’ll need to call this so that your changes take effect. you can either call it for the whole screen or for the parent element of the element you’ve changed. I don’t see any other cause for your problem at the moment o_O

What does layoutElements() really does ? it calculate the size of each panel inside the one given in argument ?



[java]

private static Element createScrollPanel( Nifty nifty, Screen screen, Element parent, Vector<ElementBuilder> list )

{

ScrollPanelBuilder scrollBuilder = new ScrollPanelBuilder( scrollId );

scrollBuilder.width( "100%" );

scrollBuilder.height( "100%" );

scrollBuilder.set( "vertical", "optional" );

scrollBuilder.set( "horizontal", "false" );

Element scrollPanel = scrollBuilder.build( nifty, screen, parent );

Element scrollContainer = scrollPanel.findElementByName( childRootId );



Element elem = createVerticalPanel( nifty, screen, scrollContainer, list );



parent.show();

parent.layoutElements();



System.out.println( "Vertical panel inside. Height : " + elem.getHeight() );

System.out.println( "Scroll panel. Height : " + scrollPanel.getHeight() );

System.out.println( "Scroll child root. Height : " + scrollContainer.getHeight() );



updateScrollBars( scrollPanel );

return scrollPanel;

}

[/java]


Vertical panel inside. Height : 250
Scroll panel. Height : 200
Scroll child root. Height : 200


But the vertical scroll bar is not displayed;
( if I put scrollBuilder.set( "vertical", "true" ); the scroll is displayed but it behaves has if the content were fitting inside the panel )




I think I need to specify something to let the scroll panel recalculate the scroll position.
For now I do nearly nothing in the method updateScrollBars :
[java]
private static void updateScrollBars( Element elem )
{
ScrollPanel scrollPanel = elem.getNiftyControl( ScrollPanel.class );
scrollPanel.setAutoScroll( AutoScroll.TOP );
scrollPanel.setVerticalPos( 0 );
//elem.layoutElements();
}
[/java]

I think I miss something here but I don't now what xD.

---

I tried to use
[java]
scrollPanel.setAutoScroll( AutoScroll.BOTTOM );
[/java]
To see if I can see the elements in the bottom, but the panel is still scrolled on top.
What does layoutElements() really does ?

Nifty layout is based on the childLayout of the parent container. If you have an element with childLayout="horizontal" and it has, for example, two child elements then these elements are layouted in respect to the "horizontal" childLayout taking any width constraints into account. When the GUI is first loaded (either from XML or being build with the Builders from Java) the initial state of the width attributes, lets say for example the first has a width="20%" and the second has a width="80%" are resolved to actual pixel coordinates. This whole process is repeated for all elements starting at the top, the screen-Element down to the very last element.

After the initial layout has been calculated there is currently no automatic relayout performed! If you change the first element width to 30% and the second element width to 70% you'll not see any change. What you need to do is to trigger the relayout by going to the parent element and say parent.layoutElements(). Nifty will then figure out how everything needs to be layouted starting at the parent you've called layoutElements() at and then down the whole hierachy of elements.

The layout process is somewhat complicated sometimes and is therefore not triggered automatically. So when you need to change several things at once you can process the layout only once and not multiple times. (One of the ideas for an eventually Nifty 2.0 would be to make this process a lot smarter and to perform relayout automatically. And now please forget that last sentence again :D :D :D)
--
About the actual problem :)
I think the ScrollPanel does not currently recognize that the content has changed. Can you try to call:
[java]public void setUp(final float stepSizeX, final float stepSizeY, final float pageSizeX, final float pageSizeY, final AutoScroll auto) {[/java]
after you've changed the content? This should internally tell the scrollbars to take the new size of the content into account.

Thanks for the informations about the layoutElements() :slight_smile:

I just tried this :

[java]

scrollPanel.setUp( 0, lineHeight, panel.getWidth(), panel.getHeight(), AutoScroll.TOP );

[/java]

But it’s no good either :frowning:



Perhaps it doesn’t see my verticalPanel as the childRoot of my scroll panel…



I’m using this one as parent :

[java]

private final static String childRootId = “#nifty-scrollpanel-child-root”;

[/java]

But I had it via a PanelBuilder. Perhaps, the scrollPanel has no knowledge that I added an element to the childRoot.

I’ve played around with this a bit and it works here.



Please make sure that pageSizeX and pageSizeY of that setUp() call are not the size of the area but instead it’s a value that is send down to the scrollbars so that they know by how much you’d like them to scroll when you click on the area below or above the scrollhandle (which will perform a page up / page down movement which is simply a bigger step then the usual clicking on the up/down buttons of the scrollbar which will increment/decrement by the given stepSize value).



The following code works for me (I’ve modified your code a bit because I don’t have all of your resources):



[java] private static Element createMapItemPopup(Nifty nifty, Screen screen, Element parent)

{

Element scrollPanel = createScrollPanel( nifty, screen, parent );

Element scrollContainer = scrollPanel.findElementByName( “#nifty-scrollpanel-child-root” );

Vector<ElementBuilder> list = new Vector<ElementBuilder>();

list.add( createHeader( "Item : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

list.add( createHeader( "Actions : " ) );

createVerticalPanel( nifty, screen, scrollContainer, list );

scrollContainer.layoutElements();

scrollPanel.getNiftyControl(ScrollPanel.class).setUp( 0, 20, 0, 20*10, AutoScroll.OFF ); // basically 20px for clicking on the scrollbar buttons and 200px when you press below or above the scrollhandle (and when the scrollbar someday supports the page up/down keys…)

return scrollPanel;

}

private static Element createScrollPanel( Nifty nifty, Screen screen, Element parent )

{

ScrollPanelBuilder scrollBuilder = new ScrollPanelBuilder( “” );

scrollBuilder.width( “100%” );

scrollBuilder.height( “100%” );

scrollBuilder.set( “vertical”, “true” );

scrollBuilder.set( “horizontal”, “false” );

return scrollBuilder.build( nifty, screen, parent );

}

private static TextBuilder createHeader( String text )

{

TextBuilder textBuilder = new TextBuilder();

textBuilder.text( " " + text );

textBuilder.width( 500 + “px” );

textBuilder.height( 20 + “px” );

textBuilder.font( “console.fnt” );

textBuilder.textHAlignLeft();

return textBuilder;

}

private static Element createVerticalPanel( Nifty nifty, Screen screen, Element parent, Vector<ElementBuilder> list )

{

PanelBuilder panelBuilder = new PanelBuilder();

panelBuilder.x( “0px” );

panelBuilder.y( “0px” );

panelBuilder.width( 500 + “px” );

panelBuilder.height( 20 * list.size() + “px” );

panelBuilder.alignLeft();

panelBuilder.valignTop();

panelBuilder.set( “childLayout”, “vertical” );

Element panel = panelBuilder.build( nifty, screen, parent );

for( int i = 0; i < list.size(); i++ )

list.get( i ).build( nifty, screen, panel );

return panel;

}

[/java]

Thank you for taking the time to recreate my problem on your computer.



Thanks to that I finally found where my problem was…

It seems that your first answer was really the solution :


there is a typo in your code:
panelBuilder.width( lineHeight() * list.size() + "px" );


But I had done some change in my code during that time, and I changed this line :
[java]scrollBuilder.set( "vertical", "true" );[/java]
to
[java]scrollBuilder.set( "vertical", "optional" );[/java]

And that is why I never got my scrollbar :'(

Since I have the number of line I want to build and the size of the parent Element, I'm able to know if I want a scrollbar or not before creating the scrollPanel.
I changed it to :
[java]
if( verticalPanelHeight > parentHeight )
scrollBuilder.set( "vertical", "true" );
else
scrollBuilder.set( "vertical", "false" );
[/java]
And now It works perfectly !

Thank you !