You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/01/03 15:50:00 UTC

[isis] branch ISIS-2476 created (now cd5b3fe)

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

danhaywood pushed a change to branch ISIS-2476
in repository https://gitbox.apache.org/repos/asf/isis.git.


      at cd5b3fe  ISIS-2476: improves the custom ui example a little

This branch includes the following new commits:

     new f1075c1  ISIS-2476: removes EntityLinksSelectorPanel, adds example MyEntityPanel (wip)
     new d2b9d48  ISIS-2476 - fleshes out custom UI example
     new 9a1b9e4  ISIS-2476: renames classes
     new cd5b3fe  ISIS-2476: improves the custom ui example a little

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[isis] 03/04: ISIS-2476: renames classes

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2476
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 9a1b9e41ea858260187bfa1726e7cd0bd7c6a27f
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Dec 30 07:32:35 2020 +0000

    ISIS-2476: renames classes
---
 .../domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java  | 2 +-
 .../domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java    | 8 +++-----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
index 817b2c2..aaf28ba 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
@@ -34,7 +34,7 @@ public class CustomUiMenu {
             cssClassFa="fa-globe",
             describedAs="Opens a Custom UI page displaying a map"
     )
-    public CustomUiVm whereInTheWorld(
+    public CustomUiVm customUiVm(
             @Latitude final String latitude,
             @Longitude final String longitude,
             @PositiveNumber final int scale
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
index 1688807..669f2e7 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
@@ -18,6 +18,8 @@
  */
 package demoapp.dom.ui.custom;
 
+import java.io.Serializable;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -36,11 +38,7 @@ import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
 @XmlType
 @XmlAccessorType(XmlAccessType.FIELD)
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.CustomUiVm")
-public class CustomUiVm implements HasAsciiDocDescription {
-
-    public String title() {
-        return "Custom UI";
-    }
+public class CustomUiVm implements HasAsciiDocDescription, Serializable {
 
     @Latitude
     @Getter @Setter


[isis] 01/04: ISIS-2476: removes EntityLinksSelectorPanel, adds example MyEntityPanel (wip)

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2476
in repository https://gitbox.apache.org/repos/asf/isis.git

commit f1075c1303a0a0c7934c985ed74da8df43cfbbd4
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Wed Dec 16 09:04:54 2020 +0000

    ISIS-2476: removes EntityLinksSelectorPanel, adds example MyEntityPanel (wip)
---
 examples/demo/wicket/pom.xml                       |  14 +
 .../java/demoapp/webapp/wicket/DemoAppWicket.java  |  12 +-
 .../webapp/wicket/customview/MyEntityPanel.html    |  28 ++
 .../webapp/wicket/customview/MyEntityPanel.java    |  56 +++
 .../wicket/customview/MyEntityPanelFactory.java    |  34 +-
 .../selector/links/EntityLinksSelectorPanel.html   |  58 ----
 .../selector/links/EntityLinksSelectorPanel.java   | 384 ---------------------
 .../bs3/Bs3GridPanelFactory.java}                  |  32 +-
 .../ComponentFactoryRegistrarDefault.java          |  15 +-
 9 files changed, 137 insertions(+), 496 deletions(-)

diff --git a/examples/demo/wicket/pom.xml b/examples/demo/wicket/pom.xml
index b01731f..d8e778e 100644
--- a/examples/demo/wicket/pom.xml
+++ b/examples/demo/wicket/pom.xml
@@ -70,6 +70,20 @@
 			<type>pom</type>
 		</dependency>
 
+		<!-- Wicket viewer components -->
+
+		<dependency>
+			<groupId>org.apache.isis.viewer</groupId>
+			<artifactId>isis-viewer-wicket-ui</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.isis.viewer</groupId>
+			<artifactId>isis-viewer-wicket-viewer</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
+
 		<!-- EXTENSIONS -->
 
 		<dependency>
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
index 917f6a9..1216076 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
@@ -18,11 +18,6 @@
  */
 package demoapp.webapp.wicket;
 
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
-import org.springframework.context.annotation.Import;
-
 import org.apache.isis.extensions.viewer.wicket.pdfjs.ui.IsisModuleExtPdfjsUi;
 import org.apache.isis.valuetypes.asciidoc.metamodel.IsisModuleValAsciidocMetaModel;
 import org.apache.isis.valuetypes.asciidoc.persistence.jdo.dn5.IsisModuleValAsciidocPersistenceJdoDn5;
@@ -31,8 +26,13 @@ import org.apache.isis.valuetypes.markdown.persistence.jdo.dn5.IsisModuleValMark
 import org.apache.isis.valuetypes.markdown.ui.wkt.IsisModuleValMarkdownUiWkt;
 import org.apache.isis.valuetypes.sse.ui.wkt.IsisModuleValSseUiWkt;
 import org.apache.isis.viewer.wicket.viewer.IsisModuleViewerWicketViewer;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Import;
 
 import demoapp.web.DemoAppManifest;
+import demoapp.webapp.wicket.customview.MyEntityPanelFactory;
 
 /**
  * Bootstrap the application.
@@ -55,6 +55,8 @@ import demoapp.web.DemoAppManifest;
     IsisModuleValAsciidocPersistenceJdoDn5.class,
     IsisModuleValMarkdownPersistenceJdoDn5.class,
 
+    // @Component's
+    MyEntityPanelFactory.class
 })
 //@Log4j2
 public class DemoAppWicket extends SpringBootServletInitializer {
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
new file mode 100644
index 0000000..0e9b129
--- /dev/null
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
@@ -0,0 +1,28 @@
+<?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.
+-->
+<html xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:panel>
+    <div class="myEntityPanel">
+        <div><p>MY CUSTOM ENTITY</p></div>
+    </div>
+</wicket:panel>
+</body>
+</html>
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
new file mode 100644
index 0000000..13c2911
--- /dev/null
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
@@ -0,0 +1,56 @@
+/*
+ *  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 demoapp.webapp.wicket.customview;
+
+import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.ui.ComponentFactory;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+
+public class MyEntityPanel extends PanelAbstract<EntityModel>  {
+
+
+    private static final long serialVersionUID = 1L;
+
+
+    public MyEntityPanel(
+            final String id,
+            final EntityModel model,
+            final ComponentFactory componentFactory) {
+        super(id, model);
+    }
+
+
+    @Override
+    public UiHintContainer getUiHintContainer() {
+        // disables hinting by this component
+        return null;
+    }
+
+
+    /**
+     * Build UI only after added to parent.
+     */
+    @Override
+    public void onInitialize() {
+        super.onInitialize();
+    }
+
+}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
similarity index 50%
copy from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java
copy to examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
index c07efe0..2859bec 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
@@ -1,23 +1,4 @@
-/*
- *  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.entity.selector.links;
+package demoapp.webapp.wicket.customview;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
@@ -27,22 +8,19 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Grid;
 import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
-import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
 import org.apache.isis.viewer.wicket.ui.components.layout.bs3.BS3GridPanel;
 
 import lombok.val;
 
-/**
- * {@link ComponentFactory} for {@link EntityLinksSelectorPanel}.
- */
-public class EntityLinksSelectorPanelFactory extends EntityComponentFactoryAbstract {
+@org.springframework.stereotype.Component
+public class MyEntityPanelFactory  extends EntityComponentFactoryAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    public EntityLinksSelectorPanelFactory() {
-        super(ComponentType.ENTITY, EntityLinksSelectorPanel.class);
+    public MyEntityPanelFactory() {
+        super(ComponentType.ENTITY, MyEntityPanel.class);
     }
 
     @Override
@@ -60,6 +38,6 @@ public class EntityLinksSelectorPanelFactory extends EntityComponentFactoryAbstr
                 return new BS3GridPanel(id, entityModel, bs3Grid);
             }
         }
-        return new EntityLinksSelectorPanel(id, entityModel, this);
+        return new MyEntityPanel(id, entityModel, this);
     }
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.html b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.html
deleted file mode 100644
index b9196d6..0000000
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<?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.
--->
-<html xmlns:wicket="http://wicket.apache.org">
-<body>
-<wicket:panel>
-    <div class="linksSelectorPanel">
-        <div class="btn-group viewLinks" wicket:id="views">
-            <button type="button" class="btn btn-xs btn-info">
-                <span wicket:id="viewButtonIcon" class="ViewLinkItem"></span>
-                <span wicket:id="viewButtonTitle" class="ViewLinkItemTitle"></span>
-            </button>
-            <button type="button" class="btn btn-xs btn-info dropdown-toggle" data-toggle="dropdown">
-                <span class="caret"></span>
-            </button>
-            <ul wicket:id="viewList" class="dropdown-menu dropdown-menu-right" role="menu">
-                <li wicket:id="viewItem" class="viewItem">
-                    <a href="#" wicket:id="viewLink">
-                        <span wicket:id="viewItemIcon" class="ViewLinkItem"></span> <span wicket:id="viewItemTitle" class="ViewLinkItemTitle">[link title]</span>
-                    </a>
-                </li>
-            </ul>
-        </div>
-
-        <span wicket:id="additionalLinks"></span>
-
-        <div class="views">
-            <div wicket:id="entity-0" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-1" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-2" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-3" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-4" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-5" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-6" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-7" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-8" class="entityLinksSelectorPanel entityComponentType"></div>
-            <div wicket:id="entity-9" class="entityLinksSelectorPanel entityComponentType"></div>
-        </div>
-    </div>
-</wicket:panel>
-</body>
-</html>
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
deleted file mode 100644
index 62202cf..0000000
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- *  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.entity.selector.links;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-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.ajax.markup.html.AjaxLink;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.link.AbstractLink;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-
-import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.primitives._Ints;
-import org.apache.isis.core.metamodel.commons.StringExtensions;
-import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
-import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
-import org.apache.isis.viewer.wicket.model.links.LinksProvider;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
-import org.apache.isis.viewer.wicket.ui.CollectionContentsAsFactory;
-import org.apache.isis.viewer.wicket.ui.ComponentFactory;
-import org.apache.isis.viewer.wicket.ui.ComponentType;
-import org.apache.isis.viewer.wicket.ui.components.actionmenu.entityactions.AdditionalLinksPanel;
-import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
-import org.apache.isis.viewer.wicket.ui.util.Components;
-import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
-
-import lombok.val;
-
-import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
-
-/**
- * Provides a list of links for selecting other views that support
- * {@link ComponentType#ENTITY} with a backing {@link EntityModel}.
- *
- * <p>
- *     TODO: this code could be simplified
- *     (pushed down common code here and for the CollectionsSelectorPanel in order to do so);
- *     haven't simplified this yet because currently there is only one view, so the markup
- *     rendered by this component 'collapses' to just show that underlying view.
- * </p>
- */
-public class EntityLinksSelectorPanel extends PanelAbstract<EntityModel>  {
-
-
-    private static final long serialVersionUID = 1L;
-
-    private static final int MAX_NUM_UNDERLYING_VIEWS = 10;
-
-    private static final String ID_ADDITIONAL_LINKS = "additionalLinks";
-
-    private static final String ID_VIEWS = "views";
-    private static final String ID_VIEW_LIST = "viewList";
-    private static final String ID_VIEW_LINK = "viewLink";
-    private static final String ID_VIEW_ITEM = "viewItem";
-    private static final String ID_VIEW_ITEM_TITLE = "viewItemTitle";
-    private static final String ID_VIEW_ITEM_ICON = "viewItemIcon";
-
-    private static final String UIHINT_VIEW = "view";
-    private static final String ID_VIEW_BUTTON_TITLE = "viewButtonTitle";
-    private static final String ID_VIEW_BUTTON_ICON = "viewButtonIcon";
-
-    private final ComponentType componentType;
-    private final String underlyingIdPrefix;
-
-    private ComponentFactory selectedComponentFactory;
-    protected Component selectedComponent;
-
-
-    public EntityLinksSelectorPanel(
-            final String id,
-            final EntityModel model,
-            final ComponentFactory factory) {
-        super(id, model);
-        this.underlyingIdPrefix = ComponentType.ENTITY.toString();
-        this.componentType = factory.getComponentType();
-    }
-
-
-    protected int determineInitialFactory(
-            final List<ComponentFactory> componentFactories,
-            final IModel<?> model) {
-        return 0;
-    }
-
-    @Override
-    public UiHintContainer getUiHintContainer() {
-        // disables hinting by this component
-        return null;
-    }
-
-
-    /**
-     * Build UI only after added to parent.
-     */
-    @Override
-    public void onInitialize() {
-        super.onInitialize();
-        ComponentFactory componentFactory = getComponentFactoryRegistry().findComponentFactoryElseFailFast(getComponentType(), getModel());
-        addAdditionalLinks(getModel());
-        addUnderlyingViews(underlyingIdPrefix, getModel(), componentFactory);
-    }
-
-    protected void addAdditionalLinks(final EntityModel model) {
-        if(!(model instanceof LinksProvider)) {
-            permanentlyHide(ID_ADDITIONAL_LINKS);
-            return;
-        }
-        LinksProvider linksProvider = (LinksProvider) model;
-        List<LinkAndLabel> links = linksProvider.getLinks();
-
-        addAdditionalLinks(this, links);
-    }
-
-    protected void addAdditionalLinks(MarkupContainer markupContainer, List<LinkAndLabel> linkAndLabels) {
-        if(linkAndLabels == null || linkAndLabels.isEmpty()) {
-            Components.permanentlyHide(markupContainer, ID_ADDITIONAL_LINKS);
-            return;
-        }
-        linkAndLabels = _Lists.newArrayList(linkAndLabels); // copy, to serialize any lazy evaluation
-
-        AdditionalLinksPanel.addAdditionalLinks(
-                markupContainer, ID_ADDITIONAL_LINKS,
-                linkAndLabels,
-                AdditionalLinksPanel.Style.INLINE_LIST);
-    }
-
-    private void addUnderlyingViews(final String underlyingIdPrefix, final EntityModel model, final ComponentFactory factory) {
-        final List<ComponentFactory> componentFactories = findOtherComponentFactories(model, factory);
-
-        final int selected = honourViewHintElseDefault(componentFactories, model);
-
-        final EntityLinksSelectorPanel selectorPanel = this;
-
-        // create all, hide the one not selected
-        final Component[] underlyingViews = new Component[MAX_NUM_UNDERLYING_VIEWS];
-        int i = 0;
-        final EntityModel emptyModel = dummyOf(model);
-        for (ComponentFactory componentFactory : componentFactories) {
-            final String underlyingId = underlyingIdPrefix + "-" + i;
-
-            Component underlyingView = componentFactory.createComponent(underlyingId,i==selected? model: emptyModel);
-            underlyingViews[i++] = underlyingView;
-            selectorPanel.addOrReplace(underlyingView);
-        }
-
-        // hide any unused placeholders
-        while(i<MAX_NUM_UNDERLYING_VIEWS) {
-            String underlyingId = underlyingIdPrefix + "-" + i;
-            permanentlyHide(underlyingId);
-            i++;
-        }
-
-        // selector
-        if (componentFactories.size() <= 1) {
-            permanentlyHide(ID_VIEWS);
-        } else {
-            final Model<ComponentFactory> componentFactoryModel = new Model<>();
-
-            selectorPanel.selectedComponentFactory = componentFactories.get(selected);
-            componentFactoryModel.setObject(selectorPanel.selectedComponentFactory);
-
-            final WebMarkupContainer views = new WebMarkupContainer(ID_VIEWS);
-
-            final Label viewButtonTitle = new Label(ID_VIEW_BUTTON_TITLE, "Hidden");
-            views.addOrReplace(viewButtonTitle);
-
-            final Label viewButtonIcon = new Label(ID_VIEW_BUTTON_ICON, "");
-            views.addOrReplace(viewButtonIcon);
-
-            final WebMarkupContainer container = new WebMarkupContainer(ID_VIEW_LIST);
-
-            views.addOrReplace(container);
-            views.setOutputMarkupId(true);
-
-            this.setOutputMarkupId(true);
-
-            final ListView<ComponentFactory> listView = new ListView<ComponentFactory>(ID_VIEW_ITEM, componentFactories) {
-
-                private static final long serialVersionUID = 1L;
-
-                @Override
-                protected void populateItem(ListItem<ComponentFactory> item) {
-
-                    final int underlyingViewNum = item.getIndex();
-
-                    final ComponentFactory componentFactory = item.getModelObject();
-                    final AbstractLink link = new AjaxLink<Void>(ID_VIEW_LINK) {
-                        private static final long serialVersionUID = 1L;
-                        @Override
-                        public void onClick(AjaxRequestTarget target) {
-                            EntityLinksSelectorPanel linksSelectorPanel = EntityLinksSelectorPanel.this;
-                            linksSelectorPanel.setViewHintAndBroadcast(underlyingViewNum, target);
-
-                            final EntityModel dummyModel = dummyOf(model);
-                            for(int i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
-                                final Component component = underlyingViews[i];
-                                if(component == null) {
-                                    continue;
-                                }
-                                final boolean isSelected = i == underlyingViewNum;
-                                PanelAbstract.setVisible(component, isSelected);
-                                component.setDefaultModel(isSelected? model: dummyModel);
-                            }
-
-                            selectorPanel.selectedComponentFactory = componentFactory;
-                            selectorPanel.selectedComponent = underlyingViews[underlyingViewNum];
-                            selectorPanel.onSelect(target);
-                            target.add(selectorPanel, views);
-                        }
-
-                        @Override
-                        protected void onComponentTag(ComponentTag tag) {
-                            super.onComponentTag(tag);
-                            Buttons.fixDisabledState(this, tag);
-                        }
-                    };
-
-                    IModel<String> title = nameFor(componentFactory);
-                    Label viewItemTitleLabel = new Label(ID_VIEW_ITEM_TITLE, title);
-                    link.add(viewItemTitleLabel);
-
-                    Label viewItemIcon = new Label(ID_VIEW_ITEM_ICON, "");
-                    link.add(viewItemIcon);
-
-                    boolean isEnabled = componentFactory != selectorPanel.selectedComponentFactory;
-                    if (!isEnabled) {
-                        viewButtonTitle.setDefaultModel(title);
-                        IModel<String> cssClass = cssClassFor(componentFactory, viewButtonIcon);
-                        viewButtonIcon.add(AttributeModifier.replace("class", "ViewLinkItem " + cssClass.getObject()));
-                        link.setVisible(false);
-                    } else {
-                        IModel<String> cssClass = cssClassFor(componentFactory, viewItemIcon);
-                        viewItemIcon.add(new CssClassAppender(cssClass));
-                    }
-
-                    item.add(link);
-                }
-
-                private IModel<String> cssClassFor(final ComponentFactory componentFactory, Label viewIcon) {
-                    IModel<String> cssClass = null;
-                    if (componentFactory instanceof CollectionContentsAsFactory) {
-                        CollectionContentsAsFactory collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
-                        cssClass = collectionContentsAsFactory.getCssClass();
-                        viewIcon.setDefaultModelObject("");
-                        viewIcon.setEscapeModelStrings(true);
-                    }
-                    if (cssClass == null) {
-                        String name = componentFactory.getName();
-                        cssClass = Model.of(StringExtensions.asLowerDashed(name));
-                        // Small hack: if there is no specific CSS class then we assume that background-image is used
-                        // the span.ViewItemLink should have some content to show it
-                        // FIX: find a way to do this with CSS (width and height don't seems to help)
-                        viewIcon.setDefaultModelObject("&#160;&#160;&#160;&#160;&#160;");
-                        viewIcon.setEscapeModelStrings(false);
-                    }
-                    return cssClass;
-                }
-
-                private IModel<String> nameFor(final ComponentFactory componentFactory) {
-                    IModel<String> name = null;
-                    if (componentFactory instanceof CollectionContentsAsFactory) {
-                        CollectionContentsAsFactory collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
-                        name = collectionContentsAsFactory.getTitleLabel();
-                    }
-                    if (name == null) {
-                        name = Model.of(componentFactory.getName());
-                    }
-                    return name;
-                }
-            };
-            container.add(listView);
-            addOrReplace(views);
-        }
-
-        for(i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
-            Component component = underlyingViews[i];
-            if(component != null) {
-                if(i != selected) {
-                    super.setVisible(component, /*visible*/ false);
-                } else {
-                    selectedComponent = component;
-                }
-            }
-        }
-    }
-
-
-
-    protected void setViewHintAndBroadcast(int viewNum, AjaxRequestTarget target) {
-        final UiHintContainer uiHintContainer = getUiHintContainer();
-        if(uiHintContainer == null) {
-            return;
-        }
-        uiHintContainer.setHint(this, UIHINT_VIEW, ""+viewNum);
-    }
-
-    /**
-     * Overrideable hook.
-     */
-    protected void onSelect(AjaxRequestTarget target) {
-    }
-
-    /**
-     * Ask for a dummy (empty) {@link Model} to pass into those components that are rendered but will be
-     * made invisible using CSS styling.
-     */
-    protected EntityModel dummyOf(EntityModel model) {
-        return model;
-    }
-
-    protected int honourViewHintElseDefault(final List<ComponentFactory> componentFactories, final IModel<?> model) {
-        // honour hints ...
-        final UiHintContainer hintContainer = getUiHintContainer();
-        if(hintContainer != null) {
-            String viewStr = hintContainer.getHint(this, UIHINT_VIEW);
-            if(viewStr != null) {
-                
-                val parseResult = _Ints.parseInt(viewStr, 10);
-                if(parseResult.isPresent()) {
-                    val viewNum = parseResult.getAsInt();
-                    if(viewNum >= 0 && viewNum < componentFactories.size()) {
-                        return viewNum;
-                    }
-                }
-                // else fall through
-                
-            }
-        }
-
-        // ... else default
-        int initialFactory = determineInitialFactory(componentFactories, model);
-        if(hintContainer != null) {
-            hintContainer.setHint(this, UIHINT_VIEW, ""+initialFactory);
-            // don't broadcast (no AjaxRequestTarget, still configuring initial setup)
-        }
-        return initialFactory;
-    }
-
-
-    private List<ComponentFactory> findOtherComponentFactories(final EntityModel model, final ComponentFactory ignoreFactory) {
-        final List<ComponentFactory> componentFactories = getComponentFactoryRegistry().findComponentFactories(componentType, model);
-        val otherFactories = _Lists.filter(componentFactories, new Predicate<ComponentFactory>() {
-            @Override
-            public boolean test(final ComponentFactory input) {
-                return input != ignoreFactory;
-            }
-        });
-        return ordered(otherFactories);
-    }
-
-    protected List<ComponentFactory> ordered(List<ComponentFactory> otherFactories) {
-        return otherFactories;
-    }
-
-
-
-}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/Bs3GridPanelFactory.java
similarity index 70%
rename from viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/Bs3GridPanelFactory.java
index c07efe0..534b0ff 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanelFactory.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/layout/bs3/Bs3GridPanelFactory.java
@@ -17,7 +17,7 @@
  *  under the License.
  */
 
-package org.apache.isis.viewer.wicket.ui.components.entity.selector.links;
+package org.apache.isis.viewer.wicket.ui.components.layout.bs3;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
@@ -30,19 +30,29 @@ import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
-import org.apache.isis.viewer.wicket.ui.components.layout.bs3.BS3GridPanel;
 
 import lombok.val;
 
 /**
- * {@link ComponentFactory} for {@link EntityLinksSelectorPanel}.
+ * {@link ComponentFactory} for {@link BS3GridPanel}.
  */
-public class EntityLinksSelectorPanelFactory extends EntityComponentFactoryAbstract {
+public class Bs3GridPanelFactory extends EntityComponentFactoryAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    public EntityLinksSelectorPanelFactory() {
-        super(ComponentType.ENTITY, EntityLinksSelectorPanel.class);
+    public Bs3GridPanelFactory() {
+        super(ComponentType.ENTITY, BS3GridPanel.class);
+    }
+
+    @Override protected ApplicationAdvice appliesTo(final IModel<?> model) {
+        final EntityModel entityModel = (EntityModel) model;
+
+        val objectAdapter = entityModel.getObject();
+        final ObjectSpecification specification = entityModel.getTypeOfSpecification();
+        final GridFacet facet = specification.getFacet(GridFacet.class);
+
+        final Grid grid = facet.getGrid(objectAdapter);
+        return ApplicationAdvice.appliesIf(grid instanceof BS3Grid);
     }
 
     @Override
@@ -53,13 +63,7 @@ public class EntityLinksSelectorPanelFactory extends EntityComponentFactoryAbstr
         final ObjectSpecification specification = entityModel.getTypeOfSpecification();
         final GridFacet facet = specification.getFacet(GridFacet.class);
 
-        final Grid grid = facet.getGrid(objectAdapter);
-        if (grid != null) {
-            if(grid instanceof BS3Grid) {
-                final BS3Grid bs3Grid = (BS3Grid) grid;
-                return new BS3GridPanel(id, entityModel, bs3Grid);
-            }
-        }
-        return new EntityLinksSelectorPanel(id, entityModel, this);
+        val grid = (BS3Grid) facet.getGrid(objectAdapter);
+        return new BS3GridPanel(id, entityModel, grid);
     }
 }
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
index ad80ce4..eef7349 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
@@ -23,11 +23,6 @@ import java.util.List;
 
 import javax.inject.Named;
 
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
@@ -49,9 +44,9 @@ import org.apache.isis.viewer.wicket.ui.components.entity.collection.EntityColle
 import org.apache.isis.viewer.wicket.ui.components.entity.header.EntityHeaderPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.entity.icontitle.EntityIconAndTitlePanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.entity.icontitle.EntityIconTitleAndCopyLinkPanelFactory;
-import org.apache.isis.viewer.wicket.ui.components.entity.selector.links.EntityLinksSelectorPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.footer.FooterPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.header.HeaderPanelFactory;
+import org.apache.isis.viewer.wicket.ui.components.layout.bs3.Bs3GridPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.property.PropertyEditFormPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.property.PropertyEditPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.blobclob.IsisBlobPanelFactory;
@@ -93,6 +88,10 @@ import org.apache.isis.viewer.wicket.ui.components.value.StandaloneValuePanelFac
 import org.apache.isis.viewer.wicket.ui.components.voidreturn.VoidReturnPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.welcome.WelcomePanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.widgets.entitysimplelink.EntityLinkSimplePanelFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
 
 import lombok.extern.log4j.Log4j2;
 
@@ -135,7 +134,6 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
     }
 
     protected void addLinksSelectorFactories(final ComponentFactoryList componentFactories) {
-        componentFactories.add(new EntityLinksSelectorPanelFactory());
         componentFactories.add(new CollectionContentsMultipleViewsPanelFactory());
     }
 
@@ -186,6 +184,9 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
     protected void addComponentFactoriesForEntity(final ComponentFactoryList componentFactories) {
 
+        // top level
+        componentFactories.add(new Bs3GridPanelFactory());
+
         // lower-level
         componentFactories.add(new EntityIconAndTitlePanelFactory());
         componentFactories.add(new EntityIconTitleAndCopyLinkPanelFactory());


[isis] 04/04: ISIS-2476: improves the custom ui example a little

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2476
in repository https://gitbox.apache.org/repos/asf/isis.git

commit cd5b3fe9e9b2a4c95cc4d01978e7330b35d9f3e1
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Jan 3 15:48:35 2021 +0000

    ISIS-2476: improves the custom ui example a little
---
 api/adoc/userguide/modules/fun/pages/mixins.adoc   | 89 ++--------------------
 .../dom/ui/custom/geocoding/GeocoderClient.java    | 49 ++++++++++++
 .../java/demoapp/dom/ui/custom/latlng/LatLng.java  | 12 +++
 .../{LatLng.java => latlng/LatLngUtils.java}       |  4 +-
 .../dom/ui/custom/{ => latlng}/Latitude.java       |  6 +-
 .../dom/ui/custom/{ => latlng}/Longitude.java      |  3 +-
 .../dom/ui/custom/{ => latlng}/PositiveNumber.java |  2 +-
 .../java/demoapp/dom/ui/custom/vm/BoundingBox.java | 38 +++++++++
 .../dom/ui/custom/{ => vm}/CustomUiMenu.java       | 26 ++++---
 .../demoapp/dom/ui/custom/{ => vm}/CustomUiVm.java | 41 ++++------
 .../ui/custom/{ => vm}/TabDemo-description.adoc    |  0
 .../dom/ui/custom/{ => vm}/TabDemo.layout.xml      |  0
 .../{LatLngTest.java => LatLngUtilsTest.java}      |  6 +-
 .../geocoding/GeocoderClientTest_geocode.java      | 22 ++++++
 .../webapp/wicket/customview/CustomUiPanel.java    | 15 +++-
 .../wicket/customview/CustomUiPanelFactory.java    |  2 +-
 16 files changed, 182 insertions(+), 133 deletions(-)

diff --git a/api/adoc/userguide/modules/fun/pages/mixins.adoc b/api/adoc/userguide/modules/fun/pages/mixins.adoc
index e4b6d87..d6782b2 100644
--- a/api/adoc/userguide/modules/fun/pages/mixins.adoc
+++ b/api/adoc/userguide/modules/fun/pages/mixins.adoc
@@ -23,20 +23,21 @@ When the mixin follows the naming convention `SomeType_mixinName` then the metho
 
 == Contributed Collection
 
-The example below shows how to contribute a collection, using xref:refguide:applib-ant:Collection.adoc[`@Collection`]:
+The example below shows how to contribute a collection, using xref:refguide:applib-ant:Collection.adoc[`@Collection`].
+The method is expected to be called "coll":
 
 [source,java]
 ----
-@Collection                                                     // <.>
-@RequiredArgsConstructor                                        // <.>
+@Collection                                     // <.>
+@RequiredArgsConstructor                        // <.>
 public class DocumentHolder_documents {
 
     private final DocumentHolder holder;
 
-    public List<Document> coll() {                              // <.>
+    public List<Document> coll() { /* ... */ }  // <.>
         ...
     }
-    public boolean hideColl() { /* ... */ }                     // <.>
+    public boolean hideColl() { /* ... */ }     // <.>
 }
 ----
 <.> indicates this is a collection mixin (that contributes its method as an association) with method name 'coll'
@@ -47,31 +48,6 @@ public class DocumentHolder_documents {
 
 The above will result in a contributed collection "documents" for all types that implement/extend from `DocumentHolder`.
 
-The following does the same thing, but using the xref:refguide:applib-ant:DomainObject.adoc[`@DomainObject`] annotation, to explicitly specify the method name:
-
-[source,java]
-----
-@Collection                                                     // <.>
-@DomainObject(nature=MIXIN, mixinMethod="coll")                 // <.>
-@RequiredArgsConstructor                                        // <.>
-public class DocumentHolder_documents {
-
-    private final DocumentHolder holder;
-
-    public List<Document> coll() {                              // <.>
-        ...
-    }
-    public boolean hideColl() { /* ... */ }                     // <.>
-}
-----
-<.> indicates this is a collection mixin (that contributes its method as an association)
-<.> nominates the method name 
-The recommended name for collections is "coll" (if none is specified the default name is "$$").
-<.> mixee is injected into the mixin
-<.> method has no side-effects, must match `@DomainObject(mixinMethod=...)` above and accept no arguments
-The mixin is a collection rather than a property because the return type is a collection, not a scalar.
-<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions.
-
 
 
 == Contributed Property
@@ -100,37 +76,12 @@ public class DocumentHolder_mostRecentDocument {
 
 This contributes a property called "mostRecentDocument".
 
-The following does the same thing, but using the xref:refguide:applib-ant:DomainObject.adoc[`@DomainObject`] annotation, to explicitly specify the method name:
-
-[source,java]
-----
-@Property                                                       // <.>
-@DomainObject(nature=MIXIN, mixinMethod="prop")                 // <.>
-@RequiredArgsConstructor                                        // <.>
-public class DocumentHolder_mostRecentDocument {
-
-    private final DocumentHolder holder;
-
-    public Document prop() {                                    // <.>
-        ...
-    }
-    public boolean hiderProp() { /* ... */ }                    // <.>
-}
-----
-<.> indicates this is a property mixin (that contributes its method as an association)
-<.> nominates the method name
-The recommended name for properties is "prop" (if none is specified the default name is "$$").
-<.> mixee is injected into the mixin
-<.> method has no side-effects, must match `@DomainObject(mixinMethod=...)` above and accept no arguments
-The mixin is a property rather than a collection because the return type is a scalar.
-<.> supporting methods (discussed in xref:business-rules.adoc[business rules]) follow the usual naming conventions.
 
 [#contributed-action]
 == Contributed Action
 
-Contributed properties can likewise be defined using xref:refguide:applib-ant:Property.adoc[`@Property`]; this implies a method called "prop":
-
-Contributed actions are defined similarly, for example:
+Contributed actions are defined similarly, using xref:refguide:applib-ant:Action.adoc[`@Action`]; this implies a method called "act".
+For example:
 
 [source,java]
 ----
@@ -154,30 +105,6 @@ Unlike contributed properties and collections, contributed actions can accept pa
 
 This contributes an action called "addDocument".
 
-The following does the same thing, but using the xref:refguide:applib-ant:DomainObject.adoc[`@DomainObject`] annotation, to explicitly specify the method name:
-
-[source,java]
-----
-@Action                                                         // <.>
-@DomainObject(nature=MIXIN, mixinMethod="act")                  // <.>
-@RequiredArgsConstructor                                        // <.>
-public class DocumentHolder_addDocument {
-
-    private final DocumentHolder holder;
-
-    public Document> act(Document doc) {                        // <.>
-        ...
-    }
-    public boolean hideAct() { /* ... */ }                      // <.>
-}
-----
-<.> indicates this is an action mixin (that contributes its method as an action)
-<.> nominates the method name
-<.> mixee is injected into the mixin
-<.> method must match `@DomainObject(mixinMethod=...)` above
-Unlike contributed properties and collections, contributed actions can accept parameters.
-<.> xref:business-rules.adoc[supporting methods] follow the usual naming conventions
-
 
 [[mixins-as-nested-classes]]
 == As Nested Classes
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/geocoding/GeocoderClient.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/geocoding/GeocoderClient.java
new file mode 100644
index 0000000..dcedbcb
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/geocoding/GeocoderClient.java
@@ -0,0 +1,49 @@
+package demoapp.dom.ui.custom.geocoding;
+
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+import lombok.SneakyThrows;
+import lombok.val;
+
+import demoapp.dom.ui.custom.latlng.LatLng;
+
+@Service
+public class GeocoderClient {
+
+    private final static String KEY = "xlc4LAkkA0BFboXOzzrp4M5F5y83VCE4";
+
+    @SneakyThrows
+    public LatLng geocode(final String address) {
+
+        val url = new URL(String.format("http://open.mapquestapi.com/geocoding/v1/address?key=%s&location=%s&outFormat=XML", KEY, address));
+
+        val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        val xmlDoc = builder.parse(url.toString());
+
+        return new LatLng(
+                extract(xmlDoc, "//latLng/lat/text()"),
+                extract(xmlDoc, "//latLng/lng/text()")
+        );
+    }
+
+    private static String extract(Document xmlDoc, String expression) throws XPathExpressionException {
+        return extract(xmlDoc, expression, 0);
+    }
+
+    private static String extract(Document xmlDoc, String expression, int index) throws XPathExpressionException {
+        val xPathExpression = XPathFactory.newInstance().newXPath().compile(expression);
+        val nodeList = (NodeList) xPathExpression.evaluate(xmlDoc, XPathConstants.NODESET);
+        val node = nodeList.item(index);
+        return node.getNodeValue();
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLng.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLng.java
new file mode 100644
index 0000000..5fedcf4
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLng.java
@@ -0,0 +1,12 @@
+package demoapp.dom.ui.custom.latlng;
+
+import lombok.Data;
+import lombok.Getter;
+
+@Data
+public class LatLng {
+    @Getter
+    private final String latitude;
+    @Getter
+    private final String longitude;
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLngUtils.java
similarity index 91%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLngUtils.java
index 4353797..d3196d6 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/LatLngUtils.java
@@ -1,4 +1,4 @@
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.latlng;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
@@ -8,7 +8,7 @@ import lombok.experimental.UtilityClass;
 
 
 @UtilityClass
-public class LatLng {
+public class LatLngUtils {
 
     public static BigDecimal toBigDecimal(final String val) {
         return new BigDecimal(val);
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Latitude.java
similarity index 88%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Latitude.java
index cd303aa..5d6a657 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Latitude.java
@@ -1,4 +1,4 @@
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.latlng;
 
 
 import java.lang.annotation.ElementType;
@@ -6,14 +6,10 @@ import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
 
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.Property;
 
-import lombok.val;
-
 @Property(
         regexPattern = Latitude.PATTERN
         , regexPatternReplacement = "Does not match format of latitude"
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Longitude.java
similarity index 93%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Longitude.java
index a0b31f9..d6cbe17 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/Longitude.java
@@ -1,4 +1,4 @@
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.latlng;
 
 
 import java.lang.annotation.ElementType;
@@ -6,7 +6,6 @@ import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.math.BigDecimal;
 
 import org.apache.isis.applib.annotation.Parameter;
 import org.apache.isis.applib.annotation.Property;
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/PositiveNumber.java
similarity index 96%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/PositiveNumber.java
index 99cacc5..1a747a4 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/latlng/PositiveNumber.java
@@ -1,4 +1,4 @@
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.latlng;
 
 
 import java.lang.annotation.ElementType;
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/BoundingBox.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/BoundingBox.java
new file mode 100644
index 0000000..11f66b9
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/BoundingBox.java
@@ -0,0 +1,38 @@
+package demoapp.dom.ui.custom.vm;
+
+import lombok.Data;
+import lombok.Getter;
+
+import demoapp.dom.ui.custom.latlng.LatLng;
+
+@Data
+public class BoundingBox {
+    @Getter
+    private final LatLng minimum;
+    @Getter
+    private final LatLng maximum;
+
+    public String getMinimumLatitude() {
+        return minimum.getLatitude();
+    }
+
+    public String getMinimumLongitude() {
+        return minimum.getLongitude();
+    }
+
+    public String getMaximumLatitude() {
+        return maximum.getLatitude();
+    }
+
+    public String getMaximumLongitude() {
+        return maximum.getLongitude();
+    }
+
+    public final String toUrl(String divider) {
+        return getMinimumLongitude() + divider + this.getMinimumLatitude() + divider + getMaximumLongitude() + divider + getMaximumLatitude();
+    }
+
+    public final String toUrl() {
+        return toUrl("%2C");
+    }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiMenu.java
similarity index 75%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiMenu.java
index aaf28ba..1f945d8 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiMenu.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.vm;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
@@ -24,36 +24,42 @@ import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.SemanticsOf;
 
+import lombok.RequiredArgsConstructor;
 import lombok.val;
 
+import demoapp.dom.ui.custom.geocoding.GeocoderClient;
+import demoapp.dom.ui.custom.latlng.PositiveNumber;
+
 @DomainService(nature=NatureOfService.VIEW, objectType = "demo.CustomUiMenu")
+@RequiredArgsConstructor
 public class CustomUiMenu {
 
+    private final GeocoderClient geocoderClient;
+
     @Action(semantics = SemanticsOf.SAFE)
     @ActionLayout(
             cssClassFa="fa-globe",
             describedAs="Opens a Custom UI page displaying a map"
     )
     public CustomUiVm customUiVm(
-            @Latitude final String latitude,
-            @Longitude final String longitude,
+            final String address,
             @PositiveNumber final int scale
     ){
         val vm = new CustomUiVm();
-        vm.setLatitude(latitude);
-        vm.setLongitude(longitude);
+
+        val latLng = geocoderClient.geocode(address);
+        vm.setAddress(address);
+        vm.setLatitude(latLng.getLatitude());
+        vm.setLongitude(latLng.getLongitude());
         vm.setScale(scale);
 
         return vm;
     }
 
     public String default0CustomUiVm() {
-        return "51.753500";
-    }
-    public String default1CustomUiVm() {
-        return "-1.253640";
+        return "London,UK";
     }
-    public int default2CustomUiVm() {
+    public int default1CustomUiVm() {
         return 1;
     }
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiVm.java
similarity index 65%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiVm.java
index 669f2e7..268cd4c 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/CustomUiVm.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package demoapp.dom.ui.custom;
+package demoapp.dom.ui.custom.vm;
 
 import java.io.Serializable;
 
@@ -27,12 +27,17 @@ import javax.xml.bind.annotation.XmlType;
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Title;
 
-import lombok.Data;
 import lombok.Getter;
 import lombok.Setter;
 
 import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+import demoapp.dom.ui.custom.latlng.LatLng;
+import demoapp.dom.ui.custom.latlng.LatLngUtils;
+import demoapp.dom.ui.custom.latlng.Latitude;
+import demoapp.dom.ui.custom.latlng.Longitude;
+import demoapp.dom.ui.custom.latlng.PositiveNumber;
 
 @XmlRootElement(name = "demo.CustomUiVm")
 @XmlType
@@ -40,6 +45,10 @@ import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
 @DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.CustomUiVm")
 public class CustomUiVm implements HasAsciiDocDescription, Serializable {
 
+    @Title
+    @Getter @Setter
+    private String address;
+
     @Latitude
     @Getter @Setter
     private String latitude;
@@ -52,33 +61,15 @@ public class CustomUiVm implements HasAsciiDocDescription, Serializable {
     @Getter @Setter
     private int scale;
 
-    @Data
-    public static class BoundingBox {
-        @Getter
-        private final String minimumLatitude;
-        @Getter
-        private final String minimumLongitude;
-        @Getter
-        private final String maximumLatitude;
-        @Getter
-        private final String maximumLongitude;
-
-        public final String toUrl(String divider) {
-            return getMinimumLongitude() + divider + getMinimumLatitude() + divider + getMaximumLongitude() + divider + getMaximumLatitude();
-        }
-        public final String toUrl() {
-            return toUrl("%2C");
-        }
-    }
     /**
      * @link https://wiki.openstreetmap.org/wiki/Bounding_Box
      */
     public BoundingBox getBoundingBox() {
-        String minLat = LatLng.add(getLatitude(), -getScale());
-        String maxLat = LatLng.add(getLatitude(), +getScale());
-        String minLng = LatLng.add(getLongitude(), -getScale());
-        String maxLng = LatLng.add(getLongitude(), +getScale());
-        return new BoundingBox(minLat, minLng, maxLat, maxLng);
+        String minLat = LatLngUtils.add(getLatitude(), -getScale());
+        String maxLat = LatLngUtils.add(getLatitude(), +getScale());
+        String minLng = LatLngUtils.add(getLongitude(), -getScale());
+        String maxLng = LatLngUtils.add(getLongitude(), +getScale());
+        return new BoundingBox(new LatLng(minLat, minLng), new LatLng(maxLat, maxLng));
     }
 
 }
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/TabDemo-description.adoc
similarity index 100%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/TabDemo-description.adoc
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/TabDemo.layout.xml
similarity index 100%
rename from examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml
rename to examples/demo/domain/src/main/java/demoapp/dom/ui/custom/vm/TabDemo.layout.xml
diff --git a/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngUtilsTest.java
similarity index 64%
rename from examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
rename to examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngUtilsTest.java
index 4aa8031..65a90d9 100644
--- a/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
+++ b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngUtilsTest.java
@@ -5,11 +5,13 @@ import org.junit.jupiter.api.Test;
 
 import lombok.val;
 
-class LatLngTest {
+import demoapp.dom.ui.custom.latlng.LatLngUtils;
+
+class LatLngUtilsTest {
 
     @Test
     void add() {
-        val add = LatLng.add("51.753500", 1);
+        val add = LatLngUtils.add("51.753500", 1);
         Assertions.assertThat(add).isEqualTo("51.763500");
     }
 }
diff --git a/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/geocoding/GeocoderClientTest_geocode.java b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/geocoding/GeocoderClientTest_geocode.java
new file mode 100644
index 0000000..ec6781b
--- /dev/null
+++ b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/geocoding/GeocoderClientTest_geocode.java
@@ -0,0 +1,22 @@
+package demoapp.dom.ui.custom.geocoding;
+
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.Test;
+
+import lombok.val;
+
+class GeocoderClientTest_geocode {
+
+    @Test
+    void happy_case() {
+
+        val client = new GeocoderClient();
+        val latLng = client.geocode("OX1 3DP,UK");
+
+        val softly = new SoftAssertions();
+        softly.assertThat(latLng.getLatitude()).isEqualTo("51.753769");
+        softly.assertThat(latLng.getLongitude()).isEqualTo("-1.256271");
+
+        softly.assertAll();
+    }
+}
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
index 0699661..912107a 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
@@ -29,7 +29,7 @@ import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
 import lombok.val;
 
-import demoapp.dom.ui.custom.CustomUiVm;
+import demoapp.dom.ui.custom.vm.CustomUiVm;
 
 public class CustomUiPanel extends PanelAbstract<EntityModel>  {
 
@@ -51,18 +51,21 @@ public class CustomUiPanel extends PanelAbstract<EntityModel>  {
         return null;
     }
 
-
     /**
      * Build UI only after added to parent.
      */
     @Override
     public void onInitialize() {
         super.onInitialize();
+        buildGui();
+    }
 
+    private void buildGui() {
         val managedObject = (ManagedObject) getModelObject();
         val customUiVm = (CustomUiVm) managedObject.getPojo();
 
         val iframe = new InlineFrame("iframe", getPage()) {
+
             @Override
             protected CharSequence getURL() {
                 val boundingBox = customUiVm.getBoundingBox();
@@ -72,11 +75,15 @@ public class CustomUiPanel extends PanelAbstract<EntityModel>  {
                         , url
                         , "mapnik");
             }
+
         };
 
         addOrReplace(iframe);
     }
 
-
-
+    @Override
+    protected void onConfigure() {
+        super.onConfigure();
+        buildGui();
+    }
 }
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
index dcad85d..7605b14 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
@@ -9,7 +9,7 @@ import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
 import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
 
-import demoapp.dom.ui.custom.CustomUiVm;
+import demoapp.dom.ui.custom.vm.CustomUiVm;
 
 @org.springframework.stereotype.Component
 @Order(OrderPrecedence.EARLY)


[isis] 02/04: ISIS-2476 - fleshes out custom UI example

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2476
in repository https://gitbox.apache.org/repos/asf/isis.git

commit d2b9d48d246ccc41faaf8ceb1c4ae7bd516a05e6
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Thu Dec 17 18:21:10 2020 +0000

    ISIS-2476 - fleshes out custom UI example
---
 .../src/main/java/demoapp/dom/menubars.layout.xml  |  4 +
 .../java/demoapp/dom/ui/custom/CustomUiMenu.java   | 60 +++++++++++++++
 .../java/demoapp/dom/ui/custom/CustomUiVm.java     | 86 ++++++++++++++++++++++
 .../main/java/demoapp/dom/ui/custom/LatLng.java    | 28 +++++++
 .../main/java/demoapp/dom/ui/custom/Latitude.java  | 37 ++++++++++
 .../main/java/demoapp/dom/ui/custom/Longitude.java | 34 +++++++++
 .../java/demoapp/dom/ui/custom/PositiveNumber.java | 33 +++++++++
 .../demoapp/dom/ui/custom/TabDemo-description.adoc |  8 ++
 .../java/demoapp/dom/ui/custom/TabDemo.layout.xml  | 68 +++++++++++++++++
 .../java/demoapp/dom/ui/custom/LatLngTest.java     | 15 ++++
 .../java/demoapp/webapp/wicket/DemoAppWicket.java  | 21 +++---
 .../{MyEntityPanel.html => CustomUiPanel.html}     | 13 +++-
 .../{MyEntityPanel.java => CustomUiPanel.java}     | 30 +++++++-
 .../wicket/customview/CustomUiPanelFactory.java    | 35 +++++++++
 .../wicket/customview/MyEntityPanelFactory.java    | 43 -----------
 15 files changed, 458 insertions(+), 57 deletions(-)

diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index 0173768..ca8adcc 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -225,6 +225,10 @@ For latest we use: https://raw.githubusercontent.com/apache/isis/master/antora/s
                 <mb3:named>Tabs</mb3:named>
                 <mb3:serviceAction objectType="demo.TabMenu" id="tabDemo" />
             </mb3:section>
+            <mb3:section>
+                <mb3:named>Custom UI</mb3:named>
+                <mb3:serviceAction objectType="demo.CustomUiMenu" id="customUiVm" />
+            </mb3:section>
         </mb3:menu>
 
         <mb3:menu>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
new file mode 100644
index 0000000..817b2c2
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiMenu.java
@@ -0,0 +1,60 @@
+/*
+ *  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 demoapp.dom.ui.custom;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.SemanticsOf;
+
+import lombok.val;
+
+@DomainService(nature=NatureOfService.VIEW, objectType = "demo.CustomUiMenu")
+public class CustomUiMenu {
+
+    @Action(semantics = SemanticsOf.SAFE)
+    @ActionLayout(
+            cssClassFa="fa-globe",
+            describedAs="Opens a Custom UI page displaying a map"
+    )
+    public CustomUiVm whereInTheWorld(
+            @Latitude final String latitude,
+            @Longitude final String longitude,
+            @PositiveNumber final int scale
+    ){
+        val vm = new CustomUiVm();
+        vm.setLatitude(latitude);
+        vm.setLongitude(longitude);
+        vm.setScale(scale);
+
+        return vm;
+    }
+
+    public String default0CustomUiVm() {
+        return "51.753500";
+    }
+    public String default1CustomUiVm() {
+        return "-1.253640";
+    }
+    public int default2CustomUiVm() {
+        return 1;
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
new file mode 100644
index 0000000..1688807
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/CustomUiVm.java
@@ -0,0 +1,86 @@
+/*
+ *  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 demoapp.dom.ui.custom;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+
+@XmlRootElement(name = "demo.CustomUiVm")
+@XmlType
+@XmlAccessorType(XmlAccessType.FIELD)
+@DomainObject(nature=Nature.VIEW_MODEL, objectType = "demo.CustomUiVm")
+public class CustomUiVm implements HasAsciiDocDescription {
+
+    public String title() {
+        return "Custom UI";
+    }
+
+    @Latitude
+    @Getter @Setter
+    private String latitude;
+
+    @Longitude
+    @Getter @Setter
+    private String longitude;
+
+    @PositiveNumber
+    @Getter @Setter
+    private int scale;
+
+    @Data
+    public static class BoundingBox {
+        @Getter
+        private final String minimumLatitude;
+        @Getter
+        private final String minimumLongitude;
+        @Getter
+        private final String maximumLatitude;
+        @Getter
+        private final String maximumLongitude;
+
+        public final String toUrl(String divider) {
+            return getMinimumLongitude() + divider + getMinimumLatitude() + divider + getMaximumLongitude() + divider + getMaximumLatitude();
+        }
+        public final String toUrl() {
+            return toUrl("%2C");
+        }
+    }
+    /**
+     * @link https://wiki.openstreetmap.org/wiki/Bounding_Box
+     */
+    public BoundingBox getBoundingBox() {
+        String minLat = LatLng.add(getLatitude(), -getScale());
+        String maxLat = LatLng.add(getLatitude(), +getScale());
+        String minLng = LatLng.add(getLongitude(), -getScale());
+        String maxLng = LatLng.add(getLongitude(), +getScale());
+        return new BoundingBox(minLat, minLng, maxLat, maxLng);
+    }
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
new file mode 100644
index 0000000..4353797
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/LatLng.java
@@ -0,0 +1,28 @@
+package demoapp.dom.ui.custom;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+
+@UtilityClass
+public class LatLng {
+
+    public static BigDecimal toBigDecimal(final String val) {
+        return new BigDecimal(val);
+    }
+
+    public static String toString(BigDecimal val) {
+        return val.toPlainString();
+    }
+
+    public static String add(final String val, final int hundredths) {
+        val scaleBd = new BigDecimal(hundredths).setScale(2, RoundingMode.HALF_UP);
+        val scaleDividedBy100 = scaleBd.divide(new BigDecimal(100), RoundingMode.HALF_UP);
+        val bd = toBigDecimal(val);
+        val newVal = bd.add(scaleDividedBy100);
+        return toString(newVal);
+    }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
new file mode 100644
index 0000000..cd303aa
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Latitude.java
@@ -0,0 +1,37 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+
+import lombok.val;
+
+@Property(
+        regexPattern = Latitude.PATTERN
+        , regexPatternReplacement = "Does not match format of latitude"
+)
+@Parameter(
+        regexPattern = Latitude.PATTERN
+        , regexPatternReplacement = "Does not match format of latitude"
+)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Latitude {
+
+    String PATTERN = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$";
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
new file mode 100644
index 0000000..a0b31f9
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/Longitude.java
@@ -0,0 +1,34 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.math.BigDecimal;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+
+@Property(
+        regexPattern = Longitude.PATTERN
+        , regexPatternReplacement = "Does not match format of longitude"
+)
+@Parameter(
+        regexPattern = Longitude.PATTERN
+        , regexPatternReplacement = "Does not match format of longitude"
+)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Longitude {
+
+    String PATTERN = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$";
+
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
new file mode 100644
index 0000000..99cacc5
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/PositiveNumber.java
@@ -0,0 +1,33 @@
+package demoapp.dom.ui.custom;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.spec.AbstractSpecification;
+
+@Property(mustSatisfy = PositiveNumber.Specification.class)
+@Parameter(mustSatisfy = PositiveNumber.Specification.class)
+@Inherited
+@Target({
+        ElementType.FIELD,
+        ElementType.METHOD,
+        ElementType.PARAMETER,
+        ElementType.ANNOTATION_TYPE
+})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PositiveNumber {
+
+    class Specification extends AbstractSpecification<Integer> {
+
+        @Override
+        public String satisfiesSafely(Integer candidate) {
+            return candidate <= 0 ? "Must be a positive number" : null;
+        }
+    }
+}
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc
new file mode 100644
index 0000000..cf204ed
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo-description.adoc
@@ -0,0 +1,8 @@
+:Notice: 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 ag [...]
+
+(since 1.x)
+
+//TODO
+
+See the sources for this demo here:
+link:${SOURCES_DEMO}/demoapp/dom/tabs[sources]
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml
new file mode 100644
index 0000000..2c20a0e
--- /dev/null
+++ b/examples/demo/domain/src/main/java/demoapp/dom/ui/custom/TabDemo.layout.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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. -->
+<bs3:grid xmlns:bs3="http://isis.apache.org/applib/layout/grid/bootstrap3"
+          xmlns:cpt="http://isis.apache.org/applib/layout/component"
+>
+    <bs3:row>
+        <bs3:col span="12" unreferencedActions="true">
+            <cpt:domainObject/>
+            <cpt:action id="clearHints"/>
+            <cpt:action id="downloadLayoutXml"/>
+            <cpt:action id="openRestApi"/>
+            <cpt:action id="rebuildMetamodel"/>
+
+            <cpt:action id="doHideField"/>
+            <cpt:action id="doShowField"/>
+
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="6">
+
+        	<bs3:tabGroup>
+
+				<bs3:tab name="Tab 1">
+					<bs3:row>
+						<bs3:col span="12">
+							<cpt:fieldSet id="fs1" name="Hideable Field">
+								<cpt:property id="field1"/>
+							</cpt:fieldSet>
+						</bs3:col>
+					</bs3:row>
+				</bs3:tab>
+
+				<bs3:tab name="Tab 2">
+					<bs3:row>
+						<bs3:col span="12">
+							<cpt:fieldSet id="fs2">
+								<cpt:property id="field2"/>
+							</cpt:fieldSet>
+						</bs3:col>
+					</bs3:row>
+				</bs3:tab>
+			</bs3:tabGroup>
+
+<!--         	<cpt:fieldSet name="Tabs" id="tabs"> -->
+<!--                 <cpt:property id="field"/> -->
+<!--             </cpt:fieldSet> -->
+
+        </bs3:col>
+        <bs3:col span="6" unreferencedCollections="true">
+
+            <cpt:fieldSet name="Description" id="description" unreferencedProperties="true">
+                <cpt:property id="description" labelPosition="NONE"/>
+
+            </cpt:fieldSet>
+
+        </bs3:col>
+    </bs3:row>
+</bs3:grid>
diff --git a/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
new file mode 100644
index 0000000..4aa8031
--- /dev/null
+++ b/examples/demo/domain/src/test/java/demoapp/dom/ui/custom/LatLngTest.java
@@ -0,0 +1,15 @@
+package demoapp.dom.ui.custom;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import lombok.val;
+
+class LatLngTest {
+
+    @Test
+    void add() {
+        val add = LatLng.add("51.753500", 1);
+        Assertions.assertThat(add).isEqualTo("51.763500");
+    }
+}
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
index 1216076..723d360 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/DemoAppWicket.java
@@ -18,6 +18,11 @@
  */
 package demoapp.webapp.wicket;
 
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.Import;
+
 import org.apache.isis.extensions.viewer.wicket.pdfjs.ui.IsisModuleExtPdfjsUi;
 import org.apache.isis.valuetypes.asciidoc.metamodel.IsisModuleValAsciidocMetaModel;
 import org.apache.isis.valuetypes.asciidoc.persistence.jdo.dn5.IsisModuleValAsciidocPersistenceJdoDn5;
@@ -26,13 +31,9 @@ import org.apache.isis.valuetypes.markdown.persistence.jdo.dn5.IsisModuleValMark
 import org.apache.isis.valuetypes.markdown.ui.wkt.IsisModuleValMarkdownUiWkt;
 import org.apache.isis.valuetypes.sse.ui.wkt.IsisModuleValSseUiWkt;
 import org.apache.isis.viewer.wicket.viewer.IsisModuleViewerWicketViewer;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
-import org.springframework.context.annotation.Import;
 
 import demoapp.web.DemoAppManifest;
-import demoapp.webapp.wicket.customview.MyEntityPanelFactory;
+import demoapp.webapp.wicket.customview.CustomUiPanelFactory;
 
 /**
  * Bootstrap the application.
@@ -56,24 +57,24 @@ import demoapp.webapp.wicket.customview.MyEntityPanelFactory;
     IsisModuleValMarkdownPersistenceJdoDn5.class,
 
     // @Component's
-    MyEntityPanelFactory.class
+    CustomUiPanelFactory.class
 })
 //@Log4j2
 public class DemoAppWicket extends SpringBootServletInitializer {
 
     /**
-     * 
+     *
      * @param args
-     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an 
+     * @implNote this is to support the <em>Spring Boot Maven Plugin</em>, which auto-detects an
      * entry point by searching for classes having a {@code main(...)}
      */
     public static void main(String[] args) {
         //IsisPresets.prototyping();
         //DebugLoggingPreset.PERSISTENCE.apply();
         //DebugLoggingPreset.ISIS_SESSION.apply();
-        
+
         SpringApplication.run(new Class[] { DemoAppWicket.class }, args);
-        
+
     }
 
 
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
similarity index 73%
rename from examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
rename to examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
index 0e9b129..fd76620 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.html
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.html
@@ -20,8 +20,17 @@
 <html xmlns:wicket="http://wicket.apache.org">
 <body>
 <wicket:panel>
-    <div class="myEntityPanel">
-        <div><p>MY CUSTOM ENTITY</p></div>
+    <div class="customUiPanel">
+        <div>
+            <p>Where in the world?</p>
+            <iframe wicket:id="iframe"
+                    id="inlineFrameExample"
+                    title="Where in the world ?"
+                    width="1200"
+                    height="800"
+                    >
+            </iframe>
+        </div>
     </div>
 </wicket:panel>
 </body>
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
similarity index 64%
rename from examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
rename to examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
index 13c2911..0699661 100644
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanel.java
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanel.java
@@ -19,18 +19,25 @@
 
 package demoapp.webapp.wicket.customview;
 
+import org.apache.wicket.markup.html.link.InlineFrame;
+
+import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
 
-public class MyEntityPanel extends PanelAbstract<EntityModel>  {
+import lombok.val;
+
+import demoapp.dom.ui.custom.CustomUiVm;
+
+public class CustomUiPanel extends PanelAbstract<EntityModel>  {
 
 
     private static final long serialVersionUID = 1L;
 
 
-    public MyEntityPanel(
+    public CustomUiPanel(
             final String id,
             final EntityModel model,
             final ComponentFactory componentFactory) {
@@ -51,6 +58,25 @@ public class MyEntityPanel extends PanelAbstract<EntityModel>  {
     @Override
     public void onInitialize() {
         super.onInitialize();
+
+        val managedObject = (ManagedObject) getModelObject();
+        val customUiVm = (CustomUiVm) managedObject.getPojo();
+
+        val iframe = new InlineFrame("iframe", getPage()) {
+            @Override
+            protected CharSequence getURL() {
+                val boundingBox = customUiVm.getBoundingBox();
+                val url = boundingBox.toUrl();
+                return String.format(
+                        "https://www.openstreetmap.org/export/embed.html?bbox=%s&layer=%s"
+                        , url
+                        , "mapnik");
+            }
+        };
+
+        addOrReplace(iframe);
     }
 
+
+
 }
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
new file mode 100644
index 0000000..dcad85d
--- /dev/null
+++ b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/CustomUiPanelFactory.java
@@ -0,0 +1,35 @@
+package demoapp.webapp.wicket.customview;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.model.IModel;
+import org.springframework.core.annotation.Order;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
+
+import demoapp.dom.ui.custom.CustomUiVm;
+
+@org.springframework.stereotype.Component
+@Order(OrderPrecedence.EARLY)
+public class CustomUiPanelFactory extends EntityComponentFactoryAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+    public CustomUiPanelFactory() {
+        super(ComponentType.ENTITY, CustomUiPanel.class);
+    }
+
+    @Override
+    protected ApplicationAdvice doAppliesTo(EntityModel entityModel) {
+        return ApplicationAdvice.appliesIf(entityModel.getObject().getPojo() instanceof CustomUiVm);
+    }
+
+    @Override
+    public Component createComponent(final String id, final IModel<?> model) {
+        final EntityModel entityModel = (EntityModel) model;
+
+        return new CustomUiPanel(id, entityModel, this);
+    }
+}
diff --git a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java b/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
deleted file mode 100644
index 2859bec..0000000
--- a/examples/demo/wicket/src/main/java/demoapp/webapp/wicket/customview/MyEntityPanelFactory.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package demoapp.webapp.wicket.customview;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.model.IModel;
-
-import org.apache.isis.applib.layout.grid.Grid;
-import org.apache.isis.applib.layout.grid.bootstrap3.BS3Grid;
-import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
-import org.apache.isis.core.metamodel.spec.ObjectSpecification;
-import org.apache.isis.viewer.wicket.model.models.EntityModel;
-import org.apache.isis.viewer.wicket.ui.ComponentType;
-import org.apache.isis.viewer.wicket.ui.components.entity.EntityComponentFactoryAbstract;
-import org.apache.isis.viewer.wicket.ui.components.layout.bs3.BS3GridPanel;
-
-import lombok.val;
-
-@org.springframework.stereotype.Component
-public class MyEntityPanelFactory  extends EntityComponentFactoryAbstract {
-
-    private static final long serialVersionUID = 1L;
-
-    public MyEntityPanelFactory() {
-        super(ComponentType.ENTITY, MyEntityPanel.class);
-    }
-
-    @Override
-    public Component createComponent(final String id, final IModel<?> model) {
-        final EntityModel entityModel = (EntityModel) model;
-
-        val objectAdapter = entityModel.getObject();
-        final ObjectSpecification specification = entityModel.getTypeOfSpecification();
-        final GridFacet facet = specification.getFacet(GridFacet.class);
-
-        final Grid grid = facet.getGrid(objectAdapter);
-        if (grid != null) {
-            if(grid instanceof BS3Grid) {
-                final BS3Grid bs3Grid = (BS3Grid) grid;
-                return new BS3GridPanel(id, entityModel, bs3Grid);
-            }
-        }
-        return new MyEntityPanel(id, entityModel, this);
-    }
-}