📄 gtk_tut_it-19.html
字号:
/* Draw pointer */ 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></CODE></BLOCKQUOTE><P><H3>Gestione degli eventi</H3><P><P>Il resto del codice del widget manipola vari tipi di eventi, e nonè differente da quello che può essere trovato in molte applicazioneGTK. Due tipi di eventi possono verificarsi: l'utente può clickare sul widget con il mouse e trascinare per muovere il puntatore, o il valore dell'oggetto Adjustmente può cambiare a causa di alcunecircostanze esterne.<P><P>Quando l'utente clicka sul widget, noi vediamo se la pressioneera veramente vicina al puntatore, e se così, memorizziamo il bottonepremuto dall'utente con il campo <CODE>button</CODE> della struttura delwidget, e prendiamo tutti gli eventi del mouse con una chiamata allafunzione <CODE>gtk_grab_add()</CODE>. Successivi movimenti del mouse causano il ricalcolo dei valori di controllo (fatto dalla funzione <CODE>gtk_dial_update_mouse</CODE>). Dipendentemente dalla politica che abbiamostabilito, gli eventi ``value_changed'' possono essere generatiistantaneamente (<CODE>GTK_UPDATE_CONTINUOUS</CODE>), dopo un certo tempo aggiuntocon la funzione <CODE>gtk_timeout_add()</CODE> (<CODE>GTK_UPDATE_DELAYED</CODE>), osolamente quando il bottone del mouse e' rilasciato (<CODE>GTK_UPDATE_DISCONTINUOUS</CODE>).<P><BLOCKQUOTE><CODE><PRE>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); /* Determina se il bottone premuto era dentro la regione del puntatore: lo facciamo calcolando la distanza parallela e perpendicolare dal punto dove il bottone del mouse e' stato premuto alla linea passante per il puntatore. */ 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></CODE></BLOCKQUOTE><P><P>Cambiamenti esterni all'Adjustment sono comunicati al nostro widgetdai segnali ``changed'' e ``value_changed''. Il gestore per queste funzioni chiama <CODE>gtk_dial_update()</CODE> per validare gliargomenti, calcolare il nuovo angolo del puntatore e ridisegnare ilwidget (chiamando <CODE>gtk_widget_draw()</CODE>).<P><BLOCKQUOTE><CODE><PRE>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></CODE></BLOCKQUOTE><P><H3>Possibili Miglioramenti</H3><P><P>Il widget Dial, da come l'abbiamo costruito, è lungo circa 670 lineedi codice C. Anche se questo potrebbe sembrare un po' troppo, abbiamorealmente fatto un bel po' con quel tanto di codice, specialmente considerando che molta della lunghezza è costituita da file header ecommmenti. Comunque ci sono alcuni miglioramenti che potrebbero esserefatti a questo widget:<P><UL><LI> Se tu provate questo widget, troverete che ci sono alcuni lampeggiamentiquando il puntatore viene trascinato in giro. Questo perchè l'intero widget è cancellato ogni volta che il puntatore viene mosso, prima di essere ridisegnato. Spesso, il modo miglioreper gestire questo tipo di problema è il disegnare il tutto su una pixmap non visibile, poi copiare il risultato finale sullo schermo in una passata sola (il widget ProgressBar viene disegnato in questomodo).</LI><LI> L'utente potrebbe essere abilitato ad usare le frecce su e giu perincrementare e diminuire il valore.</LI><LI> Potrebbe essere carino se il widget avesse i bottoni per incrementare e decrementare il valore di step. Anche se potrebbe esserepossibile usare dei widget Bottone incorporati per questo, possiamo anchefar sì che il bottone sia auto-ripentente quando premuto, come le freccein una barra di scorrimento. Molto del codice per implementare questo tipo di comportamento può essere trovato nel widget GtkRange.</LI><LI> il widget Dial potrebbe essere fatto/creato dentro un widget contenitore con un singolo widget figlio posizionato all'inizio tra i 2 bottoni menzionati prima. L'utente potrebbe poi aggiungere o una etichettao un widget ``entry'' per mostrare il valore corrente del dial.</LI></UL><P><H2><A NAME="ss19.5">19.5 Impararne di più</A></H2><P> Fin qui abbiamo esposto solo una piccola parte di tutto quello che serveper creare un widget. Se volete davvero scrivere un vostro widget, lamiglior risorsa di esempi è lo stesso codice sorgente GTK. Chiedete a voistessi alcune cose su come deve essere il widget che volete scrivere: èun widget contenitore? dovrà avere una propria finestra? è una modifica di un widget precedente? Trovate poi un widget simile e iniziate a fargli delle modifiche.Buone Fortuna.<P><HR NOSHADE><A HREF="gtk_tut_it-20.html">Avanti</A><A HREF="gtk_tut_it-18.html">Indietro</A><A HREF="gtk_tut_it.html#toc19">Indice</A></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -