2009-10-06 13 views
7

Sto cercando di fare una finestra personalizzata che sarebbe simile a questa (non importa il photoshop rapida):Come creare una finestra trasparente con pulsanti non rettangolari?

alt text http://i35.tinypic.com/20ff803.png

Il problema principale che ho è di non rendere la finestra trasparente (anche se immagino qualsiasi informazioni sono benvenute!), ma che voglio poter fare clic solo sulla parte visibile dei pulsanti. Ad esempio, se clicco appena fuori dalla parte superiore sinistra del pulsante "5", voglio che registri un clic sul pulsante "1" e non sul pulsante "5", anche se ho fatto clic sul riquadro di selezione del pulsante 5. E se clicco sul lato "angolo" in alto a sinistra del pulsante "1", voglio che faccia clic su ciò che è sotto la mia finestra e non sul pulsante. Devo anche essere in grado di ridimensionare questa finestra e le immagini nei pulsanti dovrebbero essere ridimensionate con esse.

Stavo pensando di utilizzare un NSWindow trasparente con NSButtons trasparenti contenenti PNG con alfa per ciascun pulsante ma penso che i pulsanti sovrapposti costituiranno un problema.

Ho anche sentito parlare di applicazioni come Bowtie, SweetFM e altri che utilizzano WebKit per visualizzare le loro interfacce. Se questa è una possibilità su almeno 10.3 sarebbe interessante, ma non so come funzionerebbe.

Ho cercato maschere, aree o qualcosa del genere, ma non ho ancora trovato nulla. Aggiornerò questo post come trovo più informazioni (se lo faccio!).

Eventuali suggerimenti su come procedere?

(Si noti che sto lavorando su 10.4 e idealmente ho bisogno di sostenere alcune vecchie versioni di Mac OS X anche.)

(Inoltre, ho trovato il "arrotondata finestra" esempio sui dev di Apple. Sito ma non funziona più su 10.4 quindi non sono sicuro che questa sia la strada da percorrere.)

Grazie in anticipo!

UPDATE:

(vedi aggiornamento 2, questo problema è stato risolto ora)

OK ora sto avendo un problema leggermente diverso (ma non indipendente). Quello che ho fatto finora è questo:

ho optato per fare un controllo personalizzato che sarò in grado di utilizzare per ciascuno dei miei pulsanti:

1- sottoclasse NSControl e sovrascrivere drawRect, mouseUp, mouseDragged e mouseDown . In questo modo posso ottenere il comportamento desiderato durante il trascinamento di un pulsante (sposta la finestra).

2- Caricare le immagini per ciascuno stato del pulsante (stessa forma ma una è scurita) in NSImages e disegnarle nella vista utilizzando compositeToPoint:fromRect:operation: quando necessario. Le immagini sono 185 x 185 pixel file PNG con alpha esportati da Photoshop.

3- Diventa rappresentazione bitmap del NSImage:

NSBitmapImageRep *bitRep = [[NSBitmapImageRep imageRepWithData:[unpressedImage TIFFRepresentation]] retain]; 

O

NSBitmapImageRep *bitRep = [[unpressedImage representations] objectAtIndex:0]; 

Entrambi sembrano dare lo stesso risultato. Ho provato a comporre questo rep bitmap sulla mia finestra per vedere se l'immagine ha le stesse dimensioni e l'aspetto dell'originale e tutto sembra buono (vedrai perché l'ho fatto dopo questa enumerazione ...).

4- Nei metodi di eventi del mouse, io uso la rappresentazione bitmap per ottenere il componente alfa per un particolare punto:

NSPoint mouse = [self convertPoint:[theEvent locationInWindow] fromView:nil]; // Returns the correct x and y positions on the control (i.e: between 0 and 185 for each axis). 

NSColor *colorUnderMouse = [[[unpressedImage representations] objectAtIndex:0] colorAtX:mouse.x y:mouse.y]; 
float alpha = [colorUnderMouse alphaComponent]; 

Poi stampare il valore alfa per la console, ma sto ottenendo risultati molto strani . Ottenere il componente alfa e il colore sotto il mouse sembra funzionare a prima vista (i colori e l'alfa restituiti si trovano nella mia bitmap) ma sembra che il colore non sia preso al punto giusto nello bitRep, o l'immagine contenuta nel bitRep è distorto (non è distorto quando compongo lo bitRep sulla finestra, il che è molto strano). Ancora una volta ho ricontrollato che le coordinate xey del mouse erano comprese nell'intervallo (0,185) e che la dimensione dello NSBitmapImageRep è 185 x 185.

Prima sembra che lo NSBitmapImageRep sia ruotato orizzontalmente rispetto al mio NSImage. Se capovolgo le mie coordinate i valori restituiti sembrano in gran parte OK (la maggior parte della forma sembra adattarsi ai valori restituiti) ma ci sono ancora parti dell'immagine che restituiscono valori errati.

Sono incompetente a questo punto e non ho mai lavorato con NSBitmapImageRep prima forse mi sono semplicemente dimenticato di specificare alcune informazioni di formato sulla mia immagine che sarebbero il motivo per cui i valori sono disattivati. Qualcuno potrebbe chiarirlo per favore?

Grazie ancora!

UPDATE 2:

Ok non importa, ho trovato il problema. Ho usato il metodo setColor: atX: y: per disegnare un punto colorato sul bitRep prima di stamparlo sulla mia finestra e il problema era chiaro come giorno: la coordinata Y è invertita nel bitRep, ma l'immagine è stampata con il "orientamento. Ciò significa che il bitRep ha un sistema di coordinate dell'origine in alto a sinistra "stile Windows" mentre la finestra ha l'origine del sistema di coordinate cartesiane (in basso a sinistra). Semplicemente invertendo la mia coordinata Y ho risolto i problemi con cui avevo ottenuto il componente alfa e il colore.

Questo deve essere stato scritto da qualche parte nei documenti, mi dispiace di averti disturbato a causa della mia mancanza di ricerca ...!

risposta

3

Che dire sostituendo i metodi NSResponder evento (mouseUp:, mouseDown:, ecc) nei tuoi pulsanti personalizzati (presumibilmente si sarebbe sottoclassi NSControl o NSButton per fare questi)? In questi metodi puoi calcolare correttamente il tuo rettangolo di delimitazione (o cerchio, in questo caso) e fare un semplice hit test con le coordinate del clic per vedere se il clic deve essere gestito.

Potresti anche essere in grado di trovare aiuto nei documenti di gestione degli eventi di Apple, in particolare su mouse events e custom views.

+0

Grazie, sarebbe una possibilità per il pulsante centrale, ma con i pulsanti esterni sarebbe più difficile e richiedere un sacco di codice di controllo dei limiti ... cosa succede se cambio le posizioni del mio pulsante per qualche motivo?(questo non è un progetto personale, quindi questo potrebbe essere soggetto a cambiamenti, purtroppo) Quindi dovrei riscrivere la maggior parte del mio codice di controllo vincolante ... Ci sarebbe un modo per farlo con una sorta di maschera? Dite "lascia passare il clic se la bitmap è trasparente a questo punto"? – Form

+0

Non penso che tu possa farlo con una maschera come stai pensando (rilevamento della trasparenza, ecc.). Queste sono forme piuttosto semplici (cerchi parziali per i pulsanti esterni sotto un cerchio completo per l'interno). Puoi accedere alle coordinate e alle dimensioni correnti dei controlli nel codice e utilizzarli per calcolare i limiti di hit in modo da non dover codificare nulla. Usa il fatto che il pulsante centrale è in alto a tuo vantaggio. –

+0

È possibile utilizzare il rilevamento trasparenza (alfa) e funziona molto bene e anche se il metodo che sto usando per ora è 10.4+ c'è un modo per farlo usando qualcos'altro che colorAtX: y: finché si ha accesso al bitmapRep, come ha sottolineato Mahboudz. – Form

2

Che ne dici di rendere tutto un unico controllo?Potrebbe rendere il disegno un po 'complesso, ma sembra che tu stia facendo comunque disegni personalizzati.

Se lo facevi come controllo singolo, puoi prima controllare e vedere se il punto di scatto si trova all'interno del cerchio centrale. Se non lo è, tutto ciò che devi fare è identificare il quadrante in cui si trova per sapere a quale "pulsante" appartiene (verificando anche che il clic si adatti al cerchio esterno).

In alternativa, è possibile creare una vista trasparente sui pulsanti che acquisisce gli eventi di clic e li inoltra al controllo appropriato in base alla logica appena specificata.

2

Analogamente alla risposta di Marc W, nel proprio metodo di evento è possibile controllare il valore alfa del clic sulla bitmap e ignorare se l'alfa è inferiore a un determinato valore.

Per ottenere la bitmap, read this.

Ora si dovrebbe avere un puntatore bitmap come questo (pseudocodice - dovrete riempire i pezzi):

pixels = CGBitmapContextGetData(ctx); // there's actually two ways to get pixels in the above Apple tech note 

Poi, si può fare questo per ottenere il pixel che ti interessa, e testarlo:

// I'm assuming each pixel is 24 bits of color and one byte of alpha 
#define getRed(p) ((p) & 0x000000FF) 
#define getGreen(p) ((p) & 0x0000FF00) >> 8 
#define getBlue(p) ((p) & 0x00FF0000) >> 16 
#define getAlpha(p) ((p) & 0xFF000000) >> 24 

CGPoint pixPt; 
long pixel; 
pixPt = getMouseClick(); 

pixel = pixels[pixPt.x + pixPt.y * bytesPerRow]; 

if (getAlpha(pixel) < 25) 
    // you've clicked on transparent pixels, alpha of 10% or less (alpha ranges from 0-255) 

Questo apre alcune possibilità per voi, come quello di avere un anello attorno al cerchio interno che è inerte e non appartenere a nessuno dei quadranti o cerchio centrale. Certo, ci sono sempre altri modi per fare queste cose senza ricorrere al gioco dei pixel :-)

+0

Sembra più come quello che mi piacerebbe fare, in questo modo se la forma del mio controllo cambia dovrebbe essere regolata automaticamente. Esiste un metodo per verificare l'opacità di un'immagine in un punto specifico nell'immagine di un pulsante? (Penso che sarebbe meno un lavoro di sottoclasse di un NSButton invece della sottoclasse di NSView e del disegno manuale, ma se non è possibile verificare l'alpha con un pulsante probabilmente andrò con NSView). – Form

+0

Aggiungo informazioni sulla gestione delle bitmap nella mia risposta. – mahboudz

+0

Grazie mille, vedrò cosa posso fare con quello! – Form

1

Se si generano le forme nel codice utilizzando NSBezierPath, verificare se un clic si trova all'interno di una delle forme è un elemento singolo: invia la forma un messaggio containsPoint:.

Farei questa vista singola che possiede i cinque percorsi. Quando disegni, disegna il centro per ultimo; quando rispondi a un evento del mouse, esegui prima l'hit-test. Assegna a questa vista una proprietà di azione per segmento e opzionalmente una destinazione per segmento, a seconda di ciò che ti serve.

+0

La tua soluzione è molto interessante, non sapevo che avresti potuto provare NSBezierPaths come questo. Sarà sicuramente utile. Ma nel mio caso (con le forme che cambiano essendo un'idoneità non trascurabile) penso che sarebbe molto da riscrivere se cambierò qualcosa. Proverò prima il bitmap alpha-testing e se non funziona come spero lo proverò. Grazie per aver trovato il tempo di rispondere alla mia domanda! – Form

1

Avresti bisogno di fare due cose:

  1. esclusione di NSView hitTest: metodo restituisca NIL ogni volta che il click è all'esterno dell'immagine, in modo che il click di quella vista viene ignorato e passedto la vista sovrapponendola.
  2. Ignora mouseDown: e implementa il proprio loop di tracciamento del mouse (utilizzando NSEventTrackingRunLoopMode) che controlla anche se il punto si trova in un'area non trasparente. NSBitmapImageRep o NSImage ha un colorAtX: y: metodo o qualcosa come hat che puoi usare per quest'ultimo.
0

La soluzione Mu funziona solo su pulsanti non intersecati. Ho un pulsante non rettangolare, che deve cambiare le immagini da grigio a verde.

sample button posso cambiare tasto normale gradiente come questo

properties

Questo è tutto. Spero che questo aiuti qualcuno.

Problemi correlati