PL-10022 Help icons for UI fields

This commit is contained in:
Gleb Gorelov 2017-12-01 19:59:20 +04:00
parent e4ce5365ee
commit e928e7f0cc
43 changed files with 1000 additions and 260 deletions

View File

@ -25,6 +25,7 @@ import com.haulmont.cuba.core.config.SourceType;
import com.haulmont.cuba.core.config.defaults.Default;
import com.haulmont.cuba.core.config.defaults.DefaultBoolean;
import com.haulmont.cuba.core.config.defaults.DefaultInt;
import com.haulmont.cuba.core.config.defaults.DefaultString;
import com.haulmont.cuba.core.config.type.Factory;
import com.haulmont.cuba.core.config.type.IntegerListTypeFactory;
@ -115,4 +116,9 @@ public interface DesktopConfig extends Config {
@Property("cuba.desktop.loginDialogDefaultPassword")
@Default("admin")
String getLoginDialogDefaultPassword();
@Property("cuba.desktop.showTooltipShortcut")
@Source(type = SourceType.DATABASE)
@DefaultString("F1")
String getShowTooltipShortcut();
}

View File

@ -44,6 +44,7 @@
<xs:element name="font" type="fontType"/>
<xs:element name="insets" type="insetsType"/>
<xs:element name="dimension" type="dimensionType"/>
<xs:element name="int" type="intType" maxOccurs="unbounded" minOccurs="0"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
@ -70,6 +71,11 @@
<xs:attribute type="xs:string" name="value" use="required"/>
</xs:complexType>
<xs:complexType name="intType">
<xs:attribute type="xs:string" name="property" use="required"/>
<xs:attribute type="xs:integer" name="value" use="required"/>
</xs:complexType>
<xs:complexType name="layoutSettingsType">
<xs:attribute type="xs:int" name="margin-size" use="optional"/>
<xs:attribute type="xs:int" name="spacing-size" use="optional"/>

View File

@ -21,6 +21,8 @@ import com.haulmont.cuba.desktop.sys.DesktopToolTipManager;
import com.haulmont.cuba.desktop.sys.layout.BoxLayoutAdapter;
import com.haulmont.cuba.desktop.sys.vcl.ToolTipButton;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Component.HasContextHelp;
import org.apache.commons.lang.StringUtils;
import javax.swing.*;
@ -37,20 +39,24 @@ public class ComponentCaption extends JPanel {
}
private void takeOwnerProperties() {
if (label == null) {
label = new JLabel();
add(label);
if (!(owner instanceof DesktopCheckBox)) {
if (label == null) {
label = new JLabel();
add(label);
}
label.setText(((Component.HasCaption) owner).getCaption());
}
label.setText(((Component.HasCaption) owner).getCaption());
if (((Component.HasCaption) owner).getDescription() != null) {
String contextHelpText = getContextHelpText();
if (StringUtils.isNotEmpty(contextHelpText)) {
if (toolTipButton == null) {
toolTipButton = new ToolTipButton();
toolTipButton.setFocusable(false);
DesktopToolTipManager.getInstance().registerTooltip(toolTipButton);
add(toolTipButton);
}
toolTipButton.setToolTipText(((Component.HasCaption) owner).getDescription());
toolTipButton.setToolTipText(contextHelpText);
} else if (toolTipButton != null) {
remove(toolTipButton);
toolTipButton = null;
@ -60,6 +66,15 @@ public class ComponentCaption extends JPanel {
setEnabled(owner.isEnabled());
}
protected String getContextHelpText() {
if (owner instanceof HasContextHelp) {
return DesktopComponentsHelper.getContextHelpText(
((HasContextHelp) owner).getContextHelpText(),
((HasContextHelp) owner).isContextHelpTextHtmlEnabled());
}
return null;
}
public void update() {
takeOwnerProperties();
}

View File

@ -21,6 +21,7 @@ import com.google.common.collect.Iterables;
import com.haulmont.bali.datastruct.Pair;
import com.haulmont.cuba.desktop.gui.data.DesktopContainerHelper;
import com.haulmont.cuba.desktop.sys.layout.BoxLayoutAdapter;
import com.haulmont.cuba.desktop.sys.layout.MigLayoutHelper;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Frame;
@ -74,37 +75,31 @@ public abstract class DesktopAbstractBox
remove(component);
}
int implIndex = getActualIndex(index);
// add caption first
ComponentCaption caption = null;
boolean haveDescription = false;
if (DesktopContainerHelper.hasExternalCaption(component)) {
caption = new ComponentCaption(component);
captions.put(component, caption);
impl.add(caption, layoutAdapter.getCaptionConstraints(component), implIndex); // CAUTION this dramatically wrong
implIndex++;
} else if (DesktopContainerHelper.hasExternalDescription(component)) {
caption = new ComponentCaption(component);
captions.put(component, caption);
haveDescription = true;
}
JComponent composition = DesktopComponentsHelper.getComposition(component);
//if component have description without caption, we need to wrap
// component to view Description button horizontally after component
if (haveDescription) {
boolean hasExternalCaption = DesktopContainerHelper.hasExternalCaption(component);
if (hasExternalCaption
|| DesktopContainerHelper.hasExternalContextHelp(component)) {
ComponentCaption caption = new ComponentCaption(component);
captions.put(component, caption);
JPanel wrapper = new LayoutSlot();
BoxLayoutAdapter adapter = BoxLayoutAdapter.create(wrapper);
adapter.setExpandLayout(true);
adapter.setSpacing(false);
adapter.setMargin(false);
wrapper.add(composition);
wrapper.add(caption,new CC().alignY("top"));
impl.add(wrapper, layoutAdapter.getConstraints(component), implIndex);
if (hasExternalCaption) {
adapter.setFlowDirection(BoxLayoutAdapter.FlowDirection.Y);
wrapper.add(caption, 0);
} else {
wrapper.add(caption, new CC().alignY("top"));
}
impl.add(wrapper, layoutAdapter.getConstraints(component), index);
wrappers.put(component, new Pair<>(wrapper, adapter));
} else {
impl.add(composition, layoutAdapter.getConstraints(component), implIndex);
impl.add(composition, layoutAdapter.getConstraints(component), index);
}
if (component.getId() != null) {
@ -152,16 +147,6 @@ public abstract class DesktopAbstractBox
frame.registerComponent(component);
}
protected int getActualIndex(int originalIndex) {
int index = originalIndex;
Object[] components = ownComponents.toArray();
for (int i = 0; i < originalIndex; i++) {
if (DesktopContainerHelper.hasExternalCaption((Component)components[i]))
index++;
}
return index;
}
@Override
public void remove(Component component) {
JComponent composition = DesktopComponentsHelper.getComposition(component);
@ -269,15 +254,16 @@ public abstract class DesktopAbstractBox
protected void updateComponentInternal(Component child) {
boolean componentReAdded = false;
if (DesktopContainerHelper.mayHaveExternalCaption(child)) {
if (DesktopContainerHelper.mayHaveExternalCaption(child)
|| DesktopContainerHelper.mayHaveExternalContextHelp(child)) {
if (captions.containsKey(child)
&& !DesktopContainerHelper.hasExternalCaption(child)
&& !DesktopContainerHelper.hasExternalDescription(child)) {
&& !DesktopContainerHelper.hasExternalContextHelp(child)) {
reAddChild(child);
componentReAdded = true;
} else if (!captions.containsKey(child)
&& (DesktopContainerHelper.hasExternalCaption(child)
|| DesktopContainerHelper.hasExternalDescription(child))) {
|| DesktopContainerHelper.hasExternalContextHelp(child))) {
reAddChild(child);
componentReAdded = true;
} else if (captions.containsKey(child)) {
@ -295,6 +281,19 @@ public abstract class DesktopAbstractBox
JComponent composition;
if (wrappers.containsKey(child)) {
composition = wrappers.get(child).getFirst();
CC constraints = MigLayoutHelper.getConstraints(child);
if (child.getHeight() == -1.0) {
MigLayoutHelper.applyHeight(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyHeight(constraints, 100, UNITS_PERCENTAGE, false);
}
if (child.getWidth() == -1.0) {
MigLayoutHelper.applyWidth(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyWidth(constraints, 100, UNITS_PERCENTAGE, false);
}
BoxLayoutAdapter adapter = wrappers.get(child).getSecond();
adapter.updateConstraints(DesktopComponentsHelper.getComposition(child), constraints);
} else {
composition = DesktopComponentsHelper.getComposition(child);
}

View File

@ -34,6 +34,7 @@ import com.haulmont.cuba.gui.components.compatibility.ComponentValueListenerWrap
import com.haulmont.cuba.gui.components.validators.BeanValidator;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.ValueListener;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import javax.swing.*;
@ -52,6 +53,9 @@ public abstract class DesktopAbstractField<C extends JComponent> extends Desktop
protected boolean required;
protected String requiredMessage;
protected String contextHelpText;
protected Boolean contextHelpTextHtmlEnable = false;
protected Set<Validator> validators = new LinkedHashSet<>();
// todo move nimbus constants to theme
@ -290,4 +294,32 @@ public abstract class DesktopAbstractField<C extends JComponent> extends Desktop
}
}
}
@Override
public String getContextHelpText() {
return contextHelpText;
}
@Override
public void setContextHelpText(String contextHelpText) {
if (!ObjectUtils.equals(this.contextHelpText, contextHelpText)) {
this.contextHelpText = contextHelpText;
requestContainerUpdate();
}
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return contextHelpTextHtmlEnable;
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
if (!ObjectUtils.equals(this.contextHelpTextHtmlEnable, enabled)) {
contextHelpTextHtmlEnable = enabled;
requestContainerUpdate();
}
}
}

View File

@ -28,6 +28,8 @@ import com.haulmont.cuba.desktop.sys.vcl.JTabbedPaneExt;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Frame;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import javax.swing.Action;
@ -42,6 +44,8 @@ public class DesktopComponentsHelper {
public static final int BUTTON_HEIGHT = 30;
public static final int FIELD_HEIGHT = 28;
public static final int TOOLTIP_WIDTH = 500;
// todo move nimbus constants to theme
public static final Color defaultBgColor = (Color) UIManager.get("nimbusLightBackground");
@ -458,4 +462,11 @@ public class DesktopComponentsHelper {
}
}
}
public static String getContextHelpText(String contextHelpText, boolean contextHelpTextHtmlEnabled) {
if (StringUtils.isNotEmpty(contextHelpText)) {
return contextHelpTextHtmlEnabled ? contextHelpText : StringEscapeUtils.escapeHtml(contextHelpText);
}
return null;
}
}

View File

@ -371,11 +371,13 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
DesktopAbstractComponent fieldImpl = (DesktopAbstractComponent) fieldComponent;
fci.setComposition(fieldImpl);
if (fci.getDescription() != null) {
if (fci.getContextHelpText() != null) {
ToolTipButton tooltipBtn = new ToolTipButton();
tooltipBtn.setVisible(fieldComponent.isVisible());
tooltipBtn.setToolTipText(fci.getDescription());
fci.setToolTipButton(new ToolTipButton());
tooltipBtn.setToolTipText(DesktopComponentsHelper.getContextHelpText(
fci.getContextHelpText(),
fci.isContextHelpTextHtmlEnabled()));
fci.setToolTipButton(tooltipBtn);
} else {
fci.setToolTipButton(null);
}
@ -455,11 +457,13 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
impl.add(label, labelCc.cell(colIndex * 3, insertRowIndex, 1, 1));
}
if (Strings.isNullOrEmpty(fci.getDescription())) {
if (Strings.isNullOrEmpty(fci.getContextHelpText())) {
fci.setToolTipButton(null);
} else if (fci.getToolTipButton() == null) {
ToolTipButton toolTipButton = new ToolTipButton();
toolTipButton.setToolTipText(fci.getDescription());
toolTipButton.setToolTipText(DesktopComponentsHelper.getContextHelpText(
fci.getContextHelpText(),
fci.isContextHelpTextHtmlEnabled()));
toolTipButton.setVisible(fieldComponent.isVisible());
fci.setToolTipButton(toolTipButton);
}
@ -504,11 +508,13 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
DesktopAbstractComponent fieldImpl = (DesktopAbstractComponent) fci.getComponentNN();
fci.setComposition(fieldImpl);
if (fci.getDescription() != null) {
if (fci.getContextHelpText() != null) {
ToolTipButton tooltipBtn = new ToolTipButton();
tooltipBtn.setVisible(fci.getComponentNN().isVisible());
tooltipBtn.setToolTipText(fci.getDescription());
fci.setToolTipButton(new ToolTipButton());
tooltipBtn.setToolTipText(DesktopComponentsHelper.getContextHelpText(
fci.getContextHelpText(),
fci.isContextHelpTextHtmlEnabled()));
fci.setToolTipButton(tooltipBtn);
} else {
fci.setToolTipButton(null);
}
@ -546,6 +552,12 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
if (fci.getTargetRequiredMessage() != null) {
cubaField.setRequiredMessage(fci.getTargetRequiredMessage());
}
if (fci.getTargetContextHelpText() != null) {
cubaField.setContextHelpText(fci.getTargetContextHelpText());
}
if (fci.getTargetContextHelpTextHtmlEnabled() != null) {
cubaField.setContextHelpTextHtmlEnabled(fci.getTargetContextHelpTextHtmlEnabled());
}
if (fci.getTargetEditable() != null) {
cubaField.setEditable(fci.getTargetEditable());
}
@ -1057,6 +1069,8 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
protected CollectionDatasource targetOptionsDatasource;
protected String targetCaption;
protected String targetDescription;
protected String targetContextHelpText;
protected Boolean targetContextHelpTextHtmlEnabled;
protected Formatter targetFormatter;
protected boolean isTargetCustom;
@ -1415,6 +1429,40 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
}
}
@Override
public String getContextHelpText() {
if (component instanceof Field) {
return ((Field) component).getContextHelpText();
}
return targetContextHelpText;
}
@Override
public void setContextHelpText(String contextHelpText) {
if (component instanceof Field) {
((Field) component).setContextHelpText(contextHelpText);
} else {
this.targetContextHelpText = contextHelpText;
}
}
@Override
public Boolean isContextHelpTextHtmlEnabled() {
if (component instanceof Field) {
return ((Field) component).isContextHelpTextHtmlEnabled();
}
return targetContextHelpTextHtmlEnabled;
}
@Override
public void setContextHelpTextHtmlEnabled(Boolean enabled) {
if (component instanceof Field) {
((Field) component).setContextHelpTextHtmlEnabled(enabled);
} else {
this.targetContextHelpTextHtmlEnabled = enabled;
}
}
@Override
public String getDescription() {
if (component instanceof Field) {
@ -1577,6 +1625,22 @@ public class DesktopFieldGroup extends DesktopAbstractComponent<JPanel>
this.targetRequiredMessage = targetRequiredMessage;
}
public String getTargetContextHelpText() {
return targetContextHelpText;
}
public void setTargetContextHelpText(String targetContextHelpText) {
this.targetContextHelpText = targetContextHelpText;
}
public Boolean getTargetContextHelpTextHtmlEnabled() {
return targetContextHelpTextHtmlEnabled;
}
public void setTargetContextHelpTextHtmlEnabled(Boolean targetContextHelpTextHtmlEnabled) {
this.targetContextHelpTextHtmlEnabled = targetContextHelpTextHtmlEnabled;
}
public CollectionDatasource getTargetOptionsDatasource() {
return targetOptionsDatasource;
}

View File

@ -21,6 +21,7 @@ import com.haulmont.bali.datastruct.Pair;
import com.haulmont.cuba.desktop.gui.data.DesktopContainerHelper;
import com.haulmont.cuba.desktop.sys.layout.BoxLayoutAdapter;
import com.haulmont.cuba.desktop.sys.layout.GridLayoutAdapter;
import com.haulmont.cuba.desktop.sys.layout.MigLayoutHelper;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Frame;
@ -81,30 +82,26 @@ public class DesktopGridLayout extends DesktopAbstractComponent<JPanel> implemen
}
final JComponent composition = DesktopComponentsHelper.getComposition(component);
// add caption first
ComponentCaption caption = null;
boolean haveDescription = false;
if (DesktopContainerHelper.hasExternalCaption(component)) {
caption = new ComponentCaption(component);
boolean hasExternalCaption = DesktopContainerHelper.hasExternalCaption(component);
if (hasExternalCaption
|| DesktopContainerHelper.hasExternalContextHelp(component)) {
ComponentCaption caption = new ComponentCaption(component);
captions.put(component, caption);
impl.add(caption, layoutAdapter.getCaptionConstraints(component, col, row, col2, row2));
} else if (DesktopContainerHelper.hasExternalDescription(component)) {
caption = new ComponentCaption(component);
captions.put(component, caption);
haveDescription = true;
}
// if component have description without caption, we need to wrap
// component to view Description button horizontally after component
if (haveDescription) {
JPanel wrapper = new LayoutSlot();
BoxLayoutAdapter adapter = BoxLayoutAdapter.create(wrapper);
adapter.setExpandLayout(true);
adapter.setSpacing(false);
adapter.setMargin(false);
wrapper.add(composition);
wrapper.add(caption, new CC().alignY("top"));
if (hasExternalCaption) {
adapter.setFlowDirection(BoxLayoutAdapter.FlowDirection.Y);
wrapper.add(caption, 0);
} else {
wrapper.add(caption, new CC().alignY("top"));
}
impl.add(wrapper, layoutAdapter.getConstraints(component, col, row, col2, row2));
wrappers.put(component, new Pair<>(wrapper, adapter));
} else {
@ -327,15 +324,16 @@ public class DesktopGridLayout extends DesktopAbstractComponent<JPanel> implemen
public void updateComponent(Component child) {
boolean componentReAdded = false;
if (DesktopContainerHelper.mayHaveExternalCaption(child)) {
if (DesktopContainerHelper.mayHaveExternalCaption(child)
|| DesktopContainerHelper.mayHaveExternalContextHelp(child)) {
if (captions.containsKey(child)
&& !DesktopContainerHelper.hasExternalCaption(child)
&& !DesktopContainerHelper.hasExternalDescription(child)) {
&& !DesktopContainerHelper.hasExternalContextHelp(child)) {
reAddChild(child);
componentReAdded = true;
} else if (!captions.containsKey(child)
&& (DesktopContainerHelper.hasExternalCaption(child)
|| DesktopContainerHelper.hasExternalDescription(child))) {
|| DesktopContainerHelper.hasExternalContextHelp(child))) {
reAddChild(child);
componentReAdded = true;
} else if (captions.containsKey(child)) {
@ -353,6 +351,19 @@ public class DesktopGridLayout extends DesktopAbstractComponent<JPanel> implemen
JComponent composition;
if (wrappers.containsKey(child)) {
composition = wrappers.get(child).getFirst();
CC constraints = MigLayoutHelper.getConstraints(child);
if (child.getHeight() == -1.0) {
MigLayoutHelper.applyHeight(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyHeight(constraints, 100, UNITS_PERCENTAGE, false);
}
if (child.getWidth() == -1.0) {
MigLayoutHelper.applyWidth(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyWidth(constraints, 100, UNITS_PERCENTAGE, false);
}
BoxLayoutAdapter adapter = wrappers.get(child).getSecond();
adapter.updateConstraints(DesktopComponentsHelper.getComposition(child), constraints);
} else {
composition = DesktopComponentsHelper.getComposition(child);
}

View File

@ -199,6 +199,24 @@ public class DesktopListEditor extends DesktopAbstractField<JPanel> implements L
public void setDescription(String description) {
}
@Override
public String getContextHelpText() {
return null;
}
@Override
public void setContextHelpText(String contextHelpText) {
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return false;
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
}
@Override
protected void setEditableToComponent(boolean editable) {
delegate.setEditable(editable);

View File

@ -46,6 +46,7 @@ import com.haulmont.cuba.gui.data.ValueListener;
import com.haulmont.cuba.gui.data.impl.WeakCollectionChangeListener;
import net.miginfocom.layout.CC;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
@ -601,6 +602,22 @@ public class DesktopTokenList extends DesktopAbstractField<JPanel> implements To
DesktopToolTipManager.getInstance().registerTooltip(impl);
}
@Override
public void setContextHelpText(String contextHelpText) {
if (!ObjectUtils.equals(this.contextHelpText, contextHelpText)) {
this.contextHelpText = contextHelpText;
// for now, DesktopTokenList doesn't provide context help
}
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
if (!ObjectUtils.equals(this.contextHelpTextHtmlEnable, enabled)) {
contextHelpTextHtmlEnable = enabled;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getValue() {

View File

@ -992,37 +992,33 @@ public class DesktopWindow implements Window, Component.Disposable,
remove(component);
}
int implIndex = getActualIndex(index);
ComponentCaption caption = null;
boolean haveDescription = false;
if (DesktopContainerHelper.hasExternalCaption(component)) {
caption = new ComponentCaption(component);
captions.put(component, caption);
getContainer().add(caption, layoutAdapter.getCaptionConstraints(component), implIndex); // CAUTION this dramatically wrong
implIndex++;
} else if (DesktopContainerHelper.hasExternalDescription(component)) {
caption = new ComponentCaption(component);
captions.put(component, caption);
haveDescription = true;
}
JComponent composition = DesktopComponentsHelper.getComposition(component);
// if component have description without caption, we need to wrap
// component to view Description button horizontally after component
if (haveDescription) {
boolean hasExternalCaption = DesktopContainerHelper.hasExternalCaption(component);
if (hasExternalCaption
|| DesktopContainerHelper.hasExternalContextHelp(component)) {
ComponentCaption caption = new ComponentCaption(component);
captions.put(component, caption);
JPanel wrapper = new JPanel();
BoxLayoutAdapter adapter = BoxLayoutAdapter.create(wrapper);
adapter.setExpandLayout(true);
adapter.setSpacing(false);
adapter.setMargin(false);
wrapper.add(composition);
wrapper.add(caption, new CC().alignY("top"));
getContainer().add(wrapper, layoutAdapter.getConstraints(component), implIndex);
if (hasExternalCaption) {
adapter.setFlowDirection(BoxLayoutAdapter.FlowDirection.Y);
wrapper.add(caption, 0);
} else {
wrapper.add(caption, new CC().alignY("top"));
}
getContainer().add(wrapper, layoutAdapter.getConstraints(component), index);
wrappers.put(component, new Pair<>(wrapper, adapter));
} else {
getContainer().add(composition, layoutAdapter.getConstraints(component), implIndex);
getContainer().add(composition, layoutAdapter.getConstraints(component), index);
}
if (component.getId() != null) {
componentByIds.put(component.getId(), component);
}
@ -1126,17 +1122,6 @@ public class DesktopWindow implements Window, Component.Disposable,
requestRepaint();
}
protected int getActualIndex(int originalIndex) {
int index = originalIndex;
Object[] components = ownComponents.toArray();
for (int i = 0; i < originalIndex; i++) {
if (DesktopContainerHelper.hasExternalCaption((Component) components[i])) {
index++;
}
}
return index;
}
@Override
public Component getOwnComponent(String id) {
return componentByIds.get(id);
@ -1378,15 +1363,16 @@ public class DesktopWindow implements Window, Component.Disposable,
@Override
public void updateComponent(Component child) {
boolean componentReAdded = false;
if (DesktopContainerHelper.mayHaveExternalCaption(child)) {
if (DesktopContainerHelper.mayHaveExternalCaption(child)
|| DesktopContainerHelper.mayHaveExternalContextHelp(child)) {
if (captions.containsKey(child)
&& !DesktopContainerHelper.hasExternalCaption(child)
&& !DesktopContainerHelper.hasExternalDescription(child)) {
&& !DesktopContainerHelper.hasExternalContextHelp(child)) {
reAddChild(child);
componentReAdded = true;
} else if (!captions.containsKey(child)
&& (DesktopContainerHelper.hasExternalCaption(child)
|| DesktopContainerHelper.hasExternalDescription(child))) {
|| DesktopContainerHelper.hasExternalContextHelp(child))) {
reAddChild(child);
componentReAdded = true;
} else if (captions.containsKey(child)) {
@ -1404,6 +1390,19 @@ public class DesktopWindow implements Window, Component.Disposable,
JComponent composition;
if (wrappers.containsKey(child)) {
composition = wrappers.get(child).getFirst();
CC constraints = MigLayoutHelper.getConstraints(child);
if (child.getHeight() == -1.0) {
MigLayoutHelper.applyHeight(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyHeight(constraints, 100, UNITS_PERCENTAGE, false);
}
if (child.getWidth() == -1.0) {
MigLayoutHelper.applyWidth(constraints, -1, UNITS_PIXELS, false);
} else {
MigLayoutHelper.applyWidth(constraints, 100, UNITS_PERCENTAGE, false);
}
BoxLayoutAdapter adapter = wrappers.get(child).getSecond();
adapter.updateConstraints(DesktopComponentsHelper.getComposition(child), constraints);
} else {
composition = DesktopComponentsHelper.getComposition(child);
}

View File

@ -21,6 +21,7 @@ import com.haulmont.cuba.desktop.gui.components.DesktopCheckBox;
import com.haulmont.cuba.desktop.gui.components.DesktopComponent;
import com.haulmont.cuba.desktop.gui.components.DesktopContainer;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.Component.HasContextHelp;
import com.haulmont.cuba.gui.components.Field;
import org.apache.commons.lang.StringUtils;
@ -56,6 +57,18 @@ public class DesktopContainerHelper {
return false;
}
public static boolean mayHaveExternalContextHelp(Component component) {
return component instanceof HasContextHelp;
}
public static boolean hasExternalContextHelp(Component component) {
if (component instanceof HasContextHelp) {
final String contextHelp = ((HasContextHelp) component).getContextHelpText();
return StringUtils.isNotEmpty(contextHelp);
}
return false;
}
public static void assignContainer(Component component, DesktopContainer container) {
if (component instanceof DesktopComponent) {
((DesktopComponent) component).setContainer(container);

View File

@ -41,6 +41,8 @@
<dimension property="DateField.dimension" value="110 28"/>
<!--the expected dimension of one digit. to configure width only-->
<dimension property="TimeField.digitWidth" value="23 -1"/>
<int property="Tooltip.maxWidth" value="500"/>
</ui-defaults>
<layout margin-size="5" spacing-size="3"/>

View File

@ -17,9 +17,16 @@
package com.haulmont.cuba.desktop.sys;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.desktop.DesktopConfig;
import com.haulmont.cuba.desktop.gui.components.DesktopComponentsHelper;
import com.haulmont.cuba.desktop.sys.vcl.ToolTipButton;
import com.haulmont.cuba.gui.components.KeyCombination;
import org.apache.commons.lang.StringUtils;
import javax.swing.*;
import javax.swing.text.StyleContext;
import java.awt.*;
import java.awt.event.*;
@ -28,24 +35,23 @@ import java.awt.event.*;
*/
public class DesktopToolTipManager extends MouseAdapter {
public static final int F1_CODE = 112;
protected static int CLOSE_TIME = 500;
protected static int SHOW_TIME = 1000;
private static int CLOSE_TIME = 500;
private static int SHOW_TIME = 1000;
protected boolean tooltipShowing = false;
private boolean tooltipShowing = false;
protected JToolTip toolTipWindow;
protected Popup window;
protected JComponent component;
private JToolTip toolTipWindow;
private Popup window;
private JComponent component;
protected Timer showTimer = new Timer(SHOW_TIME, null);
protected Timer closeTimer;
private Timer showTimer = new Timer(SHOW_TIME, null);
private Timer closeTimer;
private MouseListener componentMouseListener = new ComponentMouseListener();
private KeyListener fieldKeyListener = new FieldKeyListener();
private ActionListener btnActionListener = new ButtonClickListener();
protected Configuration configuration = AppBeans.get(Configuration.NAME);
protected MouseListener componentMouseListener = new ComponentMouseListener();
protected KeyListener fieldKeyListener = new FieldKeyListener();
protected ActionListener btnActionListener = new ButtonClickListener();
private static DesktopToolTipManager instance;
@ -61,23 +67,20 @@ public class DesktopToolTipManager extends MouseAdapter {
return instance;
}
private DesktopToolTipManager() {
protected DesktopToolTipManager() {
closeTimer = new Timer(CLOSE_TIME, null);
closeTimer.setRepeats(false);
closeTimer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
window.hide();
window = null;
tooltipShowing = false;
toolTipWindow.removeMouseListener(DesktopToolTipManager.this);
component.removeMouseListener(DesktopToolTipManager.this);
closeTimer.addActionListener(e -> {
window.hide();
window = null;
tooltipShowing = false;
toolTipWindow.removeMouseListener(DesktopToolTipManager.this);
component.removeMouseListener(DesktopToolTipManager.this);
}
});
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
private MouseEvent event;
protected MouseEvent event;
@Override
public void eventDispatched(AWTEvent e) {
@ -96,7 +99,7 @@ public class DesktopToolTipManager extends MouseAdapter {
}, AWTEvent.MOUSE_EVENT_MASK);
}
private boolean isPointInComponent(Point point, JComponent component) {
protected boolean isPointInComponent(Point point, JComponent component) {
if (!component.isShowing())
return false;
@ -108,7 +111,7 @@ public class DesktopToolTipManager extends MouseAdapter {
/**
* Register tooltip for component.
* Tooltip is displayed when user press F1 on focused component
* The tooltip is displayed when a user either presses F1 on a focused component or hovers over it.
* ToolTip text is taken from {@link javax.swing.JComponent#getToolTipText()}.
*
* @param component component to register
@ -116,6 +119,9 @@ public class DesktopToolTipManager extends MouseAdapter {
public void registerTooltip(final JComponent component) {
component.removeKeyListener(fieldKeyListener);
component.addKeyListener(fieldKeyListener);
component.removeMouseListener(componentMouseListener);
component.addMouseListener(componentMouseListener);
}
/**
@ -154,7 +160,7 @@ public class DesktopToolTipManager extends MouseAdapter {
btn.addActionListener(btnActionListener);
}
private void hideTooltip() {
protected void hideTooltip() {
closeTimer.stop();
if (window != null) {
window.hide();
@ -165,10 +171,14 @@ public class DesktopToolTipManager extends MouseAdapter {
}
}
private void showTooltip(JComponent field, String text) {
protected void showTooltip(JComponent field, String text) {
if (!field.isShowing())
return;
if (StringUtils.isEmpty(text)) {
return;
}
PointerInfo pointerInfo = MouseInfo.getPointerInfo();
if (pointerInfo == null) {
return;
@ -198,21 +208,43 @@ public class DesktopToolTipManager extends MouseAdapter {
}
component = field;
final JToolTip toolTip = new JToolTip();
toolTip.setTipText("<html>" + text + "</html>");
final JToolTip toolTip = new CubaToolTip();
toolTip.setTipText(text);
final Popup tooltipContainer = PopupFactory.getSharedInstance().getPopup(field, toolTip, x, y);
tooltipContainer.show();
window = tooltipContainer;
toolTipWindow = toolTip;
tooltipShowing = true;
if ((!(field instanceof ToolTipButton)) && ((field instanceof AbstractButton) || (field instanceof JLabel))) {
if (!(field instanceof ToolTipButton)) {
toolTip.addMouseListener(this);
field.addMouseListener(this);
}
}
protected class CubaToolTip extends JToolTip {
@Override
public void setTipText(String tipText) {
UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
int maxTooltipWidth = lafDefaults.getInt("Tooltip.maxWidth");
if (maxTooltipWidth == 0) {
maxTooltipWidth = DesktopComponentsHelper.TOOLTIP_WIDTH;
}
FontMetrics fontMetrics = StyleContext.getDefaultStyleContext().getFontMetrics(getFont());
int actualWidth = SwingUtilities.computeStringWidth(fontMetrics, tipText);
if (actualWidth < maxTooltipWidth) {
tipText = "<html>" + tipText + "</html>";
} else {
tipText = "<html><body width=\"" + maxTooltipWidth + "px\">" + tipText + "</body></html>";
}
super.setTipText(tipText);
}
}
@Override
public void mouseExited(MouseEvent e) {
closeTimer.start();
@ -225,9 +257,9 @@ public class DesktopToolTipManager extends MouseAdapter {
}
}
private class ComponentMouseListener extends MouseAdapter {
protected class ComponentMouseListener extends MouseAdapter {
private JComponent cmp;
protected JComponent cmp;
{
showTimer.setRepeats(false);
@ -243,7 +275,7 @@ public class DesktopToolTipManager extends MouseAdapter {
@Override
public void mouseEntered(MouseEvent e) {
if (window != null) {
if (e.getSource() != component && e.getSource() != toolTipWindow && component instanceof AbstractButton) {
if (e.getSource() != component && e.getSource() != toolTipWindow) {
hideTooltip();
cmp = (JComponent) e.getSource();
showTimer.start();
@ -272,10 +304,14 @@ public class DesktopToolTipManager extends MouseAdapter {
}
}
private class FieldKeyListener extends KeyAdapter {
protected class FieldKeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == F1_CODE) {
String showTooltipShortcut = configuration.getConfig(DesktopConfig.class).getShowTooltipShortcut();
KeyStroke keyStroke = DesktopComponentsHelper
.convertKeyCombination(KeyCombination.create(showTooltipShortcut));
if (KeyStroke.getKeyStrokeForEvent(e).equals(keyStroke)) {
hideTooltip();
JComponent field = (JComponent) e.getSource();
showTooltip(field, field.getToolTipText());
@ -287,7 +323,7 @@ public class DesktopToolTipManager extends MouseAdapter {
}
}
private class ButtonClickListener implements ActionListener {
protected class ButtonClickListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {

View File

@ -393,6 +393,8 @@ public class DesktopThemeLoaderImpl implements DesktopThemeLoader {
return loadInsets(element);
} else if ("dimension".equals(elementName)) {
return loadDimension(element);
} else if ("int".equals(elementName)) {
return Integer.parseInt(element.attributeValue("value"));
} else {
log.error("Uknown UI property value: " + elementName);
return null;

View File

@ -89,7 +89,7 @@ org.javassist/javassist = 3.21.0-GA
org.hibernate/hibernate-validator = 5.4.2.Final
org.glassfish.web/javax.el = 2.2.6
com.vaadin = 7.7.11.cuba.1
com.vaadin = 7.7.11.cuba.3
com.vaadin/vaadin-shared = ${com.vaadin}
com.vaadin/vaadin-server = ${com.vaadin}
com.vaadin/vaadin-client = ${com.vaadin}

View File

@ -346,6 +346,35 @@ public interface Component {
void setDescription(String description);
}
/**
* A sub-interface implemented by components that can provide a context help.
*/
interface HasContextHelp {
/**
* @return context help text
*/
String getContextHelpText();
/**
* Sets context help text. If set, then a special icon will be added for a field.
*
* @param contextHelpText context help text to be set
*/
void setContextHelpText(String contextHelpText);
/**
* @return true if field accepts context help text in HTML format, false otherwise
*/
boolean isContextHelpTextHtmlEnabled();
/**
* Defines if context help text can be presented as HTML.
*
* @param enabled true if field accepts context help text in HTML format, false otherwise
*/
void setContextHelpTextHtmlEnabled(boolean enabled);
}
/**
* Layout having a mouse click listener.
*/

View File

@ -22,7 +22,8 @@ import java.util.Collection;
* Base interface for "fields" - components intended to display and edit value of a certain entity attribute.
*/
public interface Field extends DatasourceComponent, Component.HasCaption, Component.HasValue, Component.Editable,
Component.BelongToFrame, Component.Validatable, Component.HasIcon {
Component.BelongToFrame, Component.Validatable, Component.HasIcon,
Component.HasContextHelp {
/**
* @return whether the field must contain a non-null value
*/

View File

@ -441,6 +441,36 @@ public interface FieldGroup extends Component, Component.BelongToFrame, Componen
* @return options datasource
*/
CollectionDatasource getOptionsDatasource();
/**
* @return context help text
*/
String getContextHelpText();
/**
* Set context help text for declarative field.
*
* If {@link #isBound()} is true and Component implements {@link Field} then sets context help text
* to the connected Component.
*
* @param contextHelpText context help text to be set
*/
void setContextHelpText(String contextHelpText);
/**
* @return true if field accepts context help text in HTML format, null if not set for a declarative field
*/
Boolean isContextHelpTextHtmlEnabled();
/**
* Defines if context help text can be presented as HTML.
* <p>
* If {@link #isBound()} is true and Component implements {@link Field} then sets this attribute
* to the connected Component.
*
* @param enabled true if field accepts context help text in HTML format
*/
void setContextHelpTextHtmlEnabled(Boolean enabled);
}
/**

View File

@ -887,6 +887,11 @@
<xs:attribute name="description" type="resourceString"/>
</xs:attributeGroup>
<xs:attributeGroup name="hasContextHelp">
<xs:attribute name="contextHelpText" type="resourceString"/>
<xs:attribute name="contextHelpTextHtmlEnabled" type="xs:boolean"/>
</xs:attributeGroup>
<xs:attributeGroup name="hasCaptionProp">
<xs:attribute name="caption" type="resourceString"/>
</xs:attributeGroup>
@ -1157,6 +1162,7 @@
<xs:attributeGroup ref="hasTabIndex"/>
<xs:attributeGroup ref="hasEditable"/>
<xs:attributeGroup ref="hasDatasource"/>
<xs:attributeGroup ref="hasContextHelp"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
@ -1205,6 +1211,7 @@
<xs:attributeGroup ref="hasEditable"/>
<xs:attributeGroup ref="hasRequirements"/>
<xs:attributeGroup ref="hasDatasource"/>
<xs:attributeGroup ref="hasContextHelp"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
@ -2106,6 +2113,7 @@
<xs:attributeGroup ref="hasEditState"/>
<xs:attributeGroup ref="hasVisibility"/>
<xs:attributeGroup ref="hasTabIndex"/>
<xs:attributeGroup ref="hasContextHelp"/>
<xs:attribute name="width" type="componentSize"/>
@ -2210,6 +2218,7 @@
<xs:attributeGroup ref="hasCaptionSource"/>
<xs:attributeGroup ref="hasDatasourceProp"/>
<xs:attributeGroup ref="hasTabIndex"/>
<xs:attributeGroup ref="hasContextHelp"/>
<xs:attribute name="refreshOptionsOnLookupClose" type="xs:boolean"/>
<xs:attribute name="position" type="tokenListPosition"/>

View File

@ -228,6 +228,19 @@ public abstract class AbstractComponentLoader<T extends Component> implements Co
}
}
protected void loadContextHelp(Component.HasContextHelp component, Element element) {
String contextHelpText = element.attributeValue("contextHelpText");
if (StringUtils.isNotEmpty(contextHelpText)) {
contextHelpText = loadResourceString(contextHelpText);
component.setContextHelpText(contextHelpText);
}
String htmlEnabled = element.attributeValue("contextHelpTextHtmlEnabled");
if (StringUtils.isNotEmpty(htmlEnabled)) {
component.setContextHelpTextHtmlEnabled(Boolean.parseBoolean(htmlEnabled));
}
}
protected boolean loadVisible(Component component, Element element) {
if (component instanceof DatasourceComponent
&& ((DatasourceComponent) component).getDatasource() != null) {

View File

@ -42,6 +42,7 @@ public abstract class AbstractFieldLoader<T extends Field> extends AbstractDatas
loadCaption(resultComponent, element);
loadIcon(resultComponent, element);
loadDescription(resultComponent, element);
loadContextHelp(resultComponent, element);
loadValidators(resultComponent, element);
loadRequired(resultComponent, element);

View File

@ -432,6 +432,7 @@ public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {
}
}
loadDescription(field, element);
loadContextHelp(field, element);
field.setXmlDescriptor(element);
@ -490,6 +491,19 @@ public class FieldGroupLoader extends AbstractComponentLoader<FieldGroup> {
return field;
}
protected void loadContextHelp(FieldGroup.FieldConfig field, Element element) {
String contextHelpText = element.attributeValue("contextHelpText");
if (StringUtils.isNotEmpty(contextHelpText)) {
contextHelpText = loadResourceString(contextHelpText);
field.setContextHelpText(contextHelpText);
}
String htmlEnabled = element.attributeValue("contextHelpTextHtmlEnabled");
if (StringUtils.isNotEmpty(htmlEnabled)) {
field.setContextHelpTextHtmlEnabled(Boolean.parseBoolean(htmlEnabled));
}
}
protected String getDefaultCaption(FieldGroup.FieldConfig fieldConfig, Datasource fieldDatasource) {
String caption = fieldConfig.getCaption();
if (caption == null) {

View File

@ -45,6 +45,7 @@ public class FileUploadFieldLoader extends AbstractFieldLoader<FileUploadField>
loadCaption(resultComponent, element);
loadDescription(resultComponent, element);
loadContextHelp(resultComponent, element);
loadTabIndex(resultComponent, element);

View File

@ -48,6 +48,7 @@ public class TokenListLoader extends AbstractFieldLoader<TokenList> {
loadCaption(resultComponent, element);
loadIcon(resultComponent, element);
loadDescription(resultComponent, element);
loadContextHelp(resultComponent, element);
loadHeight(resultComponent, element);
loadWidth(resultComponent, element);

View File

@ -31,6 +31,9 @@ import com.vaadin.shared.ui.ComponentStateUtil;
public class CubaCaptionWidget extends VCaption {
public static final String CUBA_CLASSNAME = "c-caption";
public static final String CONTEXT_HELP_CLASSNAME = "c-context-help-button";
protected Element contextHelpIndicatorElement;
protected boolean captionPlacedAfterComponentByDefault = true;
@ -141,6 +144,13 @@ public class CubaCaptionWidget extends VCaption {
captionText = null;
}
if (ComponentStateUtil.hasDescription(owner.getState())
&& captionText != null) {
addStyleDependentName("hasdescription");
} else {
removeStyleDependentName("hasdescription");
}
AriaHelper.handleInputRequired(owner.getWidget(), showRequired);
if (showRequired) {
@ -162,6 +172,24 @@ public class CubaCaptionWidget extends VCaption {
requiredFieldIndicator = null;
}
if (owner.getState() instanceof AbstractFieldState) {
AbstractFieldState state = (AbstractFieldState) owner
.getState();
if (state.contextHelpText != null && !state.contextHelpText.isEmpty()) {
if (contextHelpIndicatorElement == null) {
contextHelpIndicatorElement = DOM.createDiv();
contextHelpIndicatorElement.setClassName(CONTEXT_HELP_CLASSNAME);
DOM.insertChild(getElement(), contextHelpIndicatorElement, getContextHelpInsertPosition());
}
} else {
if (contextHelpIndicatorElement != null) {
contextHelpIndicatorElement.removeFromParent();
contextHelpIndicatorElement = null;
}
}
}
AriaHelper.handleInputInvalid(owner.getWidget(), showError);
if (showError) {
@ -209,6 +237,9 @@ public class CubaCaptionWidget extends VCaption {
if (errorIndicatorElement != null && errorIndicatorElement.getParentElement() == getElement()) {
width += WidgetUtil.getRequiredWidth(errorIndicatorElement);
}
if (contextHelpIndicatorElement != null && contextHelpIndicatorElement.getParentElement() == getElement()) {
width += WidgetUtil.getRequiredWidth(contextHelpIndicatorElement);
}
return width;
}
@ -218,6 +249,10 @@ public class CubaCaptionWidget extends VCaption {
return super.getTextElement();
}
public Element getContextHelpIndicatorElement() {
return contextHelpIndicatorElement;
}
public Element getRequiredIndicatorElement() {
return requiredFieldIndicator;
}
@ -226,6 +261,21 @@ public class CubaCaptionWidget extends VCaption {
return errorIndicatorElement;
}
@Override
protected int getInsertPosition(InsertPosition element) {
int pos = super.getInsertPosition(element);
if (contextHelpIndicatorElement != null) {
pos++;
}
return pos;
}
protected int getContextHelpInsertPosition() {
return super.getInsertPosition(null);
}
public void setCaptionHolder(CaptionHolder captionHolder) {
this.captionHolder = captionHolder;
}

View File

@ -17,7 +17,10 @@
package com.haulmont.cuba.web.toolkit.ui.client.checkbox;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.user.client.DOM;
import com.haulmont.cuba.web.toolkit.ui.CubaCheckBox;
import com.vaadin.client.VTooltip;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.checkbox.CheckBoxConnector;
import com.vaadin.shared.ui.Connect;
@ -25,6 +28,8 @@ import com.vaadin.shared.ui.Connect;
@Connect(value = CubaCheckBox.class, loadStyle = Connect.LoadStyle.EAGER)
public class CubaCheckBoxConnector extends CheckBoxConnector {
public static final String CONTEXT_HELP_CLASSNAME = "c-context-help-button";
@Override
public boolean delegateCaptionHandling() {
return getWidget().captionManagedByLayout;
@ -45,5 +50,20 @@ public class CubaCheckBoxConnector extends CheckBoxConnector {
getWidget().captionManagedByLayout = getState().captionManagedByLayout;
super.onStateChanged(stateChangeEvent);
if (!getWidget().captionManagedByLayout
&& getState().contextHelpText != null
&& !getState().contextHelpText.isEmpty()) {
getWidget().contextHelpIcon = DOM.createSpan();
getWidget().contextHelpIcon.setInnerHTML("?");
getWidget().contextHelpIcon.setClassName(CONTEXT_HELP_CLASSNAME);
Roles.getTextboxRole().setAriaHiddenState(getWidget().contextHelpIcon, true);
getWidget().getElement().appendChild(getWidget().contextHelpIcon);
DOM.sinkEvents(getWidget().contextHelpIcon, VTooltip.TOOLTIP_EVENTS);
} else if (getWidget().contextHelpIcon != null) {
getWidget().contextHelpIcon.removeFromParent();
getWidget().contextHelpIcon = null;
}
}
}

View File

@ -17,17 +17,20 @@
package com.haulmont.cuba.web.toolkit.ui.client.checkbox;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.i18n.client.HasDirection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.VCheckBox;
public class CubaCheckBoxWidget extends VCheckBox implements FocusHandler, BlurHandler {
protected boolean captionManagedByLayout = false;
protected Element contextHelpIcon;
public CubaCheckBoxWidget() {
addBlurHandler(this);

View File

@ -17,12 +17,13 @@
package com.haulmont.cuba.web.toolkit.ui.client.cssactionslayout;
import com.haulmont.cuba.web.toolkit.ui.CubaCssActionsLayout;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.haulmont.cuba.web.toolkit.ui.client.caption.CubaCaptionWidget;
import com.vaadin.client.*;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.ShortcutActionHandler;
import com.vaadin.client.ui.csslayout.CssLayoutConnector;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.MarginInfo;
@ -60,4 +61,17 @@ public class CubaCssActionsLayoutConnector extends CssLayoutConnector implements
}
}
}
@Override
protected VCaption createCaption(ComponentConnector child) {
return new CubaCaptionWidget(child, getConnection());
}
@Override
protected boolean isCaptionNeeded(ComponentConnector child) {
AbstractComponentState state = child.getState();
return super.isCaptionNeeded(child) || (state instanceof AbstractFieldState
&& ((AbstractFieldState) state).contextHelpText != null
&& !((AbstractFieldState) state).contextHelpText.isEmpty());
}
}

View File

@ -39,7 +39,7 @@ public class CubaFieldGroupLayoutComponentSlot extends CubaGridLayoutSlot implem
protected static final String INDICATORS_CLASSNAME = "caption-indicators";
protected Element requiredElement = null;
protected Element tooltipElement = null;
protected Element contextHelpIndicatorElement = null;
protected Element errorIndicatorElement = null;
protected Element rightCaption = null;
@ -365,6 +365,15 @@ public class CubaFieldGroupLayoutComponentSlot extends CubaGridLayoutSlot implem
requiredElement = null;
}
if (captionWidget.getContextHelpIndicatorElement() != null) {
captionWidget.getContextHelpIndicatorElement().removeFromParent();
contextHelpIndicatorElement = captionWidget.getContextHelpIndicatorElement();
rightCaption.appendChild(contextHelpIndicatorElement);
} else if (contextHelpIndicatorElement != null) {
contextHelpIndicatorElement.removeFromParent();
contextHelpIndicatorElement = null;
}
if (captionWidget.getErrorIndicatorElement() != null) {
captionWidget.getErrorIndicatorElement().removeFromParent();

View File

@ -25,6 +25,8 @@ import com.vaadin.client.ui.ShortcutActionHandler;
import com.vaadin.client.ui.VGridLayout;
import com.vaadin.client.ui.gridlayout.GridLayoutConnector;
import com.vaadin.client.ui.layout.VLayoutSlot;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.ui.Connect;
@Connect(CubaGridLayout.class)
@ -43,7 +45,11 @@ public class CubaGridLayoutConnector extends GridLayoutConnector implements Pain
// CAUTION copied from GridLayoutConnector.updateCaption(ComponentConnector childConnector)
VGridLayout layout = getWidget();
VGridLayout.Cell cell = layout.widgetToCell.get(childConnector.getWidget());
if (VCaption.isNeeded(childConnector.getState())) {
AbstractComponentState state = childConnector.getState();
if (VCaption.isNeeded(state)
|| (state instanceof AbstractFieldState
&& ((AbstractFieldState) state).contextHelpText != null
&& !((AbstractFieldState) state).contextHelpText.isEmpty())) {
VLayoutSlot layoutSlot = cell.slot;
VCaption caption = layoutSlot.getCaption();
if (caption == null) {

View File

@ -18,13 +18,20 @@
package com.haulmont.cuba.web.toolkit.ui.client.orderedactionslayout;
import com.haulmont.cuba.web.toolkit.ui.CubaOrderedActionsLayout;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Paintable;
import com.vaadin.client.UIDL;
import com.vaadin.client.*;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.ShortcutActionHandler;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.orderedlayout.AbstractOrderedLayoutConnector;
import com.vaadin.client.ui.orderedlayout.CaptionPosition;
import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.ComponentConstants;
import com.vaadin.shared.communication.URLReference;
import com.vaadin.shared.ui.Connect;
import java.util.List;
@Connect(CubaOrderedActionsLayout.class)
public class CubaOrderedActionsLayoutConnector extends AbstractOrderedLayoutConnector implements Paintable {
@ -46,4 +53,60 @@ public class CubaOrderedActionsLayoutConnector extends AbstractOrderedLayoutConn
}
}
}
@Override
protected void updateCaptionInternal(ComponentConnector child) {
// CAUTION copied from superclass
CubaOrderedLayoutSlot slot = (CubaOrderedLayoutSlot) getWidget().getSlot(child.getWidget());
String caption = child.getState().caption;
URLReference iconUrl = child.getState().resources
.get(ComponentConstants.ICON_RESOURCE);
String iconUrlString = iconUrl != null ? iconUrl.getURL() : null;
Icon icon = child.getConnection().getIcon(iconUrlString);
List<String> styles = child.getState().styles;
String error = child.getState().errorMessage;
boolean showError = error != null;
if (child.getState() instanceof AbstractFieldState) {
AbstractFieldState abstractFieldState = (AbstractFieldState) child
.getState();
showError = showError && !abstractFieldState.hideErrors;
}
boolean required = false;
if (child instanceof AbstractFieldConnector) {
required = ((AbstractFieldConnector) child).isRequired();
}
boolean enabled = child.isEnabled();
if (slot.hasCaption() && null == caption) {
slot.setCaptionResizeListener(null);
}
// Haulmont API
String contextHelpText = null;
if (child.getState() instanceof AbstractFieldState) {
contextHelpText = ((AbstractFieldState) child.getState()).contextHelpText;
}
// Haulmont API
slot.setCaption(caption, contextHelpText, icon, styles, error, showError, required,
enabled, child.getState().captionAsHtml);
AriaHelper.handleInputRequired(child.getWidget(), required);
AriaHelper.handleInputInvalid(child.getWidget(), showError);
AriaHelper.bindCaption(child.getWidget(), slot.getCaptionElement());
if (slot.hasCaption()) {
CaptionPosition pos = slot.getCaptionPosition();
slot.setCaptionResizeListener(slotCaptionResizeListener);
if (child.isRelativeHeight()
&& (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
getWidget().updateCaptionOffset(slot.getCaptionElement());
} else if (child.isRelativeWidth()
&& (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
getWidget().updateCaptionOffset(slot.getCaptionElement());
}
}
}
}

View File

@ -35,12 +35,16 @@ import java.util.List;
public class CubaOrderedLayoutSlot extends Slot {
public static final String CONTEXT_HELP_CLASSNAME = "c-context-help-button";
protected Element contextHelpIcon;
protected String contextHelpText;
public CubaOrderedLayoutSlot(VAbstractOrderedLayout layout, Widget widget) {
super(layout, widget);
}
@Override
public void setCaption(String captionText, Icon icon, List<String> styles,
public void setCaption(String captionText, String contextHelpText, Icon icon, List<String> styles,
String error, boolean showError, boolean required, boolean enabled, boolean captionAsHtml) {
// CAUTION copied from super
// Caption wrappers
@ -48,7 +52,7 @@ public class CubaOrderedLayoutSlot extends Slot {
final Element focusedElement = WidgetUtil.getFocusedElement();
// By default focus will not be lost
boolean focusLost = false;
if (captionText != null || icon != null || error != null || required) {
if (captionText != null || icon != null || error != null || required || contextHelpText != null) {
if (caption == null) {
caption = DOM.createDiv();
captionWrap = DOM.createDiv();
@ -134,6 +138,30 @@ public class CubaOrderedLayoutSlot extends Slot {
requiredIcon = null;
}
// Context Help
// Haulmont API
this.contextHelpText = contextHelpText;
if (contextHelpText != null && !contextHelpText.isEmpty()) {
if (contextHelpIcon == null) {
contextHelpIcon = DOM.createSpan();
// TODO decide something better (e.g. use CSS to insert the
// character)
contextHelpIcon.setInnerHTML("?");
contextHelpIcon.setClassName(CONTEXT_HELP_CLASSNAME);
// The question mark should not be read by the screen reader, as it is
// purely visual. Required state is set at the element level for
// the screen reader.
Roles.getTextboxRole().setAriaHiddenState(contextHelpIcon, true);
}
if (caption != null) {
caption.appendChild(contextHelpIcon);
}
} else if (this.contextHelpIcon != null) {
this.contextHelpIcon.removeFromParent();
this.contextHelpIcon = null;
}
// Error
if (error != null && showError) {
if (errorIcon == null) {

View File

@ -20,20 +20,17 @@ package com.haulmont.cuba.web.toolkit.ui.client.tooltip;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.haulmont.cuba.web.toolkit.ui.client.caption.CubaCaptionWidget;
import com.haulmont.cuba.web.toolkit.ui.client.checkbox.CubaCheckBoxWidget;
import com.haulmont.cuba.web.toolkit.ui.client.resizabletextarea.CubaResizableTextAreaWrapperWidget;
import com.vaadin.client.*;
import com.vaadin.client.ui.VGridLayout;
import com.vaadin.client.ui.gridlayout.GridLayoutConnector;
import com.vaadin.client.ui.layout.ComponentConnectorLayoutSlot;
import com.vaadin.client.ui.orderedlayout.Slot;
import static com.haulmont.cuba.web.toolkit.ui.client.caption.CubaCaptionWidget.CONTEXT_HELP_CLASSNAME;
public class CubaTooltip extends VTooltip {
@ -43,7 +40,11 @@ public class CubaTooltip extends VTooltip {
// If required indicators are not visible we show tooltip on mouse hover otherwise only by mouse click
protected static Boolean requiredIndicatorVisible = null;
protected Element contextHelpElement = DOM.createDiv();
public CubaTooltip() {
contextHelpElement.setClassName("c-tooltip-context-help");
DOM.appendChild(getWidget().getElement(), contextHelpElement);
tooltipEventHandler = new CubaTooltipEventHandler();
}
@ -81,13 +82,57 @@ public class CubaTooltip extends VTooltip {
Profiler.leave("VTooltip.connectHandlersToWidget");
}
@Override
protected void setTooltipText(TooltipInfo info) {
super.setTooltipText(info);
if (info.getTitle() != null && !info.getTitle().isEmpty()) {
description.removeAttribute("aria-hidden");
} else {
description.setAttribute("aria-hidden", "true");
}
String contextHelp = info.getContextHelp();
if (contextHelp != null && !contextHelp.isEmpty()) {
if (info.isContextHelpHtmlEnabled()) {
contextHelpElement.setInnerHTML(contextHelp);
} else {
if (contextHelp.contains("\n")) {
contextHelp = WidgetUtil.escapeHTML(contextHelp).replace("\n", "<br/>");
contextHelpElement.setInnerHTML(contextHelp);
} else {
contextHelpElement.setInnerText(contextHelp);
}
}
contextHelpElement.getStyle().clearDisplay();
} else {
contextHelpElement.setInnerHTML("");
contextHelpElement.getStyle().setDisplay(Style.Display.NONE);
}
}
@Override
public void hide() {
contextHelpElement.setInnerHTML("");
super.hide();
}
public class CubaTooltipEventHandler extends TooltipEventHandler {
protected ComponentConnector currentConnector = null;
protected boolean isTooltipElement(Element element) {
return (REQUIRED_INDICATOR.equals(element.getClassName())
|| ERROR_INDICATOR.equals(element.getClassName()));
return (isRequiredIndicator(element)
|| isContextHelpElement(element));
}
protected boolean isRequiredIndicator(Element element) {
return REQUIRED_INDICATOR.equals(element.getClassName())
|| ERROR_INDICATOR.equals(element.getClassName());
}
protected boolean isContextHelpElement(Element element) {
return CONTEXT_HELP_CLASSNAME.equals(element.getClassName());
}
protected void checkRequiredIndicatorVisible() {
@ -114,22 +159,21 @@ public class CubaTooltip extends VTooltip {
@Override
protected TooltipInfo getTooltipFor(Element element) {
checkRequiredIndicatorVisible();
Element originalElement = element;
if (!requiredIndicatorVisible) {
if (isClassNameExcluded(element.getClassName())) {
return null;
} else {
return super.getTooltipFor(element);
}
if (isClassNameExcluded(element.getClassName())) {
return null;
}
if (isTooltipElement(element)) {
element = element.getParentElement().cast();
int index = DOM.getChildIndex(element.getParentElement().cast(), element);
int indexOfComponent = index == 0 ? index + 1 : index - 1;
element = DOM.getChild(element.getParentElement().cast(), indexOfComponent);
Widget widget = WidgetUtil.findWidget(element);
if (!(widget instanceof CubaCheckBoxWidget)) {
int index = DOM.getChildIndex(element.getParentElement().cast(), element);
int indexOfComponent = index == 0 ? index + 1 : index - 1;
element = DOM.getChild(element.getParentElement().cast(), indexOfComponent);
}
}
ApplicationConnection ac = getApplicationConnection();
@ -159,35 +203,40 @@ public class CubaTooltip extends VTooltip {
+ " returned a tooltip even though hasTooltip claims there are no tooltips for the connector.";
currentConnector = connector;
return info;
return updateTooltip(info, originalElement);
}
return null;
}
protected TooltipInfo updateTooltip(TooltipInfo info, Element element) {
if (isContextHelpElement(element)) {
info.setTitle(null);
info.setErrorMessage(null);
} else {
info.setContextHelp(null);
checkRequiredIndicatorVisible();
if (requiredIndicatorVisible && isRequiredIndicator(element)) {
info.setTitle(null);
}
}
return info;
}
@Override
public void onMouseDown(MouseDownEvent event) {
checkRequiredIndicatorVisible();
if (requiredIndicatorVisible) {
Element element = event.getNativeEvent().getEventTarget().cast();
if (isTooltipElement(element)) {
closeNow();
handleShowHide(event, false);
} else {
hideTooltip();
}
Element element = event.getNativeEvent().getEventTarget().cast();
if (isTooltipElement(element)) {
closeNow();
handleShowHide(event, false);
} else {
hideTooltip();
}
}
@Override
protected void handleShowHide(DomEvent domEvent, boolean isFocused) {
checkRequiredIndicatorVisible();
if (!requiredIndicatorVisible) {
super.handleShowHide(domEvent, isFocused);
}
// CAUTION copied from parent with changes
Event event = Event.as(domEvent.getNativeEvent());
Element element = Element.as(event.getEventTarget());
@ -214,28 +263,25 @@ public class CubaTooltip extends VTooltip {
handleHideEvent();
currentConnector = null;
} else {
boolean hasTooltipIndicator = hasIndicators(currentConnector);
boolean elementIsIndicator = elementIsIndicator(element);
if ((hasTooltipIndicator && elementIsIndicator) || (!hasTooltipIndicator)) {
if (closing) {
closeTimer.cancel();
closing = false;
}
if (isTooltipOpen()) {
closeNow();
}
setTooltipText(info);
updatePosition(event, isFocused);
if (BrowserInfo.get().isIOS()) {
element.focus();
}
showTooltip(domEvent instanceof MouseDownEvent && elementIsIndicator);
if (closing) {
closeTimer.cancel();
closing = false;
}
if (isTooltipOpen()) {
closeNow();
}
setTooltipText(info);
updatePosition(event, isFocused);
if (BrowserInfo.get().isIOS()) {
element.focus();
}
showTooltip(domEvent instanceof MouseDownEvent && elementIsIndicator);
}
handledByFocus = isFocused;
@ -244,59 +290,7 @@ public class CubaTooltip extends VTooltip {
protected boolean elementIsIndicator(Element relativeElement) {
return relativeElement != null
&& (REQUIRED_INDICATOR.equals(relativeElement.getClassName())
|| ERROR_INDICATOR.equals(relativeElement.getClassName()));
}
protected boolean hasIndicators(ComponentConnector connector) {
if (connector == null || connector.getWidget() == null) {
return false;
}
Widget parentWidget = connector.getWidget().getParent();
if (parentWidget instanceof Slot) {
Slot slot = (Slot) parentWidget;
if (slot.getCaptionElement() != null) {
com.google.gwt.user.client.Element captionElement = slot.getCaptionElement();
for (int i = 0; i < captionElement.getChildCount(); i++) {
Node child = captionElement.getChild(i);
if (child instanceof Element
&& (elementIsIndicator(((Element) child)))) {
return true;
}
}
}
} else if (connector.getParent() instanceof GridLayoutConnector) {
GridLayoutConnector gridLayoutConnector = (GridLayoutConnector) connector.getParent();
VGridLayout gridWidget = gridLayoutConnector.getWidget();
VGridLayout.Cell cell = gridWidget.widgetToCell.get(connector.getWidget());
ComponentConnectorLayoutSlot slot = cell.slot;
if (slot != null) {
VCaption caption = slot.getCaption();
if (caption != null) {
com.google.gwt.user.client.Element captionElement = caption.getElement();
for (int i = 0; i < captionElement.getChildCount(); i++) {
Node child = captionElement.getChild(i);
if (child instanceof Element
&& (elementIsIndicator(((Element) child)))) {
return true;
}
}
}
if (caption instanceof CubaCaptionWidget) {
CubaCaptionWidget cubaCaptionWidget = (CubaCaptionWidget) caption;
if (cubaCaptionWidget.getRequiredIndicatorElement() != null
|| cubaCaptionWidget.getErrorIndicatorElement() != null) {
return true;
}
}
}
}
return false;
&& isTooltipElement(relativeElement);
}
}
}

View File

@ -426,4 +426,24 @@ public abstract class WebAbstractField<T extends com.vaadin.ui.AbstractField>
return getClass().getSimpleName();
}
@Override
public String getContextHelpText() {
return component.getContextHelpText();
}
@Override
public void setContextHelpText(String contextHelpText) {
component.setContextHelpText(contextHelpText);
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return component.isContextHelpTextHtmlEnabled();
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
component.setContextHelpTextHtmlEnabled(enabled);
}
}

View File

@ -34,6 +34,7 @@ import com.haulmont.cuba.web.toolkit.ui.CubaFieldGroup;
import com.haulmont.cuba.web.toolkit.ui.CubaFieldGroupLayout;
import com.haulmont.cuba.web.toolkit.ui.CubaFieldWrapper;
import com.vaadin.server.Sizeable;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import org.slf4j.Logger;
@ -325,6 +326,12 @@ public class WebFieldGroup extends WebAbstractComponent<CubaFieldGroupLayout>
if (fci.getTargetRequiredMessage() != null) {
cubaField.setRequiredMessage(fci.getTargetRequiredMessage());
}
if (fci.getTargetContextHelpText() != null) {
cubaField.setContextHelpText(fci.getTargetContextHelpText());
}
if (fci.getTargetContextHelpTextHtmlEnabled() != null) {
cubaField.setContextHelpTextHtmlEnabled(fci.getTargetContextHelpTextHtmlEnabled());
}
if (fci.getTargetEditable() != null) {
cubaField.setEditable(fci.getTargetEditable());
}
@ -813,6 +820,8 @@ public class WebFieldGroup extends WebAbstractComponent<CubaFieldGroupLayout>
protected CollectionDatasource targetOptionsDatasource;
protected String targetCaption;
protected String targetDescription;
protected String targetContextHelpText;
protected Boolean targetContextHelpTextHtmlEnabled;
protected Formatter targetFormatter;
protected boolean isTargetCustom;
@ -1204,6 +1213,54 @@ public class WebFieldGroup extends WebAbstractComponent<CubaFieldGroupLayout>
}
}
@Override
public String getContextHelpText() {
if (component instanceof Field) {
return ((Field) component).getContextHelpText();
}
if (composition != null && isWrapped()) {
return composition.getContextHelpText();
}
return targetContextHelpText;
}
@Override
public void setContextHelpText(String contextHelpText) {
if (component instanceof Field) {
((Field) component).setContextHelpText(contextHelpText);
} else if (composition != null && isWrapped()) {
composition.setContextHelpText(contextHelpText);
} else {
this.targetContextHelpText = contextHelpText;
}
}
@Override
public Boolean isContextHelpTextHtmlEnabled() {
if (component instanceof Field) {
return ((Field) component).isContextHelpTextHtmlEnabled();
}
if (composition != null && isWrapped()) {
return composition.isContextHelpTextHtmlEnabled();
}
return BooleanUtils.isTrue(targetContextHelpTextHtmlEnabled);
}
@Override
public void setContextHelpTextHtmlEnabled(Boolean enabled) {
if (component instanceof Field) {
checkNotNullArgument(enabled, "Unable to reset contextHelpTextHtmlEnabled " +
"flag for the bound FieldConfig");
((Field) component).setContextHelpTextHtmlEnabled(enabled);
} else if (composition != null && isWrapped()) {
checkNotNullArgument(enabled, "Unable to reset contextHelpTextHtmlEnabled " +
"flag for the bound FieldConfig");
composition.setContextHelpTextHtmlEnabled(enabled);
} else {
this.targetContextHelpTextHtmlEnabled = enabled;
}
}
@Override
public Formatter getFormatter() {
if (component instanceof HasFormatter) {
@ -1356,6 +1413,22 @@ public class WebFieldGroup extends WebAbstractComponent<CubaFieldGroupLayout>
this.targetRequiredMessage = targetRequiredMessage;
}
public String getTargetContextHelpText() {
return targetContextHelpText;
}
public void setTargetContextHelpText(String targetContextHelpText) {
this.targetContextHelpText = targetContextHelpText;
}
public Boolean getTargetContextHelpTextHtmlEnabled() {
return targetContextHelpTextHtmlEnabled;
}
public void setTargetContextHelpTextHtmlEnabled(Boolean targetContextHelpTextHtmlEnabled) {
this.targetContextHelpTextHtmlEnabled = targetContextHelpTextHtmlEnabled;
}
public CollectionDatasource getTargetOptionsDatasource() {
return targetOptionsDatasource;
}

View File

@ -203,6 +203,26 @@ public class WebLookupPickerField extends WebLookupField implements LookupPicker
return pickerField.getDescription();
}
@Override
public String getContextHelpText() {
return pickerField.getContextHelpText();
}
@Override
public void setContextHelpText(String contextHelpText) {
pickerField.setContextHelpText(contextHelpText);
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return pickerField.isContextHelpTextHtmlEnabled();
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
pickerField.setContextHelpTextHtmlEnabled(enabled);
}
@Override
@Nullable
public Action getAction(String id) {

View File

@ -119,6 +119,26 @@ public class WebResizableTextArea extends WebAbstractTextArea<CubaTextArea> impl
wrapper.setDescription(description);
}
@Override
public String getContextHelpText() {
return wrapper.getContextHelpText();
}
@Override
public void setContextHelpText(String contextHelpText) {
wrapper.setContextHelpText(contextHelpText);
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return wrapper.isContextHelpTextHtmlEnabled();
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
wrapper.setContextHelpTextHtmlEnabled(enabled);
}
@Override
public boolean isRequired() {
return wrapper.isRequired();

View File

@ -188,6 +188,26 @@ public class WebSearchPickerField extends WebSearchField implements SearchPicker
return pickerField.getCaption();
}
@Override
public String getContextHelpText() {
return pickerField.getContextHelpText();
}
@Override
public void setContextHelpText(String contextHelpText) {
pickerField.setContextHelpText(contextHelpText);
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return pickerField.isContextHelpTextHtmlEnabled();
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
pickerField.setContextHelpTextHtmlEnabled(enabled);
}
@Override
@Nullable
public Action getAction(String id) {

View File

@ -67,6 +67,26 @@ public class WebSuggestionPickerField extends WebSuggestionField implements Sugg
return pickerField.getDescription();
}
@Override
public String getContextHelpText() {
return pickerField.getContextHelpText();
}
@Override
public void setContextHelpText(String contextHelpText) {
pickerField.setContextHelpText(contextHelpText);
}
@Override
public boolean isContextHelpTextHtmlEnabled() {
return pickerField.isContextHelpTextHtmlEnabled();
}
@Override
public void setContextHelpTextHtmlEnabled(boolean enabled) {
pickerField.setContextHelpTextHtmlEnabled(enabled);
}
protected void initValueSync(WebPickerField.Picker picker) {
component.addValueChangeListener(e -> {
if (updateComponentValue)

View File

@ -24,6 +24,34 @@
display: none;
}
.c-context-help-button {
display: inline-block;
font-size: 0;
width: $v-font-size;
}
.c-context-help-button::before {
font-family: FontAwesome;
content: "\f059";
font-size: $v-font-size;
padding-left: .2em;
}
.v-required-field-indicator {
padding: 0 0 0 .2em;
}
.v-tooltip {
.c-tooltip-context-help {
margin-top: $v-tooltip-padding-vertical * 2;
overflow: auto;
}
.v-errormessage[aria-hidden="true"]+.v-tooltip-text[aria-hidden="true"]+.c-tooltip-context-help {
margin-top: 0;
}
}
@if $v-show-required-indicators == false {
.v-required-field-indicator {
display: none;

View File

@ -29,6 +29,20 @@
cursor: pointer;
}
.c-context-help-button {
display: inline-block;
color: transparent;
background: transparent no-repeat top right;
background-image: url(sprites/question.png); /** sprite-ref: components; */
width: 16px;
height: 16px;
}
.c-context-help-button:hover {
cursor: pointer;
}
.#{$primary-stylename}text {
padding-bottom: 2px;
}

View File

@ -60,5 +60,13 @@
font-weight: bold;
margin: 1px 0 4px 0;
}
.c-tooltip-context-help {
padding: 2px 4px;
border: none;
border-top: 1px solid #fffef5;
border-bottom: 1px solid #fbf8d9;
overflow: hidden;
}
}
}