пятница, 3 июня 2016 г.

Как изменились размеры окон в Берлине


Решил я наконец-то попробовать Берлинскую версию. И вот странность - перекомпиленное приложение запускается в маленьком окошке и никак не желает расширяться мышкой. Я был обескуражен.

Оказалось, что окно теперь создаётся не с теми размерами, что были заданы в дизайнере, а с какими-то 320х240. А почему мышкой их нельзя изменить? А потому, что у окна когда-то было выставлено
  Resize := ( NewWidth >= 560 ) and ( NewHeight >= 400 );
в FormCanResize. И всё (- телемаркет)! Раньше я читал большие размеры из INI и форма после сздания становилась большой и красивой. Теперь же при попытке задать свойство Height параметр NewWidth, равный текущему слишком маленькому Width=320, блокирует изменение в FormCanResize. Так же точно маленький NewHeight=240 не даёт установить большой Width, считанный из INI

Виноватым я назначил форменное свойство Position. Оно стало задавать эти удивительные  размеры 320х240 создаваемой форме при значениях poDefaultPosOnlypoDesigned(даже!) и всех poХХХCenter, т.е. вообще всех значениях кроме poDefault и poDefaultSizeOnly. Не странно ли?

Кто виноват - ясно. Что делать? Я изменил FormCanResize на

  Resize := ((NewWidth = Width) or ( NewWidth >= 560 )) and
            ((NewHeight = Height) or ( NewHeight >= 400 ));

Уже не так просто, как раньше. Но за то - надёжнее. Возможно, решением проблемы было бы и применение SetBounds для одновременного изменения размеров окна. Но я сделал как сделал.

Теперь можно задать нужные размеры окна путём хаккерской записи нужных цифр в INI перед стартом приложения или получить допустимые цифры путём "привязывания" окна к границе экрана перед закрытием его и сохранением размеров для следующего сеанса. И всё снова стало хорошо.

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

А что интересного случилось у вас в Берлине?

среда, 1 июня 2016 г.

Как запомнить привязку окна в Win7+

Иллюстрации с сайта windows.microsoft.com

Раньше для сохранения позиции окна приложения я часто использовал простой код:

procedure Store( F: TForm );
var
  wp: TWindowPlacement;
begin
  with TIniFile.Create( ChangeFileExt( Application.ExeName, '.ini' ) ) do
  try
    wp.length := SizeOf( TWindowPlacement );
    GetWindowPlacement( F.Handle, @wp );
    with wp.rcNormalPosition do begin
      WriteInteger( F.ClassName, 'Left', Left );
      WriteInteger( F.ClassName, 'Top', Top );
      WriteInteger( F.ClassName, 'Width', Right - Left );
      WriteInteger( F.ClassName, 'Height', Bottom - Top );
    end;
    WriteBool(F.ClassName, 'Maximized', F.WindowState = wsMaximized );
  finally Free;
  end;
end;

Я использовал не "внешние" (Left, Top, Width, Height) координаты окна, а "нормальные", полученные ф-ей GetWindowPlacement, т.к. в случае разворачивания окна на весь экран узнать потом его нормальный размер по "внешним" координатам нельзя. Разумеется, в случае, если окно и так имеет нормальный размер, "нормальные" координаты совпадают с "внешними". Вызов всего одной ф-ии работал на все случаи жизни.

 А для восстановления позиции и размеров я делал так:

procedure Restore( F: TForm );
begin
  with TIniFile.Create( ChangeFileExt( Application.ExeName, '.ini' ) ) do
  try
    F.Left   := ReadInteger( F.ClassName, 'Left'  , F.Left );
    F.Top    := ReadInteger( F.ClassName, 'Top'   , F.Top );
    F.Width  := ReadInteger( F.ClassName, 'Width' , F.Width );
    F.Height := ReadInteger( F.ClassName, 'Height', F.Height );
    if ReadBool(F.ClassName, 'Maximized', F.WindowState = wsMaximized ) then
      F.WindowState := wsMaximized;
  finally Free;
  end;
end;

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

Однако, в новых (конечно, всё относительно) версиях Windows появилась "привязка" окна мышью. Теперь картина стала менее радостной - для программного кода состояние "привязанного" окна TForm.WindowState остаётся прежним, "нормальные" координаты окна и флаги в структуре TWindowPlacement не меняются, и, соответственно, при перезапуске приложения его окно появляется совсем не в том виде, в каком оно было при предыдущем закрытии. Это неприятно.

Процедуру сохранения координат пришлось переписать:

procedure Store( F: TForm );
var
  wp: TWindowPlacement;
  R: Trect;
  M: Boolean;
begin
  M := F.WindowState = wsMaximized;
 
  if M then begin
    wp.length := SizeOf( TWindowPlacement );
    GetWindowPlacement( F.Handle, @wp );
    R := wp.rcNormalPosition;
  end
  else
    GetWindowRect( F.Handle, R );
 
  with TIniFile.Create( ChangeFileExt( Application.ExeName, '.ini' ) ) do
  try
    with R do begin
      WriteInteger( F.ClassName, 'Left', Left );
      WriteInteger( F.ClassName, 'Top', Top );
      WriteInteger( F.ClassName, 'Width', Right - Left );
      WriteInteger( F.ClassName, 'Height', Bottom - Top );
    end;
    WriteBool(F.ClassName, 'Maximized', M );
  finally Free;
  end;
end;
 
Т.е. теперь прежние, полученные ф-ей GetWindowPlacement "нормальные" координаты я беру только в случае, если окно "развёрнуто на весь экран". В остальных случаях я использую "внешние" координаты, полученные ф-ей GetWindowRect.

Вероятно, в будущих версиях Delphi на это обстоятельство обратят внимание. Вероятно, уже сейчас в WinAPI есть методы определения этой ситуации, просто я пока не смог этого найти. Но также вероятно, что ничего этого нет и не будет. Об этом говорит хотя бы поведение окна при привязке, разворачивании и восстановлении.


Eсли вы знаете о том, как иначе определить и использовать новый вид состояния окна, напишите.