Vaadin set mouseover validation message (tooltip) to show without delay? - vaadin

Vaadin 7.6.2
When I mouseover a field that has failed validation I see the error message as expected, however the delay before this message appears is too slow and should be immediate, as the user can click into the field BEFORE the message pops open.
Is there a way to adjust the time it takes to display a validation message?

Add the following css to your custom theme and compile. The default animate in/out time is 120ms, below I have changed the animate in time to 1ms to get rid of the validators tooltip delay.
.v-contextmenu[class*="animate-in"] {
-webkit-animation: valo-overlay-animate-in 1ms;
-moz-animation: valo-overlay-animate-in 1ms;
animation: valo-overlay-animate-in 1ms;
}
.v-contextmenu[class*="animate-out"] {
-webkit-animation: valo-animate-out-fade 120ms;
-moz-animation: valo-animate-out-fade 120ms;
animation: valo-animate-out-fade 120ms;
}
UI Code...
#Theme("mytheme")
#Widgetset("com.kevin.vaadin.sample.MyAppWidgetset")
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final TextField name = new TextField();
name.setCaption("Type your name here:");
Button button = new Button("Click Me");
button.addClickListener( e -> {
layout.addComponent(new Label("Thanks " + name.getValue()
+ ", it works!"));
});
name.addValidator(new StringLengthValidator("Name is too short.", 5, 10000000, false));
layout.addComponents(name, button);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
#WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}

Related

Avoid refreshing of content in SplitLayout

I have a splitlayout in my vaadin application in which in the first column I should show different pages added through the addToPrimary method, while in the second column I should have a page which contains an IFrame with a videoconference. Now the problem is that when I change route in the first column, even the second is updated and this refreshes the IFrame. The implementation of the showRouterLayoyutContent is the following:
#Override
public void showRouterLayoutContent(HasElement content) {
if (this.accessControl.isAccessGranted(UI.getCurrent(), ((ContentView) content).getName()) && ((ContentView) content).getName().equals("contattaView") ) {
setLayoutCall((com.vaadin.flow.component.Component) content);
}
else if (this.accessControl.isAccessGranted(UI.getCurrent(), ((ContentView) content).getName())) {
setLayoutContent((com.vaadin.flow.component.Component) content);
}
}
And the two methods setLayoutCall and setLayoutContent are the following:
private void setLayoutContent(com.vaadin.flow.component.Component content) {
split.addToPrimary(content);
}
private void setLayoutCall(com.vaadin.flow.component.Component content) {
split.addToSecondary(content);
split.setThemeName("visible-split");
}
How can I avoid to refresh the entire content when I update the first column of the split layout through navigation?
UPDATE: I'm showing also a very simple code on which I'm testing. The following class is the main layout:
private SplitLayout split = new SplitLayout();
private HorizontalLayout hl = new HorizontalLayout();
private Div firstDiv = new Div();
private Div secondDiv = new Div();
public MainView() {
Button button = new Button("Click me",
event -> Notification.show("Clicked!"));
final VerticalLayout menuBar = new VerticalLayout();
menuBar.add(new RouterLink("first view", FirstView.class));
menuBar.add(new RouterLink("second view", SecondView.class));
menuBar.setAlignItems(Alignment.CENTER);
add(menuBar);
//split.addToPrimary(firstDiv);
//split.addToSecondary(secondDiv);
//firstDiv.setId("first");
//secondDiv.setId("second");
//hl.add(firstDiv,secondDiv);
add(split);
//add(hl);
}
#Override
public void showRouterLayoutContent(HasElement element) {
if(element!=null && element.getClass().getName().contains("FirstView")) {
split.addToPrimary((Component) element);
//firstDiv.removeAll();
//firstDiv.add((Component) element);
//firstDiv.removeAll();
//firstDiv.getElement().appendChild(new Element[]{element.getElement()});
}
else if(element!=null && element.getClass().getName().contains("SecondView") ) {
secondDiv.removeAll();
secondDiv.add((Component) element);
split.addToSecondary((Component) element);
//split.addToSecondary(element.getElement().getComponent().get());
}
}
While these are the two views added to the split:
#Route(value="v1",layout=MainView.class)
public class FirstView extends VerticalLayout implements RouterLayout {
public FirstView() {
add(new Label("First View"));
}
}
#Route(value = "v2",layout=MainView.class)
public class SecondView extends VerticalLayout implements RouterLayout {
public SecondView() {
IFrame frame = new IFrame();
frame.setSrc("https://www.youtube.com/watch?v=LoigVtPCYPk&list=RDLoigVtPCYPk&start_radio=1");
add(frame);
}
}
Your comment does indeed seem to be the issue.
I recommend creating a Div wrapper for the primary content, and instead changing the content of that.
private final Div wrapper;
public MyLayout() {
wrapper = new Div();
wrapper.setSizeFull();
split.addToPrimary(wrapper);
}
private void setLayoutContent(com.vaadin.flow.component.Component content) {
wrapper.removeAll();
wrapper.add(content);
}
You might also want to do the same for the secondary. In addition, to prevent any components from being automatically removed when navigating, you can override removeRouterLayoutContent as well (available in Vaadin 14)
#Override
public void removeRouterLayoutContent(HasElement oldContent) {
// Do nothing, we remove manually in showRouterLayoutContent
}
Edit
If you can't override removeRouterLayoutContent, you can try creating your own instance of the HasElement to add. This is a bit of a hack, but might be the simplest solution.
public void showRouterLayoutContent(HasElement content) {
if (content.getClass().getSimpleName().contains("TestView")) {
// Creating a new instance should stop it from being auto removed
content = new TestView();
firstDiv.add((Component) content);
}
...
}

OptionGroup addValueChangeListener CheckBox

Vaadin 7.6.5.
I am trying to figure out why a value change listener fails in the below case?
The CheckBox is observing for addValueChangeListener OptionGroup.
#Theme("vaadindemo")
public class VaadindemoUI extends UI {
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = VaadindemoUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
OptionGroup group = new OptionGroup();
group.addItem("01");
group.setItemCaption("01", "ONE");
group.addItem("02");
group.setItemCaption("02", "TWO");
group.addItem("03");
group.setItemCaption("03", "THREE");
group.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
System.out.println("group getValue " + event.getProperty().getValue());
System.out.println("group getType " + event.getProperty().getType());
}
});
CheckBox box = new CheckBox();
box.setCaption("Check Me");
//Notify CheckBox of value change of radio button
//group.addValueChangeListener(box); -- // Code Fails
//box.addValueChangeListener(group); // Selected Radio Button is removed
TextField field = new TextField();
field.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
System.out.println("field getValue " + event.getProperty().getValue());
System.out.println("field getType " + event.getProperty().getType());
System.out.println("field getType " + field.getValue());
}
});
group.addValueChangeListener(field);// The value is reflected. How do i get only the event without over writing the value
//group.addValueChangeListener(box);
layout.addComponent(group);
layout.addComponent(box);
layout.addComponent(field);
Button button = new Button("Click Me");
button.addClickListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
layout.addComponent(new Label("Thank you for clicking"));
}
});
layout.addComponent(button);
}
}
It logs:
com.vaadin.data.util.converter.Converter$ConversionException: Unable to convert value of type java.lang.String to presentation type class java.lang.Boolean. No converter is set and the types are not compatible.
at com.vaadin.data.util.converter.ConverterUtil.convertFromModel(ConverterUtil.java:116)
at com.vaadin.ui.AbstractField.convertFromModel(AbstractField.java:736)
at com.vaadin.ui.AbstractField.convertFromModel(AbstractField.java:721)
at ...
I think you want something like...
group.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
//Do something with box here? e.g...
box.setValue(!box.getValue());
}
});
rather than...
group.addValueChangeListener(box);

Vaadin, Switching Content via setContent() not displaying

Vaadin 7.6.2
Take the following example:
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
public class MyClass extends Panel {
TextField myField = new TextField();
HorizontalLayout hLayout = new HorizontalLayout( myField );
VerticalLayout vLayout = new VerticalLayout( myField );
Button button = new Button( "Press Me" );
public MyClass() {
super();
applySettings();
}
private void applySettings() {
button.addClickListener(new ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
setContent( hLayout );
}
});
vLayout.addComponent( button );
this.setContent( vLayout );
}
}
When I click the button the vLayout disappears but the hLayout (with myField) doesn't appear. What step am I'm missing? Or, is there a different way to do this?
If I add a secondary text field, like so:
TextField myField = new TextField();
TextField myField2 = new TextField(); // tf2
HorizontalLayout hLayout = new HorizontalLayout( myField );
VerticalLayout vLayout = new VerticalLayout( myField2 ); // tf2
It appears to work, however what I'm trying to achieve is the ability to dynamically switch my layouts using the fields (and their data) from the switched-out layout.
One component can not have 2 parents at the same time (hLayout & vLayout in your case), thus if it already has one, Vaadin will remove it from the previous parent and add it as a child to the current one. This is the addComponent method inherited from AbstractComponentContainer:
/**
* This only implements the events and component parent calls. The extending
* classes must implement component list maintenance and call this method
* after component list maintenance.
*
* #see com.vaadin.ui.ComponentContainer#addComponent(Component)
*/
#Override
public void addComponent(Component c) {
// Make sure we're not adding the component inside it's own content
if (isOrHasAncestor(c)) {
throw new IllegalArgumentException(
"Component cannot be added inside it's own content");
}
if (c.getParent() != null) {
// If the component already has a parent, try to remove it
AbstractSingleComponentContainer.removeFromParent(c);
}
c.setParent(this);
fireComponentAttachEvent(c);
markAsDirty();
}
If you're in debug mode, you can somewhat see an image of the composition tree in your browser by adding ?debug to your URL, something like http://localhost:8080/?debug
Well, it looks like I may have solved it.
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
public class MyClass extends Panel {
TextField myField = new TextField();
HorizontalLayout hLayout = new HorizontalLayout();
VerticalLayout vLayout = new VerticalLayout();
Button button = new Button( "Press Me" );
public MyClass() {
super();
applySettings();
}
private void applySettings() {
button.addClickListener(new ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
vLayout.removeAllComponents(); // this is optional
hLayout.addComponent( myField );
setContent( hLayout );
}
});
vLayout.addComponents( myField, button );
this.setContent( vLayout );
}
}
If I don't add myField in the layout constructors, but add it later in the code, it seems to work.
UPDATE
After more testing it seems whichever layout LAST called addComponent() for a given field, THAT layout gets the handle to that field. This behavior seems odd and if someone could explain why this is? That would be enlightening.

Disable ValueChangeListener

I'm new of vaadin and now I'm running in the following trouble. I have a TabSheet that contains a few tabs.
Now my Tax Code component setted in immediate mode and others it has a the following ValueChangeLister
TextField taxCode = new TextField();
taxCode.setImmediate(true);
taxCode.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
if(taxCode.isModified()){
searchByTaxCode(taxCode.getValue());
}
}
});
then each time that the user changes the value to taxCode component, the method searchByTaxCode is invoked. This happens also when the user switch from Tab2 to Tab1 and I don't want this?
How can I fix this problem?
This simple test app does not fire the ValueChangeEvent when switching tabs, only when actually changing the value in the TextField. You have something more that could influence the behaviour?
public class TabSheetEventsUI extends UI {
#WebServlet(value = "/tabsheetevents/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = TabSheetEventsUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
TabSheet tabSheet = new TabSheet();
Panel tab1 = new Panel("Tab 1");
Panel tab2 = new Panel("Tab 2");
tabSheet.addTab(tab1);
tabSheet.addTab(tab2);
final TextField tab1tf = new TextField("tab1tf");
tab1tf.setImmediate(true);
tab1tf.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
System.out.println("Value change event catched.");
}
});
tab1.setContent(tab1tf);
setContent(tabSheet);
}
}

Webview with contenteditable cannot be focused programmatically

Trying to do a requestFocus() on the WebView does not work until the user has first clicked on the control.
I know this must be possible as htmlEditor can be focused this way (and I suspect it is based on a contenteditable WebView).
I am coding my own specialized htmlEditor using a webview with "contenteditable" and I would really like to be able to focus it like I can do with the standard htmlEditor.
I believe this must be an issue with Javafx and I have already submitted it to Jira, but I wonder if anyone can think of a work-around for this.
UPDATE: Issue number in jira: RT-21695
Short demostration code:
/* Demo webview */
public class WebViewConteneditableDemo extends Application {
String initialEditview = "<html><head>"
+ "</head><body contenteditable='true'>"
+"</body></html>";
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Webview focus demo");
final WebView editor = new WebView();
Button btn = new Button();
btn.setText("Test Webview focus");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
editor.requestFocus();
}
});
BorderPane root = new BorderPane();
root.setTop(btn);
root.setCenter(editor);
editor.getEngine().loadContent(initialEditview);
primaryStage.setScene(new Scene(root, 500, 450));
primaryStage.show();
}
}
The requestFocus api is just a request for focus, it does not guarantee focus.
Sometimes the internal implementation of other JavaFX controls request focus before or after you have requested focus which ends up in your requestFocus call not having any effect.
Often you can make the requestFocus call take effect by either wrapping it in a Platform.runLater or using a Timeline with a KeyFrame which invokes requestFocus after a delay.
If neither of those work, then there is likely a bug in the requestFocus processing for WebView which the JavaFX team can address in the context of the jira you filed.
Update
The specific issue in the sample code in the question was that, although the WebView was focused, the editable content element in the WebView was not focused.
I tried loading just the html from the sample code <html><head></head><body contenteditable='true'></body></html> into firefox and it behaved exactly the same as the JavaFX WebView (i.e. the editable content element was not focused until it was clicked on). So I don't believe this is an issue with WebView.
To get the editable element focused, you need to execute some script when you want it focused, for example in the javascript onload hook <body onLoad='document.body.focus();' contenteditable='true'/>
Here is an executable sample application which demonstrates programmatic control of focus behaviour of contenteditable elements in a JavaFX WebView:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewEditable extends Application {
String content = "<body bgcolor='cornsilk' onLoad='document.body.focus();' contenteditable='true'/>";
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final WebView editor = new WebView();
editor.getEngine().loadContent(content);
Button webviewFocusButton = new Button("Focus on WebView");
webviewFocusButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
editor.getEngine().executeScript("document.body.focus()");
editor.requestFocus();
}
});
Button selfFocusButton = new Button("Focus on this Button");
Label focusLabel = new Label();
focusLabel.textProperty().bind(Bindings
.when(editor.focusedProperty())
.then("WebView has the focus.")
.otherwise("WebView does not have the focus.")
);
focusLabel.setMaxWidth(Double.MAX_VALUE);
focusLabel.setStyle("-fx-background-color: coral; -fx-padding: 5;");
BorderPane layout = new BorderPane();
layout.setTop(HBoxBuilder.create().spacing(10).children(webviewFocusButton, selfFocusButton).style("-fx-padding: 10; -fx-background-color: palegreen").build());
layout.setCenter(editor);
layout.setBottom(focusLabel);
stage.setScene(new Scene(layout));
stage.show();
}
}
you can focus the webview of the HTMLEditor this way:
import com.sun.javafx.scene.web.skin.HTMLEditorSkin;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.web.HTMLEditor;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class FocusTest extends Application {
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
final HTMLEditor editor = new HTMLEditor();
final WebView editorView = (WebView) editor.lookup(".web-view");
primaryStage.setScene(new Scene(editor));
primaryStage.sizeToScene();
primaryStage.show();
Platform.runLater(() -> {
view.fireEvent(new MouseEvent(MouseEvent.MOUSE_PRESSED, 100, 100, 200, 200, MouseButton.PRIMARY, 1, false, false, false, false, false, false, false, false, false, false, null));
editor.requestFocus();
view.fireEvent(new MouseEvent(MouseEvent.MOUSE_RELEASED, 100, 100, 200, 200, MouseButton.PRIMARY, 1, false, false, false, false, false, false, false, false, false, false, null));
});
}
}

Resources