summaryrefslogtreecommitdiffstats
path: root/views
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-22 05:36:33 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-22 05:36:33 +0000
commit5e1ed6dcace94125b65e1061e2219672fda9ba1f (patch)
treefdf061bfe23ee7299675ccba30001805da3fe61f /views
parentf34350d99b4b4ffe73f68594649a8f2c61e07062 (diff)
downloadchromium_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.cc101
-rw-r--r--views/controls/textfield/gtk_views_textview.h53
-rw-r--r--views/controls/textfield/native_textfield_gtk.cc193
-rw-r--r--views/controls/textfield/native_textfield_gtk.h1
-rw-r--r--views/views.gyp2
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',