Показаны сообщения с ярлыком VCL. Показать все сообщения
Показаны сообщения с ярлыком VCL. Показать все сообщения

суббота, 29 июня 2013 г.

О "тормозах" в FireMonkey

Зачем создавать TMatrix (тем более, что она обычно - Identity) или TPosition в КАЖДОМ объекте? Или я отстал от жизни?

Я вот с VGScene провёл "тьюнинг" по этим вопросам. И стало - сильно легче.

Ну и закешировал "мелкие" часто используемые объекты.

P.S. Я всё хочу "провести тьюнинг" для текущего FM, как я сделал это для VGScene. Но - "руки не доходят"....

пятница, 31 мая 2013 г.

О VCL

Под влиянием "новомодных" тенденций слышу иногда "ах VCL это уже не модно, ах он должен вымереть как динозавры".

Если интересна МОЯ точка зрения - СКАЖУ - VCL по "архитектурности" и "практической вылизанности" - ЛУЧШАЯ библиотека, которую я видел.

А сравнивать - ЕСТЬ с чем - TurboVision, SuperVision, OWL, MFC, QT, UIKit. Что ещё забыл?

Ах да! FireMonkey! Я в FireMonkey пока ГЛУБОКО не погрузился, но я видел VGScene (предтеча FireMonkey) и ОСНОВАТЕЛЬНО ей переделывал.

Что сказать про FM? "АРХИТЕКТУРНО" она мне нравится БОЛЬШЕ, чем VCL. Концепция стилей, слоёного пирога и ОБЩЕЙ канвы - МНЕ СИЛЬНО нравятся.

Но пока FireMonkey - проигрывает VCL по "вылизанности" и стабильности. Но я думаю - "парни справятся с этой проблемой".

Я если найду время - начну глубоко погружаться в FM и тогда буду писать свои КОНКРЕТНЫЕ впечатления, замечания и рецепты по улучшению.

У меня есть "пара идей", которые я бы хотел сделать на FM. Правда у меня основной работы "вагон и маленькая тележка". Но я надеюсь, что я найду время.

Ну и раз уж я начал сравнивать - скажу ещё одно впечатление - КОНЕЧНО UIKit. Он - ХОРОШ. Особенно "для чайников". В нём МНОГО чего уж есть априори. И в нём есть "стройность концепции". Местами "кривая" лично для меня, но стройность и единообразие.

НО VCL - пока - лучший. Но с него - рано или поздно - всё равно надо слезать. Мне так почему-то кажется.

пятница, 17 мая 2013 г.

Чудесный ужас (или - все допускают ошибки)...

Нет. Я конечно очень уважаю "Остап Ибрагимыча" (разработчиков Borland'а). И это не пустые слова. Мне до их уровня - ещё расти и расти. Но у меня в голове всё никак не укладывается вот это:


  TControlActionLink = class(TActionLink)
  protected
    FClient: TControl;

...

  TWinControlActionLink = class(TControlActionLink)
  protected
    FClient: TWinControl;

....

- может быть я чего не понимаю или "не так воспитан".

По мне - одноимённые protected поля в соседних классах - это не комильфо. Особенно если они выполняют ОЧЕНЬ СХОДНЫЕ функции. Куда лучше было бы хотя бы назвать одно поле FClientControl, а другое - FClientWinControl. На мой вкус. Не говоря уж о ненужном дублировании данных.  Всегда вообще-то можно приведение типов написать. И проверку в момент присвоения (AssignClient). Не говоря уж о то, что эта ссылка - НИГДЕ не чистится. А тут имеем БОЛЬШИЕ проблемы с Detsroy. Я правда пока ещё не нащупал - какие именно. Нащупаю - обязательно напишу.

Но по мне этот код и производные от него - "дурно пахнут". Простите уж.

Я думал - хоть под XE это переделали... Ничего подобного :-(

(+)


  TToolbarButton97ActionLink = class(TControlActionLink)
  protected
    FClient: TCustomToolbarButton97;


- получается, что Borland "задал дурной тон", а другие его - банально подхватили. Без критической оценки.

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

Update. И беда как раз не в том, что "Borland сделал криво", в конце-концов - у них "всего пара классов", а в том, что сотни разработчиков эту кривизну подхватили. Ну или я чего-то недопонимаю.

Хотя не перестаю уважать разработчиков Delphi. Хорошее дело делают.

P.S. К коду FireMonkey - у меня тоже есть ряд претензий. Я их позже озвучу. Когда глубже вникну в проблему. Ну или напишу, что их нет, если пойму, что это так или убежу себя в том, что их нет.

P.P.S. Я объяснил свою позицию вроде. Буду рад, если кто-то объяснит её ошибочность.

P.P.P.S. Тут кстати явно в языке бы добавить "шаблонную типизацию с наследованием". Как-то так:

  TControlActionLink = class(TActionLink)
  template type TClientType = TControl;
  protected
    FClient: <TClientType>;
  private
    procedure AssignClient(aClient : <TClientType>);

...

  TWinControlActionLink = class(TControlActionLink)
  override template type TClientType = TWinControl;

....

С контролем всего этого хозяйства компилятором. У меня кстати подобное есть. В скриптовой машине. Правда там не статически, а в run-time это проверяется. Ну хотя бы в run-time - сейчас-то ведь не лучше. Сейчас по сути в run-time и происходит.

А ещё я бы TActionLink - сделал бы вложенным классом. Тоже с "шаблонной типизацией с наследованием". И различал бы abstract class и не abstract class. И требовал бы типизацию для неабстракных классов.

TControl = class(TCompenent)
 template type TControlActionLink = class(TActionLink)
  template type TClientType = TControl;
  protected
    FClient: <TClientType>;
  private
    procedure AssignClient(aClient : <TClientType>);
 ...
 end;
 ...
end;
 
 
TWinControl = class(TControl)
 reimplement template type TControlActionLink = class(TControl.TControlActionLink)
  override template type TClientType = TWinControl;
 end;
...
end;

-- как-то так. Сродни примесям где-то. Это можно кстати сделать через введение "скрытых виртуальных функций". То есть на уровне "препроцессора".

Ввести
function TClientType_Class: TControl; virtual;

Определять её в КАЖДОМ классе, где переопределён TClientType:


class function TControl.TControlActionLink.TClientType_Class: class of TControl; virtual;
begin
 Result := TControl;
end;
 
class function TWinControl.TControlActionLink.TClientType_Class: class of TControl; override;
begin
 Result := TWinControl;
end;
 
class function TControl.TControlActionLink_Class: class of TControl.TControlActionLink; virtual;
begin
 Result := TControl.TControlActionLink;
end;
 
class function TWinControl.TControlActionLink_Class: class of TControl.TControlActionLink; override;
begin
 Result := TWinControl.TControlActionLink;
end;



Вместо:
 FClient := aValue;
компилировать:
 FClient := aValue As TClientType_Class;

Вместо:
 FActionLink := TControlActionLink.Create(Self);
компилировать:
 FActionLink := ControlActionLink_Class.Create(Self);

Ну как-то так. Ну и по рекурсии конечно.

Ну и при статическом обращению к полю FClient и FActionLink - раскрывать их тип в зависимости уже от контекста. Для TControl подставлять одно, для TWinControl - другое. Всё кстати - срастается.


СЛОЖНО - да. Но не невозможно. У себя в скриптах - я пожалуй такое сделаю. Не вижу там препятствий.

Это конечно тоже - "Чудесный ужас". Но из другой оперы. Разрыв шаблонов.

В шаблонах кодогенерации я кстати уже сделал подобное. В шаблонах VCM (не путать с MVC, хотя это из той же оперы, только View->Controller->Model(->Data)). А я то и смотрю, что что-то - знакомое. Когда я до конца это расписал. Привести пример с картинками и кодом?

P.P.P.S. Подозреваю, что всё, что я описал - в каком-нибудь "экзотическом" языке программирования - УЖЕ есть. Мне вообще - пока не удалось - что-нибудь РЕАЛЬНО новое придумать.

суббота, 27 апреля 2013 г.

А ещё есть модуль с DockTree в котором поправлены утечки и проезды по памяти

Живёт тут - https://www.box.com/s/p97j0zo8y3zw8puruo2k
Или тут - https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/l3DockTree.pas

Если у кого-то не скомпилируется, но заинтересует - буду рад ответить на вопросы.

Почему я вдруг говорю о правках в VCL?

Сразу скажу, что далеко не все правки мои лично. И более того - многие люди, которые их делали - работают уже в иных организациях, нежели чем я.

Понятное дело, что МЫ свои проблемы - решили. И будем решать - дальше. И максимально возможное число правок перенесём в Delphi XE3 и XE4. И заодним напишем тесты - там где они не написаны.

Я не верю, что Embarcadero вдруг послушает нас и внесёт эти правки в очередной релиз. Я бы сам бы не стал слушать "какого-то Шуру Люлина со стороны". Более того - может быть найдёт контр-аргументы. Очень их интересно было бы послушать.

И уж точно я не хочу учить программировать "этих чудаков" из Borland'а. У меня - не тот уровень, чтобы кого-то учить.

Но! Пока мы считали, что Delphi - умирает. А Borland - уже умер - мы вносили эти правки по-тихому сами. И были счастливы. И мы думали, что наши правки так и останутся с нами на всю жизнь. И мы не думали, что они кому-то интересны.

Но тут нам показалось, что Delphi обретает вторую жизнь.

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

Вдруг кому что пригодится. И вдруг люди осознают, что мы правы. Ну или укажут - где мы неправы. Что тоже было бы интересно. Может быть у нас просто - "руки кривые". А если - не кривые - может быть - пригодится. (Жаль кстати, что в блоге так мало критических комментариев - создаётся впечатление, что либо всё понятно, либо совсем всё не понятно, либо - пишу я полную чушь)

И вот тогда - если наберётся критическая масса - может быть и Borland (тьфу Embarcadero) прислушается к нам.

А пока - я пойду писать тесты. На то, что смогу.

Ни в коем случае этими правками - не хочу задеть ни Borland, ни Embarcadero. Ошибки делают все. У нас например - их больше. Но мы кстати и Turbo Vision когда-то правили. По делу. Например с STD и CLD. В поиске.

Мы давно выработали себе за правило - в первую очередь искать ошибку у СЕБЯ, а потом только в стандартных библиотеках.

Но и ошибки в стандартных библиотеках - встречаются. Ну или недопонимание.

Надеюсь, что эти посты будут вам полезны.

P.S. Я думаю, что на часть правок - мы сможем найти "тикеты" в нашей базе ошибок. Но думаю, что к сожалению - не на все.

Ещё правки VCL (23)


function TCustomEdit.GetSelStart: Integer; {V} // - не работает на NT 4.0
begin
  //SendMessage(Handle, EM_GETSEL, Longint(@Result), 0);
  Result := LongRec(SendMessage(Handle, EM_GETSEL, 0, 0)).Lo;
end;


function TCustomEdit.GetSelLength: Integer; {V} // - не работает на NT 4.0
begin
  with LongRec(SendMessage(Handle, EM_GETSEL, 0, 0)) do
   Result := Hi - Lo;
end;
(*begin
  SendMessage(Handle, EM_GETSEL, Longint(@Selection.StartPos), Longint(@Selection.EndPos));
  Result := Selection.EndPos - Selection.StartPos;
end;*)


Ещё правки VCL (22)


procedure TCustomMaskEdit.CMEnter(var Message: TCMEnter);
begin
  if IsMasked and not (csDesigning in ComponentState) then
  begin
    if not (msReEnter in FMaskState) then
    begin
      FOldValue := EditText;
      inherited;
    end;
    Exclude(FMaskState, msReEnter);
    // во избежание проблем с позиционированием курсора при
    // получении фокуса
    // CheckCursor; {V}
  end
  else
    inherited;
end;

Ещё правки VCL (21)


procedure TCustomMaskEdit.WMSetFocus(var Message: TWMSetFocus);
begin
  inherited;
  // во избежание проблем с позиционированием курсора при
  // получении фокуса
  {if (IsMasked) then
    CheckCursor;} {V}
end;

Ещё правки VCL (20)


function TransparentStretchBlt(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
  SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; MaskDC: HDC; MaskX,
  MaskY: Integer; aMode : TCopyMode = cmSrcCopy): Boolean; {V}
const
  ROP_DstCopy = $00AA0029;
var
  MemDC: HDC;
  MemBmp: HBITMAP;
  Save: THandle;
  crText, crBack: TColorRef;
  SavePal: HPALETTE;
begin
  Result := True;
  if (Win32Platform = VER_PLATFORM_WIN32_NT) and (SrcW = DstW) and (SrcH = DstH) then
  begin
    MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, 1, 1));
    MemBmp := SelectObject(MaskDC, MemBmp);
    try
      MaskBlt(DstDC, DstX, DstY, DstW, DstH, SrcDC, SrcX, SrcY, MemBmp, MaskX,
        MaskY, MakeRop4(ROP_DstCopy, aMode)); {V}
    finally
      MemBmp := SelectObject(MaskDC, MemBmp);
      DeleteObject(MemBmp);
    end;
    Exit;
  end;
  SavePal := 0;
  MemDC := GDICheck(CreateCompatibleDC(0));
  try
    MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, SrcW, SrcH));
    Save := SelectObject(MemDC, MemBmp);
    SavePal := SelectPalette(SrcDC, SystemPalette16, False);
    SelectPalette(SrcDC, SavePal, False);
    if SavePal <> 0 then
      SavePal := SelectPalette(MemDC, SavePal, True)
    else
      SavePal := SelectPalette(MemDC, SystemPalette16, True);
    RealizePalette(MemDC);

(*    if (aMode = SrcCopy) then
    begin*)
     StretchBlt(MemDC, 0, 0, SrcW, SrcH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcCopy);
     StretchBlt(MemDC, 0, 0, SrcW, SrcH, SrcDC, SrcX, SrcY, SrcW, SrcH, SrcErase);
(*    end
    else
    begin
     StretchBlt(MemDC, 0, 0, SrcW, SrcH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcInvert);
     StretchBlt(MemDC, 0, 0, SrcW, SrcH, SrcDC, SrcX, SrcY, SrcW, SrcH, SrcAnd);
    end*);
    if (aMode = SrcCopy) then {V}
    begin
     crText := SetTextColor(DstDC, $0);
     crBack := SetBkColor(DstDC, $FFFFFF);
    end
    else
    begin
     crText := SetTextColor(DstDC, $FFFFFF);
     crBack := SetBkColor(DstDC, $0);
    end;//aMode = SrcCopy
    if (aMode = SrcCopy) then {V}
    begin
     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcAnd);
     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0, SrcW, SrcH, SrcInvert);
    end//aMode = SrcCopy
    else
    begin
     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcErase);
     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0, SrcW, SrcH, SrcInvert);
//     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcErase);
//     StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0, SrcW, SrcH, SrcCopy);
    end;//aMode = SrcCopy
    SetTextColor(DstDC, crText);
    SetBkColor(DstDC, crBack);

    if Save <> 0 then SelectObject(MemDC, Save);
    DeleteObject(MemBmp);
  finally
    if SavePal <> 0 then SelectPalette(MemDC, SavePal, False);
    DeleteDC(MemDC);
  end;
end;

Ещё правки VCL (19)


function TApplication.IsMDIMsg(var Msg: TMsg): Boolean;
begin
  Result := False;
  if (MainForm <> nil) and (MainForm.FormStyle = fsMDIForm) and
     (Screen.ActiveForm <> nil) {and (Screen.ActiveForm.FormStyle = fsMDIChild)} then {V}
    Result := TranslateMDISysAccel(MainForm.ClientHandle, Msg);
end;

Ещё правки VCL (18)


procedure TApplication.ModalStarted;
begin
  Restore; {V} //поднятие модального при свернутом окне все раскорячивает
  Inc(FModalLevel);
  if (FModalLevel = 1) and Assigned(FOnModalBegin) then
    FOnModalBegin(Self);
end;

Ещё правки VCL (17)


unction GetTopMostWindows(Handle: HWND; Info: Pointer): BOOL; stdcall;

 {V}
 // Винда в некоторых случаях (замечено на переключении приложений Alt+Esc)
 // самостоятельно сносит WS_EX_TOPMOST еще ДО вызова этого метода
 // поэтому дополнительно проверяем TCustomForm.FormStyle
 function lIsTopMostWindow(Handle: HWND) : boolean;
 var
  lControl : TWinControl;
 begin
  Result := (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0);
  if not Result then
  begin
   lControl := FindControl(Handle);
   Result := (lControl is TCustomForm) and
             (TCustomForm(lControl).FormStyle = fsStayOnTop);
  end;
 end;

begin
  Result := True;
  if GetWindow(Handle, GW_OWNER) = Application.Handle then
    if lIsTopMostWindow(Handle) and
      ((Application.MainForm = nil) or PTopMostEnumInfo(Info)^.IncludeMain or
      (Handle <> Application.MainForm.Handle)) then
      Application.FTopMostList.Add(Pointer(Handle))
    else
    begin
      PTopMostEnumInfo(Info)^.TopWindow := Handle;
      Result := False;
    end;
end;

Ещё правки VCL (16)


procedure TApplication.ControlDestroyed(Control: TControl);
begin
  if FMainForm = Control then FMainForm := nil;
  if FMouseControl = Control then FMouseControl := nil;
  if Screen.FActiveControl = Control then Screen.FActiveControl := nil;
  if Screen.FActiveCustomForm = Control then
  begin
   If (Control Is TCustomForm) and (TCustomForm(Control).FormStyle = fsMDIChild) then {V}
   begin
     Screen.FActiveCustomForm := FMainForm;
     Screen.FActiveForm := FMainForm;
   end
   else
   begin
     Screen.FActiveCustomForm := nil;
     Screen.FActiveForm := nil;
   end
  end;
  if Screen.FFocusedForm = Control then Screen.FFocusedForm := nil;
  if FHintControl = Control then FHintControl := nil;
  Screen.UpdateLastActive;
end;

Ещё правки VCL (15)


function TCustomForm.CloseQuery: Boolean;  {V}
var
  I: Integer;
begin
  Result := True;
  if Assigned(FOnCloseQuery) then FOnCloseQuery(Self, Result);
  If Result and
     (FormStyle = fsMDIForm) then
   begin
    for I := 0 to MDIChildCount - 1 do
     begin
      Result := MDIChildren[I].CloseQuery;
      If Not Result then Exit;
     end;
   end;
end;
{function TCustomForm.CloseQuery: Boolean;
var
  I: Integer;
begin
  if FormStyle = fsMDIForm then
  begin
    Result := False;
    for I := 0 to MDIChildCount - 1 do
      if not MDIChildren[I].CloseQuery then Exit;
  end;
  Result := True;
  if Assigned(FOnCloseQuery) then FOnCloseQuery(Self, Result);
end;}

Ещё правки VCL (14)


procedure TCustomForm.WMSysCommand(var Message: TWMSysCommand);

 function IsMDIMaximized: Boolean;
  var
    I: Integer;
  begin
   with Application.MainForm do
    for I := 0 to Pred(MDIChildCount) do
     if MDIChildren[I].WindowState = wsMaximized then
     begin
      Result := True;
      Exit;
     end;
   Result := False;
  end;

 procedure RealignMDIChildren;
  var
    I: Integer;
  begin
   with Application.MainForm do
    for I := 0 to Pred(MDIChildCount) do
     if MDIChildren[I] <> Self then
      MDIChildren[I].Realign;
  end;
var
 lMaximizedMDIChild : boolean;
begin
  lMaximizedMDIChild := (FormStyle = fsMDIChild) and IsMDIMaximized;
  with Message do
  begin
    if (CmdType and $FFF0 = SC_MINIMIZE) and (Application.MainForm = Self) then
      Application.WndProc(TMessage(Message))
    else if (CmdType and $FFF0 <> SC_MOVE) or (csDesigning in ComponentState) or
      (Align = alNone) or (WindowState = wsMinimized) then
      inherited;
    if ((CmdType and $FFF0 = SC_MINIMIZE) or (CmdType and $FFF0 = SC_RESTORE)) and
      not (csDesigning in ComponentState) then
    begin
     if (Align <> alNone) then
      RequestAlign;
     if lMaximizedMDIChild then  {V}
      RealignMDIChildren;        {V}
    end;
  end;
end;

Ещё правки VCL (13)


procedure TCustomForm.WMSize(var Message: TWMSize); {V}
 function IsMDIMaximized: Boolean;
  var
    I: Integer;
  begin
   with Application.MainForm do
    for I := 0 to Pred(MDIChildCount) do
     if MDIChildren[I].WindowState = wsMaximized then
     begin
      Result := True;
      Exit;
     end;
   Result := False;
  end;

begin
 // а то взяли моду ресайзить максимизированные MDIChild'ы
 If (FormStyle = fsMDIChild) and
    IsMDIMaximized and
    (fsCreatedMDIChild in FFormState) and
    (Application.MainForm.ActiveMDIChild <> self) then
 else
  inherited;
end;

Ещё правки VCL (12)


procedure TCustomForm.Activate;
var
 ActChild : TCustomForm; {V}
begin
  {V}
  if (FormStyle = fsMDIForm) and (FActiveControl <> nil) and
     (GetParentForm(FActiveControl).FormStyle = fsMDIChild)
   then
    Begin
     ActChild := GetActiveMDIChild;
     If ActChild <> nil then PostMessage(ActChild.Handle, CM_ACTIVATE, 0, 0);
    end;
  {^V}
  DoNestedActivation(CM_ACTIVATE, ActiveControl, Self);
  if Assigned(FOnActivate) then FOnActivate(Self);
end;

Ещё правки VCL (11)


procedure TCustomForm.WndProc(var Message: TMessage);
var
  FocusHandle: HWND;
  SaveIndex: Integer;
  MenuItem: TMenuItem;
  Canvas: TCanvas;
  DC: HDC;
begin
  with Message do
    case Msg of
      WM_ACTIVATE, WM_SETFOCUS, WM_KILLFOCUS:
        begin
          if not FocusMessages then Exit;
          if (Msg = WM_SETFOCUS) and not (csDesigning in ComponentState) then
          begin
            FocusHandle := 0;
            {V}
            // это слишком умное поведение MDI-окон мы подправим.
            // ибо нефиг забирать фокус отовсюду, откуда можно...
            if (FormStyle = fsMDIForm) and (ActiveMDIChild <> nil) and FRefocusMDIChild then
            begin
             FocusHandle := ActiveMDIChild.Handle
            end
            else
            if (FActiveControl <> nil) and (FActiveControl <> Self) then
              FocusHandle := FActiveControl.Handle;
            if FocusHandle <> 0 then
            begin
              Windows.SetFocus(FocusHandle);
              Exit;
            end;
          end
          else
           if (((Msg = WM_ACTIVATE) and (WParam = WA_INACTIVE)) or (Msg = WM_KILLFOCUS)) and not (csDesigning in ComponentState) then
           begin
            FRefocusMDIChild := (ActiveMDIChild <> nil) and IsChild(ActiveMDIChild.Handle, GetFocus);
           end;
          {/V}
        end;
      CM_EXIT:
        if HostDockSite <> nil then DeActivate;

Ещё правки VCL (10)


procedure TCustomFrame.AddActionList(ActionList: TCustomActionList);
var
  Form: TCustomForm;
begin
  Form := GetParentForm(Self);
  if Form <> nil then
  begin
    if Form.FActionLists = nil then Form.FActionLists := TList.Create;
    if Form.FActionLists.IndexOf(ActionList) = -1 then {V}
     Form.FActionLists.Add(ActionList);
  end;
end;

Ещё правки в VCL (9)


procedure TScrollingWinControl.WMSize(var Message: TWMSize);
var
  NewState: TWindowState;
////////////////////////////////////////////////////////////////////////////////
//
// Дата изменений:                                                           {V}
//   26/11/2004                                                              {V}
// Причина изменений:                                                        {V}
//                                                                           {V}
// ***************************** ОКРУЖЕНИЕ ********************************* {V}
// Есть форма, на которой находятся два компонента, один с Align = alNone,   {V}
// другой с Align = alClient.                                                {V}
//                                                                           {V}
// ***************************** ПРОБЛЕМА ********************************** {V}
// При восстановлении окна у компонента, у которого Align = alClient не      {V}
// рисовались полосы прокрутки, в случае когда на форме в правом нижнем углу {V}
// находился компонент с Align = alNone.                                     {V}
//                                                                           {V}
// *************************** ЧТО ПРОСХОДИЛО ****************************** {V}
// 1. После восстановления окна компонент с Align = alNone уходил за пределы {V}
//    клиентской области формы, у формы появлялись полосы прокрутки;         {V}
// 2. TControl.AlignControls вызываемая по WM_SIZE, перед размещением        {V}
//    компонентов, вычисляет клиентскую область используя                    {V}
//    TControl.AdjustClientRect (virtual), которая перекрыта у               {V}
//    TScrollingWinControl и учитывает скроллеры окна:                       {V}
//
//    Rect := Bounds(-HorzScrollBar.Position, -VertScrollBar.Position,
//      Max(HorzScrollBar.Range, ClientWidth), Max(ClientHeight,
//      VertScrollBar.Range));
//    inherited AdjustClientRect(Rect);
//
// 3. Учитывая, что до восстановления окна компонент с Align = alNone        {V}
//    находился в правом, нижнем углу получается, что после вычисления       {V}
//    клиентская область до восстановления и после восстановления осталась   {V}
//    такой же и для компонента с Align = alClient размер окна не            {V}
//    изменился (AdjustClientRect до и после одинаковая);                    {V}
// 4. Далее происходит размещение компонента Align = alNone не на основе     {V}
//    AdjustClientRect, а на основе клиентской области windows ClientRect.   {V}
//    Его перемещают, в соответствии с его Anchors, в пределы клиентской     {V}
//    области формы и скроллеры исчезают;                                    {V}
// 5. AdjustClientRect изменилась, но компоненты которые уже разместили об   {V}
//    этом не узнали;                                                        {V}
//                                                                           {V}
// ****************************** РЕЗЮМЕ *********************************** {V}
// Поэтому реакция на изменение размеров происходит в цикле, и если на       {V}
// выходе размер вычисляемой клиентской области изменился то нужно           {V}
// переразместить компоненты;                                                {V}
//
////////////////////////////////////////////////////////////////////////////////
   lBeforeClientRect : TRect;                                                {V}
   lAfterClientRect  : TRect;                                                {V}
   lCycleCount : Integer;
////////////////////////////////////////////////////////////////////////////////
begin
////////////////////////////////////////////////////////////////////////////////
 lCycleCount := 0;
 repeat                                                                      {V}
  Inc(lCycleCount);
  lBeforeClientRect := GetClientRect;                                        {V}
  AdjustClientRect(lBeforeClientRect);                                       {V}
////////////////////////////////////////////////////////////////////////////////
  Inc(FAutoRangeCount);
  try
    inherited;
    NewState := wsNormal;
    case Message.SizeType of
      SIZENORMAL: NewState := wsNormal;
      SIZEICONIC: NewState := wsMinimized;
      SIZEFULLSCREEN: NewState := wsMaximized;
    end;
    Resizing(NewState);
  finally
    Dec(FAutoRangeCount);
  end;
  FUpdatingScrollBars := True;
  try
    CalcAutoRange;
  finally
    FUpdatingScrollBars := False;
  end;
  if FHorzScrollBar.Visible or FVertScrollBar.Visible then
    UpdateScrollBars;
////////////////////////////////////////////////////////////////////////////////
  lAfterClientRect := GetClientRect;                                         {V}
  AdjustClientRect(lAfterClientRect);                                        {V}
 until EqualRect(lBeforeClientRect, lAfterClientRect)                        {V}
       or (lCycleCount > 8); // устраняем зависание - http://mdp.garant.ru/pages/viewpage.action?pageId=300033419&focusedCommentId=333546939#comment-333546939
////////////////////////////////////////////////////////////////////////////////
end;