DecoratedPanel/Drawer Component

Hi,

I’m trying to build a Panel/Container, which we can “decorate” using images. Ultimately, I am going to want to show/hide the contents of the Container by clicking on the Caption (like Henrik’s Drawer Component, but without the animation).

Here’s my work in progress :

The components with a background of red are a simple, non-container Component (where the VCompnent extends gwt.DecoratorPanel) - as you can see, it looks fine.

The Yellow backgrounded component is my attempt to re-use/cut-n-paste code from the Panel componentIn the GWT VComponent, I’ve added in the table elements like the DecoratorPanel does, and moved the contentNode to be a child of the center table cell. It’s all almost right - the problem is the size of the component does not take into account the CSS size of the cells on the left and right. (If I frig the size using FireBug, I see both the left and right hand sides correctly)

I’m not sure what the best thing to do here is - start with a clone of Panel, and change the generated DOM as I have been doing, or implement from scratch.

If you were trying to do what I am trying to do, where would you start, and how would you go about it?

Cheers,

Charles.

Is there some specific reason why you don’t want to make this as a CustomComponent (build everything on the server side from existing components)?

No, not specifically - but I though I would need control over the DOM in order to be able to style the image border myself.

Happy for that not to be the case, if you can point me in the right direction! I can see how to use a VerticalLayout for the contents, and how to build my own caption bar - but how does one style the CustomComponent itself ?

I’ve usually used a VerticalLayout as the root for the CustomComponent. Then I’ve created a header layout (HorizontalLayout) containing a button and a label. The button toggles the expanded/collapsed status of the component and the label contains the caption. When the button is clicked, I check if the component is expanded or collapsed. If it is collapsed, then I (of course) need to expand it, which I do by adding another layout (I call this the content layout) to the root layout. When I want to collapse the component, then I just remove the content layout from the root layout. This way you can style the header just like you’d style any other HorizontalLayout.

Yes - those are the bits I understood :wink: What I don’t get is how I can style the surrounding component - which, by default, would be simple DIV - i.e. it wouldn’t give me what I need.

However, poking about a little, it looks like I might be able to use either a GridLayout or a CustomLayout.

I’ll see what I can come up with.

Cheers,

Charles.

Ah, misunderstood you. I’d go with CustomLayout if there are a lot of special styles you need to apply.

Hi Charles,

If I were to integrate GWT DecoratorPanel to Vaadin, I’d extend it and make it implement the com.vaadin.terminal.gwt.client.Container interface.

Then I’d implement all the needed methods, and take care especially with the size calculations, since the DecoratorPanel is indeed a TABLE element, which will not behave nicely in all situations. Container.getAllocatedSpace(Widget) is the method that needs to return the space of the center cell of the table for the contained layout in the panel.

You did notice this comment from the DecoratorPanel class?

I don’t quite get what it means exactly, but just thought I’d point it out.

On the server-side I’d extend the Panel, as I suppose you’ve done as well.

Hi,

Thanks Jouni.

As Kim has pointed out, I can get the intended functionality using CustomComponent, and save a whole lot of heartache.

I originally tried exactly the approach you suggest - and indeed it is the space calculations that are giving me grief.

Are you aware of any CSS techniques I can use to get the appearance I’m after - specifically, I’m trying to do the equivalent of rounded-corners-using-images - or a box constructed of images. I have an basically have an image of a panel, which I can (and indeed have) split into the appropriate slices.

I supposed I could try and use Sliding Doors - but I’d like to keep the images transparent, allowing us to put them on different colour backgrounds.

I imagine you are doing something similar for the Window in Runo, and the groovy top-right-hand (with the description etc) in the Sampler. Any pointers from the Vaadin UI Guru would be appreciated!

Cheers,

Charles.

Hi,

I’ve got a lot further - but I’m still not quite there. Here’s a screenshot to whet your appetite.

I’ve subclassed CustomComponent and VCustomComponent (See below) : As you can see, the server side component is completely empty - the client component simply adds a load of DIVS, which are then positioned to form an image border. What I can’t quite work out how to do, though, is to adjust the size to account for the new DIVs.

I suspect that this is not the right way to do what I am trying to do - which is a shame, as it’s almost there! I think I would need to fiddle with the #updateFromUIDL method, but I’m not entirely sure. I note in passing that many of the components are not really designed for extension (private attributes, no accessor methods), which means that if I were to override #updateFromUIDL, I’d have to do a lot of cutting-and-pasting.

So - as you can see, it looks like it’s nearly right : you can (hopefully) see where I am trying to get to. What should I do? Override Panel instead of CustomComponent? Or fix my overriding of CustomComponent - and if so, how?

Cheers,

Charles.


Server Component


@com.vaadin.ui.ClientWidget(com.hpdsoftware.vaadin.components.client.ui.VDecoratedPanelNew.class)
public class DecoratedPanelNew extends CustomComponent {
}


Client Component


public class VDecoratedPanelNew extends VCustomComponent {
  
  private static final String CLASSNAME = "v-decorated-panel";
  private Element containerElement ;
  
  public VDecoratedPanelNew(){
      setElement(createCell(CLASSNAME));
      
      Element mainElement = getElement();
      mainElement.appendChild(createCell("topLeft"));
      mainElement.appendChild(createCell("topCenter"));
      mainElement.appendChild(createCell("topRight"));
      mainElement.appendChild(createCell("middleLeft"));
      
      containerElement = createCell("middleCenter");
      
      mainElement.appendChild(containerElement);
      mainElement.appendChild(createCell("middleRight"));
      mainElement.appendChild(createCell("bottomLeft"));
      mainElement.appendChild(createCell("bottomCenter"));
      mainElement.appendChild(createCell("bottomRight"));
      
      
  }

  private Element createCell(String className) {    
    Element cell = DOM.createDiv();
    cell.setClassName(className);
    return cell;
  }

  
  public Element getContainerElement() {
    return containerElement;
  }
  
}


CSS Snippet (only top row - others are very similar)


.v-decorated-panel {
	position:relative;
	background: transparent;	
	padding:6px 9px 11px 7px;	
}

.v-decorated-panel .topLeft	{
	position: absolute; overflow: hidden;
    top: 0px;             left: 0px;
    width: 7px;         height: 6px;
    background: blue;
    background: url(images/box-tl.png) no-repeat;    	
}

.v-decorated-panel .topCenter {	
	position: absolute; overflow: hidden;	
	left:7px; right:9px;
	top:0px; height:6px;
	background: url(images/box-tm.png) repeat-x;
	/*background: green;*/	
}

.v-decorated-panel .topRight	{	
	position: absolute; overflow: hidden;
    top: 0px;             right: 0px;
    width: 9px;         height: 6px;
    background: url(images/box-tr.png) repeat-x;
}	

As I see it, the only thing you need to do is override
public RenderSpace getAllocatedSpace(Widget child)
to return the offsetWidth/offsetHeight of the
containerElement
(I assume you don’t specify any other CSS for it than background), since the default render space of VCustomComponent does not take possible paddings into account.

On second though, I think you actually need to reduce the padding values from the original render space. But since that’s a private variable, you need to construct it yourself. Something like this:

public RenderSpace getAllocatedSpace(Widget child) {
    RenderSpace renderSpace = new RenderSpace();
    renderSpace.setWidth(containerElement.getOffsetWidth()); // Should suffice, since DIV's are naturally 100% wide
    renderSpace.setHeight(getElement().getOffsetHeight() - paddingTop - paddingBottom);
}

Then you just need to fetch the padding values somehow. And that’s the trickiest part, since different themes probably specify different paddings.

One option for measuring the paddings is to use the currentStyle/computedStyle methods that browsers offer (lots of hairy details involved), or use a similar tactic as we’ve used in our layout calculations:

  • store the original offset value to a temporary variable
  • set the paddings to zero (
    getStyle().setProperty(“padding”, “0”);
    )
  • measure the offset value again
  • compare the values to get the padding value
  • store the padding value to some permanent variable, so you don’t need to perform this calculation each time the panel size changes
  • restore to paddings by setting them to undefined (
    getStyle().setProperty(“padding”, “”);
    )

Hope you can get it to work, would be a great addition to the Directory.

Thanks Jouni - that’s put me so close I can almost feel it!

It’s still not quite there, but I’m off for a week - moving house.

I have removed all the padding from the css (!) and changed the getAllocatedSpace method like this


  public RenderSpace getAllocatedSpace(Widget child) {
    return new RenderSpace(
        containerElement.getOffsetWidth(), 
        containerElement.getOffsetHeight());
}

The image borders, though, are on top of the main contents. If I put back the padding into the css, I get scroll bars on the accordian.

I’m sure it’s all fairly simple… It looks right - it’s just I know it’s wrong! I’ll be back on it soon.

Cheers,

Charles

Did you ever this going enough to publish it?