2009-10-06 5 views
7

Je cherche à faire une fenêtre personnalisée qui ressemblerait à quelque chose comme ça (jamais l'esprit le photoshop rapide):Comment créer une fenêtre transparente avec des boutons non rectangulaires?

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

Le principal problème que j'ai est de ne pas faire la transparence fenêtre (même si je suppose que tout info est la bienvenue!), mais que je veux pouvoir cliquer seulement sur la partie visible des boutons. Par exemple, si je clique juste en dehors du coin supérieur gauche du bouton "5", je veux qu'il enregistre un clic sur le bouton "1" et non sur le bouton "5", même si j'ai cliqué dans le cadre du bouton 5. Et si je clique juste en dehors du coin supérieur gauche du bouton "1", je veux qu'il clique sur ce qui est sous ma fenêtre et non sur le bouton. Je dois également être en mesure de redimensionner cette fenêtre et les images dans les boutons doivent redimensionner avec eux. Je pensais utiliser un NSWindow transparent avec NSButtons transparents contenant des PNG avec alpha pour chaque bouton mais je pense que les boutons qui se chevauchent seront un problème.

J'ai aussi entendu parler d'applications comme Bowtie, SweetFM et d'autres qui utilisent WebKit pour afficher leurs interfaces. Si c'est une possibilité sur au moins 10,3, ce serait intéressant, mais je ne sais pas comment cela fonctionnerait non plus. J'ai cherché des masques de clic, des zones ou quelque chose comme ça, mais je n'ai encore rien trouvé. Je mettrai à jour ce post car je trouve plus d'informations (si je le fais!).

Des conseils sur la façon de procéder?

(Notez que je travaille sur 10.4 et idéalement je dois soutenir certaines anciennes versions de Mac OS X aussi.)

(De plus, je trouve l'exemple « fenêtre arrondi » sur le dev d'Apple. Le site mais il ne fonctionne plus sur 10.4 donc je ne suis pas sûr que ce soit le chemin à parcourir.)

Merci d'avance!

MISE À JOUR:

(voir mise à jour 2, ce problème est résolu maintenant)

OK maintenant je vais avoir une question légèrement différente (mais pas sans rapport). Ce que je l'ai fait jusqu'à présent est la suivante:

J'ai opté pour faire un contrôle personnalisé que je serai en mesure d'utiliser pour chacun de mes boutons:

1- NSControl de sous-classe et passer outre drawRect, mouseUp, mouseDragged et mouseDown . De cette façon, je peux obtenir le comportement que je veux en faisant glisser sur un bouton (déplacer la fenêtre).

2- Chargez des images pour chaque état de bouton (la même forme mais une est assombrie) dans NSImages et dessinez-les dans la vue en utilisant compositeToPoint:fromRect:operation: si nécessaire. Les images sont 185 x 185 pixels Fichiers PNG avec alpha exportés à partir de Photoshop.

3- Obtenir la représentation bitmap du NSImage:

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

OU

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

Les deux semblent donner le même résultat. J'ai essayé de composer ce représentant bitmap sur ma fenêtre pour voir si l'image a la même taille et ressemble à l'original et tout semble bien (vous verrez pourquoi je l'ai fait après cette énumération ...).

4- Dans les méthodes d'événements de la souris, j'utilise la représentation bitmap pour obtenir le composant alpha pour un point particulier:

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]; 

Puis-je imprimer la valeur alpha à la console mais je reçois des résultats très étranges . Obtenir le composant alpha et la couleur sous la souris semble fonctionner à première vue (les couleurs et les alpha retournés sont dans mon bitmap) mais il semble que la couleur ne soit pas prise au bon endroit dans le bitRep, ou l'image contenue dans le bitRep est déformé (il n'est pas déformé quand je compose le bitRep sur la fenêtre, ce qui est très étrange). Encore une fois, j'ai revérifié les coordonnées de la souris x et y étaient dans la gamme (0,185) et que la taille de la NSBitmapImageRep est 185 x 185 aussi.

D'abord, il semble que le NSBitmapImageRep est inversé horizontalement par rapport à mon NSImage. Si je retourne mes coordonnées, les valeurs retournées semblent plutôt correctes (la plupart de la forme semble correspondre aux valeurs renvoyées) mais il y a encore des parties de l'image qui retournent des valeurs erronées.

Je suis clueless à ce stade et n'ayant jamais travaillé avec NSBitmapImageRep avant peut-être j'ai juste oublié de spécifier certaines informations de format sur mon image qui serait pourquoi les valeurs sont désactivées. Quelqu'un pourrait-il clarifier cela s'il vous plaît?

Merci encore!

MISE À JOUR 2:

Ok jamais l'esprit, je trouve la question. J'ai utilisé la méthode setColor: atX: y: pour dessiner un point coloré sur le bitRep avant de l'imprimer sur ma fenêtre et le problème était clair comme jour: la coordonnée Y est inversée dans le bitRep, mais l'image est imprimée avec le "orientation" Cela signifie que le bitRep a un système de coordonnées d'origine en haut à gauche "style Windows" tandis que la fenêtre a l'origine du système de coordonnées cartésiennes (en bas à gauche). Le simple fait d'inverser ma coordonnée Y a corrigé les problèmes que j'avais pour le composant alpha et la couleur.

Cela doit avoir été écrit quelque part dans les docs, désolé de vous avoir dérangé à cause de mon manque de recherche ...!

Répondre

3

Qu'en est-il primordial des méthodes d'événement NSResponder (mouseUp:, mouseDown:, etc.) dans vos boutons personnalisés (vous auriez vraisemblablement sous-classement NSControl ou NSButton pour les rendre)? Dans ces méthodes, vous pouvez calculer correctement votre rectangle de délimitation (ou un cercle, dans ce cas) et faire un simple test avec les coordonnées du clic pour voir si le clic doit être géré.

Vous pourriez également être en mesure de trouver de l'aide dans les documents de gestion des événements d'Apple, en particulier sur mouse events et custom views.

+0

Merci, ce serait une possibilité pour le bouton central, mais avec les boutons extérieurs qui seraient plus difficiles et qui nécessiteraient un tas de bornes-vérifier le code ... et si je change les positions de mon bouton pour une raison quelconque?(Ce n'est pas un projet personnel, donc cela peut être sujet à changement malheureusement) Ensuite, je devrais réécrire la plupart de mon code de contrôle limite ... Serait-il un moyen de le faire avec une sorte de masque? Dites "laisser passer le clic si le bitmap est transparent à ce stade"? – Form

+0

Je ne pense pas que vous puissiez le faire avec un masque comme vous le pensez (détection de transparence, etc). Ce sont des formes assez basiques (cercles partiels pour les boutons extérieurs sous un cercle complet pour l'intérieur). Vous pouvez accéder aux coordonnées et aux dimensions actuelles des contrôles dans le code et les utiliser pour calculer les limites d'accès afin de ne rien coder en dur. Utilisez le fait que le bouton central est sur votre avantage. –

+0

Il est possible d'utiliser la transparence (alpha) et cela fonctionne très bien et bien que la méthode que j'utilise pour le moment soit 10.4+, il y a un moyen de le faire en utilisant autre chose que colorAtX: y: à l'bitmapRep, comme l'a souligné mahboudz. – Form

2

Que diriez-vous d'en faire un seul contrôle?Cela peut rendre le dessin un peu complexe, mais il semble que vous fassiez du dessin personnalisé de toute façon.

Si vous l'avez fait en une seule commande, vous pouvez d'abord vérifier et voir si le point de clic dans le cercle central. Si ce n'est pas, tout ce que vous avez à faire est d'identifier quel quart il est en savoir qui « bouton » il appartient à (tout en vérifiant que le clic s'inscrit dans le cercle extérieur).

Sinon, vous pouvez créer une vue transparente sur vos boutons qui capture les événements de clic et les transmet sur le contrôle approprié en fonction de la logique que je viens spécifiée.

2

Semblable à la réponse de Marc W, dans votre propre méthode d'événement, vous pouvez vérifier la valeur alpha de la cliqué sur le bitmap et ignorer si l'alpha est inférieur à une certaine valeur.

Pour obtenir le bitmap, read this.

Vous devriez maintenant avoir un pointeur bitmap comme celui-ci (pseudocode - vous devrez remplir les pièces):

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

Ensuite, vous pouvez le faire pour obtenir le pixel qui vous intéresse, et tester:

// 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) 

Cela ouvre des possibilités pour vous, comme ayant un anneau autour de votre cercle à l'intérieur qui est inerte et n'appartenir à aucun des quarts de cercle ou le cercle du milieu. Bien sûr, il y a toujours d'autres façons de faire ces choses sans recourir à des querelles de pixels :-)

+0

Cela ressemble plus à ce que je voudrais faire, de cette façon, si la forme de mon contrôle change, elle devrait s'ajuster automatiquement. Existe-t-il une méthode pour vérifier l'opacité d'une image à un point spécifique de l'image d'un bouton? (Je pense que ce serait moins de travail de sous-classer un NSButton au lieu de sous-classer NSView et dessiner manuellement, mais s'il n'est pas possible de vérifier l'alpha avec un bouton, je vais très probablement aller avec le NSView). – Form

+0

J'ajoute des informations de gestion de bitmap dans ma réponse. – mahboudz

+0

Merci beaucoup, je vais voir ce que je peux faire avec ça! – Form

1

Si vous générez les formes dans le code en utilisant NSBezierPath, tester si un clic est à l'intérieur d'une des formes est un one-liner: envoie la forme un message containsPoint:.

je ferais cette vue unique qui possède les cinq voies. Lorsque vous dessinez, tracez le centre en dernier; Lorsque vous répondez à un événement de souris, effectuez un test de hit en premier. Donnez à cette vue une propriété d'action par segment, et éventuellement une cible par segment, en fonction de ce dont vous avez besoin.

+0

Votre solution est très intéressante, je ne savais pas que vous pourriez atteindre le test NSBezierPaths comme ça. Ça va sûrement venir à portée de main. Mais dans mon cas (avec les formes changeantes étant une propabilité non négligeable) je pense que ce serait beaucoup de réécriture si jamais je change quelque chose. Je vais d'abord essayer l'alpha-test bitmap et si ça ne marche pas, j'espère que ça ira. Merci d'avoir pris le temps de répondre à ma question! – Form

1

Vous auriez besoin de faire deux choses:

  1. override hitTest de NSView: méthode pour revenir NIL chaque fois que le clic est en dehors de l'image, de sorte que le clic dans ce point de vue est ignoré et passedto la vue qui se chevauchent il. Remplacer mouseDown: et implémenter votre propre boucle de suivi de la souris (utilisant NSEventTrackingRunLoopMode) qui vérifie également si le point est dans une zone non transparente. NSBitmapImageRep ou NSImage a un colorAtX: y: méthode ou quelque chose comme chapeau que vous pouvez utiliser pour ce dernier.
0

La solution Mu ne fonctionne que sur les boutons qui ne se croisent pas. J'ai un bouton non rectangulaire, qui doit changer les images du gris au vert.

sample button Je change bouton gradient régulier comme ce

properties

c'est tout. J'espère que ça aide quelqu'un.

Questions connexes