OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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/gfx/render_text_win.h" |
| 6 |
| 7 // TODO(msw): Remove headers we're not using after Uniscribe impl... |
| 8 #include "base/i18n/break_iterator.h" |
| 9 #include "base/i18n/rtl.h" |
| 10 #include "base/logging.h" |
| 11 #include "ui/gfx/canvas.h" |
| 12 #include "ui/gfx/canvas_skia.h" |
| 13 |
| 14 namespace { |
| 15 |
| 16 // TODO(msw): Remove this and use Uniscribe rendering. |
| 17 // Compute the windows flags necessary to implement the provided text Canvas |
| 18 // flags. |
| 19 int ComputeFormatFlags(int flags, const string16& text) { |
| 20 // Setting the text alignment explicitly in case it hasn't already been set. |
| 21 // This will make sure that we don't align text to the left on RTL locales |
| 22 // just because no alignment flag was passed to DrawStringInt(). |
| 23 if (!(flags & (gfx::Canvas::TEXT_ALIGN_CENTER | |
| 24 gfx::Canvas::TEXT_ALIGN_RIGHT | |
| 25 gfx::Canvas::TEXT_ALIGN_LEFT))) { |
| 26 flags |= gfx::CanvasSkia::DefaultCanvasTextAlignment(); |
| 27 } |
| 28 |
| 29 // horizontal alignment |
| 30 int f = 0; |
| 31 if (flags & gfx::Canvas::TEXT_ALIGN_CENTER) |
| 32 f |= DT_CENTER; |
| 33 else if (flags & gfx::Canvas::TEXT_ALIGN_RIGHT) |
| 34 f |= DT_RIGHT; |
| 35 else |
| 36 f |= DT_LEFT; |
| 37 |
| 38 // vertical alignment |
| 39 if (flags & gfx::Canvas::TEXT_VALIGN_TOP) |
| 40 f |= DT_TOP; |
| 41 else if (flags & gfx::Canvas::TEXT_VALIGN_BOTTOM) |
| 42 f |= DT_BOTTOM; |
| 43 else |
| 44 f |= DT_VCENTER; |
| 45 |
| 46 if (flags & gfx::Canvas::MULTI_LINE) { |
| 47 f |= DT_WORDBREAK; |
| 48 if (flags & gfx::Canvas::CHARACTER_BREAK) |
| 49 f |= DT_EDITCONTROL; // Turns on character breaking (not documented) |
| 50 else if (!(flags & gfx::Canvas::NO_ELLIPSIS)) |
| 51 f |= DT_WORD_ELLIPSIS; |
| 52 } else { |
| 53 f |= DT_SINGLELINE; |
| 54 } |
| 55 |
| 56 if (flags & gfx::Canvas::HIDE_PREFIX) |
| 57 f |= DT_HIDEPREFIX; |
| 58 else if ((flags & gfx::Canvas::SHOW_PREFIX) == 0) |
| 59 f |= DT_NOPREFIX; |
| 60 |
| 61 if (!(flags & gfx::Canvas::NO_ELLIPSIS)) |
| 62 f |= DT_END_ELLIPSIS; |
| 63 |
| 64 // In order to make sure RTL/BiDi strings are rendered correctly, we must |
| 65 // pass the flag DT_RTLREADING to DrawText (when the locale's language is |
| 66 // a right-to-left language) so that Windows does the right thing. |
| 67 // |
| 68 // In addition to correctly displaying text containing both RTL and LTR |
| 69 // elements (for example, a string containing a telephone number within a |
| 70 // sentence in Hebrew, or a sentence in Hebrew that contains a word in |
| 71 // English) this flag also makes sure that if there is not enough space to |
| 72 // display the entire string, the ellipsis is displayed on the left hand side |
| 73 // of the truncated string and not on the right hand side. |
| 74 // |
| 75 // We make a distinction between Chrome UI strings and text coming from a web |
| 76 // page. |
| 77 // |
| 78 // For text coming from a web page we determine the alignment based on the |
| 79 // first character with strong directionality. If the directionality of the |
| 80 // first character with strong directionality in the text is LTR, the |
| 81 // alignment is set to DT_LEFT, and the directionality should not be set as |
| 82 // DT_RTLREADING. |
| 83 // |
| 84 // This heuristic doesn't work for Chrome UI strings since even in RTL |
| 85 // locales, some of those might start with English text but we know they're |
| 86 // localized so we always want them to be right aligned, and their |
| 87 // directionality should be set as DT_RTLREADING. |
| 88 // |
| 89 // Caveat: If the string is purely LTR, don't set DTL_RTLREADING since when |
| 90 // the flag is set, LRE-PDF don't have the desired effect of rendering |
| 91 // multiline English-only text as LTR. |
| 92 // |
| 93 // Note that if the caller is explicitly requesting displaying the text |
| 94 // using RTL directionality then we respect that and pass DT_RTLREADING to |
| 95 // ::DrawText even if the locale is LTR. |
| 96 if ((flags & gfx::Canvas::FORCE_RTL_DIRECTIONALITY) || |
| 97 (base::i18n::IsRTL() && |
| 98 (f & DT_RIGHT) && base::i18n::StringContainsStrongRTLChars(text))) { |
| 99 f |= DT_RTLREADING; |
| 100 } |
| 101 |
| 102 return f; |
| 103 } |
| 104 |
| 105 // TODO(msw): Use Uniscribe. |
| 106 // We make sure that LTR text we draw in an RTL context is modified |
| 107 // appropriately to make sure it maintains it LTR orientation. |
| 108 void DoDrawText(HDC hdc, |
| 109 const string16& text, |
| 110 RECT* text_bounds, |
| 111 int flags) { |
| 112 // Only adjust string directionality if both of the following are true: |
| 113 // 1. The current locale is RTL. |
| 114 // 2. The string itself has RTL directionality. |
| 115 const wchar_t* string_ptr = text.c_str(); |
| 116 int string_size = static_cast<int>(text.length()); |
| 117 |
| 118 string16 localized_text; |
| 119 if (flags & DT_RTLREADING) { |
| 120 localized_text = text; |
| 121 base::i18n::AdjustStringForLocaleDirection(&localized_text); |
| 122 string_ptr = localized_text.c_str(); |
| 123 string_size = static_cast<int>(localized_text.length()); |
| 124 } |
| 125 |
| 126 DrawText(hdc, string_ptr, string_size, text_bounds, flags); |
| 127 } |
| 128 |
| 129 } // namespace |
| 130 |
| 131 namespace gfx { |
| 132 |
| 133 RenderTextWin::RenderTextWin() |
| 134 : RenderText() { |
| 135 } |
| 136 |
| 137 RenderTextWin::RenderTextWin(const string16& text, |
| 138 const gfx::Font& font, |
| 139 const SkColor& color, |
| 140 const gfx::Rect display_rect, |
| 141 int flags) |
| 142 : RenderText(text, font, color, display_rect, flags) { |
| 143 } |
| 144 |
| 145 RenderTextWin::~RenderTextWin() { |
| 146 } |
| 147 |
| 148 void RenderTextWin::Draw(HDC drawing_context) const { |
| 149 // TODO(msw): Use Uniscribe. |
| 150 gfx::Rect r = get_display_rect(); |
| 151 RECT text_bounds = { r.x(), r.y(), r.x() + r.width(), r.y() + r.height() }; |
| 152 int flags = ComputeFormatFlags(get_flags(), text()); |
| 153 DoDrawText(drawing_context, text(), &text_bounds, flags); |
| 154 } |
| 155 |
| 156 size_t RenderTextWin::FindCursorPosition(const gfx::Point& point) const { |
| 157 // TODO(msw): Use Uniscribe. |
| 158 const gfx::Font& font = GetStyleRanges()[0]->font; |
| 159 // TODO(msw): gfx::Insets insets = GetInsets(); |
| 160 int left = 0; |
| 161 int left_pos = 0; |
| 162 int right = font.GetStringWidth(text()); |
| 163 int right_pos = text().length(); |
| 164 |
| 165 int x = point.x(); // TODO(msw): - insets.left() - text_offset_; |
| 166 if (x <= left) return left_pos; |
| 167 if (x >= right) return right_pos; |
| 168 // binary searching the cursor position. |
| 169 // TODO(oshima): use the center of character instead of edge. |
| 170 // Binary search may not work for language like arabic. |
| 171 while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) { |
| 172 int pivot_pos = left_pos + (right_pos - left_pos) / 2; |
| 173 int pivot = font.GetStringWidth(text().substr(0, pivot_pos)); |
| 174 if (pivot < x) { |
| 175 left = pivot; |
| 176 left_pos = pivot_pos; |
| 177 } else if (pivot == x) { |
| 178 return pivot_pos; |
| 179 } else { |
| 180 right = pivot; |
| 181 right_pos = pivot_pos; |
| 182 } |
| 183 } |
| 184 return left_pos; |
| 185 } |
| 186 |
| 187 std::vector<gfx::Rect> RenderTextWin::GetSubstringBounds( |
| 188 const ui::Range& range) const { |
| 189 // TODO(msw): Use Uniscribe. |
| 190 size_t start = range.GetMin(); |
| 191 size_t end = range.GetMax(); |
| 192 const gfx::Font& font = GetStyleRanges()[0]->font; |
| 193 int start_x = font.GetStringWidth(text().substr(0, start)); |
| 194 int end_x = font.GetStringWidth(text().substr(0, end)); |
| 195 std::vector<gfx::Rect> bounds; |
| 196 bounds.push_back(gfx::Rect(start_x, 0, end_x - start_x, font.GetHeight())); |
| 197 return bounds; |
| 198 } |
| 199 |
| 200 size_t RenderTextWin::GetLeftCursorPosition(size_t position, |
| 201 bool move_by_word) const { |
| 202 // TODO(msw): Use Uniscribe. |
| 203 if (!move_by_word) |
| 204 return position == 0? position : position - 1; |
| 205 // Notes: We always iterate words from the begining. |
| 206 // This is probably fast enough for our usage, but we may |
| 207 // want to modify WordIterator so that it can start from the |
| 208 // middle of string and advance backwards. |
| 209 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 210 bool success = iter.Init(); |
| 211 DCHECK(success); |
| 212 if (!success) |
| 213 return position; |
| 214 int last = 0; |
| 215 while (iter.Advance()) { |
| 216 if (iter.IsWord()) { |
| 217 size_t begin = iter.pos() - iter.GetString().length(); |
| 218 if (begin == position) { |
| 219 // The cursor is at the beginning of a word. |
| 220 // Move to previous word. |
| 221 break; |
| 222 } else if(iter.pos() >= position) { |
| 223 // The cursor is in the middle or at the end of a word. |
| 224 // Move to the top of current word. |
| 225 last = begin; |
| 226 break; |
| 227 } else { |
| 228 last = iter.pos() - iter.GetString().length(); |
| 229 } |
| 230 } |
| 231 } |
| 232 |
| 233 return last; |
| 234 } |
| 235 |
| 236 size_t RenderTextWin::GetRightCursorPosition(size_t position, |
| 237 bool move_by_word) const { |
| 238 // TODO(msw): Use Uniscribe. |
| 239 if (!move_by_word) |
| 240 return std::min(position + 1, text().length()); |
| 241 base::i18n::BreakIterator iter(text(), base::i18n::BreakIterator::BREAK_WORD); |
| 242 bool success = iter.Init(); |
| 243 DCHECK(success); |
| 244 if (!success) |
| 245 return position; |
| 246 size_t pos = 0; |
| 247 while (iter.Advance()) { |
| 248 pos = iter.pos(); |
| 249 if (iter.IsWord() && pos > position) { |
| 250 break; |
| 251 } |
| 252 } |
| 253 return pos; |
| 254 } |
| 255 |
| 256 gfx::Rect RenderTextWin::GetCursorBounds(size_t position, |
| 257 bool insert_mode) const { |
| 258 // TODO(msw): Use Uniscribe. |
| 259 NOTIMPLEMENTED(); |
| 260 return gfx::Rect(); |
| 261 } |
| 262 |
| 263 RenderText* RenderText::CreateRenderText() { |
| 264 return new RenderTextWin; |
| 265 } |
| 266 |
| 267 RenderText* RenderText::CreateRenderText(const string16& text, |
| 268 const gfx::Font& font, |
| 269 const SkColor& color, |
| 270 const gfx::Rect& display_rect, |
| 271 int flags) { |
| 272 return new RenderTextWin(text, font, color, display_rect, flags); |
| 273 } |
| 274 |
| 275 } // namespace gfx |
OLD | NEW |