Setting width of VerticalLayout in Vaadin Designer is IGNORED if object is referenced in corresponding java code - vaadin

If one creates a vertical layout class using Vaadin Designer (in Vaadin 14) and sets its width to some fixed value (eg 3%) and one has a reference to that vertical layout component in the corresponding java code, Vaadin will IGNORE the width setting. Curiously, this bug dissappears if one does NOT reference the vertical layout class in the java code. Here's a working test case to illustrate the bug. For now, my "hack" has been to avoid referencing the object in my java code, or if I must, then I think I need to explicitly reset the width logic in the java code itself. Any thoughts?
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import '#vaadin/vaadin-ordered-layout/src/vaadin-horizontal-layout.js';
class UiTestview extends PolymerElement {
static get template() {
return html`
<style include="shared-styles">
:host {
display: block;
height: 100%;
}
</style>
<vaadin-horizontal-layout class="content" style="width: 100%; height: 100%;">
<vaadin-vertical-layout id="left" style="width: 3%;background-color:blue">
<label>left label</label>
</vaadin-vertical-layout>
<label style="flex-grow: 1;background-color:red">Label</label>
</vaadin-horizontal-layout>
`;
}
static get is() {
return 'ui-testview';
}
static get properties() {
return {
// Declare your properties here.
};
}
}
customElements.define(UiTestview.is, UiTestview);
and the corresponding java code:
package org.vaadin;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.template.Id;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.templatemodel.TemplateModel;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
/**
* A Designer generated component for the ui-testview template.
*
* Designer will add and remove fields with #Id mappings but
* does not overwrite or otherwise change this file.
*/
#Route("testview")
#Tag("ui-testview")
#JsModule("./src/ui-testview.js")
public class UiTestview extends PolymerTemplate<UiTestview.UiTestviewModel> {
#Id("left")
private VerticalLayout left;
/**
* Creates a new UiTestview.
*/
public UiTestview() {
}
/**
* This model binds properties between UiTestview and ui-testview
*/
public interface UiTestviewModel extends TemplateModel {
// Add setters and getters for template properties here.
}
}

Basically the Java-side of Vaadin (at least in Vaadin 14) does not know about any initial client-side state from the template. That's why Java-getters will often not return values set in your template. Furthermore, VerticalLayout sets setWidth("100%") in its constructor which may actually override a width set in your template once you map it to your template. This is something that has been improved in Vaadin 18. I doubt this will be backported to Vaadin 14.

Related

Behavior is wrong when adding multiple Tab components to TaskCanvasTabs in Twilio Flex

I am developing a Twilio Flex plugin using Flex Ui version 2 (beta.1 and beta.2).
I wanted to add multiple tabs to TaskCanvasTabs and tried to write the following code.
import React from 'react';
import { Tab } from '#twilio/flex-ui';
import { FlexPlugin } from '#twilio/flex-plugin';
import SmsPanel from './components/SmsPanel/SmsPanel';
import IncomingVideo from './components/IncomingVideo/IncomingVideo';
const PLUGIN_NAME = 'SendSmsV2Plugin';
export default class SendSmsV2Plugin extends FlexPlugin {
constructor() {
super(PLUGIN_NAME);
}
/**
* This code is run when your plugin is being started
* Use this to modify any UI components or attach to the actions framework
*
* #param flex { typeof import('#twilio/flex-ui') }
*/
async init(flex, manager) {
const options = {
align: 'end',
};
flex.TaskCanvasTabs.Content.add(
<Tab label='SMS' key='sms-panel-tab-key'>
<SmsPanel key='sms-panel-component' />
</Tab>,
options,
);
flex.TaskCanvasTabs.Content.add(
<Tab label='Video' key='video-panel-tab-key'>
<IncomingVideo key='incoming-video-component' />
</Tab>,
options,
);
}
}
When executed, the first Tab (SMS) added will be duplicated as shown below. Thereafter, each time I select the first Tab, Call tab, etc., the first Tab added will be increased.
Can someone please tell me if the specification does not allow adding multiple tabs to TaskCanvasTabs or how to solve this problem?
I also tried with Flex Ui version 2 (beta.3) but the behavior is the same.
It seems that this issue only happens when you try to add multiple tabs on TaskCanvasTabs
To resolve this issue you could add a Tab Group which contains Tabs
flex.TaskCanvasTabs.Content.add(
<Flex.Tabs key="custom-tabs-group">
<Flex.Tab key="conversation-note-tab" label="Conversation Notes">
<ConversationNote key="conversation-note" />
</Flex.Tab>
<Flex.Tab key="label-to-conversation" label="Conversation Label">
<AttachLabelToConversation key="add-label-to-conversation" />
</Flex.Tab>
</Flex.Tabs>,
{}
);
Use this css to adjust the tab group text
/* Added CSS for merged TaskCanvasTabs into Tabs group start */
.Twilio-TaskCanvas .Twilio-TaskCanvas-default .Twilio-TaskCanvasTabs div[data-test="customer-tab-header"]:has(> div > button) button {
width: 100%;
height: 100%;
}
.Twilio-TaskCanvas .Twilio-TaskCanvas-default .Twilio-TaskCanvasTabs div[data-test="customer-tab-header"]:has(> div > button) button:hover {
background-color: unset;
}
.Twilio-TaskCanvas .Twilio-TaskCanvas-default .Twilio-TaskCanvasTabs div[data-test="customer-tab-header"]:has(> div > button) button div svg {
display: none;
}
.Twilio-TaskCanvas .Twilio-TaskCanvas-default .Twilio-TaskCanvasTabs div[data-test="customer-tab-header"]:has(> div > button) button div:after {
content: "Conversation Actions";
font-size: 14px;
font-weight: 600;
color: rgb(96, 107, 133);
padding-bottom: 6px;
}
/* Added CSS for merged TaskCanvasTabs into Tabs group end */
I heard the best solution.
TabPros is using the optional uniqueName prop instead of the key for identifying the selected tab.
So, I changed my code below. It was fixed.
flex.TaskCanvasTabs.Content.add(
<Tab label='SMS' key='sms-panel-tab-key' uniqueName='sms-panel-tab'>
<SmsPanel key='sms-panel-component' />
</Tab>,
options,
);
flex.TaskCanvasTabs.Content.add(
<Tab label='Video' key='video-panel-tab-key' uniqueName='video-panel-tab'>
<IncomingVideo key='incoming-video-component' />
</Tab>,
options,
);
The link referenced is below.
https://github.com/twilio/flex-plugin-builder/issues/327

Cannot get #CssImport to apply only to component scope

I have been wasted hours and days trying to style the Vaadin Upload component. The goal is simple:
sometimes I need the component to show the uploaded file using its built-in file list with allows delete.
other times, I want to hide this list because I have another component such as Grid to show the file details.
Now comes the problem, I can never get it to consistently work using #CssImport with themeFor=vaadin-upload and themeFor=vaadin-upload-file. Vaadin seems to compile the shadow dom and the final result varies, it mixes up the two options and whichever comes last gets applied.
I then thought maybe because the #CssImport is in the #Route component. So, I created two custom upload components that extended the vaadin-upload component with the difference being the different #CssImport (see below). That (frustratingly) still doesn't work. I inspect the document and found that the inside the shadow-dom contains both even though I never use both on the same page.
#CssImport(value = "./css/vaadin-upload-show.css", themeFor = "vaadin-upload")
public class UploadShowFiles extends Upload {
private static final long serialVersionUID = -9198630843136885092L;
public UploadShowFiles(Receiver receiver) {
super(receiver);
}
}
#CssImport(value = "./css/vaadin-upload-hidefile.css", themeFor = "vaadin-upload")
public class UploadHideFiles extends Upload {
private static final long serialVersionUID = 2344860066834705807L;
public UploadHideFiles(Receiver receiver) {
super(receiver);
setClassName("hide-upload");
}
}
The css below will appear in the shadow dom. I expect only display: none or otherwise, not both.
[name="file-list"] {
display: none !important;
height: 0px !important;
}
[name="file-list"] div[part="file-list"] {
display: none !important;
}
[name="file-list"] {
display: block !important;
height: 1.5rem !important;
}
[name="file-list"] div[part="file-list"] {
display: block !important;
}
p/s: This is my first experience using Vaadin in a project and I probably never going to use it again. Customizing anything in Vaadin is so time-consuming and painful.
Styling in the shadow DOM can indeed be tricky. The simplified theming in Vaadin 19 helps a bit.
Where the #CssImport annotation is placed affects if the CSS should be included in the document, but not which components it affects. With themeFor, it will always be applied to all matching components.
What you can do is to use the :host selector to limit which upload components it applies to. Here I am using a class-based approach:
:host(.no-file-list) [name="file-list"] {
display: none !important;
height: 0px !important;
}
:host(.no-file-list) [name="file-list"] div[part="file-list"] {
display: none !important;
}
I can then hide the file list in an upload component by adding a class:
#Route
#CssImport(value = "./styles/upload-style.css", themeFor = "vaadin-upload")
public class FileUploadTest extends VerticalLayout {
public FileUploadTest() {
Upload uploadWithFileList = new Upload();
Upload uploadWithoutFileList = new Upload();
uploadWithoutFileList.addClassName("no-file-list");
add(uploadWithFileList, uploadWithoutFileList);
}
}

Can CssLayout be used to achieve absolute positioning of components?

Currently I am using AbsoluteLayout, but because of some problems I would like to give CssLayout a try.
I would like to dynamically place image-, label- and button-components at specific positions in the layout.
AbsoluteLayout allows me to specify the position like so:
absoluteLayout.addComponent(component, "top:20px;left:20px")
Is something like this at all possible to achieve with CssLayout?
You can apply inline CSS for a component by overriding getCss on CssLayout, so the following should have the wanted result:
CssLayout cssLayout = new CssLayout() {
#Override
protected String getCss(Component component) {
// check the component here and return correct css. In this case only one component in the layout so this works..
return "position: relative; top: 10px; left: 10px";
}
};
cssLayout.setSizeFull();
cssLayout.addComponent(new Button("Hello"));

Get the width of custom element created using <polymer-element> in dart?

How do I get the width of a custom element from the element's script?
<polymer-element name="his-elem">
<div>
blah
</div>
</polymer-element>
#CustomTag('his-elem')
class blah extends PolymerElement {
#override
void attached() {
// how do I get the <his-elem>'s width (just width, no padding border stuff) here?
// document.querySelector('his-elem').getComputedStyle().width gives me "auto"...
}
}
Update
Add :host {display: block;} to his-element, then you can get the actual width via <his-elem>.clientWidth.
I use this when the other methods don't work:
this.getBoundingClientRect().width
See also https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect
**In Polymer <= 0.16
you need to call super.attached() previously
#override
void attached() {
super.attached();
print(this.getBoundingClientRect().width);
// or
print(this.getComputedStyle().width);
}
before the call to super.attached() the element isn't yet attached to the DOM and won't return a proper width. Actually with overriding attached and not calling super.attached() within the method you prevent attaching.
this represents the html element itself. So it should be this.style.width.

Transparent background in the WebView in JavaFX

I need to show my webview content over parent background pattern. Is there a straightforward way to do it?
This might be useful
final com.sun.webkit.WebPage webPage = com.sun.javafx.webkit.Accessor.getPageFor(engine);
webPage.setBackgroundColor(0);
Webview transparent I get a good solution here : https://javafx-jira.kenai.com/browse/RT-29186 read comments Harry Hur
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.w3c.dom.Document; `
public class TestTranparentApps extends Application {
#Override
public void start(Stage primaryStage) {
new WebPage(primaryStage);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class WebPage{
WebView webview;
WebEngine webengine;
public WebPage(Stage mainstage){
webview = new WebView();
webengine = webview.getEngine();
Scene scene = new Scene(webview);
scene.setFill(null);
mainstage.setScene(scene);
mainstage.initStyle(StageStyle.TRANSPARENT);
mainstage.setWidth(700);
mainstage.setHeight(100);
webengine.documentProperty().addListener(new DocListener());
webengine.loadContent("<body style='background : rgba(0,0,0,0);font-size: 70px;text-align:center;'>Test Transparent</body>");
}
class DocListener implements ChangeListener<Document>{
#Override
public void changed(ObservableValue<? extends Document> observable, Document oldValue, Document newValue) {
try {
// Use reflection to retrieve the WebEngine's private 'page' field.
Field f = webengine.getClass().getDeclaredField("page");
f.setAccessible(true);
com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(webengine);
page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
} catch (Exception e) {
}
}
}
}
}
If u have a bright background use
webView.setBlendMode(BlendMode.DARKEN);
and when dark background, then
webView.setBlendMode(BlendMode.LIGHTEN);
This will probably be fixed in some time, this feature is requested on the JavaFX bug tracker #RT-25004
You can Chroma Key your WebView over your content using Blend effects.
Update
I tried this out and implementing a true Chroma Key with the built-in Blend effects of JavaFX 2.2 in JavaFX is actually pretty difficult (and surpassed my capabilities of implementing). I managed to make the technique work with pre-chroma keyed flv video formats, but not with arbitrary nodes such as WebView.
Still, for now, you can achieve something somewhat similar in a simple way using the darken and lighten effects as martini suggests in his answer. It's not perfect, but will probably need to suffice until RT-25004 is implemented or the JavaFX platform provides a more comprehensive set of alpha compositing operations.
Since JavaFX 18, transparent WebView backgrounds are finally supported (see JDK-8090547):
WebEngine engine = webView.getEngine();
webView.setPageFill(Color.TRANSPARENT);
You need to set the Web Page Background to 0. But you might need to do some other stuff to not make it break on scrolling. The following GitHub Gist is a java agent that can be added to our app and that manipulates WebPages to be transparent. https://gist.github.com/riccardobl/18603f9de508b1ab6c9e or more up to date version https://github.com/FAForever/downlords-faf-client/blob/develop/webview-patch/src/main/java/com/faforever/client/webviewpatcher/TransparentWebViewPatch.java
Only solution that I found and was fully working.
I'm resurrecting this because I found a better solution for the problem of having a WebView over a gradient. Suppose you have a gradient like this and want to display an HTML string over it in white color that looks something like this:
<p>Integer semper, est imperdiet mattis porttitor, massa vulputate ipsum</p>
The trick is to convert your gradient to Base64 string:
iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC
Since WebView loads any HTML string, this can involve <body> that has a background. In my instance, the WebView was not occupying the whole space with the gradient, so I had to position the background as well. Following is the HTML I added to the HTML string above at the beggining:
<html>
<head>
</head>
<body style="margin: 0px; padding: 0px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC); background-repeat: repeat-x; background-position: 0 -152px;">
<span style="font-family: Arial; font-size: 12px; color: white; display: block; margin:0px;">
And at the end:
</span></body></html>
Therefore producing such code:
String desc = "<html><head></head><body style=\"margin: 0px; padding: 0px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC); background-repeat: repeat-x; background-position: 0 -152px;\"><span style=\"font-family: Arial; font-size: 12px; color: white; display: block; margin:0px;\">" + description + "</span></body></html>";
wv_desc.getEngine().loadContent(desc);
You could probably put the styles in the <head> or load them some other way for clarity I suppose, but this is a much better work-around to anything else I found on the net for this issue.

Resources