Change NavBar content in AppLayout dynamically when navigation occurs - java

I would like to dynamically add buttons in the AppLayout NavBar section when navigating to a view. I'm using RouterLink links in the drawer (wrapped in tabs) i.e. the view object is not instantiated prior to the navigation event. Is there a standard way to achieving this in Vaadin 14+?
Ideally the view navigated to would be able to inspect its parent (the layout) and access the navBar to add/remove components from it.
Here is how the AppLayout looks:
MainLayout.java
public class MainLayout extends AppLayout implements BeforeEnterObserver, TrackerConfigurator {
private Map<Tab, Component> tabSet = new HashMap<>();
public MainLayout() {
FlexLayout navBarLayout = new FlexLayout(leftNavBarLayout, navBarContentContainer, rightNavBarLayout);
navBarLayout.setWidthFull();
navBarLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
navBarLayout.setAlignItems(FlexComponent.Alignment.CENTER);
addToNavbar(navBarLayout);
// Tabs
TabWithIdentifier page0 = tabForPageWithRouter("Dashboard", new IronIcon("icomoon", "icons"), DashboardView.class);
TabWithIdentifier page1 = tabForPageWithRouter("Users", new IronIcon("icomoon", "users2"), UserView.class);
...
final Tabs tabs = new Tabs(page0, page1, ...);
tabs.setOrientation(Tabs.Orientation.VERTICAL);
addToDrawer(tabs);
}
private TabWithIdentifier tabForPageWithRouter(String title, IronIcon icon, Class classType) {
icon.setSize("1.3em");
icon.getStyle().set("margin-right", "10px");
RouterLink routerLink = new RouterLink(null, classType);
routerLink.add(icon);
routerLink.add(title);
TabWithIdentifier tab = new TabWithIdentifier(routerLink);
tab.setId(title);
tab.setIdentifier(title);
tab.addClassName("drawer-tab");
return tab;
}

Resolved this case by overriding showRouterLayoutContent(HasElement content) and creating a base view class which provides a Div container for any components
I want to add to the navbar.
#Override
public void showRouterLayoutContent(HasElement content) {
super.showRouterLayoutContent(content);
BaseView baseView = null;
if (content instanceof BaseView) {
baseView = (BaseView) content;
pageTitle = new Label(baseView.getViewTitle());
controlContainer.removeAll();
controlContainer.add(baseView.getViewIconTray());
}
fireEvent(new RouterNavigated(this));
}

Related

Accessing to navbar in vaadin 14

I'm using vaadin 14 for my application.
My MainView class extends Applayout class. This allows me to use addToNavBar(true, some Components) function which adds navigation bar to your application.
Now, In my main view, inside the navigation bar, I have register and login buttons populated. If click on these buttons, using addonclick listener I delegate to other views like Login and Registration. During these view changes, the navbar on top still stays there. However, if the user logged in or registered, I want to remove these login and register buttons in navigation bar and replace them with profile picture icon located inside the navbar. However, from child views(register,login) I couldn't find a way to access to navbar with vaadin 14. Accordingly, how can I access and change the content of the navbar from child views?
public class MainView extends AppLayout {
private static final long serialVersionUID = 1L;
private final Tabs menu;
private HorizontalLayout headerLayout;
public MainView() {
setPrimarySection(Section.NAVBAR);
headerLayout = createHeaderContent();
addToNavbar(true, headerLayout);
setDrawerOpened(false);
menu = createMenu();
addToDrawer(createDrawerContent(menu));
}
private HorizontalLayout createHeaderContent() {
headerLayout = new HorizontalLayout();
headerLayout.setId("header");
headerLayout.getThemeList().set("dark", true);
headerLayout.setWidthFull();
headerLayout.setSpacing(false);
headerLayout.setAlignItems(FlexComponent.Alignment.CENTER);
headerLayout.add(new DrawerToggle());
headerLayout.add(createWebsiteName());
headerLayout.add(createMiddleSpacingInHeader());
headerLayout.add(createLoginAndRegisterButtons());
return headerLayout;
}
private Component createLoginAndRegisterButtons() {
HorizontalLayout layout = new HorizontalLayout();
layout.setPadding(true);
layout.setSpacing(true);
layout.setAlignItems(Alignment.STRETCH);
Button register = createRegisterButton();
Button login = createLoginButton();
Image loggedInUserPicture = createLoggedInUserImage();
layout.add(register, login, loggedInUserPicture);
return layout;
}
There is currently no good API for this. One reason is that framework needs to be agnostic to how you build your menu. I have solved this by creating small interface of this kind
public interface HasTabsAccessor {
public default Tabs getTabs(Component component) {
Optional<Component> parent = component.getParent();
Tabs menu = null;
while (parent.isPresent()) {
Component p = parent.get();
if (p instanceof MainLayout) {
MainLayout main = (MainLayout) p;
menu = main.getMenu();
}
parent = p.getParent();
}
return menu;
}
}
Which I can then add to views where I need to access the menu.
#Route(value = FormLayoutView.ROUTE, layout = MainLayout.class)
#PageTitle(FormLayoutView.TITLE)
public class FormLayoutView extends VerticalLayout implements BeforeLeaveObserver, HasTabsAccessor {
...
And then just using getTabs() in the view.

How do I reset the App Layout in vaadin 14?

I'm using the App Layout component in vaadin 14 and I set up the /admin route as my default view. I set up a Tabs component to switch the main areas content for those views. It works really well, however, I have no clue how to reset the /admin route to its initial state. I tried setting up an Anchor to go to "/admin" but it refreshes the page.
MainView
#Route("admin")
public class MainView extends AppLayout {
#Autowired
public MainView() {
setPrimarySection(AppLayout.Section.DRAWER);
addToDrawer(createMenuTabs());
setContent(customView);
}
private static Tabs createMenuTabs() {
final Tabs tabs = new Tabs();
tabs.setOrientation(Tabs.Orientation.VERTICAL);
tabs.add(getAvailableTabs());
return tabs;
}
private static Tab[] getAvailableTabs() {
final List<Tab> tabs = new ArrayList<>(2);
tabs.add(createTab(VaadinIcon.HOME, "Home", CustomView.class));
tabs.add(createTab(VaadinIcon.EDIT, "Events", EventsView.class));
return tabs.toArray(new Tab[tabs.size()]);
}
private static Tab createTab(VaadinIcon icon, String title,
Class<? extends Component> viewClass) {
return createTab(populateLink(new RouterLink(null, viewClass), icon, title));
}
private static Tab createTab(Component content) {
final Tab tab = new Tab();
tab.add(content);
return tab;
}
private static <T extends HasComponents> T populateLink(T a, VaadinIcon icon, String title) {
a.add(icon.create());
a.add(title);
return a;
}
}
The only way that I have made it work is by adding a route to the custom view.
CustomView
#Route(value = "admin/home", layout = MainView.class)
public class CustomView extends VerticalLayout...
but this changes the url to .../admin/home.
I simply want to go back to /admin without a refresh. I hope I'm just missing something simple in the docs.
I ended up resolving this by giving my MainView a #RoutePrefix("admin") instead of #Route and then in my "home" view the #Route("") route. This way doesn't reload or reset the app but just gets back to the home URL without a refresh.

Toolbar dissapears from custom hover in eclipse editor

I am working on an editor plugin for Eclipse that handles my own script language. In the editor, I have a hover that shows short information about element under the mouse cursor.
Now, I am trying to create a toolbar on the bottom of the hover and place a button there that will open a more detailed description online.
I have written my code based on answer to that question. The button is visible and it works when it is clicked.
However, it disappears a short time after I move my mouse over the hover. Why is this happening and how can I prevent that?
Here is the relevant part of my code:
#Override
public IInformationControlCreator getHoverControlCreator() {
return new IInformationControlCreator() {
#Override
public IInformationControl createInformationControl(final Shell parent) {
ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
DefaultInformationControl defaultInformationControl = new DefaultInformationControl(parent, tbm);
Action action = new Action() {
#Override
public void run() {
MessageDialog.openInformation(parent, "omg", "It works.");
}
};
action.setText("123 test 321");
Bundle bundle = FrameworkUtil.getBundle(this.getClass());
URL url = FileLocator.find(bundle, new Path("icons/test.gif"), null);
action.setImageDescriptor(ImageDescriptor.createFromURL(url));
tbm.add(action);
tbm.update(true);
return defaultInformationControl;
}
};
}
When hover is created with DefaultInformationControl(parent, tbm) then toolbar is visible. However when you move mouse over the hover, then it gains focus. Then method getInformationPresenterControlCreator() from DefaultInformationControl is called.
It looks like (from source code):
public IInformationControlCreator getInformationPresenterControlCreator() {
return new IInformationControlCreator() {
/*
* #see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent,
(ToolBarManager) null, fPresenter);
}
};
}
Look at return line. It nulls your Toolbar manager. That is the reason is gone.
Quick solution might be to create a new class which extends DefaultInformationControl and then in overrides
#Override
public IInformationControlCreator getInformationPresenterControlCreator() {
return new YourOwnInformationControlCreator();
}
This way you can pass correct ToolbarManager.

How to add toolbar to java text hover eclipse

I am tring to create my own text hover plugin for eclipse.
I success to write my own code in my hover, but I try to add a toolbar to the hover (inside the new tooltip opened).
I read that I need to use the getHoverControlCreator function, and I managed to add the toolbar manager that I see when the text hover is opened while running the plugin,in the debbuger I can see that the ToolBarManger has the ToolBar that has the ToolItems, but I can't see them in the real text hover when I opened it.
this is my code:
public IInformationControlCreator getHoverControlCreator() {
return new IInformationControlCreator() {
public IInformationControl createInformationControl(Shell parent) {
ToolBar tb = new ToolBar(parent, SWT.HORIZONTAL);
ToolBarManager tbm = new ToolBarManager(tb);
DefaultInformationControl dic = new DefaultInformationControl(parent, tbm);
ToolItem ti = new ToolItem(tb, SWT.PUSH);
ti.setText("hello");
tb.update();
tb.redraw();
tbm.update(true);
parent.update();
parent.redraw();
parent.layout();
return dic;
}
This is what one of the Eclipse hover controls does:
#Override
public IInformationControl doCreateInformationControl(Shell parent) {
ToolBarManager tbm = new ToolBarManager(SWT.FLAT);
DefaultInformationControl iControl = new DefaultInformationControl(parent, tbm);
IAction action = new MyAction();
tbm.add(action);
tbm.update(true);
return iControl;
}
So it does not create the ToolBar - leave that up to DefaultInformationControl. It uses an Action in the tool bar and adds it after creating the DefaultInformationControl. It just calls update(true) at the end.
(This is a modified version of parts of org.eclipse.jdt.internal.ui.text.java.hover.NLSStringHover)
MyAction would be something like:
private class MyAction extends Action
{
MyAction()
{
super("Title", .. image descriptor ..);
setToolTipText("Tooltip");
}
#Override
public void run()
{
// TODO your code for the action
}
}

Model View Presenter and Composite Views

I'm trying to follow the MVP (specifically Passive-View) pattern in a java swing ui application.
The basic design of the application reminds a wizard control. The screen is divided to two main parts:
an active view.
a static navigation bar, with navigation buttons.
The user can use buttons to change the active view, but the bar is always displayed.
Modeling this scenario, I have a bunch of diffirent screers, each with it's own presenter, view interface and view implementation (using JPanel). Then I have a Shell presenter, view intefrace and view implementation, using a JFrame. The idea is that the shell will load first and always by displayed, showing the bottom navigation bar and leaving a space for the active view. The shell presenter will allow setting the current active screen, somewhat like this:
interface View {
}
class Presenter {
View view;
public Presenter(View view) {
this.view = view;
}
public View getView() {
return view;
}
}
interface ShellView extends View {
void setActiveView(View activeView);
}
class ShellPresenter extends Presenter {
private ShellView shellView;
public void setActivePresenter(Presenter activePresenter) {
shellView.setActiveView(activePresenter.getView());
}
}
class ShellFrame implements ShellView {
private JFrame frame;
private JPanel activePanel;
private JPanel navigationBar;
public ShellFrame() {
Container c = frame.getContentPane();
c.add(activePanel);
c.add(navigationBar);
}
public setActiveView(View activeView) {
???
}
}
The problem is in the setActiveView method: I'm not sure how to set the activeView to the activePanel, when the View interface is general and as such doens't know anything about JPanels. Obviously I wouldn't want my presenters to know about JPanels as well.
Could you modify the definition of View to:
interface View {
JComponent getContainer();
}
So that each view can easily get the view contents? The shell needn't know what implementation of JComponent is being returned.
Your View interface needs to provide some way to obtain something displayable in a JPanel:
interface View {
Component getComponent();
}
Then in ShellFrame (assuming you use BorderLayout, as I would) you can set the view in the following way:
public setActiveView(View activeView) {
activePanel.add(activeView.getComponent(), BorderLayout.CENTER);
}
The problem is that your JFrame is the View, not each individual active sub-view. The view that is active right now is a rendering job, and therefore should be done by the View, not by the Presenter. Imagine you wanted to swap in a different View that instead of having only one subview visible, they were all visible, but the one that was active had a different background color. The presenter just controls which one is active, but the View controls what is meant by active.
So your task cannot be done without breaking encapsulation because you are trying to do a job that by it's very nature breaks encapsulation. So I would do something like this:
class PresenterManager {
private Presenter activePresenter;
private List<Presenter> allPresenters;
IView view;
PresenterManager(IView view) {
this.view = view;
view.subscribe(this);
}
void addPresenter(Presenter p) {
allPresenters.add(p);
}
void setView(int index) {
view.setView(index);
activePresenter = allPresenters.get(index);
}
}
class SwingView implements IView {
List<SubView> allViews;
SubView activeView;
int lastIndex;
public void setView(int index) {
if(index != lastIndex) {
activeView.setVisible(false);
activeView = allViews.get(index);
lastIndex = index;
}
}
}

Categories

Resources