/* * Copyright (C) 2013 The Android Open Source Project * * 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 com.intellij.android.designer.model.layout.relative; import com.intellij.android.designer.designSurface.feedbacks.TextFeedback; import com.intellij.android.designer.model.RadViewComponent; import com.intellij.designer.designSurface.FeedbackLayer; import org.jetbrains.annotations.NotNull; import java.awt.*; /** * The {@link com.intellij.designer.designSurface.feedbacks.AbstractTextFeedback} class does not support multiline text, so we * have to manage multiple {@link com.intellij.designer.designSurface.feedbacks.AbstractTextFeedback} objects, one for each line. */ public class MultiLineTooltipManager { /** Horizontal delta from the mouse cursor to shift the tooltip by */ private static final int OFFSET_X = 20; /** Vertical delta from the mouse cursor to shift the tooltip by */ private static final int OFFSET_Y = 20; private final TextFeedback[] myTextFeedbacks; @NotNull private final Dimension[] mySizes; @NotNull private final boolean[] myVisible; @NotNull private final FeedbackLayer myLayer; public MultiLineTooltipManager(@NotNull FeedbackLayer layer, int maxLines) { myLayer = layer; myTextFeedbacks = new TextFeedback[maxLines]; mySizes = new Dimension[maxLines]; myVisible = new boolean[maxLines]; } @NotNull public TextFeedback getFeedback(int line) { assert line < myTextFeedbacks.length; TextFeedback feedback = myTextFeedbacks[line]; if (feedback == null) { feedback = new TextFeedback(); myTextFeedbacks[line] = feedback; myLayer.add(feedback); } return feedback; } public void setVisible(int line, boolean visible) { myVisible[line] = visible; } public void dispose() { for (TextFeedback feedback : myTextFeedbacks) { if (feedback != null) { myLayer.remove(feedback); } } } private Dimension getPreferredSize() { int width = 0; int height = 0; for (int i = 0; i < myTextFeedbacks.length; i++) { TextFeedback feedback = myTextFeedbacks[i]; if (feedback != null && myVisible[i]) { Dimension preferredSize = feedback.getPreferredSize(); width = Math.max(width, preferredSize.width); height += preferredSize.height; mySizes[i] = preferredSize; } } // Some padding width += 2; height += 2; return new Dimension(width, height); } /** * Update the tooltip such that it is placed within the given container layout. * * @param container the layout * @param mouseLocation the mouse location */ public void update(RadViewComponent container, Point mouseLocation) { // Produce the bounds of the container Rectangle bounds = container.getBounds(myLayer); Dimension preferredSize = getPreferredSize(); // TODO: Push it out of the way based on the mouse location, with a timer int x = bounds.x + bounds.width / 2 - preferredSize.width / 2; int y = bounds.y - 1 - preferredSize.height; if (y < 0) { y = bounds.y + bounds.height - preferredSize.height - 1; } for (int i = 0; i < myTextFeedbacks.length; i++) { TextFeedback feedback = myTextFeedbacks[i]; if (feedback != null) { if (myVisible[i]) { int height = mySizes[i].height; feedback.setBounds(x, y, preferredSize.width, height); y += height; feedback.setVisible(true); } else { feedback.setVisible(false); } } } } /** * Update the tooltip and position it relative to the given x,y mouse position. The * {@code below} and {@code toRightOf} flags can be used to push the tooltip out * of the way; if you for example happen to be resizing the bottom right corner * of a view, then you'd probably want the tooltip above and to the left, such that * it does not obscure potential constraints to the right and below matched by the corner. * * @param below if true, place the label below the y position * @param toRightOf if true, place the label to the right of the x position * @param x the x position * @param y the y position */ public void update(boolean below, boolean toRightOf, int x, int y) { Dimension preferredSize = getPreferredSize(); if (below) { y += OFFSET_Y; } else { y -= OFFSET_Y; y -= preferredSize.height; } if (toRightOf) { x += OFFSET_X; } else { x -= OFFSET_X; x -= preferredSize.width; } for (int i = 0; i < myTextFeedbacks.length; i++) { TextFeedback feedback = myTextFeedbacks[i]; if (feedback != null) { if (myVisible[i]) { int height = mySizes[i].height; feedback.setBounds(x, y, preferredSize.width, height); y += height; feedback.setVisible(true); } else { feedback.setVisible(false); } } } } }