Как создать прозрачное окно с непрямоугольными кнопками? - PullRequest
7 голосов
/ 07 октября 2009

Я хочу создать собственное окно, которое бы выглядело примерно так (не говоря уже о быстром фотошопе):

alt text

Основная проблема, которую я имею, состоит не в том, чтобы сделать окно прозрачным (хотя я думаю, что любая информация приветствуется!), А в том, что я хочу иметь возможность нажимать только на видимую часть кнопок. Например, если я нажимаю только за верхним левым краем кнопки «5», я хочу, чтобы он регистрировал нажатие кнопки «1», а не кнопки «5», даже если я нажал в ограничивающем поле кнопки 5. И если я щелкну только в стороне от верхнего левого «угла» кнопки «1», я хочу, чтобы он нажимал на то, что находится под моим окном, а не на кнопке. Мне также нужно иметь возможность изменить размер этого окна, и изображения в кнопках должны изменить их размер.

Я думал об использовании прозрачного NSWindow с прозрачными NSButtons, содержащими PNG с альфа-каналом для каждой кнопки, но я думаю, что перекрывающиеся кнопки будут проблемой.

Я также слышал о приложениях, таких как Bowtie, SweetFM и других, использующих WebKit для отображения своих интерфейсов. Если бы это было возможно по крайней мере на 10.3, это было бы интересно, но я не знаю, как это будет работать.

Я искал маски щелчков, области или что-то в этом роде, но пока ничего не нашел. Я буду обновлять этот пост по мере нахождения дополнительной информации (если я это сделаю!).

Любые советы о том, как действовать?

(Обратите внимание, что я работаю на 10.4 и в идеале Мне нужно также поддерживать некоторые более старые версии Mac OS X.)

(Кроме того, я нашел пример «закругленного окна» на сайте разработчиков Apple, но он больше не работает на 10.4, поэтому я не уверен, что это правильный путь.)

Заранее спасибо!

UPDATE:

(см. Обновление 2, теперь эта проблема исправлена)

Хорошо, теперь у меня немного другая (но не связанная) проблема. То, что я сделал до сих пор, это:

Я выбрал создание пользовательского элемента управления, который я смогу использовать для каждой из моих кнопок:

1 - Подкласс NSControl и переопределение drawRect, mouseUp, mouseDragged и mouseDown. Таким образом, я могу получить желаемое поведение при перетаскивании кнопки (переместить окно).

2 - Загрузить изображения для каждого состояния кнопки (одинаковой формы, но затемненной) в NSImages и нарисовать их в виде, используя compositeToPoint:fromRect:operation: при необходимости. Изображения: 185 x 185 пикселей PNG-файлы с альфа , экспортированные из Photoshop.

3 - Получить растровое представление NSImage:

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

OR

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

Кажется, что оба дают одинаковый результат. Я попытался наложить это растровое изображение на свое окно, чтобы увидеть, имеет ли изображение тот же размер и выглядит ли оно как оригинал, и все ли это хорошо (вы поймете, почему я сделал это после этого перечисления ...).

4- В методах событий мыши я использую растровое представление, чтобы получить альфа-компонент для конкретной точки:

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

Затем я печатаю альфа-значение на консоль, но получаю очень странные результаты. На первый взгляд кажется, что получение альфа-компонента и цвета под мышкой работает (возвращенные цвета и альфа приведены в моем растровом изображении), но похоже, что цвет не выбран в нужной точке в bitRep, или изображение, содержащееся в нем в bitRep искажается (это не искажается, когда я комбинирую bitRep в окне, что очень странно). Я снова проверил, что координаты мыши x и y были в диапазоне (0,185) и что размер NSBitmapImageRep также равен 185 x 185.

Сначала похоже, что NSBitmapImageRep перевернут горизонтально по сравнению с моим NSImage. Если я переворачиваю мои координаты, возвращаемые значения кажутся в основном нормальными (кажется, что большая часть формы соответствует возвращаемым значениям), но все еще есть части изображения, которые возвращают неправильные значения.

На данный момент я ничего не понимаю и раньше никогда не работал с NSBitmapImageRep, может быть, я просто забыл указать некоторую информацию о формате для моего изображения, из-за чего значения отключены. Может кто-нибудь уточнить это, пожалуйста?

Еще раз спасибо!

ОБНОВЛЕНИЕ 2:

Хорошо, неважно, я нашел проблему. Я использовал метод setColor: atX: y: чтобы нарисовать цветную точку на bitRep перед печатью в моем окне, и проблема была ясна как день: координата Y в bitRep инвертирована, но изображение печатается с «хорошим ориентация. Это означает, что bitRep имеет исходную систему координат верхнего левого угла в стиле «Windows», а окно имеет начало координат декартовой системы координат (внизу слева). Простое изменение координаты Y устранило проблемы, связанные с получением альфа-компонента и цвета.

Это должно быть написано где-то в документации, извините, что беспокою вас из-за моего отсутствия исследований ...!

Ответы [ 6 ]

3 голосов
/ 07 октября 2009

А как насчет переопределения методов события NSResponder (mouseUp:, mouseDown: и т. Д.) В пользовательских кнопках (предположительно, для создания этих подклассов NSControl или NSButton)? В этих методах вы могли бы правильно рассчитать свой ограничивающий прямоугольник (или круг, в данном случае) и выполнить простой тест попадания с координатами щелчка, чтобы увидеть, следует ли обрабатывать щелчок.

Вы также можете найти некоторую помощь в документах Apple по обработке событий, в частности о событиях мыши и пользовательских представлениях .

2 голосов
/ 07 октября 2009

Как и в ответе Марка В., в вашем собственном методе событий вы можете проверить альфа-значение нажатого на растровом изображении и игнорировать, если альфа ниже определенного значения.

Чтобы получить растровое изображение, прочитайте это .

Теперь у вас должен быть подобный растровый указатель (псевдокод - вам нужно будет заполнить фрагменты):

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)

Это открывает некоторые возможности для вас, например, наличие кольца внутри вашего внутреннего круга, которое является инертным и не принадлежит ни одному из квадрантов или среднего круга. Конечно, всегда есть другие способы сделать это, не прибегая к пиксельным спорам: -)

2 голосов
/ 07 октября 2009

Как насчет того, чтобы сделать все это единым элементом управления? Это может сделать рисование немного сложным, но похоже, что вы все равно делаете нестандартное рисование.

Если вы сделали это как один элемент управления, вы можете сначала проверить, находится ли точка щелчка внутри центрального круга. Если это не так, то все, что вам нужно сделать, - это определить, в каком квадранте он находится, чтобы узнать, к какой «кнопке» он принадлежит (при этом также проверяя, соответствует ли щелчок внешнему кругу).

В качестве альтернативы, вы можете создать прозрачное представление для ваших кнопок, которое фиксирует события нажатия и передает их в соответствующий элемент управления на основе логики, которую я только что указал.

1 голос
/ 06 января 2013

Вам нужно сделать две вещи:

  1. переопределить hitTest: метод NSView для возврата NIL всякий раз, когда щелчок находится за пределами изображения, поэтому щелчок в этом представлении игнорируется и передается представлению, перекрывающему его.
  2. Переопределите mouseDown: и реализуйте там свой собственный цикл отслеживания мыши (используя NSEventTrackingRunLoopMode), который также проверяет, находится ли точка в непрозрачной области. NSBitmapImageRep или NSImage имеет метод colorAtX: y: или что-то наподобие шляпы, которую вы можете использовать для последнего.
1 голос
/ 07 октября 2009

Если вы генерируете фигуры в коде с использованием NSBezierPath, проверка, находится ли щелчок внутри одной из фигур, является однострочным: отправьте фигуре сообщение containsPoint:.

Я бы сделал это единым представлением, которому принадлежат пять путей. При рисовании нарисуйте центр в последнюю очередь; когда отвечаешь на событие мыши, сначала проверь его. Присвойте этому представлению одно свойство действия на сегмент и, дополнительно, одну цель на сегмент, в зависимости от того, что вам нужно.

0 голосов
/ 19 сентября 2013

Му решение работает только на непересекающихся кнопках. У меня есть непрямоугольная кнопка, которая должна изменить изображения с серого на зеленый.

sample button Я меняю кнопку обычного градиента вот так

properties

вот и все. Надеюсь, это поможет кому-то.

...