/* * Copyright 2017 Red Hat, Inc. and/or its affiliates. * * Licensed 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.kie.workbench.common.stunner.client.lienzo.shape.view.ext; import com.ait.lienzo.client.core.shape.MultiPath; import com.ait.lienzo.client.core.shape.Node; import com.ait.lienzo.client.core.shape.Shape; import com.ait.lienzo.client.core.shape.wires.IControlHandle; import com.ait.lienzo.client.core.shape.wires.IControlHandleList; import com.ait.lienzo.client.core.shape.wires.LayoutContainer; import com.ait.lienzo.client.core.shape.wires.WiresLayoutContainer; import com.ait.lienzo.client.core.shape.wires.event.AbstractWiresDragEvent; import com.ait.lienzo.client.core.shape.wires.event.AbstractWiresResizeEvent; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeEndEvent; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeEndHandler; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeStartEvent; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeStartHandler; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeStepEvent; import com.ait.lienzo.client.core.shape.wires.event.WiresResizeStepHandler; import com.ait.lienzo.client.core.types.BoundingBox; import com.ait.lienzo.client.core.types.LinearGradient; import com.google.gwt.event.shared.HandlerRegistration; import org.kie.workbench.common.stunner.client.lienzo.shape.view.ViewEventHandlerManager; import org.kie.workbench.common.stunner.client.lienzo.shape.view.WiresShapeView; import org.kie.workbench.common.stunner.client.lienzo.util.LienzoShapeUtils; import org.kie.workbench.common.stunner.client.lienzo.util.ShapeControlPointsHelper; import org.kie.workbench.common.stunner.core.client.shape.view.HasControlPoints; import org.kie.workbench.common.stunner.core.client.shape.view.HasEventHandlers; import org.kie.workbench.common.stunner.core.client.shape.view.HasFillGradient; import org.kie.workbench.common.stunner.core.client.shape.view.HasTitle; import org.kie.workbench.common.stunner.core.client.shape.view.event.DragContext; import org.kie.workbench.common.stunner.core.client.shape.view.event.DragEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.DragHandler; import org.kie.workbench.common.stunner.core.client.shape.view.event.ResizeEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.ResizeHandler; import org.kie.workbench.common.stunner.core.client.shape.view.event.TextClickEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.TextDoubleClickEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.TextEnterEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.TextExitEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewEvent; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewEventType; import org.kie.workbench.common.stunner.core.client.shape.view.event.ViewHandler; public class WiresShapeViewExt<T extends WiresShapeViewExt> extends WiresShapeView<T> implements HasTitle<T>, HasControlPoints<T>, HasEventHandlers<T, Shape<?>>, HasFillGradient<T> { private ViewEventHandlerManager eventHandlerManager; private WiresTextDecorator textViewDecorator; private Type fillGradientType = null; private String fillGradientStartColor = null; private String fillGradientEndColor = null; public WiresShapeViewExt(final ViewEventType[] supportedEventTypes, final MultiPath path) { this(supportedEventTypes, path, new WiresLayoutContainer()); } public WiresShapeViewExt(final ViewEventType[] supportedEventTypes, final MultiPath path, final LayoutContainer layoutContainer) { super(path, null != layoutContainer ? layoutContainer : new WiresLayoutContainer()); initialize(supportedEventTypes); } @Override public boolean supports(final ViewEventType type) { return eventHandlerManager.supports(type); } @Override public Shape<?> getAttachableShape() { return getShape(); } @Override @SuppressWarnings("unchecked") public T setTitle(final String title) { textViewDecorator.setTitle(title); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitlePosition(final Position position) { if (textViewDecorator.setTitlePosition(position)) { removeChild(textViewDecorator.getView()); addTextAsChild(); } return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleRotation(final double degrees) { textViewDecorator.setTitleRotation(degrees); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleStrokeColor(final String color) { textViewDecorator.setTitleStrokeColor(color); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleFontFamily(final String fontFamily) { textViewDecorator.setTitleFontFamily(fontFamily); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleFontSize(final double fontSize) { textViewDecorator.setTitleFontSize(fontSize); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleAlpha(final double alpha) { textViewDecorator.setTitleAlpha(alpha); return (T) this; } @Override @SuppressWarnings("unchecked") public T setTitleStrokeWidth(final double strokeWidth) { textViewDecorator.setTitleStrokeWidth(strokeWidth); return (T) this; } @Override @SuppressWarnings("unchecked") public T moveTitleToTop() { textViewDecorator.moveTitleToTop(); return (T) this; } @Override @SuppressWarnings("unchecked") public T setFillGradient(final Type type, final String startColor, final String endColor) { this.fillGradientType = type; this.fillGradientStartColor = startColor; this.fillGradientEndColor = endColor; if (null != getShape()) { final BoundingBox bb = getShape().getBoundingBox(); final double width = bb.getWidth(); final double height = bb.getHeight(); updateFillGradient(width, height); } return (T) this; } @SuppressWarnings("unchecked") public T updateFillGradient(final double width, final double height) { if (this.fillGradientType != null && this.fillGradientStartColor != null && this.fillGradientEndColor != null) { final LinearGradient gradient = LienzoShapeUtils.getLinearGradient(fillGradientStartColor, fillGradientEndColor, width, height); getShape().setFillGradient(gradient); } return (T) this; } @Override @SuppressWarnings("unchecked") public T showControlPoints(final ControlPointType type) { IControlHandleList ctrls = loadControls(translate(type)); if (null != ctrls && ControlPointType.RESIZE.equals(type)) { // Apply this workaround for now when using the resize control points. ShapeControlPointsHelper.showOnlyLowerRightCP(ctrls); } else if (null != ctrls) { ctrls.show(); } return (T) this; } private IControlHandle.ControlHandleType translate(final ControlPointType type) { if (type.equals(ControlPointType.RESIZE)) { return IControlHandle.ControlHandleStandardType.RESIZE; } return IControlHandle.ControlHandleStandardType.MAGNET; } @Override @SuppressWarnings("unchecked") public T hideControlPoints() { IControlHandleList ctrls = getControls(); if (null != ctrls) { ctrls.hide(); } return (T) this; } @Override public boolean areControlsVisible() { return null != getControls() && getControls().isVisible(); } @Override public void destroy() { super.destroy(); if (null != textViewDecorator) { textViewDecorator.destroy(); } if (null != eventHandlerManager) { eventHandlerManager.destroy(); eventHandlerManager = null; } this.fillGradientEndColor = null; this.fillGradientStartColor = null; this.fillGradientType = null; } @Override @SuppressWarnings("unchecked") public T addHandler(final ViewEventType type, final ViewHandler<? extends ViewEvent> eventHandler) { if (supports(type)) { boolean delegate = true; if (ViewEventType.DRAG.equals(type)) { final HandlerRegistration[] registrations = registerDragHandler((DragHandler) eventHandler); if (null != registrations) { eventHandlerManager.addHandlersRegistration(type, registrations); } delegate = false; } else if (ViewEventType.RESIZE.equals(type)) { final HandlerRegistration[] registrations = registerResizeHandler((ResizeHandler) eventHandler); if (null != registrations) { eventHandlerManager.addHandlersRegistration(type, registrations); } delegate = false; } if (ViewEventType.TEXT_ENTER.equals(type)) { delegate = false; textViewDecorator.setTextEnterHandler((ViewHandler<TextEnterEvent>) eventHandler); } if (ViewEventType.TEXT_EXIT.equals(type)) { textViewDecorator.setTextExitHandler((ViewHandler<TextExitEvent>) eventHandler); delegate = false; } if (ViewEventType.TEXT_CLICK.equals(type)) { textViewDecorator.setTextClickHandler((ViewHandler<TextClickEvent>) eventHandler); delegate = false; } if (ViewEventType.TEXT_DBL_CLICK.equals(type)) { textViewDecorator.setTextDblClickHandler((ViewHandler<TextDoubleClickEvent>) eventHandler); delegate = false; } if (delegate) { eventHandlerManager.addHandler(type, eventHandler); } } return (T) this; } @Override @SuppressWarnings("unchecked") public T removeHandler(final ViewHandler<? extends ViewEvent> eventHandler) { eventHandlerManager.removeHandler(eventHandler); return (T) this; } @Override @SuppressWarnings("unchecked") public T enableHandlers() { eventHandlerManager.enable(); return (T) this; } @Override @SuppressWarnings("unchecked") public T disableHandlers() { eventHandlerManager.disable(); return (T) this; } protected void initialize(final ViewEventType[] supportedEventTypes) { initializeHandlerManager(getGroup(), null != getPath() ? getPath() : getGroup(), supportedEventTypes); initializeTextView(); } protected void initializeHandlerManager(final Node<?> node, final Node<?> path, final ViewEventType[] supportedEventTypes) { this.eventHandlerManager = createEventHandlerManager(node, path, supportedEventTypes); } protected void initializeTextView() { this.textViewDecorator = new WiresTextDecorator(eventHandlerManager); addTextAsChild(); } private ViewEventHandlerManager createEventHandlerManager(final Node<?> node, final Node<?> path, final ViewEventType[] supportedEventTypes) { if (null != getGroup()) { return new ViewEventHandlerManager(node, path, supportedEventTypes); } return null; } private void addTextAsChild() { this.addChild(textViewDecorator.getView(), textViewDecorator.getLayout()); } // TODO: listen for WiresMoveEvent's as well? private HandlerRegistration[] registerDragHandler(final ViewHandler<DragEvent> eventHandler) { if (!getAttachableShape().isDraggable()) { final DragHandler dragHandler = (DragHandler) eventHandler; setDraggable(true); HandlerRegistration dragStartReg = addWiresDragStartHandler(wiresDragStartEvent -> { final DragEvent e = buildDragEvent(wiresDragStartEvent); dragHandler.start(e); }); HandlerRegistration dragMoveReg = addWiresDragMoveHandler(wiresDragMoveEvent -> { final DragEvent e = buildDragEvent(wiresDragMoveEvent); dragHandler.handle(e); }); HandlerRegistration dragEndReg = addWiresDragEndHandler(wiresDragEndEvent -> { final DragEvent e = buildDragEvent(wiresDragEndEvent); dragHandler.end(e); }); return new HandlerRegistration[]{dragStartReg, dragMoveReg, dragEndReg}; } return null; } private HandlerRegistration[] registerResizeHandler(final ViewHandler<ResizeEvent> eventHandler) { final ResizeHandler resizeHandler = (ResizeHandler) eventHandler; setResizable(true); HandlerRegistration r0 = addWiresResizeStartHandler(new WiresResizeStartHandler() { @Override public void onShapeResizeStart(final WiresResizeStartEvent wiresResizeStartEvent) { final ResizeEvent event = buildResizeEvent(wiresResizeStartEvent); resizeHandler.start(event); } }); HandlerRegistration r1 = addWiresResizeStepHandler(new WiresResizeStepHandler() { @Override public void onShapeResizeStep(final WiresResizeStepEvent wiresResizeStepEvent) { final ResizeEvent event = buildResizeEvent(wiresResizeStepEvent); resizeHandler.handle(event); } }); HandlerRegistration r2 = addWiresResizeEndHandler(new WiresResizeEndHandler() { @Override public void onShapeResizeEnd(final WiresResizeEndEvent wiresResizeEndEvent) { final ResizeEvent event = buildResizeEvent(wiresResizeEndEvent); resizeHandler.end(event); } }); return new HandlerRegistration[]{r0, r1, r2}; } private DragEvent buildDragEvent(final AbstractWiresDragEvent sourceDragEvent) { final double x = sourceDragEvent.getX(); final double y = sourceDragEvent.getY(); final double cx = sourceDragEvent.getNodeDragEvent().getX(); final double cy = sourceDragEvent.getNodeDragEvent().getY(); final int dx = sourceDragEvent.getNodeDragEvent().getDragContext().getDx(); final int dy = sourceDragEvent.getNodeDragEvent().getDragContext().getDy(); final DragContext dragContext = new DragContext(dx, dy, () -> sourceDragEvent.getNodeDragEvent().getDragContext().reset()); return new DragEvent(x, y, cx, cy, dragContext); } private ResizeEvent buildResizeEvent(final AbstractWiresResizeEvent sourceResizeEvent) { final double x = sourceResizeEvent.getX(); final double y = sourceResizeEvent.getY(); final double cx = sourceResizeEvent.getNodeDragEvent().getX(); final double cy = sourceResizeEvent.getNodeDragEvent().getY(); final double w = sourceResizeEvent.getWidth(); final double h = sourceResizeEvent.getHeight(); return new ResizeEvent(x, y, cx, cy, w, h); } }