From d529d9867ff86f32b9d679fb532a24938e7adaff Mon Sep 17 00:00:00 2001 From: Erik Lumme Date: Wed, 13 Sep 2017 09:43:00 +0300 Subject: Migrate DynamicallyInjectingCSS --- .../articles/DynamicallyInjectingCSS.asciidoc | 307 +++++++++++++++++++++ documentation/articles/contents.asciidoc | 1 + documentation/articles/img/theme-editor.png | Bin 0 -> 23869 bytes 3 files changed, 308 insertions(+) create mode 100644 documentation/articles/DynamicallyInjectingCSS.asciidoc create mode 100644 documentation/articles/img/theme-editor.png diff --git a/documentation/articles/DynamicallyInjectingCSS.asciidoc b/documentation/articles/DynamicallyInjectingCSS.asciidoc new file mode 100644 index 0000000000..d379ec6c4d --- /dev/null +++ b/documentation/articles/DynamicallyInjectingCSS.asciidoc @@ -0,0 +1,307 @@ +[[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("
", 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("
", 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; + } +} +.... diff --git a/documentation/articles/contents.asciidoc b/documentation/articles/contents.asciidoc index 396e4dfc3b..60a555f848 100644 --- a/documentation/articles/contents.asciidoc +++ b/documentation/articles/contents.asciidoc @@ -60,4 +60,5 @@ are great, too. - link:LoadTestingWithGatling.asciidoc[Load testing with Gatling] - link:VaadinScalabilityTestingWithAmazonWebServices.asciidoc[Vaadin scalability testing with Amazon Web Services] - link:UsingFontIcons.asciidoc[Using font icons in Vaadin 7.2] +- link:DynamicallyInjectingCSS.asciidoc[Dynamically injecting CSS] - link:CreatingAUIExtension.asciidoc[Creating a UI extension] diff --git a/documentation/articles/img/theme-editor.png b/documentation/articles/img/theme-editor.png new file mode 100644 index 0000000000..6785823d4e Binary files /dev/null and b/documentation/articles/img/theme-editor.png differ -- cgit v1.2.3