📄 sec-creatingawidgetfromscratch.html
字号:
points[0].x = xc + s*dial->pointer_width/2; points[0].y = yc + c*dial->pointer_width/2; points[1].x = xc + c*dial->radius; points[1].y = yc - s*dial->radius; points[2].x = xc - s*dial->pointer_width/2; points[2].y = yc - c*dial->pointer_width/2; gtk_draw_polygon (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, points, 3, TRUE); return FALSE;}</PRE></TD></TR></TABLE></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN2747">24.4.8. Event handling</A></H2><P>The rest of the widget's code handles various types of events, andisn't too different from what would be found in many GTKapplications. Two types of events can occur - either the user canclick on the widget with the mouse and drag to move the pointer, orthe value of the Adjustment object can change due to some externalcircumstance.</P><P>When the user clicks on the widget, we check to see if the click wasappropriately near the pointer, and if so, store the button that theuser clicked with in the <TTCLASS="LITERAL">button</TT> field of the widgetstructure, and grab all mouse events with a call to<TTCLASS="LITERAL">gtk_grab_add()</TT>. Subsequent motion of the mouse causes thevalue of the control to be recomputed (by the function<TTCLASS="LITERAL">gtk_dial_update_mouse</TT>). Depending on the policy that has beenset, "value_changed" events are either generated instantly(<TTCLASS="LITERAL">GTK_UPDATE_CONTINUOUS</TT>), after a delay in a timer added with<TTCLASS="LITERAL">gtk_timeout_add()</TT> (<TTCLASS="LITERAL">GTK_UPDATE_DELAYED</TT>), or only when thebutton is released (<TTCLASS="LITERAL">GTK_UPDATE_DISCONTINUOUS</TT>).</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">static gintgtk_dial_button_press (GtkWidget *widget, GdkEventButton *event){ GtkDial *dial; gint dx, dy; double s, c; double d_parallel; double d_perpendicular; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); /* Determine if button press was within pointer region - we do this by computing the parallel and perpendicular distance of the point where the mouse was pressed from the line passing through the pointer */ dx = event->x - widget->allocation.width / 2; dy = widget->allocation.height / 2 - event->y; s = sin(dial->angle); c = cos(dial->angle); d_parallel = s*dy + c*dx; d_perpendicular = fabs(s*dx - c*dy); if (!dial->button && (d_perpendicular < dial->pointer_width/2) && (d_parallel > - dial->pointer_width)) { gtk_grab_add (widget); dial->button = event->button; gtk_dial_update_mouse (dial, event->x, event->y); } return FALSE;}static gintgtk_dial_button_release (GtkWidget *widget, GdkEventButton *event){ GtkDial *dial; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); if (dial->button == event->button) { gtk_grab_remove (widget); dial->button = 0; if (dial->policy == GTK_UPDATE_DELAYED) gtk_timeout_remove (dial->timer); if ((dial->policy != GTK_UPDATE_CONTINUOUS) && (dial->old_value != dial->adjustment->value)) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } return FALSE;}static gintgtk_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event){ GtkDial *dial; GdkModifierType mods; gint x, y, mask; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); if (dial->button != 0) { x = event->x; y = event->y; if (event->is_hint || (event->window != widget->window)) gdk_window_get_pointer (widget->window, &x, &y, &mods); switch (dial->button) { case 1: mask = GDK_BUTTON1_MASK; break; case 2: mask = GDK_BUTTON2_MASK; break; case 3: mask = GDK_BUTTON3_MASK; break; default: mask = 0; break; } if (mods & mask) gtk_dial_update_mouse (dial, x,y); } return FALSE;}static gintgtk_dial_timer (GtkDial *dial){ g_return_val_if_fail (dial != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE); if (dial->policy == GTK_UPDATE_DELAYED) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); return FALSE;}static voidgtk_dial_update_mouse (GtkDial *dial, gint x, gint y){ gint xc, yc; gfloat old_value; g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); xc = GTK_WIDGET(dial)->allocation.width / 2; yc = GTK_WIDGET(dial)->allocation.height / 2; old_value = dial->adjustment->value; dial->angle = atan2(yc-y, x-xc); if (dial->angle < -M_PI/2.) dial->angle += 2*M_PI; if (dial->angle < -M_PI/6) dial->angle = -M_PI/6; if (dial->angle > 7.*M_PI/6.) dial->angle = 7.*M_PI/6.; dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.); if (dial->adjustment->value != old_value) { if (dial->policy == GTK_UPDATE_CONTINUOUS) { gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } else { gtk_widget_draw (GTK_WIDGET(dial), NULL); if (dial->policy == GTK_UPDATE_DELAYED) { if (dial->timer) gtk_timeout_remove (dial->timer); dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, (GtkFunction) gtk_dial_timer, (gpointer) dial); } } }}</PRE></TD></TR></TABLE><P>Changes to the Adjustment by external means are communicated to ourwidget by the "changed" and "value_changed" signals. The handlersfor these functions call <TTCLASS="LITERAL">gtk_dial_update()</TT> to validate thearguments, compute the new pointer angle, and redraw the widget (bycalling <TTCLASS="LITERAL">gtk_widget_draw()</TT>).</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">static voidgtk_dial_update (GtkDial *dial){ gfloat new_value; g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); new_value = dial->adjustment->value; if (new_value < dial->adjustment->lower) new_value = dial->adjustment->lower; if (new_value > dial->adjustment->upper) new_value = dial->adjustment->upper; if (new_value != dial->adjustment->value) { dial->adjustment->value = new_value; gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / (dial->adjustment->upper - dial->adjustment->lower); gtk_widget_draw (GTK_WIDGET(dial), NULL);}static voidgtk_dial_adjustment_changed (GtkAdjustment *adjustment, gpointer data){ GtkDial *dial; g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL); dial = GTK_DIAL (data); if ((dial->old_value != adjustment->value) || (dial->old_lower != adjustment->lower) || (dial->old_upper != adjustment->upper)) { gtk_dial_update (dial); dial->old_value = adjustment->value; dial->old_lower = adjustment->lower; dial->old_upper = adjustment->upper; }}static voidgtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data){ GtkDial *dial; g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL); dial = GTK_DIAL (data); if (dial->old_value != adjustment->value) { gtk_dial_update (dial); dial->old_value = adjustment->value; }}</PRE></TD></TR></TABLE></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN2763">24.4.9. Possible Enhancements</A></H2><P>The Dial widget as we've described it so far runs about 670 lines ofcode. Although that might sound like a fair bit, we've reallyaccomplished quite a bit with that much code, especially since much ofthat length is headers and boilerplate. However, there are quite a fewmore enhancements that could be made to this widget:</P><P></P><UL><LI><P> If you try this widget out, you'll find that there is someflashing as the pointer is dragged around. This is because the entirewidget is erased every time the pointer is moved before beingredrawn. Often, the best way to handle this problem is to draw to anoffscreen pixmap, then copy the final results onto the screen in onestep. (The ProgressBar widget draws itself in this fashion.)</P></LI><LI><P> The user should be able to use the up and down arrow keys toincrease and decrease the value.</P></LI><LI><P> It would be nice if the widget had buttons to increase anddecrease the value in small or large steps. Although it would bepossible to use embedded Button widgets for this, we would also likethe buttons to auto-repeat when held down, as the arrows on ascrollbar do. Most of the code to implement this type of behavior canbe found in the Range widget.</P></LI><LI><P> The Dial widget could be made into a container widget with asingle child widget positioned at the bottom between the buttonsmentioned above. The user could then add their choice of a label orentry widget to display the current value of the dial.</P></LI></UL></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLEWIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="sec-creatingacompositewidget.html"><<< Previous</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="gtk-tut.html">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="sec-learningmore.html">Next >>></A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Creating a Composite widget</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="ch-writingyourownwidgets.html">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">Learning More</TD></TR></TABLE></DIV> </td> </tr></table> </td> </tr></table></body></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -