You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/03/03 09:47:40 UTC

[isis] branch master updated: ISIS-2877: harmonize wkt form based panels

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 35fdd45  ISIS-2877: harmonize wkt form based panels
35fdd45 is described below

commit 35fdd453672fc19e05040e15de718055bcf7f53a
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Mar 3 10:47:23 2022 +0100

    ISIS-2877: harmonize wkt form based panels
---
 .../ui/components/scalars/ScalarPanelAbstract.java | 111 +++++++++--------
 .../scalars/ScalarPanelSelectAbstract.java         |  12 +-
 .../scalars/ScalarPanelTextFieldAbstract.java      | 138 ++++++---------------
 .../ScalarPanelTextFieldWithTemporalPicker.java    |   2 +-
 .../scalars/ScalarPanelWithFormFieldAbstract.java  | 128 +++++++++++++++++++
 .../blobclob/IsisBlobOrClobPanelAbstract.java      |  74 ++++++-----
 .../scalars/markup/ParentedMarkupPanel.java        |  43 +++----
 .../components/scalars/primitive/BooleanPanel.java |  43 +------
 .../scalars/reference/ReferencePanel.java          |   2 +-
 .../ui/components/tree/ParentedTreePanel.html      |  35 ++++++
 .../ui/components/tree/ParentedTreePanel.java      |  57 ++++-----
 .../org/apache/isis/viewer/wicket/ui/util/Wkt.java |   9 ++
 12 files changed, 356 insertions(+), 298 deletions(-)

diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
index a58ebc6..3c05472 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelAbstract.java
@@ -26,13 +26,13 @@ import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
-import org.apache.wicket.behavior.AttributeAppender;
 import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.feedback.ComponentFeedbackMessageFilter;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.LabeledWebMarkupContainer;
 import org.apache.wicket.model.IModel;
+import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.PromptStyle;
@@ -90,6 +90,7 @@ implements ScalarModelSubscriber {
     private static final long serialVersionUID = 1L;
 
     protected static final String ID_SCALAR_TYPE_CONTAINER = "scalarTypeContainer";
+    protected static final String ID_SCALAR_VALUE_CONTAINER = "scalarValueContainer";
 
     protected static final String ID_SCALAR_IF_COMPACT = "scalarIfCompact";
     protected static final String ID_SCALAR_IF_REGULAR = "scalarIfRegular";
@@ -212,14 +213,36 @@ implements ScalarModelSubscriber {
     @Getter(value = AccessLevel.PROTECTED) @Accessors(fluent = true)
     private final ScalarModel scalarModel;
 
-    private Component scalarIfCompact;
-    private MarkupContainer scalarIfRegular;
+    // -- COMPACT
+
+    private Component componentIfCompact;
+    protected final Component getComponentForCompact() { return componentIfCompact; }
+    /**
+     * Builds the component to render the model when in {@link Rendering#COMPACT compact} format.
+     * <p>Is added to {@link #scalarTypeContainer}.
+     */
+    protected abstract Component createComponentForCompact();
+
+    // -- REGULAR
+
+    private MarkupContainer componentIfRegular;
+    protected final MarkupContainer getComponentForRegular() { return componentIfRegular; }
+    /**
+     * Builds the component to render the model when in {@link Rendering#REGULAR regular} format.
+     * <p>Is added to {@link #scalarTypeContainer}.
+     */
+    protected abstract MarkupContainer createComponentForRegular();
+
+    // --
 
     private WebMarkupContainer scalarTypeContainer;
+    protected final WebMarkupContainer getScalarTypeContainer() { return scalarTypeContainer; }
 
     /**
      * Populated
-     * Used by most subclasses ({@link ScalarPanelAbstract}, {@link ReferencePanel}, {@link ValueChoicesSelect2Panel}) but not all ({@link IsisBlobOrClobPanelAbstract}, {@link BooleanPanel})
+     * Used by most subclasses
+     * ({@link ScalarPanelAbstract}, {@link ReferencePanel}, {@link ValueChoicesSelect2Panel})
+     * but not all ({@link IsisBlobOrClobPanelAbstract}, {@link BooleanPanel})
      */
     private WebMarkupContainer scalarIfRegularInlinePromptForm;
 
@@ -304,11 +327,11 @@ implements ScalarModelSubscriber {
         scalarTypeContainer = Wkt.containerAdd(this, ID_SCALAR_TYPE_CONTAINER);
         Wkt.cssAppend(scalarTypeContainer, getCssClassName());
 
-        this.scalarIfCompact = createComponentForCompact();
-        this.scalarIfRegular = createComponentForRegular();
-        scalarIfRegular.setOutputMarkupId(true);
+        this.componentIfCompact = createComponentForCompact();
+        this.componentIfRegular = createComponentForRegular();
+        componentIfRegular.setOutputMarkupId(true);
 
-        scalarTypeContainer.addOrReplace(scalarIfCompact, scalarIfRegular);
+        scalarTypeContainer.addOrReplace(componentIfCompact, componentIfRegular);
 
         // find associated actions for this scalar property (only properties will have any.)
         final ScalarModel.AssociatedActions associatedActions =
@@ -319,7 +342,7 @@ implements ScalarModelSubscriber {
 
         // convert those actions into UI layer widgets
         final Can<LinkAndLabel> remainingLinkAndLabels = remainingAssociated.stream()
-        .map(LinkAndLabelFactory.forPropertyOrParameter(this.scalarModel))
+        .map(LinkAndLabelFactory.forPropertyOrParameter(scalarModel))
         .collect(Can.toCan());
 
         final InlinePromptConfig inlinePromptConfig = getInlinePromptConfig();
@@ -328,7 +351,7 @@ implements ScalarModelSubscriber {
             this.scalarIfRegularInlinePromptForm = createInlinePromptForm();
             scalarTypeContainer.addOrReplace(scalarIfRegularInlinePromptForm);
             inlinePromptLink = createInlinePromptLink();
-            scalarIfRegular.add(inlinePromptLink);
+            componentIfRegular.add(inlinePromptLink);
 
             // even if this particular scalarModel (property) is not configured for inline edits,
             // it's possible that one of the associated actions is.  Thus we set the prompt context
@@ -342,7 +365,7 @@ implements ScalarModelSubscriber {
 
             val componentToHideRef = _Refs.<Component>objectRef(inlinePromptLink);
 
-            if (this.scalarModel.getPromptStyle().isInline()
+            if (scalarModel.getPromptStyle().isInline()
                     && scalarModel.canEnterEditMode()) {
                 // we configure the prompt link if _this_ property is configured for inline edits...
                 Wkt.behaviorAddOnClick(inlinePromptLink, this::onPropertyInlineEditClick);
@@ -372,14 +395,15 @@ implements ScalarModelSubscriber {
                 && scalarModel.getMode() == ScalarRepresentation.VIEWING
                 && (scalarModel.getPromptStyle().isDialog()
                         || !scalarModel.canEnterEditMode())) {
-            getScalarValueComponent().add(new AttributeAppender("tabindex", "-1"));
+
+            Wkt.noTabbing(getValidationFeedbackReceiver());
         }
 
-        addPositioningCssTo(scalarIfRegular, remainingLinkAndLabels);
-        addActionLinksBelowAndRight(scalarIfRegular, remainingLinkAndLabels);
+        addPositioningCssTo(componentIfRegular, remainingLinkAndLabels);
+        addActionLinksBelowAndRight(componentIfRegular, remainingLinkAndLabels);
 
-        addEditPropertyTo(scalarIfRegular);
-        addFeedbackOnlyTo(scalarIfRegular, getScalarValueComponent());
+        addEditPropertyTo(componentIfRegular);
+        addFeedbackOnlyTo(componentIfRegular, getValidationFeedbackReceiver());
 
         getRendering().buildGui(this);
         addCssFromMetaModel();
@@ -500,14 +524,14 @@ implements ScalarModelSubscriber {
     }
 
     private void addFormComponentBehaviourToUpdateSubscribers() {
-        Component scalarValueComponent = getScalarValueComponent();
-        if(scalarValueComponent == null) {
+        val validationFeedbackReceiver = getValidationFeedbackReceiver();
+        if(validationFeedbackReceiver == null) {
             return;
         }
-        for (Behavior b : scalarValueComponent.getBehaviors(ScalarUpdatingBehavior.class)) {
-            scalarValueComponent.remove(b);
+        for (Behavior b : validationFeedbackReceiver.getBehaviors(ScalarUpdatingBehavior.class)) {
+            validationFeedbackReceiver.remove(b);
         }
-        scalarValueComponent.add(new ScalarUpdatingBehavior(this));
+        validationFeedbackReceiver.add(new ScalarUpdatingBehavior(this));
     }
 
     // //////////////////////////////////////
@@ -540,9 +564,9 @@ implements ScalarModelSubscriber {
 
             @Override
             public void buildGui(final ScalarPanelAbstract panel) {
-                panel.scalarIfCompact.setVisible(true);
-                panel.scalarIfRegular.setVisible(false);
-                Components.permanentlyHide(panel.scalarIfRegular, ID_SCALAR_NAME);
+                panel.componentIfCompact.setVisible(true);
+                panel.componentIfRegular.setVisible(false);
+                Components.permanentlyHide(panel.componentIfRegular, ID_SCALAR_NAME);
             }
 
         },
@@ -557,8 +581,8 @@ implements ScalarModelSubscriber {
 
             @Override
             public void buildGui(final ScalarPanelAbstract panel) {
-                panel.scalarIfRegular.setVisible(true);
-                panel.scalarIfCompact.setVisible(false);
+                panel.componentIfRegular.setVisible(true);
+                panel.componentIfCompact.setVisible(false);
             }
 
         };
@@ -578,30 +602,6 @@ implements ScalarModelSubscriber {
 
     // ///////////////////////////////////////////////////////////////////
 
-    protected final Component getComponentForRegular() {
-        return scalarIfRegular;
-    }
-
-    /**
-     * Mandatory hook method to build the component to render the model when in
-     * {@link Rendering#REGULAR regular} format.
-     *
-     * <p>
-     *     Is added to {@link #scalarTypeContainer}.
-     * </p>
-     */
-    protected abstract MarkupContainer createComponentForRegular();
-
-    /**
-     * Mandatory hook method to build the component to render the model when in
-     * {@link Rendering#COMPACT compact} format.
-     *
-     * <p>
-     *     Is added to {@link #scalarTypeContainer}.
-     * </p>
-     */
-    protected abstract Component createComponentForCompact();
-
     protected Label createScalarName(final String id, final String labelCaption) {
         final Label scalarName = Wkt.label(id, labelCaption);
         final ScalarModel scalarModel = getModel();
@@ -751,13 +751,18 @@ implements ScalarModelSubscriber {
     }
 
     /**
-     * Mandatory hook, used to determine which component to attach feedback to.
+     * Component to attach feedback to.
      */
-    protected abstract Component getScalarValueComponent();
+    @Nullable
+    protected abstract Component getValidationFeedbackReceiver();
 
 
     private void addFeedbackOnlyTo(final MarkupContainer markupContainer, final Component component) {
-        markupContainer.addOrReplace(new NotificationPanel(ID_FEEDBACK, component, new ComponentFeedbackMessageFilter(component)));
+        if(component==null) {
+            return;
+        }
+        markupContainer.addOrReplace(
+                new NotificationPanel(ID_FEEDBACK, component, new ComponentFeedbackMessageFilter(component)));
     }
 
     private void addActionLinksBelowAndRight(
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
index 401e977..de11472 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelSelectAbstract.java
@@ -45,7 +45,8 @@ import org.apache.isis.viewer.wicket.ui.util.Wkt.EventTopic;
 import lombok.NonNull;
 import lombok.val;
 
-public abstract class ScalarPanelSelectAbstract extends ScalarPanelAbstract {
+public abstract class ScalarPanelSelectAbstract
+extends ScalarPanelAbstract {
 
     private static final long serialVersionUID = 1L;
 
@@ -55,6 +56,10 @@ public abstract class ScalarPanelSelectAbstract extends ScalarPanelAbstract {
         super(id, scalarModel);
     }
 
+    @Override
+    protected Component getValidationFeedbackReceiver() {
+        return select2.asComponent();
+    }
 
     protected Select2 createSelect2(final String id) {
         final Select2 select2 = Select2.createSelect2(id, scalarModel());
@@ -103,11 +108,6 @@ public abstract class ScalarPanelSelectAbstract extends ScalarPanelAbstract {
         select2.add(new Select2Validator(scalarModel()));
     }
 
-    @Override
-    protected Component getScalarValueComponent() {
-        return select2.asComponent();
-    }
-
 
     /**
      * sets up the choices, also ensuring that any currently held value is compatible.
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
index 93d5311..fa8595e 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldAbstract.java
@@ -24,18 +24,15 @@ import java.util.Optional;
 
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.AbstractTextComponent;
+import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.form.TextArea;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.util.convert.IConverter;
-import org.apache.wicket.validation.IValidatable;
-import org.apache.wicket.validation.IValidator;
-import org.apache.wicket.validation.ValidationError;
 import org.apache.wicket.validation.validator.StringValidator;
 
 import org.apache.isis.commons.internal.base._Casts;
@@ -44,13 +41,9 @@ import org.apache.isis.core.metamodel.commons.ScalarRepresentation;
 import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.multiline.MultiLineFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.typicallen.TypicalLengthFacet;
-import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
-import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.metamodel.spec.feature.ObjectFeature;
-import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.model.util.CommonContextUtils;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Tooltips;
@@ -78,15 +71,14 @@ import lombok.val;
  * </p>
  */
 public abstract class ScalarPanelTextFieldAbstract<T extends Serializable>
-extends ScalarPanelAbstract
+extends ScalarPanelWithFormFieldAbstract
 implements TextFieldValueModel.ScalarModelProvider {
 
     private static final long serialVersionUID = 1L;
 
     protected final Class<T> cls;
 
-    @Getter(value = AccessLevel.PROTECTED)
-    private AbstractTextComponent<T> textField;
+    private AbstractTextComponent<T> formField;
 
     @Getter(value = AccessLevel.PROTECTED)
     private TextFieldVariant textFieldVariant;
@@ -144,56 +136,32 @@ implements TextFieldValueModel.ScalarModelProvider {
     // --
 
     @Override
-    protected MarkupContainer createComponentForRegular() {
+    protected FormComponent<T> createFormComponent(final ScalarModel scalarModel) {
 
         // even though only one of textField and scalarValueEditInlineContainer will ever be visible,
         // am instantiating both to avoid NPEs
         // elsewhere can use Component#isVisibilityAllowed or ScalarModel.getEditStyle() to check which is visible.
 
-        textField = createTextField(ID_SCALAR_VALUE);
-        textField.setOutputMarkupId(true);
-
-        addStandardSemantics();
-
-        //
-        // read-only/dialog edit
-        //
+        formField = createTextField(ID_SCALAR_VALUE);
+        formField.setOutputMarkupId(true);
 
-        final MarkupContainer scalarIfRegularFormGroup = createScalarIfRegularFormGroup();
-        return scalarIfRegularFormGroup;
+        return formField;
     }
 
     @Override
-    protected Component getScalarValueComponent() {
-        return textField;
-    }
-
-    protected MarkupContainer createScalarIfRegularFormGroup() {
-        Fragment textFieldFragment = createTextFieldFragment("scalarValueContainer");
-        final String name = getModel().getFriendlyName();
-        textField.setLabel(Model.of(name));
-
-        final FormGroup formGroup = new FormGroup(ID_SCALAR_IF_REGULAR, this.textField);
-        textFieldFragment.add(this.textField);
-        formGroup.add(textFieldFragment);
-
-        final String labelCaption = getRendering().getLabelCaption(textField);
-        final Label scalarName = createScalarName(ID_SCALAR_NAME, labelCaption);
-
-        getModel()
-        .getDescribedAs()
-        .ifPresent(describedAs->Tooltips.addTooltip(scalarName, describedAs));
-
-        formGroup.add(scalarName);
-
-        return formGroup;
+    protected void onFormGroupCreated(final FormGroup formGroup) {
+        super.onFormGroupCreated(formGroup);
+        formGroup.add(createScalarValueContainer(ID_SCALAR_VALUE_CONTAINER));
+        setTextFieldSizeAndMaxLengthIfSpecified();
     }
 
-    private Fragment createTextFieldFragment(final String id) {
-        return new Fragment(id, createTextFieldFragmentId(), this);
+    protected Component createScalarValueContainer(final String id) {
+        final Fragment textFieldFragment = new Fragment(id, getTextFieldFragmentId(), this);
+        textFieldFragment.add(getFormComponent());
+        return textFieldFragment;
     }
 
-    protected String createTextFieldFragmentId() {
+    protected String getTextFieldFragmentId() {
         return getTextFieldVariant().isSingleLine()
                 ? "text"
                 : "textarea";
@@ -235,14 +203,10 @@ implements TextFieldValueModel.ScalarModelProvider {
         }
     }
 
-    protected void addStandardSemantics() {
-        textField.setRequired(getModel().isRequired());
-        setTextFieldSizeAndMaxLengthIfSpecified();
-        addValidatorForIsisValidation();
-    }
-
     private void setTextFieldSizeAndMaxLengthIfSpecified() {
 
+        val formComponent = getFormComponent();
+
         final Integer maxLength = getMaxLengthOf(getModel());
         Integer typicalLength = getTypicalLenghtOf(getModel());
 
@@ -252,47 +216,17 @@ implements TextFieldValueModel.ScalarModelProvider {
         }
 
         if (typicalLength != null) {
-            textField.add(new AttributeModifier("size", Model.of("" + typicalLength)));
+            formComponent.add(new AttributeModifier("size", Model.of("" + typicalLength)));
         }
 
         if(maxLength != null) {
-            textField.add(new AttributeModifier("maxlength", Model.of("" + maxLength)));
+            formComponent.add(new AttributeModifier("maxlength", Model.of("" + maxLength)));
             if(cls.equals(String.class)) {
-                textField.add(StringValidator.maximumLength(maxLength));
+                formComponent.add(StringValidator.maximumLength(maxLength));
             }
         }
     }
 
-    private void addValidatorForIsisValidation() {
-        val scalarModel = getModel();
-
-        textField.add(new IValidator<T>() {
-            private static final long serialVersionUID = 1L;
-            private transient IsisAppCommonContext commonContext;
-
-            @Override
-            public void validate(final IValidatable<T> validatable) {
-                final T proposedValue = validatable.getValue();
-                final ManagedObject proposedAdapter = objectManager().adapt(proposedValue);
-                final String reasonIfAny = scalarModel.validate(proposedAdapter);
-                if (reasonIfAny != null) {
-                    final ValidationError error = new ValidationError();
-                    error.setMessage(reasonIfAny);
-                    validatable.error(error);
-                }
-            }
-
-            private ObjectManager objectManager() {
-                return getCommonContext().getObjectManager();
-            }
-
-            private IsisAppCommonContext getCommonContext() {
-                return commonContext = CommonContextUtils.computeIfAbsent(commonContext);
-            }
-
-        });
-    }
-
     // --
 
     /**
@@ -309,7 +243,7 @@ implements TextFieldValueModel.ScalarModelProvider {
                 getCompactFragment(CompactType.SPAN),
                 ID_SCALAR_IF_COMPACT,
                 ()->{
-                    val scalarModel = getModel();
+                    val scalarModel = scalarModel();
                     return scalarModel.isCurrentValueAbsent()
                             ? ""
                             : scalarModel.proposedValue().getValueAsParsableText().getValue();
@@ -321,7 +255,7 @@ implements TextFieldValueModel.ScalarModelProvider {
         SPAN
     }
 
-    Fragment getCompactFragment(final CompactType type) {
+    protected Fragment getCompactFragment(final CompactType type) {
         switch (type) {
         case INPUT_CHECKBOX:
             return new Fragment(ID_SCALAR_IF_COMPACT, "compactAsInputCheckbox", ScalarPanelTextFieldAbstract.this);
@@ -336,12 +270,12 @@ implements TextFieldValueModel.ScalarModelProvider {
 
     @Override
     protected InlinePromptConfig getInlinePromptConfig() {
-        return InlinePromptConfig.supportedAndHide(textField);
+        return InlinePromptConfig.supportedAndHide(getFormComponent());
     }
 
     @Override
     protected IModel<String> obtainInlinePromptModel() {
-        IModel<T> model = textField.getModel();
+        IModel<?> model = getFormComponent().getModel();
         // must be "live", for ajax updates.
         return _Casts.uncheckedCast(model);
     }
@@ -378,10 +312,10 @@ implements TextFieldValueModel.ScalarModelProvider {
     protected void onInitializeNotEditable() {
         super.onInitializeNotEditable();
 
-        textField.setEnabled(false);
+        getFormComponent().setEnabled(false);
 
         if(getWicketViewerSettings().isReplaceDisabledTagWithReadonlyTag()) {
-            Wkt.behaviorAddReplaceDisabledTagWithReadonlyTag(textField);
+            Wkt.behaviorAddReplaceDisabledTagWithReadonlyTag(getFormComponent());
         }
 
         clearTooltip();
@@ -391,10 +325,10 @@ implements TextFieldValueModel.ScalarModelProvider {
     protected void onInitializeReadonly(final String disableReason) {
         super.onInitializeReadonly(disableReason);
 
-        textField.setEnabled(false);
+        getFormComponent().setEnabled(false);
 
         if(getWicketViewerSettings().isReplaceDisabledTagWithReadonlyTag()) {
-            Wkt.behaviorAddReplaceDisabledTagWithReadonlyTag(textField);
+            Wkt.behaviorAddReplaceDisabledTagWithReadonlyTag(getFormComponent());
         }
 
         inlinePromptLink.setEnabled(false);
@@ -405,40 +339,40 @@ implements TextFieldValueModel.ScalarModelProvider {
     @Override
     protected void onInitializeEditable() {
         super.onInitializeEditable();
-        textField.setEnabled(true);
+        getFormComponent().setEnabled(true);
         inlinePromptLink.setEnabled(true);
         clearTooltip();
     }
 
     @Override
     protected void onNotEditable(final String disableReason, final Optional<AjaxRequestTarget> target) {
-        textField.setEnabled(false);
+        getFormComponent().setEnabled(false);
         inlinePromptLink.setEnabled(false);
         setTooltip(disableReason);
         target.ifPresent(ajax->{
-            ajax.add(textField);
+            ajax.add(getFormComponent());
             ajax.add(inlinePromptLink);
         });
     }
 
     @Override
     protected void onEditable(final Optional<AjaxRequestTarget> target) {
-        textField.setEnabled(true);
+        getFormComponent().setEnabled(true);
         inlinePromptLink.setEnabled(true);
         clearTooltip();
         target.ifPresent(ajax->{
-            ajax.add(textField);
+            ajax.add(getFormComponent());
             ajax.add(inlinePromptLink);
         });
     }
 
     private void setTooltip(final String tooltip) {
-        Tooltips.addTooltip(textField, tooltip);
+        Tooltips.addTooltip(getFormComponent(), tooltip);
         Tooltips.addTooltip(inlinePromptLink, tooltip);
     }
 
     private void clearTooltip() {
-        Tooltips.clearTooltip(textField);
+        Tooltips.clearTooltip(getFormComponent());
         Tooltips.clearTooltip(inlinePromptLink);
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
index a4f912e..98ad8d7 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithTemporalPicker.java
@@ -54,7 +54,7 @@ extends ScalarPanelTextFieldWithValueSemantics<T>  {
 
 
     @Override
-    protected String createTextFieldFragmentId() {
+    protected String getTextFieldFragmentId() {
         return "date";
     }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelWithFormFieldAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelWithFormFieldAbstract.java
new file mode 100644
index 0000000..88480ca
--- /dev/null
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelWithFormFieldAbstract.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.viewer.wicket.ui.components.scalars;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.validation.IValidatable;
+import org.apache.wicket.validation.IValidator;
+import org.apache.wicket.validation.ValidationError;
+
+import org.apache.isis.core.metamodel.objectmanager.ObjectManager;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+import org.apache.isis.core.runtime.context.IsisAppCommonContext;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.model.util.CommonContextUtils;
+import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
+import org.apache.isis.viewer.wicket.ui.util.Tooltips;
+import org.apache.isis.viewer.wicket.ui.util.Wkt;
+
+import lombok.val;
+
+public abstract class ScalarPanelWithFormFieldAbstract
+extends ScalarPanelAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    protected ScalarPanelWithFormFieldAbstract(final String id, final ScalarModel scalarModel) {
+        super(id, scalarModel);
+    }
+
+    @Override
+    protected final Component getValidationFeedbackReceiver() {
+        return getFormComponent();
+    }
+
+    // -- FORM COMPONENT
+
+    private FormComponent<?> formComponent;
+    protected final FormComponent<?> getFormComponent() { return formComponent; }
+    /**
+     * Builds the component to render the form field.
+     */
+    protected abstract FormComponent<?> createFormComponent(ScalarModel scalarModel);
+
+    // -- REGULAR
+
+    @Override
+    protected final MarkupContainer createComponentForRegular() {
+        val scalarModel = scalarModel();
+
+        formComponent = createFormComponent(scalarModel);
+        formComponent.setLabel(Model.of(scalarModel.getFriendlyName()));
+
+        final FormGroup formGroup = new FormGroup(ID_SCALAR_IF_REGULAR, formComponent);
+        formGroup.add(formComponent);
+
+        formComponent.setRequired(scalarModel.isRequired());
+        if(scalarModel.isRequired()
+                && scalarModel.isEnabled()) {
+            Wkt.cssAppend(formGroup, "mandatory");
+        }
+
+        final String labelCaption = getRendering().getLabelCaption(formComponent);
+        final Label scalarName = createScalarName(ID_SCALAR_NAME, labelCaption);
+        formGroup.add(scalarName);
+
+        scalarModel.getDescribedAs()
+            .ifPresent(describedAs->Tooltips.addTooltip(scalarName, describedAs));
+
+        formComponent.add(createValidator(scalarModel));
+
+        onFormGroupCreated(formGroup);
+
+        return formGroup;
+    }
+
+    // -- HOOKS
+
+    protected void onFormGroupCreated(final FormGroup formGroup) {
+    }
+
+    protected IValidator<Object> createValidator(final ScalarModel scalarModel) {
+        return new IValidator<Object>() {
+            private static final long serialVersionUID = 1L;
+            private transient IsisAppCommonContext commonContext;
+
+            @Override
+            public void validate(final IValidatable<Object> validatable) {
+                final ManagedObject proposedAdapter = objectManager().adapt(validatable.getValue());
+                final String reasonIfAny = scalarModel.validate(proposedAdapter);
+                if (reasonIfAny != null) {
+                    final ValidationError error = new ValidationError();
+                    error.setMessage(reasonIfAny);
+                    validatable.error(error);
+                }
+            }
+
+            private ObjectManager objectManager() {
+                return getCommonContext().getObjectManager();
+            }
+
+            private IsisAppCommonContext getCommonContext() {
+                return commonContext = CommonContextUtils.computeIfAbsent(commonContext);
+            }
+
+        };
+    }
+
+}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
index 3e727a9..f12adad 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/blobclob/IsisBlobOrClobPanelAbstract.java
@@ -28,11 +28,11 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.form.upload.FileUpload;
 import org.apache.wicket.markup.html.form.upload.FileUploadField;
 import org.apache.wicket.markup.html.image.Image;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LambdaModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.request.resource.IResource;
 
@@ -40,7 +40,7 @@ import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.NamedWithMimeType;
 import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelWithFormFieldAbstract;
 import org.apache.isis.viewer.wicket.ui.components.scalars.image.WicketImageUtil;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.isis.viewer.wicket.ui.util.Components;
@@ -51,20 +51,14 @@ import lombok.val;
 import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
 
 public abstract class IsisBlobOrClobPanelAbstract<T extends NamedWithMimeType>
-extends ScalarPanelAbstract {
-
+extends ScalarPanelWithFormFieldAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    private static final String ID_SCALAR_IF_REGULAR = "scalarIfRegular";
     private static final String ID_SCALAR_IF_REGULAR_DOWNLOAD = "scalarIfRegularDownload";
     private static final String ID_FILE_NAME = "fileName";
-    //private static final String ID_FILE_NAME_IF_COMPACT = "fileNameIfCompact";
     private static final String ID_SCALAR_IF_REGULAR_CLEAR = "scalarIfRegularClear";
-    private static final String ID_SCALAR_NAME = "scalarName";
-    private static final String ID_SCALAR_VALUE = "scalarValue";
     private static final String ID_IMAGE = "scalarImage";
-    private static final String ID_SCALAR_IF_COMPACT = "scalarIfCompact";
     private static final String ID_SCALAR_IF_COMPACT_DOWNLOAD = "scalarIfCompactDownload";
 
     private Image wicketImage;
@@ -79,35 +73,49 @@ extends ScalarPanelAbstract {
         EDITABLE, NOT_EDITABLE
     }
 
-    @Override
-    protected FormGroup createComponentForRegular() {
-
-        val friendlyNameModel = LambdaModel.of(()->getModel().getFriendlyName());
+//    @Override
+//    protected FormGroup createComponentForRegular() {
+//
+//        val friendlyNameModel = LambdaModel.of(()->getModel().getFriendlyName());
+//
+//        fileUploadField = createFileUploadField(ID_SCALAR_VALUE);
+//        fileUploadField.setLabel(friendlyNameModel);
+//
+//        final FormGroup scalarIfRegularFormGroup = new FormGroup(ID_SCALAR_IF_REGULAR, fileUploadField);
+//        scalarIfRegularFormGroup.add(fileUploadField);
+//
+//        Wkt.labelAdd(scalarIfRegularFormGroup, ID_SCALAR_NAME, friendlyNameModel);
+//
+//        wicketImage = asWicketImage(ID_IMAGE);
+//        if(wicketImage != null) {
+//            scalarIfRegularFormGroup.addOrReplace(wicketImage);
+//        } else {
+//            Components.permanentlyHide(scalarIfRegularFormGroup, ID_IMAGE);
+//        }
+//
+//        updateFileNameLabel(ID_FILE_NAME, scalarIfRegularFormGroup);
+//        updateDownloadLink(ID_SCALAR_IF_REGULAR_DOWNLOAD, scalarIfRegularFormGroup);
+//
+//        return scalarIfRegularFormGroup;
+//    }
 
+    @Override
+    protected FormComponent<?> createFormComponent(final ScalarModel scalarModel) {
         fileUploadField = createFileUploadField(ID_SCALAR_VALUE);
-        fileUploadField.setLabel(friendlyNameModel);
-
-        final FormGroup scalarIfRegularFormGroup = new FormGroup(ID_SCALAR_IF_REGULAR, fileUploadField);
-        scalarIfRegularFormGroup.add(fileUploadField);
-
-        Wkt.labelAdd(scalarIfRegularFormGroup, ID_SCALAR_NAME, friendlyNameModel);
+        return fileUploadField;
+    }
 
+    @Override
+    protected void onFormGroupCreated(final FormGroup formGroup) {
+        super.onFormGroupCreated(formGroup);
         wicketImage = asWicketImage(ID_IMAGE);
         if(wicketImage != null) {
-            scalarIfRegularFormGroup.addOrReplace(wicketImage);
+            formGroup.addOrReplace(wicketImage);
         } else {
-            Components.permanentlyHide(scalarIfRegularFormGroup, ID_IMAGE);
+            Components.permanentlyHide(formGroup, ID_IMAGE);
         }
-
-        updateFileNameLabel(ID_FILE_NAME, scalarIfRegularFormGroup);
-        updateDownloadLink(ID_SCALAR_IF_REGULAR_DOWNLOAD, scalarIfRegularFormGroup);
-
-        return scalarIfRegularFormGroup;
-    }
-
-    @Override
-    protected Component getScalarValueComponent() {
-        return fileUploadField;
+        updateFileNameLabel(ID_FILE_NAME, formGroup);
+        updateDownloadLink(ID_SCALAR_IF_REGULAR_DOWNLOAD, formGroup);
     }
 
     // //////////////////////////////////////
@@ -237,7 +245,7 @@ extends ScalarPanelAbstract {
             final String disabledReason,
             final Optional<AjaxRequestTarget> target) {
 
-        final MarkupContainer formComponent = (MarkupContainer) getComponentForRegular();
+        final MarkupContainer formComponent = getComponentForRegular();
         sync(formComponent, visibility, editability, disabledReason, target);
 
         // sonar-ignore-on (detects potential NPE, which is a false positive here)
@@ -336,7 +344,7 @@ extends ScalarPanelAbstract {
             final InputFieldEditability editability,
             final Optional<AjaxRequestTarget> target) {
 
-        final MarkupContainer formComponent = (MarkupContainer) getComponentForRegular();
+        final MarkupContainer formComponent = getComponentForRegular();
         formComponent.setOutputMarkupId(true); // enable ajax link
 
         final AjaxLink<Void> ajaxLink = Wkt.linkAdd(formComponent, ID_SCALAR_IF_REGULAR_CLEAR, ajaxTarget->{
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ParentedMarkupPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ParentedMarkupPanel.java
index 2ffa8c8..f325569 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ParentedMarkupPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/ParentedMarkupPanel.java
@@ -21,15 +21,13 @@ package org.apache.isis.viewer.wicket.ui.components.scalars.markup;
 import java.io.Serializable;
 
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.model.Model;
+import org.apache.wicket.model.IModel;
 
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
 import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelTextFieldWithValueSemantics;
 import org.apache.isis.viewer.wicket.ui.components.scalars.TextFieldVariant;
-import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
-import org.apache.isis.viewer.wicket.ui.util.Tooltips;
+
+import lombok.val;
 
 /**
  * Panel for rendering scalars of type {@link org.apache.isis.applib.value.Markup}.
@@ -51,31 +49,13 @@ extends ScalarPanelTextFieldWithValueSemantics<T> {
     }
 
     @Override
-    protected MarkupContainer createScalarIfRegularFormGroup() {
-
-        if(getModel().isEditMode()) {
-            // fallback to text editor
-            return super.createScalarIfRegularFormGroup();
+    protected Component createScalarValueContainer(final String id) {
+        val scalarModel = scalarModel();
+        if(scalarModel.isEditMode()) {
+            // fallback to text area
+            return super.createScalarValueContainer(id);
         }
-
-        final MarkupComponent markupComponent =
-                createMarkupComponent("scalarValueContainer");
-
-        getTextField().setLabel(Model.of(getModel().getFriendlyName()));
-
-        final FormGroup formGroup = new FormGroup(ID_SCALAR_IF_REGULAR, getTextField());
-        formGroup.add(markupComponent);
-
-        final String labelCaption = getRendering().getLabelCaption(getTextField());
-        final Label scalarName = createScalarName(ID_SCALAR_NAME, labelCaption);
-
-        getModel()
-        .getDescribedAs()
-        .ifPresent(describedAs->Tooltips.addTooltip(scalarName, describedAs));
-
-        formGroup.add(scalarName);
-
-        return formGroup;
+        return createMarkupComponent(id);
     }
 
     @Override
@@ -83,6 +63,11 @@ extends ScalarPanelTextFieldWithValueSemantics<T> {
         return createMarkupComponent(ID_SCALAR_IF_COMPACT);
     }
 
+    @Override
+    protected final IModel<String> obtainInlinePromptModel() {
+        return super.toStringConvertingModelOf(getConverter(scalarModel()));
+    }
+
     protected final MarkupComponent createMarkupComponent(final String id) {
         return markupComponentFactory.newMarkupComponent(id, getModel());
     }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/primitive/BooleanPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/primitive/BooleanPanel.java
index f69b5a6..8911319 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/primitive/BooleanPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/primitive/BooleanPanel.java
@@ -22,9 +22,8 @@ import java.util.Optional;
 
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 
@@ -32,9 +31,7 @@ import org.apache.isis.applib.annotation.LabelPosition;
 import org.apache.isis.core.metamodel.facets.objectvalue.labelat.LabelAtFacet;
 import org.apache.isis.viewer.wicket.model.models.BooleanModel;
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
-import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
-import org.apache.isis.viewer.wicket.ui.util.Tooltips;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelWithFormFieldAbstract;
 import org.apache.isis.viewer.wicket.ui.util.Wkt;
 
 import lombok.val;
@@ -45,7 +42,8 @@ import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkboxx.Che
 /**
  * Panel for rendering scalars of type {@link Boolean} or <tt>boolean</tt>.
  */
-public class BooleanPanel extends ScalarPanelAbstract {
+public class BooleanPanel
+extends ScalarPanelWithFormFieldAbstract {
 
     private static final long serialVersionUID = 1L;
 
@@ -56,46 +54,15 @@ public class BooleanPanel extends ScalarPanelAbstract {
     }
 
     @Override
-    protected MarkupContainer createComponentForRegular() {
-
-        val scalarModel = getModel();
-
+    protected FormComponent<Boolean> createFormComponent(final ScalarModel scalarModel) {
         checkBox = Wkt.checkbox(
                 ID_SCALAR_VALUE,
                 BooleanModel.forScalarModel(scalarModel),
                 scalarModel.isRequired(),
                 CheckBoxXConfig.Sizes.lg);
-
-        checkBox.setLabel(Model.of(scalarModel.getFriendlyName()));
-
-        final FormGroup scalarIfRegularFormGroup = new FormGroup(ID_SCALAR_IF_REGULAR, checkBox);
-        scalarIfRegularFormGroup.add(checkBox);
-        if(scalarModel.isRequired()
-                && scalarModel.isEnabled()) {
-            Wkt.cssAppend(scalarIfRegularFormGroup, "mandatory");
-        }
-
-        final Label scalarNameLabel = createScalarName(
-                ID_SCALAR_NAME,
-                getRendering().getLabelCaption(checkBox));
-        scalarIfRegularFormGroup.add(scalarNameLabel);
-
-        scalarModel
-            .getDescribedAs()
-            .ifPresent(describedAs->Tooltips.addTooltip(scalarIfRegularFormGroup, describedAs));
-
-        return scalarIfRegularFormGroup;
-    }
-
-    @Override
-    protected Component getScalarValueComponent() {
         return checkBox;
     }
 
-    /**
-     * Mandatory hook method to build the component to render the model when in
-     * {@link org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract.Rendering#COMPACT compact} format.
-     */
     @Override
     protected Component createComponentForCompact() {
         val checkbox = Wkt.checkbox(
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
index aa8a061..0152e77 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/reference/ReferencePanel.java
@@ -211,7 +211,7 @@ public class ReferencePanel extends ScalarPanelSelectAbstract {
         val adapter = scalarModel.getObject();
 
         // syncLinkWithInput
-        final MarkupContainer componentForRegular = (MarkupContainer) getComponentForRegular();
+        final MarkupContainer componentForRegular = getComponentForRegular();
 
         if(componentForRegular != null) {
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.html b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.html
new file mode 100644
index 0000000..039a718
--- /dev/null
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.html
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+         http://www.apache.org/licenses/LICENSE-2.0
+         
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"  
+      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"  
+      xml:lang="en"  
+      lang="en">
+<body>
+	<wicket:panel>
+		<div class="scalarNameAndValueComponentType"
+			wicket:id="scalarTypeContainer">
+
+			<wicket:container wicket:id="scalarIfRegular"></wicket:container>
+			<wicket:container wicket:id="scalarIfCompact"></wicket:container>
+		</div>
+	</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.java
index e73131a..33d2838 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/tree/ParentedTreePanel.java
@@ -20,19 +20,17 @@ package org.apache.isis.viewer.wicket.ui.components.tree;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.MarkupContainer;
-import org.apache.wicket.behavior.Behavior;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.model.Model;
 
 import org.apache.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelTextFieldTextualAbstract;
-import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelAbstract;
+
+import lombok.val;
 
 /**
  * Immutable tree, hooks into the ScalarPanelTextField without actually using its text field.
  */
 public class ParentedTreePanel
-extends ScalarPanelTextFieldTextualAbstract {
+extends ScalarPanelAbstract {
 
     private static final long serialVersionUID = 1L;
 
@@ -41,44 +39,33 @@ extends ScalarPanelTextFieldTextualAbstract {
     }
 
     @Override
-    protected MarkupContainer createScalarIfRegularFormGroup() {
-
-        if(getModel().isEditMode()) {
-            // fallback to text editor
-            return super.createScalarIfRegularFormGroup();
-        }
-
-        final Component treeComponent = createTreeComponent("scalarValueContainer");
-        final Behavior treeTheme = getTreeThemeProvider().treeThemeFor(super.getModel());
-
-        getTextField().setLabel(Model.of(getModel().getFriendlyName()));
-
-        final FormGroup formGroup = new FormGroup(ID_SCALAR_IF_REGULAR, getTextField());
-        formGroup.add(treeComponent);
-
-        final String labelCaption = getRendering().getLabelCaption(getTextField());
-        final Label scalarName = createScalarName(ID_SCALAR_NAME, labelCaption);
-        formGroup.add(scalarName);
+    protected MarkupContainer createComponentForRegular() {
+        return createTreeComponent(ID_SCALAR_IF_REGULAR);
+    }
 
-        // adds the tree-theme behavior to the container, that contains the tree component
-        return (MarkupContainer) formGroup.add(treeTheme);
+    @Override
+    protected MarkupContainer createComponentForCompact() {
+        return createTreeComponent(ID_SCALAR_IF_COMPACT);
     }
 
     @Override
-    protected Component createComponentForCompact() {
-        final Component tree = createTreeComponent(ID_SCALAR_IF_COMPACT);
+    protected InlinePromptConfig getInlinePromptConfig() {
+        return InlinePromptConfig.notSupported();
+    }
 
-        // adds the tree-theme behavior to the tree component
-        //TODO [2088] not tested yet: if tree renders without applying the theme, behavior needs
-        // to go to a container up the hierarchy
-        final Behavior treeTheme = getTreeThemeProvider().treeThemeFor(super.getModel());
-        return tree.add(treeTheme);
+    @Override
+    protected Component getValidationFeedbackReceiver() {
+        return null;
     }
 
     // -- HELPER
 
-    private Component createTreeComponent(final String id) {
-        return IsisToWicketTreeAdapter.adapt(id, getModel());
+    private MarkupContainer createTreeComponent(final String id) {
+        val scalarModel = scalarModel();
+        val tree = (MarkupContainer) IsisToWicketTreeAdapter.adapt(id, scalarModel);
+        // adds the tree-theme behavior to the tree component
+        tree.add(getTreeThemeProvider().treeThemeFor(scalarModel));
+        return tree;
     }
 
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
index a632641..114184b 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/util/Wkt.java
@@ -29,6 +29,7 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.behavior.AttributeAppender;
 import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
 import org.apache.wicket.markup.ComponentTag;
@@ -696,4 +697,12 @@ public class Wkt {
                 : String.format("Wicket.Event.publish(Isis.Topic.%s)", topic.name());
     }
 
+    // -- TABBING UTILITY
+
+    public static void noTabbing(final @Nullable Component component) {
+        if(component != null) {
+            component.add(new AttributeAppender("tabindex", "-1"));
+        }
+    }
+
 }