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/02 16:09:55 UTC

[isis] branch master updated: ISIS-2877: prepare multiline string panels to support converters

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 bd4cc04  ISIS-2877: prepare multiline string panels to support converters
bd4cc04 is described below

commit bd4cc04e02a7323a08e278906b321f47872a7947
Author: Andi Huber <ah...@apache.org>
AuthorDate: Wed Mar 2 17:05:56 2022 +0100

    ISIS-2877: prepare multiline string panels to support converters
---
 .../isis/core/metamodel/spec/ManagedObjects.java   |  2 +-
 .../valuesemantics/MarkupValueSemantics.java       |  9 ++-
 .../wkt/components/AsciiDocPanelFactoriesWkt.java  |  4 +-
 .../wkt/components/MarkdownPanelFactoriesWkt.java  |  4 +-
 .../ListeningMarkupPanelFactoriesForWicket.java    |  4 +-
 .../scalars/ScalarPanelTextFieldAbstract.java      | 78 ++++++++++++++++++++--
 .../ScalarPanelTextFieldTextualAbstract.java       |  8 ++-
 .../ScalarPanelTextFieldWithValueSemantics.java    |  8 +++
 .../ui/components/scalars/TextFieldVariant.java    | 26 ++++++++
 .../scalars/markup/MarkupPanelFactories.java       | 22 +++---
 .../scalars/markup/ParentedMarkupPanel.java        | 12 ++--
 .../scalars/string/MultiLineStringPanel.java       | 72 +-------------------
 .../org/apache/isis/viewer/wicket/ui/util/Wkt.java | 26 ++++++++
 13 files changed, 175 insertions(+), 100 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
index 0e079aa..652698b 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/spec/ManagedObjects.java
@@ -154,7 +154,7 @@ public final class ManagedObjects {
             }
             val upperBound = ClassUtils.resolvePrimitiveIfNecessary(elementType.getCorrespondingClass());
             val objectActualType = ClassUtils.resolvePrimitiveIfNecessary(object.getSpecification().getCorrespondingClass());
-            throw _Exceptions.illegalArgument("Object has incompatible type %s,"
+            throw _Exceptions.illegalArgument("Object has incompatible type %s, "
                     + "must be an instance of %s.",
                     objectActualType.getName(),
                     upperBound.getName());
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
index 3777b2f..0689dcb 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuesemantics/MarkupValueSemantics.java
@@ -64,8 +64,13 @@ implements
     // -- RENDERER
 
     @Override
-    public String titlePresentation(final ValueSemanticsProvider.Context context, final Markup value) {
-        return value != null? value.asHtml(): "[null]";
+    public String titlePresentation(final Context context, final Markup value) {
+        return render(value, Markup::toString);
+    }
+
+    @Override
+    public String htmlPresentation(final ValueSemanticsProvider.Context context, final Markup adoc) {
+        return render(adoc, Markup::asHtml);
     }
 
     // -- PARSER
diff --git a/valuetypes/asciidoc/ui/wicket/src/main/java/org/apache/isis/valuetypes/asciidoc/ui/wkt/components/AsciiDocPanelFactoriesWkt.java b/valuetypes/asciidoc/ui/wicket/src/main/java/org/apache/isis/valuetypes/asciidoc/ui/wkt/components/AsciiDocPanelFactoriesWkt.java
index 55900b0..821b9cf 100644
--- a/valuetypes/asciidoc/ui/wicket/src/main/java/org/apache/isis/valuetypes/asciidoc/ui/wkt/components/AsciiDocPanelFactoriesWkt.java
+++ b/valuetypes/asciidoc/ui/wicket/src/main/java/org/apache/isis/valuetypes/asciidoc/ui/wkt/components/AsciiDocPanelFactoriesWkt.java
@@ -39,7 +39,7 @@ public class AsciiDocPanelFactoriesWkt {
     // -- PARENTED
 
     @Component
-    public static class Parented extends MarkupPanelFactories.ParentedAbstract {
+    public static class Parented extends MarkupPanelFactories.ParentedAbstract<AsciiDoc> {
         private static final long serialVersionUID = 1L;
 
         public Parented() {
@@ -58,7 +58,7 @@ public class AsciiDocPanelFactoriesWkt {
     // -- STANDALONE
 
     @Component
-    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract {
+    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract<AsciiDoc> {
         private static final long serialVersionUID = 1L;
 
         public Standalone() {
diff --git a/valuetypes/markdown/ui/wicket/src/main/java/org/apache/isis/valuetypes/markdown/ui/wkt/components/MarkdownPanelFactoriesWkt.java b/valuetypes/markdown/ui/wicket/src/main/java/org/apache/isis/valuetypes/markdown/ui/wkt/components/MarkdownPanelFactoriesWkt.java
index 9707fea..fa2d043 100644
--- a/valuetypes/markdown/ui/wicket/src/main/java/org/apache/isis/valuetypes/markdown/ui/wkt/components/MarkdownPanelFactoriesWkt.java
+++ b/valuetypes/markdown/ui/wicket/src/main/java/org/apache/isis/valuetypes/markdown/ui/wkt/components/MarkdownPanelFactoriesWkt.java
@@ -39,7 +39,7 @@ public class MarkdownPanelFactoriesWkt {
     // -- PARENTED
 
     @Component
-    public static class Parented extends MarkupPanelFactories.ParentedAbstract {
+    public static class Parented extends MarkupPanelFactories.ParentedAbstract<Markdown> {
         private static final long serialVersionUID = 1L;
 
         public Parented() {
@@ -58,7 +58,7 @@ public class MarkdownPanelFactoriesWkt {
     // -- STANDALONE
 
     @Component
-    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract {
+    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract<Markdown> {
         private static final long serialVersionUID = 1L;
 
         public Standalone() {
diff --git a/valuetypes/sse/ui/wicket/src/main/java/org/apache/isis/valuetypes/sse/ui/wkt/markup/ListeningMarkupPanelFactoriesForWicket.java b/valuetypes/sse/ui/wicket/src/main/java/org/apache/isis/valuetypes/sse/ui/wkt/markup/ListeningMarkupPanelFactoriesForWicket.java
index c439d9d..9b7b692 100644
--- a/valuetypes/sse/ui/wicket/src/main/java/org/apache/isis/valuetypes/sse/ui/wkt/markup/ListeningMarkupPanelFactoriesForWicket.java
+++ b/valuetypes/sse/ui/wicket/src/main/java/org/apache/isis/valuetypes/sse/ui/wkt/markup/ListeningMarkupPanelFactoriesForWicket.java
@@ -41,7 +41,7 @@ public class ListeningMarkupPanelFactoriesForWicket {
     // -- PARENTED
 
     @Component
-    public static class Parented extends MarkupPanelFactories.ParentedAbstract {
+    public static class Parented extends MarkupPanelFactories.ParentedAbstract<Markup> {
         private static final long serialVersionUID = 1L;
 
         public Parented() {
@@ -71,7 +71,7 @@ public class ListeningMarkupPanelFactoriesForWicket {
     // -- STANDALONE
 
     @Component
-    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract {
+    public static class Standalone extends MarkupPanelFactories.StandaloneAbstract<Markup> {
         private static final long serialVersionUID = 1L;
 
         public Standalone() {
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 7e423c5..93d5311 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
@@ -28,6 +28,7 @@ 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.TextArea;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
@@ -38,8 +39,10 @@ import org.apache.wicket.validation.ValidationError;
 import org.apache.wicket.validation.validator.StringValidator;
 
 import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 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;
@@ -85,9 +88,24 @@ implements TextFieldValueModel.ScalarModelProvider {
     @Getter(value = AccessLevel.PROTECTED)
     private AbstractTextComponent<T> textField;
 
-    protected ScalarPanelTextFieldAbstract(final String id, final ScalarModel scalarModel, final Class<T> cls) {
+    @Getter(value = AccessLevel.PROTECTED)
+    private TextFieldVariant textFieldVariant;
+
+    protected ScalarPanelTextFieldAbstract(
+            final String id,
+            final ScalarModel scalarModel,
+            final Class<T> cls) {
+        this(id, scalarModel, cls, TextFieldVariant.SINGLE_LINE);
+    }
+
+    protected ScalarPanelTextFieldAbstract(
+            final String id,
+            final ScalarModel scalarModel,
+            final Class<T> cls,
+            final TextFieldVariant textFieldVariant) {
         super(id, scalarModel);
         this.cls = cls;
+        this.textFieldVariant = textFieldVariant;
     }
 
     // -- CONVERSION
@@ -112,8 +130,11 @@ implements TextFieldValueModel.ScalarModelProvider {
      * TextField, with converter.
      */
     protected AbstractTextComponent<T> createTextField(final String id) {
-        return Wkt.textFieldWithConverter(
-                id, newTextFieldValueModel(), cls, getConverter(getModel()));
+        return getTextFieldVariant().isSingleLine()
+                ? Wkt.textFieldWithConverter(
+                        id, newTextFieldValueModel(), cls, getConverter(getModel()))
+                : setRowsAndMaxLengthAttributesOn(Wkt.textAreaWithConverter(
+                        id, newTextFieldValueModel(), cls, getConverter(getModel())));
     }
 
     protected final TextFieldValueModel<T> newTextFieldValueModel() {
@@ -173,7 +194,19 @@ implements TextFieldValueModel.ScalarModelProvider {
     }
 
     protected String createTextFieldFragmentId() {
-        return "text";
+        return getTextFieldVariant().isSingleLine()
+                ? "text"
+                : "textarea";
+    }
+
+    @Override
+    protected String obtainInlinePromptLinkCssIfAny() {
+        return getTextFieldVariant().isSingleLine()
+                ? super.obtainInlinePromptLinkCssIfAny()
+                /* Most other components require 'form-control form-control-sm' on the owning inline prompt link.
+                 * For TextArea, however, this instead appears on the TextArea itself.
+                 */
+                : null;
     }
 
     /**
@@ -185,9 +218,21 @@ implements TextFieldValueModel.ScalarModelProvider {
             final String id,
             final IModel<String> inlinePromptModel) {
 
-        val fragment = Wkt.fragmentAddNoTab(this, id, "textInlinePrompt");
-        Wkt.labelAdd(fragment, "scalarValue", inlinePromptModel);
-        return fragment;
+        switch(getTextFieldVariant()) {
+        case SINGLE_LINE:{
+            val fragment = Wkt.fragmentAddNoTab(this, id, "textInlinePrompt");
+            Wkt.labelAdd(fragment, "scalarValue", inlinePromptModel);
+            return fragment;
+        }
+        case MULTI_LINE:{
+            val fragment = new Fragment(id, "textareaInlinePrompt", this);
+            val inlinePromptTextArea = Wkt.textAreaAddNoTab(fragment, "scalarValue", inlinePromptModel);
+            setRowsAndMaxLengthAttributesOn(inlinePromptTextArea);
+            return fragment;
+        }
+        default:
+            throw _Exceptions.unmatchedCase(getTextFieldVariant());
+        }
     }
 
     protected void addStandardSemantics() {
@@ -413,6 +458,25 @@ implements TextFieldValueModel.ScalarModelProvider {
                 .orElse(null);
     }
 
+    private Component setAttribute(final TextArea<?> textField, final String attributeName, final int i) {
+        return textField.add(AttributeModifier.replace(attributeName, ""+i));
+    }
+
+    protected <X> TextArea<X> setRowsAndMaxLengthAttributesOn(final TextArea<X> textArea) {
+        val multiLineFacet = getModel().getFacet(MultiLineFacet.class);
+        if(multiLineFacet != null) {
+            setAttribute(textArea, "rows", multiLineFacet.numberOfLines());
+        }
+
+        val maxLength = getMaxLengthOf(getModel());
+        if(maxLength != null) {
+            // in conjunction with javascript in jquery.isis.wicket.viewer.js
+            // see http://stackoverflow.com/questions/4459610/set-maxlength-in-html-textarea
+            setAttribute(textArea, "maxlength", maxLength);
+        }
+        return textArea;
+    }
+
 
 }
 
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldTextualAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldTextualAbstract.java
index b5eb5c0..55593c0 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldTextualAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldTextualAbstract.java
@@ -36,10 +36,16 @@ extends ScalarPanelTextFieldAbstract<String> {
 
     private static final long serialVersionUID = 1L;
 
-    protected ScalarPanelTextFieldTextualAbstract(final String id, final ScalarModel scalarModel) {
+    protected ScalarPanelTextFieldTextualAbstract(
+            final String id, final ScalarModel scalarModel) {
         super(id, scalarModel, String.class);
     }
 
+    protected ScalarPanelTextFieldTextualAbstract(
+            final String id, final ScalarModel scalarModel, final TextFieldVariant textFieldVariant) {
+        super(id, scalarModel, String.class, textFieldVariant);
+    }
+
     @Override
     protected final IConverter<String> getConverter(
             final @NonNull ObjectFeature propOrParam,
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithValueSemantics.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithValueSemantics.java
index bb72914..a9a5230 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithValueSemantics.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/ScalarPanelTextFieldWithValueSemantics.java
@@ -47,6 +47,14 @@ extends ScalarPanelTextFieldAbstract<T> {
         super(id, scalarModel, type);
     }
 
+    public ScalarPanelTextFieldWithValueSemantics(
+            final String id,
+            final ScalarModel scalarModel,
+            final Class<T> type,
+            final TextFieldVariant textFieldVariant) {
+        super(id, scalarModel, type, textFieldVariant);
+    }
+
     @Override
     protected final IConverter<T> getConverter(
             final @NonNull ObjectFeature propOrParam,
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/TextFieldVariant.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/TextFieldVariant.java
new file mode 100644
index 0000000..4d12c5a
--- /dev/null
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/TextFieldVariant.java
@@ -0,0 +1,26 @@
+/*
+ *  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;
+
+public enum TextFieldVariant {
+    SINGLE_LINE,
+    MULTI_LINE;
+    public boolean isSingleLine() { return this==SINGLE_LINE; }
+    public boolean isMultiLine() { return this==MULTI_LINE; }
+}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
index d342b31..32c3e4c 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/markup/MarkupPanelFactories.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.scalars.markup;
 
+import java.io.Serializable;
+
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 
@@ -47,12 +49,14 @@ public class MarkupPanelFactories {
 
     // -- PARENTED (ABSTRACT)
 
-    public static abstract class ParentedAbstract extends ComponentFactoryAbstract {
+    public static abstract class ParentedAbstract<T extends Serializable>
+    extends ComponentFactoryAbstract {
+
         private static final long serialVersionUID = 1L;
 
-        private final Class<?> valueType;
+        private final Class<T> valueType;
 
-        public ParentedAbstract(final Class<?> valueType) {
+        public ParentedAbstract(final Class<T> valueType) {
             super(ComponentType.SCALAR_NAME_AND_VALUE, ParentedMarkupPanel.class);
             this.valueType = valueType;
         }
@@ -73,7 +77,7 @@ public class MarkupPanelFactories {
 
         @Override
         public final Component createComponent(final String id, final IModel<?> model) {
-            return new ParentedMarkupPanel(id, (ScalarModel) model, this::newMarkupComponent);
+            return new ParentedMarkupPanel<T>(id, (ScalarModel) model, valueType, this::newMarkupComponent);
         }
 
         protected abstract MarkupComponent newMarkupComponent(String id, ScalarModel model);
@@ -82,12 +86,12 @@ public class MarkupPanelFactories {
 
     // -- STANDALONE (ABSTRACT)
 
-    public static abstract class StandaloneAbstract extends ComponentFactoryAbstract {
+    public static abstract class StandaloneAbstract<T> extends ComponentFactoryAbstract {
         private static final long serialVersionUID = 1L;
 
-        private final Class<?> valueType;
+        private final Class<T> valueType;
 
-        public StandaloneAbstract(final Class<?> valueType) {
+        public StandaloneAbstract(final Class<T> valueType) {
             super(ComponentType.VALUE, StandaloneMarkupPanel.class);
             this.valueType = valueType;
         }
@@ -117,7 +121,7 @@ public class MarkupPanelFactories {
 
     // -- CONCRETE COMPONENT FACTORY - PARENTED
 
-    static class Parented extends ParentedAbstract {
+    static class Parented extends ParentedAbstract<Markup> {
         private static final long serialVersionUID = 1L;
 
         public Parented() {
@@ -135,7 +139,7 @@ public class MarkupPanelFactories {
 
     // -- CONCRETE COMPONENT FACTORY - STANDALONE
 
-    static class Standalone extends StandaloneAbstract {
+    static class Standalone extends StandaloneAbstract<Markup> {
         private static final long serialVersionUID = 1L;
 
         public Standalone() {
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 7b68cac..2ffa8c8 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
@@ -18,20 +18,24 @@
  */
 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.isis.viewer.wicket.model.models.ScalarModel;
-import org.apache.isis.viewer.wicket.ui.components.scalars.string.MultiLineStringPanel;
+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;
 
 /**
  * Panel for rendering scalars of type {@link org.apache.isis.applib.value.Markup}.
  */
-public class ParentedMarkupPanel extends MultiLineStringPanel {
+public class ParentedMarkupPanel<T extends Serializable>
+extends ScalarPanelTextFieldWithValueSemantics<T> {
 
     private static final long serialVersionUID = 1L;
     private final transient MarkupComponentFactory<ScalarModel> markupComponentFactory;
@@ -39,9 +43,10 @@ public class ParentedMarkupPanel extends MultiLineStringPanel {
     public ParentedMarkupPanel(
             final String id,
             final ScalarModel scalarModel,
+            final Class<T> valueType,
             final MarkupComponentFactory<ScalarModel> markupComponentFactory) {
 
-        super(id, scalarModel);
+        super(id, scalarModel, valueType, TextFieldVariant.MULTI_LINE);
         this.markupComponentFactory = markupComponentFactory;
     }
 
@@ -81,5 +86,4 @@ public class ParentedMarkupPanel extends MultiLineStringPanel {
     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/string/MultiLineStringPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/MultiLineStringPanel.java
index f0ab578..ee9b308 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/MultiLineStringPanel.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/string/MultiLineStringPanel.java
@@ -18,21 +18,9 @@
  */
 package org.apache.isis.viewer.wicket.ui.components.scalars.string;
 
-import org.apache.wicket.AttributeModifier;
-import org.apache.wicket.Component;
-import org.apache.wicket.markup.html.form.AbstractTextComponent;
-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.isis.core.metamodel.facets.SingleIntValueFacet;
-import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet;
-import org.apache.isis.core.metamodel.facets.objectvalue.multiline.MultiLineFacet;
 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.util.Wkt;
-
-import lombok.val;
+import org.apache.isis.viewer.wicket.ui.components.scalars.TextFieldVariant;
 
 /**
  * Panel for rendering MultiLine scalars of type String
@@ -43,63 +31,7 @@ extends ScalarPanelTextFieldTextualAbstract {
     private static final long serialVersionUID = 1L;
 
     public MultiLineStringPanel(final String id, final ScalarModel scalarModel) {
-        super(id, scalarModel);
-    }
-
-    @Override
-    protected AbstractTextComponent<String> createTextField(final String id) {
-        val textArea = new TextArea<String>(id, newTextFieldValueModel());
-        setRowsAndMaxLengthAttributesOn(textArea);
-        return textArea;
-    }
-
-    @Override
-    protected String createTextFieldFragmentId() {
-        return "textarea";
-    }
-
-    @Override
-    protected Component createInlinePromptComponent(
-            final String id,
-            final IModel<String> inlinePromptModel) {
-        val fragment = new Fragment(id, "textareaInlinePrompt", this);
-        val inlinePromptTextArea = Wkt.textAreaAddNoTab(fragment, "scalarValue", inlinePromptModel);
-        setRowsAndMaxLengthAttributesOn(inlinePromptTextArea);
-        return fragment;
-    }
-
-    /**
-     * Most other components require 'form-control form-control-sm' on the owning inline prompt link.
-     * For this component, however, which uses a textarea, this instead appears on the textarea itself.
-     */
-    @Override
-    protected String obtainInlinePromptLinkCssIfAny() {
-        return null;
-    }
-
-    // -- HELPER
-
-    private void setRowsAndMaxLengthAttributesOn(final TextArea<String> textField) {
-        val multiLineFacet = getModel().getFacet(MultiLineFacet.class);
-        if(multiLineFacet != null) {
-            setAttribute(textField, "rows", multiLineFacet.numberOfLines());
-        }
-
-        val maxLength = getValueOf(getModel(), MaxLengthFacet.class);
-        if(maxLength != null) {
-            // in conjunction with javascript in jquery.isis.wicket.viewer.js
-            // see http://stackoverflow.com/questions/4459610/set-maxlength-in-html-textarea
-            setAttribute(textField, "maxlength", maxLength);
-        }
-    }
-
-    private Component setAttribute(final TextArea<String> textField, final String attributeName, final int i) {
-        return textField.add(AttributeModifier.replace(attributeName, ""+i));
-    }
-
-    private static Integer getValueOf(final ScalarModel model, final Class<? extends SingleIntValueFacet> facetType) {
-        final SingleIntValueFacet facet = model.getFacet(facetType);
-        return facet != null ? facet.value() : null;
+        super(id, scalarModel, TextFieldVariant.MULTI_LINE);
     }
 
 }
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 bfc4891..a632641 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
@@ -570,6 +570,32 @@ public class Wkt {
         return add(container, textAreaNoTab(id, textModel));
     }
 
+    /**
+     * @param converter - if {@code null} returns {@link TextArea} using Wicket's default converters.
+     */
+    public <T> TextArea<T> textAreaWithConverter(
+            final String id, final IModel<T> model, final Class<T> type,
+            final @Nullable IConverter<T> converter) {
+        return converter!=null
+            ? new TextArea<T>(id, model) {
+                    private static final long serialVersionUID = 1L;
+                    @SuppressWarnings("unchecked")
+                    @Override public <C> IConverter<C> getConverter(final Class<C> cType) {
+                        return cType == type
+                                ? (IConverter<C>) converter
+                                : super.getConverter(cType);}
+                    @Override public void error(final IValidationError error) {
+                        if(error instanceof ValidationError) {
+                            // use plain error message from ConversionException, circumventing resource bundles.
+                            this.error(((ValidationError)error).getMessage());
+                        } else {
+                            super.error(error);
+                        }
+                    }
+                }
+            : new TextArea<T>(id, model);
+    }
+
     // -- TEXT FIELD
 
     /**