package jetbrains.mps.lang.editor.diagram.runtime.jetpad.views; /*Generated by MPS */ import jetbrains.jetpad.model.property.Property; import jetbrains.jetpad.model.property.ValueProperty; import jetbrains.jetpad.values.Color; import jetbrains.jetpad.geometry.Rectangle; import jetbrains.jetpad.model.collections.list.ObservableList; import jetbrains.jetpad.geometry.Vector; import jetbrains.jetpad.model.collections.list.ObservableArrayList; import jetbrains.jetpad.mapper.Mapper; import jetbrains.jetpad.mapper.Synchronizers; import jetbrains.jetpad.mapper.MapperFactory; import java.util.ListIterator; import jetbrains.jetpad.geometry.Segment; public class ResizableSelectionFrameView extends AbstractExternalFrameView { public Property<Boolean> resizable = new ValueProperty<Boolean>(false); public Property<Boolean> showSideHandler = new ValueProperty<Boolean>(true); public Property<Integer> selectionLineWidth = new ValueProperty<Integer>(1); public Property<Color> color = new ValueProperty<Color>(Color.BLACK); public Property<Color> backgroundColor = new ValueProperty<Color>(Color.LIGHT_GRAY); public Property<Rectangle> boundsDelta = new ValueProperty<Rectangle>(); private ObservableList<Vector> myCornerResizeHandlePositions = new ObservableArrayList<Vector>(); private ObservableList<Vector> mySideResizeHandlePositions = new ObservableArrayList<Vector>(); public ResizableSelectionFrameView() { new Mapper<ResizableSelectionFrameView, ResizableSelectionFrameView>(this, this) { @Override protected void registerSynchronizers(Mapper.SynchronizersConfiguration configuration) { super.registerSynchronizers(configuration); // corner resize handle positions configuration.add(Synchronizers.forProperty(resizable, new Runnable() { public void run() { updateCornerResizeHandlePositions(resizable.get(), frameRectangle.get()); } })); configuration.add(Synchronizers.forProperty(frameRectangle, new Runnable() { public void run() { updateCornerResizeHandlePositions(resizable.get(), frameRectangle.get()); } })); // side resize handle positions configuration.add(Synchronizers.forProperty(resizable, new Runnable() { public void run() { updateSideResizeHandlePositions(resizable.get(), showSideHandler.get(), frameRectangle.get()); } })); configuration.add(Synchronizers.forProperty(showSideHandler, new Runnable() { public void run() { updateSideResizeHandlePositions(resizable.get(), showSideHandler.get(), frameRectangle.get()); } })); configuration.add(Synchronizers.forProperty(frameRectangle, new Runnable() { public void run() { updateSideResizeHandlePositions(resizable.get(), showSideHandler.get(), frameRectangle.get()); } })); configuration.add(Synchronizers.forConstantRole(this, ResizableSelectionFrameView.this, childSubList(), new MapperFactory<ResizableSelectionFrameView, SelectionFrameView>() { public Mapper<? extends ResizableSelectionFrameView, ? extends SelectionFrameView> createMapper(ResizableSelectionFrameView decoratorView) { return new Mapper<ResizableSelectionFrameView, SelectionFrameView>(decoratorView, new SelectionFrameView()) { @Override protected void registerSynchronizers(Mapper.SynchronizersConfiguration configuration) { super.registerSynchronizers(configuration); configuration.add(Synchronizers.forProperty(color, getTarget().color)); configuration.add(Synchronizers.forProperty(frameWidth, getTarget().frameWidth)); configuration.add(Synchronizers.forProperty(internalsBounds, getTarget().internalsBounds)); configuration.add(Synchronizers.forProperty(selectionLineWidth, getTarget().lineWidth)); } }; } })); // corner resize handles configuration.add(Synchronizers.forObservableRole(this, myCornerResizeHandlePositions, childSubList(), new MapperFactory<Vector, ResizeHandleView>() { public Mapper<? extends Vector, ? extends ResizeHandleView> createMapper(Vector position) { int index = myCornerResizeHandlePositions.indexOf(position); switch (index) { case 0: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(true, true), new ResizableSelectionFrameView.RectangleUpdater(true, false)); case 1: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(false, true), new ResizableSelectionFrameView.RectangleUpdater(true, false)); case 2: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(false, true), new ResizableSelectionFrameView.RectangleUpdater(false, false)); case 3: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(true, true), new ResizableSelectionFrameView.RectangleUpdater(false, false)); default: assert false : "unexpected index: " + index; } return null; } })); // side resize handles configuration.add(Synchronizers.forObservableRole(this, mySideResizeHandlePositions, childSubList(), new MapperFactory<Vector, ResizeHandleView>() { public Mapper<? extends Vector, ? extends ResizeHandleView> createMapper(Vector position) { int index = mySideResizeHandlePositions.indexOf(position); switch (index) { case 0: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(true, false)); case 1: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(false, true)); case 2: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(false, false)); case 3: return new ResizableSelectionFrameView.ResizeHandleMapper(position, new ResizableSelectionFrameView.RectangleUpdater(true, true)); default: assert false : "unexpected index: " + index; } return null; } })); } }.attachRoot(); } private void updateCornerResizeHandlePositions(boolean resizable, Rectangle rectangle) { if (!(resizable)) { myCornerResizeHandlePositions.clear(); return; } ListIterator<Vector> iterator = myCornerResizeHandlePositions.listIterator(); for (Vector nextPoint : rectangle.getBoundPoints()) { if (iterator.hasNext()) { Vector next = iterator.next(); if (nextPoint.equals(next)) { continue; } iterator.remove(); } iterator.add(nextPoint); } } private void updateSideResizeHandlePositions(boolean resizable, boolean visible, Rectangle rectangle) { if (!(resizable) || !(visible)) { mySideResizeHandlePositions.clear(); return; } for (Segment nextSegment : rectangle.getBoundSegments()) { if (nextSegment.length() < 4 * ResizeHandleView.DEFAULT_HALF_WIDTH) { mySideResizeHandlePositions.clear(); return; } } ListIterator<Vector> iterator = mySideResizeHandlePositions.listIterator(); for (Segment nextSegment : rectangle.getBoundSegments()) { Vector nextPoint = nextSegment.end.sub(nextSegment.start).div(2).add(nextSegment.start); if (iterator.hasNext()) { Vector next = iterator.next(); if (nextPoint.equals(next)) { continue; } iterator.remove(); } iterator.add(nextPoint); } } private class ResizeHandleMapper extends Mapper<Vector, ResizeHandleView> { private Property<DragHandler> myDragHandler = new ValueProperty<DragHandler>(); private ResizeHandleMapper(Vector position, ResizableSelectionFrameView.RectangleUpdater... updater) { super(position, new ResizeHandleView(position)); myDragHandler.set(new ResizableSelectionFrameView.ResizeHandleMapper.DragHandlerImpl(updater)); } @Override protected void registerSynchronizers(Mapper.SynchronizersConfiguration configuration) { super.registerSynchronizers(configuration); configuration.add(Synchronizers.forProperty(color, getTarget().color)); configuration.add(Synchronizers.forProperty(backgroundColor, getTarget().backgroundColor)); configuration.add(Synchronizers.forProperty(myDragHandler, getTarget().dragHandler)); } private class DragHandlerImpl implements DragHandler { private Rectangle myOriginalBounds; private Vector myDragStartposition; private ResizableSelectionFrameView.RectangleUpdater[] myUpdaters; private DragHandlerImpl(ResizableSelectionFrameView.RectangleUpdater... updaters) { myUpdaters = updaters; } public void dragStarted(Vector position) { myOriginalBounds = internalsBounds.get(); myDragStartposition = position; updatePosition(position); } public void updatePosition(Vector position) { Rectangle bounds = myOriginalBounds; Vector dragDelta = position.sub(myDragStartposition); for (ResizableSelectionFrameView.RectangleUpdater updater : myUpdaters) { bounds = updater.updateRect(bounds, dragDelta); } internalsBounds.set(bounds); } public void dragStopped(Vector position) { Rectangle finalBounds = internalsBounds.get(); Vector originDelta = finalBounds.origin.sub(myOriginalBounds.origin); Vector dimensionDelta = finalBounds.dimension.sub(myOriginalBounds.dimension); boundsDelta.set(new Rectangle(originDelta, dimensionDelta)); myOriginalBounds = null; myDragStartposition = null; } } } private class RectangleUpdater { private boolean myOrigin; private boolean myX; private RectangleUpdater(boolean origin, boolean x) { myOrigin = origin; myX = x; } public Rectangle updateRect(Rectangle rectangle, Vector moveDelta) { Vector origin = rectangle.origin; Vector dimension = rectangle.dimension; Vector effectiveDelta = updateVector(Vector.ZERO, moveDelta); if (myOrigin) { origin = origin.add(effectiveDelta); dimension = dimension.add(effectiveDelta.negate()); } else { dimension = dimension.add(effectiveDelta); } return new Rectangle(origin, dimension); } private Vector updateVector(Vector value, Vector newValue) { return (myX ? new Vector(newValue.x, value.y) : new Vector(value.x, newValue.y)); } } }