| Index: chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
|
| index 5601a04d6906817ae05156a2cf78e72767c8a4bf..08b12c8444504b7df408e005115ea307cc277b6a 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
|
| @@ -11,109 +11,59 @@ import android.graphics.PixelFormat;
|
| import android.graphics.Rect;
|
| import android.graphics.drawable.BitmapDrawable;
|
| import android.graphics.drawable.Drawable;
|
| -import android.os.Bundle;
|
| import android.view.Gravity;
|
| import android.view.View;
|
| import android.view.View.MeasureSpec;
|
| -import android.view.View.OnAttachStateChangeListener;
|
| import android.view.View.OnLayoutChangeListener;
|
| import android.view.ViewGroup;
|
| import android.widget.PopupWindow;
|
| -import android.widget.TextView;
|
|
|
| import org.chromium.base.ApiCompatibilityUtils;
|
| import org.chromium.chrome.R;
|
| -import org.chromium.ui.base.LocalizationUtils;
|
|
|
| /**
|
| * UI component that handles showing text bubbles.
|
| */
|
| -public class TextBubble
|
| - extends PopupWindow implements OnLayoutChangeListener, OnAttachStateChangeListener {
|
| - /** Whether to use the intrinsic padding of the bubble background as padding (boolean). */
|
| - public static final String BACKGROUND_INTRINSIC_PADDING = "Background_Intrinsic_Padding";
|
| +public abstract class TextBubble extends PopupWindow implements OnLayoutChangeListener {
|
| + /** How much of the anchor should be overlapped. */
|
| + private final float mYOverlapPercentage;
|
|
|
| - /**
|
| - * Boolean to be used for deciding whether the bubble should be anchored above or below
|
| - * the view
|
| - */
|
| - public static final String UP_DOWN = "Up_Down";
|
| -
|
| - /** Style resource Id to be used for text inside the bubble. Should be of type int. */
|
| - public static final String TEXT_STYLE_ID = "Text_Style_Id";
|
| -
|
| - /** Boolean to be used for deciding whether the bubble should be centered to the view */
|
| - public static final String CENTER = "Center";
|
| -
|
| - public static final String ANIM_STYLE_ID = "Animation_Style";
|
| + private final Rect mCachedPaddingRect = new Rect();
|
|
|
| - private final int mTooltipEdgeMargin;
|
| - private final int mTooltipTopMargin;
|
| - private final int mBubbleTipXMargin;
|
| - private boolean mAnchorBelow = false;
|
| - private boolean mCenterView = true;
|
| private int mXPosition;
|
| private int mYPosition;
|
| private View mAnchorView;
|
| - private final Rect mCachedPaddingRect = new Rect();
|
| -
|
| - // The text view inside the popup containing the tooltip text.
|
| - private final TextView mTooltipText;
|
| + private View mContentView;
|
|
|
| /**
|
| - * Constructor that uses a bundle object to fetch resources and optional boolean
|
| - * values for the {@link TextBubble}.
|
| - *
|
| - * Use CENTER for centering the tip to the anchor view.
|
| - * UP_DOWN for drawing the bubble with tip pointing up or down.
|
| - * Up is true and Down is false.
|
| - * LAYOUT_WIDTH_ID Dimension resource Id for the width of the {@link TextView} inside the
|
| - * bubble. The height is set to half of this value.
|
| - *
|
| - * @param context
|
| - * @param res Bundle object that contains resource ids and optional flags.
|
| + * Constructs a TextBubble that will point at a particular view.
|
| + * @param context Context to draw resources from.
|
| + * @param yOverlapPercentage How much the arrow should overlap the view.
|
| */
|
| - public TextBubble(Context context, Bundle res) {
|
| - mAnchorBelow = (res.containsKey(UP_DOWN) ? res.getBoolean(UP_DOWN) : true);
|
| - mCenterView = (res.containsKey(CENTER) ? res.getBoolean(CENTER) : true);
|
| - mTooltipEdgeMargin =
|
| - context.getResources().getDimensionPixelSize(R.dimen.tooltip_min_edge_margin);
|
| - mTooltipTopMargin =
|
| - context.getResources().getDimensionPixelSize(R.dimen.tooltip_top_margin);
|
| - mBubbleTipXMargin = context.getResources().getDimensionPixelSize(R.dimen.bubble_tip_margin);
|
| -
|
| - setBackgroundDrawable(new BubbleBackgroundDrawable(context, res));
|
| - setAnimationStyle(res.containsKey(ANIM_STYLE_ID) ? res.getInt(ANIM_STYLE_ID)
|
| - : android.R.style.Animation);
|
| -
|
| - mTooltipText = new TextView(context);
|
| - ApiCompatibilityUtils.setTextAppearance(mTooltipText,
|
| - (res.containsKey(TEXT_STYLE_ID) ? res.getInt(TEXT_STYLE_ID) : R.style.info_bubble));
|
| -
|
| - setContentView(mTooltipText);
|
| + public TextBubble(Context context, float yOverlapPercentage) {
|
| + super(context);
|
| + mYOverlapPercentage = yOverlapPercentage;
|
| +
|
| + setBackgroundDrawable(new BubbleBackgroundDrawable(context));
|
| + getBackground().getPadding(mCachedPaddingRect);
|
| +
|
| + mContentView = createContent(context);
|
| + setContentView(mContentView);
|
| setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
| setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
| }
|
|
|
| /**
|
| - * @return The textview for the bubble text.
|
| + * Creates the View that contains everything that should be displayed inside the bubble.
|
| */
|
| - public TextView getBubbleTextView() {
|
| - return mTooltipText;
|
| - }
|
| + protected abstract View createContent(Context context);
|
|
|
| /**
|
| * Shows a text bubble anchored to the given view.
|
| *
|
| - * @param text The text to be shown.
|
| * @param anchorView The view that the bubble should be anchored to.
|
| - * @param maxWidth The maximum width of the text bubble.
|
| - * @param maxHeight The maximum height of the text bubble.
|
| */
|
| - public void showTextBubble(String text, View anchorView, int maxWidth, int maxHeight) {
|
| - mTooltipText.setText(text);
|
| - mTooltipText.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
|
| - MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
|
| + public void show(View anchorView) {
|
| mAnchorView = anchorView;
|
| calculateNewPosition();
|
| showAtCalculatedPosition();
|
| @@ -125,83 +75,61 @@ public class TextBubble
|
| * showAtCalculatedPosition should be called explicitly.
|
| */
|
| private void calculateNewPosition() {
|
| - View offsetView = mAnchorView;
|
| - int xOffset = 0;
|
| - int yOffset = 0;
|
| - if (mAnchorBelow) yOffset = mAnchorView.getHeight();
|
| -
|
| - while (offsetView != null) {
|
| - xOffset += offsetView.getLeft();
|
| - yOffset += offsetView.getTop();
|
| - if (!(offsetView.getParent() instanceof View)) break;
|
| - offsetView = (View) offsetView.getParent();
|
| - }
|
| + measureContentView();
|
| +
|
| + // Center the bubble below of the anchor, arrow pointing upward. The overlap determines how
|
| + // much of the bubble's arrow overlaps the anchor view.
|
| + int[] anchorCoordinates = {0, 0};
|
| + mAnchorView.getLocationOnScreen(anchorCoordinates);
|
| + anchorCoordinates[0] += mAnchorView.getWidth() / 2;
|
| + anchorCoordinates[1] += (int) (mAnchorView.getHeight() * (1.0 - mYOverlapPercentage));
|
|
|
| - if (mCenterView) {
|
| - // Center the tooltip over the view (calculating the width of the tooltip text).
|
| - xOffset += mAnchorView.getWidth() / 2;
|
| - } else if (LocalizationUtils.isLayoutRtl()) {
|
| - xOffset += mAnchorView.getWidth();
|
| + int bubbleWidth = mContentView.getMeasuredWidth()
|
| + + mCachedPaddingRect.left + mCachedPaddingRect.right;
|
| + mXPosition = anchorCoordinates[0] - (bubbleWidth / 2);
|
| + mYPosition = anchorCoordinates[1];
|
| +
|
| + // Make sure the bubble stays on screen.
|
| + View rootView = mAnchorView.getRootView();
|
| + if (mXPosition > rootView.getWidth() - bubbleWidth) {
|
| + mXPosition = rootView.getWidth() - bubbleWidth;
|
| + } else if (mXPosition < 0) {
|
| + mXPosition = 0;
|
| }
|
|
|
| - int tooltipWidth = mTooltipText.getMeasuredWidth();
|
| - xOffset -= tooltipWidth / 2;
|
| + // Center the tip of the arrow.
|
| + int tipCenterXPosition = anchorCoordinates[0] - mXPosition;
|
| + ((BubbleBackgroundDrawable) getBackground()).setBubbleArrowXCenter(tipCenterXPosition);
|
|
|
| - // Account for the padding of the bubble background to ensure it is centered properly.
|
| + // Update the popup's dimensions.
|
| + setWidth(MeasureSpec.makeMeasureSpec(bubbleWidth, MeasureSpec.EXACTLY));
|
| + }
|
| +
|
| + private void measureContentView() {
|
| + View rootView = mAnchorView.getRootView();
|
| getBackground().getPadding(mCachedPaddingRect);
|
| - tooltipWidth += mCachedPaddingRect.left + mCachedPaddingRect.right;
|
| - xOffset -= mCachedPaddingRect.left;
|
|
|
| - int defaultXOffset = xOffset;
|
| + // The maximum width of the bubble is determined by how wide the root view is.
|
| + int maxContentWidth =
|
| + rootView.getWidth() - mCachedPaddingRect.left - mCachedPaddingRect.right;
|
|
|
| - View rootView = mAnchorView.getRootView();
|
| - // Make sure the tooltip does not get rendered off the screen.
|
| - if (xOffset + tooltipWidth > rootView.getWidth()) {
|
| - xOffset = rootView.getWidth() - tooltipWidth - mTooltipEdgeMargin;
|
| - } else if (xOffset < 0) {
|
| - xOffset = mTooltipEdgeMargin;
|
| - }
|
| + // The maximum height of the bubble is determined by the available space below the anchor.
|
| + int anchorYOverlap = (int) -(mYOverlapPercentage * mAnchorView.getHeight());
|
| + int maxContentHeight = getMaxAvailableHeight(mAnchorView, anchorYOverlap)
|
| + - mCachedPaddingRect.top - mCachedPaddingRect.bottom;
|
|
|
| - // Move the bubble arrow to be centered over the anchor view.
|
| - int newOffset = -(xOffset - defaultXOffset);
|
| - if (Math.abs(newOffset) > mTooltipText.getMeasuredWidth() / 2 - mBubbleTipXMargin) {
|
| - newOffset = (mTooltipText.getMeasuredWidth() / 2 - mBubbleTipXMargin)
|
| - * (int) Math.signum(newOffset);
|
| - }
|
| - ((BubbleBackgroundDrawable) getBackground()).setBubbleArrowXOffset(newOffset);
|
| -
|
| - if (mAnchorBelow) {
|
| - mXPosition = xOffset;
|
| - mYPosition = yOffset - mTooltipTopMargin;
|
| - } else {
|
| - mXPosition = xOffset;
|
| - mYPosition = mAnchorView.getRootView().getHeight() - yOffset + mTooltipTopMargin;
|
| - }
|
| + int contentWidthSpec = MeasureSpec.makeMeasureSpec(maxContentWidth, MeasureSpec.AT_MOST);
|
| + int contentHeightSpec = MeasureSpec.makeMeasureSpec(maxContentHeight, MeasureSpec.AT_MOST);
|
| + mContentView.measure(contentWidthSpec, contentHeightSpec);
|
| }
|
|
|
| /**
|
| - * Shows the TextBubble in the precalculated position. Should be called after mXPosition
|
| - * and MYPosition has been set.
|
| + * Shows the TextBubble in the precalculated position.
|
| */
|
| private void showAtCalculatedPosition() {
|
| - if (mAnchorBelow) {
|
| - showAtLocation(
|
| - mAnchorView.getRootView(), Gravity.TOP | Gravity.START, mXPosition, mYPosition);
|
| - } else {
|
| - showAtLocation(mAnchorView.getRootView(), Gravity.BOTTOM | Gravity.START, mXPosition,
|
| - mYPosition);
|
| - }
|
| - }
|
| -
|
| - // The two functions below are used for the floating animation.
|
| -
|
| - /**
|
| - * Updates the y offset of the popup bubble (applied in addition to
|
| - * the default calculated offset).
|
| - * @param yoffset The new mYOffset to be used.
|
| - */
|
| - public void setOffsetY(int yoffset) {
|
| - update(mXPosition, mYPosition + yoffset, -1, -1);
|
| + mAnchorView.addOnLayoutChangeListener(this);
|
| + showAtLocation(mAnchorView.getRootView(), Gravity.TOP | Gravity.START,
|
| + mXPosition, mYPosition);
|
| }
|
|
|
| /**
|
| @@ -210,16 +138,15 @@ public class TextBubble
|
| * @return Whether the TextBubble needs to be redrawn.
|
| */
|
| private boolean updatePosition() {
|
| + BubbleBackgroundDrawable background = (BubbleBackgroundDrawable) getBackground();
|
| +
|
| int previousX = mXPosition;
|
| int previousY = mYPosition;
|
| - int previousOffset = ((BubbleBackgroundDrawable) getBackground()).getBubbleArrowOffset();
|
| + int previousOffset = background.getBubbleArrowXCenter();
|
| calculateNewPosition();
|
| - if (previousX != mXPosition || previousY != mYPosition
|
| - || previousOffset
|
| - != ((BubbleBackgroundDrawable) getBackground()).getBubbleArrowOffset()) {
|
| - return true;
|
| - }
|
| - return false;
|
| +
|
| + return previousX != mXPosition || previousY != mYPosition
|
| + || previousOffset != background.getBubbleArrowXCenter();
|
| }
|
|
|
| @Override
|
| @@ -236,46 +163,24 @@ public class TextBubble
|
| }
|
|
|
| @Override
|
| - public void onViewAttachedToWindow(View v) {}
|
| -
|
| - @Override
|
| - public void onViewDetachedFromWindow(View v) {
|
| - dismiss();
|
| + public void dismiss() {
|
| + if (mAnchorView != null) mAnchorView.removeOnLayoutChangeListener(this);
|
| + super.dismiss();
|
| }
|
|
|
| /**
|
| - * Drawable for rendering the background for a popup bubble.
|
| - *
|
| - * <p>Using a custom class as the LayerDrawable handles padding oddly and did not allow the
|
| - * bubble arrow to be rendered below the content portion of the bubble if you specified
|
| - * padding, which is required to make it look nice.
|
| + * Drawable representing a bubble with a arrow pointing upward at something.
|
| */
|
| - static class BubbleBackgroundDrawable extends Drawable {
|
| - private final int mTooltipBorderWidth;
|
| - private final Rect mTooltipContentPadding;
|
| -
|
| + private static class BubbleBackgroundDrawable extends Drawable {
|
| private final Drawable mBubbleContentsDrawable;
|
| private final BitmapDrawable mBubbleArrowDrawable;
|
| - private boolean mUp = false;
|
| - private int mBubbleArrowXOffset;
|
| + private int mBubbleArrowXCenter;
|
|
|
| - BubbleBackgroundDrawable(Context context, Bundle res) {
|
| - mUp = (res.containsKey(UP_DOWN) ? res.getBoolean(UP_DOWN) : true);
|
| - mBubbleContentsDrawable = ApiCompatibilityUtils.getDrawable(context.getResources(),
|
| - R.drawable.bubble_white);
|
| + BubbleBackgroundDrawable(Context context) {
|
| + mBubbleContentsDrawable = ApiCompatibilityUtils.getDrawable(
|
| + context.getResources(), R.drawable.menu_bg);
|
| mBubbleArrowDrawable = (BitmapDrawable) ApiCompatibilityUtils.getDrawable(
|
| context.getResources(), R.drawable.bubble_point_white);
|
| - mTooltipBorderWidth =
|
| - context.getResources().getDimensionPixelSize(R.dimen.tooltip_border_width);
|
| -
|
| - if (res.getBoolean(BACKGROUND_INTRINSIC_PADDING, false)) {
|
| - mTooltipContentPadding = new Rect();
|
| - mBubbleContentsDrawable.getPadding(mTooltipContentPadding);
|
| - } else {
|
| - int padding = context.getResources().getDimensionPixelSize(
|
| - R.dimen.tooltip_content_padding);
|
| - mTooltipContentPadding = new Rect(padding, padding, padding, padding);
|
| - }
|
| }
|
|
|
| @Override
|
| @@ -286,30 +191,28 @@ public class TextBubble
|
|
|
| @Override
|
| protected void onBoundsChange(Rect bounds) {
|
| + super.onBoundsChange(bounds);
|
| if (bounds == null) return;
|
|
|
| - super.onBoundsChange(bounds);
|
| + // The arrow hugs the top boundary and pushes the rest of the rectangular portion of the
|
| + // callout beneath it.
|
| int halfArrowWidth = mBubbleArrowDrawable.getIntrinsicWidth() / 2;
|
| - int halfBoundsWidth = bounds.width() / 2;
|
| - if (mUp) {
|
| - int contentsTop = bounds.top + mBubbleArrowDrawable.getIntrinsicHeight()
|
| - - mTooltipBorderWidth;
|
| - mBubbleContentsDrawable.setBounds(
|
| - bounds.left, contentsTop, bounds.right, bounds.bottom);
|
| - mBubbleArrowDrawable.setBounds(
|
| - mBubbleArrowXOffset + halfBoundsWidth - halfArrowWidth, bounds.top,
|
| - mBubbleArrowXOffset + halfBoundsWidth + halfArrowWidth,
|
| - bounds.top + mBubbleArrowDrawable.getIntrinsicHeight());
|
| - } else {
|
| - int contentsBottom = bounds.bottom - mBubbleArrowDrawable.getIntrinsicHeight();
|
| - mBubbleContentsDrawable.setBounds(
|
| - bounds.left, bounds.left, bounds.right, contentsBottom);
|
| - mBubbleArrowDrawable.setBounds(
|
| - mBubbleArrowXOffset + halfBoundsWidth - halfArrowWidth,
|
| - contentsBottom - mTooltipBorderWidth,
|
| - mBubbleArrowXOffset + halfBoundsWidth + halfArrowWidth, contentsBottom
|
| - + mBubbleArrowDrawable.getIntrinsicHeight() - mTooltipBorderWidth);
|
| - }
|
| + int arrowLeft = mBubbleArrowXCenter + bounds.left - halfArrowWidth;
|
| + int arrowRight = arrowLeft + mBubbleArrowDrawable.getIntrinsicWidth();
|
| + mBubbleArrowDrawable.setBounds(
|
| + arrowLeft,
|
| + bounds.top,
|
| + arrowRight,
|
| + bounds.top + mBubbleArrowDrawable.getIntrinsicHeight());
|
| +
|
| + // Adjust the background of the callout to account for the side margins and the arrow.
|
| + Rect bubblePadding = new Rect();
|
| + mBubbleContentsDrawable.getPadding(bubblePadding);
|
| + mBubbleContentsDrawable.setBounds(
|
| + bounds.left,
|
| + bounds.top + mBubbleArrowDrawable.getIntrinsicHeight() - bubblePadding.top,
|
| + bounds.right,
|
| + bounds.bottom);
|
| }
|
|
|
| @Override
|
| @@ -330,34 +233,29 @@ public class TextBubble
|
|
|
| @Override
|
| public boolean getPadding(Rect padding) {
|
| - padding.set(mTooltipContentPadding);
|
| - if (mUp) {
|
| - padding.set(padding.left, padding.top + mBubbleArrowDrawable.getIntrinsicHeight(),
|
| - padding.right, padding.bottom);
|
| - } else {
|
| - padding.set(padding.left, padding.top, padding.right,
|
| - padding.bottom + mBubbleArrowDrawable.getIntrinsicHeight());
|
| - }
|
| + mBubbleContentsDrawable.getPadding(padding);
|
|
|
| + padding.set(padding.left,
|
| + Math.max(padding.top, mBubbleArrowDrawable.getIntrinsicHeight()),
|
| + padding.right,
|
| + padding.bottom);
|
| return true;
|
| }
|
|
|
| /**
|
| - * Updates the additional X Offset for the bubble arrow. The arrow defaults to being
|
| - * centered in the bubble, so this is delta from the center.
|
| - *
|
| + * Updates where the bubble arrow should be centered along the x-axis.
|
| * @param xOffset The offset of the bubble arrow.
|
| */
|
| - public void setBubbleArrowXOffset(int xOffset) {
|
| - mBubbleArrowXOffset = xOffset;
|
| + public void setBubbleArrowXCenter(int xOffset) {
|
| + mBubbleArrowXCenter = xOffset;
|
| onBoundsChange(getBounds());
|
| }
|
|
|
| /**
|
| - * @return the current x offset for the bubble arrow.
|
| + * @return the current x center for the bubble arrow.
|
| */
|
| - public int getBubbleArrowOffset() {
|
| - return mBubbleArrowXOffset;
|
| + public int getBubbleArrowXCenter() {
|
| + return mBubbleArrowXCenter;
|
| }
|
| }
|
| }
|
|
|