diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-22 05:36:33 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-22 05:36:33 +0000 |
commit | 5e1ed6dcace94125b65e1061e2219672fda9ba1f (patch) | |
tree | fdf061bfe23ee7299675ccba30001805da3fe61f /views | |
parent | f34350d99b4b4ffe73f68594649a8f2c61e07062 (diff) | |
download | chromium_src-5e1ed6dcace94125b65e1061e2219672fda9ba1f.zip chromium_src-5e1ed6dcace94125b65e1061e2219672fda9ba1f.tar.gz chromium_src-5e1ed6dcace94125b65e1061e2219672fda9ba1f.tar.bz2 |
Add multiline support to views::TextField.
- Add a GtkViewsTextView derived from GtkTextView and supports border and
info text when empty;
- Update NativeTextfieldGtk to use GtkViewsTextView for multiline text;
- Remove the multiline suppression in BugReportView;
BUG=chromium-os:3426
TEST=Verify feedback dialog on ChromeOS uses multiline text edit for description field.
Review URL: http://codereview.chromium.org/2138003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47986 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views')
-rw-r--r-- | views/controls/textfield/gtk_views_textview.cc | 101 | ||||
-rw-r--r-- | views/controls/textfield/gtk_views_textview.h | 53 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_gtk.cc | 193 | ||||
-rw-r--r-- | views/controls/textfield/native_textfield_gtk.h | 1 | ||||
-rw-r--r-- | views/views.gyp | 2 |
5 files changed, 309 insertions, 41 deletions
diff --git a/views/controls/textfield/gtk_views_textview.cc b/views/controls/textfield/gtk_views_textview.cc new file mode 100644 index 0000000..e285ad9 --- /dev/null +++ b/views/controls/textfield/gtk_views_textview.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "views/controls/textfield/gtk_views_textview.h" + +#include "base/utf_string_conversions.h" +#include "gfx/canvas_paint.h" +#include "gfx/insets.h" +#include "gfx/skia_utils_gtk.h" +#include "views/controls/textfield/native_textfield_gtk.h" +#include "views/controls/textfield/textfield.h" + +G_BEGIN_DECLS + +G_DEFINE_TYPE(GtkViewsTextView, gtk_views_textview, GTK_TYPE_TEXT_VIEW) + +static gint gtk_views_textview_expose_event(GtkWidget *widget, + GdkEventExpose *event) { + GtkTextView* text_view = GTK_TEXT_VIEW(widget); + GdkWindow* text_window = gtk_text_view_get_window(text_view, + GTK_TEXT_WINDOW_TEXT); + if (event->window == text_window) { + gint width, height; + gdk_drawable_get_size (text_window, &width, &height); + gtk_paint_flat_box(widget->style, text_window, + static_cast<GtkStateType>(GTK_WIDGET_STATE(widget)), GTK_SHADOW_NONE, + &event->area, widget, "textview", + 0, 0, width, height); + } + + gint result = GTK_WIDGET_CLASS(gtk_views_textview_parent_class)->expose_event( + widget, event); + + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer(text_view); + views::NativeTextfieldGtk* host = GTK_VIEWS_TEXTVIEW(widget)->host; + + GtkTextIter start; + GtkTextIter end; + gtk_text_buffer_get_bounds(text_buffer, &start, &end); + + // Paint the info text to text window if GtkTextView contains no text. + if (host && event->window == text_window && + !host->textfield()->text_to_display_when_empty().empty() && + gtk_text_iter_equal(&start, &end)) { + gfx::CanvasPaint canvas(event); + if (!canvas.is_empty()) { + gfx::Insets insets = + views::NativeTextfieldGtk::GetTextViewInnerBorder(text_view); + gfx::Font font = host->textfield()->font(); + const string16 text = host->textfield()->text_to_display_when_empty(); + canvas.DrawStringInt( + UTF16ToWide(text), font, + gfx::GdkColorToSkColor(widget->style->text[GTK_STATE_INSENSITIVE]), + insets.left(), insets.top(), + widget->allocation.width - insets.width(), font.height()); + } + } + + // Draw border and focus. + if (event->window == widget->window) { + gint width; + gint height; + gdk_drawable_get_size (widget->window, &width, &height); + + bool has_focus = GTK_WIDGET_HAS_FOCUS(widget); + + gtk_paint_shadow(widget->style, widget->window, + has_focus ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + &event->area, widget, "textview", + 0, 0, width, height); + if (has_focus) { + gtk_paint_focus(widget->style, widget->window, + static_cast<GtkStateType>(GTK_WIDGET_STATE(widget)), + &event->area, widget, "textview", + 0, 0, width, height); + } + } + + return result; +} + +static void gtk_views_textview_class_init( + GtkViewsTextViewClass* views_textview_class) { + GtkWidgetClass* widget_class = + reinterpret_cast<GtkWidgetClass*>(views_textview_class); + widget_class->expose_event = gtk_views_textview_expose_event; +} + +static void gtk_views_textview_init(GtkViewsTextView* text_view) { + text_view->host = NULL; +} + +GtkWidget* gtk_views_textview_new(views::NativeTextfieldGtk* host) { + gpointer text_view = g_object_new(GTK_TYPE_VIEWS_TEXTVIEW, NULL); + GTK_VIEWS_TEXTVIEW(text_view)->host = host; + return GTK_WIDGET(text_view); +} + +G_END_DECLS diff --git a/views/controls/textfield/gtk_views_textview.h b/views/controls/textfield/gtk_views_textview.h new file mode 100644 index 0000000..5b3684b --- /dev/null +++ b/views/controls/textfield/gtk_views_textview.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef VIEWS_CONTROLS_TEXTFIELD_GTK_VIEWS_TEXTVIEW_H_ +#define VIEWS_CONTROLS_TEXTFIELD_GTK_VIEWS_TEXTVIEW_H_ + +#include <gdk/gdk.h> +#include <gtk/gtktextview.h> + +namespace views { +class NativeTextfieldGtk; +} + +// Similar to GtkViewsEntry, GtkViewsTextView is a subclass of GtkTextView +// with a border and ability to show a custom info when text view has no text. + +G_BEGIN_DECLS + +#define GTK_TYPE_VIEWS_TEXTVIEW (gtk_views_textview_get_type()) +#define GTK_VIEWS_TEXTVIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_VIEWS_TEXTVIEW, \ + GtkViewsTextView)) +#define GTK_VIEWS_TEXTVIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_VIEWS_TEXTVIEW, \ + GtkViewsTextViewClass)) +#define GTK_IS_VIEWS_TEXTVIEW(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_VIEWS_TEXTVIEW)) +#define GTK_IS_VIEWS_TEXTVIEW_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_VIEWS_TEXTVIEW)) +#define GTK_VIEWS_TEXTVIEW_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_VIEWS_TEXTVIEW, \ + GtkViewsTextView)) + +typedef struct _GtkViewsTextView GtkViewsTextView; +typedef struct _GtkViewsTextViewClass GtkViewsTextViewClass; + +struct _GtkViewsTextView { + GtkTextView text_view; + views::NativeTextfieldGtk* host; +}; + +struct _GtkViewsTextViewClass { + GtkTextViewClass parent_class; +}; + +GtkWidget* gtk_views_textview_new(views::NativeTextfieldGtk* host); + +GType gtk_views_textview_get_type(); + +G_END_DECLS + +#endif // VIEWS_CONTROLS_TEXTFIELD_GTK_VIEWS_TEXTVIEW_H_ diff --git a/views/controls/textfield/native_textfield_gtk.cc b/views/controls/textfield/native_textfield_gtk.cc index 91f1012..f2f74a9 100644 --- a/views/controls/textfield/native_textfield_gtk.cc +++ b/views/controls/textfield/native_textfield_gtk.cc @@ -12,6 +12,7 @@ #include "gfx/insets.h" #include "gfx/skia_utils_gtk.h" #include "views/controls/textfield/gtk_views_entry.h" +#include "views/controls/textfield/gtk_views_textview.h" #include "views/controls/textfield/textfield.h" namespace views { @@ -19,13 +20,16 @@ namespace views { // A character used to hide a text in password mode. static const char kPasswordChar = '*'; +// Border width for GtkTextView. +const int kTextViewBorderWidth = 4; + //////////////////////////////////////////////////////////////////////////////// // NativeTextfieldGtk, public: NativeTextfieldGtk::NativeTextfieldGtk(Textfield* textfield) : textfield_(textfield) { - if (textfield_->style() & Textfield::STYLE_MULTILINE) - NOTIMPLEMENTED(); // We don't support multiline yet. + if (textfield_->IsMultiLine() && textfield_->IsPassword()) + NOTIMPLEMENTED(); // We don't support multiline password yet. // Make |textfield| the focused view, so that when we get focused the focus // manager sees |textfield| as the focused view (since we are just a wrapper // view). @@ -55,24 +59,59 @@ gfx::Insets NativeTextfieldGtk::GetEntryInnerBorder(GtkEntry* entry) { return gfx::Insets(2, 2, 2, 2); } +gfx::Insets NativeTextfieldGtk::GetTextViewInnerBorder(GtkTextView* text_view) { + return gfx::Insets(kTextViewBorderWidth / 2, kTextViewBorderWidth / 2, + kTextViewBorderWidth / 2, kTextViewBorderWidth / 2); +} + //////////////////////////////////////////////////////////////////////////////// // NativeTextfieldGtk, NativeTextfieldWrapper implementation: string16 NativeTextfieldGtk::GetText() const { - return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(native_view()))); + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + GtkTextIter start; + GtkTextIter end; + gtk_text_buffer_get_bounds(text_buffer, &start, &end); + + return UTF8ToUTF16(gtk_text_iter_get_visible_text(&start, &end)); + } else { + return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(native_view()))); + } } void NativeTextfieldGtk::UpdateText() { if (!native_view()) return; - gtk_entry_set_text(GTK_ENTRY(native_view()), - UTF16ToUTF8(textfield_->text()).c_str()); + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + std::string utf8 = UTF16ToUTF8(textfield_->text()); + gtk_text_buffer_set_text(text_buffer, utf8.c_str(), utf8.length()); + } else { + gtk_entry_set_text(GTK_ENTRY(native_view()), + UTF16ToUTF8(textfield_->text()).c_str()); + } } void NativeTextfieldGtk::AppendText(const string16& text) { if (!native_view()) return; - gtk_entry_append_text(GTK_ENTRY(native_view()), UTF16ToUTF8(text).c_str()); + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + GtkTextIter end; + gtk_text_buffer_get_end_iter(text_buffer, &end); + + std::string utf8 = UTF16ToUTF8(text); + gtk_text_buffer_insert(text_buffer, &end, utf8.c_str(), utf8.length()); + } else { + gtk_entry_append_text(GTK_ENTRY(native_view()), UTF16ToUTF8(text).c_str()); + } } string16 NativeTextfieldGtk::GetSelectedText() const { @@ -80,37 +119,77 @@ string16 NativeTextfieldGtk::GetSelectedText() const { return string16(); string16 result; - gint start_pos; - gint end_pos; - if (!gtk_editable_get_selection_bounds(GTK_EDITABLE(native_view()), - &start_pos, &end_pos)) - return result; // No selection. - - UTF8ToUTF16(gtk_editable_get_chars(GTK_EDITABLE(native_view()), - start_pos, end_pos), - end_pos - start_pos, &result); + + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + GtkTextIter start; + GtkTextIter end; + if (gtk_text_buffer_get_selection_bounds(text_buffer, &start, &end)) { + gchar* selected_text = gtk_text_iter_get_visible_text(&start, &end); + if (selected_text) + UTF8ToUTF16(selected_text, strlen(selected_text), &result); + } + } else { + gint start_pos; + gint end_pos; + if (!gtk_editable_get_selection_bounds(GTK_EDITABLE(native_view()), + &start_pos, &end_pos)) + return result; // No selection. + + UTF8ToUTF16(gtk_editable_get_chars(GTK_EDITABLE(native_view()), + start_pos, end_pos), + end_pos - start_pos, &result); + } + return result; } void NativeTextfieldGtk::SelectAll() { if (!native_view()) return; - // -1 as the end position selects to the end of the text. - gtk_editable_select_region(GTK_EDITABLE(native_view()), 0, -1); + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + GtkTextIter start; + GtkTextIter end; + gtk_text_buffer_get_bounds(text_buffer, &start, &end); + gtk_text_buffer_select_range(text_buffer, &start, &end); + } else { + // -1 as the end position selects to the end of the text. + gtk_editable_select_region(GTK_EDITABLE(native_view()), 0, -1); + } } void NativeTextfieldGtk::ClearSelection() { if (!native_view()) return; - gtk_editable_select_region(GTK_EDITABLE(native_view()), 0, 0); + if (textfield_->IsMultiLine()) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(native_view())); + + GtkTextMark* insert_mark = gtk_text_buffer_get_insert(text_buffer); + GtkTextIter insert; + gtk_text_buffer_get_iter_at_mark(text_buffer, &insert, insert_mark); + gtk_text_buffer_select_range(text_buffer, &insert, &insert); + } else { + gtk_editable_select_region(GTK_EDITABLE(native_view()), 0, 0); + } } void NativeTextfieldGtk::UpdateBorder() { if (!native_view()) return; - if (!textfield_->draw_border()) - gtk_entry_set_has_frame(GTK_ENTRY(native_view()), false); + if (textfield_->IsMultiLine()) { + if (!textfield_->draw_border()) + gtk_container_set_border_width(GTK_CONTAINER(native_view()), 0); + } else { + if (!textfield_->draw_border()) + gtk_entry_set_has_frame(GTK_ENTRY(native_view()), false); + } } void NativeTextfieldGtk::UpdateTextColor() { @@ -138,8 +217,14 @@ void NativeTextfieldGtk::UpdateBackgroundColor() { void NativeTextfieldGtk::UpdateReadOnly() { if (!native_view()) return; - gtk_editable_set_editable(GTK_EDITABLE(native_view()), - !textfield_->read_only()); + + if (textfield_->IsMultiLine()) { + gtk_text_view_set_editable(GTK_TEXT_VIEW(native_view()), + !textfield_->read_only()); + } else { + gtk_editable_set_editable(GTK_EDITABLE(native_view()), + !textfield_->read_only()); + } } void NativeTextfieldGtk::UpdateFont() { @@ -154,8 +239,10 @@ void NativeTextfieldGtk::UpdateFont() { void NativeTextfieldGtk::UpdateIsPassword() { if (!native_view()) return; - gtk_entry_set_visibility(GTK_ENTRY(native_view()), - !textfield_->IsPassword()); + if (!textfield_->IsMultiLine()) { + gtk_entry_set_visibility(GTK_ENTRY(native_view()), + !textfield_->IsPassword()); + } } void NativeTextfieldGtk::UpdateEnabled() { @@ -169,16 +256,19 @@ gfx::Insets NativeTextfieldGtk::CalculateInsets() { return gfx::Insets(); GtkWidget* widget = native_view(); - GtkEntry* entry = GTK_ENTRY(widget); gfx::Insets insets; - insets += GetEntryInnerBorder(entry); - - if (entry->has_frame) { - insets += gfx::Insets(widget->style->ythickness, - widget->style->xthickness, - widget->style->ythickness, - widget->style->xthickness); + if (textfield_->IsMultiLine()) { + insets += GetTextViewInnerBorder(GTK_TEXT_VIEW(widget)); + } else { + GtkEntry* entry = GTK_ENTRY(widget); + insets += GetEntryInnerBorder(entry); + if (entry->has_frame) { + insets += gfx::Insets(widget->style->ythickness, + widget->style->xthickness, + widget->style->ythickness, + widget->style->xthickness); + } } gboolean interior_focus; @@ -196,8 +286,14 @@ gfx::Insets NativeTextfieldGtk::CalculateInsets() { void NativeTextfieldGtk::SetHorizontalMargins(int left, int right) { if (!native_view()) return; - GtkBorder border = { left, right, 0, 0 }; - gtk_entry_set_inner_border(GTK_ENTRY(native_view()), &border); + if (textfield_->IsMultiLine()) { + GtkTextView* text_view = GTK_TEXT_VIEW(native_view()); + gtk_text_view_set_left_margin(text_view, left); + gtk_text_view_set_right_margin(text_view, right); + } else { + GtkBorder border = { left, right, 0, 0 }; + gtk_entry_set_inner_border(GTK_ENTRY(native_view()), &border); + } } void NativeTextfieldGtk::SetFocus() { @@ -218,7 +314,7 @@ bool NativeTextfieldGtk::IsIMEComposing() const { // static gboolean NativeTextfieldGtk::OnKeyPressEventHandler( - GtkWidget* entry, + GtkWidget* widget, GdkEventKey* event, NativeTextfieldGtk* textfield) { return textfield->OnKeyPressEvent(event); @@ -235,7 +331,7 @@ gboolean NativeTextfieldGtk::OnKeyPressEvent(GdkEventKey* event) { // static gboolean NativeTextfieldGtk::OnChangedHandler( - GtkWidget* entry, + GtkWidget* widget, NativeTextfieldGtk* textfield) { return textfield->OnChanged(); } @@ -252,16 +348,31 @@ gboolean NativeTextfieldGtk::OnChanged() { // NativeTextfieldGtk, NativeControlGtk overrides: void NativeTextfieldGtk::CreateNativeControl() { - NativeControlCreated(gtk_views_entry_new(this)); - gtk_entry_set_invisible_char(GTK_ENTRY(native_view()), - static_cast<gunichar>(kPasswordChar)); + if (textfield_->IsMultiLine()) { + NativeControlCreated(gtk_views_textview_new(this)); + if (textfield_->draw_border()) + gtk_container_set_border_width(GTK_CONTAINER(native_view()), + kTextViewBorderWidth); + } else { + NativeControlCreated(gtk_views_entry_new(this)); + gtk_entry_set_invisible_char(GTK_ENTRY(native_view()), + static_cast<gunichar>(kPasswordChar)); + } textfield_->UpdateAllProperties(); } void NativeTextfieldGtk::NativeControlCreated(GtkWidget* widget) { NativeControlGtk::NativeControlCreated(widget); - g_signal_connect(widget, "changed", - G_CALLBACK(OnChangedHandler), this); + + if (GTK_IS_TEXT_VIEW(widget)) { + GtkTextBuffer* text_buffer = gtk_text_view_get_buffer( + GTK_TEXT_VIEW(widget)); + g_signal_connect(text_buffer, "changed", + G_CALLBACK(OnChangedHandler), this); + } else { + g_signal_connect(widget, "changed", + G_CALLBACK(OnChangedHandler), this); + } g_signal_connect(widget, "key-press-event", G_CALLBACK(OnKeyPressEventHandler), this); } diff --git a/views/controls/textfield/native_textfield_gtk.h b/views/controls/textfield/native_textfield_gtk.h index 1ada4ac..ff92d4e 100644 --- a/views/controls/textfield/native_textfield_gtk.h +++ b/views/controls/textfield/native_textfield_gtk.h @@ -24,6 +24,7 @@ class NativeTextfieldGtk : public NativeControlGtk, // Returns the inner border of the entry. static gfx::Insets GetEntryInnerBorder(GtkEntry* entry); + static gfx::Insets GetTextViewInnerBorder(GtkTextView* text_view); // Overridden from NativeTextfieldWrapper: virtual string16 GetText() const; diff --git a/views/views.gyp b/views/views.gyp index 50c15b1..2344e00 100644 --- a/views/views.gyp +++ b/views/views.gyp @@ -201,6 +201,8 @@ 'controls/table/table_view_observer.h', 'controls/textfield/gtk_views_entry.cc', 'controls/textfield/gtk_views_entry.h', + 'controls/textfield/gtk_views_textview.cc', + 'controls/textfield/gtk_views_textview.h', 'controls/textfield/textfield.cc', 'controls/textfield/textfield.h', 'controls/textfield/native_textfield_gtk.cc', |