| // 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. |
| |
| #include "ui/views/examples/examples_window.h" |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <iterator> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/containers/contains.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/stl_util.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/ui_base_paths.h" |
| #include "ui/color/color_id.h" |
| #include "ui/views/background.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/tabbed_pane/tabbed_pane.h" |
| #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" |
| #include "ui/views/examples/create_examples.h" |
| #include "ui/views/examples/grit/views_examples_resources.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| namespace views::examples { |
| |
| namespace { |
| |
| constexpr char kEnableExamples[] = "enable-examples"; |
| |
| ExampleVector GetExamplesToShow(ExampleVector examples) { |
| using StringVector = std::vector<std::string>; |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| |
| std::ranges::sort(examples, {}, &ExampleBase::example_title); |
| |
| std::string enable_examples = |
| command_line->GetSwitchValueASCII(kEnableExamples); |
| |
| if (!enable_examples.empty()) { |
| // Filter examples to show based on the command line switch. |
| StringVector enabled = |
| base::SplitString(enable_examples, ";,", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| |
| // Transform list of examples to just the list of names. |
| StringVector example_names; |
| std::ranges::transform(examples, std::back_inserter(example_names), |
| &ExampleBase::example_title); |
| |
| std::ranges::sort(enabled); |
| |
| // Get an intersection of list of titles between the full list and the list |
| // from the command-line. |
| StringVector valid_examples = |
| base::STLSetIntersection<StringVector>(enabled, example_names); |
| |
| // If there are still example names in the list, only include the examples |
| // from the list. |
| if (!valid_examples.empty()) { |
| std::erase_if(examples, [&](const auto& example) { |
| return !base::Contains(valid_examples, example->example_title()); |
| }); |
| } |
| } else if (command_line->HasSwitch(kEnableExamples)) { |
| std::string titles; |
| for (auto& example : examples) { |
| titles = base::StrCat({titles, "\n\t", example->example_title()}); |
| } |
| std::cout << "By default, all examples will be shown.\n"; |
| std::cout << "You may want to specify the example(s) you want to run:" |
| << titles << '\n'; |
| } |
| return examples; |
| } |
| |
| } // namespace |
| |
| bool CheckCommandLineUsage() { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch("help")) { |
| // Print the program usage. |
| std::cout << "Usage: " << command_line->GetProgram() << " [--" |
| << kEnableExamples << "=<example1,[example2...]>]\n"; |
| return true; |
| } |
| return false; |
| } |
| |
| class ExamplesWindowContents : public WidgetDelegateView, |
| public TabbedPaneListener { |
| public: |
| ExamplesWindowContents(base::OnceClosure on_close, ExampleVector examples) |
| : on_close_(std::move(on_close)) { |
| SetHasWindowSizeControls(true); |
| SetBackground(CreateSolidBackground(ui::kColorDialogBackground)); |
| |
| auto* layout = SetLayoutManager( |
| std::make_unique<views::BoxLayout>(BoxLayout::Orientation::kVertical)); |
| |
| auto tabbed_pane = |
| std::make_unique<TabbedPane>(TabbedPane::Orientation::kVertical, |
| TabbedPane::TabStripStyle::kBorder, true); |
| |
| tabbed_pane_ = AddChildView(std::move(tabbed_pane)); |
| layout->SetFlexForView(tabbed_pane_, 1); |
| CreateSidePanel(std::move(examples)); |
| |
| status_label_ = AddChildView(std::make_unique<Label>()); |
| status_label_->SetVisible(false); |
| tabbed_pane_->SetListener(this); |
| instance_ = this; |
| } |
| |
| ExamplesWindowContents(const ExamplesWindowContents&) = delete; |
| ExamplesWindowContents& operator=(const ExamplesWindowContents&) = delete; |
| |
| ~ExamplesWindowContents() override = default; |
| |
| // Sets the status area (at the bottom of the window) to |status|. |
| void SetStatus(std::string_view status) { |
| status_label_->SetText(base::UTF8ToUTF16(status)); |
| status_label_->SetVisible(!status.empty()); |
| } |
| |
| void TabSelectedAt(int index) override { status_label_->SetVisible(false); } |
| |
| static ExamplesWindowContents* instance() { return instance_; } |
| |
| private: |
| // WidgetDelegateView: |
| std::u16string GetWindowTitle() const override { return u"Views Examples"; } |
| void WindowClosing() override { |
| instance_ = nullptr; |
| if (on_close_) { |
| std::move(on_close_).Run(); |
| } |
| } |
| gfx::Size CalculatePreferredSize( |
| const SizeBounds& /*available_size*/) const override { |
| gfx::Size size(800, 300); |
| for (size_t i = 0; i < tabbed_pane_->GetTabCount(); ++i) { |
| size.set_height( |
| std::max(size.height(), |
| tabbed_pane_->GetTabContents(i)->GetHeightForWidth(800))); |
| } |
| return size; |
| } |
| gfx::Size GetMinimumSize() const override { return gfx::Size(50, 50); } |
| |
| void CreateSidePanel(ExampleVector examples) { |
| for (auto& example : examples) { |
| auto tab_contents = std::make_unique<View>(); |
| example->CreateExampleView(tab_contents.get()); |
| example->SetContainer( |
| tabbed_pane_->AddTab(base::UTF8ToUTF16(example->example_title()), |
| std::move(tab_contents))); |
| } |
| examples_ = std::move(examples); |
| } |
| |
| static ExamplesWindowContents* instance_; |
| raw_ptr<Label> status_label_ = nullptr; |
| base::OnceClosure on_close_; |
| raw_ptr<TabbedPane> tabbed_pane_ = nullptr; |
| ExampleVector examples_; |
| }; |
| |
| // static |
| ExamplesWindowContents* ExamplesWindowContents::instance_ = nullptr; |
| |
| Widget* GetExamplesWidget() { |
| return ExamplesWindowContents::instance() |
| ? ExamplesWindowContents::instance()->GetWidget() |
| : nullptr; |
| } |
| |
| void ShowExamplesWindow(base::OnceClosure on_close, |
| ExampleVector examples, |
| gfx::NativeWindow window_context) { |
| if (ExamplesWindowContents::instance()) { |
| ExamplesWindowContents::instance()->GetWidget()->Activate(); |
| } else { |
| examples = GetExamplesToShow(std::move(examples)); |
| Widget* widget = new Widget; |
| Widget::InitParams params(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET, |
| Widget::InitParams::TYPE_WINDOW); |
| params.delegate = |
| new ExamplesWindowContents(std::move(on_close), std::move(examples)); |
| params.context = window_context; |
| params.name = kExamplesWidgetName; |
| widget->Init(std::move(params)); |
| widget->Show(); |
| } |
| } |
| |
| void PrintStatus(std::string_view status) { |
| if (ExamplesWindowContents::instance()) { |
| ExamplesWindowContents::instance()->SetStatus(status); |
| } |
| } |
| |
| } // namespace views::examples |