【问题标题】:How to create a transparent window with non-rectangular buttons?如何使用非矩形按钮创建透明窗口?
【发布时间】:2009-10-06 20:22:51
【问题描述】:

我正在寻找一个看起来像这样的自定义窗口(不要介意快速 Photoshop):

我遇到的主要问题不是使窗口透明(尽管我想欢迎任何信息!),但我希望能够仅单击按钮的可见部分。例如,如果我在“5”按钮的左上角之外单击,我希望它记录对“1”按钮而不是“5”按钮的单击,即使我单击了按钮 5 的边界框。如果我点击“1”按钮的左上角“角落”之外,我希望它点击窗口下方的任何内容,而不是按钮。我还需要能够调整此窗口的大小,并且按钮中的图片应随之调整大小。

我正在考虑使用透明 NSWindow 和透明 NSButton,每个按钮包含带有 alpha 的 PNG,但我认为重叠的按钮会是一个问题。

我还听说过诸如 Bowtie、SweetFM 和其他使用 WebKit 来显示其界面的应用程序。如果这在至少 10.3 上是可能的,那会很有趣,但我也不知道它是如何工作的。

我四处寻找点击蒙版、区域或类似的东西,但我还没有找到任何东西。当我找到更多信息时,我会更新这篇文章(如果我这样做了!)。

关于如何进行的任何提示?

(请注意,我正在开发 10.4,理想情况下我还需要支持一些旧版本的 Mac OS X。)

(另外,我在 Apple 的开发网站上找到了“圆角窗口”示例,但它不再在 10.4 上运行,所以我不确定这是要走的路。)

提前致谢!

更新:

(参见更新 2,此问题现已修复)

好的,现在我遇到了一个稍微不同(但并非不相关)的问题。到目前为止我所做的是:

我选择制作一个可以用于我的每个按钮的自定义控件:

1- 继承 NSControl 并覆盖 drawRect、mouseUp、mouseDragged 和 mouseDown。这样我可以在拖动按钮(移动窗口)时获得我想要的行为。

2- 将每个按钮状态的图像(相同形状但一个变暗)加载到 NSImage 中,并在必要时使用compositeToPoint:fromRect:operation: 将它们绘制到视图中。图片是从 Photoshop 导出的 185 x 185 像素 带 alpha 的 PNG 文件

3- 获取 NSImage 的位图表示:

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

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

两者似乎都给出了相同的结果。我尝试将此位图表示合成到我的窗口上,以查看图像是否具有相同的大小并看起来与原始图像是否相同,并且一切看起来都很好(您会在这次枚举之后明白我为什么这样做......)。

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

然后我将 alpha 值打印到控制台,但我得到了非常奇怪的结果。乍一看,获取 alpha 分量和鼠标下的颜色似乎可以工作(返回的颜色和 alpha 在我的位图中),但看起来颜色不是在bitRep 中的正确点,或者包含的图像在bitRep 中被扭曲(当我在窗口上合成bitRep 时它没有扭曲,这很奇怪)。我再次仔细检查了 x 和 y 鼠标坐标是否在 (0,185) 范围内,NSBitmapImageRep 的大小也是 185 x 185。

首先,与我的NSImage 相比,NSBitmapImageRep 看起来是水平翻转的。如果我翻转我的坐标,返回的值似乎大部分都可以(大部分形状似乎与返回的值相符),但仍有部分图像返回错误的值。

此时我一无所知,之前从未与NSBitmapImageRep 合作过,也许我只是忘记指定有关我的图像的一些格式信息,这就是这些值关闭的原因。有人可以澄清一下吗?

再次感谢!

更新 2:

好吧,没关系,我找到了问题。我使用 setColor:atX:y: 方法在 bitRep 上绘制一个彩色点,然后在我的窗口上打印它,问题很清楚:bitRep 中的 Y 坐标是反转的,但图像打印为“好“ 方向。这意味着 bitRep 具有“Windows 样式”的左上角原点坐标系,而窗口具有笛卡尔坐标系(左下角)原点。只需反转我的 Y 坐标即可解决我在获取 alpha 分量和颜色时遇到的问题。

这一定是在文档的某个地方写的,很抱歉因为我缺乏研究而打扰到你......!

【问题讨论】:

    标签: objective-c cocoa nswindow


    【解决方案1】:

    在您的自定义按钮中重写NSResponder 事件方法(mouseUp:mouseDown: 等)怎么样(假设您将继承NSControlNSButton 来制作这些)?在这些方法中,您可以正确计算边界矩形(或圆形,在这种情况下),并使用点击的坐标进行简单的点击测试,以查看是否应该处理点击。

    您还可以在 Apple 的事件处理文档中找到一些帮助,特别是关于 mouse eventscustom views

    【讨论】:

    • 谢谢,这对于中心按钮来说是一种可能性,但是使用外部按钮会更加困难并且需要一堆边界检查代码......如果我改变我的按钮位置怎么办一些理由? (这不是一个个人项目,所以不幸的是,这可能会发生变化)然后我不得不重新编写我的大部分边界检查代码......有没有办法用某种掩码来做到这一点?说“如果此时位图是透明的,让点击通过”?
    • 我不认为你可以像你想的那样用面具来做到这一点(透明度检测等)。这些是非常基本的形状(外部按钮的部分圆圈位于内部的完整圆圈下方)。您可以在代码中访问控件的当前坐标和尺寸,并使用它们来计算命中范围,因此您无需对任何内容进行硬编码。利用中心按钮在您的优势这一事实。
    • 可以使用透明度 (alpha) 检测,它工作得很好,虽然我现在使用的方法是 10.4+,但有一种方法可以使用 colorAtX:y 以外的其他方法:只要您可以访问 bitmapRep,就像 mahboudz 指出的那样。
    【解决方案2】:

    如何将其全部设为单个控件?这可能会使绘图变得有点复杂,但看起来您无论如何都在进行自定义绘图。

    如果你是作为一个单独的控件来做的,你可以先检查一下点击点是否在中心圆内。如果不是,那么您所要做的就是确定它所在的象限以知道它属于哪个“按钮”(同时还要验证点击是否适合外圈)。

    或者,您可以在按钮上创建一个透明视图,以捕获点击事件并根据我刚刚指定的逻辑将它们转发到适当的控件。

    【讨论】:

      【解决方案3】:

      与 Marc W 的响应类似,在您自己的事件方法中,您可以检查位图上单击的 alpha 值,如果 alpha 低于某个值,则忽略。

      要获取位图,read this

      现在你应该有一个像这样的位图指针(伪代码 - 你必须填写这些部分):

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

      然后,您可以这样做以获取您感兴趣的像素,并对其进行测试:

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

      这为您提供了一些可能性,例如在您的内圈周围有一个不属于任何象限或中间圈的环。当然,总有其他方法可以在不诉诸像素争吵的情况下完成这些事情:-)

      【讨论】:

      • 这看起来更像我想做的,这样如果我的控件的形状发生变化,它应该会自动调整。有没有一种方法可以检查按钮图像中特定点的图像不透明度? (我认为将 NSButton 子类化而不是子类化 NSView 并手动绘制会少一些工作,但如果无法使用按钮检查 alpha,我很可能会使用 NSView)。
      • 我正在将位图处理信息添加回我的响应中。
      • 非常感谢,我看看我能用它做什么!
      • 虽然我现在使用 Cocoa API 进行 alpha 检测,但您提到位图上下文的事实为我指明了正确的方向。当我需要支持早于 10.4 的版本时,我也会查看您的代码,但现在 NSBitmapImageRep 上的 colorAtX:y: 将起作用。感谢您的帮助!
      • 我什至不知道 colorAtX!
      【解决方案4】:

      如果您使用 NSBezierPath 在代码中生成形状,则测试是否在其中一个形状内单击是单行的:向形状发送 containsPoint: 消息。

      我会将其设为拥有五条路径的单一视图。画的时候,最后画一个中心;响应鼠标事件时,首先对其进行命中测试。根据您的需要,为此视图为每个细分提供一个操作属性,并且还可以选择为每个细分提供一个目标。

      【讨论】:

      • 你的解决方案很有趣,我不知道你可以像这样测试 NSBezierPaths。它肯定会派上用场。但在我的情况下(形状的变化是不可忽略的概率)我认为如果我改变一些东西,那将是很多重写。我会先尝试位图 alpha 测试,如果它不起作用,我希望它会尝试。感谢您花时间回答我的问题!
      【解决方案5】:

      你需要做两件事:

      1. 覆盖 NSView 的 hitTest: 方法以在点击位于图像外部时返回 NIL,因此该视图中的点击将被忽略并传递给与其重叠的视图。
      2. 覆盖mouseDown:并在其中实现您自己的鼠标跟踪循环(使用NSEventTrackingRunLoopMode),该循环还检查该点是否在非透明区域中。 NSBitmapImageRep 或 NSImage 有一个 colorAtX:y: 方法或类似帽子的东西,您可以将其用于后者。

      【讨论】:

        【解决方案6】:

        Mu 解决方案仅适用于非相交按钮。我有非矩形按钮,必须将图像从灰色变为绿色。

        我像这样更改常规渐变按钮

        仅此而已。希望这对某人有所帮助。

        【讨论】:

          猜你喜欢
          • 2018-08-12
          • 1970-01-01
          • 1970-01-01
          • 2021-04-10
          • 1970-01-01
          • 2010-10-21
          • 1970-01-01
          • 1970-01-01
          • 2016-03-03
          相关资源
          最近更新 更多