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/04/21 14:04:19 UTC

[isis] 03/03: ISIS-2340: getting our hands dirty with responsive layout

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

commit d1ecd1e905da02b3b61a791f451caa761b0d5393
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Apr 21 16:04:03 2020 +0200

    ISIS-2340: getting our hands dirty with responsive layout
---
 .../isis/applib/services/grid/GridService.java     |   1 -
 .../ui/components/object/ObjectFormView.java       | 164 ++++++++++++++++++---
 .../ui/pages/main/MainView_createHeader.java       |   4 +-
 .../binding/interaction/ObjectInteractor.java      |  17 ++-
 .../common/model/gridlayout/UiGridLayout.java      | 145 ++++++++++++++++++
 5 files changed, 304 insertions(+), 27 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/grid/GridService.java b/api/applib/src/main/java/org/apache/isis/applib/services/grid/GridService.java
index 13847cb..eced5e0 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/grid/GridService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/grid/GridService.java
@@ -22,7 +22,6 @@ import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.annotation.PropertyLayout;
 import org.apache.isis.applib.layout.grid.Grid;
 
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
index f561d49..f0f1572 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/components/object/ObjectFormView.java
@@ -22,19 +22,35 @@ import java.util.Collection;
 import java.util.Optional;
 
 import com.vaadin.flow.component.Component;
+import com.vaadin.flow.component.HasComponents;
 import com.vaadin.flow.component.button.Button;
 import com.vaadin.flow.component.formlayout.FormLayout;
+import com.vaadin.flow.component.formlayout.FormLayout.ResponsiveStep;
 import com.vaadin.flow.component.html.H1;
 import com.vaadin.flow.component.html.H3;
-import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
+import com.vaadin.flow.component.html.H4;
+import com.vaadin.flow.component.orderedlayout.FlexLayout;
 import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.component.tabs.Tab;
+import com.vaadin.flow.component.tabs.Tabs;
 import com.vaadin.flow.component.textfield.TextField;
 
+import org.apache.isis.applib.layout.component.ActionLayoutData;
+import org.apache.isis.applib.layout.component.FieldSet;
+import org.apache.isis.applib.layout.component.PropertyLayoutData;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3ClearFix;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Col;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
+import org.apache.isis.core.commons.internal.primitives._Ints;
+import org.apache.isis.core.commons.internal.primitives._Ints.Bound;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.UiComponentFactoryVaa;
 import org.apache.isis.incubator.viewer.vaadin.ui.components.collection.TableView;
 import org.apache.isis.viewer.common.model.binding.interaction.ObjectInteractor;
+import org.apache.isis.viewer.common.model.gridlayout.UiGridLayout;
 
 import lombok.NonNull;
 import lombok.val;
@@ -53,15 +69,114 @@ public class ObjectFormView extends VerticalLayout {
             @NonNull final UiComponentFactoryVaa uiComponentFactory,
             @NonNull final ManagedObject managedObject) {
         
+        
         val objectInteractor = ObjectInteractor.bind(managedObject);
         
         add(new H1(objectInteractor.getTitle()));
+        
+        val uiGridLayout = UiGridLayout.bind(managedObject);
+        
+        // force new row
+        //formLayout.getElement().appendChild(ElementFactory.createBr());
+        
+        val range1_12 = _Ints.Range.of(Bound.inclusive(1), Bound.inclusive(12));
+        
+        val gridVisistor = new UiGridLayout.Visitor<HasComponents>(this) {
+
+            @Override
+            protected HasComponents newRow(HasComponents container, BS3Row bs3Row) {
+                val uiRow = new FlexLayout();
+                container.add(uiRow);
+                //uiRow.setWidthFull();
+                uiRow.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
+                return uiRow;
+            }
+
+            @Override
+            protected HasComponents newCol(HasComponents container, BS3Col bs3col) {
+                
+                val uiCol = new VerticalLayout();
+                container.add(uiCol);
+                
+                final int span = range1_12.bounded(bs3col.getSpan());
+                ((FlexLayout)container).setFlexGrow(span, uiCol);
+                val widthEm = String.format("%.2fem", span * 100./16.); // 1em ~ 16px
+                uiCol.setWidth(null); // clear preset width style
+                uiCol.setMinWidth(widthEm);
+                
+                return uiCol;
+            }
+            
+            @Override
+            protected HasComponents newActionPanel(HasComponents container) {
+                val uiActionPanel = new FlexLayout();
+                container.add(uiActionPanel);
+                
+                uiActionPanel.setWrapMode(FlexLayout.WrapMode.WRAP); // allow line breaking
+                
+                return uiActionPanel;
+            }
+
+            @Override
+            protected HasComponents newTabGroup(HasComponents container, BS3TabGroup tabGroupData) {
+                val uiTabGroup = new Tabs();
+                container.add(uiTabGroup);
+                uiTabGroup.setOrientation(Tabs.Orientation.HORIZONTAL);
+                return uiTabGroup;
+            }
+
+            @Override
+            protected HasComponents newTab(HasComponents container, BS3Tab tabData) {
+                val uiTab = new Tab(tabData.getName());
+                container.add(uiTab);
+                return uiTab;
+            }
+            
+            @Override
+            protected HasComponents newFieldSet(HasComponents container, FieldSet fieldSetData) {
+                
+                container.add(new H4(fieldSetData.getName()));
+                
+                val uiFieldSet = new FormLayout();
+                container.add(uiFieldSet);
+                //uiFieldSet.setWidthFull();
+                
+                uiFieldSet.setResponsiveSteps(
+                        new ResponsiveStep("0", 1));
+                
+                return uiFieldSet;
+            }
 
-        val actionsLayout = new HorizontalLayout();
-        add(actionsLayout);
+            
+            @Override
+            protected void onClearfix(HasComponents container, BS3ClearFix clearFixData) {
+                // TODO Auto-generated method stub
+            }
+
+            @Override
+            protected void onAction(HasComponents container, ActionLayoutData actionData) {
+                objectInteractor.lookupAction(actionData.getId())
+                .ifPresent(action->{
+                    val uiAction = new Button(action.getName());
+                    container.add(uiAction);
+                });
+            }
+
+            @Override
+            protected void onProperty(HasComponents container, PropertyLayoutData propertyData) {
+                objectInteractor.lookupProperty(propertyData.getId())
+                .ifPresent(property->{
+                    val uiPropertyCreateRequest = objectInteractor.newUiComponentCreateRequest(property);
+                    val uiProperty = uiComponentFactory.componentFor(uiPropertyCreateRequest);
+                    container.add(uiProperty);                    
+                });
+            }
+            
+        };
+        
+        
         
-        val formLayout = new FormLayout();
-        add(formLayout);
+        uiGridLayout.visit(gridVisistor);
         
         val tablesLayout = new VerticalLayout();
         add(tablesLayout);
@@ -70,21 +185,32 @@ public class ObjectFormView extends VerticalLayout {
         
         // -- populate actions
         
-        objectInteractor
-        .streamVisisbleActions()
-        .forEach(action -> {
-            actionsLayout.add(new Button(action.getName()));
-        });
-        
-        // -- populate properties
+//        objectInteractor
+//        .streamVisisbleActions()
+//        .forEach(action -> {
+//            
+//            val button = new Button(action.getName());
+//            actionsLayout.add(button);
+//            
+//            Dialog dialog = new Dialog();
+//            dialog.add(new Label("Under Construction ... Close me with the esc-key or an outside click"));
+//
+//            dialog.setWidth("400px");
+//            dialog.setHeight("150px");
+//
+//            button.addClickListener(e->{
+//                dialog.open();
+//            });
+//
+////similar code in menu item ...            
+////            val actionModel = (ActionVaa)menuItemModel.getMenuActionUiModel();
+////            
+////            subMenu.addItem(
+////                    (Component)menuActionModel.getUiComponent(), 
+////                    e->subMenuEventHandler.accept(menuActionModel));
+//            
+//        });
         
-        objectInteractor
-        .streamVisisbleProperties()
-        .forEach(property -> {
-            val uiComponentCreateRequest = objectInteractor.newUiComponentCreateRequest(property);
-            val uiComponent = uiComponentFactory.componentFor(uiComponentCreateRequest);
-            formLayout.add(uiComponent);
-        });
         
         // -- populate collections
         
diff --git a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/pages/main/MainView_createHeader.java b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/pages/main/MainView_createHeader.java
index 71c12e0..23c6606 100644
--- a/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/pages/main/MainView_createHeader.java
+++ b/incubator/viewers/vaadin/ui/src/main/java/org/apache/isis/incubator/viewer/vaadin/ui/pages/main/MainView_createHeader.java
@@ -132,8 +132,8 @@ final class MainView_createHeader {
             val logo = new Image(
                     webAppContextPath.prependContextPathIfLocal(brandingLogo.get()), 
                     "brandingLogo");
-            logo.setWidth("48px");
-            logo.setHeight("48px");
+            logo.setWidth("48px"); //TODO make this part of the UI model
+            logo.setHeight("48px"); //TODO make this part of the UI model
             return logo;
         }
         return new Text(brandingName.orElse("App"));
diff --git a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectInteractor.java b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectInteractor.java
index bcbed4c..853f5ee 100644
--- a/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectInteractor.java
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/binding/interaction/ObjectInteractor.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.viewer.common.model.binding.interaction;
 
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Stream;
 
@@ -98,11 +99,17 @@ public class ObjectInteractor {
         
     }
 
-//    public UiComponentFactory.Request newUiComponentCreateRequest(
-//            final ObjectAction action) {
-//        // TODO Auto-generated method stub
-//        return null;
-//    }
+    public Optional<OneToOneAssociation> lookupProperty(String id) {
+        return streamVisisbleProperties()
+                .filter(property->Objects.equals(id, property.getId()))
+                .findFirst();
+    }
+
+    public Optional<ObjectAction> lookupAction(String id) {
+        return streamVisisbleActions()
+                .filter(action->Objects.equals(id, action.getId()))
+                .findFirst();
+    }
     
     
 }
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
new file mode 100644
index 0000000..3ee90cf
--- /dev/null
+++ b/viewers/common/src/main/java/org/apache/isis/viewer/common/model/gridlayout/UiGridLayout.java
@@ -0,0 +1,145 @@
+/*
+ *  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.common.model.gridlayout;
+
+import java.util.Optional;
+
+import org.apache.isis.applib.layout.component.ActionLayoutData;
+import org.apache.isis.applib.layout.component.FieldSet;
+import org.apache.isis.applib.layout.component.PropertyLayoutData;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3ClearFix;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Col;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Grid;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Row;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3Tab;
+import org.apache.isis.applib.layout.grid.bootstrap3.BS3TabGroup;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.core.metamodel.facets.object.grid.GridFacet;
+import org.apache.isis.core.metamodel.spec.ManagedObject;
+
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+
+@RequiredArgsConstructor(staticName = "bind")
+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 onClearfix(T container, BS3ClearFix clearFixData);
+        protected abstract void onAction(T container, ActionLayoutData actionData);
+        protected abstract void onProperty(T container, PropertyLayoutData propertyData);
+    }
+    
+    @NonNull private final ManagedObject managedObject;
+    private Optional<BS3Grid> gridData;
+    
+    public <T> void visit(Visitor<T> visitor) {
+        
+        if(gridData==null) {
+            gridData = Optional.ofNullable(
+                    managedObject.getSpecification().getFacet(GridFacet.class))
+            .map(gridFacet->gridFacet.getGrid(managedObject))
+            .filter(grid->grid instanceof BS3Grid)
+            .map(BS3Grid.class::cast);
+        }
+        
+        // recursively visit the grid
+        gridData
+        .ifPresent(bs3Grid->{
+            for(val bs3Row: bs3Grid.getRows()) {
+                visitRow(bs3Row, visitor.rootContainer, visitor);         
+            }
+        });
+        
+    }
+
+    private <T> void visitRow(BS3Row bs3Row, T container, Visitor<T> visitor) {
+        
+        val uiRow = visitor.newRow(container, bs3Row);
+        
+        for(val bs3RowContent: bs3Row.getCols()) {
+            if(bs3RowContent instanceof BS3Col) {
+                
+                visitCol((BS3Col) bs3RowContent, uiRow, visitor);
+                
+            } else if (bs3RowContent instanceof BS3ClearFix) {
+                visitor.onClearfix(uiRow, (BS3ClearFix) bs3RowContent);
+            } else {
+                throw new IllegalStateException("Unrecognized implementation of BS3RowContent");
+            }
+        }
+    }
+    
+    private <T> void visitCol(BS3Col bS3Col, T container, Visitor<T> visitor) {
+        val uiCol = visitor.newCol(container, bS3Col);
+        
+        if(bS3Col.getActions().size()>0) {
+            val uiActionPanel = visitor.newActionPanel(uiCol);
+            for(val action : bS3Col.getActions()) {
+                visitor.onAction(uiActionPanel, action);
+            }
+        }
+        
+        for(val fieldSet : bS3Col.getFieldSets()) {
+            visitFieldSet(fieldSet, uiCol, visitor);
+        }
+        
+        for(val tabGroup : bS3Col.getTabGroups()) {
+            visitTabGroup(tabGroup, uiCol, visitor);
+        }
+        
+        // columns having rows seems not permitted by XML schema
+        for(val bs3Row: bS3Col.getRows()) { 
+            throw _Exceptions.unsupportedOperation();
+            //visitRow(bs3Row, uiCol, visitor);         
+        }
+        
+    }
+    
+    private <T> void visitTabGroup(BS3TabGroup bS3ColTabGroup, T container, Visitor<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) {
+        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) {
+        val uiFieldSet = visitor.newFieldSet(container, cptFieldSet);
+        for(val propertyData: cptFieldSet.getProperties()) { 
+            visitor.onProperty(uiFieldSet, propertyData);         
+        }
+    }
+    
+
+}