diff --git a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/GridStackLayout.java b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/GridStackLayout.java index 6e7bec0..d7c0747 100644 --- a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/GridStackLayout.java +++ b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/GridStackLayout.java @@ -17,6 +17,18 @@ */ package org.vaadin.alump.gridstack; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.vaadin.alump.gridstack.client.shared.GridStackChildOptions; +import org.vaadin.alump.gridstack.client.shared.GridStackLayoutState; +import org.vaadin.alump.gridstack.client.shared.GridStackMoveData; +import org.vaadin.alump.gridstack.client.shared.GridStackServerRpc; + import com.vaadin.annotations.JavaScript; import com.vaadin.event.LayoutEvents; import com.vaadin.shared.Connector; @@ -25,9 +37,6 @@ import com.vaadin.shared.Registration; import com.vaadin.ui.AbstractLayout; import com.vaadin.ui.Component; -import org.vaadin.alump.gridstack.client.shared.*; - -import java.util.*; /** * Vaadin layout using gridstack.js library to layout components @@ -61,8 +70,8 @@ public void layoutClick(MouseEventDetails mouseEventDetails, Connector connector @Override public void onChildrenMoved(List moves) { Collection events = new ArrayList(); - for(GridStackMoveData move : moves) { - Component childComponent = (Component)move.child; + for (GridStackMoveData move : moves) { + Component childComponent = (Component) move.child; GridStackCoordinates oldCoordinates = getCoordinates(childComponent); GridStackChildOptions info = getState(false).childOptions.get(move.child); @@ -291,7 +300,7 @@ public Component getComponent(int x, int y) { * @return Component at slot, or null if component not found */ public Component getComponent(int x, int y, boolean acceptInsideHit) { - for(Connector connector : getState().childOptions.keySet()) { + for (Connector connector : getState().childOptions.keySet()) { GridStackChildOptions info = getState().childOptions.get(connector); if(acceptInsideHit) { if(x >= info.x && x < (info.x + info.width) && y >= info.y && y < (info.y + info.height)) { @@ -421,7 +430,7 @@ protected void fireMoveEvents(Collection events) { return; } - for(GridStackMoveEvent.GridStackMoveListener listener : moveListeners) { + for (GridStackMoveEvent.GridStackMoveListener listener : moveListeners) { listener.onGridStackMove(events); } } @@ -704,4 +713,24 @@ public Optional getChildItemStyleName(Component child) { .flatMap(o -> Optional.ofNullable(o.styleName)); } + /** + * Sets read only state to child component. If the child is set to read only it will not be able to be moved, resized or moved by another component + * + * @param child - Child component + * @param readOnly - State of the component + */ + public void setComponentReadOnly(final Component child, final boolean readOnly) { + getComponentOptions(child).readOnly = readOnly; + } + + /** + * Check if the child component is in read only state + * + * @param child - Child component + * @return true if the component is in read only state + */ + public boolean isComponentReadOnly(final Component child) { + return getComponentOptions(child).readOnly; + } + } diff --git a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/GwtGridStack.java b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/GwtGridStack.java index c3f4ea7..864dc66 100644 --- a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/GwtGridStack.java +++ b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/GwtGridStack.java @@ -17,6 +17,17 @@ */ package org.vaadin.alump.gridstack.client; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import org.vaadin.alump.gridstack.client.shared.GridStackChildOptions; +import org.vaadin.alump.gridstack.client.shared.GridStackOptions; + import com.google.gwt.core.client.Duration; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; @@ -24,11 +35,6 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; -import org.vaadin.alump.gridstack.client.shared.GridStackChildOptions; -import org.vaadin.alump.gridstack.client.shared.GridStackOptions; - -import java.util.*; -import java.util.logging.Logger; public class GwtGridStack extends ComplexPanel { @@ -78,7 +84,7 @@ public boolean isInitialized() { } public void setOptions(Integer width, Integer height, GridStackOptions options) { - if(!initialized) { + if (!initialized) { initialize(width, height, GwtGridStackOptions.createFrom(options)); } else { if(options.cellHeight != null) { @@ -91,7 +97,7 @@ public void setOptions(Integer width, Integer height, GridStackOptions options) } public void initialize(Integer width, Integer height, GwtGridStackOptions options) { - if(initialized) { + if (initialized) { LOGGER.severe("gridstack already initialized"); return; } @@ -123,7 +129,7 @@ public void add(Widget widget) { public void add(Widget widget, GridStackChildOptions info) { Element wrapper = createWrapper(info); - if(initialized) { + if (initialized) { addWidgetWrapperToGridStack(wrapper); } else { getElement().appendChild(wrapper); @@ -135,8 +141,10 @@ public void add(Widget widget, GridStackChildOptions info) { @Override public boolean remove(Widget widget) { - if(initialized) { - Element wrapper = widget.getElement().getParentElement().getParentElement(); + if (initialized) { + Element wrapper = widget.getElement() + .getParentElement() + .getParentElement(); widgetWrappers.remove(wrapper); removeWidgetWrapperFromGridStack(wrapper); wrapper.removeFromParent(); @@ -145,7 +153,8 @@ public boolean remove(Widget widget) { } protected Element createWrapper(GridStackChildOptions info) { - Element wrapper = Document.get().createDivElement(); + Element wrapper = Document.get() + .createDivElement(); wrapper.addClassName("grid-stack-item"); if(info.x >= 0 && info.y >= 0) { @@ -174,7 +183,14 @@ protected Element createWrapper(GridStackChildOptions info) { wrapper.setAttribute("data-gs-locked", "yes"); } - Element content = Document.get().createDivElement(); + if (info.readOnly) { + wrapper.setAttribute("data-gs-locked", "yes"); + wrapper.setAttribute("data-gs-no-resize", "yes"); + wrapper.setAttribute("data-gs-no-move", "yes"); + } + + final Element content = Document.get() + .createDivElement(); content.addClassName(CONTENT_CLASSNAME); if(!info.useDragHandle) { @@ -191,8 +207,9 @@ protected Element createWrapper(GridStackChildOptions info) { wrapper.appendChild(content); - if(info.useDragHandle) { - Element dragHandle = Document.get().createDivElement(); + if (info.useDragHandle) { + Element dragHandle = Document.get() + .createDivElement(); dragHandle.addClassName("separate-handle"); dragHandle.addClassName(DRAG_HANDLE_CLASSNAME); wrapper.appendChild(dragHandle); @@ -345,12 +362,15 @@ public void removeReadyListener(GwtGridStackReadyListener listener) { public void updateChild(Widget widget, GridStackChildOptions options) { - Element wrapper = widget.getElement().getParentElement().getParentElement(); + Element wrapper = widget.getElement() + .getParentElement() + .getParentElement(); updateWidgetWrapper(wrapper, options.x, options.y, options.width, options.height); updateWidgetSizeLimits(wrapper, GwtGridSizeLimits.create(options)); setLocked(wrapper, options.locked); + setReadOnly(wrapper, options.readOnly); - if(options.disableScrolling) { + if (options.disableScrolling) { wrapper.addClassName(DISABLE_SCROLLING_CLASSNAME); } else { wrapper.removeClassName(DISABLE_SCROLLING_CLASSNAME); @@ -391,9 +411,19 @@ protected native final void setLocked(Element element, boolean locked) }); }-*/; + protected native final void setReadOnly(Element element, boolean readOnly) + /*-{ + var elementId = this.@org.vaadin.alump.gridstack.client.GwtGridStack::elementId; + $wnd.$(function () { + var grid = $wnd.$('#' + elementId).data('gridstack'); + grid.locked(element, readOnly); + grid.resizable(element, !readOnly); + grid.movable(element, !readOnly); + }); + }-*/; public void commit() { - if(initialized && isAttached()) { + if (initialized && isAttached()) { nativeCommit(); } } @@ -408,7 +438,7 @@ protected native final void nativeCommit() }-*/; public void batchUpdate() { - if(initialized && isAttached()) { + if (initialized && isAttached()) { nativeBatchUpdate(); } } diff --git a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/shared/GridStackChildOptions.java b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/shared/GridStackChildOptions.java index 818d016..2ce3967 100644 --- a/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/shared/GridStackChildOptions.java +++ b/gridstack-addon/src/main/java/org/vaadin/alump/gridstack/client/shared/GridStackChildOptions.java @@ -32,6 +32,7 @@ public class GridStackChildOptions implements Serializable { public Integer maxWidth = null; public Integer maxHeight = null; public boolean locked = false; + public boolean readOnly = false; public boolean useDragHandle = false; public String styleName = null; diff --git a/gridstack-addon/src/main/resources/VAADIN/addons/gridstack/gridstack.scss b/gridstack-addon/src/main/resources/VAADIN/addons/gridstack/gridstack.scss index 81d5688..bd0b789 100644 --- a/gridstack-addon/src/main/resources/VAADIN/addons/gridstack/gridstack.scss +++ b/gridstack-addon/src/main/resources/VAADIN/addons/gridstack/gridstack.scss @@ -171,6 +171,10 @@ $gridstack_item_padding: $gridstack_horizontal_padding / 2 !default; } } + &.ui-draggable-disabled .grid-stack-item-drag-handle.separate-handle { + display: none !important; + } + &:hover .grid-stack-item-drag-handle.separate-handle { display: block; } diff --git a/gridstack-demo/src/main/java/org/vaadin/alump/gridstack/demo/TestView.java b/gridstack-demo/src/main/java/org/vaadin/alump/gridstack/demo/TestView.java index bd90e60..96dc4ed 100644 --- a/gridstack-demo/src/main/java/org/vaadin/alump/gridstack/demo/TestView.java +++ b/gridstack-demo/src/main/java/org/vaadin/alump/gridstack/demo/TestView.java @@ -1,21 +1,35 @@ package org.vaadin.alump.gridstack.demo; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +import org.vaadin.alump.gridstack.GridStackButton; +import org.vaadin.alump.gridstack.GridStackCoordinates; +import org.vaadin.alump.gridstack.GridStackLayout; + import com.vaadin.event.LayoutEvents; import com.vaadin.icons.VaadinIcons; -import com.vaadin.navigator.Navigator; -import com.vaadin.navigator.View; -import com.vaadin.navigator.ViewChangeListener; -import com.vaadin.server.ExternalResource; import com.vaadin.server.Resource; import com.vaadin.server.ThemeResource; -import com.vaadin.ui.*; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Image; +import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Panel; +import com.vaadin.ui.PasswordField; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ValoTheme; -import org.vaadin.alump.gridstack.GridStackButton; -import org.vaadin.alump.gridstack.GridStackCoordinates; -import org.vaadin.alump.gridstack.GridStackLayout; - -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; /** * Main test/demo view of GridStackLayout @@ -33,6 +47,7 @@ public class TestView extends AbstractView { private Random rand = new Random(0xDEADBEEF); private Component locked; + private final Component readOnly; // This value can be used as x and y when client side can pick the best slot private final static int CLIENT_SELECTS = GridStackLayout.CLIENT_SIDE_SELECTS; @@ -78,6 +93,10 @@ public TestView() { locked.setWidth(100, Unit.PERCENTAGE); gridStack.addComponent(locked, 1, 0, 3, 1); + this.readOnly = new Label("This component can be set to read only (moving and resizing is disabled and moving children over will not move this)"); + this.readOnly.setWidth(100, Unit.PERCENTAGE); + this.gridStack.addComponent(this.readOnly, 0, 8, 3, 1); + gridStack.addComponent(createForm(), 0, 5, 2, 3, false); gridStack.addComponent(createConsole(), 0, 3, 4, 2); @@ -90,7 +109,7 @@ public TestView() { final int eventId = eventCounter.getAndIncrement(); events.stream().forEach(event -> { if(event.getMovedChild() instanceof TestItem) { - TestItem item = (TestItem)event.getMovedChild(); + TestItem item = (TestItem) event.getMovedChild(); item.setHeader(event.getNew()); } addEvent("event #" + eventId + ": Moved from " + event.getOld().toString() + " to " @@ -115,7 +134,7 @@ private Component createToolbar() { })); toolbar.addComponent(createButton(VaadinIcons.TRASH, "Remove component", e -> { - if(gridStack.getComponentCount() < 1) { + if (gridStack.getComponentCount() < 1) { Notification.show("Nothing to remove!"); return; } @@ -157,6 +176,13 @@ private Component createToolbar() { }); toolbar.addComponent(lockItem); + final CheckBox readOnlyItem = new CheckBox("Read only child"); + readOnlyItem.setDescription("Define if item with text \"Read only\" is read only or not"); + readOnlyItem.addValueChangeListener(e -> { + this.gridStack.setComponentReadOnly(this.readOnly, e.getValue()); + }); + toolbar.addComponent(readOnlyItem); + return toolbar; } @@ -203,7 +229,7 @@ private Component createForm() { password.addStyleName(ValoTheme.TEXTFIELD_SMALL); password.setCaption("Password:"); layout.addComponent(password); - Button login = new GridStackButton("Login", e-> Notification.show("Logged in?")); + Button login = new GridStackButton("Login", e -> Notification.show("Logged in?")); login.addStyleName(ValoTheme.BUTTON_FRIENDLY); login.addStyleName(ValoTheme.BUTTON_SMALL); layout.addComponent(login); @@ -249,7 +275,8 @@ private Component createImage() { if(clickedAtChild) { sb.append(": "); - sb.append(gridStack.getCoordinates(e.getChildComponent()).toString()); + sb.append(gridStack.getCoordinates(e.getChildComponent()) + .toString()); } addEvent(sb.toString()); @@ -262,7 +289,8 @@ private void reorderAll() { private void moveRandomChildToAnotherFreePosition() { List children = new ArrayList<>(); - gridStack.iterator().forEachRemaining(child -> children.add(child)); + gridStack.iterator() + .forEachRemaining(child -> children.add(child)); if(!children.isEmpty()) { addEvent("Move random child to new position..."); Collections.shuffle(children, rand);