blob: 91d57c0e75864cfb66326038cf1742538c91692c [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/containers/fixed_flat_map.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
// To use CreateVectorIconFromSource.
#define GFX_VECTOR_ICONS_UNSAFE
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_utils.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/examples/example_base.h"
#include "ui/views/examples/examples_main_proc.h"
#include "ui/views/examples/vector_example.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/view.h"
#if BUILDFLAG(IS_WIN)
#include "base/strings/utf_string_conversions.h"
#endif
namespace {
std::optional<SkColor> ParseHexColor(const std::string& color_str) {
std::string lower_color_str = base::ToLowerASCII(color_str);
static constexpr auto kColorMap =
base::MakeFixedFlatMap<std::string_view, SkColor>({
{"black", SK_ColorBLACK},
{"blue", SK_ColorBLUE},
{"cyan", SK_ColorCYAN},
{"darkgray", SK_ColorDKGRAY},
{"gray", SK_ColorGRAY},
{"green", SK_ColorGREEN},
{"lightgray", SK_ColorLTGRAY},
{"magenta", SK_ColorMAGENTA},
{"red", SK_ColorRED},
{"transparent", SK_ColorTRANSPARENT},
{"white", SK_ColorWHITE},
{"yellow", SK_ColorYELLOW},
});
auto it = kColorMap.find(lower_color_str);
if (it != kColorMap.end()) {
return it->second;
}
std::string hex = color_str;
if (hex.starts_with("#")) {
hex = hex.substr(1);
} else if (hex.starts_with("0x") || hex.starts_with("0X")) {
hex = hex.substr(2);
}
uint32_t value = 0;
if (!base::HexStringToUInt(hex, &value)) {
return std::nullopt;
}
if (hex.length() <= 6) {
return SkColorSetA(value, 0xFF);
}
return value;
}
class DynamicVectorIcon {
public:
explicit DynamicVectorIcon(const std::string& icon_source) {
gfx::ParsePathElements(icon_source, path_elements_);
for (const auto& path : path_elements_) {
reps_.push_back({path});
}
icon_ = std::make_unique<gfx::VectorIcon>(reps_.data(), reps_.size(),
"DynamicVectorIcon");
}
DynamicVectorIcon(const DynamicVectorIcon&) = delete;
DynamicVectorIcon& operator=(const DynamicVectorIcon&) = delete;
const gfx::VectorIcon& GetVectorIcon() const { return *icon_; }
private:
std::vector<std::vector<gfx::PathElement>> path_elements_;
std::vector<gfx::VectorIconRep> reps_;
std::unique_ptr<gfx::VectorIcon> icon_;
};
class VectorIconViewerExample : public views::examples::ExampleBase {
public:
VectorIconViewerExample(const std::string& icon_source,
std::optional<int> custom_size,
std::optional<SkColor> icon_color,
std::optional<SkColor> bg_color)
: ExampleBase("Vector Icon Viewer"),
dynamic_icon_(views::examples::CleanUpContents(icon_source)),
custom_size_(std::move(custom_size)),
icon_color_(icon_color),
bg_color_(bg_color) {}
VectorIconViewerExample(const VectorIconViewerExample&) = delete;
VectorIconViewerExample& operator=(const VectorIconViewerExample&) = delete;
~VectorIconViewerExample() override = default;
void CreateExampleView(views::View* container) override {
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal);
layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
container->SetLayoutManager(std::move(layout));
container->SetBackground(views::CreateSolidBackground(SK_ColorGRAY));
auto image_view = std::make_unique<views::ImageView>();
image_view->SetBackground(
views::CreateSolidBackground(bg_color_.value_or(SK_ColorWHITE)));
if (custom_size_) {
image_view->SetPreferredSize(gfx::Size(*custom_size_, *custom_size_));
}
int size = custom_size_.value_or(
gfx::GetDefaultSizeOfVectorIcon(dynamic_icon_.GetVectorIcon()));
if (size <= 0) {
size = 128;
}
image_view->SetImage(ui::ImageModel::FromImageSkia(
gfx::CreateVectorIcon(dynamic_icon_.GetVectorIcon(), size,
icon_color_.value_or(SK_ColorBLACK))));
container->AddChildView(std::move(image_view));
}
private:
DynamicVectorIcon dynamic_icon_;
std::optional<int> custom_size_;
std::optional<SkColor> icon_color_;
std::optional<SkColor> bg_color_;
};
} // namespace
int main(int argc, char** argv) {
base::CommandLine::Init(argc, argv);
TestTimeouts::Initialize();
base::AtExitManager at_exit;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::CommandLine::StringVector args = command_line->GetArgs();
std::optional<int> custom_size;
if (command_line->HasSwitch("size")) {
std::string size_str = command_line->GetSwitchValueASCII("size");
if (size_str.empty() && !args.empty()) {
#if BUILDFLAG(IS_WIN)
size_str = base::WideToUTF8(args.front());
#else
size_str = args.front();
#endif
args.erase(args.begin());
}
int parsed_size = 0;
if (base::StringToInt(size_str, &parsed_size)) {
custom_size = parsed_size;
} else {
LOG(ERROR) << "Invalid size: " << size_str;
return 1;
}
}
std::optional<SkColor> icon_color;
if (command_line->HasSwitch("icon-color")) {
std::string color_str = command_line->GetSwitchValueASCII("icon-color");
if (color_str.empty() && !args.empty()) {
#if BUILDFLAG(IS_WIN)
color_str = base::WideToUTF8(args.front());
#else
color_str = args.front();
#endif
args.erase(args.begin());
}
icon_color = ParseHexColor(color_str);
if (!icon_color) {
LOG(ERROR) << "Invalid icon color: " << color_str;
return 1;
}
}
std::optional<SkColor> bg_color;
if (command_line->HasSwitch("background-color")) {
std::string color_str =
command_line->GetSwitchValueASCII("background-color");
if (color_str.empty() && !args.empty()) {
#if BUILDFLAG(IS_WIN)
color_str = base::WideToUTF8(args.front());
#else
color_str = args.front();
#endif
args.erase(args.begin());
}
bg_color = ParseHexColor(color_str);
if (!bg_color) {
LOG(ERROR) << "Invalid background color: " << color_str;
return 1;
}
}
if (args.empty()) {
LOG(ERROR) << "Usage: vector_icon_viewer [--size size] [--icon-color "
"color] [--background-color color] <path_to_icon_file>";
return 1;
}
base::FilePath file_path(args[0]);
std::string file_content;
if (!base::ReadFileToString(file_path, &file_content)) {
LOG(ERROR) << "Failed to read file: " << file_path;
return 1;
}
views::examples::ExampleVector examples;
examples.push_back(std::make_unique<VectorIconViewerExample>(
file_content, custom_size, icon_color, bg_color));
return static_cast<int>(
views::examples::ExamplesMainProc(false, std::move(examples)));
}