@Theme(Lumo.class)
public class PlaygroundAppLayout extends ApplicationLayout {
private static final long serialVersionUID = -5604950024464910529L;
public PlaygroundAppLayout() {
super(new PlaygroundApplicationConfig());
}
}
Application Framework
ApplicationLayout
When using linkki Application Framework, all views should be displayed in a customized ApplicationLayout
which provides the application frame. Therefore, the first step to use application framework is to create a subclass of ApplicationLayout
.
The main purpose of this class is to hold the custom implementation of ApplicationConfig in which the application frame is configured. This custom implementation can be passed to the super constructor. The implementation of ApplicationLayout
itself should have a default constructor which makes it usable as a RouterLayout.
The ApplicationLayout
that surrounds the view with an ApplicationHeader
and an optional ApplicationFooter
.
The header is designed to have multiple menu actions on the left and may have some specific items like help menu or preferences on the right side.
The footer is hidden by default and can simply contain some application info.
Between ApplicationHeader
and ApplicationFooter
is the main area that displays the current view.
ApplicationMenuItemDefinition
MenuItems
can be added to the ApplicationHeader
using ApplicationMenuItemDefinition
.
For creation, ApplicationMenuItemDefinition
provides following constructors:
-
ApplicationMenuItemDefinition(String, Handler)
: Name of the menu and aHandler
that is executed on click. -
ApplicationMenuItemDefinition(String, List<ApplicationMenuItemDefinition>)
: Name of the menu and a list ofApplicationMenuItemDefinitions
as sub-menu items. Sub-menus are added in order as defined in the sub-menu list. -
ApplicationMenuItemDefinition(String, String)
: Name of the menu and a URL as String to navigate to on click. This method handles both external and internal links. -
ApplicationMenuItemDefinition(String, Class<? extends Component>)
: Name of the menu and aClass
to navigate to. The given class should be a route component.
More complex menu items can be implemented by creating menu item definitions directly instead of inheriting. If the implementation needs to be customizable, it can be easily done by composing ApplicationMenuItems
.
The following example uses a factory class to create the menu. A menu item contains one sub-menu item with caption "New". If only one sub-sub-menu item is given, the sub-menu-item shows a notification on click (caption → New). If multiple sub-sub-menu items are given, those are shown in the sub-menu item (caption → New → sub-sub-menu items). This can be used in ApplicationConfig#getMenuItemDefinitions()
public class CustomMenuItemDefinitionCreator {
public static ApplicationMenuItemDefinition createMenuItem(String name, List<MySubSubMenuItem> subSubMenuItems) {
return new ApplicationMenuItemDefinition(name, Collections.singletonList(createSubMenuItem(subSubMenuItems)));
}
private static ApplicationMenuItemDefinition createSubMenuItem(List<MySubSubMenuItem> subSubMenuItems) {
String subMenuName = "New";
if (subSubMenuItems.size() > 1) {
return new ApplicationMenuItemDefinition(subMenuName,
subSubMenuItems.stream()
.map(i -> new ApplicationMenuItemDefinition(i.getName(), () -> i.getCommand().apply()))
.collect(Collectors.toList()));
} else if (subSubMenuItems.size() == 1) {
MySubSubMenuItem onlyItem = subSubMenuItems.get(0);
return new ApplicationMenuItemDefinition(onlyItem.getName(), onlyItem.getCommand());
} else {
return new ApplicationMenuItemDefinition(subMenuName, Handler.NOP_HANDLER);
}
}
public static class MySubSubMenuItem {
private String name;
public MySubSubMenuItem(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Handler getCommand() {
return () -> Notification.show(name);
}
}
}
To add a MenuItem
when to your own ApplicationMenu
or SubMenu
the following methods can be used:
-
createItem(ApplicationMenu)
: Creates and adds aMenuItem
to the givenApplicationMenu
. -
createItem(SubMenu)
: Creates and adds aMenuItem
to the givenSubMenu
.
ApplicationMenuItemDefinition startMenuItemDefinition = new ApplicationMenuItemDefinition("Start", () -> goToStart());
ApplicationMenu menuBar = new ApplicationMenu();
startMenuItemDefinition.createItem(menuBar);
ApplicationMenuItemDefinition itemDefinition = new ApplicationMenuItemDefinition("Start", () -> goToStart());
-- new MenuBar to create item for sub-menu
MenuBar menuBar = new MenuBar();
SubMenu subMenu = menuBar.addItem("item").getSubMenu();
itemDefinition.createItem(subMenu);
Application Configuration
The main aspects of the application are configured using the ApplicationConfig
. This interface needs to be implemented once in every linkki application and provided to the ApplicationLayout
.
ApplicationConfig
refers to an ApplicationInfo
which defines basic information about the application such as name and copyright. Furthermore, parts of ApplicationLayout
is configured by the ApplicationConfig
, such as the header, footer, as well as converters that should be used to convert values between UI and the underlying model.
Components
The linkki application framework contains the following components:
LinkkiTabLayout
The LinkkiTabLayout
is a UI component that gives access to several different views.
On the left there is a vertical bar containing icons (buttons) for every sheet. The tooltip of the button displays the name of the corresponding sheet. The content of the selected sheet is displayed to the right of the bar.
In order to create a sidebar instantiate the LinkkiTabLayout
and add LinkkiTabSheets
that can be built using a builder. Every sheet requires an icon, a name and a content supplier (Supplier<Component>
). The supplier is called when the sheet is selected for the first time (lazy initialization). This approach only creates the content if it is requested by the user. It also spreads out the component creation, avoiding long loading times at the start.
Additionally you can supply a UiUpdateObserver
that is triggered every time the sheet is selected, that means it gets visible. Use this observer to update your binding context in case of changes to the underlying model while the sheet was not selected. Our sample application contains a sidebar layout with two sheets that highlights the utility of that approach. The second sheet displays a list of reports which are created on the first sheet. Thus the second sheet needs to be updated every time it is selected because the underlying data might have changed in the meantime.
addTabSheets(LinkkiTabSheet.builder("CreateReport")
.caption(VaadinIcon.STAR_HALF_LEFT_O.create())
.content(this::createReportLayout)
.build(),
LinkkiTabSheet.builder("ReportList")
.caption(VaadinIcon.FILE_O.create())
.content(this::createReportListLayout)
.build());
Headline
For every sheet it is useful to have a headline that describes the current content. It natively has a headline caption and could be extended by subclasses.
To use a Headline
simply instantiate and add the component to your content.
If you want the Headline’s title to be updated dynamically, you can also bind it to a PMO. To do so, create a PMO containing a corresponding getter method for Headline#HEADER_TITLE
:
public class HeadlinePmo {
private List<Report> reports;
public HeadlinePmo(List<Report> reports) {
this.reports = reports;
}
public String getHeaderTitle() {
return "Report List - Existing reports: " + reports.size();
}
}
Then bind it with the headline:
new Binder(headline, headlinePmo).setupBindings(getBindingContext());