1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/gtk/gtk_chrome_button.h" 6 7 #include "base/basictypes.h" 8 #include "chrome/browser/ui/gtk/nine_box.h" 9 #include "grit/ui_resources.h" 10 #include "ui/gfx/gtk_util.h" 11 12 namespace { 13 14 // The theme graphics for when the mouse is over the button. 15 NineBox* g_nine_box_prelight; 16 // The theme graphics for when the button is clicked. 17 NineBox* g_nine_box_active; 18 19 } // namespace 20 21 G_BEGIN_DECLS 22 23 #define GTK_CHROME_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),\ 24 GTK_TYPE_CHROME_BUTTON,\ 25 GtkChromeButtonPrivate)) 26 typedef struct _GtkChromeButtonPrivate GtkChromeButtonPrivate; 27 28 struct _GtkChromeButtonPrivate { 29 int paint_state; 30 31 // If true, we use images provided by the theme instead of GTK's default 32 // button rendering. 33 gboolean use_gtk_rendering; 34 35 gdouble hover_state; 36 }; 37 38 G_DEFINE_TYPE(GtkChromeButton, gtk_chrome_button, GTK_TYPE_BUTTON) 39 static gboolean gtk_chrome_button_expose(GtkWidget* widget, 40 GdkEventExpose* event); 41 42 static void gtk_chrome_button_class_init(GtkChromeButtonClass* button_class) { 43 gtk_rc_parse_string( 44 "style \"chrome-button\" {" 45 " xthickness = 2 " 46 " GtkButton::child-displacement-x = 0" 47 " GtkButton::child-displacement-y = 0" 48 " GtkButton::inner-border = { 0, 0, 0, 0 }" 49 "}" 50 "widget_class \"*.<GtkChromeButton>\" style \"chrome-button\""); 51 52 GtkWidgetClass* widget_class = 53 reinterpret_cast<GtkWidgetClass*>(button_class); 54 widget_class->expose_event = gtk_chrome_button_expose; 55 56 g_nine_box_prelight = new NineBox( 57 IDR_TEXTBUTTON_HOVER_TOP_LEFT, 58 IDR_TEXTBUTTON_HOVER_TOP, 59 IDR_TEXTBUTTON_HOVER_TOP_RIGHT, 60 IDR_TEXTBUTTON_HOVER_LEFT, 61 IDR_TEXTBUTTON_HOVER_CENTER, 62 IDR_TEXTBUTTON_HOVER_RIGHT, 63 IDR_TEXTBUTTON_HOVER_BOTTOM_LEFT, 64 IDR_TEXTBUTTON_HOVER_BOTTOM, 65 IDR_TEXTBUTTON_HOVER_BOTTOM_RIGHT); 66 67 g_nine_box_active = new NineBox( 68 IDR_TEXTBUTTON_PRESSED_TOP_LEFT, 69 IDR_TEXTBUTTON_PRESSED_TOP, 70 IDR_TEXTBUTTON_PRESSED_TOP_RIGHT, 71 IDR_TEXTBUTTON_PRESSED_LEFT, 72 IDR_TEXTBUTTON_PRESSED_CENTER, 73 IDR_TEXTBUTTON_PRESSED_RIGHT, 74 IDR_TEXTBUTTON_PRESSED_BOTTOM_LEFT, 75 IDR_TEXTBUTTON_PRESSED_BOTTOM, 76 IDR_TEXTBUTTON_PRESSED_BOTTOM_RIGHT); 77 78 GObjectClass* gobject_class = G_OBJECT_CLASS(button_class); 79 g_type_class_add_private(gobject_class, sizeof(GtkChromeButtonPrivate)); 80 } 81 82 static void gtk_chrome_button_init(GtkChromeButton* button) { 83 GtkChromeButtonPrivate* priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); 84 priv->paint_state = -1; 85 priv->use_gtk_rendering = FALSE; 86 priv->hover_state = -1.0; 87 88 gtk_widget_set_can_focus(GTK_WIDGET(button), FALSE); 89 } 90 91 static gboolean gtk_chrome_button_expose(GtkWidget* widget, 92 GdkEventExpose* event) { 93 GtkChromeButtonPrivate *priv = GTK_CHROME_BUTTON_GET_PRIVATE(widget); 94 int paint_state = priv->paint_state < 0 ? 95 gtk_widget_get_state(widget) : priv->paint_state; 96 97 if (priv->use_gtk_rendering) { 98 // We have the superclass handle this expose when we aren't using custom 99 // rendering AND we're in either the prelight or active state so that we 100 // get the button border for the current GTK theme drawn. 101 if (paint_state == GTK_STATE_PRELIGHT || paint_state == GTK_STATE_ACTIVE) { 102 // Set the state of button->depressed so we paint pressed even if the 103 // actual state of the button is something else. 104 GTK_BUTTON(widget)->depressed = (paint_state == GTK_STATE_ACTIVE); 105 return GTK_WIDGET_CLASS(gtk_chrome_button_parent_class)->expose_event 106 (widget, event); 107 } 108 } else { 109 double effective_hover_state = paint_state == GTK_STATE_PRELIGHT ? 110 1.0 : 0.0; 111 // |paint_state| overrides |hover_state|. 112 if (priv->hover_state >= 0.0 && priv->paint_state < 0) 113 effective_hover_state = priv->hover_state; 114 115 if (paint_state == GTK_STATE_ACTIVE) { 116 g_nine_box_active->RenderToWidget(widget); 117 } else { 118 g_nine_box_prelight->RenderToWidgetWithOpacity(widget, 119 effective_hover_state); 120 } 121 } 122 123 // If we have a child widget, draw it. 124 if (gtk_bin_get_child(GTK_BIN(widget))) { 125 gtk_container_propagate_expose(GTK_CONTAINER(widget), 126 gtk_bin_get_child(GTK_BIN(widget)), 127 event); 128 } 129 130 return FALSE; 131 } 132 133 GtkWidget* gtk_chrome_button_new(void) { 134 return GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_BUTTON, NULL)); 135 } 136 137 void gtk_chrome_button_set_paint_state(GtkChromeButton* button, 138 GtkStateType state) { 139 g_return_if_fail(GTK_IS_CHROME_BUTTON(button)); 140 141 GtkChromeButtonPrivate *priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); 142 priv->paint_state = state; 143 144 gtk_widget_queue_draw(GTK_WIDGET(button)); 145 } 146 147 void gtk_chrome_button_unset_paint_state(GtkChromeButton* button) { 148 g_return_if_fail(GTK_IS_CHROME_BUTTON(button)); 149 150 GtkChromeButtonPrivate *priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); 151 priv->paint_state = -1; 152 153 gtk_widget_queue_draw(GTK_WIDGET(button)); 154 } 155 156 void gtk_chrome_button_set_use_gtk_rendering(GtkChromeButton* button, 157 gboolean value) { 158 g_return_if_fail(GTK_IS_CHROME_BUTTON(button)); 159 GtkChromeButtonPrivate *priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); 160 priv->use_gtk_rendering = value; 161 } 162 163 void gtk_chrome_button_set_hover_state(GtkChromeButton* button, 164 gdouble state) { 165 GtkChromeButtonPrivate* priv = GTK_CHROME_BUTTON_GET_PRIVATE(button); 166 if (state >= 0.0 && state <= 1.0) 167 priv->hover_state = state; 168 else 169 priv->hover_state = -1.0; 170 gtk_widget_queue_draw(GTK_WIDGET(button)); 171 } 172 173 G_END_DECLS 174