Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(184)

Unified Diff: ui/gfx/render_text_linux.cc

Issue 7511029: Implement Pango RenderText for Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: fix cursor bounds for RTL UI Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« ui/gfx/render_text_linux.h ('K') | « ui/gfx/render_text_linux.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/gfx/render_text_linux.cc
===================================================================
--- ui/gfx/render_text_linux.cc (revision 96870)
+++ ui/gfx/render_text_linux.cc (working copy)
@@ -4,17 +4,674 @@
#include "ui/gfx/render_text_linux.h"
+#include <pango/pangocairo.h>
+#include <algorithm>
+
+#include "base/logging.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/gtk_util.h"
+#include "unicode/uchar.h"
+
+#define U16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
msw 2011/08/19 23:16:59 Make these functions instead of pre-processor dire
xji 2011/08/22 23:57:28 removed. already defined in include in uchar.h
+#define U16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+// TODO(xji): massage ARGB to RGB.
+#define COLOR_16_TO_32_BIT(c) ((c)/0xff * 0xffff)
+
+// TODO(xji): index saved in upper layer are utf16 index. Pango uses utf8 index.
msw 2011/08/19 23:16:59 That's unfortunate, I believe Uniscribe is UTF16,
xji 2011/08/22 23:57:28 I looked at those Uniscibe functions you used. In
+// Since caret_pos is used internally, we could save utf8 index for caret_pos
+// to avoid conversion.
+
namespace gfx {
RenderTextLinux::RenderTextLinux()
- : RenderText() {
+ : layout_(NULL),
+ layout_line_(NULL) {
}
RenderTextLinux::~RenderTextLinux() {
+ ResetLayout();
}
RenderText* RenderText::CreateRenderText() {
return new RenderTextLinux;
}
+int RenderTextLinux::GetStringWidth() {
+ PangoLayout* layout = EnsureLayout();
+ int width, height;
+ pango_layout_get_pixel_size(layout, &width, &height);
+ return width;
+}
+
+void RenderTextLinux::Draw(Canvas* canvas) {
+ PangoLayout* layout = EnsureLayout();
+ Rect bounds(display_rect());
+
+ // Clip the canvas to the text display area.
+ CanvasSkia* canvas_skia = static_cast<CanvasSkia*>(canvas);
+ canvas_skia->ClipRectInt(
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+
+ // TODO(xji): is this Begin/EndPlatformPaint correct? How about performance?
+ cairo_t* cr = skia::BeginPlatformPaint(canvas_skia);
+ cairo_save(cr);
+ cairo_rectangle(cr, bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ cairo_clip(cr);
+
+ int text_width, text_height;
+ pango_layout_get_pixel_size(layout, &text_width, &text_height);
+ // Vertically centered.
+ int text_y = bounds.y() + ((bounds.height() - text_height) / 2);
+ int text_x = bounds.x() + GetUpdatedDisplayOffset().x();
+ cairo_move_to(cr, text_x, text_y);
+ pango_cairo_show_layout(cr, layout);
+
+ // Destructor.
+ cairo_restore(cr);
+ skia::EndPlatformPaint(canvas_skia);
+
+ // Paint cursor.
+ bounds = GetUpdatedCursorBounds();
+ if (cursor_visible() && focused() && !bounds.IsEmpty()) {
+ if (!bounds.IsEmpty())
+ canvas->DrawRectInt(kCursorColor,
+ bounds.x(),
+ bounds.y(),
+ bounds.width(),
+ bounds.height());
+ }
+}
+
+SelectionModel RenderTextLinux::FindCursorPosition(const Point& point) {
+ // TODO(xji): when points outside of text, return HOME/END position.
+ PangoLayout* layout = EnsureLayout();
+
+ if (text().empty())
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+
+ int adjusted_x =
+ point.x() - display_rect().x() - GetUpdatedDisplayOffset().x();
+ int caret_pos, trailing;
+ pango_layout_xy_to_index(layout,
+ adjusted_x * PANGO_SCALE,
+ point.y() * PANGO_SCALE,
+ &caret_pos,
+ &trailing);
+
+ size_t selection_end = caret_pos;
+ if (trailing > 0) {
+ const char* layout_text = pango_layout_get_text(layout);
+ selection_end = g_utf8_offset_to_pointer(layout_text + caret_pos, trailing)
msw 2011/08/19 23:16:59 Yuck! GTK? We're trying to remove all GTK; is ther
xji 2011/08/22 23:57:28 those from glib or glib-object are fine from oshim
+ - layout_text;
+ }
+
+ return SelectionModel(
+ Utf8IndexToUtf16Index(text(), selection_end),
+ Utf8IndexToUtf16Index(text(), caret_pos),
+ trailing > 0 ? SelectionModel::TRAILING : SelectionModel::LEADING);
+}
+
+Rect RenderTextLinux::GetCursorBounds(const SelectionModel& selection,
+ bool insert_mode) {
+ PangoLayout* layout = EnsureLayout();
+
+ PangoRectangle pos;
+ pango_layout_index_to_pos(
msw 2011/08/19 23:16:59 why not just call pango_layout_index_to_pos on sel
xji 2011/08/22 23:57:28 Done.
+ layout, Utf16IndexToUtf8Index(text(), selection.caret_pos()), &pos);
+
+ int x;
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ if (caret_placement == SelectionModel::TRAILING)
+ x = (pos.x + pos.width);
+ else
+ x = pos.x;
+
+ if (pango_layout_get_alignment(layout_) == PANGO_ALIGN_RIGHT)
msw 2011/08/19 23:16:59 Can you comment on why this is offsetting by one?
xji 2011/08/22 23:57:28 Done.
+ x -= 1;
+ x = x / PANGO_SCALE;
+ x += display_rect().x() + GetUpdatedDisplayOffset().x();
+
+ const Font& font = default_style().font;
+ int h = std::min(display_rect().height(), font.GetHeight());
msw 2011/08/19 23:16:59 Is the PangoRectangle's height inaccurate? Otherwi
xji 2011/08/22 23:57:28 Ah, yes, you are right. I can use pos.height. I di
msw 2011/08/23 08:01:01 Good. FYI someone could set a different font/size
+ Rect bounds(x, (display_rect().height() - h) / 2, 1, h);
msw 2011/08/19 23:16:59 It looks like a width of 0 is okay for rendering a
xji 2011/08/22 23:57:28 Done.
+
+ size_t position = selection.selection_end();
+ if (!insert_mode && text().length() != position)
+ AdjustBoundsForNonInsertMode(selection, pos, &bounds);
+
+ return bounds;
+}
+
+SelectionModel RenderTextLinux::GetLeftSelectionModel(
+ const SelectionModel& current, BreakType break_type) {
msw 2011/08/19 23:16:59 Put each argument on its own line if the whole sig
xji 2011/08/22 23:57:28 Done.
+ EnsureLayout();
+
+ if (break_type == LINE_BREAK)
+ return GetSelectionModelForVisualLeftmost();
+ if (break_type == CHARACTER_BREAK)
+ return GetLeftSelectionModelByGrapheme(current);
+ // TODO(xji): implementation.
+ return RenderText::GetLeftSelectionModel(current, break_type);
msw 2011/08/19 23:16:59 Sigh, word break is breaking our backs.
+}
+
+SelectionModel RenderTextLinux::GetRightSelectionModel(
+ const SelectionModel& current, BreakType break_type) {
msw 2011/08/19 23:16:59 Ditto for arguments on separate lines.
xji 2011/08/22 23:57:28 Done.
+ EnsureLayout();
+
+ if (break_type == LINE_BREAK) {
+ const char* layout_text = pango_layout_get_text(layout_);
msw 2011/08/19 23:16:59 FYI, You might find it easier to implement somethi
xji 2011/08/22 23:57:28 Done.
+ PangoDirection base_dir =
+ pango_find_base_dir(layout_text, strlen(layout_text));
msw 2011/08/19 23:16:59 Maybe add a TODO like my // TODO(msw): Implement R
xji 2011/08/22 23:57:28 I've changed it to use GetTextDirection(). it retu
+ if (base_dir == PANGO_DIRECTION_RTL ||
+ base_dir == PANGO_DIRECTION_WEAK_RTL)
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+ return SelectionModel(
+ text().length(), text().length(), SelectionModel::LEADING);
msw 2011/08/19 23:16:59 Perhaps we should discuss the merits of using text
xji 2011/08/22 23:57:28 I've changed to your implementation.
+ }
+ if (break_type == CHARACTER_BREAK)
+ return GetRightSelectionModelByGrapheme(current);
+ // TODO(xji): implementation.
+ return RenderText::GetRightSelectionModel(current, break_type);
+}
+
+size_t RenderTextLinux::GetIndexOfPreviousGrapheme(size_t position) {
+ EnsureLayout();
+ return Utf16IndexOfAdjacentGrapheme(position, PREVIOUS);
+}
+
+SelectionModel RenderTextLinux::GetSelectionModelForVisualLeftmost() const {
+ const char* layout_text = pango_layout_get_text(layout_);
msw 2011/08/19 23:16:59 Do you need to EnsureLayout();?
xji 2011/08/22 23:57:28 this one is a private function, and it is always c
msw 2011/08/23 08:01:01 I think you're okay.
+ PangoDirection base_dir =
+ pango_find_base_dir(layout_text, strlen(layout_text));
+
+ if (base_dir == PANGO_DIRECTION_RTL || base_dir == PANGO_DIRECTION_WEAK_RTL) {
+ GSList* first_visual_run = layout_line_->runs;
+ if (first_visual_run) {
+ PangoItem* item =
+ reinterpret_cast<PangoLayoutRun*>(first_visual_run->data)->item;
msw 2011/08/19 23:16:59 As far as I can tell, PangoLayoutRun != PangoItem:
xji 2011/08/22 23:57:28 |item| is assigned to PangoLayoutRun->item.
msw 2011/08/23 08:01:01 Doh, thanks for clarifying!
+ if (item->analysis.level % 2 == 0) { // LTR.
+ size_t utf16_index = Utf8IndexToUtf16Index(text(), item->offset);
+ return SelectionModel(
+ text().length(), utf16_index, SelectionModel::LEADING);
+ } else { // RTL.
+ size_t utf16_index = Utf8IndexToUtf16Index(
+ text(), item->offset + item->length);
msw 2011/08/19 23:16:59 Should this be item->length - 1? Or I guess gettin
xji 2011/08/22 23:57:28 Yes.
msw 2011/08/23 08:01:01 Function definition/declaration is all on one line
+ utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
+ return SelectionModel(
+ text().length(), utf16_index, SelectionModel::TRAILING);
msw 2011/08/19 23:16:59 Ditto for moving "text().length()," to the line ab
xji 2011/08/22 23:57:28 Done.
+ }
+ }
+ }
+ return SelectionModel(0, 0, SelectionModel::LEADING);
+}
+
+GSList* RenderTextLinux::GetRunContainsCaretPos(const SelectionModel& current,
+ CursorMovementDirection dir,
+ bool* at_boundary) const {
+ GSList* run_contains_caret_pos = NULL;
+ *at_boundary = false;
+
+ GSList* run = layout_line_->runs;
+ while (run) {
+ PangoItem* pango_item = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
xji 2011/08/22 23:57:28 ditto
+ size_t caret_pos = current.caret_pos();
+ size_t run_end_utf16_index =
+ Utf8IndexToUtf16Index(text(), pango_item->offset + pango_item->length);
+ size_t run_start_utf16_index =
+ Utf8IndexToUtf16Index(text(), pango_item->offset);
+
+ if (caret_pos >= run_start_utf16_index && caret_pos < run_end_utf16_index) {
+ run_contains_caret_pos = run;
+
+ size_t selection_end = current.selection_end();
+ SelectionModel::CaretPlacement caret_placement =
+ current.caret_placement();
+
+ if (caret_placement == SelectionModel::LEADING &&
+ caret_pos == run_start_utf16_index) {
+ *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2) ||
+ (dir == LEFT && pango_item->analysis.level % 2 == 0);
+ } else if (caret_placement == SelectionModel::TRAILING &&
+ selection_end == run_end_utf16_index) {
msw 2011/08/19 23:16:59 Why are we checking selection_end instead of caret
xji 2011/08/22 23:57:28 I am checking selecion_end == run_end, you are che
+ *at_boundary = (dir == RIGHT && pango_item->analysis.level % 2 == 0) ||
+ (dir == LEFT && pango_item->analysis.level % 2);
+ }
+ break;
+ }
+ run = run->next;
+ }
+ return run_contains_caret_pos;
+}
+
+size_t RenderTextLinux::Utf8IndexOfAdjacentGrapheme(
+ size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
msw 2011/08/19 23:16:59 Ditto for argument lines.
xji 2011/08/22 23:57:28 Done.
+ PangoLogAttr* log_attrs;
+ gint n_attrs;
+ pango_layout_get_log_attrs(layout_, &log_attrs, &n_attrs);
+
+ const char* layout_text = pango_layout_get_text(layout_);
+ const char* ch = layout_text +
+ Utf16IndexToUtf8Index(text(), utf16_index_of_current_grapheme);
+ int char_offset = static_cast<int>(g_utf8_pointer_to_offset(layout_text, ch));
msw 2011/08/19 23:16:59 Yuck, more GTK!
+
+ if (pos == PREVIOUS) {
+ if (ch > layout_text) {
+ do {
+ ch = g_utf8_find_prev_char(layout_text, ch);
msw 2011/08/19 23:16:59 Yuck, more GTK!
+ --char_offset;
+ } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
+ if (!ch)
+ ch = layout_text;
+ }
+ } else {
+ if (ch < layout_text + strlen(layout_text)) {
+ do {
+ ch = g_utf8_find_next_char(ch, NULL);
msw 2011/08/19 23:16:59 Yuck, more GTK!
+ ++char_offset;
+ } while (ch && *ch && !log_attrs[char_offset].is_cursor_position);
+ if (ch >= layout_text + strlen(layout_text))
msw 2011/08/19 23:16:59 Could |ch| be null here (noting your check above).
xji 2011/08/22 23:57:28 pango text should be null terminated. I do not thi
+ ch = layout_text + strlen(layout_text);
+ }
+ }
+
+ size_t utf8_index = static_cast<size_t>(ch - layout_text);
+ // TODO(xji): how costly those g_free is?
+ g_free(log_attrs);
msw 2011/08/19 23:16:59 Yuck, more GTK!
+ return utf8_index;
+}
+
+size_t RenderTextLinux::Utf16IndexOfAdjacentGrapheme(
+ size_t utf16_index_of_current_grapheme, RelativeLogicalPosition pos) const {
msw 2011/08/19 23:16:59 Ditto for argument lines.
xji 2011/08/22 23:57:28 Done.
+ size_t utf8_index = Utf8IndexOfAdjacentGrapheme(
+ utf16_index_of_current_grapheme, pos);
+ return Utf8IndexToUtf16Index(text(), utf8_index);
+}
+
+SelectionModel RenderTextLinux::FirstSelectionModelInsideRun(
+ const PangoItem* run) const {
+ size_t utf16_index = Utf8IndexToUtf16Index(text(), run->offset);
msw 2011/08/19 23:16:59 Out-dent the function contents two spaces.
xji 2011/08/22 23:57:28 Done.
+ return SelectionModel(Utf16IndexOfAdjacentGrapheme(utf16_index, NEXT),
msw 2011/08/19 23:16:59 Can you split this up onto two lines like you did
xji 2011/08/22 23:57:28 Done.
+ utf16_index,
+ SelectionModel::TRAILING);
+}
+
+SelectionModel RenderTextLinux::LastSelectionModelInsideRun(
+ const PangoItem* run) const {
+ size_t utf16_index = Utf8IndexToUtf16Index(
msw 2011/08/19 23:16:59 Out-dent the function contents two spaces.
xji 2011/08/22 23:57:28 Done.
+ text(), run->offset + run->length);
+ utf16_index = Utf16IndexOfAdjacentGrapheme(utf16_index, PREVIOUS);
+ return SelectionModel(utf16_index, utf16_index, SelectionModel::LEADING);
+}
+
+SelectionModel RenderTextLinux::LeftmostSelectionModelInsideRun(
+ const PangoItem* run) const {
+ if (run->analysis.level % 2 == 0)
+ return FirstSelectionModelInsideRun(run);
+ return LastSelectionModelInsideRun(run);
+}
+
+SelectionModel RenderTextLinux::RightmostSelectionModelInsideRun(
+ const PangoItem* run) const {
+ if (run->analysis.level % 2)
+ return FirstSelectionModelInsideRun(run);
+ return LastSelectionModelInsideRun(run);
+}
+
+bool RenderTextLinux::GetVisuallyAdjacentCursorPositionForEnd(
+ const SelectionModel& current,
msw 2011/08/19 23:16:59 Out-dent these argument lines two spaces.
xji 2011/08/22 23:57:28 Done.
+ CursorMovementDirection dir,
+ SelectionModel* adjacent) const {
+ size_t text_length = text().length();
+ if (current.selection_end() == text_length &&
+ current.caret_pos() == text_length &&
+ current.caret_placement() == SelectionModel::LEADING) {
+ // Visually rightmost END position.
+ if (dir == LEFT) {
+ GSList* last_run = GetLastRun();
+ if (last_run) {
+ *adjacent = RightmostSelectionModelInsideRun(
msw 2011/08/19 23:16:59 Does this work in a RTL context with plain RTL tex
xji 2011/08/22 23:57:28 it works. but this function is no longer needed.
+ reinterpret_cast<PangoLayoutRun*>(last_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ }
+ }
+ adjacent->set_selection_start(adjacent->selection_end());
+ return true;
+ }
+ return false;
+}
+
+// Assume caret_pos in |current| is n, 'l' represents leading in
+// caret_placement and 't' represents trailing in caret_placement. Following
+// is the calculation from (caret_pos, caret_placement) in |current| to
+// (caret_pos, caret_placement, selection_end) when moving cursor left by
msw 2011/08/19 23:16:59 While I can't thank you enough to writing the psue
xji 2011/08/22 23:57:28 Done.
+// one grapheme (for simplilcity, assume each grapheme is one character).
+// If n is in LTR run,
+// (n, t) ---> (n, l, n)
+// (n, l) ---> (n-1, l, n-1) if n is inside run (not at boundary).
+// (n, l) ---> goto across run case if n is at run boundary;
+// If n is in RTL run,
+// (n, l) --> (n, t, n+1);
+// (n, t) --> (n+1, t n+2) if n is inside run;
+// (n, t) --> goto across run case if n is at run boundar;
msw 2011/08/19 23:16:59 boundar*y*
xji 2011/08/22 23:57:28 Done.
+// If n is at run boudary, get its visually left run,
msw 2011/08/19 23:16:59 bou*n*dary
xji 2011/08/22 23:57:28 Done.
+// if left run is LTR run,
+// (n, t) --> (left run's end, l, left run's end);
+// If rght run is RTL run,
+// (n, t) --> (left run's begin, t, left run's begin + 1);
+//
+// TODO(xji): duplication with GetRightSelectionModelByGrapheme.
+SelectionModel RenderTextLinux::GetLeftSelectionModelByGrapheme(
+ const SelectionModel& current) const {
+ SelectionModel left(current);
+ if (GetVisuallyAdjacentCursorPositionForEnd(current, LEFT, &left))
+ return left;
+
+ bool at_run_boundary = false;
+ GSList* run = GetRunContainsCaretPos(current, LEFT, &at_run_boundary);
+ if (run == NULL) {
+ left.set_selection_start(left.selection_end());
+ return left;
+ }
+
+ PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ if (box->analysis.level % 2 == 0) { // LTR box.
+ if (current.caret_placement() == SelectionModel::TRAILING) {
+ left.set_caret_placement(SelectionModel::LEADING);
+ left.set_selection_end(left.caret_pos());
+ } else { // caret_placement == TRAILING.
+ if (at_run_boundary) {
+ GSList* prev_run = GetPreviousRun(run);
+ if (prev_run) {
+ return RightmostSelectionModelInsideRun(
+ reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ }
+ } else { // !at_run_boundary.
+ size_t prev = Utf16IndexOfAdjacentGrapheme(left.caret_pos(), PREVIOUS);
+ left.set_caret_pos(prev);
+ left.set_selection_end(prev);
+ }
+ }
+ } else { // RTL box.
+ if (current.caret_placement() == SelectionModel::LEADING) {
+ left.set_caret_placement(SelectionModel::TRAILING);
+ left.set_selection_end(
+ Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
+ } else { // caret_placement == LEADING
+ if (at_run_boundary) {
+ GSList* prev_run = GetPreviousRun(run);
+ if (prev_run) {
+ return RightmostSelectionModelInsideRun(
+ reinterpret_cast<PangoLayoutRun*>(prev_run->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ }
+ } else { // !at_run_boundary
+ // current.seletion_end - current.caret_pos
+ // == NumOfCharsInGrapheme(caret_pos);
+ left.set_caret_pos(current.selection_end());
+ left.set_selection_end(
+ Utf16IndexOfAdjacentGrapheme(left.caret_pos(), NEXT));
+ }
+ }
+ }
+
+ left.set_selection_start(left.selection_end());
+ return left;
+}
+
+// Assume caret_pos in |current| is n, 'l' represents leading in
msw 2011/08/19 23:16:59 Ditto on triplet order and line endings.
xji 2011/08/22 23:57:28 Done.
+// caret_placement and 't' represents trailing in caret_placement. Following
+// is the calculation from (caret_pos, caret_placement) in |current| to
+// (caret_pos, caret_placement, selection_end) when moving cursor right by
+// one grapheme (for simplilcity, assume each grapheme is one character).
+// If n is in LTR run,
+// (n, l) ---> (n, t, n+1)
+// (n, t) ---> (n+1, t, n+2) if n is inside run (not at boundary).
+// (n, t) ---> goto across run case if n is at run boundary;
+// If n is in RTL run,
+// (n, t) --> (n, l, n);
+// (n, l) --> (n-1, l, n-1) if n is inside run;
+// (n, l) --> goto across run case if n is at run boundar;
msw 2011/08/19 23:16:59 boundar*y*
xji 2011/08/22 23:57:28 Done.
+// If n is at run boudary, get its visually right run,
msw 2011/08/19 23:16:59 bou*n*dary
xji 2011/08/22 23:57:28 Done.
+// if right run is LTR run,
+// (n, t) --> (right run's begin, t, right run's begin + 1);
+// If rght run is RTL run,
+// (n, t) --> (right run's end, l, right run's end);
+SelectionModel RenderTextLinux::GetRightSelectionModelByGrapheme(
+ const SelectionModel& current) const {
+ SelectionModel right(current);
+ if (GetVisuallyAdjacentCursorPositionForEnd(current, RIGHT, &right))
+ return right;
+
+ bool at_run_boundary = false;
+ GSList* run = GetRunContainsCaretPos(current, RIGHT, &at_run_boundary);
+ if (run == NULL) {
+ right.set_selection_start(right.selection_end());
+ return right;
+ }
+
+ PangoItem* box = reinterpret_cast<PangoLayoutRun*>(run->data)->item;
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ if (box->analysis.level % 2 == 0) { // LTR box.
+ if (current.caret_placement() == SelectionModel::LEADING) {
+ right.set_caret_placement(SelectionModel::TRAILING);
+ right.set_selection_end(
+ Utf16IndexOfAdjacentGrapheme(current.caret_pos(), NEXT));
+ } else { // caret_placement == TRAILING.
+ if (at_run_boundary) {
+ if (run->next) {
+ return LeftmostSelectionModelInsideRun(
+ reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ }
+ } else { // !at_run_boundary.
+ // current.seletion_end - current.caret_pos
+ // == NumOfCharsInGrapheme(caret_pos);
+ right.set_caret_pos(current.selection_end());
+ right.set_selection_end(
+ Utf16IndexOfAdjacentGrapheme(right.caret_pos(), NEXT));
+ }
+ }
+ } else { // RTL box.
+ if (current.caret_placement() == SelectionModel::TRAILING) {
+ right.set_caret_placement(SelectionModel::LEADING);
+ right.set_selection_end(right.caret_pos());
+ } else { // caret_placement == LEADING
+ if (at_run_boundary) {
+ if (run->next) {
+ return LeftmostSelectionModelInsideRun(
+ reinterpret_cast<PangoLayoutRun*>(run->next->data)->item);
msw 2011/08/19 23:16:59 Same question about PangoLayoutRun and PangoItem c
+ }
+ } else { // !at_run_boundary
+ // current.seletion_end - current.caret_pos
+ // == NumOfCharsInGrapheme(caret_pos);
+ size_t prev = Utf16IndexOfAdjacentGrapheme(right.caret_pos(), PREVIOUS);
+ right.set_caret_pos(prev);
+ right.set_selection_end(prev);
+ }
+ }
+ }
+
+ right.set_selection_start(right.selection_end());
+ return right;
+}
+
+PangoLayout* RenderTextLinux::EnsureLayout() {
+ if (layout_ == NULL) {
+ CanvasSkia canvas(display_rect().width(), display_rect().height(), false);
+ // TODO(xji): how costly is the Begin/EndPlatformPaint? Can I get cairo
+ // context in another way?
+ cairo_t* cr = skia::BeginPlatformPaint(&canvas);
+
+ layout_ = pango_cairo_create_layout(cr);
+ SetupPangoLayout(layout_, text(), default_style().font,
+ display_rect().width(), GetTextDirection(),
+ CanvasSkia::DefaultCanvasTextAlignment());
+ pango_layout_set_height(layout_, display_rect().height() * PANGO_SCALE);
+ SetupPangoAttributes(layout_);
+
+ skia::EndPlatformPaint(&canvas);
+
+ layout_line_ = pango_layout_get_line_readonly(layout_, 0);
+ }
+ return layout_;
+}
+
+void RenderTextLinux::ResetLayout() {
+ // set_cached_bounds_and_offset_valid(false) is done in RenderText for every
+ // operation that trigger ResetLayout().
msw 2011/08/19 23:16:59 Yeah, we might want to make a more general shared
+ if (layout_) {
+ g_object_unref(layout_);
msw 2011/08/19 23:16:59 Yuck, more GTK!
+ layout_ = NULL;
+ }
+ if (layout_line_) {
+ // TODO(xji): do I need ref/unref?
+ // pango_layout_line_unref(layout_line_);
msw 2011/08/19 23:16:59 Sounds like you should ref and unref it to retain
xji 2011/08/22 23:57:28 Done.
+ layout_line_ = NULL;
+ }
+}
+
+void RenderTextLinux::SetupPangoAttributes(PangoLayout* layout) {
+ PangoAttrList* attrs = pango_attr_list_new();
+ // Set selection background color.
+ SkColor selection_color =
+ focused() ? kFocusedSelectionColor : kUnfocusedSelectionColor;
+ PangoAttribute* pango_attr = pango_attr_background_new(
+ COLOR_16_TO_32_BIT(SkColorGetR(selection_color)),
msw 2011/08/19 23:16:59 Can you use SkColorGetR/G/B or similar instead her
xji 2011/08/22 23:57:28 I am using SKColorGetR/G/B, but I need to convert
+ COLOR_16_TO_32_BIT(SkColorGetG(selection_color)),
+ COLOR_16_TO_32_BIT(SkColorGetB(selection_color)));
+ AppendPangoAttribute(MinOfSelection(), MaxOfSelection(),
+ pango_attr, attrs);
+
+ StyleRanges ranges_of_style(style_ranges());
+ ApplyCompositionAndSelectionStyles(&ranges_of_style);
+
+ for (StyleRanges::const_iterator i = ranges_of_style.begin();
+ i < ranges_of_style.end(); ++i) {
+ const Font& font = !i->underline ? i->font :
+ i->font.DeriveFont(0, i->font.GetStyle() | Font::UNDERLINED);
+ pango_attr = pango_attr_font_desc_new(
+ reinterpret_cast<PangoFontDescription*>(font.GetNativeFont()));
+ AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
+
+ SkColor foreground = i->foreground;
+ pango_attr = pango_attr_foreground_new(
+ COLOR_16_TO_32_BIT(SkColorGetR(foreground)),
+ COLOR_16_TO_32_BIT(SkColorGetG(foreground)),
+ COLOR_16_TO_32_BIT(SkColorGetB(foreground)));
+ AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
+
+ if (i->strike) {
+ pango_attr = pango_attr_strikethrough_new(true);
+ AppendPangoAttribute(i->range.start(), i->range.end(), pango_attr, attrs);
+ }
+ }
+
+ pango_layout_set_attributes(layout, attrs);
+ pango_attr_list_unref(attrs);
+}
+
+void RenderTextLinux::AppendPangoAttribute(size_t start,
+ size_t end,
+ PangoAttribute* pango_attr,
+ PangoAttrList* attrs) {
+ pango_attr->start_index = Utf16IndexToUtf8Index(text(), start);
+ pango_attr->end_index = Utf16IndexToUtf8Index(text(), end);
+ pango_attr_list_insert(attrs, pango_attr);
+}
+
+GSList* RenderTextLinux::GetPreviousRun(GSList* run) const {
msw 2011/08/19 23:16:59 lol at singly linked lists :)
+ GSList* current = layout_line_->runs;
+ GSList* prev = NULL;
+ while (current) {
+ if (current == run)
+ return prev;
+ prev = current;
+ current = current->next;
+ }
+ return NULL;
+}
+
+GSList* RenderTextLinux::GetLastRun() const {
+ GSList* current = layout_line_->runs;
msw 2011/08/19 23:16:59 i'm done loling, now i'm sighing :(
+ while (current && current->next) {
+ current = current->next;
+ }
+ return current;
+}
+
+size_t RenderTextLinux::Utf16IndexToUtf8Index(const string16& text,
+ size_t index) const {
+ int utf8_index = 0;
+ for (size_t i = 0; i < index; ++i) {
+ if (text[i] < 0x80) {
+ ++utf8_index;
+ } else if (text[i] < 0x800) {
+ utf8_index += 2;
+ } else {
+ if ((U16_IS_LEAD(text[i]))) {
+ if (i + 1 < index) {
+ if (U16_IS_TRAIL(text[i + 1])) {
+ // A surrogate pair.
+ utf8_index += 4;
+ ++i;
+ continue;
+ }
+ } else if (i + 1 < text.length() && U16_IS_TRAIL(text[i + 1])) {
+ // A split surrogate pair, stop at the leading.
+ break;
+ }
+ }
+ utf8_index += 3;
+ }
+ }
+ return utf8_index;
+}
+
+
+size_t RenderTextLinux::Utf8IndexToUtf16Index(const string16& text,
+ size_t index) const {
+ // TODO(xji): DUP with utf16IndexToUtf8Index.
+ size_t utf8_index = 0;
+ size_t utf16_index = 0;
+ for (; utf16_index < text.length(); ++utf16_index) {
+ if (text[utf16_index] < 0x80) {
+ ++utf8_index;
+ } else if (text[utf16_index] < 0x800) {
+ utf8_index += 2;
+ } else {
+ if ((U16_IS_LEAD(text[utf16_index]))) {
+ if (utf16_index + 1 < text.length()) {
+ if (U16_IS_TRAIL(text[utf16_index + 1])) {
+ // A surrogate pair.
+ utf8_index += 4;
+ if (utf8_index >= index)
+ break;
+ ++utf16_index;
+ continue;
+ }
+ }
+ }
+ utf8_index += 3;
+ }
+ if (utf8_index > index)
+ break;
+ }
+ return utf16_index;
+}
+
+void RenderTextLinux::AdjustBoundsForNonInsertMode(
msw 2011/08/19 23:16:59 I think this function should be in-lined in GetCur
xji 2011/08/22 23:57:28 Done.
+ const SelectionModel& selection,
+ const PangoRectangle& pos,
+ Rect* bounds) const {
+ SelectionModel::CaretPlacement caret_placement = selection.caret_placement();
+ if (caret_placement == SelectionModel::LEADING) {
+ if (pos.width > 0) {
+ bounds->set_width(pos.width);
+ } else {
+ bounds->set_x(pos.x + pos.width);
+ bounds->set_width(-pos.width);
+ }
+ } else {
+ // TODO(xji): what is the expected behavior? For example, "abcFED",
+ // when |selection| == SelectionModel(3, 2, TRAILING), in insert-mode,
+ // cursor is at right of 'c'. In non-insert-mode, is the bounds around 'D'?
msw 2011/08/19 23:16:59 I'd say yes, but I'm not sure if you're asking me
xji 2011/08/22 23:57:28 I implement as this. we can always change it if it
+ }
+}
+
} // namespace gfx
« ui/gfx/render_text_linux.h ('K') | « ui/gfx/render_text_linux.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698