--- /dev/null
+[[dynamically-injecting-css]]
+Dynamically injecting CSS
+-------------------------
+
+In most cases you will style your components using SASS or CSS and
+create a theme for the application which you include with the `@Theme`
+annotation. This is always the preferred way of theming your
+application. But in some cases this is not enough. Sometimes you will
+want your user to be able to change some property on-the-fly without
+providing pre-made class names. To do this you can use CSS style
+injection. In this example I am going to show you how you can use CSS
+injection to create an editor which you can use to modify text visually
+with, a WYSIWYG of sorts. Here is an image of the final component I am
+going to create:
+
+image:img/theme-editor.png[Theme editor]
+
+First lets start by defining the UI of the editor component, it looks
+like this:
+
+[source,java]
+....
+private Component createEditor(String text) {
+ Panel editor = new Panel("Text Editor");
+ editor.setWidth("580px");
+ VerticalLayout panelContent = new VerticalLayout();
+ panelContent.setSpacing(true);
+ panelContent.setMargin(new MarginInfo(true, false, false, false));
+ editor.setContent(panelContent);
+ // Create the toolbar
+ HorizontalLayout toolbar = new HorizontalLayout();
+ toolbar.setSpacing(true);
+ toolbar.setMargin(new MarginInfo(false, false, false, true));
+ // Create the font family selector
+ toolbar.addComponent(createFontSelect());
+ // Create the font size selector
+ toolbar.addComponent(createFontSizeSelect());
+ // Create the text color selector
+ toolbar.addComponent(createTextColorSelect());
+ // Create the background color selector
+ toolbar.addComponent(createBackgroundColorSelect());
+ panelContent.addComponent(toolbar);
+ panelContent.setComponentAlignment(toolbar, Alignment.MIDDLE_LEFT);
+ // Spacer between toolbar and text
+ panelContent.addComponent(new Label("<hr />", ContentMode.HTML));
+ // The text to edit
+ TextArea textLabel = new TextArea(null, text);
+ textLabel.setWidth("100%");
+ textLabel.setHeight("200px");
+ // IMPORTANT: We are here setting the style name of the label, we are going to use this in our injected styles to target the label
+ textLabel.setStyleName("text-label");
+ panelContent.addComponent(textLabel);
+ return editor;
+}
+....
+
+Basically the editor component is a Panel with a text area and some
+buttons which you can use to modify the text area text with. The
+important thing here is that we give the text area a style name
+"text-label". With this style name we will be able to inject CSS styles
+targeted at that text area and modify colors and fonts of it. Lets next
+take a look at how the controls in the toolbar is implemented. They are
+all pretty similar but lets first take a look at how the Font selector
+was made:
+
+[source,java]
+....
+private Component createFontSelect() {
+ final ComboBox select = new ComboBox(null,
+ Arrays.asList("Arial", "Helvetica", "Verdana", "Courier", "Times", "sans-serif"));
+ select.setValue("Arial");
+ select.setWidth("200px");
+ select.setInputPrompt("Font");
+ select.setDescription("Font");
+ select.setImmediate(true);
+ select.setNullSelectionAllowed(false);
+ select.setNewItemsAllowed(false);
+ select.addValueChangeListener(new ValueChangeListener() {
+ @Override
+ public void valueChange( ValueChangeEvent event ) {
+ // Get the new font family
+ String fontFamily = select.getValue().toString();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+ styles.add(".v-app .v-textarea.text-label { font-family:" + fontFamily + "; }");
+ }
+ });
+ return select;
+}
+....
+
+The important part here is what is inside the `ValueChangeListener`. Once
+we get the value from the ComboBox we are ready to inject it to the page
+so the user can visually see what have changed. To do this we fetch the
+StyleSheet for the current Page by calling `Page.getCurrent()`. Once we
+have the current Page we can get its StyleSheet by calling
+`Page.getstyleSheet()`. Once we got the StyleSheet we are free to inject
+any CSS string into the page by using `StyleSheet.inject(String css)`. As
+you see here we use the style name we gave to the TextArea as the
+selector and apply the font-family attribute to change the font family
+to the one the user has selected. For the sake of clarity, lets look at
+how another one, the text color selector, was implemented:
+
+[source,java]
+....
+private Component createTextColorSelect( ) {
+ // Colorpicker for changing text color
+ ColorPicker textColor = new ColorPicker("Color", Color.BLACK);
+ textColor.setWidth("110px");
+ textColor.setCaption("Color");
+ textColor.addColorChangeListener(new ColorChangeListener() {
+ @Override
+ public void colorChanged( ColorChangeEvent event ) {
+ // Get the new text color
+ Color color = event.getColor();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new color as a style
+ styles.add(".v-app .v-textarea.text-label { color:" + color.getCSS() + "; }");
+ }
+ });
+ return textColor;
+}
+....
+
+Again, the important part in this method is in the `ColorChangeListener`.
+Basically here we do the exactly same thing as we did with the font
+family except here we are dealing with a color. To change the color of
+the text we just simply apply the 'color' attribute for text area. The
+`ColorPicker.Color` even provides us with a convenient method of directly
+converting the received Color object into a CSS color. And finally, for
+completeness, here is the full example code which will produce the demo
+application in the picture above for you to try out:
+
+[source,java]
+....
+/**
+ * Imports and package definition omitted
+ */
+public class CSSInjectWithColorpicker extends UI {
+ @Override
+ protected void init( VaadinRequest request ) { // Create a text editor
+ Component editor =
+ createEditor("Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+ + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+ + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+ + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+ + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+ + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+ + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+ + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+ + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+ + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+ + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+ + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+ + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+ + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+ + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."
+ + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a "
+ + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi "
+ + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. "
+ + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis "
+ + "quam, ac urna eros est cras id cras, eleifend eu mattis nec.");
+ VerticalLayout content = new VerticalLayout(editor);
+ content.setMargin(true);
+ setContent(content);
+ }
+
+ /**
+ * Creates a text editor for visually editing text
+ *
+ * @param text The text editor
+ * @return
+ */
+ private Component createEditor( String text ) {
+ Panel editor = new Panel("Text Editor");
+ editor.setWidth("580px");
+ VerticalLayout panelContent = new VerticalLayout();
+ panelContent.setSpacing(true);
+ panelContent.setMargin(new MarginInfo(true, false, false, false));
+ editor.setContent(panelContent);
+ // Create the toolbar
+ HorizontalLayout toolbar = new HorizontalLayout();
+ toolbar.setSpacing(true);
+ toolbar.setMargin(new MarginInfo(false, false, false, true));
+ // Create the font family selector
+ toolbar.addComponent(createFontSelect());
+ // Create the font size selector
+ toolbar.addComponent(createFontSizeSelect());
+ // Create the text color selector
+ toolbar.addComponent(createTextColorSelect());
+ // Create the background color selector
+ toolbar.addComponent(createBackgroundColorSelect());
+ panelContent.addComponent(toolbar);
+ panelContent.setComponentAlignment(toolbar, Alignment.MIDDLE_LEFT);
+ // Spacer between toolbar and text
+ panelContent.addComponent(new Label("<hr />", ContentMode.HTML));
+ // The text to edit
+ TextArea textLabel = new TextArea(null, text);
+ textLabel.setWidth("100%");
+ textLabel.setHeight("200px");
+ // IMPORTANT: We are here setting the style name of the label, we are going to use this in our injected styles to
+ // target the label
+ textLabel.setStyleName("text-label");
+ panelContent.addComponent(textLabel);
+ return editor;
+ }
+
+ /**
+ * Creates a background color select dialog
+ */
+ private Component createBackgroundColorSelect( ) {
+ ColorPicker bgColor = new ColorPicker("Background", Color.WHITE);
+ bgColor.setWidth("110px");
+ bgColor.setCaption("Background");
+ bgColor.addColorChangeListener(new ColorChangeListener() {
+ @Override
+ public void colorChanged( ColorChangeEvent event ) {
+ // Get the new background color
+ Color color = event.getColor();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new background color
+ styles.add(".v-app .v-textarea.text-label { background-color:" + color.getCSS() + "; }");
+ }
+ });
+ return bgColor;
+ }
+
+ /**
+ * Create a text color selection dialog
+ */
+ private Component createTextColorSelect( ) {
+ // Colorpicker for changing text color
+ ColorPicker textColor = new ColorPicker("Color", Color.BLACK);
+ textColor.setWidth("110px");
+ textColor.setCaption("Color");
+ textColor.addColorChangeListener(new ColorChangeListener() {
+
+ @Override
+ public void colorChanged( ColorChangeEvent event ) {
+ // Get the new text color
+ Color color = event.getColor();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new color as a style
+ styles.add(".v-app .v-textarea.text-label { color:" + color.getCSS() + "; }");
+ }
+ });
+ return textColor;
+ }
+
+ /**
+ * Creates a font family selection dialog
+ */
+ private Component createFontSelect( ) {
+ final ComboBox select =
+ new ComboBox(null, Arrays.asList("Arial", "Helvetica", "Verdana", "Courier", "Times", "sans-serif"));
+ select.setValue("Arial");
+ select.setWidth("200px");
+ select.setInputPrompt("Font");
+ select.setDescription("Font");
+ select.setImmediate(true);
+ select.setNullSelectionAllowed(false);
+ select.setNewItemsAllowed(false);
+ select.addValueChangeListener(new ValueChangeListener() {
+ @Override
+ public void valueChange( ValueChangeEvent event ) {
+ // Get the new font family
+ String fontFamily = select.getValue().toString();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+ styles.add(".v-app .v-textarea.text-label { font-family:" + fontFamily + "; }");
+ }
+ });
+ return select;
+ }
+
+ /**
+ * Creates a font size selection control
+ */
+ private Component createFontSizeSelect( ) {
+ final ComboBox select = new ComboBox(null, Arrays.asList(8, 9, 10, 12, 14, 16, 20, 25, 30, 40, 50));
+ select.setWidth("100px");
+ select.setValue(12);
+ select.setInputPrompt("Font size");
+ select.setDescription("Font size");
+ select.setImmediate(true);
+ select.setNullSelectionAllowed(false);
+ select.setNewItemsAllowed(false);
+ select.addValueChangeListener(new ValueChangeListener() {
+ @Override
+ public void valueChange( ValueChangeEvent event ) {
+ // Get the new font size
+ Integer fontSize = (Integer) select.getValue();
+ // Get the stylesheet of the page
+ Styles styles = Page.getCurrent().getStyles();
+ // inject the new font size as a style. We need .v-app to override Vaadin's default styles here
+ styles.add(".v-app .v-textarea.text-label { font-size:" + String.valueOf(fontSize) + "px; }");
+ }
+ });
+ return select;
+ }
+}
+....