简体   繁体   English

如何使用非矩形按钮创建透明窗口?

[英]How to create a transparent window with non-rectangular buttons?

I am looking to make a custom window which would look something like this (never mind the quick photoshop): 我想制作一个看起来像这样的自定义窗口(不要介意快速的Photoshop):

替代文字

The main problem I have is not to make the window transparent (although I guess any info is welcome!), but that I want to be able to click only on the visible part of the buttons. 我的主要问题是不要让窗口透明(虽然我猜任何信息都是受欢迎的!),但我希望能够只点击按钮的可见部分。 For example, if I click just outside the top left of the "5" button, I want it to register a click on the "1" button and not the "5" button, even though I clicked in button 5's bounding box. 例如,如果我点击“5”按钮左上方的外部,我希望它在“1”按钮上注册,而不是“5”按钮,即使我点击了按钮5的边界框。 And if I click just ouside of the "1" button's top left "corner", I want it to click on whatever is beneath my window and not on the button. 如果我点击“1”按钮的左上角“”“,我希望它点击窗口下方的任何内容而不是按钮上的内容。 I also need to be able to resize this window and the pictures in the buttons should resize with them. 我还需要能够调整此窗口的大小,按钮中的图片应该随之调整大小。

I was thinking of using a transparent NSWindow with transparent NSButtons containing PNGs with alpha for each button but I think the overlapping buttons will be a problem. 我正在考虑使用透明的NSWindow,透明的NSButtons包含每个按钮的带有alpha的PNG,但我认为重叠按钮将是一个问题。

I have also heard about applications like Bowtie, SweetFM and others using WebKit to display their interfaces. 我也听说过Bowtie,SweetFM等应用程序以及其他使用WebKit显示其界面的应用程序。 If that is a possibility on at least 10.3 that would be interesting, but I don't know how that would work either. 如果这是至少10.3的可能性,这将是有趣的,但我不知道这将如何工作。

I looked around for click masks, areas or something like that but I haven't found anything yet. 我四处寻找点击面具,区域或类似的东西,但我还没有找到任何东西。 I will update this post as I find more info (if I do!). 我将更新这篇文章,因为我发现更多信息(如果我这样做!)。

Any hints on how to proceed? 关于如何进行的任何提示?

(Note that I am working on 10.4 and ideally I need to support some older versions of Mac OS X too.) (请注意,我正在使用10.4, 理想情况下我也需要支持一些旧版本的Mac OS X.)

(Also, I found the "rounded window" example on Apple's dev. site but it doesn't run on 10.4 anymore so I'm not sure this is the way to go.) (另外,我在Apple的开发网站上找到了“圆形窗口”示例,但它不再在10.4上运行,所以我不确定这是不是可行的方法。)

Thanks in advance! 提前致谢!

UPDATE: 更新:

(see update 2, this problem is fixed now) (见更新2,此问题现已修复)

OK now I'm having a slightly different (but not unrelated) issue. 好的,现在我的问题略有不同(但并非无关)。 What I did so far is this: 到目前为止我做的是:

I opted for making a custom control that I'll be able to use for each of my buttons: 我选择制作一个自定义控件,我可以用于每个按钮:

1- Subclass NSControl and override drawRect, mouseUp, mouseDragged and mouseDown. 1-子类NSControl并覆盖drawRect,mouseUp,mouseDragged和mouseDown。 This way I can get the behavior I want when dragging on a button (move the window). 这样我可以在拖动按钮时移动我想要的行为(移动窗口)。

2- Load images for each button states (same shape but one is darkened) into NSImages and draw them into the view using compositeToPoint:fromRect:operation: when necessary. 2-将每个按钮状态(相同形状但一个变暗)加载到NSImages中,并使用compositeToPoint:fromRect:operation:将其绘制到视图中compositeToPoint:fromRect:operation:必要时。 The images are 185 x 185 pixels PNG files with alpha exported from Photoshop. 这些图像是185 x 185像素的PNG文件,从Photoshop导出alpha

3- Get the NSImage's bitmap representation: 3-获取NSImage的位图表示:

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

OR 要么

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

Both seem to give the same result. 两者似乎都给出了相同的结果。 I tried compositing this bitmap rep onto my window to see if the image has same size and look as the original and all seems good (you'll see why I did that after this enumeration...). 我尝试将这个位图rep合成到我的窗口上以查看图像是否具有相同的大小并且看起来像原始图像并且一切看起来都很好(你会明白为什么我在这个枚举之后就这样做了......)。

4- In the mouse event methods, I use the bitmap representation to get the alpha component for a particular point: 4-在鼠标事件方法中,我使用位图表示来获取特定点的alpha分量:

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

Then I print the alpha value to the console but I am getting very weird results. 然后我将alpha值打印到控制台,但我得到了非常奇怪的结果。 Getting the alpha component and the color under the mouse seems to work at first glance (the colors and alpha returned are in my bitmap) but it looks like the color is not taken at the right point in the bitRep , or the image contained in the bitRep is distorted (it isn't distorted when I composite the bitRep on the window though, which is very strange). 获取alpha组件和鼠标下的颜色似乎乍一看(返回的颜色和alpha都在我的位图中),但看起来颜色不是在bitRep中的正确位置,或者包含在bitRep失真(虽然我在窗口合成bitRep时没有失真,这很奇怪)。 Again I double-checked the x and y mouse coordinates were in the (0,185) range and that the size of the NSBitmapImageRep is 185 x 185 too. 再次我双重检查x和y鼠标坐标分别在(0185)范围和所述的尺寸NSBitmapImageRep是185 X 185太。

First it looks like the NSBitmapImageRep is flipped horizontally compared to my NSImage . 首先,看起来NSBitmapImageRep与我的NSImage相比水平翻转。 If I flip my coordinates the values returned seem mostly OK (most of the shape seems to fit with the values returned) but there are still parts of the image which return wrong values. 如果我翻转我的坐标,返回的值似乎大部分都可以(大部分形状似乎与返回的值一致)但是仍然有部分图像返回错误的值。

I'm clueless at this point and having never worked with NSBitmapImageRep before maybe I just forgot to specify some format information about my image which would be why the values are off. 我在这一点上一无所知,并且从未使用过NSBitmapImageRep之前,我可能只是忘了指定一些关于我的图像的格式信息,这就是为什么关闭这些值的原因。 Could someone clarify this please? 有人可以澄清一下吗?

Thanks again! 再次感谢!

UPDATE 2: 更新2:

Ok never mind, I found the issue. 好吧没关系,我发现了这个问题。 I used the setColor:atX:y: method to draw a colored dot on the bitRep before printing it on my window and the issue was clear as day: the Y coordinate is reversed in the bitRep, but the image is printed with the "good" orientation. 我使用setColor:atX:y:方法在bitRep上绘制一个彩色圆点,然后将它打印在我的窗口上,问题就像白天一样明显:在bitRep中Y坐标反转,但是图像打印出的是“好” “方向。 This means that the bitRep has "Windows-style" top-left origin coordinate system while the window has the cartesian coordinate system (bottom-left) origin. 这意味着bitRep具有“Windows风格”的左上角原点坐标系,而窗口具有笛卡尔坐标系(左下角)原点。 Simply reversing my Y coordinate fixed the issues I had getting the alpha component and color. 简单地反转我的Y坐标修复了我获得alpha分量和颜色的问题。

This must have been written somewhere in the docs, sorry to have bothered you because of my lack of research...! 这一定是写在文档的某个地方,很遗憾因为我缺乏研究而困扰你......!

What about overriding the NSResponder event methods ( mouseUp: , mouseDown: , etc) in your custom buttons (presumably you'd be subclassing NSControl or NSButton to make these)? 如何在自定义按钮中覆盖NSResponder事件方法( mouseUp:mouseDown:等)(可能你NSControlNSButton子类NSControl这些)? In those methods you could properly calculate your bounding rectangle (or circle, in this case) and do a simple hit test with the coordinates of the click to see if the click should be handled. 在这些方法中,您可以正确计算边界矩形(或圆形,在本例中)并使用单击的坐标进行简单的命中测试,以查看是否应该处理单击。

You might also be able to find some help in Apple's event handling docs, specifically about mouse events and custom views . 您也可以在Apple的事件处理文档中找到一些帮助,特别是有关鼠标事件自定义视图的帮助

What about making it all a single control? 把它全部作为一个控制怎么样? It might make drawing a little bit complex, but it looks like you're doing custom drawing anyway. 它可能会使绘图变得有点复杂,但看起来你正在做自定义绘图。

If you did it as a single control, you could first check and see if the click point is within the center circle. 如果您将其作为单个控件执行,则可以先检查并查看点击点是否在中心圆内。 If it's not, then all you have to do is identify which quadrant it's in to know which "button" it belongs to (while also verifying that the click fits within the outer circle). 如果不是,那么你要做的就是确定它所在的象限,以便知道它属于哪个“按钮”(同时还要验证点击是否适合外圈)。

Alternatively, you could create a transparent view over your buttons that captures the click events and forwards them on to the appropriate control based on the logic I just specified. 或者,您可以在按钮上创建一个透明视图,捕获单击事件并根据我刚刚指定的逻辑将它们转发到相应的控件。

Similar to Marc W's response, in your own event method you can check the alpha value of the clicked on the bitmap and ignore if the alpha is lower than a certain value. 与Marc W的响应类似,在您自己的事件方法中,您可以检查点击位图的alpha值,并忽略alpha是否低于某个值。

To get the bitmap, read this . 要获取位图,请阅读此内容

Now you should have a bitmap pointer like this (pseudocode - you'll have to fill in the pieces): 现在你应该有一个这样的位图指针(伪代码 - 你必须填写碎片):

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

Then, you can do this to get the pixel you are interested in, and test it: 然后,您可以执行此操作以获取您感兴趣的像素,并对其进行测试:

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

This opens up some possibilities for you, such as having a ring around your inside circle that is inert and won't belong to any of the quadrants or the middle circle. 这为您开辟了一些可能性,例如在您的内圈周围有一个惰性且不属于任何象限或中间圆圈的环。 Of course, there's always other ways to do these things without resorting to pixel-wrangling :-) 当然,总是有其他方法来做这些事情而不诉诸像素争吵:-)

You'd need to do two things: 你需要做两件事:

  1. override NSView's hitTest: method to return NIL whenever the click is outside the image, so the click in that view is ignored and passedto the view overlapping it. 覆盖NSView的hitTest:方法,只要点击位于图像外,就返回NIL,因此忽略该视图中的点击并传递到与其重叠的视图。
  2. Override mouseDown: and implement your own mouse tracking loop in there (Using NSEventTrackingRunLoopMode) that also checks whether the point is in a non-transparent area. 覆盖mouseDown:并在其中实现自己的鼠标跟踪循环(使用NSEventTrackingRunLoopMode),该循环还检查该点是否在非透明区域中。 NSBitmapImageRep or NSImage has a colorAtX:y: method or something like hat which you can use for the latter. NSBitmapImageRep或NSImage有一个colorAtX:y:方法或类似帽子的东西,可以用于后者。

If you generate the shapes in code using NSBezierPath, testing whether a click is inside one of the shapes is a one-liner: send the shape a containsPoint: message. 如果使用NSBezierPath在代码中生成形状,则测试单击是否在其中一个形状内部是一行的:将形状发送到containsPoint:消息。

I'd make this a single view that owns the five paths. 我将这个视图拥有五条路径。 When drawing, draw the center one last; 绘图时,最后绘制中心; when responding to a mouse event, hit-test it first. 在响应鼠标事件时,首先点击测试它。 Give this view one action property per segment, and optionally one target per segment as well, depending on what you need. 根据您的需要,为每个细分提供一个操作属性,每个细分也可以选择一个目标。

Mu solution works only on non-intersect buttons. Mu解决方案仅适用于非交叉按钮。 I have non-rectangular button, that must change images from gray to green. 我有非矩形按钮,必须将图像从灰色变为绿色。

样品按钮 I change regular gradient button like this 我像这样更改常规渐变按钮

性能

thats all. 就这样。 Hope this help someone. 希望这能有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM