chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tail/AlignmentManager.java

// Copyright 2019 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.omnibox.suggestions.tail;

import android.util.ArraySet;
import android.view.View;

import org.chromium.ui.base.ViewUtils;

/**
 * Coordinates horizontal alignment of the tail suggestions. Tail suggestions are aligned to - the
 * user input in the Omnibox, when possible, - to each other (left edge) when longest tail
 * suggestion makes it impossible to align it to user input.
 *
 * <p>Examples:
 *
 * <p>Aligned to User input:
 *
 * <pre>
 *    ( User Query In Omni             )
 *    [           ... Omnibox          ]
 *    [           ... Omnibox Android  ]
 * </pre>
 *
 * <p>Aligned to longest suggestion:
 *
 * <pre>
 *    ( Longer User Query In The Omni  )
 *    [             ... Omnibox        ]
 *    [             ... Omnibox Android]
 * </pre>
 */
class AlignmentManager {
    private final ArraySet<View> mVisibleTailSuggestions = new ArraySet<>();
    private int mLongestFullTextWidth;
    private int mLongestCompletionWidth;

    /**
     * Compute additional suggestion text offset for tail suggestions. Ensures that tail suggestion
     * is - aligned with user query, if adjusted suggestion can still fit in the available space, or
     * right edge otherwise, - all tail suggestions are left aligned with each other.
     *
     * @param requestor View requesting the pad.
     * @param completionTextWidth Length of the text that is displayed in the suggestion.
     * @param fullTextWidth Total length of the query (user input + tail).
     * @param textAreaWidth Maximum area size available for tail suggestion.
     * @return additional padding required to properly adjust tail suggestion.
     */
    int requestStartPadding(
            View requestor, int completionTextWidth, int fullTextWidth, int textAreaWidth) {
        final int lastLongestFullTextWidth = mLongestFullTextWidth;
        mLongestFullTextWidth = Math.max(mLongestFullTextWidth, fullTextWidth);
        mLongestCompletionWidth = Math.max(mLongestCompletionWidth, completionTextWidth);

        // If there is enough space to render entire user text, padding is equal to everything not
        // covered by query text.
        if (textAreaWidth >= mLongestFullTextWidth) {
            return (fullTextWidth - completionTextWidth);
        }

        // Only re-layout all children, if we found a new longest text.
        if (mLongestFullTextWidth != lastLongestFullTextWidth) {
            relayoutAllViewsExcept(requestor);
        }

        return Math.max(textAreaWidth - mLongestCompletionWidth, 0);
    }

    /**
     * Register view to receive layout requests in cases where it may be misaligned with other
     * views.
     *
     * @param View view to register.
     */
    void registerView(View view) {
        mVisibleTailSuggestions.add(view);
    }

    /**
     * Re-layout all views except supplied one. This call is needed to re-align all existing
     * suggestion views, if the newly added view causes lack of horizontal alignment.
     *
     * @param excluded View to be excluded from re-layout process.
     */
    private void relayoutAllViewsExcept(View excluded) {
        final int count = mVisibleTailSuggestions.size();
        for (int index = 0; index < count; ++index) {
            final View view = mVisibleTailSuggestions.valueAt(index);
            if (view == excluded) continue;
            ViewUtils.requestLayout(view, "AlignmentManger.relayoutAllViewsExcept");
        }
    }
}