| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/examples/multiline_example.h" |
| |
| #include <stddef.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/events/event.h" |
| #include "ui/gfx/range/range.h" |
| #include "ui/gfx/render_text.h" |
| #include "ui/views/background.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/button/checkbox.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/controls/textfield/textfield.h" |
| #include "ui/views/examples/grit/views_examples_resources.h" |
| #include "ui/views/layout/table_layout.h" |
| #include "ui/views/view.h" |
| |
| using l10n_util::GetStringUTF16; |
| using l10n_util::GetStringUTF8; |
| |
| namespace views { |
| namespace examples { |
| |
| namespace { |
| |
| gfx::Range ClampRange(gfx::Range range, uint32_t max) { |
| range.set_start(std::min(range.start(), max)); |
| range.set_end(std::min(range.end(), max)); |
| return range; |
| } |
| |
| // A Label with a clamped preferred width to demonstrate wrapping. |
| class PreferredSizeLabel : public Label { |
| public: |
| PreferredSizeLabel() = default; |
| |
| PreferredSizeLabel(const PreferredSizeLabel&) = delete; |
| PreferredSizeLabel& operator=(const PreferredSizeLabel&) = delete; |
| |
| ~PreferredSizeLabel() override = default; |
| |
| // Label: |
| gfx::Size CalculatePreferredSize() const override { |
| return gfx::Size(50, Label::CalculatePreferredSize().height()); |
| } |
| }; |
| |
| } // namespace |
| |
| // A simple View that hosts a RenderText object. |
| class MultilineExample::RenderTextView : public View { |
| public: |
| RenderTextView() : render_text_(gfx::RenderText::CreateRenderText()) { |
| render_text_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
| render_text_->SetColor(SK_ColorBLACK); |
| render_text_->set_selection_color(SK_ColorBLACK); |
| render_text_->set_selection_background_focused_color(SK_ColorGRAY); |
| render_text_->SetMultiline(true); |
| SetBorder(CreateSolidBorder(2, SK_ColorGRAY)); |
| } |
| |
| RenderTextView(const RenderTextView&) = delete; |
| RenderTextView& operator=(const RenderTextView&) = delete; |
| |
| void OnPaint(gfx::Canvas* canvas) override { |
| View::OnPaint(canvas); |
| render_text_->Draw(canvas); |
| } |
| |
| gfx::Size CalculatePreferredSize() const override { |
| // Turn off multiline mode to get the single-line text size, which is the |
| // preferred size for this view. |
| render_text_->SetMultiline(false); |
| gfx::Size size(render_text_->GetContentWidth(), |
| render_text_->GetStringSize().height()); |
| size.Enlarge(GetInsets().width(), GetInsets().height()); |
| render_text_->SetMultiline(true); |
| return size; |
| } |
| |
| int GetHeightForWidth(int w) const override { |
| // TODO(ckocagil): Why does this happen? |
| if (w == 0) |
| return View::GetHeightForWidth(w); |
| const gfx::Rect old_rect = render_text_->display_rect(); |
| gfx::Rect rect = old_rect; |
| rect.set_width(w - GetInsets().width()); |
| render_text_->SetDisplayRect(rect); |
| int height = render_text_->GetStringSize().height() + GetInsets().height(); |
| render_text_->SetDisplayRect(old_rect); |
| return height; |
| } |
| |
| void SetText(const std::u16string& new_contents) { |
| // Color and style the text inside |test_range| to test colors and styles. |
| const size_t range_max = new_contents.length(); |
| gfx::Range color_range = ClampRange(gfx::Range(1, 21), range_max); |
| gfx::Range bold_range = ClampRange(gfx::Range(4, 10), range_max); |
| gfx::Range italic_range = ClampRange(gfx::Range(7, 13), range_max); |
| |
| render_text_->SetText(new_contents); |
| render_text_->SetColor(SK_ColorBLACK); |
| render_text_->ApplyColor(0xFFFF0000, color_range); |
| render_text_->SetStyle(gfx::TEXT_STYLE_UNDERLINE, false); |
| render_text_->ApplyStyle(gfx::TEXT_STYLE_UNDERLINE, true, color_range); |
| render_text_->ApplyStyle(gfx::TEXT_STYLE_ITALIC, true, italic_range); |
| render_text_->ApplyWeight(gfx::Font::Weight::BOLD, bold_range); |
| InvalidateLayout(); |
| } |
| |
| void SetMaxLines(int max_lines) { |
| render_text_->SetMaxLines(max_lines); |
| render_text_->SetElideBehavior(max_lines ? gfx::ELIDE_TAIL : gfx::NO_ELIDE); |
| } |
| |
| private: |
| void OnBoundsChanged(const gfx::Rect& previous_bounds) override { |
| render_text_->SetDisplayRect(GetContentsBounds()); |
| } |
| |
| std::unique_ptr<gfx::RenderText> render_text_; |
| }; |
| |
| MultilineExample::MultilineExample() |
| : ExampleBase(GetStringUTF8(IDS_MULTILINE_SELECT_LABEL).c_str()) {} |
| |
| MultilineExample::~MultilineExample() = default; |
| |
| void MultilineExample::CreateExampleView(View* container) { |
| container->SetLayoutManager(std::make_unique<views::TableLayout>()) |
| ->AddColumn(LayoutAlignment::kStart, LayoutAlignment::kCenter, |
| TableLayout::kFixedSize, |
| TableLayout::ColumnSize::kUsePreferred, 0, 0) |
| .AddColumn(LayoutAlignment::kStretch, LayoutAlignment::kStretch, 1.0f, |
| TableLayout::ColumnSize::kFixed, 0, 0) |
| .AddRows(4, TableLayout::kFixedSize); |
| |
| const std::u16string kTestString = u"qwertyالرئيسيةasdfgh"; |
| |
| container->AddChildView( |
| std::make_unique<Label>(GetStringUTF16(IDS_MULTILINE_RENDER_TEXT_LABEL))); |
| render_text_view_ = |
| container->AddChildView(std::make_unique<RenderTextView>()); |
| render_text_view_->SetText(kTestString); |
| |
| label_checkbox_ = container->AddChildView(std::make_unique<Checkbox>( |
| GetStringUTF16(IDS_MULTILINE_LABEL), |
| base::BindRepeating( |
| [](MultilineExample* example) { |
| example->label_->SetText(example->label_checkbox_->GetChecked() |
| ? example->textfield_->GetText() |
| : std::u16string()); |
| }, |
| base::Unretained(this)))); |
| label_checkbox_->SetChecked(true); |
| label_checkbox_->SetRequestFocusOnPress(false); |
| label_ = container->AddChildView(std::make_unique<PreferredSizeLabel>()); |
| label_->SetText(kTestString); |
| label_->SetMultiLine(true); |
| label_->SetBorder(CreateSolidBorder(2, SK_ColorCYAN)); |
| |
| elision_checkbox_ = container->AddChildView(std::make_unique<Checkbox>( |
| GetStringUTF16(IDS_MULTILINE_ELIDE_LABEL), |
| base::BindRepeating( |
| [](MultilineExample* example) { |
| example->render_text_view_->SetMaxLines( |
| example->elision_checkbox_->GetChecked() ? 3 : 0); |
| }, |
| base::Unretained(this)))); |
| elision_checkbox_->SetChecked(false); |
| elision_checkbox_->SetRequestFocusOnPress(false); |
| container->AddChildView(std::make_unique<View>()); |
| |
| auto* label = container->AddChildView( |
| std::make_unique<Label>(GetStringUTF16(IDS_MULTILINE_SAMPLE_TEXT_LABEL))); |
| textfield_ = container->AddChildView(std::make_unique<Textfield>()); |
| textfield_->set_controller(this); |
| textfield_->SetText(kTestString); |
| textfield_->SetAssociatedLabel(label); |
| } |
| |
| void MultilineExample::ContentsChanged(Textfield* sender, |
| const std::u16string& new_contents) { |
| render_text_view_->SetText(new_contents); |
| if (label_checkbox_->GetChecked()) |
| label_->SetText(new_contents); |
| example_view()->InvalidateLayout(); |
| example_view()->SchedulePaint(); |
| } |
| |
| } // namespace examples |
| } // namespace views |