Friday, December 15, 2006

Hiding a TNotebook tab

While developing with TNotebook, i found some problems using TPage.TabVisible property. If we Toggle TabVisible forth and back of more then one page, the tab title does not correspond to the page content. So i took a look into this issue.

To examine the bug i created a test project with a notebook containing 3 pages (Page1, Page2 and Page3 - creative names no?). If we set TabVisible of Page1 to False, everything is fine: the remaining pages are correct. Setting it back to True also works like expected. However, after setting TabVisible of Page1 and the Page2 to false the problem appears: the remaining page (which should be Page3) has the Title of the Page2 and no content (should have a TLabel) inside it.

To the work. A look in TCustomPage.SetTabVisible lead us to TCustomNotebook.AddRemovePageHandle. In this routine, if the TabVisible is set to true a page is added otherwise removed through a widgetset function. Here's the problem: to add the page its passed the VisibleIndex as the insert position but to remove the PageIndex is used as the position instead. In our example, when all Pages are visible, PageIndex = VisibleIndex so no problems, when Page1 is not visible Page2 gets VisibleIndex = 0 and PageIndex = 1. In this situation, setting Page2.TabVisible to false informs the widgetset to remove the page with index 1 (PageIndex) when should be 0 (VisibleIndex).

After the fix, the problem vanishes, but it still not working as should. If Page1 is the active tab and Page1.TabVisible is set to false no page is selected and the content area stay blank. Worse: Notebook.ActivePage still points to Page1. More work.

Examining the ActivePage property we discover that it depends of the TNotebook.PageIndex, so is necessary to update the pageindex properly after toggling the TabVisible. I found that in TNotebook.RemovePage method the new pageindex already computed but does not consider the TabVisible state. I reworked the routine creating to auxiliary functions ,with the suggestive names of SetNewPageIndexAfterPageRemove and GetNearestVisiblePage, which takes in account the TabVisible state.

The test program and the patch can be found in the bugreport.

The patch also does some cleanup in the code:
  • TCustomPage.SetParent set TabVisible to false before calling RemovePage. I removed the TabVisible assignment since RemovePage already does the job.
  • In GetActivePageComponent and GetActivePage i changed from PageIndex to fPageIndex (Notice that in other functions fPageIndex is already used)
PS: I tested in win32 but notice that the modifications are done only in LCL level so should not be problems with other interfaces.

No comments: