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