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 |