// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.compositor.overlays.strip;
import android.content.Context;
import android.graphics.Rect;
import android.util.FloatProperty;
import androidx.annotation.ColorInt;
import org.chromium.base.MathUtils;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.ui.base.LocalizationUtils;
/**
* {@link StripLayoutGroupTitle} is used to keep track of the strip position and rendering
* information for a particular tab group title indicator on the tab strip so it can draw itself
* onto the GL canvas.
*/
public class StripLayoutGroupTitle extends StripLayoutView {
private final Context mContext;
/**
* Get the bounds of the view w.r.t screen.
*
* @param out Screen coordinates of the view to populate.
* @param windowRectSupplier Supplier for holder window bounds for computation.
*/
public void getDrawBoundsOnScreen(Rect out, Supplier<Rect> windowRectSupplier) {
float dpToPx = mContext.getResources().getDisplayMetrics().density;
int leftWithOffset = (int) (getPaddedX() * dpToPx) + windowRectSupplier.get().left;
int topWithOffset = (int) (getPaddedY() * dpToPx) + windowRectSupplier.get().top;
out.set(
leftWithOffset,
topWithOffset,
(int) (leftWithOffset + (getPaddedWidth() * dpToPx)),
(int) (topWithOffset + (getPaddedHeight() * dpToPx)));
}
/** Delegate for additional group title functionality. */
public interface StripLayoutGroupTitleDelegate {
/**
* Releases the resources associated with this group indicator.
*
* @param rootId The root ID of the given group indicator.
*/
void releaseResourcesForGroupTitle(int rootId);
/**
* Rebuilds the resources associated with this group indicator.
*
* @param groupTitle This group indicator.
*/
void rebuildResourcesForGroupTitle(StripLayoutGroupTitle groupTitle);
/**
* Handles group title click action.
*
* @param groupTitle The group title that was clicked.
*/
void handleGroupTitleClick(StripLayoutGroupTitle groupTitle);
}
/** A property for animations to use for changing the width of the bottom indicator. */
public static final FloatProperty<StripLayoutGroupTitle> BOTTOM_INDICATOR_WIDTH =
new FloatProperty<>("bottomIndicatorWidth") {
@Override
public void setValue(StripLayoutGroupTitle object, float value) {
object.setBottomIndicatorWidth(value);
}
@Override
public Float get(StripLayoutGroupTitle object) {
return object.getBottomIndicatorWidth();
}
};
// Position constants.
private static final int MIN_VISUAL_WIDTH_DP = 24;
private static final int MAX_VISUAL_WIDTH_DP = 156;
private static final int MARGIN_TOP_DP = 7;
private static final int MARGIN_BOTTOM_DP = 9;
private static final int MARGIN_START_DP = 13;
private static final int MARGIN_END_DP = 9;
private static final int TEXT_PADDING_DP = 8;
private static final int CORNER_RADIUS_DP = 7;
private static final float BOTTOM_INDICATOR_HEIGHT_DP = 2.f;
private static final int WIDTH_MARGINS_DP = MARGIN_START_DP + MARGIN_END_DP;
private static final int EFFECTIVE_MIN_WIDTH = MIN_VISUAL_WIDTH_DP + WIDTH_MARGINS_DP;
private static final int EFFECTIVE_MAX_WIDTH = MAX_VISUAL_WIDTH_DP + WIDTH_MARGINS_DP;
// External influences.
private final StripLayoutGroupTitleDelegate mDelegate;
// Tab group variables.
// Tab group's root Id this view refers to.
private int mRootId;
private String mTitle;
@ColorInt private int mColor;
// Bottom indicator variables
private float mBottomIndicatorWidth;
/**
* Create a {@link StripLayoutGroupTitle} that represents the TabGroup for the {@code rootId}.
*
* @param delegate The delegate for additional strip group title functionality.
* @param incognito Whether or not this tab group is Incognito.
* @param rootId The root ID for the tab group.
*/
public StripLayoutGroupTitle(
Context context,
StripLayoutGroupTitleDelegate delegate,
boolean incognito,
int rootId) {
super(incognito);
assert rootId != Tab.INVALID_TAB_ID : "Tried to create a group title for an invalid group.";
mRootId = rootId;
mContext = context;
mDelegate = delegate;
}
@Override
void onVisibilityChanged(boolean newVisibility) {
if (newVisibility) {
mDelegate.rebuildResourcesForGroupTitle(this);
} else {
mDelegate.releaseResourcesForGroupTitle(mRootId);
}
}
@Override
public void setIncognito(boolean incognito) {
assert false : "Incognito state of a group title cannot change";
}
@Override
public boolean hasClickAction() {
return ChromeFeatureList.sTabStripGroupCollapse.isEnabled();
}
@Override
public boolean hasLongClickAction() {
// TODO(https://crbug.com/333777015): Implement long press to drag tab group.
return false;
}
@Override
public void handleClick(long time) {
mDelegate.handleGroupTitleClick(this);
}
/**
* @return DrawX accounting for padding.
*/
public float getPaddedX() {
return getDrawX() + (LocalizationUtils.isLayoutRtl() ? MARGIN_END_DP : MARGIN_START_DP);
}
/**
* @return DrawY accounting for padding.
*/
public float getPaddedY() {
return getDrawY() + MARGIN_TOP_DP;
}
/**
* @return Width accounting for padding.
*/
public float getPaddedWidth() {
return getWidth() - MARGIN_START_DP - MARGIN_END_DP;
}
/**
* @return Height accounting for padding.
*/
public float getPaddedHeight() {
return getHeight() - MARGIN_TOP_DP - MARGIN_BOTTOM_DP;
}
/**
* @return The tint color resource that represents the tab group title indicator background.
*/
public @ColorInt int getTint() {
return mColor;
}
/**
* @param color The color used when displaying this group.
*/
public void updateTint(@ColorInt int color) {
mColor = color;
}
/**
* @return The group's title.
*/
protected String getTitle() {
return mTitle;
}
protected void updateTitle(String title, float textWidth) {
mTitle = title;
// Account for view padding & margins. Increment to prevent off-by-one rounding errors
// adding a title fade when unnecessary.
float viewWidth = textWidth + (TEXT_PADDING_DP * 2) + WIDTH_MARGINS_DP + 1;
setWidth(MathUtils.clamp(viewWidth, EFFECTIVE_MIN_WIDTH, EFFECTIVE_MAX_WIDTH));
}
/**
* @return The group's root ID.
*/
public int getRootId() {
return mRootId;
}
/**
* @param rootId The tab group's new rootId. Should be synced with the {@link
* org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter}.
*/
protected void updateRootId(int rootId) {
mRootId = rootId;
}
/**
* @return The padding for the title text.
*/
public int getTitleTextPadding() {
return TEXT_PADDING_DP;
}
/**
* @return The corner radius for the title container.
*/
public int getCornerRadius() {
return CORNER_RADIUS_DP;
}
/**
* @return The width of the bottom indicator should be applied to this tab group.
*/
public float getBottomIndicatorWidth() {
return mBottomIndicatorWidth;
}
/**
* @param bottomIndicatorWidth The width of the bottom indicator should be applied to this tab
* group.
*/
public void setBottomIndicatorWidth(float bottomIndicatorWidth) {
mBottomIndicatorWidth = bottomIndicatorWidth;
}
/**
* @return The height of the bottom indicator should be applied to this tab group.
*/
public float getBottomIndicatorHeight() {
return BOTTOM_INDICATOR_HEIGHT_DP;
}
}