1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.dialog.formdialog;
35
36 import static com.vaadin.server.Sizeable.Unit.PERCENTAGE;
37 import static java.util.stream.Collectors.toList;
38 import static java.util.stream.StreamSupport.stream;
39
40 import info.magnolia.context.Context;
41 import info.magnolia.i18nsystem.SimpleTranslator;
42 import info.magnolia.ui.dialog.ResurfaceDialogViewImpl;
43 import info.magnolia.ui.vaadin.extension.FocusFieldCaption;
44 import info.magnolia.ui.vaadin.form.FormSection;
45 import info.magnolia.ui.vaadin.form.FormViewReduced;
46 import info.magnolia.ui.vaadin.form.field.FieldLayout;
47
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Locale;
53 import java.util.Optional;
54 import java.util.function.Supplier;
55 import java.util.stream.Stream;
56
57 import javax.inject.Inject;
58
59 import com.vaadin.server.ErrorMessage;
60 import com.vaadin.server.Sizeable;
61 import com.vaadin.ui.AbstractComponent;
62 import com.vaadin.ui.ComboBox;
63 import com.vaadin.ui.Component;
64 import com.vaadin.ui.ComponentContainer;
65 import com.vaadin.ui.FormLayout;
66 import com.vaadin.ui.TabSheet;
67 import com.vaadin.ui.themes.ValoTheme;
68 import com.vaadin.v7.data.Item;
69 import com.vaadin.v7.data.Validatable;
70 import com.vaadin.v7.ui.AbstractField;
71 import com.vaadin.v7.ui.Field;
72
73
74
75
76 public class ResurfaceFormViewImpl extends ResurfaceDialogViewImpl implements FormView {
77
78 private final SimpleTranslator i18n;
79 private final Supplier<Locale> userLanguage;
80
81 private Item itemDatasource;
82
83 private String title;
84 private TabSheet tabSheet = new TabSheet();
85 private ComboBox<Locale> languageSelector;
86 private FormViewReduced.Listener listener;
87 private boolean invalidFieldFocused;
88
89 @Inject
90 public ResurfaceFormViewImpl(SimpleTranslator i18n, Context context) {
91 this.i18n = i18n;
92 this.userLanguage = context::getLocale;
93
94 tabSheet.setSizeFull();
95 tabSheet.addStyleName("content");
96 tabSheet.addStyleName(ValoTheme.TABSHEET_FRAMED);
97
98 createLocaleSelector();
99 this.setContent(() -> tabSheet);
100 }
101
102 @Override
103 public void setCurrentLocale(Locale locale) {
104 this.languageSelector.setValue(locale);
105 }
106
107 @Override
108 public void setAvailableLocales(List<Locale> locales) {
109 if (locales != null && !locales.isEmpty()) {
110 languageSelector.clear();
111 languageSelector.setItems(locales);
112 getActionAreaView().setToolbarComponent(languageSelector);
113 }
114 }
115
116 @Override
117 public void setItemDataSource(Item newDataSource) {
118 this.itemDatasource = newDataSource;
119 }
120
121 @Override
122 public Item getItemDataSource() {
123 return this.itemDatasource;
124 }
125
126 @Override
127 public void setCaption(String caption) {
128 this.title = caption;
129 }
130
131 @Override
132 public String getTitle() {
133 return this.title;
134 }
135
136 @Override
137 public void addField(Field<?> field) {
138
139 }
140
141 @Override
142 public void addFormSection(String tabName, FormSection inputFields) {
143 final Component formLayout = new FormLayout(new ArrayList<>(inputFields.getComponents())
144 .stream()
145 .map(component -> createFieldLayout(component, inputFields.getComponentHelpDescription(component)))
146 .toArray(Component[]::new)
147 );
148 final TabSheet.Tab tab = this.tabSheet.addTab(formLayout, tabName);
149 tab.setId(inputFields.getName());
150 formLayout.setWidth(95, PERCENTAGE);
151 FocusFieldCaption.focusCaptionOf((FormLayout) formLayout);
152 }
153
154 private Component createFieldLayout(Component field, String componentHelpDescription) {
155 FieldLayout fieldLayout = FieldLayout.of(field, componentHelpDescription);
156 fieldLayout.setCaption(field.getCaption());
157 if (field instanceof AbstractField) {
158 fieldLayout.setRequiredIndicatorVisible(((AbstractField) field).isRequired());
159 }
160 return fieldLayout;
161 }
162
163 @Override
164 public void showValidation(boolean isVisible) {
165 invalidFieldFocused = false;
166 getComponents().forEach(field -> {
167 FieldLayout fieldLayout = null;
168 if (field instanceof FieldLayout) {
169 fieldLayout = (FieldLayout) field;
170 field = fieldLayout.getField();
171 }
172 if (field instanceof AbstractField) {
173 AbstractField abstractField = (AbstractField) field;
174 abstractField.setValidationVisible(isVisible);
175 if (fieldLayout != null) {
176 fieldLayout.getValidationStatusHandler().accept(
177 Optional.ofNullable(abstractField.getErrorMessage())
178 .map(ErrorMessage::getFormattedHtmlMessage)
179 .orElse("")
180 );
181 if (!invalidFieldFocused && !abstractField.isValid()) {
182 fieldLayout.focus();
183 invalidFieldFocused = true;
184 }
185 }
186 }
187 });
188 }
189
190 @Override
191 public void setShowAllEnabled(boolean enabled) {
192
193 }
194
195 @Override
196 public void setDescriptionVisibility(boolean isVisible) {
197
198 }
199
200 @Override
201 public boolean isValid() {
202 return getFields().stream().allMatch(Validatable::isValid);
203 }
204
205 @Override
206 public List<FormSection> getFormSections() {
207 return stream(tabSheet.spliterator(), false)
208 .map(component -> new FormSectionAdapter((ComponentContainer) component, tabSheet.getTab(component).getId()))
209 .collect(toList());
210 }
211
212 @Override
213 @Deprecated
214 public Collection<Field<?>> getFields() {
215 return getComponents()
216 .map(component -> component instanceof FieldLayout ? ((FieldLayout) component).getField() : component)
217 .filter(component -> component instanceof Field)
218 .map(component -> (Field<?>) component)
219 .collect(toList());
220 }
221
222 private Stream<Component> getComponents() {
223 return getFormSections().stream()
224 .flatMap(sections -> sections.getComponents().stream());
225 }
226
227 @Override
228 public void setListener(FormViewReduced.Listener listener) {
229 this.listener = listener;
230 }
231
232 @Override
233 public AbstractComponent asVaadinComponent() {
234
235
236
237
238
239 if (tabSheet.getComponentCount() == 1) {
240 tabSheet.addStyleName("single-tab");
241 } else {
242 tabSheet.removeStyleName("single-tab");
243 }
244
245 return tabSheet;
246 }
247
248 private void createLocaleSelector() {
249 languageSelector = new ComboBox<>();
250
251 languageSelector.setWidth(150, Sizeable.Unit.PIXELS);
252 languageSelector.setDescription(i18n.translate("languageSelector.description"));
253 languageSelector.setItemCaptionGenerator(locale -> {
254
255 Locale preferredLocale = userLanguage.get();
256 String label = locale.getDisplayLanguage(preferredLocale);
257 if (!locale.getDisplayCountry(preferredLocale).isEmpty()) {
258 label += " (" + locale.getDisplayCountry(preferredLocale) + ")";
259 }
260 return label;
261 });
262 languageSelector.setEmptySelectionAllowed(false);
263 languageSelector.setTextInputAllowed(false);
264 languageSelector.addValueChangeListener(event -> {
265 if (this.listener != null) {
266 this.listener.localeChanged(event.getValue());
267 }
268 });
269 }
270
271 void updateFormView(List<FormSection> sections) {
272 tabSheet.removeAllComponents();
273 sections.forEach(section -> addFormSection(section.getName(), section));
274 }
275
276 private final class FormSectionAdapter extends FormSection {
277
278 private final ComponentContainer layout;
279
280 FormSectionAdapter(ComponentContainer layout, String name) {
281 this.layout = layout;
282 setName(name);
283 }
284
285 @Override
286 public void setComponentHelpDescription(Component c, String description) {
287 getState().helpDescriptions.put(c, description);
288 }
289
290 @Override
291 public String getComponentHelpDescription(Component c) {
292 return getState().helpDescriptions.get(c);
293 }
294
295 @Override
296 public void addComponent(Component c) {
297 accessTabLayout().addComponent(c);
298 }
299
300 ComponentContainer accessTabLayout() {
301 return layout;
302 }
303
304 @Override
305 public void removeAllComponents() {
306 accessTabLayout().removeAllComponents();
307 }
308
309 @Override
310 public void removeComponent(Component c) {
311 accessTabLayout().removeComponent(c);
312 }
313
314 @Override
315 public Iterator<Component> iterator() {
316 return accessTabLayout().iterator();
317 }
318
319 @Override
320 public int getComponentCount() {
321 return accessTabLayout().getComponentCount();
322 }
323
324 @Override
325 public List<Component> getComponents() {
326 return stream(accessTabLayout().spliterator(), false).collect(toList());
327 }
328 }
329 }