/******************************************************************************* * Copyright (c) 2016 itemis AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.mvc.fx.parts; import java.util.List; import java.util.Set; import org.eclipse.gef.common.adapt.AdapterKey; import org.eclipse.gef.fx.nodes.GeometryNode; import org.eclipse.gef.fx.utils.NodeUtils; import org.eclipse.gef.geometry.planar.ICurve; import org.eclipse.gef.geometry.planar.IGeometry; import org.eclipse.gef.geometry.planar.Rectangle; import org.eclipse.gef.mvc.fx.models.SelectionModel; import org.eclipse.gef.mvc.fx.viewer.IViewer; import com.google.common.reflect.TypeToken; import com.google.inject.Provider; import javafx.collections.ListChangeListener; import javafx.scene.Node; import javafx.scene.paint.Color; import javafx.scene.shape.StrokeType; /** * The {@link FocusFeedbackPart} visualizes focus feedback. */ public class FocusFeedbackPart extends AbstractFeedbackPart<GeometryNode<IGeometry>> { private static final double DEFAULT_STROKE_WIDTH = 1.5d; private Provider<? extends IGeometry> feedbackGeometryProvider; private ListChangeListener<IContentPart<? extends Node>> selectionModelObserver = new ListChangeListener<IContentPart<? extends Node>>() { @Override public void onChanged( ListChangeListener.Change<? extends IContentPart<? extends Node>> c) { refreshVisual(); } }; /** * Default constructor. */ public FocusFeedbackPart() { } @Override protected void doActivate() { super.doActivate(); SelectionModel selectionModel = getViewer() .getAdapter(SelectionModel.class); selectionModel.selectionUnmodifiableProperty() .addListener(selectionModelObserver); } @Override protected GeometryNode<IGeometry> doCreateVisual() { GeometryNode<IGeometry> visual = new GeometryNode<>(); visual.setFill(Color.TRANSPARENT); visual.setMouseTransparent(true); visual.setManaged(false); visual.setStrokeType(StrokeType.OUTSIDE); visual.setStrokeWidth(DEFAULT_STROKE_WIDTH); visual.setStroke(getFocusStroke()); return visual; } @Override protected void doDeactivate() { SelectionModel selectionModel = getViewer() .getAdapter(SelectionModel.class); selectionModel.selectionUnmodifiableProperty() .removeListener(selectionModelObserver); super.doDeactivate(); } @Override public void doRefreshVisual(GeometryNode<IGeometry> visual) { Set<IVisualPart<? extends Node>> anchorages = getAnchoragesUnmodifiable() .keySet(); if (anchorages.isEmpty()) { return; } IVisualPart<? extends Node> anchorage = anchorages.iterator().next(); IRootPart<? extends Node> root = anchorage.getRoot(); if (root == null) { return; } IGeometry feedbackGeometry = getFeedbackGeometry(); if (feedbackGeometry == null) { return; } // determine selection IViewer viewer = root.getViewer(); List<IContentPart<? extends Node>> selected = viewer .getAdapter(SelectionModel.class).getSelectionUnmodifiable(); // FIXME: Investigate why the StrokeType needs to be set before setting // the geometry in order to prevent a vertical offset. // adjust feedback depending on geometry if (feedbackGeometry instanceof ICurve) { // stroke centered visual.setStrokeType(StrokeType.CENTERED); // increase geometry size if selected if (selected.contains(anchorage)) { visual.setStrokeWidth( SelectionFeedbackPart.DEFAULT_STROKE_WIDTH * 2); } else { visual.setStrokeWidth(DEFAULT_STROKE_WIDTH); } } else { // stroke outside visual.setStrokeType(StrokeType.OUTSIDE); } // update geometry visual.setGeometry(feedbackGeometry); // adjust feedback depending on geometry if (!(feedbackGeometry instanceof ICurve)) { // increase geometry size if selected if (selected.contains(anchorage)) { Rectangle feedbackBounds = feedbackGeometry.getBounds(); visual.resizeGeometry(feedbackBounds.getWidth() + SelectionFeedbackPart.DEFAULT_STROKE_WIDTH * 2, feedbackBounds.getHeight() + SelectionFeedbackPart.DEFAULT_STROKE_WIDTH * 2); visual.relocateGeometry( feedbackBounds.getX() - SelectionFeedbackPart.DEFAULT_STROKE_WIDTH, feedbackBounds.getY() - SelectionFeedbackPart.DEFAULT_STROKE_WIDTH); } } } /** * Returns the {@link IGeometry} that is provided by this part's * {@link #setGeometryProvider(Provider) geometry provider}. * * @return The {@link IGeometry} that is provided by this part's geometry * provider. */ protected IGeometry getFeedbackGeometry() { return NodeUtils.sceneToLocal(getVisual().getParent(), feedbackGeometryProvider.get()); } /** * Returns the {@link Color} that is used to stroke focus feedback. * * @return The {@link Color} that is used to stroke focus feedback. */ @SuppressWarnings("serial") protected Color getFocusStroke() { Provider<Color> focusFeedbackColorProvider = getViewer() .getAdapter(AdapterKey.get(new TypeToken<Provider<Color>>() { }, DefaultFocusFeedbackPartFactory.FOCUS_FEEDBACK_COLOR_PROVIDER)); return focusFeedbackColorProvider == null ? DefaultFocusFeedbackPartFactory.DEFAULT_FOCUS_FEEDBACK_COLOR : focusFeedbackColorProvider.get(); } /** * Sets the <code>Provider<IGeometry></code> of this part to the given * value. * * @param geometryProvider * The new <code>Provider<IGeometry></code> for this part. */ public void setGeometryProvider( Provider<? extends IGeometry> geometryProvider) { feedbackGeometryProvider = geometryProvider; } }