chromium/ui/gfx/text_elider.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file implements utility functions for eliding and formatting UI text.
//
// Note that several of the functions declared in text_elider.h are implemented
// in this file using helper classes in an unnamed namespace.

#include "ui/gfx/text_elider.h"

#include <stdint.h>

#include <algorithm>
#include <memory>
#include <numeric>
#include <string>
#include <vector>

#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/i18n/break_iterator.h"
#include "base/i18n/char_iterator.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/notreached.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "third_party/icu/source/common/unicode/rbbi.h"
#include "third_party/icu/source/common/unicode/uloc.h"
#include "third_party/icu/source/common/unicode/umachine.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/text_utils.h"

ASCIIToUTF16;
UTF8ToUTF16;
WideToUTF16;

namespace gfx {

namespace {

#if BUILDFLAG(IS_IOS)
// The returned string will have at least one character besides the ellipsis
// on either side of '@'; if that's impossible, a single ellipsis is returned.
// If possible, only the username is elided. Otherwise, the domain is elided
// in the middle, splitting available width equally with the elided username.
// If the username is short enough that it doesn't need half the available
// width, the elided domain will occupy that extra width.
std::u16string ElideEmail(const std::u16string& email,
                          const FontList& font_list,
                          float available_pixel_width) {
  if (GetStringWidthF(email, font_list) <= available_pixel_width)
    return email;

  // Split the email into its local-part (username) and domain-part. The email
  // spec allows for @ symbols in the username under some special requirements,
  // but not in the domain part, so splitting at the last @ symbol is safe.
  const size_t split_index = email.find_last_of('@');
  DCHECK_NE(split_index, std::u16string::npos);
  std::u16string username = email.substr(0, split_index);
  std::u16string domain = email.substr(split_index + 1);
  DCHECK(!username.empty());
  DCHECK(!domain.empty());

  // Subtract the @ symbol from the available width as it is mandatory.
  const std::u16string kAtSignUTF16 = u"@";
  available_pixel_width -= GetStringWidthF(kAtSignUTF16, font_list);

  // Check whether eliding the domain is necessary: if eliding the username
  // is sufficient, the domain will not be elided.
  const float full_username_width = GetStringWidthF(username, font_list);
  const float available_domain_width =
      available_pixel_width -
      std::min(full_username_width,
               GetStringWidthF(username.substr(0, 1) + kEllipsisUTF16,
                               font_list));
  if (GetStringWidthF(domain, font_list) > available_domain_width) {
    // Elide the domain so that it only takes half of the available width.
    // Should the username not need all the width available in its half, the
    // domain will occupy the leftover width.
    // If |desired_domain_width| is greater than |available_domain_width|: the
    // minimal username elision allowed by the specifications will not fit; thus
    // |desired_domain_width| must be <= |available_domain_width| at all cost.
    const float desired_domain_width =
        std::min(available_domain_width,
                 std::max(available_pixel_width - full_username_width,
                          available_pixel_width / 2));
    domain = ElideText(domain, font_list, desired_domain_width, ELIDE_MIDDLE);
    // Failing to elide the domain such that at least one character remains
    // (other than the ellipsis itself) remains: return a single ellipsis.
    if (domain.length() <= 1U)
      return std::u16string(kEllipsisUTF16);
  }

  // Fit the username in the remaining width (at this point the elided username
  // is guaranteed to fit with at least one character remaining given all the
  // precautions taken earlier).
  available_pixel_width -= GetStringWidthF(domain, font_list);
  username = ElideText(username, font_list, available_pixel_width, ELIDE_TAIL);
  return username + kAtSignUTF16 + domain;
}
#endif

bool GetDefaultWhitespaceElision(bool elide_in_middle,
                                 bool elide_at_beginning) {}

}  // namespace

// U+2026 in utf8
const char kEllipsis[] =;
const char16_t kEllipsisUTF16[] =;
const char16_t kForwardSlash =;

StringSlicer::StringSlicer(const std::u16string& text,
                           const std::u16string& ellipsis,
                           bool elide_in_middle,
                           bool elide_at_beginning,
                           std::optional<bool> elide_whitespace)
    :{}

std::u16string StringSlicer::CutString(size_t length,
                                       bool insert_ellipsis) const {}

std::u16string ElideFilename(const base::FilePath& filename,
                             const FontList& font_list,
                             float available_pixel_width) {}

std::u16string ElideText(const std::u16string& text,
                         const FontList& font_list,
                         float available_pixel_width,
                         ElideBehavior behavior) {}

bool ElideString(const std::u16string& input,
                 size_t max_len,
                 std::u16string* output) {}

namespace {

// Internal class used to track progress of a rectangular string elide
// operation.  Exists so the top-level ElideRectangleString() function
// can be broken into smaller methods sharing this state.
class RectangleString {};

void RectangleString::AddString(const std::u16string& input) {}

bool RectangleString::Finalize() {}

void RectangleString::AddLine(const std::u16string& line) {}

void RectangleString::AddWord(const std::u16string& word) {}

void RectangleString::Append(const std::u16string& string) {}

void RectangleString::NewLine(bool output) {}

// Internal class used to track progress of a rectangular text elide
// operation.  Exists so the top-level ElideRectangleText() function
// can be broken into smaller methods sharing this state.
class RectangleText {};

void RectangleText::AddString(const std::u16string& input) {}

int RectangleText::Finalize() {}

void RectangleText::AddLine(const std::u16string& line) {}

int RectangleText::WrapWord(const std::u16string& word) {}

int RectangleText::AddWordOverflow(const std::u16string& word) {}

int RectangleText::AddWord(const std::u16string& word) {}

void RectangleText::AddToCurrentLine(const std::u16string& text) {}

void RectangleText::AddToCurrentLineWithWidth(const std::u16string& text,
                                              float text_width) {}

bool RectangleText::NewLine() {}

}  // namespace

bool ElideRectangleString(const std::u16string& input,
                          size_t max_rows,
                          size_t max_cols,
                          bool strict,
                          std::u16string* output) {}

int ElideRectangleText(const std::u16string& input,
                       const FontList& font_list,
                       float available_pixel_width,
                       int available_pixel_height,
                       WordWrapBehavior wrap_behavior,
                       std::vector<std::u16string>* lines) {}

std::u16string TruncateString(const std::u16string& string,
                              size_t length,
                              BreakType break_type) {}

}  // namespace gfx