You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/06/25 10:23:28 UTC

[isis] branch master updated: ISIS-2340: fx: add fallback component factory

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 9ecf4e4  ISIS-2340: fx: add fallback component factory
9ecf4e4 is described below

commit 9ecf4e4c1628442bd480ad7e9fae1799166dadeb
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jun 25 12:23:08 2020 +0200

    ISIS-2340: fx: add fallback component factory
---
 .../incubator/viewer/javafx/model/util/_fx.java    |  85 ++++++-
 .../javafx/ui/IsisModuleIncViewerJavaFxUi.java     |   5 +-
 .../ui/components/UiComponentHandlerFx.java}       |  20 +-
 .../javafx}/ui/components/debug/DebugField.java    |  58 ++---
 .../javafx/ui/components/field/CustomFieldFx.java  |  88 +++++++
 .../javafx/ui/components/object/ObjectViewFx.java  | 257 ++++++++++-----------
 .../ui/components/other/FallbackFieldFactory.java  |  20 +-
 .../viewer/javafx/ui/main/UiController.java        |  32 +--
 .../vaadin/ui/components/debug/DebugField.java     |   2 +
 .../vaadin/ui/components/object/ObjectViewVaa.java |   6 +-
 .../ui/components/other/FallbackFieldFactory.java  |   2 +-
 .../viewer/common/model}/debug/DebugUiModel.java   |   2 +-
 .../common/model/gridlayout/UiGridLayout.java      |  38 +--
 13 files changed, 384 insertions(+), 231 deletions(-)

diff --git a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
index eeb6857..cd0d2ec 100644
--- a/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
+++ b/incubator/viewers/javafx/model/src/main/java/org/apache/isis/incubator/viewer/javafx/model/util/_fx.java
@@ -23,43 +23,105 @@ import lombok.experimental.UtilityClass;
 
 import javafx.beans.property.ReadOnlyStringWrapper;
 import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Accordion;
+import javafx.scene.control.Button;
 import javafx.scene.control.Label;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
 import javafx.scene.control.TableColumn;
 import javafx.scene.control.TableView;
+import javafx.scene.control.TitledPane;
+import javafx.scene.layout.GridPane;
 import javafx.scene.layout.HBox;
 import javafx.scene.layout.Pane;
 import javafx.scene.layout.VBox;
 
 @UtilityClass
-public class _fx {
+public final class _fx {
 
     // -- OBSERVABLES
-    
+
     public static ObservableValue<String> newStringReadonly(String value) {
         val string = new ReadOnlyStringWrapper(value);
         return string;
     }
-    
+
     // -- COMPONENT FACTORIES
-    
+
     public static Label newLabel(Pane container, String label) {
         val component = new Label(label);
         container.getChildren().add(component);
         return component;
     }
     
+    public static Button newButton(Pane container, String label, EventHandler<ActionEvent> eventHandler) {
+        val component = new Button(label);
+        container.getChildren().add(component);
+        component.setOnAction(eventHandler);
+        return component;
+    }
+    
     public static HBox newHBox(Pane container) {
         val component = new HBox();
         container.getChildren().add(component);
         return component;
     }
-    
+
     public static VBox newVBox(Pane container) {
         val component = new VBox();
         container.getChildren().add(component);
         return component;
     }
+
+    public static GridPane newGrid(Pane container) {
+        val component = new GridPane();
+        container.getChildren().add(component);
+        return component;
+    }
     
+    public static GridPane newGrid(TitledPane container) {
+        val component = new GridPane();
+        container.setContent(component);
+        return component;
+    }
+    
+    public static <T extends Node> T addGridCell(GridPane container, T cellNode, int column, int row) {
+        container.add(cellNode, column, row);
+        return cellNode;
+    }
+
+    public static TabPane newTabGroup(Pane container) {
+        val component = new TabPane();
+        container.getChildren().add(component);
+        return component;
+    }
+
+    public static Tab newTab(TabPane container, String label) {
+        val component = new Tab(label);
+        container.getTabs().add(component);
+        return component;
+    }
+    
+    public static Accordion newAccordion(Pane container) {
+        val component = new Accordion();
+        container.getChildren().add(component);
+        return component;
+    }
+
+    public static TitledPane newTitledPane(Accordion container, String label) {
+        val component = new TitledPane();
+        container.getPanes().add(component);
+        component.setText(label);
+        component.setAnimated(true);
+        return component;
+    }
+    
+
     /**
      * @param <S> The type of the TableView generic type (i.e. S == TableView&lt;S&gt;)
      * @param <T> The type of the content in all cells in this TableColumn.
@@ -73,9 +135,16 @@ public class _fx {
         tableView.getColumns().add(column);
         return column;
     }
+
+    // -- LAYOUTS
     
-    
-    
-    
+    public static GridPane formLayout(GridPane component) {
+        component.setAlignment(Pos.CENTER);
+        component.setHgap(10);
+        component.setVgap(10);
+        component.setPadding(new Insets(25, 25, 25, 25));    
+        return component;
+    }
+
 
 }
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/IsisModuleIncViewerJavaFxUi.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/IsisModuleIncViewerJavaFxUi.java
index 2b49f14..986ac12 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/IsisModuleIncViewerJavaFxUi.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/IsisModuleIncViewerJavaFxUi.java
@@ -23,6 +23,7 @@ import org.springframework.context.annotation.Import;
 
 import org.apache.isis.incubator.viewer.javafx.model.events.IsisModuleIncViewerJavaFxModel;
 import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentFactoryFx;
+import org.apache.isis.incubator.viewer.javafx.ui.components.other.FallbackFieldFactory;
 import org.apache.isis.incubator.viewer.javafx.ui.main.UiBuilder;
 import org.apache.isis.incubator.viewer.javafx.ui.main.UiController;
 import org.apache.isis.viewer.common.model.IsisModuleViewerCommon;
@@ -37,11 +38,13 @@ import org.apache.isis.viewer.common.model.IsisModuleViewerCommon;
         IsisModuleViewerCommon.class,
         IsisModuleIncViewerJavaFxModel.class,
         
-        
         // @Components's
         UiBuilder.class,
         UiController.class,
         
+        // component factories
+        FallbackFieldFactory.class,
+        
         // @Service's
         UiComponentFactoryFx.class
 
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/UiComponentHandlerFx.java
similarity index 62%
copy from incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java
copy to incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/UiComponentHandlerFx.java
index 174c6f6..0c82431 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/UiComponentHandlerFx.java
@@ -16,23 +16,13 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.incubator.viewer.vaadin.ui.components.debug;
+package org.apache.isis.incubator.viewer.javafx.ui.components;
 
-import java.util.Map;
+import org.apache.isis.viewer.common.model.binding.UiComponentFactory;
 
-import org.apache.isis.core.commons.internal.collections._Maps;
+import javafx.scene.Node;
 
-import lombok.Value;
+public interface UiComponentHandlerFx 
+extends UiComponentFactory.Handler<Node> {
 
-@Value(staticConstructor = "of")
-public class DebugUiModel {
-
-    private final String summaryText;
-    private final Map<String, String> keyValuePairs = _Maps.newLinkedHashMap();
-    
-    public DebugUiModel withProperty(String key, String value) {
-        keyValuePairs.put(key, value);
-        return this;
-    }
-    
 }
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/debug/DebugField.java
similarity index 51%
copy from incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java
copy to incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/debug/DebugField.java
index a03dc34..192d771 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/debug/DebugField.java
@@ -16,34 +16,37 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.incubator.viewer.vaadin.ui.components.debug;
+package org.apache.isis.incubator.viewer.javafx.ui.components.debug;
 
-import com.vaadin.flow.component.customfield.CustomField;
-import com.vaadin.flow.component.details.Details;
-import com.vaadin.flow.component.details.DetailsVariant;
-import com.vaadin.flow.component.formlayout.FormLayout;
-import com.vaadin.flow.component.textfield.TextArea;
+import java.util.concurrent.atomic.LongAdder;
+
+import org.apache.isis.incubator.viewer.javafx.model.util._fx;
+import org.apache.isis.incubator.viewer.javafx.ui.components.field.CustomFieldFx;
+import org.apache.isis.viewer.common.model.debug.DebugUiModel;
 
 import lombok.val;
 
-public class DebugField extends CustomField<DebugUiModel> {
+import javafx.scene.control.Accordion;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TitledPane;
+import javafx.scene.layout.GridPane;
+
+public class DebugField extends CustomFieldFx<DebugUiModel> {
 
-    private static final long serialVersionUID = 1L;
-    
-    private final Details details = new Details();
     private DebugUiModel model;
+    private final GridPane detailGrid;
+    private final TitledPane detailPane;
 
     public DebugField(String label) {
         super();
         setLabel(label);
-        add(details);
-
-        val formLayout = new FormLayout();
-        details.addContent(formLayout);
-        details.setSummaryText("Debug");
-        details.addThemeVariants(DetailsVariant.SMALL);
+        val accordion = add(new Accordion());
+        detailPane = _fx.newTitledPane(accordion, "Debug");
+        detailGrid = _fx.formLayout(_fx.newGrid(detailPane));
     }
 
+
     @Override
     protected DebugUiModel generateModelValue() {
         return model;
@@ -53,24 +56,21 @@ public class DebugField extends CustomField<DebugUiModel> {
     protected void setPresentationValue(DebugUiModel model) {
         this.model = model;
         
-        details.setSummaryText(model.getSummaryText());
-
-        details.getContent().findFirst()
-        .map(FormLayout.class::cast)
-        .ifPresent(formLayout->{
+        detailPane.setText(model.getSummaryText());
+        
+        val rowIndex = new LongAdder();
         
-            formLayout.removeAll();
+        model.getKeyValuePairs().forEach((k, v)->{
             
-            model.getKeyValuePairs().forEach((k, v)->{
-                val textArea = new TextArea();
-                textArea.setLabel(k);
-                textArea.setValue(v);
-                textArea.setInvalid(true);
-                formLayout.add(textArea);
-            });
+            val row = rowIndex.intValue();
             
+            _fx.addGridCell(detailGrid, new Label(k), 0, row);
+            _fx.addGridCell(detailGrid, new TextArea(v), 1, row);
+            
+            rowIndex.increment();
         });
         
+        
     }
 
     
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/field/CustomFieldFx.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/field/CustomFieldFx.java
new file mode 100644
index 0000000..6cfa30b
--- /dev/null
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/field/CustomFieldFx.java
@@ -0,0 +1,88 @@
+/*
+ *  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.incubator.viewer.javafx.ui.components.field;
+
+import java.util.Objects;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Pane;
+
+public abstract class CustomFieldFx<T> extends Pane {
+
+    protected final Label label = new Label();
+    
+    private final ObjectProperty<T> value = new SimpleObjectProperty<T>();
+    
+    public void setLabel(String label) {
+        this.label.setText(label);
+        getChildren().add(this.label);
+    }
+
+    
+//    /**
+//     * Sets the value of this object. If the new value is not equal to
+//     * {@code getValue()}, fires a value change event. May throw
+//     * {@code IllegalArgumentException} if the value is not acceptable.
+//     * <p>
+//     * <i>Implementation note:</i> the implementing class should document
+//     * whether null values are accepted or not, and override
+//     * {@link #getEmptyValue()} if the empty value is not {@code null}.
+//     *
+//     * @param newValue
+//     *            the new value
+//     * @throws IllegalArgumentException
+//     *             if the value is invalid
+//     */
+    public void setValue(T newValue) {
+        if(Objects.equals(value.getValue(), newValue)) {
+            return; // ignore
+        }
+        value.setValue(newValue);
+        setPresentationValue(newValue);
+    }
+    
+    protected <N extends Node> N add(N node) {
+        getChildren().add(node);
+        return node;
+    }
+    
+    /**
+     * This method should return the value of the field, based on value of the internal fields.
+     *
+     * @return new value of the field.
+     */
+    protected abstract T generateModelValue();
+
+    
+    /**
+     * This method should be implemented to set the value of the fields contained
+     * in this custom field according to the value of the parameter.
+     * It can also be use to show the value to the user in some way, 
+     * like placing it in an element contained on the field.
+     *
+     * @param newPresentationValue The new presentation value.
+     */
+    protected abstract void setPresentationValue(T value);
+    
+    
+
+}
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/object/ObjectViewFx.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/object/ObjectViewFx.java
index 4684efd..a2f26c6 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/object/ObjectViewFx.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/object/ObjectViewFx.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.incubator.viewer.javafx.ui.components.object;
 
+import java.util.function.Consumer;
+
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.layout.component.ActionLayoutData;
 import org.apache.isis.applib.layout.component.CollectionLayoutData;
@@ -31,10 +33,12 @@ import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
 import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
 import org.apache.isis.core.metamodel.interactions.managed.ActionInteraction;
 import org.apache.isis.core.metamodel.interactions.managed.CollectionInteraction;
+import org.apache.isis.core.metamodel.interactions.managed.ManagedAction;
 import org.apache.isis.core.metamodel.interactions.managed.PropertyInteraction;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.incubator.viewer.javafx.model.util._fx;
 import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentFactoryFx;
+import org.apache.isis.incubator.viewer.javafx.ui.components.collections.TableViewFx;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory;
 import org.apache.isis.viewer.common.model.binding.interaction.ObjectBinding;
 import org.apache.isis.viewer.common.model.gridlayout.UiGridLayout;
@@ -42,10 +46,8 @@ import org.apache.isis.viewer.common.model.gridlayout.UiGridLayout;
 import lombok.NonNull;
 import lombok.val;
 
-import javafx.scene.Parent;
-import javafx.scene.control.Label;
-import javafx.scene.control.Tab;
-import javafx.scene.layout.HBox;
+import javafx.geometry.Insets;
+import javafx.scene.control.TabPane;
 import javafx.scene.layout.Pane;
 import javafx.scene.layout.VBox;
 
@@ -53,8 +55,9 @@ public class ObjectViewFx extends VBox {
     
     public static ObjectViewFx fromObject(
             @NonNull final UiComponentFactoryFx uiComponentFactory,
+            @NonNull final Consumer<ManagedAction> actionEventHandler,
             @NonNull final ManagedObject managedObject) {
-        return new ObjectViewFx(uiComponentFactory, managedObject);
+        return new ObjectViewFx(uiComponentFactory, actionEventHandler, managedObject);
     }
     
     /**
@@ -63,6 +66,7 @@ public class ObjectViewFx extends VBox {
      */
     protected ObjectViewFx(
             final UiComponentFactoryFx uiComponentFactory,
+            final Consumer<ManagedAction> actionEventHandler,
             final ManagedObject managedObject) {
 
 
@@ -70,135 +74,130 @@ public class ObjectViewFx extends VBox {
 
         val uiGridLayout = UiGridLayout.bind(managedObject);
 
-        // force new row
-        //formLayout.getElement().appendChild(ElementFactory.createBr());
-
-//        val gridVisistor = new UiGridLayout.Visitor<Pane>(this) {
-//
-//            @Override
-//            protected void onObjectTitle(Pane container, DomainObjectLayoutData domainObjectData) {
-//                _fx.newLabel(container, objectInteractor.getTitle());
-//            }
-//
-//            @Override
-//            protected Pane newRow(Pane container, BS3Row bs3Row) {
-//                val uiRow = _fx.newHBox(container);
-//                //uiRow.setWidthFull();
-//                //uiRow.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
-//                return uiRow;
-//            }
-//
-//            @Override
-//            protected Parent newCol(Parent container, BS3Col bs3col) {
-//
-//                val uiCol = _fx.newVBox(container);
-//                
-//                val uiCol = new VerticalLayout();
-//                container.add(uiCol);
-//
+        val gridVisistor = new UiGridLayout.Visitor<Pane, TabPane>(this) {
+
+            @Override
+            protected void onObjectTitle(Pane container, DomainObjectLayoutData domainObjectData) {
+                _fx.newLabel(container, objectInteractor.getTitle());
+            }
+
+            @Override
+            protected Pane newRow(Pane container, BS3Row bs3Row) {
+                val uiRow = _fx.newHBox(container);
+                //uiRow.setWidthFull();
+                //uiRow.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
+                return uiRow;
+            }
+
+            @Override
+            protected Pane newCol(Pane container, BS3Col bs3col) {
+
+                val uiCol = _fx.newVBox(container);
+                
+                uiCol.setOpaqueInsets(new Insets(10, 10, 10, 10));
+                
+
 //                final int span = bs3col.getSpan();
 //                ((FlexLayout)container).setFlexGrow(span, uiCol);
 //                val widthEm = String.format("%dem", span * 3); // 1em ~ 16px
 //                uiCol.setWidth(null); // clear preset width style
 //                uiCol.setMinWidth(widthEm);
-//
-//                return uiCol;
-//            }
-//
-//            @Override
-//            protected Pane newActionPanel(Pane container) {
-//                val uiActionPanel = new FlexLayout();
-//                container.add(uiActionPanel);
-//
-//                uiActionPanel.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
-//                uiActionPanel.setAlignItems(Alignment.BASELINE);
-//                return uiActionPanel;
-//            }
-//
-//            @Override
-//            protected Pane newTabGroup(Pane container, BS3TabGroup tabGroupData) {
-//                val uiTabGroup = new Tabs();
-//                container.add(uiTabGroup);
-//                uiTabGroup.setOrientation(Tabs.Orientation.HORIZONTAL);
-//                return uiTabGroup;
-//            }
-//
-//            @Override
-//            protected Pane newTab(Pane container, BS3Tab tabData) {
-//                val uiTab = new Tab(tabData.getName());
-//                container.add(uiTab);
-//                return uiTab;
-//            }
-//
-//            @Override
-//            protected Pane newFieldSet(Pane container, FieldSet fieldSetData) {
-//
-//                container.add(new H2(fieldSetData.getName()));
-//
-//                val uiFieldSet = new FormLayout();
-//                container.add(uiFieldSet);
-//
-//                uiFieldSet.setResponsiveSteps(
-//                        new ResponsiveStep("0", 1)); // single column only
-//
-//                return uiFieldSet;
-//            }
-//
-//
-//            @Override
-//            protected void onClearfix(Pane container, BS3ClearFix clearFixData) {
-//                // TODO Auto-generated method stub
-//            }
-//
-//            @Override
-//            protected void onAction(Pane container, ActionLayoutData actionData) {
-//                
-//                val owner = objectInteractor.getManagedObject();
-//                ActionInteraction.start(owner, actionData.getId())
-//                .checkVisibility(Where.OBJECT_FORMS)
-//                .get()
-//                .ifPresent(managedAction -> {
-//                    val uiAction = ActionButton.forManagedAction(uiComponentFactory, managedAction);
-//                    container.add(uiAction);
-//                });
-//            }
-//
-//            @Override
-//            protected void onProperty(Pane container, PropertyLayoutData propertyData) {
-//                
-//                val owner = objectInteractor.getManagedObject();
-//                
-//                PropertyInteraction.start(owner, propertyData.getId())
-//                .checkVisibility(Where.OBJECT_FORMS)
-//                .get()
-//                .ifPresent(managedProperty -> {
-//                    val uiProperty = uiComponentFactory
-//                            .componentFor(UiComponentFactory.Request.of(Where.OBJECT_FORMS, managedProperty));
-//                    container.add(uiProperty);
-//                });
-//            }
-//
-//            @Override
-//            protected void onCollection(Pane container, CollectionLayoutData collectionData) {
-//                
-//                val owner = objectInteractor.getManagedObject();
-//                
-//                CollectionInteraction.start(owner, collectionData.getId())
-//                .checkVisibility(Where.OBJECT_FORMS)
-//                .get()
-//                .ifPresent(managedCollection -> {
-//                    container.add(new H3(managedCollection.getName()));
-//                    
-//                    val uiCollection = TableViewVaa.forManagedCollection(managedCollection);
-//                    container.add(uiCollection);
-//                });
-//                
-//            }
-//
-//        };
-//
-//        uiGridLayout.visit(gridVisistor);
-//        setWidthFull();
+
+                return uiCol;
+            }
+
+            @Override
+            protected Pane newActionPanel(Pane container) {
+                val uiActionPanel = _fx.newHBox(container);
+                
+                //uiActionPanel.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
+                //uiActionPanel.setAlignItems(Alignment.BASELINE);
+                return uiActionPanel;
+            }
+
+            @Override
+            protected TabPane newTabGroup(Pane container, BS3TabGroup tabGroupData) {
+                val uiTabGroup = _fx.newTabGroup(container);
+                return uiTabGroup;
+            }
+
+            @Override
+            protected Pane newTab(TabPane container, BS3Tab tabData) {
+                val uiTab = _fx.newTab(container, tabData.getName());
+                val uiTabContentPane = new VBox();
+                uiTab.setContent(uiTabContentPane);
+                return uiTabContentPane; 
+            }
+
+            @Override
+            protected Pane newFieldSet(Pane container, FieldSet fieldSetData) {
+
+                _fx.newLabel(container, fieldSetData.getName())
+                .setStyle("isis-field-set-label"); // corresponds to H2 (html)
+                
+                val uiFieldSet = _fx.newGrid(container);
+                return uiFieldSet;
+            }
+
+
+            @Override
+            protected void onClearfix(Pane container, BS3ClearFix clearFixData) {
+                // TODO Auto-generated method stub
+            }
+
+            @Override
+            protected void onAction(Pane container, ActionLayoutData actionData) {
+                
+                val owner = objectInteractor.getManagedObject();
+                ActionInteraction.start(owner, actionData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedAction -> {
+                    _fx.newButton(
+                            container, 
+                            actionData.getNamed(), 
+                            event->actionEventHandler.accept(managedAction));
+                });
+            }
+
+            @Override
+            protected void onProperty(Pane container, PropertyLayoutData propertyData) {
+                
+                val owner = objectInteractor.getManagedObject();
+                
+                PropertyInteraction.start(owner, propertyData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedProperty -> {
+                    val uiProperty = uiComponentFactory
+                            .componentFor(UiComponentFactory.Request.of(Where.OBJECT_FORMS, managedProperty));
+                    container.getChildren().add(uiProperty);
+                });
+            }
+
+            @Override
+            protected void onCollection(Pane container, CollectionLayoutData collectionData) {
+                
+                val owner = objectInteractor.getManagedObject();
+                
+                CollectionInteraction.start(owner, collectionData.getId())
+                .checkVisibility(Where.OBJECT_FORMS)
+                .get()
+                .ifPresent(managedCollection -> {
+                    
+                    _fx.newLabel(container, managedCollection.getName())
+                    .setStyle("isis-collection-label"); // corresponds to H3 (html)
+                    
+                    val uiCollection = TableViewFx.forManagedCollection(managedCollection);
+                    container.getChildren().add(uiCollection);
+                });
+                
+            }
+
+        };
+
+        uiGridLayout.visit(gridVisistor);
+        //setWidthFull();
 
     }
 
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
similarity index 83%
copy from incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java
copy to incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
index f281cac..0118b18 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/components/other/FallbackFieldFactory.java
@@ -16,34 +16,34 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.incubator.viewer.vaadin.ui.components.other;
+package org.apache.isis.incubator.viewer.javafx.ui.components.other;
 
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-import com.vaadin.flow.component.Component;
-
 import org.springframework.core.annotation.Order;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.core.commons.internal.collections._Maps;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
-import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa;
-import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
-import org.apache.isis.incubator.viewer.vaadin.ui.components.debug.DebugField;
-import org.apache.isis.incubator.viewer.vaadin.ui.components.debug.DebugUiModel;
+import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentFactoryFx;
+import org.apache.isis.incubator.viewer.javafx.ui.components.UiComponentHandlerFx;
+import org.apache.isis.incubator.viewer.javafx.ui.components.debug.DebugField;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
+import org.apache.isis.viewer.common.model.debug.DebugUiModel;
 
 import lombok.val;
 
+import javafx.scene.Node;
+
 @org.springframework.stereotype.Component
 @Order(OrderPrecedence.LAST)
-public class FallbackFieldFactory implements UiComponentHandlerVaa {
+public class FallbackFieldFactory implements UiComponentHandlerFx {
     
-    @Inject private Provider<UiComponentFactoryVaa> uiComponentFactory;
+    @Inject private Provider<UiComponentFactoryFx> uiComponentFactory;
 
     @Override
     public boolean isHandling(Request request) {
@@ -51,7 +51,7 @@ public class FallbackFieldFactory implements UiComponentHandlerVaa {
     }
 
     @Override
-    public Component handle(Request request) {
+    public Node handle(Request request) {
         
         val spec = request.getObjectFeature().getSpecification();
         
diff --git a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiController.java b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiController.java
index 145cbcd..9427370 100644
--- a/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiController.java
+++ b/incubator/viewers/javafx/ui/src/main/java/org/apache/isis/incubator/viewer/javafx/ui/main/UiController.java
@@ -70,29 +70,31 @@ public class UiController {
         
         val commonContext = IsisAppCommonContext.of(metaModelContext);
         
-        val leftMenuBuilder = MenuBuilderFx.of(menuBarLeft, this::onMenuAction);
-        val rightMenuBuilder = MenuBuilderFx.of(menuBarRight, this::onMenuAction);
+        val leftMenuBuilder = MenuBuilderFx.of(menuBarLeft, this::onActionLinkClicked);
+        val rightMenuBuilder = MenuBuilderFx.of(menuBarRight, this::onActionLinkClicked);
         
         header.getPrimary().buildMenuItems(commonContext, leftMenuBuilder);
         header.getSecondary().buildMenuItems(commonContext, rightMenuBuilder);
         header.getTertiary().buildMenuItems(commonContext, rightMenuBuilder);
     }
 
-    private void onMenuAction(ManagedAction managedAction) {
-        log.info("about to invoke action {}", managedAction.getIdentifier());
-        
-        val resultOrVeto = managedAction.invoke(Can.empty());
-        
-        val result = resultOrVeto.leftIfAny();
+    private void onActionLinkClicked(ManagedAction managedAction) {
+        log.info("about to build an action prompt for {}", managedAction.getIdentifier());
 
-        if (result.getSpecification().isParentedOrFreeCollection()) {
-            replaceContent(TableViewFx.fromCollection(result));
-        } else {
-            replaceContent(ObjectViewFx.fromObject(uiComponentFactory, result));
-        }
+        // TODO get an ActionPrompt, then on invocation show the result in the content view
         
-        // TODO get an ActionPrompt, then on invocation show the result in the contentView
-        sampleTextArea.setText(String.format("action result to be rendered here\nfor %s", managedAction.getIdentifier()));
+        isisInteractionFactory.runAnonymous(()->{
+            
+            val resultOrVeto = managedAction.invoke(Can.empty());
+            
+            val result = resultOrVeto.leftIfAny();
+
+            if (result.getSpecification().isParentedOrFreeCollection()) {
+                replaceContent(TableViewFx.fromCollection(result));
+            } else {
+                replaceContent(ObjectViewFx.fromObject(uiComponentFactory, this::onActionLinkClicked, result));
+            }
+        });
     }
     
     private void replaceContent(Node node) {
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java
index a03dc34..6a9f399 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugField.java
@@ -24,6 +24,8 @@ import com.vaadin.flow.component.details.DetailsVariant;
 import com.vaadin.flow.component.formlayout.FormLayout;
 import com.vaadin.flow.component.textfield.TextArea;
 
+import org.apache.isis.viewer.common.model.debug.DebugUiModel;
+
 import lombok.val;
 
 public class DebugField extends CustomField<DebugUiModel> {
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectViewVaa.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectViewVaa.java
index 3a95a79..d5189c4 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectViewVaa.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectViewVaa.java
@@ -80,7 +80,7 @@ public class ObjectViewVaa extends VerticalLayout {
         // force new row
         //formLayout.getElement().appendChild(ElementFactory.createBr());
 
-        val gridVisistor = new UiGridLayout.Visitor<HasComponents>(this) {
+        val gridVisistor = new UiGridLayout.Visitor<HasComponents, Tabs>(this) {
 
             @Override
             protected void onObjectTitle(HasComponents container, DomainObjectLayoutData domainObjectData) {
@@ -134,7 +134,7 @@ public class ObjectViewVaa extends VerticalLayout {
             }
 
             @Override
-            protected HasComponents newTabGroup(HasComponents container, BS3TabGroup tabGroupData) {
+            protected Tabs newTabGroup(HasComponents container, BS3TabGroup tabGroupData) {
                 val uiTabGroup = new Tabs();
                 container.add(uiTabGroup);
                 uiTabGroup.setOrientation(Tabs.Orientation.HORIZONTAL);
@@ -142,7 +142,7 @@ public class ObjectViewVaa extends VerticalLayout {
             }
 
             @Override
-            protected HasComponents newTab(HasComponents container, BS3Tab tabData) {
+            protected HasComponents newTab(Tabs container, BS3Tab tabData) {
                 val uiTab = new Tab(tabData.getName());
                 container.add(uiTab);
                 return uiTab;
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java
index f281cac..11e4833 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/other/FallbackFieldFactory.java
@@ -34,8 +34,8 @@ import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentHandlerVaa;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.debug.DebugField;
-import org.apache.isis.incubator.viewer.vaadin.ui.components.debug.DebugUiModel;
 import org.apache.isis.viewer.common.model.binding.UiComponentFactory.Request;
+import org.apache.isis.viewer.common.model.debug.DebugUiModel;
 
 import lombok.val;
 
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/debug/DebugUiModel.java
similarity index 94%
rename from incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java
rename to viewers/common/src/main/java/org/apache/isis/viewer/common/model/debug/DebugUiModel.java
index 174c6f6..6362805 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/debug/DebugUiModel.java
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/debug/DebugUiModel.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.incubator.viewer.vaadin.ui.components.debug;
+package org.apache.isis.viewer.common.model.debug;
 
 import java.util.Map;
 
diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/gridlayout/UiGridLayout.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/gridlayout/UiGridLayout.java
index 2df6633..bd7ea3d 100644
--- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/gridlayout/UiGridLayout.java
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/gridlayout/UiGridLayout.java
@@ -45,26 +45,26 @@ import lombok.val;
 public class UiGridLayout {
 
     @RequiredArgsConstructor
-    public static abstract class Visitor<T> {
-        private final T rootContainer;
-        protected abstract T newActionPanel(T container);
-        protected abstract T newRow(T container, BS3Row rowData);
-        protected abstract T newCol(T container, BS3Col colData);
-        protected abstract T newTabGroup(T container, BS3TabGroup tabGroupData);
-        protected abstract T newTab(T container, BS3Tab tabData);
-        protected abstract T newFieldSet(T container, FieldSet fieldSetData);
-        protected abstract void onObjectTitle(T container, DomainObjectLayoutData domainObjectData);
-        protected abstract void onClearfix(T container, BS3ClearFix clearFixData);
-        protected abstract void onAction(T container, ActionLayoutData actionData);
-        protected abstract void onProperty(T container, PropertyLayoutData propertyData);
-        protected abstract void onCollection(T container, CollectionLayoutData collectionData);
+    public static abstract class Visitor<C, T> {
+        private final C rootContainer;
+        protected abstract C newActionPanel(C container);
+        protected abstract C newRow(C container, BS3Row rowData);
+        protected abstract C newCol(C container, BS3Col colData);
+        protected abstract T newTabGroup(C container, BS3TabGroup tabGroupData);
+        protected abstract C newTab(T tabGroup, BS3Tab tabData);
+        protected abstract C newFieldSet(C container, FieldSet fieldSetData);
+        protected abstract void onObjectTitle(C container, DomainObjectLayoutData domainObjectData);
+        protected abstract void onClearfix(C container, BS3ClearFix clearFixData);
+        protected abstract void onAction(C container, ActionLayoutData actionData);
+        protected abstract void onProperty(C container, PropertyLayoutData propertyData);
+        protected abstract void onCollection(C container, CollectionLayoutData collectionData);
         
     }
     
     @NonNull private final ManagedObject managedObject;
     private _Lazy<Optional<BS3Grid>> gridData = _Lazy.threadSafe(this::initGridData);
     
-    public <T> void visit(Visitor<T> visitor) {
+    public <C, T> void visit(Visitor<C, T> visitor) {
         
         // recursively visit the grid
         gridData.get()
@@ -84,7 +84,7 @@ public class UiGridLayout {
         .map(BS3Grid.class::cast);
     }
 
-    private <T> void visitRow(BS3Row bs3Row, T container, Visitor<T> visitor) {
+    private <C, T> void visitRow(BS3Row bs3Row, C container, Visitor<C, T> visitor) {
         
         val uiRow = visitor.newRow(container, bs3Row);
         
@@ -101,7 +101,7 @@ public class UiGridLayout {
         }
     }
     
-    private <T> void visitCol(BS3Col bS3Col, T container, Visitor<T> visitor) {
+    private <C, T> void visitCol(BS3Col bS3Col, C container, Visitor<C, T> visitor) {
         val uiCol = visitor.newCol(container, bS3Col);
         
         val hasDomainObject = bS3Col.getDomainObject()!=null; 
@@ -142,21 +142,21 @@ public class UiGridLayout {
         
     }
     
-    private <T> void visitTabGroup(BS3TabGroup bS3ColTabGroup, T container, Visitor<T> visitor) {
+    private <C, T> void visitTabGroup(BS3TabGroup bS3ColTabGroup, C container, Visitor<C, T> visitor) {
         val uiTabGroup = visitor.newTabGroup(container, bS3ColTabGroup);
         for(val bs3Tab: bS3ColTabGroup.getTabs()) { 
             visitTab(bs3Tab, uiTabGroup, visitor);
         }
     }
     
-    private <T> void visitTab(BS3Tab bS3Tab, T container, Visitor<T> visitor) {
+    private <C, T> void visitTab(BS3Tab bS3Tab, T container, Visitor<C, T> visitor) {
         val uiTab = visitor.newTab(container, bS3Tab);
         for(val bs3Row: bS3Tab.getRows()) { 
             visitRow(bs3Row, uiTab, visitor);         
         }
     }
     
-    private <T> void visitFieldSet(FieldSet cptFieldSet, T container, Visitor<T> visitor) {
+    private <C, T> void visitFieldSet(FieldSet cptFieldSet, C container, Visitor<C, T> visitor) {
         val uiFieldSet = visitor.newFieldSet(container, cptFieldSet);
         for(val propertyData: cptFieldSet.getProperties()) { 
             visitor.onProperty(uiFieldSet, propertyData);