1. Trang chủ
  2. » Công Nghệ Thông Tin

apress foundations_of gtk plus development 2007 phần 8 doc

76 233 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 76
Dung lượng 2 MB

Nội dung

408 CHAPTER 11 ■ CREATING CUSTOM WIDGETS G_BEGIN_DECLS #define MY_MARQUEE_TYPE (my_marquee_get_type ()) #define MY_MARQUEE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ MY_MARQUEE_TYPE, MyMarquee)) #define MY_MARQUEE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ MY_MARQUEE_TYPE, MyMarqueeClass)) #define IS_MY_MARQUEE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ MY_MARQUEE_TYPE)) #define IS_MY_MARQUEE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ MY_MARQUEE_TYPE)) typedef struct _MyMarquee MyMarquee; typedef struct _MyMarqueeClass MyMarqueeClass; struct _MyMarquee { GtkWidget widget; }; struct _MyMarqueeClass { GtkWidgetClass parent_class; }; GType my_marquee_get_type (void) G_GNUC_CONST; GtkWidget* my_marquee_new (void); void my_marquee_set_message (MyMarquee *marquee, const gchar *message); gchar* my_marquee_get_message (MyMarquee *marquee); void my_marquee_set_speed (MyMarquee *marquee, gint speed); gint my_marquee_get_speed (MyMarquee *marquee); void my_marquee_slide (MyMarquee *marquee); G_END_DECLS #endif /* __MY_MARQUEE_H__ */ Since MyMarquee is a new widget, it will be directly derived from GtkWidget. This is shown by the fact that MyMarquee contains a GtkWidget object and MyMarqueeClass contains a GtkWidgetClass class. Recall that neither of these members should be declared as pointers! Deriving the widget from GtkWidget allows you to take advantage of all of the signals and prop- erties that are common to every widget, including event handling. 7931.book Page 408 Thursday, March 1, 2007 8:06 PM CHAPTER 11 ■ CREATING CUSTOM WIDGETS 409 The widget will have two properties that the programmer can set and retrieve. The user can use my_marquee_set_message() to change the message that is scrolled by the widget. The speed is an integer between 1 and 50. The message will be moved this many pixels to the left every time my_marquee_slide() is called. Creating the MyMarquee Widget Now that the header file is created, Listing 11-18 performs basic initialization such as declaring the private class, enumerating properties, and creating a new GType. There are no new signals associated with this widget, so the signal enumeration and array of signal identifiers are omitted. Listing 11-18. Defining MyMarqueePrivate and MyMarquee GType (mymarquee.c) #include "mymarquee.h" #define MARQUEE_MIN_WIDTH 300 #define MY_MARQUEE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ MY_MARQUEE_TYPE, MyMarqueePrivate)) typedef struct _MyMarqueePrivate MyMarqueePrivate; struct _MyMarqueePrivate { gchar *message; gint speed; gint current_x; }; enum { PROP_0, PROP_MESSAGE, PROP_SPEED }; /* Get a GType that corresponds to MyMarquee. The first time this function is * called (on object instantiation), the type is registered. */ GType my_marquee_get_type () { static GType marquee_type = 0; 7931.book Page 409 Thursday, March 1, 2007 8:06 PM 410 CHAPTER 11 ■ CREATING CUSTOM WIDGETS if (!marquee_type) { static const GTypeInfo marquee_info = { sizeof (MyMarqueeClass), NULL, NULL, (GClassInitFunc) my_marquee_class_init, NULL, NULL, sizeof (MyMarquee), 0, (GInstanceInitFunc) my_marquee_init, }; marquee_type = g_type_register_static (GTK_TYPE_WIDGET, "MyMarquee", &marquee_info, 0); } return marquee_type; } Listing 11-18 shows the first part of the implementation of the MyMarquee widget. We begin this file by creating the MyMarqueePrivate structure, which will be used to hold the values of necessary widget properties. This includes the displayed message, the scrolling speed, and the current horizontal position of the message. The next position of the message will be calculated based on this position, which allows us to easily handle resizing of the widget. Since MyMarquee is derived directly from GtkWidget, you will need to register the widget with a parent class type of GTK_TYPE_WIDGET, as shown in the implementation of my_marquee_get_type(). The implementation of this function is almost an exact replica of my_ip_address_get_type(). Listing 11-19 shows the MyMarquee class and instance initialization functions. In my_marquee_class_init(), you will notice that we not only override functions in the GObjectClass but also in the GtkWidgetClass. Listing 11-19. Initializing the MyMarquee Class and Structure /* Initialize the MyMarqueeClass class by overriding standard functions, * registering a private class and setting up signals and properties. */ static void my_marquee_class_init (MyMarqueeClass *klass) { GObjectClass *gobject_class; GtkWidgetClass *widget_class; gobject_class = (GObjectClass*) klass; widget_class = (GtkWidgetClass*) klass; 7931.book Page 410 Thursday, March 1, 2007 8:06 PM CHAPTER 11 ■ CREATING CUSTOM WIDGETS 411 /* Override the standard functions for setting and retrieving properties. */ gobject_class->set_property = my_marquee_set_property; gobject_class->get_property = my_marquee_get_property; /* Override the standard functions for realize, expose, and size changes. */ widget_class->realize = my_marquee_realize; widget_class->expose_event = my_marquee_expose; widget_class->size_request = my_marquee_size_request; widget_class->size_allocate = my_marquee_size_allocate; /* Add MyMarqueePrivate as a private data class of MyMarqueeClass. */ g_type_class_add_private (klass, sizeof (MyMarqueePrivate)); /* Register four GObject properties, the message and the speed. */ g_object_class_install_property (gobject_class, PROP_MESSAGE, g_param_spec_string ("message", "Marquee Message", "The message to scroll", "", G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_SPEED, g_param_spec_int ("speed", "Speed of the Marquee", "The percentage of movement every second", 1, 50, 25, G_PARAM_READWRITE)); } /* Initialize the actual MyMarquee widget. This function is used to set up * the initial view of the widget and set necessary properties. */ static void my_marquee_init (MyMarquee *marquee) { MyMarqueePrivate *priv = MY_MARQUEE_GET_PRIVATE (marquee); priv->current_x = MARQUEE_MIN_WIDTH; priv->speed = 25; } The next step is to implement the class and instance initialization functions that were ref- erenced by the GTypeInfo object. In this example, in addition to overriding functions in the parent GObjectClass, we also need to override a few in GtkWidgetClass. These include overrid- ing calls for realizing and exposing the widget as well as size requests and allocations. You need to be especially careful when overriding functions in GtkWidgetClass, because they perform crucial tasks for the widget. You can render the widget unusable if you do not per- form all of the necessary functions. I would recommend that you view how other GTK+ widgets 7931.book Page 411 Thursday, March 1, 2007 8:06 PM 412 CHAPTER 11 ■ CREATING CUSTOM WIDGETS implement overridden functions when you do it yourself. For a full list of functions that can be overridden, you should view the GtkWidgetClass structure in <gtk/gtkwidget.h>. The MyMarqueePrivate structure was also added in the class initialization function to MyMarqueeClass with g_type_class_add_private(). Since the object is not stored as a member of the MyMarqueeClass structure, you need to use the definition of MY_MARQUEE_GET_PRIVATE() to retrieve the MyMarqueePrivate object, as shown in the instance initialization function. In my_marquee_init(), the current position of the message is set to be displayed beyond the right side of the widget. By default, the message will then be scrolled 25 pixels to the left when my_marquee_slide() is programmatically called. The implementations of the overridden set_property() and get_property() functions are similar to the previous example. These functions are displayed in Listing 11-20, which allow the user to set and retrieve the message and speed properties of the widget. Listing 11-20. Setting and Retrieving MyMarquee Properties /* This function is called when the programmer gives a new value for a widget * property with g_object_set(). */ static void my_marquee_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MyMarquee *marquee = MY_MARQUEE (object); switch (prop_id) { case PROP_MESSAGE: my_marquee_set_message (marquee, g_value_get_string (value)); break; case PROP_SPEED: my_marquee_set_speed (marquee, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /* This function is called when the programmer requests the value of a widget * property with g_object_get(). */ static void my_marquee_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) 7931.book Page 412 Thursday, March 1, 2007 8:06 PM CHAPTER 11 ■ CREATING CUSTOM WIDGETS 413 { MyMarquee *marquee = MY_MARQUEE (object); MyMarqueePrivate *priv = MY_MARQUEE_GET_PRIVATE (marquee); switch (prop_id) { case PROP_MESSAGE: g_value_set_string (value, priv->message); break; case PROP_SPEED: g_value_set_int (value, priv->speed); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } Listing 11-21 shows the implementation of my_marquee_new(). This is the function that the programmer can call to create a new MyMarquee widget. It is simply a convenience function, so you do not have to call g_object_new() directly. Listing 11-21. Creating a New MyMarquee Widget GtkWidget* my_marquee_new () { return GTK_WIDGET (g_object_new (my_marquee_get_type (), NULL)); } Realizing the Widget Where the implementation of this widget is different from MyIPAddress is the overridden GtkWidgetClass functions. The first of these functions is my_marquee_realize(), shown in Listing 11-22. This function is called when the MyMarquee instance is first realized. Listing 11-22. Realizing the MyMarquee Widget static void my_marquee_realize (GtkWidget *widget) { MyMarquee *marquee; GdkWindowAttr attributes; gint attr_mask; g_return_if_fail (widget != NULL); g_return_if_fail (IS_MY_MARQUEE (widget)); 7931.book Page 413 Thursday, March 1, 2007 8:06 PM 414 CHAPTER 11 ■ CREATING CUSTOM WIDGETS /* Set the GTK_REALIZED flag so it is marked as realized. */ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); marquee = MY_MARQUEE (widget); /* Create a new GtkWindowAttr object that will hold info about the GdkWindow. */ attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget); attributes.event_mask |= (GDK_EXPOSURE_MASK); attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); /* Create a new GdkWindow for the widget. */ attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attr_mask); gdk_window_set_user_data (widget->window, marquee); /* Attach a style to the GdkWindow and draw a background color. */ widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); gdk_window_show (widget->window); } The first tasks performed by my_marquee_realize() are to check whether the widget is non- NULL and whether it is a MyMarquee widget. The gtk_return_if_fail() function is used to return from the function if either test returns FALSE. You should always perform these tests, because your program can respond unexpectedly otherwise. The purpose of the realization function is to set up a GdkWindow for the instance of the wid- get so that it can be rendered to the screen. To do this, you first need a GdkWindowAttr object that holds the desired properties of the new GdkWindow. Table 11-3 describes of all of the GtkWindowAttr structure’s members. Table 11-3. GtkWindowAttr Members Variable Description gchar *title The title of the window or NULL if the window is not a top-level window. This usually does not need to be set. gint event_mask A bitmask of GDK events that will be recognized by the widget. You can use gtk_widget_get_events() to retrieve all of the events that are currently associated with the widget and then add your own. 7931.book Page 414 Thursday, March 1, 2007 8:06 PM CHAPTER 11 ■ CREATING CUSTOM WIDGETS 415 In our implementation of my_marquee_realize(), we first set the horizontal and vertical positions of the widget, which are relative to the top-left corner of the parent window. This is easy, since they are already provided by the widget’s allocation. The allocation also provides the initial width and height of the widget. The next member, wclass, is set to one of two values. GDK_INPUT_OUTPUT refers to a nor- mal GdkWindow widget, which should be used for most widgets. GDK_INPUT_ONLY is an invisible GdkWindow widget that is used to receive events. Next, you can set the window type, which is determined by a value from the following GdkWindowType enumeration: • GDK_WINDOW_ROOT: A window that has no parent window and will cover the whole screen. This is usually only used by the window manager. • GDK_WINDOW_TOPLEVEL: A top-level window that will usually have decorations. For exam- ple, GtkWindow uses this window type. • GDK_WINDOW_CHILD: A child window of a top-level window or another child window. This is used for most widgets that are not top-level windows themselves. • GDK_WINDOW_DIALOG: This window type is depreciated and should not be used. • GDK_WINDOW_TEMP: A window that is only going to be displayed temporarily, such as a GtkMenu widget. • GDK_WINDOW_FOREIGN: A foreign window type implemented by another library that needs to be wrapped as a GdkWindow widget. gint x, y The x and y coordinates of the GdkWindow object with respect to the parent window. You can retrieve these values from the widget’s allocation. gint width, height The width and height of the GdkWindow object. You can retrieve these values from the widget’s allocation. GdkWindowClass wclass This should be set to GDK_INPUT_OUTPUT for most GdkWindow objects or GDK_INPUT_ONLY if the window will be invisible. GdkVisual *visual A GdkVisual object to use for the window. The default can be retrieved with gtk_widget_get_visual(). GdkColormap *colormap A GdkColormap object to use for the window. The default can be retrieved with gtk_widget_get_colormap(). GdkWindowType window_type The type of window that will be displayed as defined by the GdkWindowType enumeration. GdkCursor *cursor An optional GdkCursor object that will be displayed when the mouse is over the top of the widget. gchar *wmclass_name This property should be ignored. For more information, view the documentation on gtk_window_set_wmclass(). gchar *wmclass_class This property should be ignored. For more information, view the documentation on gtk_window_set_wmclass(). gboolean override_redirect If set to TRUE, the widget will bypass the window manager. Variable Description 7931.book Page 415 Thursday, March 1, 2007 8:06 PM 416 CHAPTER 11 ■ CREATING CUSTOM WIDGETS The next call sets the event mask for the GdkWindow. A call to gtk_widget_get_events() returns all events that are already installed on the widget, and then we add GDK_EXPOSURE_MASK to the list. This will make sure that our exposure function will be called. Next, we set the GdkVisual object that will be used for the GdkWindow widget. This object is used to describe specific information about the video hardware. In most cases, you should use the default GdkVisual assigned to the widget, which you can retrieve with gtk_widget_get_visual(). The last property set in the GdkWindowAttr structure is the color map. Again, we use gtk_widget_get_colormap() to retrieve the default color map for the widget, since you will usually not need to edit this. The next step is to create a mask of specific GdkWindowAttributesType values, which indi- cate which fields in the GdkWindowAttr should be honored. In this example, the specified x and y coordinates, GdkVisual, and GdkColormap will be used. attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; We now have enough information to create a new GdkWindow for the widget with gdk_window_new(). This function accepts the parent GdkWindow, a GdkWindowAttr object, and a mask of attributes to honor. GdkWindow* gdk_window_new (GdkWindow *parent, GdkWindowAttr *attributes, gint attributes_mask); Next, the GtkWidget should be stored as the user data of the GdkWindow for custom widgets with gdk_window_set_user_data(). This ensures that widget events such as expose-event are recognized. If you do not call this, events will not be recognized. void gdk_window_set_user_data (GdkWindow *window, gpointer user_data); The window’s style is then attached to the window with gtk_style_attach(), which will begin the process of creating graphics contexts for the style. You should always make sure to store the returned value, since it may be a new style. GtkStyle* gtk_style_attach (GtkStyle *style, GdkWindow *window); Once the style is attached to the window, the background of the window is set. The gtk_style_set_background() sets the background color of the GdkWindow to the color specified by the GtkStyle in the given state. void gtk_style_set_background (GtkStyle *style, GdkWindow *window, GtkStyleType state_type); Lastly, the window is displayed to the user with a call to gdk_window_show(). If you do not call this function, the widget will never be visible to the user. This function will also make sure that all of the necessary initialization has been performed. 7931.book Page 416 Thursday, March 1, 2007 8:06 PM CHAPTER 11 ■ CREATING CUSTOM WIDGETS 417 Specifying Size Requests and Allocations We also overrode the size request and allocation functions of the parent GtkWindowClass. The my_marquee_size_request() function in Listing 11-23 was simply used to specify default width and height values to the requisition. Listing 11-23. Handling Size Requests and Allocations /* Handle size requests for the widget. This function forces the widget to have * an initial size set according to the predefined width and the font size. */ static void my_marquee_size_request (GtkWidget *widget, GtkRequisition *requisition) { PangoFontDescription *fd; g_return_if_fail (widget != NULL || requisition != NULL); g_return_if_fail (IS_MY_MARQUEE (widget)); fd = widget->style->font_desc; requisition->width = MARQUEE_MIN_WIDTH; requisition->height = (pango_font_description_get_size (fd) / PANGO_SCALE) + 10; } /* Handle size allocations for the widget. This does the actual resizing of the * widget to the requested allocation. */ static void my_marquee_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { MyMarquee *marquee; g_return_if_fail (widget != NULL || allocation != NULL); g_return_if_fail (IS_MY_MARQUEE (widget)); widget->allocation = *allocation; marquee = MY_MARQUEE (widget); if (GTK_WIDGET_REALIZED (widget)) { gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); } } 7931.book Page 417 Thursday, March 1, 2007 8:06 PM [...]... Thursday, March 8, 2007 7:02 PM CHAPTER 12 ■ ADDITIONAL GTK+ WIDGETS gtk_ init (&argc, &argv); w = g_slice_new (Widgets); w->window = gtk_ window_new (GTK_ WINDOW_TOPLEVEL); gtk_ window_set_title (GTK_ WINDOW (w->window), "Printing"); gtk_ container_set_border_width (GTK_ CONTAINER (w->window), 10); g_signal_connect (G_OBJECT (w->window), "destroy", G_CALLBACK (gtk_ main_quit), NULL); w->chooser = gtk_ file_chooser_button_new... (marqueetest.c) #include #include "mymarquee.h" int main (int argc, char *argv[]) { GtkWidget *window, *marquee; PangoFontDescription *fd; gtk_ init (&argc, &argv); window = gtk_ window_new (GTK_ WINDOW_TOPLEVEL); gtk_ window_set_title (GTK_ WINDOW (window), "MyMarquee"); gtk_ container_set_border_width (GTK_ CONTAINER (window), 10); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_ main_quit),... image for the system tray icon before setting the object as visible if you use that initialization function GtkStatusIcon* GtkStatusIcon* GtkStatusIcon* GtkStatusIcon* GtkStatusIcon* gtk_ status_icon_new (); gtk_ status_icon_new_from_pixbuf gtk_ status_icon_new_from_file gtk_ status_icon_new_from_stock gtk_ status_icon_new_from_icon_name (GdkPixbuf *pixbuf); (const gchar *filename); (const gchar *stock_id);... ("Select a File", GTK_ FILE_CHOOSER_ACTION_OPEN); gtk_ file_chooser_set_current_folder (GTK_ FILE_CHOOSER (w->chooser), g_get_home_dir ()); print = gtk_ button_new_from_stock (GTK_ STOCK_PRINT); g_signal_connect (G_OBJECT (print), "clicked", G_CALLBACK (print_file), (gpointer) w); hbox = gtk_ hbox_new (FALSE, 5); gtk_ box_pack_start (GTK_ BOX (hbox), w->chooser, FALSE, FALSE, 0); gtk_ box_pack_start (GTK_ BOX (hbox),... *chooser; PrintData *data; } Widgets; GtkPrintSettings *settings; static static static static void void void void print_file (GtkButton*, Widgets*); begin_print (GtkPrintOperation*, GtkPrintContext*, Widgets*); draw_page (GtkPrintOperation*, GtkPrintContext*, gint, Widgets*); end_print (GtkPrintOperation*, GtkPrintContext*, Widgets*); int main (int argc, char *argv[]) { GtkWidget *hbox, *print; Widgets... that you should draw to GtkLayout’s bin_window member instead of GtkWidget’s window For example, you would draw to GTK_ LAYOUT(layout)->bin_window instead of GTK_ WIDGET(layout)->window This allows child widgets to be correctly embedded into the widget 7931.book Page 437 Thursday, March 8, 2007 7:02 PM CHAPTER 12 ■ ADDITIONAL GTK+ WIDGETS New GtkLayout widgets are created with gtk_ layout_new(), which... (GtkWidget*, GdkEventMotion*, GPtrArray*); key_pressed (GtkWidget*, GdkEventKey*, GPtrArray*); expose_event (GtkWidget*, GdkEventExpose*, GPtrArray*); int main (int argc, char *argv[]) { GtkWidget *window, *area; GPtrArray *parray; gtk_ init (&argc, &argv); window = gtk_ window_new (GTK_ WINDOW_TOPLEVEL); gtk_ window_set_title (GTK_ WINDOW (window), "Drawing Areas"); gtk_ widget_set_size_request (window, 400, 300);... March 8, 2007 7:02 PM CHAPTER 12 ■ ADDITIONAL GTK+ WIDGETS To customize how the GtkCalendar widget is displayed and how it interacts with the user, you should use gtk_ calendar_set_display_options() to set a bitwise list of GtkCalendarDisplayOptions values The nondeprecated values of this enumeration follow: • GTK_ CALENDAR_SHOW_HEADING: If set, the name of the month and the year will be displayed • GTK_ CALENDAR_SHOW_DAY_NAMES:... interact with the GtkDrawingArea widget and use events with it 7931.book Page 433 Thursday, March 8, 2007 7:02 PM CHAPTER 12 ■ ADDITIONAL GTK+ WIDGETS Listing 12-1 A Simple Drawing Program (drawingareas.c) #include #include static static static static gboolean gboolean gboolean gboolean button_pressed (GtkWidget*, GdkEventButton*, GPtrArray*); motion_notify (GtkWidget*, GdkEventMotion*,... NULL to both function parameters Since GtkLayout has native scrolling support, it can be much more useful than GtkDrawingArea when you need to use it with a scrolled window GtkWidget* gtk_ layout_new (GtkAdjustment *hadjustment, GtkAdjustment *vadjustment); However, GtkLayout does add some overhead, since it is capable of containing widgets as well Because of this, GtkDrawingArea is a better choice if . char *argv[]) { GtkWidget *window, *marquee; PangoFontDescription *fd; gtk_ init (&argc, &argv); window = gtk_ window_new (GTK_ WINDOW_TOPLEVEL); gtk_ window_set_title (GTK_ WINDOW (window),. properties of the new GdkWindow. Table 11-3 describes of all of the GtkWindowAttr structure’s members. Table 11-3. GtkWindowAttr Members Variable Description gchar *title The title of the window. from GtkWidget allows you to take advantage of all of the signals and prop- erties that are common to every widget, including event handling. 7931.book Page 4 08 Thursday, March 1, 2007 8: 06 PM CHAPTER

Ngày đăng: 05/08/2014, 10:20

TỪ KHÓA LIÊN QUAN