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

Поиск:

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


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


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

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



Чучмек, выскажись по-подробнее....

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


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


НЭТ БИЛЭТ
**


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

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



Если окно приложения перекрыто другими окнами и становится активным, то для окна вызывается OnPaint
А если окно не перекрыто - то  OnPaint не вызывается.
Отсюда решение: По клику на иконку в трее сделать окно активным и запустить ожидание события  OnPaint (0,5 сек)
Если событие не произошло - скрыть окно.

Это сообщение отредактировал(а) Чучмек - 12.1.2012, 21:55


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


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


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

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



Чучмек, хм, как самый крайний вариант может и сойдет, но, имхо, не очень красивое решение...


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


Эксперт
***


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

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



Выяснил, что все функции работают только в контексте цепочки окна. Я не нашёл ни одной функции, которая давала бы доступ к глобальному Z-порядку.

Для теста использовал такой код:
Код
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;

function GetProcessImageFileName(hProcess: THandle; lpImageFileName: PWideChar; nSize: DWORD): DWORD; stdcall; external 'PsAPI.dll' name 'GetProcessImageFileNameW';

procedure TForm1.triconClick(Sender: TObject);

  function GetPrevWnd(const AWnd: HWND): HWND;
  // тут - всякий бред, я экспериментировал
  type
    TParams = record
      PID: DWord;
      Src: HWND;
      Dst: HWND;
    end;
    PParams = ^TParams;

    function Enumer(AWnd: HWND; Params: PParams): BOOL; stdcall;
    var
      PID: DWORD;
    begin
      Result := True;
      if GetWindowThreadProcessId(AWnd, PID) <> 0 then
      begin
        if PID <> Params.PID then
        begin
          Params.Dst := AWnd;
          Result := False;
        end;
      end;
    end;

  var
    PID: DWORD;
    Params: TParams;
  begin
    if GetWindowThreadProcessId(AWnd, PID) = 0 then
    begin
      Result := 0;
      Exit;
    end;

    Params.PID := PID;
    Params.Src := AWnd;
    Params.Dst := 0;
    EnumWindows(@Enumer, Integer(@Params));
    Result := Params.Dst;
  end;

  function GetWindowText(const AWnd: HWND): String;
  var
    Buf1, Buf2: String;
    PID: DWORD;
    Handle: THandle;
  begin
    SetLength(Buf1, 10240);
    SetLength(Buf1, GetClassName(AWnd, PChar(Buf1), Length(Buf1)));

    SetLength(Buf2, 10240);
    SetLength(Buf2, Windows.GetWindowText(AWnd, PChar(Buf2), Length(Buf2)));

    if GetWindowThreadProcessId(AWnd, PID) <> 0 then
    begin
      Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID);
      try
        SetLength(Result, 10240);
        SetLength(Result, GetProcessImageFileName(Handle, PChar(Result), Length(Result)));
        if Result = '' then
          Result := SysErrorMessage(GetLastError);
      finally
        CloseHandle(Handle);
      end;
      Result := ExtractFileName(Result);
    end
    else
      Result := '?';

    Result := Format('[%s] "%s" (%s)', [Buf1, Buf2, Result]);
  end;

begin
  Label1.Caption := 'Application.Handle: ' + IntToHex(Application.Handle, 8);
  Label2.Caption := 'Application.MainForm.Handle: ' + IntToHex(Application.MainForm.Handle, 8);
  Label3.Caption := 'GetForegroundWindow: ' + IntToHex(GetForegroundWindow, 8) + ' ' + GetWindowText(GetForegroundWindow);
  Label4.Caption := 'GetActiveWindow: ' + IntToHex(GetActiveWindow, 8) + ' ' + GetWindowText(GetActiveWindow);
  Label5.Caption := 'Next(GetForegroundWindow): ' + IntToHex(GetNextWindow(GetForegroundWindow, GW_HWNDNEXT), 8) + ' ' + GetWindowText(GetNextWindow(GetForegroundWindow, GW_HWNDNEXT));
  Label6.Caption := 'Next(GetActiveWindow): ' + IntToHex(GetNextWindow(GetActiveWindow, GW_HWNDNEXT), 8) + ' ' + GetWindowText(GetNextWindow(GetActiveWindow, GW_HWNDNEXT));
  Label7.Caption := 'Prev(GetForegroundWindow): ' + IntToHex(GetNextWindow(GetForegroundWindow, GW_HWNDPREV), 8) + ' ' + GetWindowText(GetNextWindow(GetForegroundWindow, GW_HWNDPREV));
  Label8.Caption := 'Prev(GetActiveWindow): ' + IntToHex(GetNextWindow(GetActiveWindow, GW_HWNDPREV), 8) + ' ' + GetWindowText(GetNextWindow(GetActiveWindow, GW_HWNDPREV));
  Label9.Caption := 'LastActive(GetForegroundWindow): ' + IntToHex(GetLastActivePopup(GetForegroundWindow), 8) + ' ' + GetWindowText(GetLastActivePopup(GetForegroundWindow));
  Label10.Caption := 'LastActive(GetActiveWindow): ' + IntToHex(GetLastActivePopup(GetActiveWindow), 8) + ' ' + GetWindowText(GetLastActivePopup(GetActiveWindow));
  Label11.Caption := 'MyPrev(GetForegroundWindow): ' + IntToHex(GetPrevWnd(GetForegroundWindow), 8) + ' ' + GetWindowText(GetPrevWnd(GetForegroundWindow));
  Label12.Caption := 'MyPrev(GetActiveWindow): ' + IntToHex(GetPrevWnd(GetActiveWindow), 8) + ' ' + GetWindowText(GetPrevWnd(GetActiveWindow));

  {
  if Application.MainForm.Visible then
    Application.Minimize
  else
    Application.Restore;
  }
end;


Предлагаю попробовать так: перечислить все окна. Отобрать из них те, которые видимые, не SOT и пересекают твой клиентский прямоугольник. Если такие есть - ты перекрыт, давай наверх. Если таких нет - ты наверху, убирайся в трей.

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


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


НЭТ БИЛЭТ
**


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

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



PtVisible


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


Опытный
**


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

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



http://delphimaster.net/view/2-1169797326
Или вот, что получилось у меня.
Код

function EnumProc(Handle: THandle; var Foreground: THandle): Boolean; stdcall;
var
  ClassName: array [Byte] of Char;
begin
  GetClassName(Handle, ClassName, SizeOf(ClassName));
  Foreground := Handle;
  Result := not IsWindowEnabled(Handle) or not IsWindowVisible(Handle) or
    (GetWindow(Handle, GW_OWNER) <> 0) or
    (GetWindowLongPtr(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) or
    (ClassName = 'NotifyIconOverflowWindow') or
    (ClassName = 'Shell_TrayWnd') or
    (ClassName = 'Internet Explorer_Hidden');
end;

function GetForeground: THandle;
begin
  EnumWindows(@EnumProc, NativeInt(@Result));
end;


Это сообщение отредактировал(а) Qu1nt - 13.1.2012, 17:12
PM MAIL   Вверх
MetalFan
Дата 13.1.2012, 09:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(CodeMonkey @  13.1.2012,  01:23 Найти цитируемый пост)
и пересекают твой клиентский прямоугольник.

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


Цитата(CodeMonkey @  13.1.2012,  01:23 Найти цитируемый пост)
глянуть в сторону GetClipBox

а вот это посмотрим

Цитата(Чучмек @  13.1.2012,  02:53 Найти цитируемый пост)
PtVisible 

хм, и что, проверять каждый пиксель формы? ну как еще один крайний вариант - сойдет. хотя оно же не учитывает так называемые SOT окна наверняка.


Qu1nt, да, тема стара, как мир)))
предложенный код исходит из предположения, что EnumWindows перечисляет окна в порядке видимости сверху вниз? А список исключения по ClassName если вдруг поменяется? программа какая-либо сторонняя вдруг так же вылезет "наверх"... спасибо, как вариант, возможно прокатит. Но как-то на мой взгляд не надежно....

Добавлено @ 09:12
з.ы. как же ритлабовцы в своем thebat добились нужного поведения...  и uTorrent тоже как надо работает....

Это сообщение отредактировал(а) MetalFan - 13.1.2012, 09:15


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


Эксперт
***


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

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



Не видел TheBat, но может они читерят smile

Например, первый клик - если окно видимо, то наверх его. Если второй клик (но не дабл-клик) происходит менее чем через 2 сек после первого - то свернуть окно, если позднее - то начать с начала.


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


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


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

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



CodeMonkey, неа... даж если надо окном бата/utorrenta поместить, к примеру, калькулятор, то при клике на иконку в трее окно не свернется в трей, а "переедет" на передний план.

Сегодня попробую все предложенные варианты, отпишусь.

Добавлено через 2 минуты и 29 секунд
вот еще рещение, как раз, как ты писал с проверкой на пересечение окон, что выше по Z-порядку находятся. В общем надо пробовать.


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


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


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

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



Небольшой подитог: все способы, определяющие частично перекрытие окна не катят на мой взгляд. т.к. неправильно сработают в след.ситуации: наше окно открыто не на весь экран и видно полностью, рядом АКТИВНО окно другого приложения так же не на весь экран и не перекрывающее наше окно. В этом случае по логике с перекрытиями наше окно считается поверх всех, т.к. видно полностью, что на мой взгляд неправильно.

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

и одна вот эта часть проверки, кмк, так же написана неверно (скобок не хватает):
Код

...
(GetWindowLongPtr(Handle, GWL_EXSTYLE and WS_EX_TOPMOST) <> 0) or
...

Разве должно быть не так:
Код

((GetWindowLongPtr(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST) <> 0) or




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


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


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

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



В общем модифицировал функцию от Qu1nt и все заработало ПОЧТИ как надо:
Код

function EnumProc(Handle: HWND; LResult: LPARAM): Boolean; stdcall;
begin
  PInteger(LResult)^:= Handle;
  Result := not IsWindowEnabled(Handle) or
            not IsWindowVisible(Handle) or
            ( GetWindow(Handle, GW_OWNER) <> 0 ) or
            ( GetWindowLongPtr(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0 );
end;

function GetForeground: THandle;
begin
  EnumWindows(@EnumProc, NativeInt(@Result));
end;

Код

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


Это Почти заключается в небольшом отличии от поведения ранееупомянутой мыши.
Наше окно всегда считается foreground, даже если сейчас активно другое окно с ExStyle = WS_EX_TOPMOST...
С одной стороны это наверное правильно... Замена проверки ExStyle на WS_EX_TOOLWINDOW привело к тому, что если на экране есть одно SOT-окно, то наше окно вообще перестает сворачиваться (такое же поведение и у uTorrent, кстати).
Как же в бате сделали разработчики, чтобы все работало как надо красиво... загадка.



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


Опытный
**


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

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



Ой, не там скобку убрал:
Код

(GetWindowLongPtr(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) 

Подход основывался на:
http://stackoverflow.com/questions/210504/...ke-alt-tab-does
http://code.google.com/p/k-kovalev-persona...DataProvider.cs
PM MAIL   Вверх
Qu1nt
Дата 13.1.2012, 17:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



MetalFan, уточни какого поведения ты хочешь добиться.
PM MAIL   Вверх
MetalFan
Дата 13.1.2012, 18:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Qu1nt, поведение я ж вроде описывал.
Необходимо момент нажатия на иконку в трее:
1. Если окно свернуто, то развернуть его.
2. Если окно приложения активно в данный момент, то свернуть его "в трей".
3. Если окно развернуто из трея, но не активно в данный момент, то сделать его активным.

в п.3 как раз и загвоздка. Как корректно определить, активно ли окно в момент нажатия на иконку в трее.
Самый приближенный к "правде" вариант - последний приведенный мною на основании твоего кода.



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


Опытный
**


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

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



Вот еще вариант:
Код

function EnumProc(Handle: HWND; LParam: LPARAM): BOOL; stdcall;
begin
  PHandle(LParam)^ := Handle;
  Result := (GetParent(Handle) <> 0) or
    (GetWindowLongPtr(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0);
end;

function GetForeground: THandle;
begin
  EnumWindows(@EnumProc, LPARAM(@Result));
end;

Разницы между поведением этого кода и The Bat! 5 не увидел. 
PM MAIL   Вверх
Страницы: (4) Все 1 [2] 3 4 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Для новичков"
SnowyMetalFan
bemsPoseidon
Rrader

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

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

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

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


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

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


 




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


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

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