blob: 045e44e4f3a591ea50d75fa8f0bd824b8f75743b [file] [log] [blame]
/*
* 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);
}
}
}
}
}