📄 x2259.html
字号:
NAME="AEN2325">大小磋商</H2><P>在包含构件的窗口第一次被显示前和每当窗口布局改变时,GTK 会询问每个子构件所期望的大小。函数 <TTCLASS="LITERAL">gtk_dial_size_request()</TT> 处理这个请求。因为我们的构件不是一个容器构件,且在其上也没有容器构件,我们仅仅返回一个合理的缺省值。</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">static void gtk_dial_size_request (GtkWidget *widget, GtkRequisition *requisition){ requisition->width = DIAL_DEFAULT_SIZE; requisition->height = DIAL_DEFAULT_SIZE;}</PRE></TD></TR></TABLE><P>在所有的构件已经请求了一个想要的大小之后,就开始计算窗口的布局,且每个子构件被告知它们实际的大小。通常,它至少是请求的大小,但是,如果,比如用户调整了窗口的大小,它偶尔可能小于请求的大小。函数 <TTCLASS="LITERAL">gtk_dial_size_allocate()</TT> 处理大小通知。注意在计算部件将要使用的大小的同时,这个例程也把构件的 X 窗口移到新位置和和设置新的大小。</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">static voidgtk_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation){ GtkDial *dial; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) { dial = GTK_DIAL (widget); gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); dial->radius = MAX(allocation->width,allocation->height) * 0.45; dial->pointer_width = dial->radius / 5; }}</PRE></TD></TR></TABLE></DIV><DIVCLASS="SECT2"><H2CLASS="SECT2"><ANAME="AEN2333"><TTCLASS="LITERAL">gtk_dial_expose()</TT></H2><P>像前面讲的一样,构件的所有的绘制在暴露事件处理函数里做。这里不需要多讲,除了它用三维阴影法,按照存储在构件的风格里的颜色,用函数 <TTCLASS="LITERAL">gtk_draw_polygon</TT> 绘制表的指针。</P><TABLEBORDER="0"BGCOLOR="#E0E0E0"WIDTH="100%"><TR><TD><PRECLASS="PROGRAMLISTING">static gintgtk_dial_expose (GtkWidget *widget, GdkEventExpose *event){ GtkDial *dial; GdkPoint points[3]; gdouble s,c; gdouble theta; gint xc, yc; gint tick_length; gint i; 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); if (event->count > 0) return FALSE; dial = GTK_DIAL (widget); gdk_window_clear_area (widget->window, 0, 0, widget->allocation.width, widget->allocation.height); xc = widget->allocation.width/2; yc = widget->allocation.height/2; /* 绘制刻度 */ for (i=0; i<25; i++) { theta = (i*M_PI/18. - M_PI/6.); s = sin(theta); c = cos(theta); tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2; gdk_draw_line (widget->window, widget->style->fg_gc[widget->state], xc + c*(dial->radius - tick_length), yc - s*(dial->radius - tick_length), xc + c*dial->radius, yc - s*dial->radius); } /* 绘制指针 */ s = sin(dial->angle); c = cos(dial->angle); 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="AEN2339">事件处理</H2><P>我们的构件还剩下处理各种类型的事件的代码,但我们会发现和许多其它 GTK 程序里的没多大区别。可以产生两种类型的事件,一个是用户可以点击构件并拖动指针,另一个是通过外部的情况来改变调整对象的值。</P><P>当用户点击构件时,我们检查看这个点击是否是在表盘的指针里,如果是这样,把用户所点击的按钮存入构件结构的 <TTCLASS="LITERAL">button</TT> 域,并且调用 <TTCLASS="LITERAL">gtk_grab_add()</TT> 强占所有鼠标事件。随后的鼠标移动引发控制值被重新计算(通过函数 <TTCLASS="LITERAL">gtk_dial_update_mouse</TT>)。按照已经设定的方式(policy),"value_changed" 事件被立即产生 (<TTCLASS="LITERAL">GTK_UPDATE_CONTINUOUS</TT>),在用<TTCLASS="LITERAL">gtk_timeout_add()</TT>添加的定时器里定义的一段延迟后 (<TTCLASS="LITERAL">GTK_UPDATE_DELAYED</TT>),或只在按钮被释放时 (<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); /* 判断按钮是否是在表盘指针你按下 - 我们通过计算鼠标按下 点到表盘指针中线的水平和垂直距离来判断。 */ 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>通过外部方式产生的对Adjustment的改变通过 "changed" 和 "value_changed" 信号传到我们的构件。处理这些事情的处理函数将调用<TTCLASS="LITERAL">gtk_dial_update()</TT>来验证参数,计算新的表盘指针角度,并重新绘制构件 (通过调用 <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="AEN2355">可能的增强</H2><P>迄今为止我们描绘的Dial构件有大约670行代码。不过我们真正完成的只有一点点,头文件和模板占了其中的很大一部分。然而,对这个构件还有很多地方可以进行增强。</P><P></P><UL><LI><P>如果你试一下这个构件,你会发现当拖动pointer转圈的时候有闪烁。这是因为每次表盘指针移动,整个构件在重绘前都要被擦除。最好的处理这个问题的方法就是把这些变化绘制到一个不显示在屏幕上的pixmap上,然后一步将最后结果直接复制到屏幕上。(进度条构件就是以这种方式绘制它自身。)</P></LI><LI><P>用户应该可以通过上下光标键来增加或减少这个值。</P></LI><LI><P>最好让构件有一些按钮来小步或大步增加或减少这个值。虽然有可能用你含的(embedded)按钮来实现这个,但我们还是想让这个按钮在持续被按下的时候认为用户按下了很多次,就像滚动条上的箭头一样。在范围构件的代码中可以找到实现这种动作的大部分代码。</P></LI><LI><P>表盘构件可以做成一个容器构件,在以上所述的按钮中间表盘构件的底部放置一个简单的子构件。用户可以自己选择加入一个标签或文本输入构件来显示表盘的当前值。</P></LI></UL></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLESUMMARY="Footer navigation table"WIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="x2152.html"ACCESSKEY="P"><<< Previous</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="book1.html"ACCESSKEY="H">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="x2367.html"ACCESSKEY="N">Next >>></A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">创建一个复合构件</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="c2132.html"ACCESSKEY="U">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">深入的学习</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -