Problem grouping buttons in a toolbar with border

I successfully figured out how to get a decent looking toolbar with borders around the toolbar items based on some code from the tutorial. I am having no luck trying to group two buttons and make them look like a single toolbar item.

The issue is that the border for the grouped item never looks quite right. I ended up using a WeeLayout both for the toolbar and for the grouped button part of the toolbar. I couldn’t get even normal borders to work properly with any of the built-in layouts including CssLayout, which one of the posts states does support borders. I also tried the reindeermods add-on but couldn’t get borders to work well there either. Here is the code which highlights the issue:


import org.vaadin.weelayout.WeeLayout;

import com.vaadin.terminal.Resource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.NativeButton;

public class Toolbar extends WeeLayout {

    private static final long serialVersionUID = 2666221646559979346L;
    private static final String STYLENAME = "toolbar";

    public Toolbar() {
        super(Direction.HORIZONTAL);
        setMargin(false);
        setStyleName(STYLENAME);
    }

    @Override
    public void addComponent(final Component c) {
        super.addComponent(c);
        setComponentAlignment(c, Alignment.MIDDLE_CENTER);
    }

    public void addNewButton(final String caption, final Resource icon, final String width) {
        final Button b = new NativeButton(caption);
        b.setIcon(icon);
        b.setWidth(width);
        b.setHeight("100%");
        addComponent(b);
    }

    public void groupTwoButtons(final String caption1, final Resource icon1, final String caption2,
            final Resource icon2, final String totalWidth) {
        // only CssLayout supports borders
        final WeeLayout wee = new WeeLayout(Direction.HORIZONTAL);
        wee.setMargin(false);
        wee.setStyleName("toolbar");

        final Button b1 = new NativeButton(caption1);
        b1.setIcon(icon1);
        b1.setHeight("100%");
        wee.addComponent(b1);

        final Button b2 = new NativeButton(caption2);
        b2.setIcon(icon2);
        b2.setHeight("100%");
        wee.addComponent(b2);

        wee.setWidth(totalWidth);
        wee.setHeight("55px");
        addComponent(wee);
    }
}

import org.vaadin.weelayout.WeeLayout;
import org.vaadin.weelayout.WeeLayout.Direction;

import com.vaadin.Application;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.AbsoluteLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;

public class ToolbarTestApplication extends Application {

    private static final long serialVersionUID = -2449768291413727223L;
    private WeeLayout mainContentLayout;
    private Toolbar toolbarNoGroup, toolbarWithGroup;
    private final Window mainWindow = new Window("Toolbar Test");

    public static final ThemeResource icon1 = new ThemeResource("../images/arrow_left.png");
    public static final ThemeResource icon2 = new ThemeResource("../images/funnel.png");
    public static final ThemeResource icon3 = new ThemeResource("../images/eye.png");
    public static final ThemeResource icon4 = new ThemeResource("../images/function.png");
    public static final ThemeResource icon5 = new ThemeResource("../images/chart_pie.png");

    @Override
    public void init() {
        try2();
    }

    private void try1() {
        setTheme("mytheme");
        // setTheme("reindeermods");
        mainContentLayout = new WeeLayout(Direction.VERTICAL);
        mainContentLayout.setImmediate(true);
        mainContentLayout.setWidth("100%");
        mainContentLayout.setHeight("100%");
        mainContentLayout.setMargin(true);

        // mainToolbar
        toolbarNoGroup = new Toolbar();
        toolbarNoGroup.setImmediate(false);
        toolbarNoGroup.setWidth("100.0%");
        toolbarNoGroup.setHeight("62px");

        final Label spacer1 = new Label("");
        spacer1.setHeight("1em");

        toolbarWithGroup = new Toolbar();
        toolbarWithGroup.setImmediate(false);
        toolbarWithGroup.setWidth("100.0%");
        toolbarWithGroup.setHeight("62px");

        mainContentLayout.addComponent(toolbarNoGroup);
        mainContentLayout.addComponent(spacer1);
        mainContentLayout.addComponent(toolbarWithGroup);

        final AbsoluteLayout layoutWithMargin = new AbsoluteLayout();
        layoutWithMargin.addComponent(mainContentLayout,
                "top:10px;left:10px;bottom:10px;right:10px");
        setUpToolbar(toolbarNoGroup, false);
        setUpToolbar(toolbarWithGroup, true);
        mainWindow.setContent(layoutWithMargin);
        setMainWindow(mainWindow);
    }

    private void try2() {
        setTheme("mytheme");

        // mainToolbar
        toolbarNoGroup = new Toolbar();
        toolbarNoGroup.setImmediate(false);
        toolbarNoGroup.setWidth("100.0%");
        toolbarNoGroup.setHeight("62px");

        final Label spacer1 = new Label("");
        spacer1.setHeight("1em");

        toolbarWithGroup = new Toolbar();
        toolbarWithGroup.setImmediate(false);
        toolbarWithGroup.setWidth("100.0%");
        toolbarWithGroup.setHeight("62px");

        final AbsoluteLayout layoutWithMargin = new AbsoluteLayout();
        layoutWithMargin.addComponent(toolbarNoGroup, "top:10px;left:10px;bottom:10px;right:10px");
        layoutWithMargin
                .addComponent(toolbarWithGroup, "top:90px;left:10px;bottom:10px;right:10px");
        setUpToolbar(toolbarNoGroup, false);
        setUpToolbar(toolbarWithGroup, true);
        mainWindow.setContent(layoutWithMargin);
        setMainWindow(mainWindow);
    }

    private void setUpToolbar(final Toolbar bar, final boolean group) {
        // undefined ensures no space between components
        bar.setWidth("-1px");

        bar.addNewButton("Icon1", icon1, "65px");
        bar.addNewButton("Icon2", icon2, "60px");

        if (group) {
            bar.groupTwoButtons("No icon long text", null, "", icon3, "120px");
        } else {
            bar.addNewButton("No Icon long text", null, "70px");
            bar.addNewButton("Icon3", icon3, "40px");
        }
        bar.addNewButton("Icon4", icon4, "45px");
        bar.addNewButton("Icon5", icon5, "55px");
    }

}
@import "../runo/styles.css";

/* makes textfields, like the username in the login dialog, not resizable */ 
.v-textarea {
	resize: none;
}

/* from vaadin tutorial 
Everything is done with nativebutton instead of button, though, since 
the regular button doesn't work properly with centered text and images 
and with the caption broken up across lines AND with width specified.
Nativebutton works fine, though.
*/
.toolbar .v-nativebutton {
	display: block;
	background: transparent;
	border-style: outset;
	border-width: medium;
}

/* Makes the caption break across lines */
.toolbar .v-nativebutton .v-nativebutton-caption {
	display: block;
	white-space: normal;
	text-align: center;
}

.toolbar .v-nativebutton img.v-nativebutton	{
	display: block;
	margin-left: auto;
	margin-right: auto;
	margin-bottom: 5px;
}

.toolbar .v-nativebutton span.v-nativebutton-caption {
	font-size: x-small;
	text-shadow: #fafafa 1px 1px 0;
}

.toolbar .v-nativebutton .v-nativebutton-wrap, 
.toolbar .v-disabled.v-nativebutton .v-nativebutton-wrap{
	background: transparent;
	border: none;
	-webkit-border-radius: 0;
	-moz-border-radius: 0;
	border-radius: 0;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	box-shadow: none;
}

.toolbar .v-nativebutton:active .v-nativebutton-wrap,
.toolbar .v-nativebutton.v-pressed .v-nativebutton-wrap	 {
	background: transparent;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	box-shadow: none;
}
 
.toolbar .v-weelayout {
	display: block;
	background: transparent;
	border-style: outset;
	border-width: medium;
}

.toolbar .v-weelayout .v-nativebutton {
	display: block;
	background: transparent;
	border-style: none;
	border-width: none;	
}

/* Makes the caption break across lines */
.toolbar .v-weelayout .v-nativebutton .v-nativebutton-caption {
	display: block;
	white-space: normal;
	text-align: center;
}

.toolbar .v-weelayout .v-nativebutton img.v-nativebutton	{
	display: block;
	margin-left: auto;
	margin-right: auto;
	margin-bottom: 5px;
}

.toolbar .v-weelayout .v-nativebutton span.v-nativebutton-caption {
	font-size: x-small;
	text-shadow: #fafafa 1px 1px 0;
}

.toolbar .v-weelayout .v-nativebutton .v-nativebutton-wrap, 
.toolbar .v-weelayout .v-disabled.v-nativebutton .v-nativebutton-wrap{
	background: transparent;
	border: none;
	-webkit-border-radius: 0;
	-moz-border-radius: 0;
	border-radius: 0;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	box-shadow: none;
}

.toolbar .v-weelayout .v-nativebutton:active .v-nativebutton-wrap,
.toolbar .v-weelayout .v-nativebutton.v-pressed .v-nativebutton-wrap	 {
	background: transparent;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	box-shadow: none;
}

I love using Vaadin but what should be fairly standard display issues have the potential to throw you down the rabbit hole. It is also surprising there is no standard Toolbar widget, given how central this is to so many applications.

Thanks for any help.

Jonathan

I have usually done it the same way as the Chameleon theme does. it. That is that you use any layout, add buttons there, and give the first and last buttons aditional theme names. In the custom button theme you remove the backgrounds on v-button and v-button-wrap, add a 1px wide background to v-button that is repeated, add top and bottom border but not left and right. With the added “first” and “last” style names you add the left and right borders to form a unified border around the toolbar.

Check out
http://dev.vaadin.com/browser/addons/ChameleonTheme/WebContent/VAADIN/themes/chameleon/compound/segment/segment.css

Chameleon also has a utility class adding those styles - see
Segment.java
. It does not support removing buttons, but that would be trivial to add.