Tuesday, June 9, 2009

The Empire Strikes Back

The last changes to the LCL and my docking manager have made docking much more useful, across all platforms. The lack of dockable forms, on some platforms, lead me to a new notebook, whose pages can be undocked by dragging their tabs. But now a lurking bug started to manifest, and this one deserves another change to the LCL :-(

The woes started with the flawed Delphi model, that did not initialize the DropAlign before invoking PositionDockRect; everything happened to work only because the DropAlign was determined later, and then was used in the next invocation of PositionDockRect. Next came the docking manager, that was ignored in the entire determination of the DropAlign. After some experiments I found an patch, that finally looked acceptable to Paul, and was added to the LCL. Now the docking manager had a chance in TDockManager.PositionDockRect to determine the drop zone and alignment, and to store these values in the docking object.

Unfortunately that patch results in a wrong DropAlign in the very last step of docking, when a control is re-docked within its host docksite. Then it can happen that the already taken removal of the control leads to a changed site (tree) layout, with a new DockRect and DropAlign.

Now it's time again to revert to one of my earlier suggestions, to let the docking manager determine the DropAlign in GetDockEdge, so that PositionDockRect will find the DropAlign already set properly and must not do any new arbitration. TControl.GetDockEdge only has to find out, whether its HostDockSite uses an DockManger, and invoke its new GetDockEdge method instead of the standard procedure. Remember that GetDockEdge can return nothing but left/right or top/bottom alignment, because it has no idea of dock zones, notebook docking or other special docking modes, implemented in a specialized docking manager.

The new TDockManager.GetDockEdge method can implement the same algorithm as used in TControl.GetDockEdge, or any other algorithm in an overridden method. One problem is the mouse position, relative to the DropOnControl. When a docksite contains dock headers, in addition to the docked controls, no DropOnControl can be found when the mouse hovers over an header. And when a control is found, its provided BoundsRect only covers the control, not the dock zone. This means that the drag manager should immediately invoke the dockmanager's GetDockEdge, and supply it with the mouse position within the dock site, regardless of any DropOnControl. OTOH a usable default implementation of the new TDockManager.GetDockEdge will fall back to the TControl.GetDockEdge method. Consequently the dockmanager should receive the docking object, that contains all required information for all possible cases.

1 comment:

  1. The flawed Delphi docking model results in another problem: currently the DropAlign is determined before dmDragMove, but after sending demDragLeave and dmDragEnter. Consequently the invalid DropAlign will cause an invalid DockRect from PositionDockRect, and the docking manager will signal no valid target, by setting DragTarget=Nil, so that the following determination of the DropAlign is skipped. Again the Delphi model only happens to work, based on the old values in the dock object. Any OnDockOver handler can be confused by these old values in dmDragEnter.

    ReplyDelete