Corso di Visual C++

Cambiare forma al cursore del mouse

 

Nell'ultima puntata abbiamo visto come effettuare la cattura del mouse, cosa questa molto utile a coloro che chiedono qualcosa in più alle proprie applicazioni; questa puntata è invece dedicata a quelli che vogliono proprio poterci fare tutto!.

La prima cosa che potrebbe saltarci in mente è di cambiarne la forma con un'altra o disegnata da noi o predefinita di Windows.

Diciamo subito che la risorsa cursore è associata alla struttura WNDCLASS della classe finestra; ricordo brevemente che una classe finestra è una qualsiasi classe derivata da CWnd come ad esempio CEdit, CDialog, CToolBar...

Per registrare una classe con la struttura WNDCLASS bisogna utilizzare la funzione AfxRegisterWndClass, il cui prototipo è il seguente:

LPCTSTR AFXAPI AfxRegisterWndClass( UINT nClassStyle, HCURSOR hCursor = 0, HBRUSH hbrBackground = 0, HICON hIcon = 0 );

 

Il valore di ritorno è il nome della Window Class registrata e che bisogna passare alla funzione Create di CWnd per creare la finestra.

Per ora il parametro che ci interessa è il secondo, hCursor, l'handle ad una risorsa di tipo cursore.

Nota che se viene lasciato a zero, viene caricato il cursore standard IDC_ARROW ( il cursore a forma di freccia ).

Questo handle può essere recuperato o dai cursori standard di Windows o da un nostro cursore personalizzato.

Ad esempio se vogliamo caricare il cursore a croce, il cui ID è IDC_CROSS, scriviamo una cosa del genere:

hCursor = AfxGetApp()->LoadStandardCursor(IDC_CROSS);

Se vogliamo invece caricare il cursore che noi abbiamo creato con l'editor di risorse e che abbiamo nominato IDC_MYCURSOR, scriviamo quanto segue:

hCursor = AfxGetApp()->LoadCursor(IDC_CROSS);

Al posto dell'identificativo numerico, a LoadCursor può essere passato anche la stringa che identifica la risorsa.

Il comportamento che Windows ha per i cursori è molto semplice.

Quando il cursore si sposta, viene cancellato dalla vecchia posizione (ripristinando lo sfondo precedente al passaggio del mouse ) e disegnato in quella nuova ( bella scoperta!! ).

Prima però di essere ridisegnato in quella nuova si va a testare il tipo della nuova destinazione: se non è un'area client di qualche finestra viene disegnato un cursore a freccia ( IDC_ARROW ), in caso contrario viene utilizzato il cursore standard associato a quella classe ( il cui handle è stato specificato nella struttura WND_CLASS ).

Prima di disegnare il cursore giusto però Windows richiama la funzione ::SetCursor che provvede anche a restituire l'handle del cursore precedentemente utilizzato.

Eccone il prototipo:

HCURSOR SetCursor(HCURSOR hCursor);

Ad esempio vi sarà capitato di notare che quando spostiamo il puntatore del mouse in un controllo di modifica, questo si trasforma in una barretta verticale, no?. Questo accade perché il cursore di default della classe CEdit è il cursore IDC_IBEAM, da non confondere con IDC_BEAN che è il cursore a fagiolo :-)

Appare subito l'evidente limitazione di poter utilizzare, con questo modo, un solo tipo di cursore. Per ovviare a tale problema sembrerebbe ragionevole scavalcare la funzione SetCursor con una personalizzata ad hoc.

In quest'ultima, quello che dobbiamo fare è testare la posizione del mouse, se è nell'area client sostituiamo il cursore con quello nostro, altrimenti richiamiamo l'implementazione di default; es:

BOOL CMyView::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)

{

if (nHitTest == HTCLIENT)

{

SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));

return TRUE;

}

return CFormView::OnSetCursor(pWnd, nHitTest, message);

}

In questo caso la classe finestra è derivata da CFormView e quando il cursore è nell'area client vogliamo che sia visualizzato il cursore a croce. Viene ritornato TRUE nel caso in oggetto per notificare che il cursore è stato assegnato e non c'è bisogno di ulteriori elaborazioni del messaggio spedito WM_SETCURSOR.

In questo caso non abbiamo fatto molto oltre che settare il cursore, ma prima di farlo avremmo potuto testare delle condizioni per scegliere la forma più opportuna. Ad esempio avremmo potuto scegliere di utilizzare un cursore a freccia se una certa variabile booleana vale TRUE o un cursore di attesa se questa vale FALSE, o ancora scegliere un cursore diverso in dipendenza della sua posizione.

Nella prossima puntata vedremo come nascondere il cursore, come visualizzare il cursore a clessidra e come rilevare lo stato dei pulsanti del mouse.

 

Torna all'indice Generale del corso di Corso di Visual C++ di Software Planet