1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/views/examples/multiline_example.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <utility>
12 
13 #include "base/macros.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "ui/events/event.h"
16 #include "ui/gfx/render_text.h"
17 #include "ui/views/background.h"
18 #include "ui/views/border.h"
19 #include "ui/views/controls/button/checkbox.h"
20 #include "ui/views/controls/label.h"
21 #include "ui/views/controls/textfield/textfield.h"
22 #include "ui/views/layout/grid_layout.h"
23 #include "ui/views/view.h"
24 
25 using base::ASCIIToUTF16;
26 
27 namespace views {
28 namespace examples {
29 
30 namespace {
31 
ClampRange(gfx::Range range,uint32_t max)32 gfx::Range ClampRange(gfx::Range range, uint32_t max) {
33   range.set_start(std::min(range.start(), max));
34   range.set_end(std::min(range.end(), max));
35   return range;
36 }
37 
38 // A Label with a clamped preferred width to demonstrate wrapping.
39 class PreferredSizeLabel : public Label {
40  public:
41   PreferredSizeLabel() = default;
42   ~PreferredSizeLabel() override = default;
43 
44   // Label:
CalculatePreferredSize() const45   gfx::Size CalculatePreferredSize() const override {
46     return gfx::Size(50, Label::CalculatePreferredSize().height());
47   }
48 
49  private:
50   DISALLOW_COPY_AND_ASSIGN(PreferredSizeLabel);
51 };
52 
53 }  // namespace
54 
55 // A simple View that hosts a RenderText object.
56 class MultilineExample::RenderTextView : public View {
57  public:
RenderTextView()58   RenderTextView() : render_text_(gfx::RenderText::CreateRenderText()) {
59     render_text_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
60     render_text_->SetColor(SK_ColorBLACK);
61     render_text_->SetMultiline(true);
62     SetBorder(CreateSolidBorder(2, SK_ColorGRAY));
63   }
64 
OnPaint(gfx::Canvas * canvas)65   void OnPaint(gfx::Canvas* canvas) override {
66     View::OnPaint(canvas);
67     render_text_->Draw(canvas);
68   }
69 
CalculatePreferredSize() const70   gfx::Size CalculatePreferredSize() const override {
71     // Turn off multiline mode to get the single-line text size, which is the
72     // preferred size for this view.
73     render_text_->SetMultiline(false);
74     gfx::Size size(render_text_->GetContentWidth(),
75                    render_text_->GetStringSize().height());
76     size.Enlarge(GetInsets().width(), GetInsets().height());
77     render_text_->SetMultiline(true);
78     return size;
79   }
80 
GetHeightForWidth(int w) const81   int GetHeightForWidth(int w) const override {
82     // TODO(ckocagil): Why does this happen?
83     if (w == 0)
84       return View::GetHeightForWidth(w);
85     const gfx::Rect old_rect = render_text_->display_rect();
86     gfx::Rect rect = old_rect;
87     rect.set_width(w - GetInsets().width());
88     render_text_->SetDisplayRect(rect);
89     int height = render_text_->GetStringSize().height() + GetInsets().height();
90     render_text_->SetDisplayRect(old_rect);
91     return height;
92   }
93 
SetText(const base::string16 & new_contents)94   void SetText(const base::string16& new_contents) {
95     // Color and style the text inside |test_range| to test colors and styles.
96     const size_t range_max = new_contents.length();
97     gfx::Range color_range = ClampRange(gfx::Range(1, 21), range_max);
98     gfx::Range bold_range = ClampRange(gfx::Range(4, 10), range_max);
99     gfx::Range italic_range = ClampRange(gfx::Range(7, 13), range_max);
100 
101     render_text_->SetText(new_contents);
102     render_text_->SetColor(SK_ColorBLACK);
103     render_text_->ApplyColor(0xFFFF0000, color_range);
104     render_text_->SetStyle(gfx::TEXT_STYLE_UNDERLINE, false);
105     render_text_->ApplyStyle(gfx::TEXT_STYLE_UNDERLINE, true, color_range);
106     render_text_->ApplyStyle(gfx::TEXT_STYLE_ITALIC, true, italic_range);
107     render_text_->ApplyWeight(gfx::Font::Weight::BOLD, bold_range);
108     InvalidateLayout();
109   }
110 
SetMaxLines(int max_lines)111   void SetMaxLines(int max_lines) {
112     render_text_->SetMaxLines(max_lines);
113     render_text_->SetElideBehavior(max_lines ? gfx::ELIDE_TAIL : gfx::NO_ELIDE);
114   }
115 
116  private:
OnBoundsChanged(const gfx::Rect & previous_bounds)117   void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
118     render_text_->SetDisplayRect(GetContentsBounds());
119   }
120 
121   std::unique_ptr<gfx::RenderText> render_text_;
122 
123   DISALLOW_COPY_AND_ASSIGN(RenderTextView);
124 };
125 
MultilineExample()126 MultilineExample::MultilineExample() : ExampleBase("Multiline RenderText") {}
127 
128 MultilineExample::~MultilineExample() = default;
129 
CreateExampleView(View * container)130 void MultilineExample::CreateExampleView(View* container) {
131   const base::string16 kTestString = base::WideToUTF16(
132       L"qwerty"
133       L"\x627\x644\x631\x626\x64A\x633\x64A\x629"
134       L"asdfgh");
135 
136   auto render_text_view = std::make_unique<RenderTextView>();
137   render_text_view->SetText(kTestString);
138 
139   auto label = std::make_unique<PreferredSizeLabel>();
140   label->SetText(kTestString);
141   label->SetMultiLine(true);
142   label->SetBorder(CreateSolidBorder(2, SK_ColorCYAN));
143 
144   auto label_checkbox =
145       std::make_unique<Checkbox>(ASCIIToUTF16("views::Label:"), this);
146   label_checkbox->SetChecked(true);
147   label_checkbox->set_request_focus_on_press(false);
148 
149   auto elision_checkbox =
150       std::make_unique<Checkbox>(ASCIIToUTF16("elide text?"), this);
151   elision_checkbox->SetChecked(false);
152   elision_checkbox->set_request_focus_on_press(false);
153 
154   auto textfield = std::make_unique<Textfield>();
155   textfield->set_controller(this);
156   textfield->SetText(kTestString);
157 
158   GridLayout* layout =
159       container->SetLayoutManager(std::make_unique<views::GridLayout>());
160 
161   ColumnSet* column_set = layout->AddColumnSet(0);
162   column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0.0f,
163                         GridLayout::USE_PREF, 0, 0);
164   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1.0f,
165                         GridLayout::FIXED, 0, 0);
166 
167   layout->StartRow(0, 0);
168   layout->AddView(std::make_unique<Label>(ASCIIToUTF16("gfx::RenderText:")));
169   render_text_view_ = layout->AddView(std::move(render_text_view));
170 
171   layout->StartRow(0, 0);
172   label_checkbox_ = layout->AddView(std::move(label_checkbox));
173   label_ = layout->AddView(std::move(label));
174 
175   layout->StartRow(0, 0);
176   elision_checkbox_ = layout->AddView(std::move(elision_checkbox));
177 
178   layout->StartRow(0, 0);
179   layout->AddView(std::make_unique<Label>(ASCIIToUTF16("Sample Text:")));
180   textfield_ = layout->AddView(std::move(textfield));
181 }
182 
ContentsChanged(Textfield * sender,const base::string16 & new_contents)183 void MultilineExample::ContentsChanged(Textfield* sender,
184                                        const base::string16& new_contents) {
185   render_text_view_->SetText(new_contents);
186   if (label_checkbox_->GetChecked())
187     label_->SetText(new_contents);
188   example_view()->InvalidateLayout();
189   example_view()->SchedulePaint();
190 }
191 
ButtonPressed(Button * sender,const ui::Event & event)192 void MultilineExample::ButtonPressed(Button* sender, const ui::Event& event) {
193   if (sender == label_checkbox_) {
194     label_->SetText(label_checkbox_->GetChecked() ? textfield_->GetText()
195                                                   : base::string16());
196   } else if (sender == elision_checkbox_) {
197     render_text_view_->SetMaxLines(elision_checkbox_->GetChecked() ? 3 : 0);
198   }
199   example_view()->InvalidateLayout();
200   example_view()->SchedulePaint();
201 }
202 
203 }  // namespace examples
204 }  // namespace views
205