Модераторы: Snowy, MetalFan, bems, Poseidon

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Запуздырить иконку в трей (c), Delphi XE, TTrayIcon, кривые руки 
:(
    Опции темы
MetalFan
Дата 1.5.2011, 17:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



Простите за заголовок темы, вроде как все должно быть просто...
Но возникла небольшая сложность, что-то не соображу, как правильно победить.
Цель: Сворачивать приложение в трей (+убирать из панели задач) , по щелчку по иконке - разворачивать (возвращать в панель задач)
Компоненты (Delphi XE): TTrayIcon, TApplicationEvents. Первому назначен OnClick, второму OnMinimize/OnRestore
Код :
Код

procedure TForm1.appevtMinimize(Sender: TObject);
begin
  tricon.Visible := True;
  Hide;
end;

procedure TForm1.appevtRestore(Sender: TObject);
begin
  tricon.Visible := False;
  Show;
  SetForegroundWindow(Handle);
end;

procedure TForm1.triconClick(Sender: TObject);
begin
  if WindowState = wsMinimized then
    Application.Restore
  else
    Application.Minimize;
end;

в аттаче тестовое приложение с указанным кодом.
Проблема: на первый взгляд все хорошо: сворачиваем - получаем иконку в трее, разворачиваем - получаем приложение в панели задач...
Но! При сворачивании по клику по кнопке в панели задач приложение обратно из трея не разворачивается(
Городил собственную обработку сворачивания/разворачивания (вместо Application.Minimize/Restore), но там другие косяки повылазили...
Может кто сталкивался и знает изящное решение?
Всем спасибо за внимание,
wbw, MetalFan.


Это сообщение отредактировал(а) MetalFan - 1.5.2011, 17:59

Присоединённый файл ( Кол-во скачиваний: 23 )
Присоединённый файл  TrayIcon.zip 1,14 Kb


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Rrader
  Дата 1.5.2011, 18:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Inspired =)
***


Профиль
Группа: Экс. модератор
Сообщений: 1535
Регистрация: 7.5.2005

Репутация: 18
Всего: 191



Как увиделось, проблема в том, что IsIconic работает по-разному в разных случаях сворачивания. А что, если зафорсить единое поведение?
Код

procedure TForm1.appevtMinimize(Sender: TObject);
begin
  tricon.Visible := True;
  SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  Hide;
end;



--------------------
Let's do this quickly!
Rest in peace, Vit!
PM MAIL Skype   Вверх
MetalFan
Дата 1.5.2011, 18:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



Rrader, Да, на IsIconic тоже обратил внимание... почему-то IsIconic "слетает" после того, как Hide  форме делается...
А SC_MINIMIZE приходит до вызова Application.OnMinimize (точнее как раз OnMinimize вызывается вследствие прихода SC_MINIMIZE) судя по коду 
TApplication.WndProc...
Но твой вариант работает верно... спасибо за идею)
Может вообще есть более корректный подход, чем который я изначально привел?
кстати, IsIconic возвращает неверно выставленный внутренний флаг TApplication.FAppIsIconic в моем случае... который выставляется по приходу WM_ACTIVATEAPP, после того, как приложение свернуто... хм. косяк что-ли где-то в VCL.

Это сообщение отредактировал(а) MetalFan - 1.5.2011, 18:52


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
MetalFan
Дата 11.1.2012, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



Подниму сию тему...
В общем решил исходную задачу следующим рабочим кодом, который прекрасно отрабатывает по клику на иконку в трее (используется стандартный компонент - TTrayIcon):
Код

procedure TForm1.DoRestoreOrMinimize;
begin
  if WindowState = wsMinimized then
  begin
    WindowState := wsNormal;
    Application.Restore;
    Show;
    SetForegroundWindow(Handle);
  end
  else
  begin
    Application.Minimize;
    Hide;
  end;
end;

Данный код сворачивает приложение "в трей", если оно "развернуто", либо разворачивает его из оного, если оно было свернуто.
Иконка в трее теперь видна всегда.

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

if GetForegroundWindow <> Handle then 
  SetForegroundWindow(Handle)
else
...старый код

Но в момент клика на иконку в трее, при вызове данного кода, GetForegroundWindow всегда <> Self.Handle
Другие, пришедшие в голову проверки (GetTopWindow, Application.Active, Applicaiton.MainForm.Active, etc...) так же не срабатывают, т.к. в момент клика активным окном на переднем плане становится окно тулбара винды...

Собственно уважаемые коллеги, какие будут соображения на эту тему?

p.s.По скольку вопрос напрямую связан с сабжевым вопросом, то создавать новую тему не вижу смысла

Это сообщение отредактировал(а) MetalFan - 12.1.2012, 08:07


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Чучмек
Дата 11.1.2012, 23:35 (ссылка)    | (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 7
Всего: 41



Цитата(MetalFan @  1.5.2011,  17:36 Найти цитируемый пост)
TTrayIcon

А Shell_NotifyIcon недостаточно?




--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
CodeMonkey
Дата 12.1.2012, 07:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



Это баг в Delphi.

Application.Restore первым делом проверяет "свёрнутость" окна. Проблема в том, что делает она это на Handle себя, а не формы (это при MainFormOnTaskbar = True). Application.Handle и Application.MainForm.Handle - это два разных окна и их состояния могут быть несогласованными, что мы и получаем в этом примере.

Решение: сворачивать Application.

Код
procedure TForm1.appevtMinimize(Sender: TObject);
begin
  tricon.Visible := True;
  // Application:
  if (Application.Handle <> 0) and (Application.Handle <> Application.MainFormHandle) then
    ShowWindow(Application.Handle, SW_MINIMIZE);
  // Form:
  Application.MainForm.Hide;
end;

procedure TForm1.appevtRestore(Sender: TObject);
begin
  tricon.Visible := False;
  // Application:
  if (Application.Handle <> 0) and (Application.Handle <> Application.MainFormHandle) then
    ShowWindow(Application.Handle, SW_NORMAL);
  // Form:
  Application.MainForm.Show;
  if Application.MainForm.WindowState = wsMinimized then
    Application.MainForm.WindowState := wsNormal;
  // Activation:
  SetForegroundWindow(Application.MainFormHandle);
end;

procedure TForm1.triconClick(Sender: TObject);
begin
  if Application.MainForm.Visible then
    Application.Minimize
  else
    Application.Restore;
end;


Добавлено через 2 минуты и 34 секунды
P.S. Эх, проморгал исходную тему, а то сохранил бы тебе полгода мучений smile


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
CodeMonkey
Дата 12.1.2012, 07:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



Цитата
Задача: модифицировать


Чего-то слона-то я и не заметил...

Не очень понял как может быть "если приложение развернуто, но не на переднем плане" одновременно с "при клике мышью на иконке в трее". Ты же скрываешь икноку при показе окна.

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


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
MetalFan
Дата 12.1.2012, 07:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



Цитата(Чучмек @  11.1.2012,  23:35 Найти цитируемый пост)
А Shell_NotifyIcon недостаточно?

по делу есть что сказать?


Цитата(CodeMonkey @  12.1.2012,  07:28 Найти цитируемый пост)
Не очень понял как может быть "если приложение развернуто, но не на переднем плане" одновременно с "при клике мышью на иконке в трее". Ты же скрываешь икноку при показе окна.

В том то и дело, что рассматриваем вариант, когда иконка в трее видна всегда.
Предложенный второй отрезок кода при его вызове работает так: Если приложение свернуто - разворачивает, если развернуто, то сворачивает... 
Необходимо модифицировать: Если приложение НЕ свернуто в трей, но, к примеру, сейчас активно окно другого приложения, то в этом случае наше приложение должно "переехать" на передний план, а не свернуться в трей, при клике по его иконке в этом трее.
Такое поведение наблюдается, к примеру, у The Ваt при соответствующих настройках.
Исходный же алгоритм ВСЕГДА сворачивает приложение, если оно не свернуто.

В итоге задача сводится к тому, что как в момент клика на иконке в трее определить, что ДО клика было активно окно нашего приложения.

Это сообщение отредактировал(а) MetalFan - 12.1.2012, 08:04


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
CodeMonkey
Дата 12.1.2012, 11:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



А, всё, понял.

Это просто: возьми Foreground или Active окно (надо проверить, как там с SOT-окнами), найди следующее по Z-порядку (GetNextWindow). Если твоё - то ты сверху, минимизируй. Если не твоё, то ты перекрыт - сворачивайся.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
MetalFan
Дата 12.1.2012, 11:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



CodeMonkey, не сработало) пробовал так уже. В момент клика, даже если перед этим было активно наше окно, то GetNextWindow от GetForegroundWindow возвращает вообще другое окно, никак не относящееся к нашему приложению.
з.ы. а что такое SOT-окна?

Это сообщение отредактировал(а) MetalFan - 12.1.2012, 11:20


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
CodeMonkey
Дата 12.1.2012, 11:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



SOT = Stay On Top

Через Spy++ проверь, что там GetNextWindow возвращает. По нему же можно цепочку Z order проверить (вроде).


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
MetalFan
Дата 12.1.2012, 11:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



CodeMonkey, вот моя пока нерабочая модификация кода:
Код

  if WindowState = wsMinimized then
  begin
    WindowState := wsNormal;
    Application.Restore;
    Show;
    SetForegroundWindow(Handle);
  end
  else
  begin
    if (GetForegroundWindow = Handle) or (GetNextWindow(GetForegroundWindow, GW_HWNDNEXT) = Handle) then
    begin
      Application.Minimize;
      Hide;
    end
    else
      SetForegroundWindow(Handle);
  end;

Смотрел в S++, в момент нажатия по трею GetNextWindow(GetForegroundWindow, GW_HWNDNEXT) и GetNextWindow(GetForegroundWindow, GW_HWNDPREV) возвращает вообще левые окна... что и видно в S++.

тестовое приложение в аттаче.

Это сообщение отредактировал(а) MetalFan - 12.1.2012, 12:18

Присоединённый файл ( Кол-во скачиваний: 8 )
Присоединённый файл  TrayTest.zip 1,19 Kb


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
CodeMonkey
Дата 12.1.2012, 12:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1839
Регистрация: 24.6.2008
Где: Россия, Тверь

Репутация: 29
Всего: 89



Цитата
вообще левые окна...


А что это за окна? Чьи они? И какие у них атрибуты?

Добавлено через 1 минуту и 2 секунды
Кстати, а ты пробовал GetLastActivePopup?


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы.
PM MAIL WWW ICQ Skype GTalk Jabber   Вверх
MetalFan
Дата 12.1.2012, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

Репутация: 22
Всего: 128



Цитата(CodeMonkey @  12.1.2012,  12:30 Найти цитируемый пост)
А что это за окна? Чьи они? И какие у них атрибуты?

просто окна других приложений. среди них нет моего.

Цитата(CodeMonkey @  12.1.2012,  12:30 Найти цитируемый пост)
Кстати, а ты пробовал GetLastActivePopup? 

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


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
Чучмек
Дата 12.1.2012, 21:23 (ссылка)    | (голосов:5) Загрузка ... Загрузка ... Быстрая цитата Цитата


НЭТ БИЛЭТ
**


Профиль
Группа: Участник
Сообщений: 841
Регистрация: 11.5.2008
Где: СССР

Репутация: 7
Всего: 41



OnPaint


--------------------
умную мысль держи при себе, а дурной - поделись с другими 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Для новичков"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1494 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.