Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Delphi: Для новичков > Запуздырить иконку в трей (c) |
Автор: MetalFan 1.5.2011, 17:36 | ||
Простите за заголовок темы, вроде как все должно быть просто... Но возникла небольшая сложность, что-то не соображу, как правильно победить. Цель: Сворачивать приложение в трей (+убирать из панели задач) , по щелчку по иконке - разворачивать (возвращать в панель задач) Компоненты (Delphi XE): TTrayIcon, TApplicationEvents. Первому назначен OnClick, второму OnMinimize/OnRestore Код :
в аттаче тестовое приложение с указанным кодом. Проблема: на первый взгляд все хорошо: сворачиваем - получаем иконку в трее, разворачиваем - получаем приложение в панели задач... Но! При сворачивании по клику по кнопке в панели задач приложение обратно из трея не разворачивается( Городил собственную обработку сворачивания/разворачивания (вместо Application.Minimize/Restore), но там другие косяки повылазили... Может кто сталкивался и знает изящное решение? Всем спасибо за внимание, wbw, MetalFan. |
Автор: Rrader 1.5.2011, 18:05 | ||
Как увиделось, проблема в том, что IsIconic работает по-разному в разных случаях сворачивания. А что, если зафорсить единое поведение?
|
Автор: MetalFan 1.5.2011, 18:35 |
Rrader, Да, на IsIconic тоже обратил внимание... почему-то IsIconic "слетает" после того, как Hide форме делается... А SC_MINIMIZE приходит до вызова Application.OnMinimize (точнее как раз OnMinimize вызывается вследствие прихода SC_MINIMIZE) судя по коду TApplication.WndProc... Но твой вариант работает верно... спасибо за идею) Может вообще есть более корректный подход, чем который я изначально привел? кстати, IsIconic возвращает неверно выставленный внутренний флаг TApplication.FAppIsIconic в моем случае... который выставляется по приходу WM_ACTIVATEAPP, после того, как приложение свернуто... хм. косяк что-ли где-то в VCL. |
Автор: MetalFan 11.1.2012, 21:27 | ||||
Подниму сию тему... В общем решил исходную задачу следующим рабочим кодом, который прекрасно отрабатывает по клику на иконку в трее (используется стандартный компонент - TTrayIcon):
Данный код сворачивает приложение "в трей", если оно "развернуто", либо разворачивает его из оного, если оно было свернуто. Иконка в трее теперь видна всегда. Задача: модифицировать код так, чтобы при вызове этого кода в момент клика мышью на иконке в трее добавился третий вариант реакции - если приложение развернуто, но не на переднем плане, перенести его на передний план... Казалось бы, что сложного, во вторую ветку добавить проверку вида
Но в момент клика на иконку в трее, при вызове данного кода, GetForegroundWindow всегда <> Self.Handle Другие, пришедшие в голову проверки (GetTopWindow, Application.Active, Applicaiton.MainForm.Active, etc...) так же не срабатывают, т.к. в момент клика активным окном на переднем плане становится окно тулбара винды... Собственно уважаемые коллеги, какие будут соображения на эту тему? p.s.По скольку вопрос напрямую связан с сабжевым вопросом, то создавать новую тему не вижу смысла |
Автор: Чучмек 11.1.2012, 23:35 |
А Shell_NotifyIcon недостаточно? |
Автор: CodeMonkey 12.1.2012, 07:12 | ||
Это баг в Delphi. Application.Restore первым делом проверяет "свёрнутость" окна. Проблема в том, что делает она это на Handle себя, а не формы (это при MainFormOnTaskbar = True). Application.Handle и Application.MainForm.Handle - это два разных окна и их состояния могут быть несогласованными, что мы и получаем в этом примере. Решение: сворачивать Application.
Добавлено через 2 минуты и 34 секунды P.S. Эх, проморгал исходную тему, а то сохранил бы тебе полгода мучений ![]() |
Автор: CodeMonkey 12.1.2012, 07:28 | ||
Чего-то слона-то я и не заметил... Не очень понял как может быть "если приложение развернуто, но не на переднем плане" одновременно с "при клике мышью на иконке в трее". Ты же скрываешь икноку при показе окна. В любом случае проверка должна опираться на видимость окна. Видимо окно - на первый план его, нет - восстановить. |
Автор: CodeMonkey 12.1.2012, 11:02 |
А, всё, понял. Это просто: возьми Foreground или Active окно (надо проверить, как там с SOT-окнами), найди следующее по Z-порядку (GetNextWindow). Если твоё - то ты сверху, минимизируй. Если не твоё, то ты перекрыт - сворачивайся. |
Автор: MetalFan 12.1.2012, 11:19 |
CodeMonkey, не сработало) пробовал так уже. В момент клика, даже если перед этим было активно наше окно, то GetNextWindow от GetForegroundWindow возвращает вообще другое окно, никак не относящееся к нашему приложению. з.ы. а что такое SOT-окна? |
Автор: CodeMonkey 12.1.2012, 11:41 |
SOT = Stay On Top Через Spy++ проверь, что там GetNextWindow возвращает. По нему же можно цепочку Z order проверить (вроде). |
Автор: MetalFan 12.1.2012, 11:48 | ||
CodeMonkey, вот моя пока нерабочая модификация кода:
Смотрел в S++, в момент нажатия по трею GetNextWindow(GetForegroundWindow, GW_HWNDNEXT) и GetNextWindow(GetForegroundWindow, GW_HWNDPREV) возвращает вообще левые окна... что и видно в S++. тестовое приложение в аттаче. |
Автор: CodeMonkey 12.1.2012, 12:30 | ||
А что это за окна? Чьи они? И какие у них атрибуты? Добавлено через 1 минуту и 2 секунды Кстати, а ты пробовал GetLastActivePopup? |
Автор: MetalFan 12.1.2012, 13:46 |
просто окна других приложений. среди них нет моего. пробовал. Если в него отдаешь 0, то возвращается 0, если отдаешь хэндл формы, то получаешь тот же хэндл формы. |
Автор: Чучмек 12.1.2012, 21:23 |
OnPaint |
Автор: MetalFan 12.1.2012, 21:30 |
Чучмек, выскажись по-подробнее.... |
Автор: Чучмек 12.1.2012, 21:55 |
Если окно приложения перекрыто другими окнами и становится активным, то для окна вызывается OnPaint А если окно не перекрыто - то OnPaint не вызывается. Отсюда решение: По клику на иконку в трее сделать окно активным и запустить ожидание события OnPaint (0,5 сек) Если событие не произошло - скрыть окно. |
Автор: MetalFan 12.1.2012, 22:58 |
Чучмек, хм, как самый крайний вариант может и сойдет, но, имхо, не очень красивое решение... |
Автор: CodeMonkey 13.1.2012, 01:23 | ||
Выяснил, что все функции работают только в контексте цепочки окна. Я не нашёл ни одной функции, которая давала бы доступ к глобальному Z-порядку. Для теста использовал такой код:
Предлагаю попробовать так: перечислить все окна. Отобрать из них те, которые видимые, не SOT и пересекают твой клиентский прямоугольник. Если такие есть - ты перекрыт, давай наверх. Если таких нет - ты наверху, убирайся в трей. Добавлено через 3 минуты и 59 секунд Возможно, вместо перечисления окон можно глянуть в сторону GetClipBox. |
Автор: Чучмек 13.1.2012, 02:53 |
PtVisible |
Автор: Qu1nt 13.1.2012, 03:49 | ||
http://delphimaster.net/view/2-1169797326 Или вот, что получилось у меня.
|
Автор: MetalFan 13.1.2012, 09:11 |
не понял, как это определить... если просто сравнить пересечение прямоугольников окон. так если чужое окно будет под или над моим, получается, что прямоугольники пересекуться... а вот это посмотрим хм, и что, проверять каждый пиксель формы? ну как еще один крайний вариант - сойдет. хотя оно же не учитывает так называемые SOT окна наверняка. Qu1nt, да, тема стара, как мир))) предложенный код исходит из предположения, что EnumWindows перечисляет окна в порядке видимости сверху вниз? А список исключения по ClassName если вдруг поменяется? программа какая-либо сторонняя вдруг так же вылезет "наверх"... спасибо, как вариант, возможно прокатит. Но как-то на мой взгляд не надежно.... Добавлено @ 09:12 з.ы. как же ритлабовцы в своем thebat добились нужного поведения... и uTorrent тоже как надо работает.... |
Автор: CodeMonkey 13.1.2012, 09:20 |
Не видел TheBat, но может они читерят ![]() Например, первый клик - если окно видимо, то наверх его. Если второй клик (но не дабл-клик) происходит менее чем через 2 сек после первого - то свернуть окно, если позднее - то начать с начала. |
Автор: MetalFan 13.1.2012, 09:34 |
CodeMonkey, неа... даж если надо окном бата/utorrenta поместить, к примеру, калькулятор, то при клике на иконку в трее окно не свернется в трей, а "переедет" на передний план. Сегодня попробую все предложенные варианты, отпишусь. Добавлено через 2 минуты и 29 секунд http://delphimaster.net/view/4-1124275353/3, как раз, как ты писал с проверкой на пересечение окон, что выше по Z-порядку находятся. В общем надо пробовать. |
Автор: MetalFan 13.1.2012, 15:46 | ||||
Небольшой подитог: все способы, определяющие частично перекрытие окна не катят на мой взгляд. т.к. неправильно сработают в след.ситуации: наше окно открыто не на весь экран и видно полностью, рядом АКТИВНО окно другого приложения так же не на весь экран и не перекрывающее наше окно. В этом случае по логике с перекрытиями наше окно считается поверх всех, т.к. видно полностью, что на мой взгляд неправильно. Вариант Qu1nt вроде как работает правильно, только вот не нравятся мне проверка на классы окон, ибо может появиться вдруг еще какой-нибудь класс, который сломает сию добрую логику... и одна вот эта часть проверки, кмк, так же написана неверно (скобок не хватает):
Разве должно быть не так:
|
Автор: MetalFan 13.1.2012, 17:14 | ||||
В общем модифицировал функцию от Qu1nt и все заработало ПОЧТИ как надо:
Это Почти заключается в небольшом отличии от поведения ранееупомянутой мыши. Наше окно всегда считается foreground, даже если сейчас активно другое окно с ExStyle = WS_EX_TOPMOST... С одной стороны это наверное правильно... Замена проверки ExStyle на WS_EX_TOOLWINDOW привело к тому, что если на экране есть одно SOT-окно, то наше окно вообще перестает сворачиваться (такое же поведение и у uTorrent, кстати). Как же в бате сделали разработчики, чтобы все работало как надо красиво... загадка. |
Автор: Qu1nt 13.1.2012, 17:15 | ||
Ой, не там скобку убрал:
Подход основывался на: http://stackoverflow.com/questions/210504/enumerate-windows-like-alt-tab-does http://code.google.com/p/k-kovalev-personal/source/browse/trunk/HotKeying/HotKeying/Window/WindowDataProvider.cs |
Автор: Qu1nt 13.1.2012, 17:31 |
MetalFan, уточни какого поведения ты хочешь добиться. |
Автор: MetalFan 13.1.2012, 18:41 |
Qu1nt, поведение я ж вроде описывал. Необходимо момент нажатия на иконку в трее: 1. Если окно свернуто, то развернуть его. 2. Если окно приложения активно в данный момент, то свернуть его "в трей". 3. Если окно развернуто из трея, но не активно в данный момент, то сделать его активным. в п.3 как раз и загвоздка. Как корректно определить, активно ли окно в момент нажатия на иконку в трее. Самый приближенный к "правде" вариант - последний приведенный мною на основании твоего кода. |
Автор: Qu1nt 13.1.2012, 20:53 | ||
Вот еще вариант:
Разницы между поведением этого кода и The Bat! 5 не увидел. |
Автор: CodeMonkey 14.1.2012, 05:36 |
В описании EnumWindows не сказано, в каком порядке она обходит окна. |
Автор: Чучмек 14.1.2012, 09:34 | ||
|
Автор: b8195108 14.1.2012, 14:28 | ||
|
Автор: MetalFan 14.1.2012, 14:41 | ||
CodeMonkey, действительно, не сказано... но вроде бы работает. Может лучше конечно на GetNextWindow переделать... Чучмек, тоже какой-то колхоз. b8195108, не катит, т.к.
|
Автор: b8195108 14.1.2012, 15:19 |
MetalFan, сорри, пропустил. зы имхо не совсем правильная логика) |
Автор: MetalFan 16.1.2012, 12:17 |
Qu1nt, отличия есть. если перейти с нашего окна к SOT-окну, то при клике по иконке в трее наша программа сворачивается, хотя хотелось бы, чтобы она активировалась. |
Автор: Чучмек 17.1.2012, 00:39 | ||||||||
Вот как выглядит функция обратного вызова для EnumWindows у uTorrent
А вот сам вызов EnumWindows
|
Автор: MetalFan 17.1.2012, 09:26 |
Чучмек, ну во первых, uTorrent работает не совсем корректно в случае, когда на экране есть SOT-окно - он перестает сворачиваться в трей вообще. ну и во вторых, спасибо конечно за реверс-инжиниринг, но ты хоть проверил, что у тебя получилось то?) |
Автор: Чучмек 17.1.2012, 13:11 | ||
Вот теперь проверил. Работает.
|
Автор: MetalFan 17.1.2012, 13:34 |
Чучмек, опять же работает без учета текущего активного topmost окна... т.е. так же, как и мой последний вариант. Но как вариант сойдет. Я, кстати, остановился на варианте с GetNextWindow... т.к. действительно не стоит полагаться, что enumwindows обходит и будет обходить окна именно в порядке от верхнего к нижнему... |
Автор: Чучмек 17.1.2012, 13:47 |
Это уже не ко мне. Это к создателям uTorrent. Там, где ты про колхоз писал... Суть в том, чтобы после клика в трее "попросить" систему восстановить порядок окон. |
Автор: MetalFan 17.1.2012, 13:51 |
Чучмек, да это понятно) просто uTorrent, как оказалось, не очень удачным примером оказался. А про колхоз - ну посылать сочетания клавиш для того, чтобы "попросить" систему о чем-то, имхо, не совсем правильно... Но все равно спасибо за активную помощь в поиске решения) ![]() |
Автор: Чучмек 17.1.2012, 13:54 |
А какой удачный? |
Автор: MetalFan 17.1.2012, 14:10 |
Чучмек, идеальным, на мой взгляд, считаю поведение TheBat. |
Автор: Qu1nt 17.1.2012, 15:29 | ||
MetalFan, очередная итерация.
Всё короче и короче ![]() |
Автор: Чучмек 18.1.2012, 14:10 |
The Bat! написан на delphi или на чем-то подобном, а delphi компилит ###код ![]() Но из того что удалось понять следует следующее: Сообщение трея $0401 Сообщения трея принимает не основная форма. В случае когда основное окно The Bat есть foregroun: По сообщению основного окна WM_ACTIVATE/WA_INACTIVE запоминается текущее время. Если клик в трее происходит через "менее чем ...", окно сворачивается в трей. Добавлю: TheBat время получает через GetTickCount менее чем - это 500 тиков. |
Автор: MetalFan 18.1.2012, 14:29 |
Чучмек, я так и предполагал... одна из мыслей была как раз по определению некой временной дельты между потерей активности приложения и кликом по трею... Теперь за дело говорю - спасибо! |
Автор: Qu1nt 18.1.2012, 15:00 |
MetalFan, чем мой последний вариант не устроил? |
Автор: CodeMonkey 18.1.2012, 20:20 | ||
Эге, я же http://forum.vingrad.ru/index.php?showtopic=328395&view=findpost&p=2448208. |
Автор: kuzduk 13.9.2012, 12:22 | ||
Эгегей! Я знаю как просто и красиво решить данную проблему, когда при нажатии на трей-иконку надо определить активна ли была наша программа или нет! Моё решение такое:
-------------------- через Z-последовательности определить ничего не получилось - куча посторонних окон... также можно попробовать поиграть с GetLastActivePopup(GetDesktopWindow) -------------------- всем рекомендую качать мой модуль KuTray для работы с иконкой в трее и панели задач: http://kuzduk.narod.ru/_tray.html ![]() также смотрите другие мои полезные модули и программы: http://kuzduk.narod.ru/_tvor.html ![]() |
Автор: Akella 3.2.2013, 00:29 | ||
Кто-нибудь сталкивался с тем, что в Windows 7 x64 значок TTrayIcon не исчезает из области уведомления, хотя программа закрывается корректно? Используется D2007 и стандартная компонента TTrayIcon, но немного переделанная:
|
Автор: gesper 3.2.2013, 03:52 |
А не переделаный так же все делает? |
Автор: Akella 3.2.2013, 10:47 |
Всё равно остаётся |