Cocoa Subclassing: Creazione di un HUD

scritto da Francesco Germinara

 

Introduzione

Il lavoro di seguito presentato vuole essere un semplice inizio per la realizzazione di controlli di interfaccia utenti (Pulsanti, Slider, ecc.) di tipo “PRO”, ossia con una interfaccia grafica diversa da quella standard Acqua, simile a quella visibile in Aperture o DVD Player ecc.

 

Contemporaneamente, vuole essere una guida iniziale alla realizzazione di tecniche di subclassing dei controlli e delle classi standard cocoa.

 

Chiunque lo desideri può contribuire a questo progetto inviandomi, il proprio materiale o implementando nuovi controlli che saranno aggiunti alle classi base.

 

Se altri programmatori cocoa si vogliono cimentare nella realizzazione di una palette per IB con i controlli Hud presenti in questo lavoro (ed in quelli che spero seguiranno...), sarebbe una bella cosa J ...

 

Definizioni

HUD = Heads Up Display è un pannello di controllo, traslucido, che implementa uno o piu’ controlli utente che rimane aperto durante l’esecuzione del programma per consentire all’utente di modificare determinati parametri.

Un esempio di un pannello HUD è il seguente, (preso da Aperture  della Apple)

 

Le parti componenti di un HUD sono

Una finestra con:

 quattro angoli arrotondati

 una zona grigio chiaro per il titolo

 una zona grigio scuro (quasi nera) per il resto

 un titolo con scritta in bianco

 un pulsante di chiusura

 semitrasparente

 uno o piu’ controlli utente con attribuiti visivi

 “compatibili e gradevoli con il resto della finestra”

 

Come ho pensato di procedere

Per prima cosa ho deciso di creare una versione di finestra “NSWindow” personalizzata e quindi ho subclassato la classe NSWindow creando la FGHUDWindow

Lo scopo fondamentale della nuova classe, è quello di creare una finestra senza sfondo e senza bordo e di renderla visibile sopra tutte le altre.

Inoltre, tale classe, si occupa di creare un Pulsante (NSButton) per la chiusura della finestra stessa.

 

Successivamente ho bisogno di creare una classe NSView personale da appiccicare alla FGHUDWindow e quindi ho subclassato una NSView standard creando la FGHUDView

 

Lo scopo fondamentale di questa vista è quello di disegnare lo sfondo cosi’ come richiesto dalle caratteristiche dell’HUD in precedenza analizzate, quindi angoli arrotondati, due colori ed il titolo della finestra e fornire due metodi per impostare il testo del titolo ed un eventuale testo di tooltip.

 

//Imposto l'eventuale testo di tool tip

-(void) setToolTipText:(NSString *) aString;

//Imposto il titolo

-(void) setTitle:(NSString *) aString;

 

Nota: per poter disegnare la finestra con i rettangoli arrotondati ho creato una “Categoria” , ossia una classe che espande le funzionalità di una altra classe già esistente, che si chiama FGNSBezierPathAddition

 

Tale categoria implementa i metodi bezierPathWithRoundedRectBottom e bezierPathWithRoundedRectTop usati per disegnare lo sfondo della vista

 

In tale categoria sono inoltre presenti i metodi

customHorizontalFillWithCallbacks

linearGradientFillWithStartColor

bilinearGradientFillWithOuterColor

che ho preso in prestito da internet e di cui non conosco l’autore, che comunque ringrazio.
Questi metodi mi serviranno per disegnare gli oggetti usando un riempimento lineare  o bilineare invece di un solo colore piatto, in modo da dare un’immagine più gradevole al controllo.

 

Una volta che ho la finestra e la vista, ho un pannello HUD vuoto, quindi procedo con il subclassare i vari controlli che voglio implementare nella mia finestra.

Per iniziare ho subclassato un NSSlider.

 

Per subclassare un controllo standard, occorre in realtà subclassare due diversi elementi, il controllo e la cella del controllo, quindi nel caso specifico ho dovuto subclassare NSSlider (il controllo) e NSSliderCell (la cella).

 

Le mie nuove classi personalizzate quindi si chiamano FGHudSlider e FGHudSliderCell

 

Subclassing della NSSlider -> FGHudSlider

L’unico motivo per cui occorre subclassare tale controllo è per dire che deve usare una Cella personalizzata (nel mio caso, la FGHudSliderCell).
Per fare cio’ occorre “sovrascrivere” il metodo
+ (Class)cellClass in modo che esso restituisca una cella di tipo FGHudSliderCell.

 

Subclassing della NSSliderCell -> FGHudSliderCell

La nuova classe si occupa invece di disegnare il controllo, di conseguenza viene realizzato uno slider orizzontale che usa i colori base del mio Hud e per il quale è possibile richiamare i seguenti metodi per modificarne in qualche modo il comportamento o il disegno del controllo stesso.

setUseGradient    

setUseCustomKnob

setUseFillColorSel

setFillColorSel

 

Ho subclassato il controllo NSButton e di conseguenza NSButtonCell, per poter gestire un pulsante adatto al mio pannello Hud ed ho realizzato le classi FGHudButton e FGHudButtonCell

 

Subclassing della NSButton -> FGHudButton

Lo scopo principale è ovviamente quello di “sovrascrivere” il metodo cellClass, in modo da fare restituire una Cella di tipo FGHudButtonCell

 


Ho inoltre creato i seguenti metodi per gestire diversi tipi di pulsanti

 

setCellButtonType

setImageButtonWithName

setTitle

 

Per concludere questa prima parte, riepilogo le attività e gli oggetti creati

 

Organization Chart

 

 

MODO DI UTILIZZO

Insieme a questo documento, trovare un esempio completo di progetto in XCODE 2.1 /2.2 nel quale sono presenti sia le classi citate sia due ulteriori classi (una classe delegata per l’oggetto Finestra Principale) e un oggetto Test per verificare che i messaggi dei vari controlli (clic, spostamento del know dello slider ecc. siano correttamente ricevuti).

 

In sintesi per proceder occorre da Interface builder importare i files .h (header) degli oggetti personalizzati FGHudxxxx, quindi tramite Layout Manager specificare il tipo corretto di classe da utilizzare per subclassare il controllo o la finestra o la vista.

 

Esempio:

 

Clicco sulla NSWindow, layout manager mi dice che è una NSWidow, invece la cambio in FGHudWindow, quindi metto una vista, NSView e sempre tramite layout manager dico che è una FGHudView, quindi metto le varie viste una per ogni controllo e con layout manager dico che tipo di controllo subclassano (es. FGHudSlider e FGHunButton).

 

Creo una classe delegata per la finestra principale e creo gli outlet per i vari elementi personalizzati che voglio gestire da codice.  Questo è un esempio della mia classe delegata che trovare nel progetto

 

//Impostazione dati di personalizzazione della finestra HUD

 

//Nota: per utilizzare le classi

 

// 1) In IB - importo il files .h delle classi FGHudxxxx

// 2)       - tramite layout manager, cambio le classi agli oggetti standard (subclass)

// 3)       - creo oggetto delegato della window (questo)

// 4)       - collego la FGHudWindow e la FGHudView agli oultet di questa classe

// 5)       - imposto i dati via codice nella awakeFromNib di questa classe

 

#import <Cocoa/Cocoa.h>

 

#import "FGHUDWindow.h"

#import "FGHUDView.h"

#import "FGHudButton.h"

 

@interface theMainWindowDelegate : NSObject

{

    IBOutlet FGHUDView *theFGHudView;

    IBOutlet FGHUDWindow *theFGHudWindow;

      IBOutlet FGHudButton *btnTest1;

      IBOutlet FGHudButton *btnTest2;

      IBOutlet FGHudButton *btnTest3;

      IBOutlet FGHudButton *btnTest4;

}

@end

 

E questa è l’implementazione

 

#import "theMainWindowDelegate.h"

 

@implementation theMainWindowDelegate

 

- (void) awakeFromNib

{

 [theFGHudView setToolTipText:@"Sono una FGHudView..."];

 [theFGHudView setTitle:@"Dimostrazione Hud"];

 

 [btnTest1 setImageButtonWithName:@"sliderMax.tiff"];

 [btnTest1 setCellButtonType:1];

 [btnTest2 setCellButtonType:2];

 [btnTest3 setCellButtonType:3];

 [btnTest4 setCellButtonType:4];

 [btnTest4 setTitle:@"Test"];

}

@end

 

A presto e spero che l’argomento sia di interesse e che la spiegazione sufficientemente chiara.

 

F.Germinara

www.germinara.it

info@germinara.it

 

Il link del progetto è il seguente http://www.germinara.it/FGBseHUDPart1.zip